;============================================================================
; Faxanadu (U).nes
;
; PRG15_MIRROR ($c000 - $ffdf)
;============================================================================
.segment "PRG15_MIRROR"
.reloc
[c000]UI_SetHUDPPUAttributes:
;
; Set the PPUADDR to 0x23C0, the top row where the status
; bar resides.
;
[c000]a9 23LDA #$23
[c002]8d 06 20STA a:PPUADDR; Set upper PPUADDR as 0x23.
[c005]a9 c0LDA #$c0
[c007]8d 06 20STA a:PPUADDR; Set upper PPUADDR as 0xC0.
[c00a]ae 8d 03LDX a:UI_AttributeDataIndex; X = HUD attribute data index, computed when setting up the screen.
[c00d]bd 1b c0LDA a:HUD_ATTRIBUTE_DATA_BY_INDEX,X; Load the value for the attribute data.
[c010]a2 08LDX #$08; X = 8 (loop counter).
[c012]@_writeLoop:
[c012]8d 07 20STA a:PPUDATA; Write to the PPU.
[c015]caDEX; X--
[c016]d0 faBNE @_writeLoop; If X > 0, loop.
;
; Draw the HUD.
;
[c018]4c 58 c0JMP UI_DrawHUD; Jump to draw the HUD.
;============================================================================
; Attribute data used for the HUD.
;
; These are indexed by values from lookup table
;
PALETTE_INDEX_TO_UI_BG_ATTRIBUTE_INDEX (stored in
;
UI_AttributeDataIndex.
;
; Only values 0, 1, 2, and 3 are used.
;
; The rest seem to be unused, but many end up styled to
; better match regions of the game.
;
; XREFS:
;
UI_SetHUDPPUAttributes
;============================================================================
[c01b]HUD_ATTRIBUTE_DATA_BY_INDEX:
[c01b]00.byte $00; [0]: Dartmoor, Castle of Fraternal, King Grieve's Room
[c01c]55.byte $55; [1]: Start Screen
[c01d]aa.byte $aa; [2]: Most exterior areas.
[c01e]ff.byte $ff; [3]: Most interior areas.
[c01f]41.byte $41; [4]: Here and below are unused.
[c020]20.byte $20; [5]:
[c021]04.byte $04; [6]:
[c022]07.byte $07; [7]:
[c023]08.byte $08; [8]:
[c024]09.byte $09; [9]:
[c025]0a.byte $0a; [10]:
[c026]61.byte $61; [11]:
[c027]20.byte $20; [12]:
[c028]04.byte $04; [13]:
[c029]0b.byte $0b; [14]:
[c02a]0c.byte $0c; [15]:
[c02b]0d.byte $0d; [16]:
[c02c]0e.byte $0e; [17]:
[c02d]56.byte $56; [18]:
[c02e]20.byte $20; [19]:
[c02f]03.byte $03; [20]:
[c030]0f.byte $0f; [21]:
[c031]10.byte $10; [22]:
[c032]11.byte $11; [23]:
;============================================================================
; DEADCODE
;============================================================================
[c033]DEADCODE_FUN_PRG15_MIRROR__c033:
[c033]a0 00LDY #$00
[c035]b1 02LDA (Temp_Addr_L),Y
[c037]85 e8STA PPU_TargetAddr
[c039]c8INY
[c03a]b1 02LDA (Temp_Addr_L),Y
[c03c]85 e9STA PPU_TargetAddr_U
[c03e]c8INY
[c03f]b1 02LDA (Temp_Addr_L),Y
[c041]48PHA
[c042]20 dc cfJSR PPUBuffer_QueueCommandOrLength
[c045]68PLA
[c046]85 00STA Temp_00
[c048]a0 03LDY #$03
[c04a]@LAB_PRG15_MIRROR__c04a:
[c04a]b1 02LDA (Temp_Addr_L),Y
[c04c]9d 00 05STA a:PPUBuffer,X
[c04f]e8INX
[c050]c8INY
[c051]c6 00DEC Temp_00
[c053]d0 f5BNE @LAB_PRG15_MIRROR__c04a
[c055]86 20STX PPUBuffer_WriteOffset
[c057]60RTS
[c058]UI_DrawHUD:
[c058]a9 00LDA #$00
[c05a]20 90 f9JSR UI_DrawTimeValue; Draw the time value.
[c05d]20 e7 f9JSR UI_DrawGoldValue; Draw the gold value.
[c060]20 fb cfJSR PPUBuffer_DrawAll; Flush to the PPU.
[c063]20 75 f9JSR UI_DrawPlayerExperience; Draw the player experience value.
[c066]20 fb cfJSR PPUBuffer_DrawAll; Flush to the PPU.
[c069]20 fb cfJSR PPUBuffer_DrawAll; Flush to the PPU.
[c06c]20 bd c0JSR UI_DrawPlayerHP; Draw the player's HP bar.
[c06f]20 fb cfJSR PPUBuffer_DrawAll; Flush to the PPU.
[c072]ad 9a 03LDA a:Player_MP; Load the player's MP.
[c075]20 85 faJSR Player_SetMP; Set it and draw.
[c078]4c fb cfJMP PPUBuffer_DrawAll; Flush to the PPU.
;============================================================================
; Increase the player's HP.
;
; This will be capped to 80HP.
;
; INPUTS:
; A:
; The number of health points to add.
;
;
Player_HP_U:
; The upper byte of player health to add to.
;
; OUTPUTS:
;
Player_HP_U:
; The new upper byte of player health.
;
; CALLS:
;
UI_DrawPlayerHPValue
;
; XREFS:
;
Player_HandleTouchBread
;
Player_FillHPAndMP
;
Player_UseRedPotion
;============================================================================
[c07b]Player_AddHP:
;
; Update the player's HP with the provided value.
;
[c07b]18CLC; Clear carry so it's not added.
[c07c]6d 31 04ADC a:Player_HP_U; Add the provided HP to the player's health.
[c07f]8d 31 04STA a:Player_HP_U; Store as the new health.
;
; Check against the cap.
;
[c082]c9 50CMP #$50; Cap at 80 HP.
[c084]90 05BCC @_drawHP; Jump if it's under.
[c086]a9 50LDA #$50; Else, cap it to 80HP.
[c088]8d 31 04STA a:Player_HP_U; Store as the new health.
[c08b]@_drawHP:
[c08b]4c 75 faJMP UI_DrawPlayerHPValue
[c08e]Player_ReduceHP:
;
; Reduce the lower byte of player health.
;
[c08e]ad 32 04LDA a:Player_HP_L; Load the lower byte of the player's health.
[c091]38SEC
[c092]ed bc 04SBC a:Arg_PlayerHealthDelta_L; Subtract the specified lower byte of health.
[c095]8d 32 04STA a:Player_HP_L; Store it as the new player health.
;
; Reduce the upper byte of player health.
;
[c098]ad 31 04LDA a:Player_HP_U; Load the upper byte of the player's health.
[c09b]ed bd 04SBC a:Arg_PlayerHealthDelta_U; Subtract the specified upper byte of health.
[c09e]8d 31 04STA a:Player_HP_U; Store it as the new player health.
[c0a1]b0 17BCS @_isStillAlive; If there's still health left, the player is still alive.
;
; The player is out of health. They may die.
;
[c0a3]a9 00LDA #$00
[c0a5]8d 31 04STA a:Player_HP_U; Set the player's upper byte of health to 0.
[c0a8]20 bd c0JSR UI_DrawPlayerHP; Draw the health.
;
; Check if the player has an elixir before killing them.
;
[c0ab]ad 2c 04LDA a:SpecialItems; Load the player's special items.
[c0ae]29 08AND #$08; Check if the player has an elixir.
[c0b0]f0 03BEQ @_killPlayer; If not, kill the player.
;
; The player has an elixir. Fill up their health instead
; of killing the player.
;
[c0b2]4c ca c4JMP Player_UseElixir; Use the Elixir.
;
; Mark the player as dead.
;
[c0b5]@_killPlayer:
[c0b5]a9 01LDA #$01; Mark the player as dead.
[c0b7]8d 38 04STA a:PlayerIsDead; Store that.
;
; Draw the player's health.
;
[c0ba]@_isStillAlive:
[c0ba]4c bd c0JMP UI_DrawPlayerHP; Draw player health.
[c0bd]UI_DrawPlayerHP:
[c0bd]ad 31 04LDA a:Player_HP_U; Load the player's current health.
[c0c0]4c 75 faJMP UI_DrawPlayerHPValue; Draw it.
;============================================================================
; Decrement the MP for a spell.
;
; This will look at the selected magic type and deduct the
; cost from the player's total MP if there's enough to cast.
;
; INPUTS:
;
Player_MP:
; The player's current MP.
;
;
SelectedMagic:
; The selected magic.
;
;
MAGIC_COSTS:
; The table of magic costs per spell.
;
; OUTPUTS:
; C:
; 0 = Cost deducted for the spell.
; 1 = Not enough MP for the spell.
;
;
Player_MP:
; The new MP amount.
;
; CALLS:
;
Player_SetMP
;
; XREFS:
;
Player_CastMagic
;============================================================================
[c0c3]Player_ReduceMP:
[c0c3]ae c0 03LDX a:SelectedMagic; Load the selected magic spell.
[c0c6]ad 9a 03LDA a:Player_MP; Load the player's total MP.
[c0c9]38SEC
[c0ca]fd a9 b7SBC a:MAGIC_COSTS,X; Look up the cost of this spell.
[c0cd]90 08BCC @_notEnoughMP; If there's not enough MP for the spell, then jump.
;
; The player has enough MP for the spell.
;
[c0cf]8d 9a 03STA a:Player_MP; Else, store the reduced MP.
[c0d2]20 85 faJSR Player_SetMP; Set that on the player and draw.
[c0d5]18CLC; Clear the carry flag to indicate a success.
[c0d6]60RTS
;
; The player does not have enough mana.
;
[c0d7]@_notEnoughMP:
[c0d7]38SEC; Set the carry flag to 1 to indicate not enough MP.
[c0d8]60RTS
;============================================================================
; Add points to the player's MP.
;
; This will be capped to 80 points.
;
; INPUTS:
; A:
; The MP to add.
;
;
Player_MP:
; The player's current MP.
;
; OUTPUTS:
;
Player_MP:
; The new MP.
;
; CALLS:
;
Player_SetMP
;
; XREFS:
;
Player_FillHPAndMP
;============================================================================
[c0d9]Player_AddMP:
;
; Add the provided MP to the player's MP.
;
[c0d9]18CLC; Clear carry so it's not added.
[c0da]6d 9a 03ADC a:Player_MP; Add to the player's MP.
[c0dd]8d 9a 03STA a:Player_MP; Store it.
[c0e0]c9 50CMP #$50; Check if it's > 80 points.
[c0e2]90 05BCC @_drawMP; If we're under the cap, return.
;
; Cap the MP to the total allowed amount.
;
[c0e4]a9 50LDA #$50; Cap to 80 points.
;
; Store it.
;
[c0e6]8d 9a 03STA a:Player_MP
[c0e9]@_drawMP:
[c0e9]4c 85 faJMP Player_SetMP; Set the player's mana points and draw.
;============================================================================
; NOTE: This is used exclusively by the Unknown 29 sprite behavior.
;
; XREFS:
;
SpriteBehavior_Unknown_29_SomeSetup
;============================================================================
[c0ec]Player_Something_ChangeHP:
[c0ec]8aTXA
[c0ed]48PHA
[c0ee]a9 00LDA #$00
[c0f0]8d c0 04STA a:Temp1_SomethingChangedHP
[c0f3]8d c1 04STA a:Temp2_SomethingChangedHP
[c0f6]a2 10LDX #$10
[c0f8]2e bc 04ROL a:Arg_PlayerHealthDelta_L
[c0fb]2e bd 04ROL a:Arg_PlayerHealthDelta_U
[c0fe]@LAB_PRG15_MIRROR__c0fe:
[c0fe]2e c0 04ROL a:Temp1_SomethingChangedHP
[c101]2e c1 04ROL a:Temp2_SomethingChangedHP
[c104]ad c0 04LDA a:Temp1_SomethingChangedHP
[c107]cd be 04CMP a:SpriteBehaviorUnknown20_SomethingXOrY
[c10a]ad c1 04LDA a:Temp2_SomethingChangedHP
[c10d]ed bf 04SBC a:BYTE_04bf
[c110]90 12BCC @LAB_PRG15_MIRROR__c124
[c112]ad c0 04LDA a:Temp1_SomethingChangedHP
[c115]ed be 04SBC a:SpriteBehaviorUnknown20_SomethingXOrY
[c118]8d c0 04STA a:Temp1_SomethingChangedHP
[c11b]ad c1 04LDA a:Temp2_SomethingChangedHP
[c11e]ed bf 04SBC a:BYTE_04bf
[c121]8d c1 04STA a:Temp2_SomethingChangedHP
[c124]@LAB_PRG15_MIRROR__c124:
[c124]2e bc 04ROL a:Arg_PlayerHealthDelta_L
[c127]2e bd 04ROL a:Arg_PlayerHealthDelta_U
[c12a]caDEX
[c12b]d0 d1BNE @LAB_PRG15_MIRROR__c0fe
[c12d]68PLA
[c12e]aaTAX
[c12f]60RTS
[c130]Screen_ClearSprites:
[c130]a2 07LDX #$07; X = 7 (loop counter -- looping 8 times)
[c132]@_clearSpritesLoop:
[c132]a9 ffLDA #$ff
[c134]9d cc 02STA a:CurrentSprites_Entities,X; Set sprite to 0xFF (cleared).
[c137]a9 00LDA #$00
[c139]9d 44 03STA a:CurrentSprites_HP,X; Set sprite hit points to 0.
[c13c]9d 4c 03STA a:CurrentSprites_HitCounter,X; Set sprite hit counter to 0.
[c13f]9d d4 02STA a:CurrentSprites_Behaviors,X; Set sprite subtype to 0.
[c142]caDEX; X--
[c143]10 edBPL @_clearSpritesLoop; If we're not done, loop.
;
; We're out of the loop. Reset the PPU draw offset and restore
; screen state.
;
[c145]20 6f cdJSR CurrentSprite_ResetPPUTileOffset; Reset the PPU offset.
[c148]a9 ffLDA #$ff
[c14a]8d c7 03STA a:IScript_PortraitID; Clear any portrait shown on screen.
[c14d]a5 0bLDA PPU_Mask; Load the current screen color mode.
[c14f]29 feAND #$fe; Set to color mode (clear bit 1)
[c151]85 0bSTA PPU_Mask; Store the new color mode.
[c153]60RTS
[c154]Screen_LoadAllScreenInfo:
;
; Set the address of the sprites-for-screen lookup table
; for the area.
;
[c154]a5 24LDA Area_CurrentArea; A = Current area index.
[c156]0aASL A; Convert to a word offset for the lookup table.
;
; Set the starting address of the screen data.
;
[c157]a8TAY; Y = A
[c158]b9 10 82LDA a:AREA_SPRITE_ADDRESSES,Y; Load the upper byte of the address.
[c15b]85 02STA Temp_Addr_L; Store for reading.
[c15d]b9 11 82LDA a:AREA_SPRITE_ADDRESSES+1,Y; Load the lower byte of the address.
[c160]85 03STA Temp_Addr_U; Store it.
;
; Check the first byte of the address. If 0xFF,
; nothing will be loaded.
;
[c162]a0 01LDY #$01; Y = 1
[c164]b1 02LDA (Temp_Addr_L),Y; Load the address for the screens list.
[c166]c9 ffCMP #$ff; Is it 0xFF?
[c168]f0 49BEQ RETURN_C1B3; If so, there's nothing to load. Return.
;
; Set the start address for a screen's sprite and metadata
; information.
;
[c16a]a5 63LDA Area_CurrentScreen; A = Current screen index.
[c16c]0aASL A; Convert to a word offset for the lookup table.
[c16d]a8TAY; Y = A
;
; Read the address for the screen information.
;
[c16e]b1 02LDA (Temp_Addr_L),Y; Load the lower byte of the screen address.
[c170]85 caSTA Sprites_ReadInfoAddr; Store it as the lower byte of the of the sprites read address.
[c172]c8INY; Y++
[c173]b1 02LDA (Temp_Addr_L),Y; Load the upper byte.
[c175]85 cbSTA Sprites_ReadInfoAddr_U; And store it.
;
; Find the end of the sprites list for the screen.
;
; Sprites are in 2-byte pairs of (Entity ID, Sprite Value).
; This will skip those bytes until the end of the list is
; hit.
;
[c177]a0 00LDY #$00; Y = 0
[c179]@_readLoop:
[c179]b1 caLDA (Sprites_ReadInfoAddr),Y; Load the next byte from the screen data.
[c17b]c9 ffCMP #$ff; Is it 0xFF?
[c17d]d0 10BNE @_prepareNextLoopIter; If not, jump to prepare for the next loop
;
; The sprite information is 0xFF, so we're done looping.
; Read the screen information.
;
[c17f]c8INY; Y++
[c180]98TYA; A = Y
[c181]18CLC
[c182]65 caADC Sprites_ReadInfoAddr; Increment the read address by 2 bytes.
[c184]85 ccSTA Screen_ExtraInfoAddr
[c186]a5 cbLDA Sprites_ReadInfoAddr_U; Load the upper byte.
[c188]69 00ADC #$00; Add carry, if lower byte overflowed.
[c18a]85 cdSTA Screen_ExtraInfoAddr_U; Store as the new upper byte.
[c18c]4c 99 c1JMP Screen_LoadSpecialEventID; Read the extra screen information.
;
; Increment by 2 (the sprite entity and value).
;
[c18f]@_prepareNextLoopIter:
[c18f]c8INY; Y++
[c190]c8INY; Y++
[c191]d0 e6BNE @_readLoop; If Y > 0, loop.
;
; v-- Fall through --v
;
;============================================================================
; Clear the special event ID for the current screen.
;
; This disables any custom logic specific to certain screens.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
CurrentScreen_SpecialEventID:
; Set to 0xFF (unset).
;
; XREFS:
;
Screen_LoadSpecialEventID
;============================================================================
[c193]Screen_SetNoSpecialEventID:
;
; No special event information was found for the screen.
; Clear that state (set to 0xFF).
;
[c193]a9 ffLDA #$ff
[c195]8d 2e 04STA a:CurrentScreen_SpecialEventID; Clear the special event ID (0xFF).
[c198]60RTS
;============================================================================
; Load extra information about the current screen.
;
; Screens may contain a special event ID, which adds custom
; logic to perform on each tick on a screen. This is used
; for three things:
;
; 1. Managing the pushable block status on the path to
; Mascon.
; 2. Boss battle logic.
; 3. Final boss logic.
;
; INPUTS:
;
Screen_ExtraInfoAddr:
;
Screen_ExtraInfoAddr+1:
; The address containing the extra screen
; information.
;
; OUTPUTS:
;
CurrentScreen_SpecialEventID:
; The special event ID loaded from the screen info,
; or 0xFF (unset) if not found.
;
; XREFS:
;
Screen_LoadAllScreenInfo
;============================================================================
[c199]Screen_LoadSpecialEventID:
;
; Begin scanning for a 0xFF in the screen information. This
; will indicate the start of extra information for the screen
; (special screen event states, IScripts entrypoint references).
;
[c199]a0 00LDY #$00; Y = 0 (loop counter)
[c19b]@_scanLoop:
[c19b]b1 ccLDA (Screen_ExtraInfoAddr),Y; Load the next byte in the screen info data.
[c19d]c9 ffCMP #$ff; Is it 0xFF?
[c19f]d0 0eBNE @_noFFMarker; If so, we found the start of the extra data to load. Jump.
;
; We found the start of the extra info for the screen. We're
; looking for 0x80, which indicates special event code will
; run on this screen every tick. The type of event will be
; indicated in the following byte.
;
; An example of this would be running code when a boss is
; defeated, producing an item.
;
[c1a1]c8INY; Y++
[c1a2]b1 ccLDA (Screen_ExtraInfoAddr),Y; Load the next byte to see what we're working with.
[c1a4]c9 80CMP #$80; Is it 0x80?
[c1a6]d0 ebBNE Screen_SetNoSpecialEventID; If not, then we won't be running special event code on this screen.
;
; This screen runs special event code. Load it.
;
[c1a8]c8INY; Y++
[c1a9]b1 ccLDA (Screen_ExtraInfoAddr),Y; Load the next byte containing the special event ID.
[c1ab]8d 2e 04STA a:CurrentScreen_SpecialEventID; Store it while on the screen.
[c1ae]60RTS
[c1af]@_noFFMarker:
[c1af]c8INY; Y++
;
; If we hit 256 loops, we're done.
;
[c1b0]d0 e9BNE @_scanLoop; If we haven't wrapped, then loop.
[c1b2]60RTS; Else, we're done.
[c1b3]RETURN_C1B3:
[c1b3]60RTS
[c1b4]Screen_LoadSpriteInfo:
;
; Switch to ROM bank 11, where sprite information lives.
;
[c1b4]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[c1b7]48PHA; Push it to the stack.
[c1b8]a2 0bLDX #$0b
[c1ba]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Load all the sprites for this screen.
;
[c1bd]20 54 c1JSR Screen_LoadAllScreenInfo; Load the sprite information for the screen.
;
; Switch bank to our previous bank.
;
[c1c0]68PLA; Pop A (our saved bank).
[c1c1]aaTAX; X = A
[c1c2]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
;
; Begin our loop for reading all sprites for the screen.
;
[c1c5]@_readSpriteLoop:
[c1c5]a0 00LDY #$00; Y = 0 (info read offset)
;
; Switch back to ROM bank 11 (sprite info).
;
[c1c7]ad 00 01LDA a:CurrentROMBank; A = current ROM bank
[c1ca]48PHA; Push to the stack.
[c1cb]a2 0bLDX #$0b
[c1cd]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Read information on the next sprite on the screen.
;
[c1d0]b1 caLDA (Sprites_ReadInfoAddr),Y; Read the current byte of the screen information.
[c1d2]c9 ffCMP #$ff; Is it 0xFF (our end-of-sprites list)?
[c1d4]f0 2cBEQ @_restoreBankAndReturn; If it is, we're done. Restore our bank and return.
;
; Store the sprite's entity ID.
;
[c1d6]8d 8b 03STA a:CurrentSprite_Entity; Store this byte as the sprite entity ID.
;
; Load the sprite's Y block position.
;
; This will be the upper nibble of the byte following the
; sprite entity ID.
;
[c1d9]c8INY; Y++
[c1da]b1 caLDA (Sprites_ReadInfoAddr),Y; Load the next byte.
[c1dc]29 f0AND #$f0; Take only the upper nibble.
[c1de]8d 8a 03STA a:CurrentSprite_YPos; Store it as the sprite's starting Y position.
;
; Load the sprite's X block position.
;
; This will be the lower nibble, which will then be placed
; in the upper nibble.
;
[c1e1]b1 caLDA (Sprites_ReadInfoAddr),Y; Load the same byte.
[c1e3]0aASL A; Shift the X coordinate from the lower nibble to the upper nibble.
[c1e4]0aASL A
[c1e5]0aASL A
[c1e6]0aASL A
[c1e7]8d 89 03STA a:CurrentSprite_XPos; Store as the sprite's X position.
;
; Restore our bank.
;
[c1ea]68PLA; Pull A (the saved bank) from the stack.
[c1eb]aaTAX; X = A
[c1ec]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
;
; Finish processing this sprite. We'll add to the next
; available slot and fill in any information needed based
; on what we loaded.
;
[c1ef]20 05 c2JSR Sprites_PopulateNextAvailableSprite; Add this sprite to the next available slot.
;
; Increment the read address for the next sprite in the
; screen data.
;
[c1f2]a5 caLDA Sprites_ReadInfoAddr; A = lower byte of the screen information address
[c1f4]18CLC
[c1f5]69 02ADC #$02; Increment by 2 (sprite entity ID and position).
[c1f7]85 caSTA Sprites_ReadInfoAddr; Store as the new lower byte of the address.
[c1f9]a5 cbLDA Sprites_ReadInfoAddr_U; A = upper byte of the screen information address
[c1fb]69 00ADC #$00; Add carry if the lower byte overflowed.
[c1fd]85 cbSTA Sprites_ReadInfoAddr_U; Store it as the new upper byte of the address.
[c1ff]4c c5 c1JMP @_readSpriteLoop; Loop to read the next sprite.
[c202]@_restoreBankAndReturn:
[c202]4c d6 f3JMP MMC1_UpdatePRGBankToStackA
[c205]Sprites_PopulateNextAvailableSprite:
;
; Prepare the sprite loop.
;
[c205]a2 07LDX #$07; 7 = loop counter
[c207]@_loop:
[c207]8e 78 03STX a:CurrentSpriteIndex; Set it
[c20a]bd cc 02LDA a:CurrentSprites_Entities,X; Load the sprite entity at this index.
[c20d]c9 ffCMP #$ff; Is it 0xFF (unset)?
[c20f]d0 41BNE @_prepareNextIter; If not, jump to prepare for next loop.
;
; This sprite index is unset. Populate it.
;
[c211]a9 00LDA #$00; 0 = unset
[c213]9d e4 02STA a:CurrentSprites_Phases,X; Clear the sprite phase.
[c216]9d dc 02STA a:CurrentSprites_Flags,X; Clear the sprite flags.
[c219]a9 ffLDA #$ff; 0xFF = unset
[c21b]9d 34 03STA a:CurrentSprites_HitByMagicBehavior,X; Clear the hit by magic behavior.
;
; Set the sprite's starting X/Y position.
;
[c21e]ad 89 03LDA a:CurrentSprite_XPos; Load the current sprite X position to set.
[c221]95 baSTA CurrentSprites_XPos_Full,X; Set for the sprite index.
[c223]ad 8a 03LDA a:CurrentSprite_YPos; Load the Y position.
[c226]95 c2STA CurrentSprites_YPos,X; Set for the sprite index.
[c228]ad 8b 03LDA a:CurrentSprite_Entity; Load the sprite entity.
[c22b]9d cc 02STA a:CurrentSprites_Entities,X; Set it.
[c22e]a8TAY; Y = A (entity)
[c22f]b9 df b4LDA a:SPRITE_ENTITIES_HITBOX_TYPES,Y; Load the hitbox type for the entity.
[c232]9d 04 03STA a:CurrentSprites_HitBoxTypes,X; Set it.
[c235]b9 a9 b5LDA a:SPRITE_ENTITIES_HP,Y; Load the HP for the entity.
[c238]9d 44 03STA a:CurrentSprites_HP,X; Set it.
;
; Prepare the behavior scripts for the entity.
;
[c23b]98TYA; A = Y (entity)
[c23c]0aASL A; Convert to a word boundary.
[c23d]a8TAY; Y = A
[c23e]b9 2d adLDA a:SPRITE_BSCRIPTS,Y; Load the lower byte of the behavior script for the entity.
[c241]9d 54 03STA a:CurrentSprites_BehaviorAddrs_L,X; Set the lower byte of the address.
[c244]b9 2e adLDA a:SPRITE_BSCRIPTS+1,Y; Load the upper byte.
[c247]9d 5c 03STA a:CurrentSprites_BehaviorAddrs_U,X; Set the upper byte.
[c24a]a9 ffLDA #$ff; A = 0xFF (unset)
[c24c]9d d4 02STA a:CurrentSprites_Behaviors,X; Clear the current behavior state.
[c24f]4c 5b c2JMP Sprites_LoadSpriteValue; Load the value for the sprite and return.
[c252]@_prepareNextIter:
[c252]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index.
[c255]caDEX; X--
[c256]10 afBPL @_loop; If >= 0, loop.
[c258]60RTS
;============================================================================
; Banks storing the images for a range of sprites.
;
; XREFS:
;
Sprites_StoreBankForCurrentSprite
;============================================================================
[c259]SPRITE_IMAGE_BANKS:
[c259]06.byte $06; [0]: Bank for sprites 0-54
[c25a]SPRITE_IMAGE_BANKS_1_:
[c25a]07.byte $07; [1]: Bank for sprites 55-100
[c25b]Sprites_LoadSpriteValue:
[c25b]8aTXA; A = X (sprite index)
[c25c]a8TAY; Y = A
;
; Save the current ROM bank and switch to bank 11.
;
[c25d]ad 00 01LDA a:CurrentROMBank; Get the current ROM bank.
[c260]48PHA; Push to the stack.
[c261]a2 0bLDX #$0b; X = 11 (bank)
[c263]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Restore our sprite index to X so it's not clobbered
; when this returns.
;
[c266]98TYA; A = Y
[c267]aaTAX; X = A
;
; Load the extra information for this sprite and store it
; in the sprite list.
;
[c268]a0 00LDY #$00; Y = 0
[c26a]b1 ccLDA (Screen_ExtraInfoAddr),Y; A = Extra information for the sprite.
[c26c]9d 6c 03STA a:CurrentSprites_Values,X; Store in CurrentSprites_Values.
[c26f]c9 ffCMP #$ff; Is it 0xFF (unset)?
[c271]f0 06BEQ @_restoreBankAndReturn; If so, we're done. Jump.
;
; Increment the position in the info data.
;
[c273]e6 ccINC Screen_ExtraInfoAddr; Increment the lower byte of the offset in the extra info data.
[c275]d0 02BNE @_restoreBankAndReturn; If this didn't wrap to 0, we're done. Jump.
[c277]e6 cdINC Screen_ExtraInfoAddr_U; Else, increment the upper byte as well.
[c279]@_restoreBankAndReturn:
[c279]4c d6 f3JMP MMC1_UpdatePRGBankToStackA; Restore the bank saved to the stack.
;============================================================================
; Determine and store the bank for images for the current sprite.
;
; This will determine which bank would store images for the
; currently-processed sprite.
;
; If the sprite entity ID is 0-54, this will be bank 6.
;
; If the sprite entity ID is 55-100, this will be bank 7.
;
; INPUTS:
;
CurrentSprite_Entity:
; The entity ID for the currently-processed sprite.
;
;
SPRITE_IMAGE_BANKS:
; The lookup table for sprite entity IDs to banks.
;
; OUTPUTS:
;
CurrentSprite_TilesBank:
; The bank for the images for this sprite.
;
; XREFS:
;
GameLoop_LoadSpriteImages
;============================================================================
[c27c]Sprites_StoreBankForCurrentSprite:
[c27c]a0 00LDY #$00; Y = 0
[c27e]ad 8b 03LDA a:CurrentSprite_Entity; A = current sprite entity
[c281]c9 37CMP #$37; Is it < sprite 55 (Unused Child sprite)?
[c283]90 01BCC @_loadBank; If yes, then load bank from table at index 0.
[c285]c8INY; Else, load bank from table at index 1.
;
; Load the image for the bank.
;
[c286]@_loadBank:
[c286]b9 59 c2LDA a:SPRITE_IMAGE_BANKS,Y; Load the address for the sprite entity.
[c289]8d 86 03STA a:CurrentSprite_TilesBank; Store as the bank for this sprite.
[c28c]@_return:
[c28c]60RTS
[c28d]GameLoop_LoadSpriteImages:
;
; Save state and prepare for image loading.
;
[c28d]ad 78 03LDA a:CurrentSpriteIndex; A = current sprite index
[c290]48PHA; Push it to the stack.
[c291]20 6f cdJSR CurrentSprite_ResetPPUTileOffset; Reset the PPU drawing coordinates.
;
; Begin our loop for all 7 sprite slots.
;
[c294]a2 07LDX #$07; X = 7 (sprite index counter)
;
; Set this as the currently-processed sprite, and update
; the entity.
;
; We'll only process if the entity is not unset.
;
[c296]@_loop:
[c296]8e 78 03STX a:CurrentSpriteIndex; Store it as the currently-processed sprite index.
[c299]bd cc 02LDA a:CurrentSprites_Entities,X; Load the associated sprite entity ID.
[c29c]c9 ffCMP #$ff; Is it 0xFF (unset)?
[c29e]f0 3cBEQ @_nextLoop; If so, jump and prepare for the next loop.
[c2a0]8d 8b 03STA a:CurrentSprite_Entity; Store this as the entity at this index.
;
; Load the image for this sprite.
;
[c2a3]20 7c c2JSR Sprites_StoreBankForCurrentSprite; Store the image bank for the current sprite.
[c2a6]20 b5 cdJSR Sprites_LoadImageForCurrentSprite; Load the sprite's image from the bank.
;
; Special-case the Pakukame boss. If that was placed, then
; replace it with a Lilith instead. They spawn Liliths.
;
[c2a9]ad 8b 03LDA a:CurrentSprite_Entity; Load the current entity.
[c2ac]c9 30CMP #$30; Is it the Pakukame boss?
[c2ae]d0 19BNE @_loadSpriteInfo; If not, jump.
;
; This is the Pakukame. Swap in a Lilith and load the image.
;
; This will then restore the entity back to Pakukame.
; Effectively, each Lilith on screen seems to be backed by
; a Pakukame.
;
[c2b0]ad 8b 03LDA a:CurrentSprite_Entity; Load the Pakukame entity ID
[c2b3]48PHA; Push to the stack.
[c2b4]a5 9aLDA CurrentSprite_StartPPUTileOffset; Load the sprite's starting PPU tile offset.
[c2b6]48PHA; Push it to the stack.
[c2b7]a9 09LDA #$09; A = 9 (Lilith)
[c2b9]8d 8b 03STA a:CurrentSprite_Entity; Store as the new entity.
[c2bc]20 7c c2JSR Sprites_StoreBankForCurrentSprite; Store the bank for Lilith.
[c2bf]20 b5 cdJSR Sprites_LoadImageForCurrentSprite; Load Lilth's image from the bank.
[c2c2]68PLA; Pop the PPU offset row.
[c2c3]85 9aSTA CurrentSprite_StartPPUTileOffset; Store it again.
[c2c5]68PLA; Pop the current sprite entity.
[c2c6]8d 8b 03STA a:CurrentSprite_Entity; Store it again.
;
; Store the starting PPU tile offset for the sprite.
;
; This will be stored in this sprite's slot in the current
; sprites information.
;
[c2c9]@_loadSpriteInfo:
[c2c9]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index
[c2cc]a5 9aLDA CurrentSprite_StartPPUTileOffset; A = Start PPU tile offset
[c2ce]9d 2c 03STA a:CurrentSprites_PPUOffsets,X; Set this in the PPU tile offsets for this sprite.
;
; Check if the sprite is on-screen or off-screen.
;
; If the Y position is off the screen (row 30 or higher),
; then the sprite will be unset.
;
[c2d1]b5 c2LDA CurrentSprites_YPos,X; Load the Y position for this sprite.
[c2d3]c9 f0CMP #$f0; Is it less than 0xF0?
[c2d5]90 05BCC @_nextLoop; If so, loop.
;
; It's off-screen. Unset it.
;
[c2d7]a9 ffLDA #$ff; Else, it's off the screen.
[c2d9]9d cc 02STA a:CurrentSprites_Entities,X; Unset the entity.
;
; Done with any loading required for the sprite. Prepare for
; the next loop, if needed.
;
[c2dc]@_nextLoop:
[c2dc]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index (loop counter).
[c2df]caDEX; X--
[c2e0]10 b4BPL @_loop; If X >= 0, loop.
;
; We're done. Restore the original current sprite index and
; wait until everything's drawn to the screen.
;
[c2e2]68PLA; Pull A (current sprite index) from the stack.
[c2e3]8d 78 03STA a:CurrentSpriteIndex; Store that back as the current sprite index.
[c2e6]4c f4 cfJMP PPUBuffer_WaitUntilClear; Wait until everything's drawn to the screen.
[c2e9]CastMagic_RunUpdateSpellHandler:
;
; These two lines are deadcode. It might be the developers
; attempting to hard-code Deluge and Thunder during testing?
; Each gets overriden immediately.
;
[c2e9]a9 00LDA #$00; A = 0 (deadcode)
[c2eb]a9 01LDA #$01; A = 1 (deadcode)
[c2ed]ad b3 02LDA a:CastMagic_Type; A = Cast magic type
;
; If 0xFF, return.
;
[c2f0]30 22BMI @_return
;
; This was an active, cast magic spell. Set state.
;
[c2f2]20 15 c3JSR CastMagic_CalculateVisibility; Calculate visibility for the magic spell based on foreground elements.
;
; Manage the X position of the spell.
;
[c2f5]ad b6 02LDA a:CastMagic_XPos_Full; Load the magic's X position.
[c2f8]85 27STA Arg_DrawSprite_PosX; Set as an argument for future appearance updates.
;
; Set some unused state not used in the finished game.
;
[c2fa]a5 9fLDA Screen_Maybe_ScrollXCounter
[c2fc]85 2bSTA Unused_Sprite_ScrollPosX
;
; Manage the Y position of the spell.
;
[c2fe]ad b8 02LDA a:CastMagic_YPos_Full
[c301]85 28STA Arg_DrawSprite_PosY; Set as an argument for future appearance updates.
;
; Set more unused state not used in the finished game.
;
[c303]a5 a2LDA Player_Something_ScrollPosY
[c305]85 2aSTA Unused_Sprite_ScrollPosY
;
; Set the finish handler for this type as the next function
; to run.
;
[c307]ad b3 02LDA a:CastMagic_Type; Load the magic type.
[c30a]0aASL A; Convert to a word boundary for the lookup table.
[c30b]a8TAY; Y = A
[c30c]b9 28 bbLDA a:CAST_MAGIC_UPDATE_FINISH_HANDLERS+1,Y; Load the lower byte of the finish handler.
[c30f]48PHA; Push to the stack.
[c310]b9 27 bbLDA a:CAST_MAGIC_UPDATE_FINISH_HANDLERS,Y; Load the upper byte.
[c313]48PHA; Push.
[c314]@_return:
[c314]60RTS; Return and then call the function.
[c315]CastMagic_CalculateVisibility:
;
; Set the default to visible.
;
[c315]a9 00LDA #$00
[c317]85 b8STA Temp_MovingSpriteVisibility; Set the default visibility to visible.
;
; Convert the cast magic's position to block positions.
;
[c319]ad b8 02LDA a:CastMagic_YPos_Full; Load the magic's Y position.
[c31c]85 b6STA Arg_PixelPosY; Set it as an argument for getting the block property.
[c31e]ad b6 02LDA a:CastMagic_XPos_Full; Load the magic's X position.
[c321]18CLC
[c322]69 04ADC #$04; Add 4 to that to better check block alignment.
[c324]85 b5STA Arg_PixelPosX; Set it as an argument.
[c326]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[c329]20 c3 e8JSR ScreenBuffer_LoadBlockProperty; Get the block property for this position.
;
; Check if a block overlaps the left.
;
[c32c]c9 04CMP #$04; Is this block type 4?
[c32e]f0 08BEQ @_leftIsObscured; If so, the left is obscured.
[c330]c9 0dCMP #$0d; Else, is this block type 13?
[c332]f0 04BEQ @_leftIsObscured; If so, the left is obscured.
[c334]c9 09CMP #$09; Else, is this block type 9?
[c336]d0 06BNE @_checkRightObscured; If not, the left is not obscured. Start checking the right.
;
; This is a foreground layer block. The trailing part
; is obscured.
;
[c338]@_leftIsObscured:
[c338]a5 b8LDA Temp_MovingSpriteVisibility; Load the cast magic's visibility state.
[c33a]09 01ORA #$01; Mark the left-hand side as obscured.
[c33c]85 b8STA Temp_MovingSpriteVisibility; Store it.
;
; Now check the right-hand side of the magic.
;
[c33e]@_checkRightObscured:
[c33e]a5 b5LDA Arg_PixelPosX; Load the pixel X position.
[c340]18CLC
[c341]69 08ADC #$08; Add 8 to it, to better check the right side.
[c343]85 b5STA Arg_PixelPosX; Store as the new pixel argument.
[c345]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[c348]20 c3 e8JSR ScreenBuffer_LoadBlockProperty; Load the block property.
;
; Check if a block overlaps the right.
;
[c34b]c9 04CMP #$04; Is this block 4?
[c34d]d0 12BNE @_checkNeedToFlipBits; If not, it's not obscured.
[c34f]c9 04CMP #$04; Again, is this block 4?
[c351]f0 08BEQ @_rightIsObscured; If so, the right is obscured.
[c353]c9 0dCMP #$0d; Else, is this block 13?
[c355]f0 04BEQ @_rightIsObscured; If so, the right is obscured.
[c357]c9 09CMP #$09; Else, is this block 9?
[c359]d0 06BNE @_checkNeedToFlipBits; If not, it's not obscured. Move to the next step.
;
; The trailing side of the magic is obscured.
;
[c35b]@_rightIsObscured:
[c35b]a5 b8LDA Temp_MovingSpriteVisibility; Load the cast magic's visibility state.
[c35d]09 02ORA #$02; Mark the right-hand side as obscured.
[c35f]85 b8STA Temp_MovingSpriteVisibility; Store it.
;
; Check if the magic is partially-obscured (just left or
; right). If so, make sure the flips are flipped for the
; facing direction.
;
[c361]@_checkNeedToFlipBits:
[c361]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index.
[c364]ad b4 02LDA a:CastMagic_Flags; A = cast magic flags.
[c367]29 40AND #$40; Is it facing right?
[c369]f0 0dBEQ @_facingRight; If so, there's nothing to do. We're done.
[c36b]a5 b8LDA Temp_MovingSpriteVisibility; A = cast magic visibility
[c36d]f0 0bBEQ @_storeVisibility; If the block is fully visible, jump.
[c36f]c9 03CMP #$03; Else, is it full obscured?
[c371]f0 07BEQ @_storeVisibility; If yes, jump.
;
; Swap the bits to face the other direction.
;
[c373]49 03EOR #$03; Swap the visibility of leading and trailing flags.
[c375]4c 7a c3JMP @_storeVisibility; Jump to store.
[c378]@_facingRight:
[c378]a5 b8LDA Temp_MovingSpriteVisibility; Load the previously-calculated visibility from above.
[c37a]@_storeVisibility:
[c37a]85 26STA MovingSpriteVisibility; Store the new visibility state.
[c37c]60RTS
[c37d]CastMagic_SetAppearance:
[c37d]ac b3 02LDY a:CastMagic_Type; Y = cast magic type
[c380]18CLC
[c381]79 87 c3ADC a:SPRITE_MAGIC_IMAGE_ADDRS_U,Y; Add the value from the lookup table.
[c384]4c 57 f0JMP Sprite_SetAppearanceAddrFromOffset; Set the sprite appearance.
;============================================================================
; Table of upper address bytes for magic sprite images.
;
; XREFS:
;
CastMagic_SetAppearance
;============================================================================
[c387]SPRITE_MAGIC_IMAGE_ADDRS_U:
[c387]95.byte $95; [0]: Deluge
[c388]99.byte $99; [1]: Thunder
[c389]9b.byte $9b; [2]: Fire
[c38a]9d.byte $9d; [3]: Death
[c38b]a1.byte $a1; [4]: Tilte
[c38c]a5.byte $a5; [5]: UNUSED: Deluge after first hit
[c38d]99.byte $99; [6]: Thunder after first hit
[c38e]9b.byte $9b; [7]: Fire after first hit
[c38f]a5.byte $a5; [8]: UNUSED: Death after first hit
[c390]a5.byte $a5; [9]: UNUSED
[c391]a5.byte $a5; [10]: UNUSED: Hit wall effect
[c392]a5.byte $a5; [11]: Tilte after first hit
[c393]CastMagic_UpdateSpriteDirection:
[c393]ad b4 02LDA a:CastMagic_Flags; Load the cast magic's flags.
[c396]29 40AND #$40; Take only the facing direction bit.
[c398]85 29STA CurrentSprite_FlipMask; Set as the sprite's flip mask.
[c39a]60RTS
[c39b]CastMagic_FinishHandler_Deluge:
[c39b]20 93 c3JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c39e]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3a0]4aLSR A; Shift the interrupt counter to generate an appearance index.
[c3a1]4aLSR A
[c3a2]29 03AND #$03; Keep the 2 right-most bits (effectively, bits 3 and 4 of the interrupt counter).
[c3a4]4c 7d c3JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3a7]CastMagic_FinishHandler_Thunder:
[c3a7]20 93 c3JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c3aa]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3ac]4aLSR A; Shift the interrupt counter to generate a value to check against.
[c3ad]4aLSR A
[c3ae]b0 05BCS @_return; If this should not update the appearance, return.
[c3b0]29 01AND #$01; Keep the right-most bit (effectively, bit 2 of the interrupt counter).
[c3b2]4c 7d c3JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3b5]@_return:
[c3b5]60RTS
[c3b6]CastMagic_FinishHandler_Fire:
[c3b6]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3b8]29 02AND #$02; Is bit 1 set?
[c3ba]d0 01BNE @_updateFire; If so, jump to update.
[c3bc]60RTS
[c3bd]@_updateFire:
[c3bd]20 93 c3JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c3c0]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3c2]4aLSR A; Shift the interrupt counter to generate an appearance index.
[c3c3]4aLSR A
[c3c4]29 01AND #$01; Keep the right-most bit (effectively, bit 2 of the interrupt counter).
[c3c6]4c 7d c3JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3c9]CastMagic_FinishHandler_Death:
[c3c9]20 93 c3JSR CastMagic_UpdateSpriteDirection; Update the sprite's direction.
[c3cc]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3ce]4aLSR A; Shift the interrupt counter to generate an appearance index.
[c3cf]4aLSR A
[c3d0]4aLSR A
[c3d1]29 03AND #$03; Keep the 2 right-most bits (effectively, bits 3 and 4 of the interrupt counter).
[c3d3]4c 7d c3JMP CastMagic_SetAppearance; Set that as the appearance index.
;============================================================================
; Finish updating the Tilte spell.
;
; This will set the appearance to 0, 1, 2, or 3,
; based on the current phase of the magic spell and
; the interrupt counter.
;
; The appearance will be:
;
; * 0: Magic phase is even;
; Interrupt counter has bit 3 set.
;
; * 1: Magic phase is even;
; Interrupt counter does not have bit 3 set.
;
; * 2: Magic phase is odd;
; Interrupt counter has bit 1 and 2 unset.
;
; * 3: Magic phase is odd;
; Interrupt counter has bit 1 unset and 2 set.
;
; INPUTS:
;
CastMagic_Phase:
; The phase of the magic spell.
;
;
InterruptCounter:
; The current interrupt counter.
;
; OUTPUTS:
; None.
;
; CALLS:
;
CastMagic_SetAppearance
;
; XREFS:
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb2f]
;============================================================================
[c3d6]CastMagic_FinishHandler_Tilte:
[c3d6]20 93 c3JSR CastMagic_UpdateSpriteDirection; Update the sprite's direction.
;
; Check if the first bit of the magic's phase is 0 or 1.
;
[c3d9]ad ba 02LDA a:CastMagic_Phase; Load the magic's phase.
[c3dc]4aLSR A; Shift bit 0 into Carry.
[c3dd]b0 0dBCS @_phaseIs1; If it's set, jump.
;
; The magic phase is 0. Update the appearance.
;
; The appearance value will be 1 if the interrupt counter
; does not have bit 3 set.
;
[c3df]a0 00LDY #$00; Y = 0
[c3e1]a5 1aLDA InterruptCounter; A = interrupt counter
[c3e3]29 08AND #$08; Keep bit 3.
[c3e5]f0 01BEQ @_setAppearance1; If 0, jump to use 0 as the appearance value.
[c3e7]c8INY; Y++ (appearance value == 1)
[c3e8]@_setAppearance1:
[c3e8]98TYA; A = 0 or 1 (appearance value)
[c3e9]4c 7d c3JMP CastMagic_SetAppearance; Set the appearance of the magic.
;
; The magic phase is 1. Update the appearance if the
; interrupt counter has bit 1 unset.
;
[c3ec]@_phaseIs1:
[c3ec]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c3ee]4aLSR A; Divide by 2.
[c3ef]4aLSR A; And place in Carry.
[c3f0]90 01BCC @_setAppearance2; If 0, jump to update appearance.
[c3f2]60RTS
;
; The appearance will be updated based on the interrupt
; counter. It will be 3 if bit 2 is unset, or 4 if set.
;
[c3f3]@_setAppearance2:
[c3f3]29 01AND #$01; Keep bit 0.
[c3f5]18CLC
[c3f6]69 02ADC #$02; Add 2.
[c3f8]4c 7d c3JMP CastMagic_SetAppearance; Set the appearance of the magic.
;============================================================================
; UNUSED: Finish handling updates to Deluge and Death spells that have
; collided at least once.
;
; This would maintain the sprite's direction and appearance.
;
; It's never actually run, due to both these spells clearing
; immediately when colliding.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; CALLS:
;
CastMagic_SetAppearance
;
CastMagic_UpdateSpriteDirection
;
; XREFS:
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb31]
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb37]
;============================================================================
[c3fb]CastMagic_FinishHandler_DelugeOrDeathAfterHit:
[c3fb]20 93 c3JSR CastMagic_UpdateSpriteDirection; Update the sprite direction.
[c3fe]a9 00LDA #$00
[c400]4c 7d c3JMP CastMagic_SetAppearance; Set the appearance to 0.
;============================================================================
; UNUSED: Add a Magic Hit Wall effect.
;
; This was meant to be used by the Deluge and Fire magic.
; Upon hitting a wall, it would briefly spawn two explosion
; sprites (using the diagonal Tilte sprite) up against the
; collided block, before disappearing.
;
; This may have been wired off because there appears to be
; a race condition in the Fire spell, where it may not
; always show.
;
; This can be reactivated using the following Game Genie
; codes:
;
; ZAVLOUNN
; ZEUUSUNN
;
; INPUTS:
;
CastMagic_Unused_HitWallDeltaPosY:
; The delta Y position for the hit wall sprite.
;
;
CastMagic_YPos_Full:
; The Y position of the magic sprite.
;
; OUTPUTS:
;
CurrentSprite_FlipMask:
; Set to 0.
;
;
Arg_DrawSprite_PosY:
;
Temp_00:
; Clobbered.
;
; CALLS:
;
CastMagic_SetAppearance
;
; XREFS:
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb3b]
;============================================================================
[c403]CastMagic_FinishHandler_HitWallEffect:
;
; Set the default flip mask of the sprite to 0 (face right).
;
[c403]a9 00LDA #$00
[c405]85 29STA CurrentSprite_FlipMask; Set flip mask to face right.
;
; Calculate a multiplier used to position the explosion
; sprites.
;
; This will be the delta Y position * 2.
;
[c407]ad bb 02LDA a:CastMagic_Unused_HitWallDeltaPosY; A = Delta Y position.
[c40a]0aASL A; A *= 2
[c40b]85 00STA Temp_00; Save it temporarily.
;
; Position and draw the top explosion sprite.
;
; This will be MagicY - (DeltaY * 2).
;
[c40d]ad b8 02LDA a:CastMagic_YPos_Full; A = Magic Y position.
[c410]38SEC
[c411]e5 00SBC Temp_00; A -= Effect delta Y position.
[c413]85 28STA Arg_DrawSprite_PosY; Set as the new sprite's Y position.
[c415]a9 02LDA #$02
[c417]20 7d c3JSR CastMagic_SetAppearance; Draw the top explosion sprite at that position.
;
; Position and draw the bottom explosion sprite.
;
; This will be MagicY + 16 + (DeltaY * 2).
;
[c41a]ad bb 02LDA a:CastMagic_Unused_HitWallDeltaPosY; A = Delta Y position.
[c41d]0aASL A; A *= 2
[c41e]18CLC
[c41f]69 10ADC #$10; A += 16
[c421]18CLC
[c422]6d b8 02ADC a:CastMagic_YPos_Full; A += Magic Y position.
[c425]85 28STA Arg_DrawSprite_PosY; Save it as the new sprite's Y position.
[c427]a9 03LDA #$03
[c429]4c 7d c3JMP CastMagic_SetAppearance; Draw the bottom explosion sprite at that position.
;============================================================================
; Draw the Tilte explosion sprites after hitting an enemy.
;
; There are 4 sprites for the explosion, each moving in a
; diagonal direction outward from the impact point.
;
; This is computed by looping through each of the sprites
; and then calculating new X and Y positions for each, plus
; the appropriate tile appearance offset. XOR masks are used
; to turn the magic counter into a positive or negative
; number (depending on which piece of the explosion this is)
; in order to move that part in the correct direction.
;
; INPUTS:
;
CastMagic_Counter:
; The lifetime counter for the explosion.
;
;
CastMagic_XPos_Full:
; The center X position for the explosion.
;
;
CastMagic_YPos_Full:
; The center Y position for the explosion.
;
;
MAGICHITHANDLER_TILTE_X_XORS:
; The XOR table for generating positive/negative X
; deltas for each part of the explosion.
;
;
MAGICHITHANDLER_TILTE_Y_XORS:
; The XOR table for generating positive/negative Y
; deltas for each part of the explosion.
;
;
MAGICHITHANDLER_TILTE_APPEARANCE_OFFSETS:
; The appearance offsets for each part of the
; explosion.
;
; OUTPUTS:
;
Arg_DrawSprite_PosX:
;
Arg_DrawSprite_PosY:
;
Temp_00:
; Clobbered.
;
; CALLS:
;
CastMagic_SetAppearance
;
; XREFS:
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb3d]
;============================================================================
[c42c]CastMagic_FinishHandler_TilteAfterFirstHit:
;
; Clear the flip mask for the sprite.
;
[c42c]a9 00LDA #$00; A = 0.
[c42e]85 29STA CurrentSprite_FlipMask; Set as the flip mask (cleared).
;
; Prepare our loop. This will loop 4 times (3 -> 0, inclusive),
; calculating X and Y positions and an appearance offset for the
; sprite based on
CastMagic_Counter (which will go from 1
; through 15).
;
; The loop counter corresponds to the corner of the explosion
; being processed:
;
; 0 = Top-left
; 1 = Top-right
; 2 = Bottom-left
; 3 = Bottom-right
;
[c430]a2 03LDX #$03; X = 3 (loop counter).
;
; Begin calculating a distance from the center based on the
; counter value.
;
; This will multiply the counter by 2 and then calculate a
; positive or negative value for X based on which corner
; of the explosion is being processed.
;
[c432]@_loop:
[c432]ad b9 02LDA a:CastMagic_Counter; Load the magic counter.
[c435]0aASL A; Multiply by 2.
[c436]48PHA; Push the result to the stack.
;
; Calculate the X position of the sprite.
;
[c437]85 00STA Temp_00; And store it for temporary usage.
[c439]5d 6c c4EOR a:MAGICHITHANDLER_TILTE_X_XORS,X; XOR with the index in our lookup table based on the loop counter.
[c43c]10 04BPL @_calcFinalXPosition; If the result is a positive value, jump.
[c43e]85 00STA Temp_00; Else, store our XOR'd result in our temp variable.
[c440]e6 00INC Temp_00; And increment it.
;
; Add our calculated position to the current X position,
; producing a final result. This will be set as the
; destination for drawing.
;
[c442]@_calcFinalXPosition:
[c442]ad b6 02LDA a:CastMagic_XPos_Full; Load the block X position of the magic sprite.
[c445]18CLC
[c446]65 00ADC Temp_00; Add our temp value.
[c448]85 27STA Arg_DrawSprite_PosX; Set as the X position argument.
;
; Calculate the Y position of the sprite.
;
[c44a]68PLA; Pop the previously-calculated magic counter * 2 result.
[c44b]85 00STA Temp_00; Store it back in our temp variable.
[c44d]5d 70 c4EOR a:MAGICHITHANDLER_TILTE_Y_XORS,X; XOR with the index in our lookup table based on the loop counter.
[c450]10 04BPL @_calcFinalYPosition; If the result is a positive value, jump.
[c452]85 00STA Temp_00; Else, store our XOR'd result in our temp variable.
[c454]e6 00INC Temp_00; And increment it.
[c456]@_calcFinalYPosition:
[c456]ad b8 02LDA a:CastMagic_YPos_Full; Load the Y position of the magic sprite.
[c459]18CLC
[c45a]65 00ADC Temp_00; Add the temp variable.
[c45c]85 28STA Arg_DrawSprite_PosY; Store as the Y position argument.
;
; Calculate the sprite tile offset.
;
[c45e]8aTXA; A = loop counter value.
[c45f]48PHA; Push it to the stack.
[c460]bd 74 c4LDA a:MAGICHITHANDLER_TILTE_APPEARANCE_OFFSETS,X; Load the offset based on the counter.
;
; Draw the tile for this part of the explosion.
;
[c463]20 7d c3JSR CastMagic_SetAppearance; Set the appearance of the sprite.
[c466]68PLA; Pop our loop counter.
[c467]aaTAX; Assign back to X.
[c468]caDEX; Decrement the loop counter.
[c469]10 c7BPL @_loop; If >= 0, loop.
[c46b]60RTS
;============================================================================
; Table of XORs for creating X deltas for parts of the Tilte explosion.
;
; XREFS:
;
CastMagic_FinishHandler_TilteAfterFirstHit
;============================================================================
[c46c]MAGICHITHANDLER_TILTE_X_XORS:
[c46c]ff.byte $ff; [0]: Top-left
[c46d]00.byte $00; [1]: Top-right
[c46e]ff.byte $ff; [2]: Bottom-left
[c46f]00.byte $00; [3]: Bottom-right
;============================================================================
; Table of XORs for creating X deltas for parts of the Tilte explosion.
;
; XREFS:
;
CastMagic_FinishHandler_TilteAfterFirstHit
;============================================================================
[c470]MAGICHITHANDLER_TILTE_Y_XORS:
[c470]ff.byte $ff; [0]: Top-left
[c471]ff.byte $ff; [1]: Top-right
[c472]00.byte $00; [2]: Bottom-left
[c473]00.byte $00; [3]: Bottom-right
;============================================================================
; Table of appearance offsets for parts of the Tilte explosion.
;
; XREFS:
;
CastMagic_FinishHandler_TilteAfterFirstHit
;============================================================================
[c474]MAGICHITHANDLER_TILTE_APPEARANCE_OFFSETS:
[c474]00.byte $00; [0]: Top-left
[c475]02.byte $02; [1]: Top-right
[c476]01.byte $01; [2]: Bottom-left
[c477]03.byte $03; [3]: Bottom-right
;============================================================================
; Check whether the user wants to use the selected item.
;
; This will check if the player has both initiated an action
; to use the item and is in a position to use one.
;
; If these conditions are met, the action handler for the
; item will be invoked.
;
; INPUTS:
;
Player_Flags:
; The player's current flags.
;
;
Joy1_ButtonMask:
; The currently-held buttons.
;
;
Joy1_ChangedButtonMask:
; The newly-pressed buttons.
;
;
SelectedItem:
; The selected item.
;
; OUTPUTS:
; None
;
; XREFS:
;
EndGame_MainLoop
;
Game_MainLoop
;============================================================================
[c478]GameLoop_CheckUseCurrentItem:
[c478]a5 a4LDA Player_Flags; Load the player's flags.
;
; Check if the player is in a state where they'd be allowed
; to use an item.
;
[c47a]29 05AND #$05
[c47c]d0 1eBNE GameLoop_UseItem_Return
;
; Check if the Down button has been pressed. If not, return.
;
[c47e]a5 16LDA Joy1_ButtonMask; Load the button mask.
[c480]29 04AND #$04; Check if the Down button is pressed.
[c482]f0 18BEQ GameLoop_UseItem_Return; If not, return.
;
; Check if the B button has been pressed. If not, return.
;
[c484]ad 19 00LDA a:Joy1_ChangedButtonMask; Load the changed button mask.
[c487]29 40AND #$40; Check if the B button is pressed.
[c489]f0 11BEQ GameLoop_UseItem_Return; If not, return.
;
; Down-B was pressed. The item can be used, if a valid one is
; selected. Figure out if there's a valid one first...
;
[c48b]ad c1 03LDA a:SelectedItem; Load the selected item.
[c48e]0aASL A; Multiply by 2 to get a word boundary for address lookup.
[c48f]a8TAY; Y = A
[c490]c0 22CPY #$22; Is it a valid item?
[c492]b0 08BCS GameLoop_UseItem_Return; If not, return.
;
; Look up this item in the jump table and run it.
;
[c494]b9 9e c4LDA a:USE_ITEM_JUMP_TABLE+1,Y; Load the lower byte of the item handler address.
[c497]48PHA; Push it.
[c498]b9 9d c4LDA a:USE_ITEM_JUMP_TABLE,Y; Load the upper byte.
[c49b]48PHA; Push it.
[c49c]GameLoop_UseItem_Return:
[c49c]60RTS
;============================================================================
; Handler functions for using specific items.
;
; XREFS:
;
GameLoop_CheckUseCurrentItem
;============================================================================
[c49d]USE_ITEM_JUMP_TABLE:
[c49d]9b c4pointer GameLoop_UseItem_Return-1; [0]: Ring of Elf
[c49f]9b c4pointer GameLoop_UseItem_Return-1; [1]: Ring of Ruby
[c4a1]9b c4pointer GameLoop_UseItem_Return-1; [2]: Ring of Dworf
[c4a3]9b c4pointer GameLoop_UseItem_Return-1; [3]: Demon's Ring
[c4a5]9b c4pointer GameLoop_UseItem_Return-1; [4]: "A" Key
[c4a7]9b c4pointer GameLoop_UseItem_Return-1; [5]: "K" Key
[c4a9]9b c4pointer GameLoop_UseItem_Return-1; [6]: "Q" Key
[c4ab]9b c4pointer GameLoop_UseItem_Return-1; [7]: "J" Key
[c4ad]9b c4pointer GameLoop_UseItem_Return-1; [8]: "Jo" Key
[c4af]15 c6pointer Player_UseMattock-1; [9]: Mattock
[c4b1]9b c4pointer GameLoop_UseItem_Return-1; [10]: Magical Rod
[c4b3]9b c4pointer GameLoop_UseItem_Return-1; [11]: Crystal
[c4b5]9b c4pointer GameLoop_UseItem_Return-1; [12]: Lamp
[c4b7]c7 c5pointer Player_UseHourGlass-1; [13]: Hour Glass
[c4b9]9b c4pointer GameLoop_UseItem_Return-1; [14]: Book
[c4bb]78 c5pointer Player_UseWingBoots-1; [15]: Wing Boots
[c4bd]32 c5pointer Player_UseRedPotion-1; [16]: Red Potion
[c4bf]Player_ClearSelectedItem:
[c4bf]48PHA; Push A to the stack.
[c4c0]a9 ffLDA #$ff
[c4c2]8d c1 03STA a:SelectedItem; Set selected item to 0xFF (unset).
[c4c5]20 dc c8JSR UI_ClearSelectedItemPic; Update the UI for the cleared image.
[c4c8]68PLA; Pop A from the stack.
[c4c9]60RTS
[c4ca]Player_UseElixir:
[c4ca]ad 2c 04LDA a:SpecialItems; Load the special items.
[c4cd]29 f7AND #$f7; Clear the elixir bit.
[c4cf]8d 2c 04STA a:SpecialItems; Store it.
;
; Run the "Use Elixir" IScript.
;
; IScript 0x85 via jump to
IScripts_Begin.
;
[c4d2]a9 85LDA #$85; 0x85 == Use Elixir.
[c4d4]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c4d7]0c.byte BANK_12_LOGIC; Bank = 12
[c4d8]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[c4da]Player_FillHPAndMP:
;
; Progressively fill the HP, 4 units at a time.
;
; Start by playing the sound.
;
[c4da]a9 13LDA #$13; Set sound 0x13 (fill HP/MP bar).
[c4dc]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the HP.
;
[c4df]a9 04LDA #$04; Set the HP to 4 units.
[c4e1]20 7b c0JSR Player_AddHP; Add it to the player.
[c4e4]20 46 dcJSR Game_DrawScreenInFrozenState; Draw the screen, and prevent updating until done.
;
; Wait 4 frames of updates before filling up the next units
; of HP, to help animate the bar.
;
[c4e7]20 25 caJSR WaitForNextFrame; Wait a bit.
[c4ea]20 a8 cbJSR Sprites_FlipRanges
[c4ed]20 25 caJSR WaitForNextFrame
[c4f0]20 a8 cbJSR Sprites_FlipRanges
[c4f3]20 25 caJSR WaitForNextFrame
[c4f6]20 a8 cbJSR Sprites_FlipRanges
[c4f9]20 25 caJSR WaitForNextFrame
[c4fc]20 a8 cbJSR Sprites_FlipRanges
;
; Check if we're done filling up HP.
;
[c4ff]ad 31 04LDA a:Player_HP_U; Load the player's HP.
[c502]c9 50CMP #$50; Is it below the cap of 80HP?
[c504]90 d4BCC Player_FillHPAndMP; If so, loop.
;
; Progressively fill the MP, 4 units at a time.
;
; Start by playing the sound.
;
[c506]@_fillMPLoop:
[c506]a9 13LDA #$13; Set sound 0x13 (fill HP/MP bar).
[c508]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the MP.
;
[c50b]a9 04LDA #$04; Set the MP to 4 units.
[c50d]20 d9 c0JSR Player_AddMP; Add it to the player.
[c510]20 46 dcJSR Game_DrawScreenInFrozenState; Draw the screen but keep all sprites frozen.
;
; Wait 4 frames of updates before filling up the next units
; of MP, to help animate the bar.
;
[c513]20 25 caJSR WaitForNextFrame; Wait a bit.
[c516]20 a8 cbJSR Sprites_FlipRanges
[c519]20 25 caJSR WaitForNextFrame
[c51c]20 a8 cbJSR Sprites_FlipRanges
[c51f]20 25 caJSR WaitForNextFrame
[c522]20 a8 cbJSR Sprites_FlipRanges
[c525]20 25 caJSR WaitForNextFrame
[c528]20 a8 cbJSR Sprites_FlipRanges
;
; Check if we're done filling up MP.
;
[c52b]ad 9a 03LDA a:Player_MP; Load the player's MP.
[c52e]c9 50CMP #$50; Is it below the cap of 80MP?
[c530]90 d4BCC @_fillMPLoop; If so, loop.
[c532]60RTS
[c533]Player_UseRedPotion:
;
; Run the "Used Red Potion" IScript.
;
; IScript 0x80 via jump to
IScripts_Begin.
;
[c533]a9 80LDA #$80; 0x80 == Use Red Potion IScript.
[c535]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c538]0c.byte BANK_12_LOGIC; Bank = 12
[c539]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[c53b]@_afterFarJump:
[c53b]20 bf c4JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play the initial sound effect.
;
[c53e]a9 1aLDA #$1a; 0x1A == Initial item used sound effect.
[c540]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Set up the HP fill loop. This will fill up to 80.
;
[c543]a2 50LDX #$50; X = Maximum HP / our loop counter.
[c545]@_loop:
[c545]8aTXA; A = X
[c546]48PHA; Push it to the stack.
;
; Play the unit fill sound effect.
;
[c547]a9 13LDA #$13; 0x13 = Unit of HP fill sound effect.
[c549]20 e4 d0JSR Sound_PlayEffect; Play the sound.
;
; Add 2HP.
;
[c54c]a9 02LDA #$02
[c54e]20 7b c0JSR Player_AddHP; Add the 2HP to the player.
[c551]20 46 dcJSR Game_DrawScreenInFrozenState
;
; Wait 4 frames worth of updates before filling the next 2HP.
;
[c554]20 25 caJSR WaitForNextFrame
[c557]20 a8 cbJSR Sprites_FlipRanges
[c55a]20 25 caJSR WaitForNextFrame
[c55d]20 a8 cbJSR Sprites_FlipRanges
[c560]20 25 caJSR WaitForNextFrame
[c563]20 a8 cbJSR Sprites_FlipRanges
[c566]20 25 caJSR WaitForNextFrame
[c569]20 a8 cbJSR Sprites_FlipRanges
;
; Check if we're done.
;
[c56c]68PLA; Pop the max HP.
[c56d]aaTAX; X = A
[c56e]ad 31 04LDA a:Player_HP_U; Load the player's current HP.
[c571]c9 50CMP #$50; Is the player at max HP?
[c573]b0 03BCS @_return; If so, return.
[c575]caDEX; X--
[c576]d0 cdBNE @_loop; If X > 0, loop.
[c578]@_return:
[c578]60RTS
;============================================================================
; Use the Wing Boots item.
;
; This will play the Wing Boots message and a sound
; effect, and remove the item from the inventory.
;
; The duration of the Wing Boots will depend on the
; player's title:
;
; * 40 seconds: Novice, Aspirant, Battler, Fighter
; * 30 seconds: Adept, Chevalier, Veteran, Warrior
; * 20 seconds: Swordman, Hero, Soldier, Myrmidon
; * 10 seconds: Champion, Superhero, Paladin, Lor
;
; INPUTS:
;
PlayerTitle:
; The player's current title.
;
;
TITLE_TO_WINGBOOTS_DURATION:
; The table of player titles to Wingboots duration.
;
; OUTPUTS:
;
DurationWingBoots:
; The starting number of seconds remaining for the
; Wing Boots.
;
; CALLS:
;
Player_ClearSelectedItem:
;
Sound_PlayEffect
;
MMC1_LoadBankAndJump
;
UI_DrawTimeValue
;
; XREFS:
;
USE_ITEM_JUMP_TABLE [$PRG15_MIRROR::c4bb]
;============================================================================
[c579]Player_UseWingBoots:
;
; Run the "Wing Boots Used" IScript.
;
; IScript 0x83 via jump to
IScripts_Begin.
;
[c579]a9 83LDA #$83; 0x83 == Wing Boots used IScript.
[c57b]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c57e]0c.byte BANK_12_LOGIC; Bank = 12
[c57f]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the item.
;
[c581]@_afterFarJump:
[c581]20 bf c4JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play a sound effect for the item usage.
;
[c584]a9 1aLDA #$1a; Set the sound to play to 0x1A (special item sound).
[c586]20 e4 d0JSR Sound_PlayEffect; Play the sound.
;
; Calculate the index into the duration table based on
; the player's title.
;
[c589]ad 37 04LDA a:PlayerTitle; Load the player's title.
[c58c]4aLSR A; Divide by 4.
[c58d]4aLSR A
[c58e]aaTAX; X = A (index)
[c58f]bd 99 c5LDA a:TITLE_TO_WINGBOOTS_DURATION,X; Look up in the duration table.
[c592]8d 29 04STA a:DurationWingBoots; Set that as the starting duration.
[c595]20 90 f9JSR UI_DrawTimeValue; Draw the time on the HUD.
[c598]60RTS
;============================================================================
; Wing Boots durations by player title.
;
; The duration decreases every 4 player titles.
;
; XREFS:
;
Player_UseWingBoots
;============================================================================
[c599]TITLE_TO_WINGBOOTS_DURATION:
[c599]28.byte $28; [0]: 40 seconds: Novice, Aspirant, Battler, Fighter
[c59a]1e.byte $1e; [1]: 30 seconds: Adept, Chevalier, Veteran, Warrior
[c59b]14.byte $14; [2]: 20 seconds: Swordman, Hero, Soldier, Myrmidon
[c59c]0a.byte $0a; [3]: 10 seconds: Champion, Superhero, Paladin, Lord
;============================================================================
; Decrement the Wing Boot's duration.
;
; This will decrement and then draw the time in the HUD.
;
; INPUTS:
;
DurationWingBoots:
; The remaining duration for the Wing Boots.
;
;
InterruptCounter:
; The current interrupt counter.
;
; OUTPUTS:
;
DurationWingBoots:
; The new duration for the Wing Boots.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
UI_DrawTimeValue
;
; XREFS:
;
GameLoop_CountdownItems
;============================================================================
[c59d]Game_DecWingBootsDuration:
;
; Check if there's any time remaining on the Wing Boots.
;
[c59d]ad 29 04LDA a:DurationWingBoots; Load the remaining time on the Wing Boots.
[c5a0]30 25BMI @_return; If the counter is unset (0xFF), return.
;
; Update the player's status to mark Wing Boots as on.
;
[c5a2]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[c5a4]09 80ORA #$80; Enable the Wing Boots bit.
[c5a6]85 a5STA Player_StatusFlag; Store it.
;
; Update at most once every second.
;
[c5a8]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c5aa]29 3fAND #$3f; Check if we're at the 1 second mark.
[c5ac]d0 19BNE @_return; If not, return.
;
; Reduce time on the Wing Boots by a second.
;
[c5ae]ce 29 04DEC a:DurationWingBoots; Decrement the remaining time for the Wing Boots.
[c5b1]ad 29 04LDA a:DurationWingBoots; Load that duration
[c5b4]30 03BMI @_wingBootsDone; If it wrapped from 0 to 0xFF, it's used up. Jump.
[c5b6]4c 90 f9JMP UI_DrawTimeValue; Else, draw the time remaining.
;
; The Wing Boots ran out. Remove the flag from the player's
; status.
;
[c5b9]@_wingBootsDone:
[c5b9]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[c5bb]29 7fAND #$7f; Clear the Wing Boots flag.
[c5bd]85 a5STA Player_StatusFlag; Store it.
;
; Run "Wing Boots are gone" IScript.
;
; IScript 0x96 via jump to
IScripts_Begin.
;
[c5bf]a9 96LDA #$96; 0x96 == Wing Boots are gone IScript.
[c5c1]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[c5c4]0c.byte BANK_12_LOGIC; Bank = 12
[c5c5]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; It will resume here.
;
[c5c7]@_return:
[c5c7]60RTS
;============================================================================
; Use the Hour Glass.
;
; This will invoke the IScript for the Hour Glass's
; message, play a sound, and begin the Hour Glass
; effect.
;
; The player's health will be reduced by half in the
; process.
;
; INPUTS:
;
Player_HP_U:
;
Player_HP_L:
; The player's current health.
;
; OUTPUTS:
;
Player_HP_U:
;
Player_HP_L:
; The player's new health, reduced by half.
;
;
DurationHourGlass:
; The duration of the Hour Glass.
;
;
Music_Current:
; The music to play while the Hour Glass is
; active.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Player_ClearSelectedItem
;
Sound_PlayEffect
; UI_DrawPlayerHP
;
; XREFS:
;
USE_ITEM_JUMP_TABLE [$PRG15_MIRROR::c4b7]
;============================================================================
[c5c8]Player_UseHourGlass:
;
; Run the "Hour Glass Used" IScript.
;
; IScript 0x82 via jump to
IScripts_Begin.
;
[c5c8]a9 82LDA #$82; Set the IScript to run to 0x82.
[c5ca]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c5cd]0c.byte BANK_12_LOGIC; Bank = 12
[c5ce]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the item.
;
[c5d0]@_afterFarJump:
[c5d0]20 bf c4JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play a sound effect for the item usage.
;
[c5d3]a9 1aLDA #$1a; Set the sound to play to 0x1A (special item sound).
[c5d5]20 e4 d0JSR Sound_PlayEffect; Play the sound.
;
; Reduce the player's health by half. This is the cost of
; the Hour Glass.
;
[c5d8]4e 31 04LSR a:Player_HP_U; Divide the upper value of the player's HP by half.
[c5db]6e 32 04ROR a:Player_HP_L; Divide the lower byte of the player's HP by half, and add carry from the upper.
[c5de]20 bd c0JSR UI_DrawPlayerHP; Draw the new HP.
;
; Set the duration of the Hour Glass to 15 seconds.
;
[c5e1]a9 0fLDA #$0f; Set the duration to 15 seconds.
[c5e3]8d 2a 04STA a:DurationHourGlass
;
; Change the music.
;
[c5e6]a9 0bLDA #$0b; Set the new music to play.
[c5e8]85 faSTA Music_Current; And play it.
[c5ea]60RTS
[c5eb]Game_DecHourGlassDuration:
;
; Check if there's any time remaining on the Hour Glass.
;
[c5eb]ad 2a 04LDA a:DurationHourGlass; Load the remaining time on the Hour Glass.
[c5ee]30 18BMI @_return; If the counter is unset (0xFF), return.
;
; Update at most once every second.
;
[c5f0]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c5f2]29 3fAND #$3f; Check if we're at the 1 second mark.
[c5f4]d0 12BNE @_return; If not, return.
;
; Reduce time on the Hour Glass by a second.
;
[c5f6]ce 2a 04DEC a:DurationHourGlass; Decrement the remaining time for the Hour Glass.
[c5f9]10 0dBPL @_return; If it wrapped from 0 to 0xFF, it's used up. Jump.
;
; Run "Hour Glass is gone" IScript.
;
; IScript 0x97 via jump to
IScripts_Begin.
;
[c5fb]a9 97LDA #$97; 0x97 == Hour Glass is gone IScript.
[c5fd]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[c600]0c.byte BANK_12_LOGIC; Bank = 12
[c601]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Load the area's default music.
;
[c603]@_afterFarJump:
[c603]ad d1 03LDA a:Areas_DefaultMusic; Load the music for the area.
[c606]85 faSTA Music_Current; Store it.
[c608]@_return:
[c608]60RTS
[c609]Player_Add100XP:
[c609]a9 64LDA #$64; A = 100 (lower byte of XP)
[c60b]8d ec 00STA a:Temp_Int24; Store it as an argument.
[c60e]a9 00LDA #$00; A = 0 (upper byte of XP).
[c610]8d ed 00STA a:Temp_Int24_M; Store it as an argument.
[c613]4c 57 f9JMP Player_UpdateExperience; Update the player's experience using those values.
[c616]Player_UseMattock:
;
; Check if the block check position will be in bounds.
;
; The offset will be dependent on the facing direction.
;
; If facing left, this will check the block immediately
; to the left (0xFF -- wrapping around the screen).
;
; If facing right, this will check block + 16 (within or
; at the end of the blocks to clear).
;
[c616]a0 00LDY #$00; Lookup table index = 0 (default)
[c618]a5 a4LDA Player_Flags; Load the player's flags.
[c61a]29 40AND #$40; Is the player facing right?
[c61c]f0 01BEQ @LAB_PRG15_MIRROR__c61f; If not, jump.
;
; The player is facing right. Set our offset into the block
; distances table to 1.
;
[c61e]c8INY; Lookup table index = 1
[c61f]@LAB_PRG15_MIRROR__c61f:
[c61f]a5 9eLDA Player_PosX_Block; Get the player's X position.
[c621]18CLC
[c622]79 8d c6ADC a:USE_MATTOCK_BLOCK_DISTANCES,Y; Add the block distance for the facing direction.
[c625]c9 f0CMP #$f0; Is the player too near to the right edge of the screen?
[c627]b0 63BCS @_return; If so, return.
;
; Begin fetching the block at this offset.
;
; Same logic as above for block calculation.
;
[c629]85 b5STA Arg_PixelPosX; Store the calculated position as the X argument.
[c62b]a5 a1LDA Player_PosY; Load the player's current Y position.
[c62d]85 b6STA Arg_PixelPosY; Store that as the Y argument.
[c62f]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert those pixel positions to block positions.
;
; Check if the screen buffer contains the blocks to clear.
; If not, there's nothing to do.
;
; Each block is composed of 4 tiles, which are represented
; in the lookup table. This only needs to check for the
; presence of one of those blocks.
;
[c632]a5 24LDA Area_CurrentArea; Load the current area.
[c634]0aASL A; Convert to a 4 byte index.
[c635]0aASL A
[c636]a8TAY; Y = index
[c637]bd 00 06LDA a:ScreenBuffer,X; Load the value from the screen buffer.
[c63a]d9 8f c6CMP a:USE_MATTOCK_BLOCK_TRANSITIONS,Y; Is this block already cleared by the Mattock?
[c63d]f0 01BEQ @_useMattock; If not, use the Mattock to clear them.
[c63f]60RTS; Else, return.
;
; The Mattock will be used. Store the offsets into the
; screen buffer and array on the stack for later.
;
[c640]@_useMattock:
[c640]8aTXA; A = X
[c641]48PHA; Push to the stack.
[c642]98TYA; A = Y
[c643]48PHA; Push to the stack.
;
; Run the "Used Mattock" IScript.
;
; IScript 0x81 via jump to
IScripts_Begin.
;
[c644]a9 81LDA #$81
[c646]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c649]0c.byte BANK_12_LOGIC; Bank = 12
[c64a]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the Mattock.
;
[c64c]@_afterFarJump:
[c64c]20 bf c4JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play the sound effect for the Mattock usage.
;
[c64f]a9 1aLDA #$1a; 0x1A == Special item sound effect.
[c651]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Prepare for the loop.
;
; This loop will handle animating the destruction/crumbling
; of blocks. This uses 4 frames of animation, all defined in
;
USE_MATTOCK_BLOCK_TRANSITIONS.
;
[c654]68PLA; Pull A from the stack.
[c655]a8TAY; Y = A
[c656]68PLA; Pull A from the stack.
[c657]aaTAX; X = A
;
; Only update every 4 frames, to get a quick animation effect.
;
[c658]@_loop:
[c658]20 25 caJSR WaitForNextFrame; Wait for 4 frames.
[c65b]20 25 caJSR WaitForNextFrame
[c65e]20 25 caJSR WaitForNextFrame
[c661]20 25 caJSR WaitForNextFrame
;
; Update the screen to place the next block in the sequence.
;
[c664]98TYA; A = Y
[c665]48PHA; Push to the stack.
[c666]8aTXA; A = X
[c667]48PHA; Push to the stack.
[c668]b9 8f c6LDA a:USE_MATTOCK_BLOCK_TRANSITIONS,Y; Load the block for this position.
[c66b]9d 00 06STA a:ScreenBuffer,X; And store it in the screen buffer.
[c66e]20 c5 d7JSR Area_SetBlocks; Update the level data.
;
; Update to the next block.
;
[c671]8aTXA; A = X
[c672]18CLC
[c673]69 10ADC #$10; A += 32
[c675]aaTAX; X = A
[c676]ad c9 03LDA a:Arg_BlockAttributesIndex; Load a value.
[c679]9d 00 06STA a:ScreenBuffer,X; And write it to the screen buffer.
[c67c]20 c5 d7JSR Area_SetBlocks; Upate the level data.
;
; Prepare for the next loop iteration.
;
[c67f]68PLA; Pull A from the stack.
[c680]aaTAX; X = A
[c681]68PLA; Pull A from the stack.
[c682]a8TAY; Y = A
[c683]c8INY; Y++
[c684]98TYA; A = Y
[c685]29 03AND #$03; Have we finished updating the blocks?
[c687]f0 03BEQ @_return; If so, return.
[c689]4c 58 c6JMP @_loop; Else, loop.
[c68c]@_return:
[c68c]60RTS
;============================================================================
; Block distances for checking if a player is on-screen.
;
; This is used when using the Mattock.
;
; XREFS:
;
Player_UseMattock
;============================================================================
[c68d]USE_MATTOCK_BLOCK_DISTANCES:
[c68d]ff.byte $ff; [0]:
[c68e]USE_MATTOCK_BLOCK_DISTANCES_1_:
[c68e]10.byte $10; [1]:
;============================================================================
; New block IDs to place when clearing blocks.
;
; This is divided into regions, each with 4 blocks.
; Those 4 blocks are an animation transition between
; the placed blocks (index 0 within a group) and
; cleared blocks (index 3).
;
; XREFS:
;
Player_UseMattock
;============================================================================
[c68f]USE_MATTOCK_BLOCK_TRANSITIONS:
[c68f]00.byte $00; [0]: Eolis
[c690]00.byte $00; [1]:
[c691]00.byte $00; [2]:
[c692]00.byte $00; [3]:
[c693]63.byte $63; [4]: Apolune
[c694]85.byte $85; [5]:
[c695]86.byte $86; [6]:
[c696]42.byte $42; [7]:
[c697]00.byte $00; [8]: Forepaw
[c698]00.byte $00; [9]:
[c699]00.byte $00; [10]:
[c69a]00.byte $00; [11]:
[c69b]00.byte $00; [12]: Mascon
[c69c]00.byte $00; [13]:
[c69d]00.byte $00; [14]:
[c69e]00.byte $00; [15]:
[c69f]00.byte $00; [16]: Victim
[c6a0]00.byte $00; [17]:
[c6a1]00.byte $00; [18]:
[c6a2]00.byte $00; [19]:
[c6a3]00.byte $00; [20]: Conflate
[c6a4]00.byte $00; [21]:
[c6a5]00.byte $00; [22]:
[c6a6]00.byte $00; [23]:
[c6a7]00.byte $00; [24]: Daybreak
[c6a8]00.byte $00; [25]:
[c6a9]00.byte $00; [26]:
[c6aa]00.byte $00; [27]:
[c6ab]00.byte $00; [28]: Evil Fortress
[c6ac]00.byte $00; [29]:
[c6ad]00.byte $00; [30]:
[c6ae]00.byte $00; [31]:
;============================================================================
; Clear timers on all temporary status items.
;
; This applies to the Glove, Ointment, Wing Boots, and
; Hour Glass.
;
; INPUTS:
; None
;
; OUTPUTS:
;
DurationGlove:
;
DurationHourGlass:
;
DurationOintment:
;
DurationWingBoots:
; All set to 0xFF (not active).
;
; XREFS:
;
Game_Start
;
Player_Spawn
;============================================================================
[c6af]Game_ClearTimedItems:
[c6af]a9 ffLDA #$ff
[c6b1]8d 28 04STA a:DurationGlove; Clear the Glove duration.
[c6b4]8d 27 04STA a:DurationOintment; Clear the Ointment duration.
[c6b7]8d 29 04STA a:DurationWingBoots; Clear the Wing Boots duration.
[c6ba]8d 2a 04STA a:DurationHourGlass; Clear the Hour Glass duration.
[c6bd]60RTS
;============================================================================
; Handle picking up the Hour Glass.
;
; This will invoke the IScript, play the sound, and add
; the Hour Glass to the inventory.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
Player_PickUpItem
;
; XREFS:
;
Player_PickUp
;============================================================================
[c6be]Player_PickUpHourGlass:
;
; Run the "Got Hour Glass" IScript.
;
; IScript 0x8A via jump to
IScripts_Begin.
;
[c6be]a9 8aLDA #$8a; 0x8A == Hour Glass picked up IScript.
[c6c0]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c6c3]0c.byte BANK_12_LOGIC; Bank = 12
[c6c4]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6c6]@_afterFarJump:
[c6c6]a9 08LDA #$08; 0x08 == Item picked up sound.
[c6c8]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Pick up the item.
;
[c6cb]a9 0dLDA #$0d; 0xD == Hour Glass.
[c6cd]4c cd c8JMP Player_PickUpItem; Pick it up.
;============================================================================
; Handle picking up the Wing Boots for the quest.
;
; This will invoke the IScript, play the sound, and add
; the Wing Boots to the inventory.
;
; INPUTS:
;
Quests:
; The player's current completed quests.
;
; OUTPUTS:
;
Quests:
; The updated set of completed quests.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Player_PickUpItem
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c6d0]Player_PickUpWingBootsWithQuest:
[c6d0]ad 2d 04LDA a:Quests; Load the completed quests.
[c6d3]09 08ORA #$08; Mark this quest as completed.
[c6d5]8d 2d 04STA a:Quests; Store it back out.
;
; v-- Fall through --v
;
;============================================================================
; Handle picking up the Wing Boots.
;
; This will invoke the IScript, play the sound, and add
; the Wing Boots to the inventory.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Player_PickUpItem
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c6d8]Player_PickUpWingBoots:
;
; Run the "Got Wing Boots" IScript.
;
; IScript 0x89 via jump to
IScripts_Begin.
;
[c6d8]a9 89LDA #$89; 0x89 == Wing boots picked up IScript.
[c6da]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c6dd]0c.byte BANK_12_LOGIC; Bank = 12
[c6de]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6e0]@_afterFarJump:
[c6e0]a9 08LDA #$08; 0x8 == Item pick-up sound.
[c6e2]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Pick up the item.
;
[c6e5]a9 0fLDA #$0f; 0xF == Wing Boots.
[c6e7]4c cd c8JMP Player_PickUpItem; Pick it up.
;============================================================================
; Pick up the Battle Suit and add to the inventory.
;
; This will invoke the IScript, play the sound, and then
; attempt to cap the armor insert position and add the item
; there.
;
; BUGS:
; This has a bug where it checks the caps against the
; WEAPON slots, not the ARMOR slots!
;
; Player_PickUpDragonSlayer has the inverse
; bug.
;
; INPUTS:
;
NumberOfWeapons:
; The number of weapons the player is carrying.
;
; OUTPUTS:
;
ArmorInventory:
; The updated armor inventory.
;
;
NumberOfArmors:
; The number of armors in the inventory.
;
; See CALLS.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c6ea]Player_PickUpBattleSuit:
;
; Run the "Got Battle Suit" IScript.
;
; IScript 0x8B via jump to
IScripts_Begin.
;
[c6ea]a9 8bLDA #$8b; 0x8B == Battle Suit picked up IScript.
[c6ec]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c6ef]0c.byte BANK_12_LOGIC; Bank = 12
[c6f0]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6f2]@_afterFarJump:
[c6f2]a9 08LDA #$08; 0x08 == Item picked up sound.
[c6f4]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Cap the number of WEAPONS in the inventory.
;
; Yes, weapons. This *should* be armor. This code is swapped
; with the code that equips the Dragon Slayer.
;
[c6f7]ae c2 03LDX a:NumberOfWeapons; X = Number of weapons.
[c6fa]e0 04CPX #$04; If < 4
[c6fc]90 02BCC @_addItem; Then, add it to the latest slot.
[c6fe]a2 03LDX #$03; Else, cap X to 3.
;
; Add the Battle Suit to the inventory.
;
[c700]@_addItem:
[c700]a9 03LDA #$03; 0x03 == Battle Suit
[c702]9d a1 03STA a:ArmorInventory,X; Set in the inventory slot
;
; Increase the number of armors collected.
;
[c705]e8INX; X++
[c706]8e c3 03STX a:NumberOfArmors; Store as the number of armors.
[c709]60RTS
;============================================================================
; Pick up the Battle Helmet and add to the inventory.
;
; This will invoke the IScript, play the sound, and then
; cap the shield insertion point and add the item there.
;
; Keep in mind, the Battle Helmet is considered a shield in
; this game.
;
; INPUTS:
;
NumberOfShields:
; The number of shields the player is carrying.
;
; OUTPUTS:
;
ShieldInventory:
; The updated armor inventory.
;
;
NumberOfShields:
; The number of armors in the inventory.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c70a]Player_PickUpBattleHelmet:
;
; Run the "Got Battle Helmet" IScript.
;
; IScript 0x8C via jump to
IScripts_Begin.
;
[c70a]a9 8cLDA #$8c; 0x8C == Battle Helmet picked up IScript.
[c70c]20 59 f8JSR MMC1_LoadBankAndJump; Run it.
[c70f]0c.byte BANK_12_LOGIC; Bank = 12
[c710]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c712]@_afterFarJump:
[c712]a9 08LDA #$08; 0x08 == Item picked up sound.
[c714]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Cap the number of shields in the inventory.
;
[c717]ae c4 03LDX a:NumberOfShields; X = Number of shields.
[c71a]e0 04CPX #$04; If X < 4:
[c71c]90 02BCC @_addItem; Then, add to the latest slot.
[c71e]a2 03LDX #$03; Else, cap X to 3.
;
; Add the Battle Helmet to the inventory.
;
[c720]@_addItem:
[c720]a9 03LDA #$03; 0x03 == Battle Helmet.
[c722]9d a5 03STA a:ShieldInventory,X; Set in the inventory slot.
;
; Increase the number of shields collected.
;
[c725]e8INX; X++
[c726]8e c4 03STX a:NumberOfShields; Store as the number of shields.
[c729]60RTS
;============================================================================
; Pick up the Dragon Slayer and add to the inventory.
;
; This will invoke the IScript, play the sound, and then
; attempt to cap the armor insert position and add the item
; there.
;
; BUGS:
; This has a bug where it checks the caps against the
; ARMOR slots, not the WEAPON slots!
;
;
Player_PickUpBattleSuit has the inverse
; bug.
;
; INPUTS:
;
NumberOfArmors:
; The number of armors the player is carrying.
;
; OUTPUTS:
;
WeaponInventory:
; The updated weapon inventory.
;
;
NumberOfWeapons:
; The number of weapons in the inventory.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c72a]Player_PickUpDragonSlayer:
;
; Run the "Got Dragon Slayer" IScript.
;
; IScript 0x8D via jump to
IScripts_Begin.
;
[c72a]a9 8dLDA #$8d; 0x8D == Dragon Slayer picked up IScript.
[c72c]20 59 f8JSR MMC1_LoadBankAndJump; Run it.
[c72f]0c.byte BANK_12_LOGIC; Bank = 12
[c730]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c732]@_afterFarJump:
[c732]a9 08LDA #$08; 0x08 == Item picked up sound.
[c734]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Cap the number of ARMORS in the inventory.
;
; Yes, armors. This *should* be weapons. This code is swapped
; with the code that equips the Battle Suit.
;
[c737]ae c3 03LDX a:NumberOfArmors; X = number of armors.
[c73a]e0 04CPX #$04; If < 4
[c73c]90 02BCC @_addItem; Then, add it to the latest slot.
[c73e]a2 03LDX #$03; Else, cap X to 3.
;
; Add the Dragon Slayer to the inventory.
;
[c740]@_addItem:
[c740]a9 03LDA #$03; 0x03 == Dragon Slayer
[c742]9d 9d 03STA a:WeaponInventory,X; Set in the inventory slot.
;
; Increase the number of weapons collected.
;
[c745]e8INX; X++
[c746]8e c2 03STX a:NumberOfWeapons; Store as the number of weapons.
[c749]60RTS
;============================================================================
; Handle picking up the Mattock for the quest.
;
; This will invoke the IScript, play the sound, and set
; the Mattock bit in the quests and in the inventory.
;
; INPUTS:
;
Quests:
; The current completed quests.
;
; OUTPUTS:
;
Quests:
; The updated completed quests.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Player_PickUpItem
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c74a]Player_PickUpMattockWithQuest:
[c74a]ad 2d 04LDA a:Quests; Load the quests bitmask.
[c74d]09 10ORA #$10; Bit 0x10 == Mattock
[c74f]8d 2d 04STA a:Quests; Save it.
;
; v-- Fall through to the next function. --v
;
;============================================================================
; Handle picking up the Mattock.
;
; This will invoke the IScript, play the sound, and add
; the Mattock to the inventory.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Player_PickUpItem
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c752]Player_PickUpMattock:
;
; Run the "Got Mattock" IScript.
;
; IScript 0x88 via jump to
IScripts_Begin.
;
[c752]a9 88LDA #$88; 0x88 == Mattock picked up IScript.
[c754]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c757]0c.byte BANK_12_LOGIC; Bank = 12
[c758]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c75a]@_afterFarJump:
[c75a]a9 08LDA #$08; 0x08 == Item picked up sound.
[c75c]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Load the Mattock into the inventory.
;
[c75f]a9 09LDA #$09; 0x09 == Mattock
[c761]4c cd c8JMP Player_PickUpItem; Pick up the Mattock.
[c764]Player_PickUp:
;
; Check if this is the Mattock.
;
[c764]c9 50CMP #$50; 0x50 == Mattock
[c766]f0 eaBEQ Player_PickUpMattock; If 0x50, this is the Mattock. Pick it up.
;
; Check if this is the Magical Rod.
;
[c768]c9 57CMP #$57; 0x57 == Magical Rod
[c76a]d0 03BNE @_checkBattleSuit
[c76c]4c 10 c8JMP Player_PickUpMagicalRod; This is the Magical Rod. Pick it up.
;
; Check if this is the battle suit.
;
[c76f]@_checkBattleSuit:
[c76f]c9 58CMP #$58; 0x58 == Battle Suit
[c771]d0 03BNE @_checkBattleHelmet
[c773]4c ea c6JMP Player_PickUpBattleSuit; This is the Battle Suit. Pick it up.
;
; Check if this is the battle helmet.
;
[c776]@_checkBattleHelmet:
[c776]c9 59CMP #$59; 0x59 == Battle Helmet
[c778]f0 90BEQ Player_PickUpBattleHelmet; This is the Battle Helmet. Pick it up.
;
; Check if this is the Dragon Slayer.
;
[c77a]c9 5aCMP #$5a; 0x5A == Dragon Slayer
[c77c]f0 acBEQ Player_PickUpDragonSlayer; This is the Dragon Slayer. Pick it up.
;
; Check if this is the Mattock.
;
[c77e]c9 5bCMP #$5b; 0x5B == Mattock
[c780]f0 c8BEQ Player_PickUpMattockWithQuest; This is the Mattock. Pick it up.
;
; Check if this is the Wing Boots.
;
[c782]c9 55CMP #$55; 0x55 == Wing Boots
[c784]d0 03BNE @_checkWingBootsQuest8
[c786]4c d8 c6JMP Player_PickUpWingBoots; This is the Wing Boots. Pick it up.
;
; Check if this is the Wing Boots (with Quest 0x08).
;
[c789]@_checkWingBootsQuest8:
[c789]c9 5cCMP #$5c; 0x5C == Wing Boots for Quest 8
[c78b]d0 03BNE @_checkHourGlass
[c78d]4c d0 c6JMP Player_PickUpWingBootsWithQuest; This is the Wing Boots for Quest 8. Pick it up.
;
; Check if this is the Hour Glass.
;
[c790]@_checkHourGlass:
[c790]c9 56CMP #$56; 0x56 == Hour Glass
[c792]d0 03BNE @_checkRedPotion
[c794]4c be c6JMP Player_PickUpHourGlass; This is the Hour Glass. Pick it up.
;
; Check if this is the Red Potion.
;
[c797]@_checkRedPotion:
[c797]c9 5dCMP #$5d; 0x5D == Red Potion (1)
[c799]d0 03BNE @_checkPoison
[c79b]4c 26 c8JMP Player_PickUpRedPotion; This is the Red Potion (1). Pick it up.
;
; Check if this is the Poison.
;
[c79e]@_checkPoison:
[c79e]c9 5eCMP #$5e; 0x5E == Poison
[c7a0]d0 03BNE @_checkGlove1
[c7a2]4c 3c c8JMP Player_PickUpPoison; This is the Poison. Pick it up.
[c7a5]@_checkGlove1:
[c7a5]c9 5fCMP #$5f; 0x5F == Glove
[c7a7]f0 26BEQ Player_PickUpGlove; This is the Glove. Pick it up.
;
; Check if this is the Ointment.
;
[c7a9]c9 60CMP #$60; 0x60 == Ointment
[c7ab]d0 03BNE @_checkGlove2
[c7ad]4c 7a c8JMP Player_PickUpOintment; This is the Ointment. Pick it up.
[c7b0]@_checkGlove2:
[c7b0]38SEC
[c7b1]e9 48SBC #$48
[c7b3]a8TAY
[c7b4]f0 19BEQ Player_PickUpGlove; This is the Glove. Pick it up.
;
; Check if this is the Black Onyx.
;
[c7b6]88DEY; 0x49 == Black Onyx
[c7b7]f0 2bBEQ Player_PickUpBlackOnyx; If 0x49, this is the Black Onyx. Pick it up.
;
; Check if this is the Pendant.
;
[c7b9]88DEY; 0x4A == Pendant
[c7ba]f0 3eBEQ Player_PickUpPendant; If 0x4A, this is the pendant. Pick it up.
;
; Check if this is the Red Potion (another variant).
;
[c7bc]88DEY; 0x4B == Red Potion (2)
[c7bd]f0 67BEQ Player_PickUpRedPotion; If 0x4B, this is the Red Potion (2). Pick it up.
;
; Check if this is the Poison.
;
[c7bf]88DEY; 0x4C == Poison
[c7c0]f0 7aBEQ Player_PickUpPoison; If 0x4C, this is the Poison. Pick it up.
;
; Check if this is the Elixir.
;
[c7c2]88DEY; 0x4D == Elixir
[c7c3]d0 03BNE @_checkOintment
[c7c5]4c 64 c8JMP Player_PickUpElixir; This is the Elixir. Pick it up.
;
; Check if this is the Ointment.
;
[c7c8]@_checkOintment:
[c7c8]88DEY; 0x4E == Ointment
[c7c9]d0 03BNE @_exit
[c7cb]4c 7a c8JMP Player_PickUpOintment; This is the Ointment. Pick it up.
[c7ce]@_exit:
[c7ce]60RTS
;============================================================================
; Handle picking up the Glove.
;
; This will invoke the IScript, play the sound, and enable
; the Glove state.
;
; Picking up the glove awards 100XP.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
Player_Add100XP
;
; XREFS:
;
Player_PickUp
;============================================================================
[c7cf]Player_PickUpGlove:
;
; Run the "Got Glove" IScript.
;
; IScript 0x92 via jump to
IScripts_Begin.
;
[c7cf]a9 92LDA #$92; 0x92 == Glove picked up IScript.
[c7d1]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c7d4]0c.byte BANK_12_LOGIC; Bank = 12
[c7d5]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c7d7]@_afterFarJump:
[c7d7]a9 08LDA #$08; 0x08 == Item picked up sound.
[c7d9]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Activate the Glove by setting its duration, and then
; add 100XP to the player.
;
[c7dc]a9 14LDA #$14; A = 20 seconds.
[c7de]8d 28 04STA a:DurationGlove; Set as the Glove duration.
[c7e1]4c 09 c6JMP Player_Add100XP; Give the player 100XP.
;============================================================================
; Handle picking up the Black Onyx.
;
; This will invoke the IScript, play the sound, and add
; the Black Onyx to the special items inventory.
;
; INPUTS:
;
SpecialItems:
; The player's special items.
;
; OUTPUTS:
;
SpecialItems:
; The updated special items.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c7e4]Player_PickUpBlackOnyx:
;
; Run the "Got Black Onyx" IScript.
;
; IScript 0x8E via jump to
IScripts_Begin.
;
[c7e4]a9 8eLDA #$8e; 0x8E == Black Onyx picked up IScript.
[c7e6]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c7e9]0c.byte BANK_12_LOGIC; Bank = 12
[c7ea]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c7ec]@_afterFarJump:
[c7ec]a9 08LDA #$08; 0x08 == Item picked up sound.
[c7ee]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the item to the player's special items list.
;
[c7f1]ad 2c 04LDA a:SpecialItems; Load the current special items bitmask.
[c7f4]09 01ORA #$01; Bit 0x01 == Black Onyx.
[c7f6]8d 2c 04STA a:SpecialItems; Save it back out.
[c7f9]60RTS
;============================================================================
; Handle picking up the Pendant.
;
; This will invoke the IScript, play the sound, and add
; the Pendant to the special items inventory.
;
; INPUTS:
;
SpecialItems:
; The player's special items.
;
; OUTPUTS:
;
SpecialItems:
; The updated special items.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c7fa]Player_PickUpPendant:
;
; Run the "Got Glove" IScript.
;
; IScript 0x8F via jump to
IScripts_Begin.
;
[c7fa]a9 8fLDA #$8f; 0x8F = Pendant picked up IScript.
[c7fc]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c7ff]0c.byte BANK_12_LOGIC; Bank = 12
[c800]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c802]@_afterFarJump:
[c802]a9 08LDA #$08; 0x08 = Item picked up sound.
[c804]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the item to the player's special items list.
;
[c807]ad 2c 04LDA a:SpecialItems; Load the curent special items bitmask.
[c80a]09 02ORA #$02; Bit 0x02 == Pendant.
[c80c]8d 2c 04STA a:SpecialItems; Save it back out.
[c80f]60RTS
;============================================================================
; Handle picking up the Magical Rod.
;
; This will invoke the IScript, play the sound, and add
; the Magical Rod to the special items inventory.
;
; INPUTS:
;
SpecialItems:
; The player's special items.
;
; OUTPUTS:
;
SpecialItems:
; The updated special items.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c810]Player_PickUpMagicalRod:
;
; Run the "Got Magical Rod" IScript.
;
; IScript 0x90 via jump to
IScripts_Begin.
;
[c810]a9 90LDA #$90; 0x90 == Magical Rod picked up IScript.
[c812]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c815]0c.byte BANK_12_LOGIC; Bank = 12
[c816]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c818]@_afterFarJump:
[c818]a9 08LDA #$08; 0x08 == Item picked up sound.
[c81a]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the Magical Rod to the inventory.
;
[c81d]ad 2c 04LDA a:SpecialItems; Load the current special items bitmask.
[c820]09 04ORA #$04; Bit 0x04 == Magical Rod.
[c822]8d 2c 04STA a:SpecialItems; Store it.
[c825]60RTS
;============================================================================
; Handle picking up the Red Potion.
;
; This will invoke the IScript, play the sound, and add
; the Red Potion to the inventory.
;
; INPUTS:
; None
;
; OUTPUTS:
; X:
; Set to the current sprite index.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
Player_PickUpItem
;
; XREFS:
;
Player_PickUp
;============================================================================
[c826]Player_PickUpRedPotion:
;
; Run the "Got Red Potion" IScript.
;
; IScript 0x87 via jump to
IScripts_Begin.
;
[c826]a9 87LDA #$87; 0x87 == Red Potion picked up IScript.
[c828]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c82b]0c.byte BANK_12_LOGIC; Bank = 12
[c82c]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c82e]@_afterFarJump:
[c82e]a9 08LDA #$08; 0x08 == Item picked up sound.
[c830]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the Red Potion to the inventory.
;
[c833]a9 10LDA #$10; 0x10 == Red Potion.
[c835]20 cd c8JSR Player_PickUpItem; Pick up the item.
[c838]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index (restored?)
[c83b]60RTS
;============================================================================
; Handle picking up the Poison.
;
; This will invoke the IScript, play the sound, and decrease
; the player health.
;
; INPUTS:
; None
;
; OUTPUTS:
; X:
; Set to the current sprite index.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
Player_ReduceHP
;
; XREFS:
;
Player_PickUp
;============================================================================
[c83c]Player_PickUpPoison:
;
; Run the "Got Poison" IScript.
;
; IScript 0x91 via jump to
IScripts_Begin.
;
[c83c]a9 91LDA #$91; 0x91 = Poison picked up IScript.
[c83e]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c841]0c.byte BANK_12_LOGIC; Bank = 12
[c842]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for the player taking damage.
;
[c844]@_afterFarJump:
[c844]a9 04LDA #$04; 0x084 = Player took damage sound.
[c846]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Set temporary invincibility frames for the player.
;
[c849]a9 3cLDA #$3c; A = 60 iframes
[c84b]85 adSTA Player_InvincibilityPhase; Set it as the starting invincibility phase.
;
; Mark the player as hit.
;
[c84d]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[c84f]09 02ORA #$02; Bit 0x02 == Player was hit.
[c851]85 a5STA Player_StatusFlag; Save it.
;
; Remove 16 points from the player's health.
;
[c853]a9 00LDA #$00; A = 0
[c855]8d bc 04STA a:Arg_PlayerHealthDelta_L; Store as the lower byte of HP to remove.
[c858]a9 10LDA #$10; A = 16
[c85a]8d bd 04STA a:Arg_PlayerHealthDelta_U; Store as the upper byte of HP to remove.
[c85d]20 8e c0JSR Player_ReduceHP; Reduce the HP by that amount.
[c860]ae 78 03LDX a:CurrentSpriteIndex; X = current sprite index (restored?)
[c863]60RTS
;============================================================================
; Handle picking up the Elixir.
;
; This will invoke the IScript, play the sound, and add
; the Elixir to the special items inventory.
;
; INPUTS:
; None
;
; OUTPUTS:
; X:
; Set to the current sprite index.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
; XREFS:
;
Player_PickUp
;============================================================================
[c864]Player_PickUpElixir:
;
; Run the "Got Elixir" IScript.
;
; IScript 0x86 via jump to
IScripts_Begin.
;
[c864]a9 86LDA #$86; 0x86 == Elixir picked up IScript.
[c866]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c869]0c.byte BANK_12_LOGIC; Bank = 12
[c86a]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c86c]@_afterFarJump:
[c86c]a9 08LDA #$08; 0x08 == Item picked up sound.
[c86e]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Add the Elixir to the inventory.
;
[c871]ad 2c 04LDA a:SpecialItems; Load the current special items bitmask.
[c874]09 08ORA #$08; Bit 0x08 == Elixir.
[c876]8d 2c 04STA a:SpecialItems; Save it back out.
[c879]60RTS
;============================================================================
; Handle picking up the Ointment.
;
; This will invoke the IScript, play the sound, and set
; the Ointment state.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
Sound_PlayEffect
;
Player_Add100XP
;
; XREFS:
;
Player_PickUp
;============================================================================
[c87a]Player_PickUpOintment:
;
; Run "Got Ointment" IScript.
;
; IScript 0x94 via jump to
IScripts_Begin.
;
[c87a]a9 94LDA #$94; 0x94 == Ointment picked up IScript.
[c87c]20 59 f8JSR MMC1_LoadBankAndJump; Run IScript:
[c87f]0c.byte BANK_12_LOGIC; Bank = 12
[c880]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c882]@_afterFarJump:
[c882]a9 08LDA #$08; 0x08 == Item picked up sound.
[c884]20 e4 d0JSR Sound_PlayEffect; Play it.
;
; Activate the Ointment by setting its duration, and then
; add 100XP to the player.
;
; Duration is initially set to a base of 30, but will be
; modified by level separately.
;
[c887]a9 1eLDA #$1e; A = 30 seconds.
[c889]8d 27 04STA a:DurationOintment; Set as the Ointment duration.
[c88c]4c 09 c6JMP Player_Add100XP; Give the player 100XP.
[c88f]GameLoop_CountdownItems:
[c88f]20 eb c5JSR Game_DecHourGlassDuration
[c892]20 9d c5JSR Game_DecWingBootsDuration
[c895]20 9b c8JSR Game_DecGloveDuration
[c898]4c b4 c8JMP Game_DecOintmentDuration
;============================================================================
; Decrement the Glove's duration.
;
; INPUTS:
;
DurationGlove:
; The remaining duration for the glove.
;
;
InterruptCounter:
; The current interrupt counter.
;
; OUTPUTS:
;
DurationGlove:
; The new duration for the glove.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
; XREFS:
;
GameLoop_CountdownItems
;============================================================================
[c89b]Game_DecGloveDuration:
;
; Check if the Glove is active.
;
[c89b]ad 28 04LDA a:DurationGlove; Load the Glove's duration.
[c89e]30 13BMI @_return; If it's 0xFF (not activated), return.
;
; Check if a second has passed.
;
[c8a0]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c8a2]29 3fAND #$3f; Check if it's an even second boundary.
[c8a4]d0 0dBNE @_return; If not, return.
;
; Decrement and check if the Glove is still active.
;
[c8a6]ce 28 04DEC a:DurationGlove; Decrement the Glove's duration by 1 second.
[c8a9]10 08BPL @_return; If it's >= 0, it's still active. Return.
;
; Countdown finished.
;
; Run "Glove is gone" IScript.
;
; IScript 0x93 via jump to
IScripts_Begin.
;
[c8ab]a9 93LDA #$93; 0x93 == Glove is gone IScript.
[c8ad]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[c8b0]0c.byte BANK_12_LOGIC; Bank = 12
[c8b1]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[c8b3]@_return:
[c8b3]60RTS
;============================================================================
; Decrement the Ointment's duration.
;
; INPUTS:
;
DurationOintment:
; The remaining duration for the Ointment.
;
;
InterruptCounter:
; The current interrupt counter.
;
; OUTPUTS:
;
DurationOintment:
; The new duration for the Ointment.
;
; CALLS:
;
MMC1_LoadBankAndJump
;
; XREFS:
;
GameLoop_CountdownItems
;============================================================================
[c8b4]Game_DecOintmentDuration:
;
; Check if the Ointment is active.
;
[c8b4]ad 27 04LDA a:DurationOintment; Load the Ointment's duration.
[c8b7]30 13BMI @_return; If it's 0xFF (not activated), return.
;
; Check if a second has passed.
;
[c8b9]a5 1aLDA InterruptCounter; Load the interrupt counter.
[c8bb]29 3fAND #$3f; Check if it's an even second boundary.
[c8bd]d0 0dBNE @_return; If not, return.
;
; Decrement and check if the Ointment is still active.
;
[c8bf]ce 27 04DEC a:DurationOintment; Decrement the Ointment's duration by 1 second.
[c8c2]10 08BPL @_return; If it's >= 0, it's still active. Return.
;
; Countdown finished.
;
; Run "Ointment is gone" IScript.
;
; IScript 0x95 via jump to
IScripts_Begin.
;
[c8c4]a9 95LDA #$95; 0x95 == Ointment is gone IScript.
[c8c6]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[c8c9]0c.byte BANK_12_LOGIC; Bank = 12
[c8ca]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[c8cc]@_return:
[c8cc]60RTS
;============================================================================
; Pick up a standard item and place it in the inventory.
;
; This will be placed in the last available inventory slot.
; If the inventory is full, this does nothing.
;
; INPUTS:
; A:
; The ID of the item to place in the inventory.
;
;
NumberOfItems:
; The number of items the player has.
;
; OUTPUTS:
;
ItemInventory:
; The updated item inventory.
;
;
NumberOfItems:
; The new number of items the player has.
;
; XREFS:
;
Player_PickUpHourGlass
;
Player_PickUpMattock
;
Player_PickUpRedPotion
;
Player_PickUpWingBoots
;============================================================================
[c8cd]Player_PickUpItem:
;
; Check first if the inventory is full.
;
[c8cd]ae c6 03LDX a:NumberOfItems; Load the number of items in the inventory.
[c8d0]e0 08CPX #$08; Is there room?
[c8d2]b0 07BCS @_return; If full, exit.
;
; There's room. Set the item in the inventory and increment
; the item counter.
;
[c8d4]9d ad 03STA a:ItemInventory,X; Store the item in the next available slot.
[c8d7]e8INX; Set the next available slot.
[c8d8]8e c6 03STX a:NumberOfItems
[c8db]@_return:
[c8db]60RTS
[c8dc]UI_ClearSelectedItemPic:
;
; Set the draw position to 0x13C0 (top-left of selected
; item tiles).
;
[c8dc]a9 13LDA #$13; A = 0x13
[c8de]8d e9 00STA a:PPU_TargetAddr_U; Set as upper byte.
[c8e1]a9 c0LDA #$c0; A = 0xC0
[c8e3]8d e8 00STA a:PPU_TargetAddr; Set as lower byte.
;
; Begin the draw loop. This will draw 4 tiles of the sprite.
; All 4 tiles making up the sprite are adjacent within this
; tile attribute.
;
[c8e6]a2 04LDX #$04; X = 4 (counter).
[c8e8]@_vertLoop:
[c8e8]8aTXA; A = X
[c8e9]48PHA; Push A to the stack.
;
; Set the draw length as 16 bytes (this tile).
;
[c8ea]a9 10LDA #$10; A = 16
[c8ec]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Write as the length.
[c8ef]a0 10LDY #$10; Y = 16
[c8f1]a9 00LDA #$00; A = 0
[c8f3]@_horizLoop:
[c8f3]9d 00 05STA a:PPUBuffer,X; Write a 0 to the PPU buffer.
[c8f6]e8INX; X++
[c8f7]88DEY; Y--
[c8f8]d0 f9BNE @_horizLoop; If not 0, loop.
;
; This row is finished. Prepare to draw the next (if any).
;
[c8fa]86 20STX PPUBuffer_WriteOffset; Store X as the new upper bounds.
[c8fc]ad e8 00LDA a:PPU_TargetAddr; A = target address to draw to.
[c8ff]18CLC
[c900]69 10ADC #$10; Increment by 16 (next tile).
[c902]8d e8 00STA a:PPU_TargetAddr; Store as the new lower byte of the target address.
[c905]ad e9 00LDA a:PPU_TargetAddr_U; Load the upper byte.
[c908]69 00ADC #$00; Add the carry flag, if any.
[c90a]8d e9 00STA a:PPU_TargetAddr_U; And save it.
[c90d]68PLA; Pull A from the stack (full 0..4 loop counter).
[c90e]aaTAX; X = A
[c90f]caDEX; X--
[c910]d0 d6BNE @_vertLoop; If > 0, loop.
[c912]60RTS
[c913]Game_Init:
[c913]78SEI
[c914]d8CLD
[c915]a2 ffLDX #$ff; X = 0xFF
[c917]9aTXS; Push to stack.
;
; Set the PPU settings.
;
; For PPUMASK:
;
; * Color
; * Show background and sprites in leftmost 8 pixels
;
;
; For PPUCTRL:
;
; * Base nametable address = 0x2000
; * VRAM address increment per CPU read/write: add 1, going across
; * Sprite pattern table address for 8x8 sprites: 0x1000
; * Background pattern table address: 0x1000
; * Sprite size: 8x8 pixels
; * PPU master/slave select: Read backdrop from EXT pins
; * Vblank NMI disabled
;
[c918]a9 00LDA #$00; A = 0
[c91a]8d 01 20STA a:PPUMASK; Set as the PPU mask.
[c91d]a9 18LDA #$18; A = 0x18
[c91f]8d 00 20STA a:PPUCTRL; Set as the PPU control.
;
; Read the PPUSTATUS register several times before writing.
;
; NESdev says this is common to ensure writes occur in the
; correct order.
;
[c922]ad 02 20LDA a:PPUSTATUS; Read PPU status.
[c925]@_readPPUStatus1:
[c925]ad 02 20LDA a:PPUSTATUS; Read PPU status.
[c928]10 fbBPL @_readPPUStatus1; If > -1, loop.
[c92a]@_readPPUStatus2:
[c92a]ad 02 20LDA a:PPUSTATUS; Read PPU status.
[c92d]10 fbBPL @_readPPUStatus2; If > -1, loop.
;
; Set PPUMASK to 0 to allow transferring large amounts of
; Data to VRAM.
;
[c92f]a9 00LDA #$00; A = 0
[c931]8d 01 20STA a:PPUMASK; Set as the PPU mask.
[c934]a2 ffLDX #$ff; X = 0xFF
[c936]9aTXS; Transfer to stack.
;
; Clear RAM, setting all ranges of bytes used for state to 0.
;
[c937]a2 00LDX #$00; X = 0 (loop counter)
[c939]@_loop:
[c939]e0 fcCPX #$fc; Is X >= 252?
[c93b]b0 02BCS @_clearState; If so, jump.
[c93d]95 00STA Temp_00,X; Clear memory at address X.
[c93f]@_clearState:
[c93f]9d 00 02STA a:Temp_0200,X; Clear memory at $0200 + X
[c942]9d 00 03STA a:CurrentSprites_InternalBehaviorStates_4_,X; Clear memory at $0300 + X
[c945]9d 00 04STA a:FirstColumnInRightScreen_10_,X; Clear memory at $0400 + X
[c948]9d 00 05STA a:PPUBuffer,X; Clear memory at $0500 + X
[c94b]9d 00 06STA a:ScreenBuffer,X; Clear memory at $0600 + X
[c94e]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Clear memory at $0700 + X
[c951]e8INX; X++
[c952]d0 e5BNE @_loop; If X != 0, loop.
[c954]20 bf cbJSR Game_InitMMCAndBank; Initialize the MMC1 chip and bank.
[c957]20 78 caJSR Game_InitScreenAndMusic; Initialize the screen and music.
[c95a]4c 6a daJMP Game_InitStateForStartScreen; Initialize the start screen.
;
; Check whether the OAM needs to be reset.
;
;
; XREFS:
;
OnInterrupt
;
[c95d]OnInterrupt__updatePPUOAMAndAudio:
[c95d]a5 1bLDA Game_NeedOAMReset; Check the reset flag.
[c95f]f0 0cBEQ OnInterrupt__updatePPUAndAudio; If 0, don't reset. Jump to PPU/audio management.
[c961]a9 00LDA #$00
[c963]8d 03 20STA a:OAMADDR; Reset OAMADDR to 0.
[c966]85 1bSTA Game_NeedOAMReset; Clear the reset flag.
[c968]a9 07LDA #$07
[c96a]8d 14 40STA a:OAMDMA; Initialize the OAM, writing from $0700.
[c96d]OnInterrupt__updatePPUAndAudio:
[c96d]20 d6 c9JSR PPU_HandleOnInterrupt; Run PPU on-interrupt handlers.
;
; Update the audio so music keeps playing.
;
;
; XREFS:
;
OnInterrupt
;
[c970]OnInterrupt__updateAudio:
[c970]e6 1aINC InterruptCounter; Increment the interrupt counter.
[c972]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[c975]48PHA; Push it to the stack.
[c976]a2 05LDX #$05
[c978]20 85 ccJSR MMC1_EnsurePRG; Switch to bank 5.
[c97b]20 09 80JSR thunk_Music_HandleOnInterrupt; Run audio interrupt handlers.
[c97e]20 03 80JSR thunk_SoundEffects_HandleOnInterrupt; Run sound playback interrupt handlers.
[c981]68PLA; Pop the bank from the stack.
[c982]aaTAX
[c983]20 85 ccJSR MMC1_EnsurePRG; Switch back to the bank.
[c986]4c d0 c9JMP OnInterrupt__popAndReturn; Prepare to exit the interrupt handler.
;
; Main interrupt handling code won't be run. Update the
; paused interrupt counter state and clear the PPU mask
; to enable transfering large amounts of data to VRAM.
;
;
; XREFS:
;
OnInterrupt
;
[c989]OnInterrupt__noProcessInterrupts:
[c989]a6 14LDX PauseInterruptCounter; Load the interrupt counter.
[c98b]e0 02CPX #$02; Is it < 2?
[c98d]b0 02BCS @_clearPPUMask; If so, jump.
[c98f]e6 14INC PauseInterruptCounter; Increment the counter.
[c991]@_clearPPUMask:
[c991]a9 00LDA #$00
[c993]8d 01 20STA a:PPUMASK; Clear PPUMASK.
[c996]4c 70 c9JMP OnInterrupt__updateAudio; Update the audio.
[c999]OnInterrupt:
;
; Push all registers to the stack.
;
[c999]48PHA; Push A to the stack.
[c99a]8aTXA; Set A = X.
[c99b]48PHA; Push A (X) to the stack.
[c99c]98TYA; Set A = Y.
[c99d]48PHA; Push A (Y) to the stack.
;
; Check what will be handled this interrupt.
;
[c99e]a5 13LDA Game_InterruptHandlersEnabled; Check whether interrupt handling code should be run.
[c9a0]f0 e7BEQ OnInterrupt__noProcessInterrupts; If not, jump.
;
; Interrupts may be run. See if it's time to run them.
;
[c9a2]a5 10LDA Game_InterruptsHandledLatch; Check if we're on a frame where we should run interrupts.
[c9a4]d0 b7BNE OnInterrupt__updatePPUOAMAndAudio; Jump to update the PPU, OAM, and Audio.
;
; Ready to handle routine operations.
;
[c9a6]e6 10INC Game_InterruptsHandledLatch; Flip the toggle.
;
; Reset the OAM.
;
; This will write $00 to OAMADDR and then use OAMDMA.
;
[c9a8]a9 00LDA #$00
[c9aa]8d 03 20STA a:OAMADDR; Clear OAMADDR.
[c9ad]85 1bSTA Game_NeedOAMReset; Turn off the reset flag.
[c9af]a9 07LDA #$07
[c9b1]8d 14 40STA a:OAMDMA; Initialize the OAM, writing from SPRITE_0_RANGE_1_START ($0700).
;
; Update the PPU state.
;
[c9b4]20 d6 c9JSR PPU_HandleOnInterrupt
;
; Switch to bank 5 (audio logic) and run audio updates.
;
[c9b7]ad 00 01LDA a:CurrentROMBank; Load the current bank.
[c9ba]48PHA; Push it to the stack.
[c9bb]a2 05LDX #$05
[c9bd]20 85 ccJSR MMC1_EnsurePRG; Switch to bank 5.
[c9c0]20 09 80JSR thunk_Music_HandleOnInterrupt; Run audio interrupt handling code.
[c9c3]20 03 80JSR thunk_SoundEffects_HandleOnInterrupt; Run sound playback interrupt handling code.
[c9c6]68PLA; Pop the previous bank from the stack.
[c9c7]aaTAX
[c9c8]20 85 ccJSR MMC1_EnsurePRG; Switch back to it.
;
; Handle input management and final state.
;
[c9cb]20 35 caJSR Input_HandleOnInterrupt; Run input interrupt handlers.
[c9ce]e6 1aINC InterruptCounter; Increment the interrupt counter.
;
; Restore all registers from the stack.
;
;
; XREFS:
;
OnInterrupt
;
[c9d0]OnInterrupt__popAndReturn:
[c9d0]68PLA; Pull A from the stack.
[c9d1]a8TAY; Set Y = A
[c9d2]68PLA; Pull A from the stack.
[c9d3]aaTAX; Set X = A
[c9d4]68PLA; Pull A from the satck
[c9d5]OnHardwareInterrupt:
[c9d5]40RTI; Return from interrupt
;============================================================================
; Update the PPU on interrupt.
;
; This will run any screen scroll handlers, ensure the right
; PPU flags pertaining to scroll, and then set the new scroll
; screen and offsets.
;
; INPUTS:
;
PPU_Mask:
; The screen mask to set.
;
;
PPU_ControlFlags:
; The saved PPU control flags.
;
;
Sprites_Sprite0Mode:
; The current sprite 0 placement mode for the
; screen, dictating the flags to set.
;
;
PPU_ScrollScreen:
; The scroll screen to set.
;
;
PPU_ScrollX:
; The horizontal scroll offset to set.
;
; OUTPUTS:
;
PPUCTRL:
;
PPUMASK:
;
PPUSCROLL:
; The updated PPU state.
;
; CALLS:
;
PPUBuffer_Draw
;
Screen_RunWriteScrollDataHandler
;
; XREFS:
;
OnInterrupt
;============================================================================
[c9d6]PPU_HandleOnInterrupt:
;
; Handle any scrolling-related screen updates and flush
; the PPU buffer to the screen.
;
[c9d6]20 1d d6JSR Screen_RunWriteScrollDataHandler; Run the handler for any screen scrolling.
[c9d9]20 3c cfJSR PPUBuffer_Draw; Flush the PPU buffer to the screen.
[c9dc]a5 0bLDA PPU_Mask; Load the stored PPU mask.
[c9de]8d 01 20STA a:PPUMASK; And set it on the PPU.
[c9e1]a5 5aLDA Sprites_Sprite0Mode; Load the sprite 0 mode.
[c9e3]30 2cBMI @_setScroll; If 0xFF (no sprite 0), jump.
[c9e5]a5 5bLDA PPU_ForceLowerPatternTables; Load the Force Lower Pattern Tables setting.
[c9e7]f0 0aBEQ @_clearNametable2400; If not forcing, then jump.
;
; Clear the following flags:
;
; * Nametable 2400
; * Sprite pattern 1000
; * BGTable Addr 1000
;
[c9e9]a5 0aLDA PPU_ControlFlags; Load the saved PPU control flags.
[c9eb]29 e6AND #$e6; Clear flags.
[c9ed]8d 00 20STA a:PPUCTRL; Set it on the PPU.
[c9f0]4c fa c9JMP @_resetScroll; Jump to update scroll positions.
;
; Clear the Nametable 2400 flag.
;
[c9f3]@_clearNametable2400:
[c9f3]a5 0aLDA PPU_ControlFlags
[c9f5]29 feAND #$fe
[c9f7]8d 00 20STA a:PPUCTRL
;
; Reset the screen scroll.
;
[c9fa]@_resetScroll:
[c9fa]a9 00LDA #$00
[c9fc]8d 05 20STA a:PPUSCROLL; Clear horizontal screen scroll.
[c9ff]8d 05 20STA a:PPUSCROLL; Clear vertical screen scroll.
;
; Wait until sprite 0 isn't hit in the sprite update.
;
[ca02]@_waitSprite0NotHitLoop:
[ca02]2c 02 20BIT a:PPUSTATUS; Test the Sprite 0 Hit flag.
[ca05]70 fbBVS @_waitSprite0NotHitLoop; If set, loop.
;
; Now wait until it is hit.
;
[ca07]@_waitSprite0Hit:
[ca07]2c 02 20BIT a:PPUSTATUS; Test the Sprite 0 Hit flag.
[ca0a]50 fbBVC @_waitSprite0Hit; if not set, loop.
;
; Loop 160 times, giving the screen time to update.
;
[ca0c]a2 a0LDX #$a0; X = 160 (loop counter).
[ca0e]@_loop160Times:
[ca0e]caDEX; X--
[ca0f]d0 fdBNE @_loop160Times; If > 0, loop.
;
; Set the scroll state for the PPU.
;
; This will set the nametable for the scroll screen and
; set the horizontal scroll offset. Vertical will stay 0.
;
[ca11]@_setScroll:
[ca11]a5 0dLDA PPU_ScrollScreen; Load the current scroll screen.
[ca13]29 01AND #$01; Convert to an even/odd value (0 or 1) to select the nametable.
[ca15]05 0aORA PPU_ControlFlags; OR with the PPU controls.
[ca17]8d 00 20STA a:PPUCTRL; Store as the new PPU control flags.
[ca1a]a5 0cLDA PPU_ScrollX; Load the calculated scroll X.
[ca1c]8d 05 20STA a:PPUSCROLL; Write as the horizontal scroll offset.
[ca1f]a9 00LDA #$00; Hard-code scroll Y as 0.
[ca21]8d 05 20STA a:PPUSCROLL; Write as the vertical scroll offset.
[ca24]60RTS
[ca25]WaitForNextFrame:
[ca25]a9 00LDA #$00; A = 0
[ca27]85 10STA Game_InterruptsHandledLatch; Set as the frame toggle state.
[ca29]@_loop:
[ca29]a5 10LDA Game_InterruptsHandledLatch; Load the frame toggle state from the interrupt handler.
[ca2b]f0 fcBEQ @_loop; If it's 0, loop.
[ca2d]60RTS; Else, return.
[ca2e]WaitForInterrupt:
[ca2e]a5 1aLDA InterruptCounter; Load the interrupt count.
[ca30]@_loop:
[ca30]c5 1aCMP InterruptCounter; Has the interrupt counter changed?
[ca32]f0 fcBEQ @_loop; If not, loop.
[ca34]60RTS
;============================================================================
; Handle input management on interrupt.
;
; This will read from the controller ports, storing
; the current button bitmasks and determining which buttons
; have been held down and which have changed since the last
; time the interrupt handler was run.
;
; Controller 2 is read for the purpose of the debug level
; switch code at
Debug_ChooseArea.
;
; INPUTS:
;
JOY1:
;
JOY2:
; The controller inputs.
;
;
Joy1_ButtonMask:
; The previous controller 1 button mask.
;
; OUTPUTS:
;
Joy1_ButtonMask:
; The new controller 1 button mask.
;
;
Joy1_ChangedButtonMask:
; The buttons that changed since the last interrupt.
;
;
Joy1_PrevButtonMask:
; The buttons held down since last interrupt.
;
;
Joy2_ButtonMask:
; The new controller 2 button mask.
;
; XREFS:
;
OnInterrupt
;============================================================================
[ca35]Input_HandleOnInterrupt:
;
; Store the current button mask as the new previous button mask.
;
[ca35]a5 16LDA Joy1_ButtonMask; A = Stored controller 1 button mask
[ca37]85 18STA Joy1_PrevButtonMask; Store as the previous button mask.
;
; Prepare to read the controller state.
;
[ca39]a9 01LDA #$01; A = 1
[ca3b]8d 16 40STA a:JOY1; Trigger loading controller 1 bits.
[ca3e]a9 00LDA #$00
[ca40]8d 16 40STA a:JOY1; Prepare for read.
[ca43]85 16STA Joy1_ButtonMask; Reset controller 1 button mask to 0.
[ca45]85 17STA Joy2_ButtonMask; Reset controller 2 button mask to 0.
;
; Read the controller 1 state into the button mask.
;
[ca47]a2 08LDX #$08; X = 8 (loop counter)
[ca49]@_loop1:
[ca49]ad 16 40LDA a:JOY1; A = Current controller 1 state.
[ca4c]29 03AND #$03; Take the first two bits.
[ca4e]4aLSR A; Shift right 1. Bit 0 goes into Carry.
[ca4f]26 16ROL Joy1_ButtonMask; Rotate the target bitmask left, moving Carry into bit 0.
[ca51]05 16ORA Joy1_ButtonMask; OR to the generated bitmask.
[ca53]85 16STA Joy1_ButtonMask; Store as the new button mask.
[ca55]caDEX; X--
[ca56]d0 f1BNE @_loop1; If > 0, loop.
;
; Read the controller 2 state into the button mask.
;
; This is only used for the debug level skip code.
;
[ca58]a2 08LDX #$08; X = 8 (loop counter).
[ca5a]@_loop2:
[ca5a]ad 17 40LDA a:JOY2; A = Current controller 2 state.
[ca5d]29 01AND #$01; Take the first bit.
[ca5f]4aLSR A; Shift right 1. Bit 0 goes into Carry.
[ca60]26 17ROL Joy2_ButtonMask; Rotate the target bitmask left, moving Carry into bit 0.
[ca62]caDEX; X--
[ca63]d0 f5BNE @_loop2; If > 0, loop.
;
; Compute the changed button mask between the last read and now.
;
[ca65]a5 16LDA Joy1_ButtonMask; Load the controller 1 button mask.
[ca67]45 18EOR Joy1_PrevButtonMask; XOR with the previously-changed, yielding the button differences.
[ca69]25 16AND Joy1_ButtonMask; AND with the new button mask.
[ca6b]85 19STA Joy1_ChangedButtonMask; And store as the changed button mask.
[ca6d]60RTS
;============================================================================
; Return a pseudo-random value.
;
; Every time this is called, a new pseudo-random value will
; be returned.
;
; The set of random values is the first 256 bytes of bank
; 14. If any buttons are pressed, the button bitmask will
; affect the random value (the result value and button
; bitmask is XOR'd).
;
; INPUTS:
;
Random_Offset:
; The current offset into the bank.
;
;
Joy1_ButtonMask:
; The controller 1 button mask.
;
; OUTPUTS:
; A:
; The pseudo-random value.
;
;
Random_Offset:
; The incremented offset (which will wrap from 0xFF
; to 0x00).
;
; XREFS:
;
BScript_Action_RandomlyFlipXDirection
;
BScript_Action_RandomlyFlipYDirection
;============================================================================
[ca6e]GetRandom:
[ca6e]a6 daLDX Random_Offset; Load the current offset into the bank.
[ca70]bd 00 80LDA a:Sprites_UpdateAll,X; Load the byte value at that offset.
[ca73]45 16EOR Joy1_ButtonMask; XOR with the controler 1 button mask.
[ca75]e6 daINC Random_Offset; Increment the offset.
[ca77]60RTS
[ca78]Game_InitScreenAndMusic:
;
; Initialize PPU settings.
;
[ca78]a9 10LDA #$10
[ca7a]85 0aSTA PPU_ControlFlags; Set PPU control flags to BGTable and Address $1000.
[ca7c]a9 00LDA #$00
[ca7e]85 0cSTA PPU_ScrollX; Clear scroll X.
[ca80]85 0dSTA PPU_ScrollScreen; Clear scroll screen.
[ca82]85 57STA PPU_ScrollY; Clear scroll Y.
[ca84]a9 1eLDA #$1e
[ca86]85 0bSTA PPU_Mask; Set screen color mode to enable sprites, BG, and showing left-most 8 pixels.
[ca88]20 4f cbJSR Screen_ResetSpritesForNonGame; Reset sprites for a non-game screen.
[ca8b]20 bc caJSR PPU_InitAttributeAndNameTables; Initialize attributes and nametables.
;
; Stop any interrupt handling for the game.
;
[ca8e]a9 00LDA #$00
[ca90]85 13STA Game_InterruptHandlersEnabled; Disable interrupt handlers.
[ca92]8d 2f 04STA a:Maybe_Game_Ready; Disable the game ready state.
;
; Switch to bank 5 and set up sound.
;
[ca95]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ca98]48PHA; Push it to the stack.
[ca99]a2 05LDX #$05; Bank 5 = Sound.
[ca9b]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
[ca9e]20 06 80JSR thunk_Audio_InitPlayingState; Initialize the sound play state.
[caa1]20 00 80JSR thunk_SoundEffects_Init; Initialize sound.
[caa4]68PLA; Pop the previous bank from the stack.
[caa5]aaTAX; X = previous bank.
[caa6]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
;
; Re-enable the game.
;
[caa9]a9 00LDA #$00
[caab]85 10STA Game_InterruptsHandledLatch; Disable the interrupt handlers latch.
[caad]8d 20 01STA a:Game_PausedState; Disable the pause state.
[cab0]85 5bSTA PPU_ForceLowerPatternTables; Disable forcing lower pattern tables.
[cab2]4c 2f cbJMP PPU_InitVBlank; Initialize VBlank.
;============================================================================
; Set the PPU address to the specified value.
;
; INPUTS:
; A:
; The upper byte of the address.
;
; X:
; The lower byte of the address.
;
; OUTPUTS:
;
PPUADDR:
; The updated address.
;
; XREFS:
;
PPU_InitAttributeAndNameTables
;============================================================================
[cab5]PPU_SetAddr:
[cab5]8d 06 20STA a:PPUADDR; Set the upper byte.
[cab8]8e 06 20STX a:PPUADDR; Set the lower byte.
[cabb]60RTS
;============================================================================
; Initialize the attribute and name tables for the PPU.
;
; This will write the following:
;
; * Name table 0: 8 bytes of 0x20
; * Attribute table 0: 320 bytes of 0x55
; * Attribute table 1: 320 bytes of 0x55
;
; INPUTS:
; None.
;
; OUTPUTS:
;
PPUDATA:
; PPU state will be set.
;
;
Temp_PPU_NameTableValue:
; Clobbered.
;
; CALLS:
;
PPU_SetAddr
;
PPU_WriteRowsWithInitial
;
; XREFS:
;
Game_InitScreenAndMusic
;============================================================================
[cabc]PPU_InitAttributeAndNameTables:
;
; Store a value to write in name table 0.
;
; There's no good reason this is stored in its own variable and then
; almost immediately accessed, except that this code likely contained
; other possible values to include here based on other state. Any such
; code no longer exists.
;
[cabc]a9 20LDA #$20; A = 0x20 (value to write).
[cabe]85 23STA Temp_PPU_NameTableValue; Store it temporarily.
;
; Set address 0x2000: Name table 0.
;
; Write 32 to 8 bytes.
;
[cac0]a9 20LDA #$20; Upper byte of table address.
[cac2]a2 00LDX #$00; Lower byte of table address.
[cac4]20 b5 caJSR PPU_SetAddr; Set that as the address to write to.
[cac7]a0 08LDY #$08; 8 full rows.
[cac9]a2 00LDX #$00; 0 starting columns before full rows.
[cacb]a5 23LDA Temp_PPU_NameTableValue; Value to write (from above).
[cacd]20 ed caJSR PPU_WriteRowsWithInitial; Write to name table 0.
;
; Set address 0x23C0: Attribute table 0.
;
; Write 64 entries of byte 0x55 to the table. This is color bit 01
; for each quadrant of each sprite.
;
[cad0]a9 23LDA #$23; Upper byte of table address.
[cad2]a2 c0LDX #$c0; Lower byte of table address.
[cad4]20 b5 caJSR PPU_SetAddr; Set as the address to write to.
[cad7]a2 40LDX #$40; Write 64 initial bytes.
[cad9]a0 01LDY #$01; Plus 1 full row-length of columns (256 bytes).
[cadb]a9 55LDA #$55; Value to write.
[cadd]20 ed caJSR PPU_WriteRowsWithInitial; Write to attribute table 0.
;
; Set address 0x27C0: Attribute table 1.
;
; Write 64 entries here with the same information.
;
[cae0]a9 27LDA #$27; Upper byte of table address.
[cae2]a2 c0LDX #$c0; Lower byte of table address.
[cae4]20 b5 caJSR PPU_SetAddr; Set as the address to write to.
[cae7]a2 40LDX #$40; Write 64 initial bytes.
[cae9]a0 01LDY #$01; Plus 1 full row-length of columns (256 bytes).
[caeb]a9 55LDA #$55
;
; v-- Fall through --v
; Value to write.
;
;============================================================================
; Write a value repeatedly to the PPU.
;
; This writes a value to the PPU up to X times, and then
; again 256 * Y times.
;
; INPUTS:
; A:
; The value to write.
;
; X:
; Initial number of bytes to write.
;
; Y:
; The number of full rows' worth of columns to fill after
; the initial column span (256 * Y).
;
; OUTPUTS:
;
PPUDATA:
; Updated with the new written data.
;
; XREFS:
;
PPU_InitAttributeAndNameTables
;
PPU_WriteRowsWithInitial
;============================================================================
[caed]PPU_WriteRowsWithInitial:
[caed]8d 07 20STA a:PPUDATA; Write the value to PPUDATA.
[caf0]caDEX; X--
[caf1]d0 faBNE PPU_WriteRowsWithInitial; If X > 0, loop.
;
; An inner loop was finished. Decrement and begin again.
;
; It appears it should write 256 more times on this iteration.
;
[caf3]88DEY; Y--
[caf4]d0 f7BNE PPU_WriteRowsWithInitial; If Y > 0, loop
[caf6]60RTS
[caf7]PPU_WaitUntilFlushed:
;
; Wait until the PPU buffer has been emptied (flushed to the
; PPU).
;
[caf7]a5 20LDA PPUBuffer_WriteOffset; Load the upper bounds of the PPU buffer.
[caf9]c5 1fCMP PPUBuffer_ReadOffset; Has the read offset hit the upper bounds?
[cafb]d0 faBNE PPU_WaitUntilFlushed; If not, loop until it's cleared.
;
; Pause interrupt handling for 2 interrupts.
;
[cafd]a9 00LDA #$00
[caff]85 14STA PauseInterruptCounter; Reset the paused interrupt counter.
[cb01]85 13STA Game_InterruptHandlersEnabled; Disable processing of interrupts.
[cb03]85 5bSTA PPU_ForceLowerPatternTables
;
; Wait for 2 interrupts (at least).
;
[cb05]@_waitForInterruptLoop:
[cb05]a5 14LDA PauseInterruptCounter; Load the paused interrupt counter.
[cb07]c9 02CMP #$02; Has it reached 2 interrupts?
[cb09]90 faBCC @_waitForInterruptLoop; If not, loop.
[cb0b]60RTS
;============================================================================
; DEADCODE
;
; XREFS:
;
DEADCODE_FUN_PRG15_MIRROR__cb0c
;============================================================================
[cb0c]DEADCODE_FUN_PRG15_MIRROR__cb0c:
[cb0c]ad 02 20LDA a:PPUSTATUS
[cb0f]30 fbBMI DEADCODE_FUN_PRG15_MIRROR__cb0c
[cb11]@LAB_PRG15_MIRROR__cb11:
[cb11]ad 02 20LDA a:PPUSTATUS
[cb14]10 fbBPL @LAB_PRG15_MIRROR__cb11
[cb16]60RTS
[cb17]Screen_ResetForGamePlay:
[cb17]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset the sprites for game play mode.
[cb1a]a9 01LDA #$01
[cb1c]85 13STA Game_InterruptHandlersEnabled; Enable interrupt handlers.
[cb1e]60RTS
;============================================================================
; DEADCODE
;============================================================================
[cb1f]DEADCODE_FUN_PRG15_MIRROR__cb1f:
[cb1f]20 3f cbJSR DEADCODE_Sprites_ResetForDefaultsMode1
[cb22]a9 01LDA #$01
[cb24]85 13STA Game_InterruptHandlersEnabled
[cb26]60RTS
[cb27]Screen_ResetForNonGame:
[cb27]20 4f cbJSR Screen_ResetSpritesForNonGame; Reset the sprites for a non-game mode.
[cb2a]a9 01LDA #$01
[cb2c]85 13STA Game_InterruptHandlersEnabled; Enable interrupt handlers.
[cb2e]60RTS
;============================================================================
; Initialize VBlank for the PPU.
;
; INPUTS:
;
PPU_ControlFlags:
; The current PPU control flags.
;
; OUTPUTS:
;
PPU_ControlFlags:
; The updated PPU control flags.
;
; XREFS:
;
Game_InitScreenAndMusic
;============================================================================
[cb2f]PPU_InitVBlank:
;
; Enable VBlank NMI.
;
[cb2f]a5 0aLDA PPU_ControlFlags; Load the PPU control flags.
[cb31]09 80ORA #$80; Enable the VBlank bit.
[cb33]d0 04BNE @_savePPUCtrl; If not 0, jump. (DEADCODE: It will never be 0, so always jump).
;
; DEADCODE: Disable VBlank NMI.
;
; We should never actually end up here, since the
; value we compare against 0 above is always at least
; 0x80.
;
[cb35]a5 0aLDA PPU_ControlFlags
[cb37]29 7fAND #$7f
;
; Write our copy of the bitmask and set on the PPU.
;
[cb39]@_savePPUCtrl:
[cb39]85 0aSTA PPU_ControlFlags; Store the saved PPU control flags.
[cb3b]8d 00 20STA a:PPUCTRL; Store the flags in the PPU.
[cb3e]60RTS
;============================================================================
; DEADCODE
;
; XREFS:
;
DEADCODE_FUN_PRG15_MIRROR__cb1f
;============================================================================
[cb3f]DEADCODE_Sprites_ResetForDefaultsMode1:
[cb3f]a9 01LDA #$01
[cb41]85 5aSTA Sprites_Sprite0Mode
[cb43]85 5bSTA PPU_ForceLowerPatternTables
[cb45]d0 0cBNE Sprites_Reset
;
; v-- Fall through --v
;
[cb47]Screen_ResetSpritesForGamePlay:
[cb47]a9 00LDA #$00
[cb49]85 5aSTA Sprites_Sprite0Mode; Set sprite 0 to X=8, Y=23.
[cb4b]85 5bSTA PPU_ForceLowerPatternTables; Disable forcing lower pattern tables.
[cb4d]f0 04BEQ Sprites_Reset; Reset sprites. This will always jump.
[cb4f]Screen_ResetSpritesForNonGame:
[cb4f]a9 ffLDA #$ff; A = 0xFF (no sprite 0)
[cb51]85 5aSTA Sprites_Sprite0Mode; Store it.
;
; v-- Fall through --v
;
[cb53]Sprites_Reset:
;
; Reset state.
;
; Some of this seem to be remnants from an older design.
;
[cb53]a0 00LDY #$00; Y = 0
[cb55]84 33STY Sprites_PPUOffset; Sprites_PPUOffset = 0
[cb57]84 34STY Sprites_Unused_0034; Sprites_Unused_0034 = 0
[cb59]84 35STY Sprites_Unused_0035; Sprites_Unused_0035 = 0
[cb5b]84 38STY Sprites_Unused_0038; Sprites_Unused_0038 = 0
[cb5d]84 37STY Sprites_Unused_0037; Sprites_Unused_0037 = 0
[cb5f]84 39STY Sprites_SlotsFull; Set sprite slots to not be full.
[cb61]84 25STY Sprites_SpriteSlot; Set the starting sprite slot to 0.
[cb63]a5 5aLDA Sprites_Sprite0Mode; Load our byte from before.
;
; Check whether sprite 0 should be hard-coded.
;
[cb65]30 1bBMI @_beginFillSpriteSlots; If 0xFF, then jump.
[cb67]e6 25INC Sprites_SpriteSlot; Increment the sprite slot (start new sprites at sprite 1).
[cb69]a8TAY; Y = A (index)
[cb6a]b9 96 cbLDA a:SPRITE0_START_Y,Y; Load the Y for the sprite.
[cb6d]8d 00 07STA a:SPRITE_0_RANGE_1_START; Store for sprite 0's Y.
[cb70]a9 7fLDA #$7f; 0x7F == tile ID
[cb72]8d 01 07STA a:SPRITE0_TILE; Store for sprite 0's tile ID.
[cb75]a9 23LDA #$23; 0x23 == Palette index 3, behind background
[cb77]8d 02 07STA a:SPRITE0_ATTRS; Store for sprite 0's attributes.
[cb7a]b9 98 cbLDA a:SPRITE0_START_X,Y; Load the X for the sprite.
[cb7d]8d 03 07STA a:SPRITE0_X; Store for sprite 0's X.
[cb80]a0 04LDY #$04; Y = 4 (loop counter).
;
; Begin filling all sprite slots with a Y = 240 (off-screen).
;
[cb82]@_beginFillSpriteSlots:
[cb82]a9 f0LDA #$f0; A = 0xF0 (default Y for unused slots).
[cb84]@_fillSpriteSlotsLoop:
[cb84]99 00 07STA a:SPRITE_0_RANGE_1_START,Y; Set it for this sprite slot's Y.
[cb87]c8INY; Increment slot byte by 4.
[cb88]c8INY
[cb89]c8INY
[cb8a]c8INY
[cb8b]d0 f7BNE @_fillSpriteSlotsLoop; If not 0, loop.
;
; Flip the start of the sprite slot range between 0 and 32.
;
[cb8d]a5 1cLDA Sprites_StartSlotRange; Load the frame flags.
[cb8f]29 80AND #$80; Cap to 0 or 32.
[cb91]49 80EOR #$80; Flip it between 0 and 32.
[cb93]85 1cSTA Sprites_StartSlotRange; Store it.
[cb95]60RTS
;============================================================================
; Start Y locations for the default sprite 0.
;
; XREFS:
;
Sprites_Reset
;============================================================================
[cb96]SPRITE0_START_Y:
[cb96]17.byte $17; [0]: SPRITE_0_RANGE_1_START when Sprites_Sprite0Mode == 0
[cb97]48.byte $48; [1]: SPRITE_0_RANGE_1_START when Sprites_Sprite0Mode == 0xFF
;============================================================================
; Start X locations for the default sprite 0.
;
; XREFS:
;
Sprites_Reset
;============================================================================
[cb98]SPRITE0_START_X:
[cb98]08.byte $08; [0]: SPRITE0_X when Sprites_Sprite0Mode == 0
[cb99]00.byte $00; [1]: SPRITE0_X when Sprites_Sprite0Mode == 0xFF
[cb9a]Game_WaitForOAMReset:
[cb9a]20 47 cbJSR Screen_ResetSpritesForGamePlay
[cb9d]a9 01LDA #$01
[cb9f]85 1bSTA Game_NeedOAMReset
[cba1]a5 1aLDA InterruptCounter
[cba3]@_waitForInterruptLoop:
[cba3]c5 1aCMP InterruptCounter
[cba5]f0 fcBEQ @_waitForInterruptLoop
[cba7]60RTS
;============================================================================
; Flip sprite data between slot ranges.
;
; This updates the OAM data for the sprites, copying between
; ranges 0-31 and 32-63. Only data in sprites 1 onward are
; copied. Sprite 0 is left alone.
;
; INPUTS:
;
SPRITE_0_RANGE_1_START:
; The start of the sprite data.
;
; OUTPUTS:
;
SPRITE_0_RANGE_1_START:
; The start of the updated sprite data.
;
; XREFS:
;
GameLoop_CheckPauseGame
;
Player_FillHPAndMP
;
Player_UseRedPotion
;============================================================================
[cba8]Sprites_FlipRanges:
[cba8]a2 04LDX #$04; X = 4 (sprite data offset)
[cbaa]a0 84LDY #$84; Y = 0x84 (start of loop counter)
[cbac]@_loop:
[cbac]bd 00 07LDA a:SPRITE_0_RANGE_1_START,X; Load the sprite Y from this offset in range 1.
[cbaf]48PHA; Push it to the stack.
[cbb0]b9 00 07LDA a:SPRITE_0_RANGE_1_START,Y; Load the sprite Y from this offset in range 2.
[cbb3]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Copy to range 1.
[cbb6]68PLA; Pop the previous Y from the stack.
[cbb7]99 00 07STA a:SPRITE_0_RANGE_1_START,Y; Copy it to range 2.
[cbba]e8INX; X++ (sprite data offset)
[cbbb]c8INY; Y++ (loop counter)
[cbbc]d0 eeBNE @_loop; If not wrapped to 0, loop.
[cbbe]60RTS
;============================================================================
; Initialize the MMC1 chip and switch to bank 14.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
MMC1_ShiftSync:
; Set to 0.
;
;
CurrentROMBank:
; Set to bank 14.
;
;
SavedPRGBank:
; Set to bank 14.
;
; CALLS:
;
MMC1_Init
;
MMC1_UpdateROMBank
;
; XREFS:
;
Game_Init
;============================================================================
[cbbf]Game_InitMMCAndBank:
[cbbf]a9 00LDA #$00
[cbc1]85 12STA MMC1_ShiftSync
[cbc3]20 d0 cbJSR MMC1_Init; Initialize the MMC1 chipset.
[cbc6]a2 0eLDX #$0e
[cbc8]8e 00 01STX a:CurrentROMBank; Set the previous and current ROM banks to 14.
[cbcb]86 11STX SavedPRGBank
[cbcd]4c 1a ccJMP MMC1_UpdateROMBank; Switch to the bank.
;============================================================================
; Initialize the MMC1 chip.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; XREFS:
;
Game_InitMMCAndBank
;============================================================================
[cbd0]MMC1_Init:
[cbd0]a9 ffLDA #$ff
[cbd2]8d ff ffSTA a:$ffff
[cbd5]a9 0eLDA #$0e; Horizontal Mirroring
Regular Mirroring
Swap ROM bank at 0x8000
Swap 8K of VROM at PPU 0x0000
Don't reset
[cbd7]8d ff 9fSTA a:$9fff
[cbda]4aLSR A
[cbdb]8d ff 9fSTA a:$9fff
[cbde]4aLSR A
[cbdf]8d ff 9fSTA a:$9fff
[cbe2]4aLSR A
[cbe3]8d ff 9fSTA a:$9fff
[cbe6]4aLSR A
[cbe7]8d ff 9fSTA a:$9fff; VROM_SIZE_SELECT
[cbea]a9 00LDA #$00; Select VROM bank at 0x0000
Switch 4KB only
Don't reset
[cbec]8d ff bfSTA a:$bfff
[cbef]4aLSR A
[cbf0]8d ff bfSTA a:$bfff
[cbf3]4aLSR A
[cbf4]8d ff bfSTA a:$bfff
[cbf7]4aLSR A
[cbf8]8d ff bfSTA a:$bfff
[cbfb]4aLSR A
[cbfc]8d ff bfSTA a:$bfff; VROM_PAGE_SELECT_1
[cbff]a9 00LDA #$00; Select VROM bank at 0x1000
Switch 4KB only
Don't reset
[cc01]8d ff dfSTA a:$dfff
[cc04]4aLSR A
[cc05]8d ff dfSTA a:$dfff
[cc08]4aLSR A
[cc09]8d ff dfSTA a:$dfff
[cc0c]4aLSR A
[cc0d]8d ff dfSTA a:$dfff
[cc10]4aLSR A
[cc11]8d ff dfSTA a:$dfff
[cc14]60RTS
[cc15]MMC1_SaveROMBankAndUpdateTo:
[cc15]ad 00 01LDA a:CurrentROMBank; Get the currently-loaded ROM bank
[cc18]85 11STA SavedPRGBank
;
; v-- Fall through --v
;
[cc1a]MMC1_UpdateROMBank:
[cc1a]8e 00 01STX a:CurrentROMBank; Set the bank we're switching to.
[cc1d]@_setupMMC1:
[cc1d]a9 01LDA #$01
[cc1f]85 12STA MMC1_ShiftSync
;
; Write 5 LSBs of A to $FFFF, one bit per write (MMC1 serial protocol):
;
[cc21]8aTXA; ROM_PAGE_SELECT parameter in X
[cc22]8d ff ffSTA a:$ffff
[cc25]4aLSR A
[cc26]8d ff ffSTA a:$ffff
[cc29]4aLSR A
[cc2a]8d ff ffSTA a:$ffff
[cc2d]4aLSR A
[cc2e]8d ff ffSTA a:$ffff
[cc31]4aLSR A
[cc32]8d ff ffSTA a:$ffff
;
; Check the MMC1 synchronization state.
;
; If 0, this will re-initialize the MMC1 and switch banks.
;
; If 1, the interrupt handler is already switching banks.
; This can then return.
;
[cc35]a5 12LDA MMC1_ShiftSync
[cc37]c9 01CMP #$01
[cc39]f0 47BEQ @_finish
;
; Slow path. Re-initialize the MMC1.
;
[cc3b]a9 ffLDA #$ff
[cc3d]8d ff ffSTA a:$ffff; Reset the MMC1 shifter/controler.
[cc40]a9 0eLDA #$0e; Horizontal Mirroring
Regular Mirroring
Swap ROM bank at 0x8000
Swap 8K of VROM at PPU 0x000
Don't reset
;
; Set the CHR banking mode.
;
[cc42]8d ff 9fSTA a:$9fff
[cc45]4aLSR A
[cc46]8d ff 9fSTA a:$9fff
[cc49]4aLSR A
[cc4a]8d ff 9fSTA a:$9fff
[cc4d]4aLSR A
[cc4e]8d ff 9fSTA a:$9fff
[cc51]4aLSR A
[cc52]8d ff 9fSTA a:$9fff
;
; Set CHR bank 0 = 0.
;
[cc55]a9 00LDA #$00; Select VROM bank at 0x000
Switch 4K only
Don't reset
[cc57]8d ff bfSTA a:$bfff
[cc5a]4aLSR A
[cc5b]8d ff bfSTA a:$bfff
[cc5e]4aLSR A
[cc5f]8d ff bfSTA a:$bfff
[cc62]4aLSR A
[cc63]8d ff bfSTA a:$bfff
[cc66]4aLSR A
[cc67]8d ff bfSTA a:$bfff
;
; Set CHR bank 1 = 0.
;
[cc6a]a9 00LDA #$00; Select VROM bank at 0x1000
Switch 4K only
Don't reset
[cc6c]8d ff dfSTA a:$dfff
[cc6f]4aLSR A
[cc70]8d ff dfSTA a:$dfff
[cc73]4aLSR A
[cc74]8d ff dfSTA a:$dfff
[cc77]4aLSR A
[cc78]8d ff dfSTA a:$dfff
[cc7b]4aLSR A
[cc7c]8d ff dfSTA a:$dfff
[cc7f]4c 1d ccJMP @_setupMMC1
[cc82]@_finish:
[cc82]c6 12DEC MMC1_ShiftSync
[cc84]60RTS
;============================================================================
; Watchdog handler to ensure the correct ROM bank is set.
;
; This is called by the interrupt handler to keep the MMC1
; in a known configuration and keep the desired ROM bank
; set.
;
; If other code is in the middle of switching ROM banks,
; this will notice and perform a full MMC1 re-initialization
; before setting the bank.
;
; INPUTS:
; X:
; The ROM bank to ensure is set.
;
;
MMC1_ShiftSync:
; Flag indicating if a ROM bank is currently being
; set.
;
; OUTPUTS:
;
CurrentROMBank:
; The new ROM bank.
;
;
MMC1_ShiftSync:
; Flag used to manage/reinitialize MMC1 state.
;
; XREFS:
;
OnInterrupt
;============================================================================
[cc85]MMC1_EnsurePRG:
[cc85]8e 00 01STX a:CurrentROMBank
;
; Check if any code was in the process of switching banks.
;
; If 0, we can exit.
;
; If 1,
MMC1_UpdateROMBank was in the process of
; switching banks.
;
[cc88]a5 12LDA MMC1_ShiftSync
[cc8a]f0 46BEQ @_fastPath
;
; The sync state was set, so something is switching banks.
; Increment the sync state and reset the MMC1 chip.
;
[cc8c]e6 12INC MMC1_ShiftSync
;
; Reset MMC1.
;
[cc8e]a9 ffLDA #$ff
[cc90]8d ff ffSTA a:$ffff
[cc93]a9 0eLDA #$0e; Horizontal Mirroring
Regular Mirroring
Swap ROM bank at 0x8000
Swap 8K of VROM at PPU 0x000
Don't reset
[cc95]@_setMMC1State:
[cc95]8d ff 9fSTA a:$9fff
[cc98]4aLSR A
[cc99]8d ff 9fSTA a:$9fff
[cc9c]4aLSR A
[cc9d]8d ff 9fSTA a:$9fff
[cca0]4aLSR A
[cca1]8d ff 9fSTA a:$9fff
[cca4]4aLSR A
[cca5]8d ff 9fSTA a:$9fff
;
; Set CHR bank 0 = 0
;
[cca8]a9 00LDA #$00
[ccaa]8d ff bfSTA a:$bfff
[ccad]4aLSR A
[ccae]8d ff bfSTA a:$bfff
[ccb1]4aLSR A
[ccb2]8d ff bfSTA a:$bfff
[ccb5]4aLSR A
[ccb6]8d ff bfSTA a:$bfff
[ccb9]4aLSR A
[ccba]8d ff bfSTA a:$bfff
;
; Set CHR bank 1 = 0.
;
[ccbd]a9 00LDA #$00
[ccbf]8d ff dfSTA a:$dfff
[ccc2]4aLSR A
[ccc3]8d ff dfSTA a:$dfff
[ccc6]4aLSR A
[ccc7]8d ff dfSTA a:$dfff
[ccca]4aLSR A
[cccb]8d ff dfSTA a:$dfff
[ccce]4aLSR A
[cccf]8d ff dfSTA a:$dfff
;
; Set the bank.
;
[ccd2]@_fastPath:
[ccd2]8aTXA
[ccd3]8d ff ffSTA a:$ffff
[ccd6]4aLSR A
[ccd7]8d ff ffSTA a:$ffff
[ccda]4aLSR A
[ccdb]8d ff ffSTA a:$ffff
[ccde]4aLSR A
[ccdf]8d ff ffSTA a:$ffff
[cce2]4aLSR A
[cce3]8d ff ffSTA a:$ffff
[cce6]60RTS
[cce7]MMC1_RestorePrevROMBank:
[cce7]a6 11LDX SavedPRGBank
[cce9]4c 1a ccJMP MMC1_UpdateROMBank
;============================================================================
; Remove vertical lines from buffered tiles.
;
; This PPUBuffer draw command will read 16 bytes from the PPU
; buffer and progressively remove vertical lines for them. It's
; used for the player death animation.
;
; This takes the following input from the PPU buffer:
;
; 1. The animation phase.
; 2. The upper address of tiles within the PPU.
; 3. The lower address of tiles within the PPU.
;
; Starting at this address, this will take each of the 16 tiles
; in order and apply a mask to AND out pixels from the tile,
; applying a dissolving animation (using the masking table at
;
PPUBUFFER_DRAWCOMMAND_REMOVE_VERT_LINES_MASKS.
;
; INPUTS:
;
PPUBuffer:
; The PPU buffer to read arguments from.
;
;
PPUBuffer_ReadOffset:
; The offset within the PPU buffer to read from.
;
;
PPUADDR:
; The PPU address to read and write.
;
;
PPUDATA:
; The PPU data to read and write.
;
;
PPUBUFFER_DRAWCOMMAND_REMOVE_VERT_LINES_MASKS:
; The masking table for line removal.
;
; OUTPUTS:
;
PPUBuffer_ReadOffset:
; The updated offset within the PPU buffer.
;
;
PPUADDR:
; The written PPU address.
;
;
PPUDATA:
; The written PPU data.
;
;
Temp_06:
;
Temp_07:
;
Temp_08:
; Clobbered.
;
; TEMP VARIABLES:
;
Temp_06
;
; XREFS:
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc8]
;============================================================================
[ccec]PPUBuffer_DrawCommand_RemoveVerticalLines:
;
; Read the first byte from the buffer as the phase.
;
[ccec]a6 1fLDX PPUBuffer_ReadOffset; Load our current PPU buffer offset into X.
[ccee]bd 00 05LDA a:PPUBuffer,X; Load the first address byte from the buffer into A.
[ccf1]e8INX; Increment buffer offset (X = X + 1).
[ccf2]85 08STA Temp_08; Store the upper byte (A) into a temporary variable.
;
; Read the upper address from the buffer and set in PPUADDR.
;
[ccf4]bd 00 05LDA a:PPUBuffer,X; Load the next byte from the buffer into A.
[ccf7]e8INX; Increment buffer offset.
[ccf8]a8TAY; Y = lower byte.
[ccf9]8c 06 20STY a:PPUADDR; Write the upper byte to PPUADDR.
;
; Read the lower address from the buffer and set in PPUADDR.
;
[ccfc]bd 00 05LDA a:PPUBuffer,X
[ccff]e8INX
[cd00]86 1fSTX PPUBuffer_ReadOffset; Set the new offset.
[cd02]aaTAX
[cd03]8e 06 20STX a:PPUADDR; Write the lower byte to PPUADDR.
;
; Begin preparing for the loop. There will be 16 iterations.
;
[cd06]a9 10LDA #$10; Set to 16 iterations in Temp_06.
[cd08]85 06STA Temp_06
[cd0a]ad 07 20LDA a:PPUDATA; Read a byte from PPUDATA.
[cd0d]@_loop:
[cd0d]86 07STX Temp_07; Store X (offset) into our temp state.
[cd0f]ad 07 20LDA a:PPUDATA; Read the next byte of data.
;
; Set the address again. The PPUDATA read advanced the offset.
;
[cd12]8c 06 20STY a:PPUADDR; Set upper byte of PPU address to Y.
[cd15]8e 06 20STX a:PPUADDR; Set lower byte of PPU address to X.
;
; Add the phase byte and loop index together and get the three
; least-significant bits. This will be our index into the lookup
; table.
;
[cd18]48PHA
[cd19]a5 06LDA Temp_06; Load the loop index.
[cd1b]18CLC
[cd1c]65 08ADC Temp_08; Add the phase byte to it.
[cd1e]29 07AND #$07; Retain only the 3 least-significant bits.
[cd20]aaTAX; Store in X.
[cd21]68PLA
;
; Take the PPU data we loaded before and AND the value in the
; lookup table. This will be the replacement for the data we
; loaded.
;
[cd22]3d 33 cdAND a:PPUBUFFER_DRAWCOMMAND_REMOVE_VERT_LINES_MASKS,X
[cd25]8d 07 20STA a:PPUDATA; Set as the new PPU data.
;
; Load the offset into the buffer and increment.
;
[cd28]a6 07LDX Temp_07; Load the saved offset.
[cd2a]e8INX; Increment by 1.
;
; Prepare either for the next loop or for termination.
;
[cd2b]ad 07 20LDA a:PPUDATA; Load our next PPU data byte for the next loop.
[cd2e]c6 06DEC Temp_06; Decrement our loop index by 1.
[cd30]d0 dbBNE @_loop; If not 0, loop.
[cd32]60RTS
[cd33]PPUBUFFER_DRAWCOMMAND_REMOVE_VERT_LINES_MASKS:
[cd33]fe.byte $fe; [0]:
[cd34]df.byte $df; [1]:
[cd35]f7.byte $f7; [2]:
[cd36]fd.byte $fd; [3]:
[cd37]bf.byte $bf; [4]:
[cd38]ef.byte $ef; [5]:
[cd39]7f.byte $7f; [6]:
[cd3a]fb.byte $fb; [7]:
;============================================================================
; Rotate 16 bytes of tiles right by 1 pixel.
;
; This PPUBuffer draw command will read the upper and
; lower bytes of a PPU address and then iterate over the
; next 16 bytes at that address, rotating all data right
; by 1 pixel.
;
; INPUTS:
; {@sybmol PPUBuffer}:
; The PPU buffer to read from.
;
;
PPUBuffer_ReadOffset:
; The offset within the PPU buffer to read from.
;
;
PPUADDR:
; The PPU address to read and write.
;
;
PPUDATA:
; The PPU data to read and write.
;
; OUTPUTS:
;
PPUBuffer_ReadOffset:
; The updated offset within the PPU buffer.
;
;
PPUADDR:
; The written PPU address.
;
;
PPUDATA:
; The written PPU data.
;
; TEMP VARIABLES:
;
Temp_06
;
; XREFS:
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc4]
;============================================================================
[cd3b]PPUBuffer_DrawCommand_RotateTilesRight1Pixel:
;
; Read the first two bytes from the offset and set as
; the PPUADDR.
;
[cd3b]a6 1fLDX PPUBuffer_ReadOffset; Load our current PPU buffer offset.
[cd3d]bd 00 05LDA a:PPUBuffer,X; Load the first address byte from the buffer.
[cd40]e8INX
[cd41]a8TAY; Y = byte 1
[cd42]8c 06 20STY a:PPUADDR; Set as the upper byte of the PPU address.
[cd45]bd 00 05LDA a:PPUBuffer,X; Load the next address byte.
[cd48]e8INX
[cd49]86 1fSTX PPUBuffer_ReadOffset; Update the offset to skip these bytes.
[cd4b]aaTAX
[cd4c]8e 06 20STX a:PPUADDR; Set as the lower byte of the PPU address.
;
; Begin preparing for drawing.
;
; We'll draw 16 bytes from the buffer.
;
[cd4f]a9 10LDA #$10; Set our loop for 16 bytes.
[cd51]85 06STA Temp_06
[cd53]ad 07 20LDA a:PPUDATA; Load byte 1 from the PPU.
[cd56]@_loop:
[cd56]ad 07 20LDA a:PPUDATA; Load a byte from the PPU.
;
; Set the address again. The PPUDATA read advanced the offset.
;
[cd59]8c 06 20STY a:PPUADDR; Set upper byte of PPU address to Y.
[cd5c]8e 06 20STX a:PPUADDR; Set lower byte of PPU address to X.
;
; Rotate the tile data from this address right by 1 pixel.
;
[cd5f]48PHA
[cd60]4aLSR A; Rotate the pixel data right by 1.
[cd61]68PLA
[cd62]6aROR A; OR it to the data rotated left by 7.
[cd63]8d 07 20STA a:PPUDATA; Store the rotated data.
[cd66]e8INX; Increment the lower byte of the PPU address.
[cd67]ad 07 20LDA a:PPUDATA
;
; Check if we're at the end of the loop.
;
; If we've processed 16 entries, we're done.
;
[cd6a]c6 06DEC Temp_06; Decrement our loop counter.
[cd6c]d0 e8BNE @_loop; If it's > 0, loop.
[cd6e]60RTS
[cd6f]CurrentSprite_ResetPPUTileOffset:
[cd6f]a9 09LDA #$09
[cd71]85 99STA CurrentSprite_TargetPPUTileAddr_U; Set upper to 0x09.
[cd73]a9 00LDA #$00
[cd75]85 98STA CurrentSprite_TargetPPUTileAddr; Set lower to 0x00.
[cd77]60RTS
[cd78]CurrentSprite_LoadTilesInfo:
;
; Save the current ROM bank to the stack.
;
[cd78]ad 00 01LDA a:CurrentROMBank; Load the current bank.
[cd7b]48PHA; Push to the stack.
;
; Switch to the bank for the current sprite's images.
;
[cd7c]ae 86 03LDX a:CurrentSprite_TilesBank; Load the bank where the images for the current sprite are found
[cd7f]20 1a ccJSR MMC1_UpdateROMBank
;
; Read the first byte of the ROM bank and store as the start of
; the lower byte of the sprite images address.
;
[cd82]ad 00 80LDA a:ROMBankStart; Read byte from $8000.
[cd85]85 02STA Temp_Addr_L; Store as the lower byte of the address.
;
; Read the second byte as the upper address, and add 0x80 to it.
;
[cd87]ad 01 80LDA a:ROMBankStart+1; Read byte from $8001.
[cd8a]18CLC
[cd8b]69 80ADC #$80; Add 0x80.
[cd8d]85 03STA Temp_Addr_U; Store as the upper byte of the address.
;
; Compute the starting index for the sprite based on the
; entity ID.
;
; Entities 0-55 are in the first sprite bank. 56+ are in
; the second.
;
[cd8f]ad 8b 03LDA a:CurrentSprite_Entity; Load the current sprite ID
[cd92]c9 37CMP #$37; Check if we're on the next bank.
[cd94]90 02BCC @_loadImages
[cd96]e9 37SBC #$37; We are, so subtract 55 for the new sprite index.
;
; Compute the address for the sprite image.
;
[cd98]@_loadImages:
[cd98]0aASL A; Convert the sprite ID to a word boundary.
[cd99]a8TAY; Y = A
[cd9a]b1 02LDA (Temp_Addr_L),Y; Get the pointer to the sprite data from the bank address computed above.
[cd9c]85 96STA CurrentSprite_LoadTileAddr; A = Lower byte of the image data address.
[cd9e]c8INY
[cd9f]b1 02LDA (Temp_Addr_L),Y; A = Upper byte of the address.
[cda1]18CLC
[cda2]69 80ADC #$80; Add 0x80 to the upper address.
[cda4]85 97STA CurrentSprite_LoadTileAddr_U; Upper byte of pointer to the sprite bitmap
;
; Get the PPU tile numbers for the sprite entity and store
; it for lookup.
;
[cda6]ad 8b 03LDA a:CurrentSprite_Entity; Load the current sprite ID
[cda9]a8TAY
[cdaa]b9 1b ceLDA a:SPRITES_PPU_TILE_COUNTS,Y; Get the number of tiles needed
[cdad]85 9bSTA CurrentSprite_LoadTileCount; Store that for the render
;
; Restore the previous bank.
;
[cdaf]68PLA; Pop the previous bank.
[cdb0]aaTAX; Transfer to X.
[cdb1]20 1a ccJSR MMC1_UpdateROMBank; Update to the bank.
[cdb4]60RTS
[cdb5]Sprites_LoadImageForCurrentSprite:
[cdb5]a5 98LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte of the current PPU tile address to read from.
[cdb7]85 00STA Temp_00; Store it temporarily.
[cdb9]a5 99LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte of the PPU tile address.
[cdbb]06 00ASL Temp_00; Multiply by 16.
[cdbd]2aROL A
[cdbe]06 00ASL Temp_00
[cdc0]2aROL A
[cdc1]06 00ASL Temp_00
[cdc3]2aROL A
[cdc4]06 00ASL Temp_00
[cdc6]2aROL A; A = Lower byte of the offset.
[cdc7]85 9aSTA CurrentSprite_StartPPUTileOffset; Store the upper byte as the starting offset.
;
; Load the tile information for the sprite, and proceed to
; load into the PPU if the sprite entity has tiles.
;
[cdc9]20 78 cdJSR CurrentSprite_LoadTilesInfo; Look up the sprite data pointer for this.
[cdcc]a5 9bLDA CurrentSprite_LoadTileCount; A = Tile count.
[cdce]d0 01BNE @_loadSpriteToPPUBuffer; If count > 0, jump to load tile data.
[cdd0]60RTS
;
; Switch to the bank containing the tiles to load.
;
[cdd1]@_loadSpriteToPPUBuffer:
[cdd1]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[cdd4]48PHA; Push it to the stack.
[cdd5]ae 86 03LDX a:CurrentSprite_TilesBank; X = Bank for the tiles.
[cdd8]20 1a ccJSR MMC1_UpdateROMBank; Switch to that bank.
;
; Set the PPU address to load tiles into.
;
[cddb]a5 99LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte of the target PPU address.
[cddd]85 e9STA PPU_TargetAddr_U; Set it.
[cddf]a5 98LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte.
[cde1]85 e8STA PPU_TargetAddr; Set it.
;
; Queue loading 16 bytes for the current tile.
;
[cde3]a9 10LDA #$10; A = 16 (bytes)
[cde5]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue as the length, and set X as the write index.
;
; Copy the sprite data for this tile to the PPU buffer.
;
[cde8]a0 00LDY #$00; Y = 0 (loop counter)
[cdea]@_copySpriteImage:
[cdea]b1 96LDA (CurrentSprite_LoadTileAddr),Y; Load a byte from the tile.
[cdec]9d 00 05STA a:PPUBuffer,X; Write to the PPU buffer at X.
[cdef]e8INX; X++ (write offset)
[cdf0]c8INY; Y++ (loop counter)
[cdf1]c0 10CPY #$10; Is X < 16?
[cdf3]90 f5BCC @_copySpriteImage; If so, loop.
;
; 16 bytes of sprite data was written to the PPU buffer.
; Update the new write position for the buffer.
;
[cdf5]86 20STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
;
; Restore the previous bank.
;
[cdf7]68PLA; Pull the previous bank from the stack.
[cdf8]aaTAX; Set to X.
[cdf9]20 1a ccJSR MMC1_UpdateROMBank; And switch to it.
;
; Update the source tile address to advance to the next tile.
;
[cdfc]a5 96LDA CurrentSprite_LoadTileAddr; Load the lower byte of the tile address.
[cdfe]18CLC
[cdff]69 10ADC #$10; Add 16 (the bytes we just read).
[ce01]85 96STA CurrentSprite_LoadTileAddr; And store it.
[ce03]a5 97LDA CurrentSprite_LoadTileAddr_U; Load the upper byte.
[ce05]69 00ADC #$00; Add Carry, if the lower byte wrapped.
[ce07]85 97STA CurrentSprite_LoadTileAddr_U; And store it.
;
; Update the target PPU tile address to advance to the next
; tile.
;
[ce09]a5 98LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte of the target address.
[ce0b]18CLC
[ce0c]69 10ADC #$10; Add 16.
[ce0e]85 98STA CurrentSprite_TargetPPUTileAddr; And store it.
[ce10]a5 99LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte.
[ce12]69 00ADC #$00; Add Carry, if the lower byte wrapped.
[ce14]85 99STA CurrentSprite_TargetPPUTileAddr_U; And store it.
;
; Decrement the tile count, and loop if there are tiles
; remaining.
;
[ce16]c6 9bDEC CurrentSprite_LoadTileCount; Decrement the remaining tile count.
[ce18]d0 b7BNE @_loadSpriteToPPUBuffer; If > 0, loop.
[ce1a]60RTS
;============================================================================
; The number of PPU tiles each sprite needs.
;
; XREFS:
;
CurrentSprite_LoadTilesInfo
;============================================================================
[ce1b]SPRITES_PPU_TILE_COUNTS:
[ce1b]01.byte $01; [0]:
[ce1c]01.byte $01; [1]: Dropped: Bread
[ce1d]01.byte $01; [2]: Dropped: Coin
[ce1e]01.byte $01; [3]: Enemy: ?
[ce1f]10.byte $10; [4]: Enemy: Raiden
[ce20]10.byte $10; [5]: Enemy: Necron Aides
[ce21]10.byte $10; [6]: Enemy: Zombie
[ce22]08.byte $08; [7]: Enemy: Hornet
[ce23]06.byte $06; [8]: Enemy: Bihoruda
[ce24]06.byte $06; [9]: Enemy: Lilith
[ce25]07.byte $07; [10]: Magic: ?
[ce26]06.byte $06; [11]: Enemy: Yuinaru
[ce27]0c.byte $0c; [12]: Enemy: Snowman
[ce28]10.byte $10; [13]: Enemy: Nash
[ce29]10.byte $10; [14]: Enemy: Fire Giant
[ce2a]12.byte $12; [15]: Enemy: Ishiisu
[ce2b]0d.byte $0d; [16]: Enemy: Execution Hood
[ce2c]26.byte $26; [17]: Boss: Rokusutahn
[ce2d]10.byte $10; [18]: Boss: unused (round body of snake boss)
[ce2e]00.byte $00; [19]: Effect: Enemy death
[ce2f]00.byte $00; [20]: Effect: Lightning ball
[ce30]16.byte $16; [21]: Enemy: Charron
[ce31]17.byte $17; [22]: Enemy: ? (Unused)
[ce32]10.byte $10; [23]: Enemy: Geributa
[ce33]0e.byte $0e; [24]: Enemy: Sugata
[ce34]12.byte $12; [25]: Enemy: Grimlock
[ce35]0c.byte $0c; [26]: Enemy: Giant Bees
[ce36]0e.byte $0e; [27]: Enemy: Myconid
[ce37]10.byte $10; [28]: Enemy: Naga
[ce38]10.byte $10; [29]: Enemy: Skeleton Knight (unused)
[ce39]12.byte $12; [30]: Enemy: Giant Strider
[ce3a]12.byte $12; [31]: Enemy: Sir Gawaine
[ce3b]1f.byte $1f; [32]: Enemy: Maskman
[ce3c]16.byte $16; [33]: Enemy: Wolfman
[ce3d]0f.byte $0f; [34]: Enemy: Yareeka
[ce3e]10.byte $10; [35]: Enemy: Magman
[ce3f]13.byte $13; [36]: Enemy: Curly-tailed guy with spear (unused)
[ce40]10.byte $10; [37]: Enemy: ? (unused)
[ce41]11.byte $11; [38]: Enemy: Ikeda
[ce42]10.byte $10; [39]: Enemy: Muppet guy (unused)
[ce43]10.byte $10; [40]: Enemy: Lamprey
[ce44]10.byte $10; [41]: Enemy: ? (unused)
[ce45]13.byte $13; [42]: Enemy: Monodron
[ce46]0c.byte $0c; [43]: Enemy: Winged skeleton (unused)
[ce47]12.byte $12; [44]: Enemy: Tamazutsu
[ce48]3e.byte $3e; [45]: Boss: Ripasheiku
[ce49]33.byte $33; [46]: Boss: Zoradohna
[ce4a]1c.byte $1c; [47]: Boss: Borabohra
[ce4b]0e.byte $0e; [48]: Boss: Pakukame
[ce4c]25.byte $25; [49]: Boss: Zorugeriru
[ce4d]54.byte $54; [50]: Boss: King Grieve
[ce4e]69.byte $69; [51]: Boss: Shadow Eura
[ce4f]10.byte $10; [52]: NPC: Walking Man 1
[ce50]10.byte $10; [53]: NPC: Blue lady (unused)
[ce51]09.byte $09; [54]: NPC: Child (unused)
[ce52]08.byte $08; [55]: NPC: Armor Salesman
[ce53]0b.byte $0b; [56]: NPC: Martial Artist
[ce54]0b.byte $0b; [57]: NPC: Priest
[ce55]14.byte $14; [58]: NPC: King
[ce56]0c.byte $0c; [59]: NPC: Magic Teacher
[ce57]08.byte $08; [60]: NPC: Key Salesman
[ce58]0a.byte $0a; [61]: NPC: Smoking Man
[ce59]0e.byte $0e; [62]: NPC: Man in Chair
[ce5a]0a.byte $0a; [63]: NPC: Sitting Man
[ce5b]0d.byte $0d; [64]: NPC: Meat Salesman
[ce5c]10.byte $10; [65]: NPC: Lady in Blue Dress with Cup
[ce5d]10.byte $10; [66]: NPC: Guard
[ce5e]0b.byte $0b; [67]: NPC: Doctor
[ce5f]0e.byte $0e; [68]: NPC: Walking Woman 1
[ce60]0d.byte $0d; [69]: NPC: Walking Woman 2
[ce61]09.byte $09; [70]: Enemy: Eyeball (unused)
[ce62]08.byte $08; [71]: Enemy: Zozura
[ce63]02.byte $02; [72]: Item: Glove
[ce64]02.byte $02; [73]: Item: Black Onyx
[ce65]04.byte $04; [74]: Item: Pendant
[ce66]02.byte $02; [75]: Item: Red Potion
[ce67]02.byte $02; [76]: Item: Poison
[ce68]04.byte $04; [77]: Item: Elixir
[ce69]02.byte $02; [78]: Item: Ointment
[ce6a]00.byte $00; [79]: Trigger: Intro
[ce6b]02.byte $02; [80]: Item: Mattock
[ce6c]00.byte $00; [81]: Magic: ?
[ce6d]0c.byte $0c; [82]: Effect: Fountain
[ce6e]00.byte $00; [83]: Magic: ?
[ce6f]00.byte $00; [84]: Magic: Enemy Fireball
[ce70]04.byte $04; [85]: Item: Wing Boots
[ce71]02.byte $02; [86]: Item: Hour Glass
[ce72]04.byte $04; [87]: Item: Magical Rod
[ce73]02.byte $02; [88]: Item: Battle Suit
[ce74]04.byte $04; [89]: Item: Battle Helmet
[ce75]04.byte $04; [90]: Item: Dragon Slayer
[ce76]02.byte $02; [91]: Item: Mattock
[ce77]04.byte $04; [92]: Item: Wing Boots (from quest)
[ce78]02.byte $02; [93]: Item: Red Potion
[ce79]02.byte $02; [94]: Item: Poison
[ce7a]02.byte $02; [95]: Item: Glove
[ce7b]02.byte $02; [96]: Item: Ointment
[ce7c]0c.byte $0c; [97]: Effect: Spring of Trunk
[ce7d]0c.byte $0c; [98]: Effect: Spring of Sky
[ce7e]0c.byte $0c; [99]: Effect: Spring of Tower
[ce7f]00.byte $00; [100]: Effect: Boss Death
;============================================================================
; Load common sprites from bank 8.
;
; This will load 80 sprites (5 passes of 16 sprites at
; 1,280 bytes total) from bank 8 into the PPU.
;
; The sprites consist of the coins, bread, damage effects,
; magic, HUD and textbox symbols, and the hand cursor.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank.
;
; OUTPUTS:
;
PPUADDR:
;
PPUDATA:
; Written sprite data.
;
;
Temp_00:
;
Temp_Addr_L:
;
Temp_Addr_U:
; Clobbered.
;
; CALLS:
;
MMC1_UpdateROMBank
;
; XREFS:
;
Game_InitStateForSpawn
;
Screen_SetupNew
;============================================================================
[ce80]Sprites_LoadCommon:
;
; Save the current ROM bank and switch to bank 8.
;
[ce80]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ce83]48PHA; Push it to the stack.
[ce84]a2 08LDX #$08; Bank 8 == sprite data.
[ce86]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Set the start address for the sprites to load.
;
[ce89]ad 08 80LDA a:TILES_COMMON_START_REF; Load the lower byte of the address of the first sprite (a coin).
[ce8c]85 02STA Temp_Addr_L; Store it.
[ce8e]ad 09 80LDA a:TILES_COMMON_START_REF+1; Load the upper byte.
[ce91]18CLC; Clear Carry.
[ce92]69 80ADC #$80; Add 0x80 to the upper byte.
[ce94]85 03STA Temp_Addr_U; And store as the upper byte of the address.
;
; Set the sprite tile load count to 5 passes of 256 bytes,
; yielding 80 sprites.
;
[ce96]a9 05LDA #$05; 5 = number of passes of 256 bytes of sprite tiles to load.
[ce98]85 00STA Temp_00; Store it for the loop.
;
; Set the PPU address to $0400 (within the loaded PPU tiles).
;
[ce9a]a9 04LDA #$04; 0x04 == Upper byte.
[ce9c]8d 06 20STA a:PPUADDR; Write it.
[ce9f]a0 00LDY #$00; 0x00 == Lower byte and loop counter.
[cea1]8c 06 20STY a:PPUADDR; Write it.
;
; Begin loading and writing sprites, 5 passes of 256 bytes.
;
[cea4]@_loadLoop:
[cea4]b1 02LDA (Temp_Addr_L),Y; Load the address of the sprite.
[cea6]8d 07 20STA a:PPUDATA; Write to the PPU data.
[cea9]c8INY; Y++ (loop counter).
[ceaa]d0 f8BNE @_loadLoop; If not incremented back up to 0, loop.
[ceac]e6 03INC Temp_Addr_U; Increment the upper byte of the write address.
[ceae]c6 00DEC Temp_00; Decrement the number of passes remaining.
[ceb0]d0 f2BNE @_loadLoop; If > 0, loop.
;
; Loading is complete. Restore the previous bank.
;
[ceb2]68PLA; Pop the previous bank from the stack.
[ceb3]aaTAX; X = A (bank)
[ceb4]20 1a ccJSR MMC1_UpdateROMBank; Switch back to the bank.
[ceb7]60RTS
[ceb8]Area_LoadTiles:
;
; Load the address for the tile data based on the tiles index.
;
[ceb8]a5 95LDA Area_TilesIndex; Set A = tileset index
[ceba]0aASL A; Set A = tileset index * 2 (word offset)
[cebb]a8TAY; Set Y = word offset into the pointer table
[cebc]b9 07 cfLDA a:TILE_INDEX_TO_ADDR,Y; Read the low byte from the table
[cebf]85 93STA Area_TilesReadAddress; Save low byte to TilesAddress
[cec1]b9 08 cfLDA a:TILE_INDEX_TO_ADDR+1,Y; Read the high byte from the table
[cec4]85 94STA Area_TilesReadAddress+1; Set high byte to TilesAddress
;
; Set the PPU to increment by +1 after each PPUDATA write.
;
[cec6]20 2b cfJSR PPU_SetVRAMIncrementAdd1Across; Set PPUDATA to increment by 1 per write.
;
; Load and set the PPU address for the current set of
; tiles. Set the upper byte from the table and lower
; byte to 0.
;
; Upper byte will be 0x18 or 0x1A.
;
[cec9]a4 95LDY Area_TilesIndex; Load the tiles index.
[cecb]b9 19 cfLDA a:TILE_INDEX_TO_PPU_ADDR_UPPER,Y; Convert to a PPU address via lookup table (0x18 or 0x1A).
[cece]8d 06 20STA a:PPUADDR; Write as the upper byte.
[ced1]a9 00LDA #$00; 0x00 = Lower byte (start of page).
[ced3]8d 06 20STA a:PPUADDR; Write as the lower byte.
;
; Set the remaining number of tiles.
;
; This is a 16-bit integer composed of:
;
;
Temp_08: Lower byte
;
Temp_09: Upper byte
;
[ced6]85 08STA Temp_08; Set lower byte of the remaining tile count to 0.
[ced8]b9 22 cfLDA a:TILE_INDEX_TO_NUM_CHR_PAGES,Y; Load the number of pages to load.
[cedb]85 09STA Temp_09; Set as the upper byte.
;
; Save the current bank and switch to bank 4 (tile data).
;
[cedd]ad 00 01LDA a:CurrentROMBank; A = Current ROM bank.
[cee0]48PHA; Push it to the stack.
[cee1]a2 04LDX #$04; 4 = Tiles
[cee3]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 4 (tile data)
;
; Begin loading tiles into PPUDATA.
;
[cee6]a0 00LDY #$00; Set Y = 0 (start page of loop)
;
; Stream 1 byte from tiles to the PPU.
;
[cee8]@_copyLoop:
[cee8]b1 93LDA (Area_TilesReadAddress),Y; Load a byte from the tiles.
[ceea]8d 07 20STA a:PPUDATA; Write to the PPU.
;
; Increment our source pointer (Y++).
;
; If it wraps up to 0, we need to increment the upper byte
; of the tiles address it's loading from.
;
[ceed]c8INY; Y++ (source pointer).
[ceee]d0 02BNE @_noCarry; If wrapped to 0, jump.
[cef0]e6 94INC Area_TilesReadAddress+1; Increment the upper byte of the tiles address.
;
; Decrement the 16-bit remaining tile count.
;
[cef2]@_noCarry:
[cef2]a5 08LDA Temp_08; Load the lower byte of the remaining count.
[cef4]38SEC
[cef5]e9 01SBC #$01; Subtract 1.
[cef7]85 08STA Temp_08; And set it.
[cef9]a5 09LDA Temp_09; Load the upper byte of the remaining count.
[cefb]e9 00SBC #$00; Subtract carry, if lower wrapped.
[cefd]85 09STA Temp_09; And set it.
[ceff]b0 e7BCS @_copyLoop; If count > 0, loop.
;
; Switch back to the previous bank.
;
[cf01]68PLA; Pop the previous bank.
[cf02]aaTAX; Transfer to X.
[cf03]20 1a ccJSR MMC1_UpdateROMBank; And switch to it.
[cf06]60RTS
;============================================================================
; Offsets of the tiles into bank 4.
;
; Index into the table are based on
Area_TilesIndex.
;
; XREFS:
;
Area_LoadTiles
;============================================================================
[cf07]TILE_INDEX_TO_ADDR:
[cf07]00 80pointer AREA_TILESETS_EOLIS; [0]: Eolis
[cf09]00 98pointer AREA_TILESETS_BRANCH; [1]: Branch
[cf0b]00 88pointer AREA_TILESETS_TRUNK; [2]: Trunk
[cf0d]00 90pointer AREA_TILESETS_MIST; [3]: Mist
[cf0f]00 b8pointer AREA_TILESETS_DARTMOOR_EVIL_LAIR; [4]: Dartmoor Castle
Evil Lair
[cf11]00 a0pointer AREA_TILESETS_TOWNS; [5]: Towns
[cf13]00 a8pointer AREA_TILESETS_KINGSROOM_GURU_HOSPITAL; [6]: King's Room
Guru Room
Hospital
[cf15]00 aepointer AREA_TILESETS_SHOPS_HOUSE_TAVERN; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf17]00 b4pointer AREA_TILESETS_MARTIALARTS_MAGICTRAINER; [8]: Martial Arts
Magic Trainer
;============================================================================
; Which parts of the PPU tiles are loaded.
;
; Index into the table are based on
Area_TilesIndex.
;
; XREFS:
;
Area_LoadTiles
;============================================================================
[cf19]TILE_INDEX_TO_PPU_ADDR_UPPER:
[cf19]18.byte $18; [0]: Eolis
[cf1a]18.byte $18; [1]: Branches
[cf1b]18.byte $18; [2]: Trunk
[cf1c]18.byte $18; [3]: Mist
[cf1d]18.byte $18; [4]: Dartmoor Castle
Evil Lair
[cf1e]18.byte $18; [5]: Towns
[cf1f]1a.byte $1a; [6]: King's Room
Guru Room
Hospital
[cf20]1a.byte $1a; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf21]1a.byte $1a; [8]: Martial Arts
Magic Trainer
;============================================================================
; The number of CHR pages to load for a tiles index.
;
; Index into the table are based on
Area_TilesIndex.
;
; The results are further translated as:
;
; 8 == 0x80
; 6 == 0x60
; 4 == 0x40
;
; XREFS:
;
Area_LoadTiles
;============================================================================
[cf22]TILE_INDEX_TO_NUM_CHR_PAGES:
[cf22]08.byte $08; [0]: Eolis
[cf23]08.byte $08; [1]: Branches
[cf24]08.byte $08; [2]: Trunk
[cf25]08.byte $08; [3]: Mist
[cf26]08.byte $08; [4]: Dartmoor Castle
Evil Lair
[cf27]08.byte $08; [5]: Towns
[cf28]06.byte $06; [6]: King's Room
Guru Room
Hospital
[cf29]06.byte $06; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf2a]04.byte $04; [8]: Martial Arts
Magic Trainer
;============================================================================
; Set the VRAM address increment per CPU read/write of PPUDATA to 0: Add 1,
; going across.
;
; INPUTS:
;
PPU_ControlFlags:
; The saved PPU control flags.
;
; OUTPUTS:
;
PPUCTRL:
;
PPU_ControlFlags:
; The updated PPU control flags.
;
; XREFS:
;
Area_LoadTiles
;============================================================================
[cf2b]PPU_SetVRAMIncrementAdd1Across:
[cf2b]a5 0aLDA PPU_ControlFlags; Load the PPU control flags.
[cf2d]29 fbAND #$fb; Set VRAM address increment per CPU read/write of PPUDATA to 0: Add 1, going across.
[cf2f]85 0aSTA PPU_ControlFlags; Set it to SavedPPUCtrl.
[cf31]8d 00 20STA a:PPUCTRL; Set it to PPUCTRL.
[cf34]60RTS
;============================================================================
; DEADCODE: Clear the PPU buffer.
;
; Both the offset and upper bounds will be reset back to 0.
;
; INPUTS:
; None
;
; OUTPUTS:
;
PPUBuffer_ReadOffset:
; The offset to clear.
;
;
PPUBuffer_WriteOffset:
; The upper bounds value to clear.
;============================================================================
[cf35]PPUBuffer_Clear:
[cf35]a9 00LDA #$00
[cf37]85 1fSTA PPUBuffer_ReadOffset; Set offset = 0
[cf39]85 20STA PPUBuffer_WriteOffset; Set upper bounds = 0
[cf3b]RETURN_CF3B:
[cf3b]60RTS
;============================================================================
; Draw from the PPU buffer to the PPU.
;
; This will take the entries in the current buffer and draw
; them to the PPU. Buffer entries may contain a command
; opcode, or it may contain values to write.
;
; Let's look into the two methods for drawing.
;
; 1. Command control
;
; If the first byte to process in the buffer is a value
; decreasing from 0x00 to 0xFA, it will be treated as a
; command, and the entry in
;
PPUBUFFER_DRAW_COMMANDS will
; process the remaining bytes (or just return).
;
; The following commands are supported:
;
; 0x00: Write palette data
;
;
PPUBuffer_DrawCommand_WritePalette
;
; 0xFC: Rotate 16 pixels of tiles right by 1 pixel at
; a given address (at the next two bytes from
; the buffer).
;
;
PPUBuffer_DrawCommand_RotateTilesRight1Pixel
;
; 0xFA: TODO
;
PPUBuffer_DrawCommand_RemoveVerticalLines
;
; Commands 0xFF, 0xFE, 0xFD, and 0xFB just return.
;
; Once a command is processed, the operation is complete.
; Further entries in the buffer will be processed in
; future calls.
;
; 2. Data drawing
;
; The first byte at the buffer offset will contain a
; value with a PPUCTRL flag in bit 7 and the number of
; bytes to write in the remaining bit.
;
; The second and third contain the upper and lower bytes
; of PPUADDR (respectively).
;
; Additional bytes are the <length> bytes to write.
;
; Drawing finishes after any of the following conditions
; are met:
;
; 1. The buffer is empty.
; 2. 6 entries from the buffer have been drawn.
; 3. A maximum number of bytes have been written
; (48 bytes).
;
; INPUTS:
;
PPUBuffer:
; The PPU buffer to process.
;
;
PPUBuffer_ReadOffset:
; The offset within the PPU buffer to process.
;
;
PPUBuffer_WriteOffset:
; The upper bounds within the PPU buffer to process.
;
; OUTPUTS:
;
PPUCTRL:
; PPU control flags.
;
;
PPUADDR:
; PPU write address (reset back to 0).
;
;
PPUDATA:
; PPU draw data.
;
; TEMP VARIABLES:
;
PPUBuffer_Temp_PendingEntryCount:
; The maximum number of entries remaining to
; process.
;
;
PPUBuffer_Temp_TotalByteLength:
; The total number of bytes processed.
;
; CALLS:
;
PPUBuffer_DrawCommand_WritePalette
;
PPUBuffer_DrawCommand_RotateTilesRight1Pixel
;
PPUBuffer_DrawCommand_RemoveVerticalLines
;
; XREFS:
;
PPUBuffer_DrawAll
;
PPU_HandleOnInterrupt
;
Player_DrawSpriteImmediately
;============================================================================
[cf3c]PPUBuffer_Draw:
;
; Check if there's anything in the buffer to write.
; If not, we're done.
;
[cf3c]a5 1fLDA PPUBuffer_ReadOffset; A = current buffer offset
[cf3e]c5 20CMP PPUBuffer_WriteOffset; Compare against the upper bound of the PPU buffer.
[cf40]f0 f9BEQ RETURN_CF3B; If they're the same, we have nothing to write. We're done.
;
; Check the value in the offset and see if it appears to
; be a command opcode of some sort (an index into
;
PPUBUFFER_DRAW_COMMANDS.
;
[cf42]a6 1fLDX PPUBuffer_ReadOffset; X = current PPU offset
[cf44]a9 00LDA #$00; Take a value from $00 to $FA (decreasing) and turn it
into an index.
$00 = 0
$FF = 1
$FE = 2
$FD = 3
$FC = 4
$FB = 5
$FA = 6
[cf46]38SEC
[cf47]fd 00 05SBC a:PPUBuffer,X; Get the value from the buffer at this offset.
[cf4a]c9 07CMP #$07; Check if it's a command code.
[cf4c]b0 0dBCS @_beginDraw; If not, draw normally.
;
; It is a command code. Increment to the next byte, look up
; the entry for the command in our table, and schedule the
; address following for the execution after this returns.
;
[cf4e]e6 1fINC PPUBuffer_ReadOffset; Consume the byte in the buffer.
[cf50]0aASL A
[cf51]a8TAY; Begin looking up in the table.
[cf52]b9 bd cfLDA a:PPUBUFFER_DRAW_COMMANDS+1,Y; High byte of the command address.
[cf55]48PHA; Push to the stack.
[cf56]b9 bc cfLDA a:PPUBUFFER_DRAW_COMMANDS,Y; Low byte of the command address.
[cf59]48PHA; Push to the stack.
[cf5a]60RTS; Return. The next address following will execute.
;
; This is standard data to write to the PPU, not a command.
;
[cf5b]@_beginDraw:
[cf5b]a9 d0LDA #$d0; Total byte length that can be written.
[cf5d]85 22STA PPUBuffer_Temp_TotalByteLength; Set it.
[cf5f]a9 06LDA #$06; Max number of entries in the buffer that can be written.
[cf61]85 21STA PPUBuffer_Temp_PendingEntryCount; Set it.
;
; Loop through the buffer until either it's cleared or we've
; processed 6 entries.
;
[cf63]@_loopBuffer:
[cf63]a6 1fLDX PPUBuffer_ReadOffset; X = Read offset into the buffer.
[cf65]e4 20CPX PPUBuffer_WriteOffset; Compare it to the write offset.
[cf67]f0 4aBEQ @_finishLoop; If it's the same, the loop is finished.
;
; Compute the PPUCTRL to set for this series of writes.
;
; If the most-significant bit is set, we'll set it to
; Add32 Down mode. Otherwise, we'll set it to Add1 Across
; mode.
;
[cf69]a5 0aLDA PPU_ControlFlags; A = PPU control flags.
[cf6b]29 fbAND #$fb; Set to Add 1 Across.
[cf6d]a8TAY; Y = Resulting flags.
[cf6e]bd 00 05LDA a:PPUBuffer,X; Load the byte from the buffer.
[cf71]10 06BPL @_processData; If bit 7 is set, jump.
[cf73]29 7fAND #$7f; Else, set VBLank Disable.
[cf75]c8INY; And Inc Add 32 Down.
[cf76]c8INY
[cf77]c8INY
[cf78]c8INY
[cf79]@_processData:
[cf79]8c 00 20STY a:PPUCTRL; Store the new control flags.
;
; Load the first byte from the buffer at our offset.
; Mask out bit 7 (our flag above). This will be our
; data length counter.
;
[cf7c]bd 00 05LDA a:PPUBuffer,X; A = value from the buffer
[cf7f]29 7fAND #$7f; Mask out control bit 7.
[cf81]a8TAY; Y = A (save until we load the address)
[cf82]e8INX; Set to the next byte.
[cf83]bd 00 05LDA a:PPUBuffer,X; Load the upper PPU byte address from the buffer.
[cf86]8d 06 20STA a:PPUADDR; Set as the upper byte in PPUADDR.
[cf89]e8INX; X = X + 1
[cf8a]bd 00 05LDA a:PPUBuffer,X; Load the lower PPU byte address from the buffer.
[cf8d]8d 06 20STA a:PPUADDR; Set as the lower byte in PPUADDR.
[cf90]e8INX; X = X + 1
[cf91]98TYA; Y = A (restore from above)
[cf92]18CLC
[cf93]65 22ADC PPUBuffer_Temp_TotalByteLength; Add the length to our running total.
[cf95]85 22STA PPUBuffer_Temp_TotalByteLength
;
; Begin writing <length> bytes from the buffer to the PPU.
;
[cf97]@_loopData:
[cf97]bd 00 05LDA a:PPUBuffer,X; Load the byte to draw at our current offset.
[cf9a]e8INX; Increment the offset.
[cf9b]8d 07 20STA a:PPUDATA; Set the byte in our PPUDATA.
[cf9e]88DEY; Decrement the nuber of bytes to write.
[cf9f]d0 f6BNE @_loopData; If we're not done yet, loop.
;
; We're done writing the data bytes. We can now update the
; offset and check whether to move on to the next, or if
; we're done with the buffer for now.
;
; We're done with the buffer if we've hit 6 entries, maxed
; out our byte length counter, or hit a new command opcode.
;
[cfa1]86 1fSTX PPUBuffer_ReadOffset; Increment our offset into the buffer to skip the length
and address bytes and all the data bytes.
[cfa3]c6 21DEC PPUBuffer_Temp_PendingEntryCount; Decrement our total allowed entries for this operation.
[cfa5]f0 0cBEQ @_finishLoop; If we can't do any more, we're done.
[cfa7]bc 00 05LDY a:PPUBuffer,X; Load the next byte to process from the buffer.
[cfaa]88DEY
[cfab]c0 f9CPY #$f9; Check if it's a command byte.
[cfad]b0 04BCS @_finishLoop; If it is, we're done. This will be handled next call.
[cfaf]a5 22LDA PPUBuffer_Temp_TotalByteLength; Check the total byte length.
[cfb1]30 b0BMI @_loopBuffer; If there's room to go, loop.
;
; We're done with everything. Clear out the PPU addresses and
; return.
;
[cfb3]@_finishLoop:
[cfb3]a9 00LDA #$00
[cfb5]8d 06 20STA a:PPUADDR; Set upper PPUADDR to 0.
[cfb8]8d 06 20STA a:PPUADDR; Set lower PPUADDR to 0.
;
; v-- Fall through --v
;
;============================================================================
; No-op draw command.
;
; This is just used as a destination for the draw commands
; lookup table below. It's also the end of the function.
; above.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; XREFS:
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfbe]
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc0]
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc2]
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc6]
;============================================================================
[cfbb]PPUBuffer_DrawCommand_Noop:
[cfbb]60RTS
;============================================================================
; Handlers for special PPU buffer draw commands.
;
; XREFS:
;
PPUBuffer_Draw
;============================================================================
[cfbc]PPUBUFFER_DRAW_COMMANDS:
[cfbc]15 d0pointer PPUBuffer_DrawCommand_WritePalette-1; [0]: Command 0x00: Write Palette to PPU
[cfbe]ba cfpointer PPUBuffer_DrawCommand_Noop-1; [1]: Command 0xFF
[cfc0]ba cfpointer PPUBuffer_DrawCommand_Noop-1; [2]: Command 0xFE
[cfc2]ba cfpointer PPUBuffer_DrawCommand_Noop-1; [3]: Command 0xFD
[cfc4]3a cdpointer PPUBuffer_DrawCommand_RotateTilesRight1Pixel-1; [4]: Command 0xFC: Rotate Tiles Right
[cfc6]ba cfpointer PPUBuffer_DrawCommand_Noop-1; [5]: Command 0xFB
[cfc8]eb ccpointer PPUBuffer_DrawCommand_RemoveVerticalLines-1; [6]: Command 0xFA: Remove Vertical Lines
;============================================================================
; Wait for capacity in the PPU buffer.
;
; This will block until the PPU buffer has been flushed and
; there's at least 36 bytes remaining.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; CALLS:
;
PPUBuffer_HasCapacity
;
; XREFS:
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_WaitForCapacity
;============================================================================
[cfca]PPUBuffer_WaitForCapacity:
[cfca]20 d0 cfJSR PPUBuffer_HasCapacity; Is there capacity?
[cfcd]90 fbBCC PPUBuffer_WaitForCapacity; If not, loop.
[cfcf]60RTS
;============================================================================
; Return whether there's at least 36 bytes of PPU buffer free.
;
; XXX Not right. Re-analyze this.
;
; INPUTS:
;
PPUBuffer_ReadOffset:
; The current offset into the PPU buffer.
;
;
PPUBuffer_WriteOffset:
; The upper bounds of the PPU buffer.
;
; OUTPUTS:
; C:
; 0 = Fewer than 36 bytes available in the buffer.
; 1 = 36 or more bytes available in the buffer.
;
; XREFS:
;
PPUBuffer_WaitForCapacity
;============================================================================
[cfd0]PPUBuffer_HasCapacity:
[cfd0]a5 1fLDA PPUBuffer_ReadOffset; A = PPU buffer offset.
[cfd2]38SEC
[cfd3]e5 20SBC PPUBuffer_WriteOffset; A = offset - upper
[cfd5]f0 02BEQ @_return; If (offset - upper) == 0, jump to return false.
[cfd7]c9 24CMP #$24; Set whether there's at least 36 bytes difference.
[cfd9]@_return:
[cfd9]60RTS
[cfda]09 80.byte $09,$80; word
[cfdc]PPUBuffer_QueueCommandOrLength:
;
; Wait for capacity in the buffer.
;
[cfdc]48PHA; Push the length/command to the stack.
[cfdd]20 ca cfJSR PPUBuffer_WaitForCapacity; Wait for capacity in the buffer.
;
; There's capacity. Set up to write.
;
; Start with the length or command byte.
;
[cfe0]68PLA
[cfe1]a6 20LDX PPUBuffer_WriteOffset; Load the upper bounds of the buffer.
[cfe3]9d 00 05STA a:PPUBuffer,X; Store the provided value (A) there.
[cfe6]e8INX
[cfe7]a5 e9LDA PPU_TargetAddr_U; Load the upper address byte.
[cfe9]9d 00 05STA a:PPUBuffer,X; Write it to the buffer.
[cfec]e8INX
[cfed]a5 e8LDA PPU_TargetAddr; Load the lower address byte.
[cfef]9d 00 05STA a:PPUBuffer,X; Write it to the buffer.
[cff2]e8INX
[cff3]60RTS
[cff4]PPUBuffer_WaitUntilClear:
[cff4]a5 20LDA PPUBuffer_WriteOffset; Load the upper bounds of the buffer.
[cff6]c5 1fCMP PPUBuffer_ReadOffset; Does it == the offset?
[cff8]d0 faBNE PPUBuffer_WaitUntilClear; If not, loop.
[cffa]60RTS
;============================================================================
; Invoke all draws on the PPU buffer until it's cleared.
;
; This will continuously run
PPUBuffer_Draw until
; the
; draw buffer has been cleared.
;
; INPUTS:
;
PPUBuffer_ReadOffset:
; The offset to compare.
;
;
PPUBuffer_WriteOffset:
; The upper bounds to compare.
;
; OUTPUTS:
; None
;
; CALLS:
;
PPUBuffer_Draw
;
; XREFS:
;
PPUBuffer_DrawAll
;
UI_DrawHUD
;============================================================================
[cffb]PPUBuffer_DrawAll:
[cffb]20 3c cfJSR PPUBuffer_Draw; Draw from the buffer.
[cffe]a5 20LDA PPUBuffer_WriteOffset; Load the upper bounds.
[d000]c5 1fCMP PPUBuffer_ReadOffset; Does it match the offset?
[d002]d0 f7BNE PPUBuffer_DrawAll; If not, loop.
[d004]60RTS
;============================================================================
; DEADCODE
;
; This would have loaded background palette 4 and queue
; writing it to the PPU.
;
; It's not used in the shipped game.
;============================================================================
[d005]UNUSED_PPUBuffer_DrawCommand_WriteBackgroundPalette4:
[d005]a9 04LDA #$04
[d007]20 3b d0JSR Screen_LoadUIPalette
[d00a]4c 90 d0JMP PPUBuffer_WritePalette
;============================================================================
; DEADCODE
;
; This would have loaded the background palette for the
; current screen and queue writing it to the PPU.
;
; It's not used in the shipped game.
;============================================================================
[d00d]UNUSED_WriteCurrentBackgroundPalette:
[d00d]ad d0 03LDA a:Screen_PaletteIndex
[d010]20 3b d0JSR Screen_LoadUIPalette
[d013]4c 90 d0JMP PPUBuffer_WritePalette
;============================================================================
; Draw Command 0x00: Write the screen palette.
;
; This flushes
Screen_PaletteData_Tiles to the PPU,
; writing both the background and sprite palette.
;
; INPUTS:
;
Screen_PaletteData_Tiles:
; The screen palette to write.
;
; OUTPUTS:
;
PPUADDR:
;
PPUDATA:
; The written PPU data.
;
; XREFS:
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfbc]
;============================================================================
[d016]PPUBuffer_DrawCommand_WritePalette:
;
; Write all 32 palettes to 0x3F00.
;
[d016]a9 3fLDA #$3f; 0x3F == Upper byte.
[d018]8d 06 20STA a:PPUADDR; Write as the upper PPU address.
[d01b]a2 00LDX #$00; 0x00 == Lower byte.
[d01d]8e 06 20STX a:PPUADDR; Write as the lower PPU address.
;
; Loop through 32 bytes of palette data and write to the PPU.
;
[d020]a0 20LDY #$20; Y = 32 (loop counter).
[d022]@_paletteLoop:
[d022]bd 93 02LDA a:Screen_PaletteData_Tiles,X; A = Palette byte at the index.
[d025]8d 07 20STA a:PPUDATA; Write to the PPU.
[d028]e8INX; Increment the PPU address.
[d029]88DEY; Y-- (loop counter).
[d02a]d0 f6BNE @_paletteLoop; If > 0, loop.
;
; Avoid palette corruption by setting to address 0x3F00,
; and then zeroing out (0x0000).
;
; See https://www.nesdev.org/wiki/PPU_registers#Palette_corruption
;
[d02c]a9 3fLDA #$3f; 0x3F == Upper byte.
[d02e]8d 06 20STA a:PPUADDR; Write as the upper PPU address.
[d031]8c 06 20STY a:PPUADDR; Zero out the lower byte.
[d034]8c 06 20STY a:PPUADDR; Now zero out the upper byte.
[d037]8c 06 20STY a:PPUADDR; And the lower again.
[d03a]60RTS
[d03b]Screen_LoadUIPalette:
[d03b]a8TAY; Y = A (palette index).
;
; Save the current bank and load bank 11 (palette data).
;
[d03c]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[d03f]48PHA; Push it to the stack.
[d040]a2 0bLDX #$0b
[d042]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Set the attribute data index for the HUD.
;
[d045]b9 f0 81LDA a:BYTE_81f0,Y; Load the HUD attribute data for this index.
[d048]8d 8d 03STA a:UI_AttributeDataIndex; Set it for the HUD/textboxes.
;
; Restore our current bank.
;
[d04b]68PLA; Pop A (previous bank) from the stack.
[d04c]aaTAX; X = A
[d04d]20 1a ccJSR MMC1_UpdateROMBank; Switch back to the bank.
;
; Get the offset into the bank.
;
[d050]98TYA; A = Y (palette index).
[d051]20 9b d0JSR Palette_IndexToROMOffset16; Convert to a relative offset into the bank.
[d054]69 00ADC #$00; Add 0 + C (also 0) to the lower byte.
[d056]85 08STA Temp_08; Store it.
[d058]a5 09LDA Temp_09; Load the computed upper byte.
[d05a]69 80ADC #$80; Add 0x80.
[d05c]85 09STA Temp_09; Store it.
;
; Write that palette into the sprite palette.
;
[d05e]a0 0fLDY #$0f; Start writing into index 16 (sprite palette).
[d060]d0 12BNE Screen_SetPaletteData; Load it.
;
; v-- Fall through --v
;
[d062]Screen_LoadSpritePalette:
[d062]8d d4 03STA a:Palette_SpritePaletteIndex; Set the palette index based on the provided A.
;
; Compute the ROM address for this palette.
;
; This should be 0x81C0 + palette offset.
;
[d065]20 9b d0JSR Palette_IndexToROMOffset16; Convert the relative address for the palette.
[d068]69 c0ADC #$c0; Add 0xC0 to the lower byte.
[d06a]85 08STA Temp_08; Store it.
[d06c]a5 09LDA Temp_09; Load the computed upper byte.
[d06e]69 81ADC #$81; Add 0x81 to it.
[d070]85 09STA Temp_09; Store it back.
;
; Save the previous bank and switch to bank 11 (palette data).
;
[d072]a0 1fLDY #$1f; Y = Destination for the palette (31).
;
; v-- Fall through --v
;
[d074]Screen_SetPaletteData:
;
; Save the current bank.
;
[d074]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[d077]48PHA; Push it to the stack.
[d078]a2 0bLDX #$0b
[d07a]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Copy 16 bytes of palette into ROM.
;
[d07d]98TYA; A = Y (palette data index)
[d07e]aaTAX; X = A
[d07f]a0 0fLDY #$0f; Y = 15 (loop counter)
[d081]@_loadPaletteLoop:
[d081]b1 08LDA (Temp_08),Y; Load the byte to copy.
[d083]9d 93 02STA a:Screen_PaletteData_Tiles,X; Store as the palette data at the index.
[d086]caDEX; X-- (destination index)
[d087]88DEY; Y-- (loop counter)
[d088]10 f7BPL @_loadPaletteLoop; If >= 0, loop.
;
; Switch bank to the previous bank.
;
[d08a]68PLA; Pop the previous bank.
[d08b]aaTAX; X = A
[d08c]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
[d08f]60RTS
[d090]PPUBuffer_WritePalette:
[d090]a9 00LDA #$00; A = 0
[d092]a6 20LDX PPUBuffer_WriteOffset; X = upper bounds of the buffer
[d094]9d 00 05STA a:PPUBuffer,X; Store the 0 in the buffer at the upper bounds.
[d097]e8INX; Increment the upper bounds.
[d098]86 20STX PPUBuffer_WriteOffset
[d09a]60RTS
;============================================================================
; Return a 16-bit offset for a value.
;
; This multiplies the provided index by 16 and splits the
; 16-bit result into a low byte (A) and a high byte
; (
Temp_09).
;
; Carry is cleared on exit so the caller can 16-bit add a
; base address with ADC (low) / ADC (high) cleanly.
;
; This is used by palette code to build a ROM pointer in
; the form of:
;
; PaletteAddr = Base + (index * 16)
;
; INPUTS:
; A:
; The index to convert to a 16-byte offset.
;
; OUTPUTS:
;
Temp_09:
; The high byte (index * 16)
;
; A:
; The low byte (index * 16)
;
; C (Carry):
; Cleared (0) for subsequent 16-bit ADC adds.
;
; Y:
; Set to 0 (clobbered).
;
; XREFS:
;
Screen_LoadSpritePalette
;
Screen_LoadUIPalette
;
Screen_SetFadeOutPalette
;============================================================================
[d09b]Palette_IndexToROMOffset16:
[d09b]a0 00LDY #$00; Y = 0
[d09d]84 09STY Temp_09; Store it temporarily.
[d09f]0aASL A; Shift left, upper bit into Carry.
[d0a0]26 09ROL Temp_09; Rotate left, carry into bit 0, bit 7 into carry.
[d0a2]0aASL A; Repeat 3 more times, moving lower nibble into A, upper nibble into Temp_09.
[d0a3]26 09ROL Temp_09
[d0a5]0aASL A
[d0a6]26 09ROL Temp_09
[d0a8]0aASL A
[d0a9]26 09ROL Temp_09
[d0ab]18CLC; Clear carry.
[d0ac]60RTS; Return A and Temp_09.
[d0ad]Screen_SetFadeOutPalette:
;
; Get an address for the provided palette in bank 11.
;
[d0ad]20 9b d0JSR Palette_IndexToROMOffset16; Convert the palette to a 16-bit integer.
[d0b0]69 00ADC #$00; Add 0 and carry (which is also 0) to the lower byte.
[d0b2]85 08STA Temp_08; Store temporarily as the lower byte.
[d0b4]a5 09LDA Temp_09; Load it back into A.
[d0b6]69 80ADC #$80; Add 0x80.
[d0b8]85 09STA Temp_09; Store as the upper byte.
;
; Save the current bank and switch to bank 11.
;
[d0ba]ad 00 01LDA a:CurrentROMBank; Load the current bank.
[d0bd]48PHA; Push it to the stack.
[d0be]a2 0bLDX #$0b; 11 = Sprite info/palettes bank.
[d0c0]20 1a ccJSR MMC1_UpdateROMBank; Switch to that bank.
[d0c3]ae 30 04LDX a:Screen_FadeOutStage
;
; Beginning looping through the 16 bytes of palette.
;
; This will load the palette for the current screen and
; then subtract a value from a fade-out table
; (
FADE_OUT_DELTA_TABLE) based on the fade-out
; cycle (
Screen_FadeOutStage).
;
[d0c6]a0 0fLDY #$0f; Y = 15 (loop counter).
[d0c8]@_loop:
[d0c8]b1 08LDA (Temp_08),Y; Load the palette data at Y.
[d0ca]38SEC; Set C = 1 so SBC won't subtract C.
[d0cb]fd e0 d0SBC a:FADE_OUT_DELTA_TABLE,X; Subtract the value form the lookup table at the transition index.
[d0ce]b0 02BCS @_setPaletteData; If > 0, jump to use this value as the palette.
;
; The subtraction yielded a palette value <= 0, so
; hard-code to 15.
;
[d0d0]a9 0fLDA #$0f; Set palette = 15.
[d0d2]@_setPaletteData:
[d0d2]99 93 02STA a:Screen_PaletteData_Tiles,Y; Set the palette data to A at index Y.
[d0d5]88DEY; Y--
[d0d6]10 f0BPL @_loop; If >= 0, loop.
;
; Restore the previous bank.
;
[d0d8]68PLA; Pull the previous bank from the stack.
[d0d9]aaTAX; Set in X.
[d0da]20 1a ccJSR MMC1_UpdateROMBank; And switch to that bank.
[d0dd]4c 90 d0JMP PPUBuffer_WritePalette; Append to the PPU buffer to trigger a screen update.
;============================================================================
; Table of values to subtract from a palette to fade-out the current screen.
;
; XREFS:
;
Screen_SetFadeOutPalette
;============================================================================
[d0e0]FADE_OUT_DELTA_TABLE:
[d0e0]10.byte $10; [0]:
[d0e1]20.byte $20; [1]:
[d0e2]30.byte $30; [2]:
[d0e3]40.byte $40; [3]:
[d0e4]Sound_PlayEffect:
[d0e4]8d 36 04STA a:Temp_SoundIDToPlay; Store the provided sound effect to play.
;
; Push X and Y on the stack. They will be clobbered when
; playing the sound.
;
[d0e7]8aTXA; Push X
[d0e8]48PHA
[d0e9]98TYA; Push Y
[d0ea]48PHA
[d0eb]ad 36 04LDA a:Temp_SoundIDToPlay; Load the sound ID we stored.
[d0ee]20 6f f3JSR SoundEffect_SetCurrent; Play the sound.
;
; Pop X and Y from the stack and restore.
;
[d0f1]68PLA; Pop A into Y.
[d0f2]a8TAY
[d0f3]68PLA; Pop A into X.
[d0f4]aaTAX
[d0f5]60RTS
;============================================================================
; TODO: Document Area_ScrollScreenRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_SetupNew
;============================================================================
[d0f6]Area_ScrollScreenRight:
[d0f6]a9 00LDA #$00
[d0f8]85 47STA Screen_Maybe_ScrollHorizDirection
[d0fa]85 6dSTA MaybeUnused_006d
[d0fc]85 47STA Screen_Maybe_ScrollHorizDirection
[d0fe]a9 00LDA #$00
[d100]85 57STA PPU_ScrollY
[d102]85 0cSTA PPU_ScrollX
[d104]85 0dSTA PPU_ScrollScreen
[d106]85 77STA Screen_NextScrollDataHandlerIndex
[d108]85 73STA Screen_ScrollHandlersPending
[d10a]85 74STA Screen_ScrollHorizBlocksLoaded
[d10c]85 75STA Screen_MaybeUnused_0075
[d10e]85 76STA Screen_ScrollHorizAttrsLoaded
[d110]a2 00LDX #$00
[d112]86 0cSTX PPU_ScrollX
[d114]e8INX
[d115]86 0dSTX PPU_ScrollScreen
[d117]a2 01LDX #$01
[d119]20 27 d1JSR Area_LoadScrollData
[d11c]@_loop:
[d11c]20 e7 d2JSR Screen_HandleScroll
[d11f]20 1d d6JSR Screen_RunWriteScrollDataHandler
[d122]a5 54LDA Screen_ScrollDirection
[d124]10 f6BPL @_loop
[d126]60RTS
;============================================================================
; TODO: Document Area_LoadScrollData
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenRight
;
Area_ScrollTo
;============================================================================
[d127]Area_LoadScrollData:
[d127]86 54STX Screen_ScrollDirection
[d129]a5 0dLDA PPU_ScrollScreen
[d12b]85 59STA PPU_ScrollScreenHoriz
[d12d]a5 0cLDA PPU_ScrollX
[d12f]85 45STA Screen_ScrollHorizLoadCounter
[d131]ad 00 01LDA a:CurrentROMBank
[d134]48PHA
[d135]a2 03LDX #$03
[d137]20 1a ccJSR MMC1_UpdateROMBank
[d13a]a9 00LDA #$00
[d13c]85 03STA Temp_Addr_U
[d13e]a5 63LDA Area_CurrentScreen
[d140]0aASL A
[d141]26 03ROL Temp_Addr_U
[d143]0aASL A
[d144]26 03ROL Temp_Addr_U
[d146]18CLC
[d147]65 8aADC CurrentArea_ScrollingDataAddr
[d149]85 02STA Temp_Addr_L
[d14b]a5 03LDA Temp_Addr_U
[d14d]65 8bADC ScrollingData_U
[d14f]85 03STA Temp_Addr_U
[d151]a0 00LDY #$00
[d153]@_loadScrollData:
[d153]b1 02LDA (Temp_Addr_L),Y
[d155]99 66 00STA a:Area_ScreenToTheLeft,Y
[d158]c8INY
[d159]c0 04CPY #$04
[d15b]90 f6BCC @_loadScrollData
[d15d]68PLA
[d15e]aaTAX
[d15f]20 1a ccJSR MMC1_UpdateROMBank
[d162]a6 8cLDX CurrentArea_ROMBank
[d164]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo
;
; Load blocks to the left of the screen.
;
[d167]a5 66LDA Area_ScreenToTheLeft
[d169]20 f6 d1JSR Area_LoadBlocks
[d16c]a2 0fLDX #$0f
[d16e]a0 00LDY #$00
[d170]@_copyLastColumnLeftScreenLoop:
[d170]bd 00 06LDA a:ScreenBuffer,X
[d173]99 e6 03STA a:LastColumnLeftScreen,Y
[d176]8aTXA
[d177]18CLC
[d178]69 10ADC #$10
[d17a]aaTAX
[d17b]c8INY
[d17c]c0 10CPY #$10
[d17e]90 f0BCC @_copyLastColumnLeftScreenLoop
;
; Load blocks to the right of the screen.
;
[d180]a5 67LDA Area_ScreenToTheRight
[d182]20 f6 d1JSR Area_LoadBlocks
[d185]a2 00LDX #$00
[d187]a0 00LDY #$00
[d189]@_copyRowScreenAboveLoop:
[d189]bd 00 06LDA a:ScreenBuffer,X
[d18c]99 f6 03STA a:FirstColumnInRightScreen,Y
[d18f]8aTXA
[d190]18CLC
[d191]69 10ADC #$10
[d193]aaTAX
[d194]c8INY
[d195]c0 10CPY #$10
[d197]90 f0BCC @_copyRowScreenAboveLoop
;
; Load blocks from the screen above.
;
[d199]a5 68LDA Area_ScreenAbove
[d19b]20 f6 d1JSR Area_LoadBlocks; Load blocks from screen above.
[d19e]a2 f0LDX #$f0; X = 240
[d1a0]@_copyRowScreenBelowLoop:
[d1a0]bd d0 05LDA a:$05d0,X
[d1a3]9d 16 03STA a:$0316,X
[d1a6]e8INX; X++
[d1a7]d0 f7BNE @_copyRowScreenBelowLoop
;
; Load blocks from the screen below.
;
[d1a9]a5 69LDA Area_ScreenBelow
[d1ab]20 f6 d1JSR Area_LoadBlocks; Load blocks from screen below.
[d1ae]a2 f0LDX #$f0
[d1b0]@LAB_PRG15_MIRROR__d1b0:
[d1b0]bd 10 05LDA a:$0510,X
[d1b3]9d 26 03STA a:$0326,X
[d1b6]e8INX
[d1b7]d0 f7BNE @LAB_PRG15_MIRROR__d1b0
;
; Load blocks for the current screen.
;
[d1b9]a5 63LDA Area_CurrentScreen
[d1bb]20 f6 d1JSR Area_LoadBlocks
[d1be]20 e7 ccJSR MMC1_RestorePrevROMBank
[d1c1]a5 54LDA Screen_ScrollDirection
[d1c3]c9 02CMP #$02
[d1c5]b0 0fBCS @_VerticalScroll
[d1c7]aaTAX
[d1c8]a9 00LDA #$00
[d1ca]85 b4STA Screen_ScrollPlayerTransitionCounter
[d1cc]a5 a1LDA Player_PosY
[d1ce]85 b3STA Maybe_PlayerY_ForScroll
[d1d0]bd e7 d1LDA a:SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE,X
[d1d3]85 b2STA Maybe_PlayerX_ForScroll
[d1d5]60RTS
[d1d6]@_VerticalScroll:
[d1d6]29 01AND #$01
[d1d8]aaTAX
[d1d9]a5 9eLDA Player_PosX_Block
[d1db]85 b2STA Maybe_PlayerX_ForScroll
[d1dd]a9 00LDA #$00
[d1df]85 b4STA Screen_ScrollPlayerTransitionCounter
[d1e1]bd e9 d1LDA a:SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE_2_,X
[d1e4]85 b3STA Maybe_PlayerY_ForScroll
[d1e6]60RTS
[d1e7]SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE:
[d1e7]00.byte $00; [0]:
[d1e8]f0.byte $f0; [1]:
[d1e9]SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE_2_:
[d1e9]00.byte $00; [2]:
[d1ea]d0.byte $d0; [3]:
;============================================================================
; Fill the screen buffer with 0s.
;
; This will clear 256 bytes of screen data from $0600
; to $06FF.
;
; INPUTS:
; None
;
; OUTPUTS:
;
ScreenBuffer:
; 256 bytes of screen buffer will be cleared.
;
; XREFS:
;
Area_LoadBlocks
;============================================================================
[d1eb]ScreenBuffer_Clear:
[d1eb]a2 00LDX #$00; X = 0
[d1ed]a9 00LDA #$00; A = 0
;
; Begin writing empty blocks.
;
[d1ef]@_loop:
[d1ef]9d 00 06STA a:ScreenBuffer,X; Write 0 to the screen buffer.
[d1f2]e8INX; X = X + 1
[d1f3]d0 faBNE @_loop; If X hasn't wrapped back to 0, loop.
[d1f5]60RTS
;============================================================================
; Load blocks for the given screen from the level data.
;
; This will load the compressed block data for a given
; screen. Data is stored in by using a control byte and
; optional data bytes.
;
; Control bytes dictate where the next block comes from.
; The following are supported:
;
; 0x00: Copy the block from one position to the left
; (or last block on previous row, if at column 0).
;
; 0x01: Copy from the block in the row directly above.
;
; 0x02: Copy from the block directly up and to the left
; (or last block 2 rows up, if at column 0)
;
; 0x03: Read the next value as the block.
;
; INPUTS:
; A:
; The index of the screen to load.
;
;
Area_ScreenBlocksOffset:
; The offset into the blocks data for the current
; screen.
;
; OUTPUTS:
;
ScreenBuffer:
; Updated with loaded block data.
;
;
LoadCompressedScreenData_ByteOffset:
;
LoadCompressedScreenData_BitOffset:
;
Temp_LoadedBlockValue:
;
Temp_LoadedBlocksCount:
;
Temp_08:
;
Temp_09:
; Clobbered.
;
; CALLS:
;
Area_LoadNextCompressedScreenBit
;
ScreenBuffer_Clear
;
; XREFS:
;
Area_LoadScrollData
;============================================================================
[d1f6]Area_LoadBlocks:
[d1f6]c9 ffCMP #$ff; Check if the screen index is set.
[d1f8]f0 f1BEQ ScreenBuffer_Clear; If not, then clear the screen buffer.
;
; We'll be loading blocks for this screen. Calculate
; the address we'll be loading from.
;
[d1fa]0aASL A; Multiply screen index by 2
[d1fb]a8TAY; Y = New screen index offset value
[d1fc]b1 7aLDA (Area_ScreenBlocksOffset),Y; Set the lower block data address for this screen index.
[d1fe]85 08STA Temp_08; Store it.
[d200]c8INY; Y++
[d201]b1 7aLDA (Area_ScreenBlocksOffset),Y; Set the upper block data address for this screen index.
[d203]18CLC
[d204]69 80ADC #$80; Add 0x80 to the upper byte.
[d206]85 09STA Temp_09; Store this as our total CHR page count.
;
; Set our initial state for compressed block loading.
;
[d208]a9 00LDA #$00; A = 0
[d20a]85 5eSTA LoadCompressedScreenData_ByteOffset; Set byte offset to 0.
[d20c]85 5dSTA Temp_LoadedBlocksCount; Set count to 0.
[d20e]85 5fSTA LoadCompressedScreenData_BitOffset; Set bit offset to 0.
;
; Load the control bits for the block. This will dictate
; how loading will proceed.
;
[d210]@_nextBlock:
[d210]a9 00LDA #$00
[d212]85 5cSTA Temp_LoadedBlockValue; Begin loading this block.
;
; Shift our block value left by 2 and add load the next two
; compressed bits into those spots.
;
[d214]20 58 d2JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d217]26 5cROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
[d219]20 58 d2JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d21c]26 5cROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
;
; Check what bits we just loaded.
;
[d21e]a5 5cLDA Temp_LoadedBlockValue; Load the current block value.
[d220]29 03AND #$03; Check the 2 least-significant bits we loaded.
[d222]aaTAX; X = Our loaded bits value
[d223]e0 03CPX #$03; Is the value 3?
[d225]f0 0dBEQ @_read8BitBlock; If so, start a new block.
;
; Control 0x00, 0x01, or 0x02:
;
; Load the block value stored in the screen buffer at
; the current offset + bitValue-specific offset.
;
[d227]a5 5dLDA Temp_LoadedBlocksCount; Else, load our blocks count.
[d229]18CLC
[d22a]7d 73 d2ADC a:BLOCK_DATA_OFFSETS_FOR_BIT_VALUES,X; Add that to the value for this bit in the lookup table.
[d22d]aaTAX; Set as X
[d22e]bd 00 06LDA a:ScreenBuffer,X; Load the block value from the screen buffer at the offset for this bit value.
[d231]4c 44 d2JMP @_storeBlock; Proceed to store the block.
;
; Control 0x03: Read an 8-bit block.
;
[d234]@_read8BitBlock:
[d234]a2 08LDX #$08; X = 8 (total bits)
[d236]a9 00LDA #$00; A = 0 (new block value)
[d238]85 5cSTA Temp_LoadedBlockValue; Store that value.
;
; Load the block data. We'll load 8 bits worth.
;
; This is essentially going to be the same as the byte
; value, except the bits loaded may span multiple bytes.
;
[d23a]@_load8BitsOfBlockData:
[d23a]20 58 d2JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d23d]26 5cROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
[d23f]caDEX; X-- (total bits)
[d240]d0 f8BNE @_load8BitsOfBlockData; If we're not at 0, loop.
[d242]a5 5cLDA Temp_LoadedBlockValue; Else, load the resulting block value.
;
; Store the block value in the screen buffer at the
; current offset.
;
[d244]@_storeBlock:
[d244]a6 5dLDX Temp_LoadedBlocksCount; X = loaded block count, as our new offset
[d246]9d 00 06STA a:ScreenBuffer,X; Store the byte in the screen buffer at that offset.
[d249]e6 5dINC Temp_LoadedBlocksCount; Increment our block count.
[d24b]d0 c3BNE @_nextBlock; If we haven't loaded 256 blocks, loop.
;
; We're done loading the blocks. We now need to
; fill in the last row of 16 blocks with zeroes.
;
[d24d]a2 f0LDX #$f0; X = 0xF0 (offset into the screen buffer to fill)
[d24f]a9 00LDA #$00; A = 0 (value to store)
;
; Fill the last line.
;
[d251]@_fillLastLine:
[d251]9d 00 06STA a:ScreenBuffer,X; Store in the screen buffer.
[d254]e8INX; X++
[d255]d0 faBNE @_fillLastLine; If we haven't hit 16 blocks (value 256 and wrapped around), then loop.
[d257]60RTS
[d258]Area_LoadNextCompressedScreenBit:
[d258]a5 5fLDA LoadCompressedScreenData_BitOffset; Load the current bit.
[d25a]d0 06BNE @_update; Is this 0? If so, branch.
;
; We're at the first bit. We need to load the byte we'll be
; working with from RAM as an index into
Temp_07+1.
;
[d25c]a4 5eLDY LoadCompressedScreenData_ByteOffset; Get the offset of the byte we'll be processing.
[d25e]b1 08LDA (Temp_08),Y; Load the next byte.
[d260]85 60STA LoadCompressedScreenData_CurByte
;
; Shift the current byte left by 1 and increment the bit
; offset by 1, for comparison.
;
[d262]@_update:
[d262]06 60ASL LoadCompressedScreenData_CurByte; Shift the current byte we're processing.
[d264]08PHP; Save all flags.
[d265]e6 5fINC LoadCompressedScreenData_BitOffset; Increment the next bit in the byte.
;
; See if the lower 3 bits are cleared. If so, we need to
; advance the byte offset and reset the bit offset.
;
[d267]a5 5fLDA LoadCompressedScreenData_BitOffset; Check if we've hit the last bit in the byte.
[d269]29 07AND #$07
[d26b]d0 04BNE @_return; If we have any data in bits 1-3...
[d26d]85 5fSTA LoadCompressedScreenData_BitOffset; Reset the current bit to 0.
[d26f]e6 5eINC LoadCompressedScreenData_ByteOffset; Increase the byte offset.
[d271]@_return:
[d271]28PLP; Restore all flags.
[d272]60RTS
[d273]BLOCK_DATA_OFFSETS_FOR_BIT_VALUES:
[d273]ff.byte $ff; [0]:
[d274]f0.byte $f0; [1]:
[d275]ef.byte $ef; [2]:
;============================================================================
; Load block properties from Bank 3 into RAM.
;
; This will loop through all block properties for the
; current area, loading it into a table in RAM.
;
; Each pair of bytes in the block properties come
; together as the two nibbles for a new byte. The
; first byte represents the lower nibble, and the
; second byte's value is shifted into the upper nibble.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank to save and restore.
;
;
CurrentArea_BlockPropertiesAddr:
; The address of the current area's block
; properties data.
;
; OUTPUTS:
;
BlockProperties:
; The block properties in RAM to update.
;
; CALLS:
;
MMC1_UpdateROMBank
;
; XREFS:
;
Screen_StopScrollAndLoadBlockProperties
;============================================================================
[d276]Area_LoadBlockProperties:
;
; Switch to bank 3, where the level data is stored.
;
[d276]ad 00 01LDA a:CurrentROMBank
[d279]48PHA
[d27a]a2 03LDX #$03
[d27c]20 1a ccJSR MMC1_UpdateROMBank
;
; Prepare for the read loop.
;
[d27f]a0 00LDY #$00; Y = 0
[d281]a2 00LDX #$00; X = 0
;
; Load the lower nibble from the block properties.
;
[d283]@_nextBlockProperty:
[d283]b1 88LDA (CurrentArea_BlockPropertiesAddr),Y; A = block property in the current area at offset Y.
[d285]29 0fAND #$0f; Retain the lower nibble.
[d287]85 00STA Temp_00; Store that for later lookup.
;
; Load the next block property as the upper nibble.
;
[d289]c8INY; Increment the index in the block properties table.;
[d28a]b1 88LDA (CurrentArea_BlockPropertiesAddr),Y; Load it into A.
[d28c]0aASL A; Move the lower nibble to the upper nibble.
[d28d]0aASL A
[d28e]0aASL A
[d28f]0aASL A
;
; Combine the two nibbles for the new value and store it in
; BlockProperties.
;
[d290]05 00ORA Temp_00; OR it to the lower nibble we saved.
[d292]9d 3c 04STA a:BlockProperties,X; Store it.
;
; Keep going while the index is positive.
;
[d295]c8INY; Y = Y + 1
[d296]e8INX; X = X + 1
[d297]10 eaBPL @_nextBlockProperty; If we haven't overflowed, loop.
;
; We're done. Switch back to our previous bank.
;
[d299]68PLA; Pull our saved bank.
[d29a]aaTAX; Set to X and...
[d29b]20 1a ccJSR MMC1_UpdateROMBank; Switch banks.
[d29e]60RTS
[d29f]Screen_StopScrollAndLoadBlockProperties:
[d29f]a9 ffLDA #$ff
[d2a1]85 54STA Screen_ScrollDirection
[d2a3]4c 76 d2JMP Area_LoadBlockProperties
;============================================================================
; TODO: Document Screen_HandleScrollUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d2a6]Screen_HandleScrollUp:
[d2a6]a5 57LDA PPU_ScrollY
[d2a8]d0 02BNE @LAB_PRG15_MIRROR__d2ac
[d2aa]a9 d0LDA #$d0
[d2ac]@LAB_PRG15_MIRROR__d2ac:
[d2ac]38SEC
[d2ad]e9 01SBC #$01
[d2af]85 57STA PPU_ScrollY
[d2b1]d0 05BNE @LAB_PRG15_MIRROR__d2b8
[d2b3]c6 58DEC PPU_ScrollScreenVert
[d2b5]20 9f d2JSR Screen_StopScrollAndLoadBlockProperties
[d2b8]@LAB_PRG15_MIRROR__d2b8:
[d2b8]a5 57LDA PPU_ScrollY
[d2ba]85 44STA Screen_ScrollVertLoadCounter
[d2bc]ad 00 01LDA a:CurrentROMBank
[d2bf]48PHA
[d2c0]a2 03LDX #$03
[d2c2]20 1a ccJSR MMC1_UpdateROMBank
[d2c5]20 93 d3JSR Screen_LoadDataUp
[d2c8]68PLA
[d2c9]aaTAX
[d2ca]20 1a ccJSR MMC1_UpdateROMBank
[d2cd]60RTS
;============================================================================
; TODO: Document Screen_UpdateForScroll
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_UpdateForScroll
;
Screen_UpdateForScroll
;============================================================================
[d2ce]Screen_UpdateForScroll:
[d2ce]20 48 e0JSR Game_UpdatePlayerOnScroll; Update the player position during scroll.
[d2d1]20 e7 d2JSR Screen_HandleScroll; Update the screen state/tiles on scroll.
[d2d4]20 1d d6JSR Screen_RunWriteScrollDataHandler; Write data to the PPU based on the scroll direction.
[d2d7]a5 54LDA Screen_ScrollDirection; Load the scroll direction.
[d2d9]c9 02CMP #$02; Is it left or right?
[d2db]b0 05BCS @_isLeftOrRight
;
; The screen is scrolling up, down, or not at all.
;
[d2dd]a5 0cLDA PPU_ScrollX; Load the scroll X delta.
[d2df]d0 edBNE Screen_UpdateForScroll; If it's not 0, loop.
[d2e1]60RTS
;
; The screen is scrolling left or right.
;
[d2e2]@_isLeftOrRight:
[d2e2]a5 57LDA PPU_ScrollY; Load the scroll Y delta.
[d2e4]d0 e8BNE Screen_UpdateForScroll; If it's not 0, loop.
[d2e6]RETURN_D2E6:
[d2e6]60RTS; Else, return.
;============================================================================
; TODO: Document Screen_HandleScroll
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenRight
;
Game_MainLoop
;
Screen_UpdateForScroll
;============================================================================
[d2e7]Screen_HandleScroll:
[d2e7]a6 54LDX Screen_ScrollDirection; Load the scroll direction.
[d2e9]f0 5fBEQ Screen_HandleScrollLeft; If 0, jump to scroll left.
[d2eb]caDEX
[d2ec]f0 30BEQ Screen_HandleScrollRight; If 1, jump to scroll right.
[d2ee]caDEX
[d2ef]f0 b5BEQ Screen_HandleScrollUp; If 2, jump to scroll up.
[d2f1]caDEX
[d2f2]d0 f2BNE RETURN_D2E6; If anything but 3 (down), return.
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Screen_HandleScrollDown
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;============================================================================
[d2f4]Screen_HandleScrollDown:
;
; The screen is scrolling down.
;
[d2f4]a5 57LDA PPU_ScrollY
[d2f6]18CLC
[d2f7]69 01ADC #$01
[d2f9]85 57STA PPU_ScrollY
[d2fb]c9 d0CMP #$d0
[d2fd]90 09BCC @LAB_PRG15_MIRROR__d308
[d2ff]a9 00LDA #$00
[d301]85 57STA PPU_ScrollY
[d303]e6 58INC PPU_ScrollScreenVert
[d305]20 9f d2JSR Screen_StopScrollAndLoadBlockProperties
[d308]@LAB_PRG15_MIRROR__d308:
[d308]a5 57LDA PPU_ScrollY
[d30a]85 44STA Screen_ScrollVertLoadCounter
[d30c]ad 00 01LDA a:CurrentROMBank
[d30f]48PHA
[d310]a2 03LDX #$03
[d312]20 1a ccJSR MMC1_UpdateROMBank
[d315]20 a6 d3JSR Screen_LoadBlocksDown
[d318]68PLA
[d319]aaTAX
[d31a]20 1a ccJSR MMC1_UpdateROMBank
[d31d]60RTS
;============================================================================
; TODO: Document Screen_HandleScrollRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d31e]Screen_HandleScrollRight:
;
; The screen is scrolling right.
;
[d31e]a5 0cLDA PPU_ScrollX
[d320]18CLC
[d321]69 01ADC #$01
[d323]85 0cSTA PPU_ScrollX
[d325]08PHP
[d326]a5 59LDA PPU_ScrollScreenHoriz
[d328]69 00ADC #$00
[d32a]85 59STA PPU_ScrollScreenHoriz
[d32c]85 0dSTA PPU_ScrollScreen
[d32e]28PLP
[d32f]90 03BCC @LAB_PRG15_MIRROR__d334
[d331]20 9f d2JSR Screen_StopScrollAndLoadBlockProperties
[d334]@LAB_PRG15_MIRROR__d334:
[d334]a5 0cLDA PPU_ScrollX
[d336]85 45STA Screen_ScrollHorizLoadCounter
[d338]ad 00 01LDA a:CurrentROMBank
[d33b]48PHA
[d33c]a2 03LDX #$03
[d33e]20 1a ccJSR MMC1_UpdateROMBank
[d341]20 dc d4JSR Screen_LoadDataRight
[d344]68PLA
[d345]aaTAX
[d346]20 1a ccJSR MMC1_UpdateROMBank
[d349]60RTS
;============================================================================
; TODO: Document Screen_HandleScrollLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d34a]Screen_HandleScrollLeft:
;
; The screen is scrolling left.
;
[d34a]a5 45LDA Screen_ScrollHorizLoadCounter; Load the screen blocks horizontal load counter.
[d34c]c9 fcCMP #$fc; Is it >= 0xFC?
[d34e]b0 14BCS @_checkLoadBlocks; If so, jump.
;
; We haven't loaded 252 columns yet.
;
; Start by reducing the scroll X counter by 1.
;
[d350]a5 0cLDA PPU_ScrollX; Load the scroll X counter.
[d352]38SEC
[d353]e9 01SBC #$01; Subtract 1.
[d355]85 0cSTA PPU_ScrollX; And store it.
[d357]08PHP; Push flags to the stack.
[d358]a5 0dLDA PPU_ScrollScreen; Load the scroll screen we're using.
[d35a]e9 00SBC #$00; Subtract carry from above.
[d35c]85 0dSTA PPU_ScrollScreen; Store it.
[d35e]28PLP; Pop flags from the stack.
[d35f]d0 03BNE @_checkLoadBlocks; If scroll X > 0, jump.
;
;
PPU_ScrollX hit 0, so we can stop scrolling and load
; the block properties for the new screen.
;
[d361]20 9f d2JSR Screen_StopScrollAndLoadBlockProperties; Stop scrolling and load the block properties.
[d364]@_checkLoadBlocks:
[d364]a5 45LDA Screen_ScrollHorizLoadCounter; Load the screen blocks horizontal load counter.
[d366]38SEC
[d367]e9 01SBC #$01; Subtract 1.
[d369]85 45STA Screen_ScrollHorizLoadCounter; Store it.
[d36b]a5 59LDA PPU_ScrollScreenHoriz; Load the index of the screen using for scrolling horizontally.
[d36d]e9 00SBC #$00; Subtract carry, if wrapping above.
[d36f]85 59STA PPU_ScrollScreenHoriz; Store it.
;
; Check if we're ready to load blocks to the left.
;
; This will only load if the screen index has changed or
; the scroll X is >= 4.
;
[d371]c5 0dCMP PPU_ScrollScreen; Is the horizontal scroll screen the same as the current screen?
[d373]d0 07BNE @_loadBlocks; If not, jump to load blocks.
[d375]a5 0cLDA PPU_ScrollX; Load the scroll X position.
[d377]c9 04CMP #$04; Is it >= 4?
[d379]b0 01BCS @_loadBlocks; If so, jump to load blocks.
[d37b]60RTS; Else, we're done.
;
; Save the current ROM bank and switch to bank 3 (Area metadata).
;
[d37c]@_loadBlocks:
[d37c]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[d37f]48PHA; Push it to the stack.
[d380]a2 03LDX #$03; Bank 3 = Area metadata.
[d382]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
;
; Load data for the screen to the left of the current screen.
;
[d385]20 f0 d4JSR Screen_LoadDataLeft; Load data to the left of the current screen.
;
; Switch back to the saved bank.
;
[d388]68PLA; Pull the current bank from the stack.
[d389]aaTAX; X = A (bank).
[d38a]20 1a ccJSR MMC1_UpdateROMBank; Switch back to that bank.
[d38d]60RTS
;============================================================================
; Load block attributes when scrolling vertically.
;
; This sets the loading mode to load attributes, and then
; load a round of blocks vertically.
;
; INPUTS:
; X:
; 0 = Load blocks to the top.
; 1 = Load blocks to the bottom.
;
; OUTPUTS:
;
Screen_ScrollLoadMode:
; The mode set to loading attributes.
;
; CALLS:
;
Screen_LoadBlockDataVert
;
; XREFS:
;
Screen_LoadBlocksDown
;
Screen_LoadDataUp
;============================================================================
[d38e]Screen_LoadAttrsVert:
[d38e]e6 6eINC Screen_ScrollLoadMode; Set load mode to 1 (load attributes)
[d390]4c ba d3JMP Screen_LoadBlockDataVert; Load the data.
;============================================================================
; Load screen data for the screen above.
;
; Every 8 counts (7, 15, 23, ...), this will load
; attribute data.
;
; Every 8 counts (3, 11, 19, ...), this will load
; tile data.
;
; INPUTS:
;
Screen_ScrollVertLoadCounter:
; The load counter for blocks.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_LoadAttrsVert
;
Screen_LoadBlockDataVert
;
; XREFS:
;
Screen_HandleScrollUp
;============================================================================
[d393]Screen_LoadDataUp:
[d393]a2 00LDX #$00; 0 = Load Tiles mode
[d395]86 6eSTX Screen_ScrollLoadMode; Set as the default mode.
[d397]a5 44LDA Screen_ScrollVertLoadCounter; A = Vertical blocks counter
[d399]29 0fAND #$0f; Keep the lower nibble.
[d39b]c9 07CMP #$07; Is it 7 (every 8 blocks, starting at 7)?
[d39d]f0 efBEQ Screen_LoadAttrsVert; If so, load attributes.
[d39f]29 07AND #$07; Else, keep the lower 3 bits.
[d3a1]c9 03CMP #$03; Is it 3 (every 8 blocks, starting at 3)?
[d3a3]f0 15BEQ Screen_LoadBlockDataVert; If so, load tiles.
[d3a5]60RTS; Else, return.
;============================================================================
; Load screen data for the screen below.
;
; Every 16 counts (8, 24, 40, ...), this will load
; attribute data.
;
; Every 8 counts (4, 12, 20, ...), this will load
; tile data.
;
; INPUTS:
;
Screen_ScrollVertLoadCounter:
; The load counter for blocks.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_LoadAttrsVert
;
Screen_LoadBlockDataVert
;
; XREFS:
;
Screen_HandleScrollDown
;============================================================================
[d3a6]Screen_LoadBlocksDown:
[d3a6]a2 00LDX #$00; 0 = Load Tiles mode
[d3a8]86 6eSTX Screen_ScrollLoadMode; Set as the default mode.
[d3aa]e8INX; X++ (reuse as "is down" flag, set to 1).
[d3ab]a5 44LDA Screen_ScrollVertLoadCounter; A = Vertical blocks counter
[d3ad]29 0fAND #$0f; Keep the lower nibble.
[d3af]c9 08CMP #$08; Is it 8 (every 16 blocks, starting at 8)?
[d3b1]f0 dbBEQ Screen_LoadAttrsVert; If so, load attributes.
[d3b3]29 07AND #$07; Else, keep the lower 3 bits.
[d3b5]c9 04CMP #$04; Is it 4 (every 8 blocks, starting at 4)?
[d3b7]f0 01BEQ Screen_LoadBlockDataVert; If so, load tiles.
[d3b9]60RTS; Else, return.
[d3ba]Screen_LoadBlockDataVert:
[d3ba]a5 44LDA Screen_ScrollVertLoadCounter
[d3bc]18CLC
[d3bd]7d cb d4ADC a:BYTE_ARRAY_PRG15_MIRROR__d4cb,X
[d3c0]85 00STA Temp_00
[d3c2]a5 58LDA PPU_ScrollScreenVert
[d3c4]69 00ADC #$00
[d3c6]85 49STA Unused_Blocks_0049
[d3c8]a5 63LDA Area_CurrentScreen
[d3ca]85 6dSTA MaybeUnused_006d
[d3cc]a5 00LDA Temp_00
[d3ce]29 f0AND #$f0
[d3d0]85 48STA Temp_Blocks_0048
[d3d2]a9 00LDA #$00
[d3d4]18CLC
[d3d5]65 48ADC Temp_Blocks_0048
[d3d7]85 08STA Temp_08
[d3d9]a9 06LDA #$06
[d3db]69 00ADC #$00
[d3dd]85 09STA Temp_09
[d3df]a5 6eLDA Screen_ScrollLoadMode
[d3e1]f0 03BEQ @LAB_PRG15_MIRROR__d3e6
[d3e3]4c 45 d4JMP @LAB_PRG15_MIRROR__d445
[d3e6]@LAB_PRG15_MIRROR__d3e6:
[d3e6]a0 00LDY #$00
[d3e8]a2 00LDX #$00
[d3ea]@LAB_PRG15_MIRROR__d3ea:
[d3ea]84 06STY Temp_06
[d3ec]b1 08LDA (Temp_08),Y
[d3ee]a8TAY
[d3ef]b1 80LDA (CurrentArea_BlockData1StartAddr),Y
[d3f1]85 6fSTA CurrentArea_BlockData1CurAddr
[d3f3]b1 82LDA (CurrentArea_BlockData2StartAddr),Y
[d3f5]85 70STA CurrentArea_BlockData2CurAddr
[d3f7]b1 84LDA (CurrentArea_BlockData3StartAddr),Y
[d3f9]85 71STA CurrentArea_BlockData3CurAddr
[d3fb]b1 86LDA (CurrentArea_BlockData4StartAddr),Y
[d3fd]85 72STA CurrentArea_BlockData4CurAddr
[d3ff]a5 44LDA Screen_ScrollVertLoadCounter
[d401]29 08AND #$08
[d403]4aLSR A
[d404]4aLSR A
[d405]a8TAY
[d406]b9 6f 00LDA a:CurrentArea_BlockData1CurAddr,Y
[d409]9d 20 02STA a:DataArray,X
[d40c]b9 70 00LDA a:CurrentArea_BlockData2CurAddr,Y
[d40f]9d 21 02STA a:DataArray_1_,X
[d412]e8INX
[d413]e8INX
[d414]a4 06LDY Temp_06
[d416]c8INY
[d417]c0 10CPY #$10
[d419]90 cfBCC @LAB_PRG15_MIRROR__d3ea
[d41b]a9 00LDA #$00
[d41d]85 4fSTA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d41f]a5 44LDA Screen_ScrollVertLoadCounter
[d421]29 f8AND #$f8
[d423]0aASL A
[d424]26 4fROL Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d426]0aASL A
[d427]26 4fROL Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d429]18CLC
[d42a]69 80ADC #$80
[d42c]85 4eSTA Screen_ScrollVertBlocks_PPUTileMapAddr_L
[d42e]a5 4fLDA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d430]69 00ADC #$00
[d432]85 4fSTA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d434]a5 59LDA PPU_ScrollScreenHoriz
[d436]29 01AND #$01
[d438]0aASL A
[d439]0aASL A
[d43a]09 20ORA #$20
[d43c]05 4fORA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d43e]85 4fSTA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d440]a9 01LDA #$01
[d442]85 73STA Screen_ScrollHandlersPending
[d444]60RTS
[d445]@LAB_PRG15_MIRROR__d445:
[d445]a5 44LDA Screen_ScrollVertLoadCounter
[d447]29 16AND #$16
[d449]4aLSR A
[d44a]4aLSR A
[d44b]4aLSR A
[d44c]85 01STA Temp_01
[d44e]a0 f8LDY #$f8
[d450]a9 00LDA #$00
[d452]@LAB_PRG15_MIRROR__d452:
[d452]99 92 01STA a:Screen_ScrollVert_ZeroData,Y
[d455]c8INY
[d456]d0 faBNE @LAB_PRG15_MIRROR__d452
[d458]@LAB_PRG15_MIRROR__d458:
[d458]84 06STY Temp_06
[d45a]b1 08LDA (Temp_08),Y
[d45c]a8TAY
[d45d]b1 7eLDA (CurrentArea_BlockAttributesAddr),Y
[d45f]85 00STA Temp_00
[d461]a5 06LDA Temp_06
[d463]29 01AND #$01
[d465]05 01ORA Temp_01
[d467]a8TAY
[d468]a5 00LDA Temp_00
[d46a]39 d3 d4AND a:BLOCK_ATTRS_BITMASKS,Y
[d46d]48PHA
[d46e]a5 06LDA Temp_06
[d470]4aLSR A
[d471]a8TAY
[d472]68PLA
[d473]19 8a 02ORA a:Screen_ScrollVertPPUAttrData,Y
[d476]99 8a 02STA a:Screen_ScrollVertPPUAttrData,Y
[d479]a4 06LDY Temp_06
[d47b]c8INY
[d47c]c0 10CPY #$10
[d47e]90 d8BCC @LAB_PRG15_MIRROR__d458
[d480]a2 f0LDX #$f0
[d482]a5 44LDA Screen_ScrollVertLoadCounter
[d484]29 10AND #$10
[d486]f0 02BEQ @LAB_PRG15_MIRROR__d48a
[d488]a2 0fLDX #$0f
[d48a]@LAB_PRG15_MIRROR__d48a:
[d48a]86 06STX Temp_06
[d48c]a5 44LDA Screen_ScrollVertLoadCounter
[d48e]29 e0AND #$e0
[d490]4aLSR A
[d491]4aLSR A
[d492]85 00STA Temp_00
[d494]18CLC
[d495]69 08ADC #$08
[d497]a8TAY
[d498]18CLC
[d499]69 c0ADC #$c0
[d49b]85 52STA Screen_ScrollVertBlocks_PPUAttrAddr_L
[d49d]a5 59LDA PPU_ScrollScreenHoriz
[d49f]29 01AND #$01
[d4a1]aaTAX
[d4a2]0aASL A
[d4a3]0aASL A
[d4a4]09 23ORA #$23
[d4a6]85 53STA Screen_ScrollVertBlocks_PPUAttrAddr_U
[d4a8]bd cf d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_L,X
[d4ab]85 08STA Temp_08
[d4ad]bd d1 d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_U,X
[d4b0]85 09STA Temp_09
[d4b2]a2 00LDX #$00
[d4b4]@LAB_PRG15_MIRROR__d4b4:
[d4b4]b1 08LDA (Temp_08),Y
[d4b6]25 06AND Temp_06
[d4b8]1d 8a 02ORA a:Screen_ScrollVertPPUAttrData,X
[d4bb]91 08STA (Temp_08),Y
[d4bd]9d 8a 02STA a:Screen_ScrollVertPPUAttrData,X
[d4c0]c8INY
[d4c1]e8INX
[d4c2]e0 08CPX #$08
[d4c4]90 eeBCC @LAB_PRG15_MIRROR__d4b4
[d4c6]a9 01LDA #$01
[d4c8]85 75STA Screen_MaybeUnused_0075
[d4ca]60RTS
[d4cb]BYTE_ARRAY_PRG15_MIRROR__d4cb:
[d4cb]00.byte $00; [0]:
[d4cc]00.byte $00; [1]:
[d4cd]00 08.byte $00,$08; byte
[d4cf]SET_BLOCKS_TILEMAP_OFFSETS_L:
[d4cf]42.byte $42; [0]:
[d4d0]42.byte $42; [1]:
[d4d1]SET_BLOCKS_TILEMAP_OFFSETS_U:
[d4d1]02.byte $02; [0]:
[d4d2]02.byte $02; [1]:
;============================================================================
; Block attribute masks during screen scrolling.
;
; This is used for both vertical and horizontal scrolling.
;
; XREFS:
;
Screen_LoadBlockDataVert
;
Screen_LoadBlocksHoriz
;============================================================================
[d4d3]BLOCK_ATTRS_BITMASKS:
[d4d3]03.byte $03; [0]: Mask bits 0 and 1
[d4d4]0c.byte $0c; [1]: Mask bits 2 and 3
[d4d5]30.byte $30; [2]: Mask bits 4 and 5
[d4d6]c0.byte $c0; [3]: Mask bits 6 and 7
;============================================================================
; Load block attributes when scrolling horizontally.
;
; This sets the loading mode to load attributes, and then
; load a round of blocks horizontally.
;
; INPUTS:
; X:
; 0 = Load blocks to the left.
; 1 = Load blocks to the right.
;
; OUTPUTS:
;
Screen_ScrollLoadMode:
; The mode set to loading attributes.
;
; CALLS:
;
Screen_LoadBlocksHoriz
;
; XREFS:
;
Screen_LoadDataLeft
;
Screen_LoadDataRight
;============================================================================
[d4d7]Screen_LoadAttrsHoriz:
[d4d7]e6 6eINC Screen_ScrollLoadMode; Set load mode to 1 (load attributes).
[d4d9]4c 03 d5JMP Screen_LoadBlocksHoriz; Load the data.
;============================================================================
; Load screen data for the screen to the right.
;
; Every 4 counts (2, 6, 10, ...), this will load
; attribute data.
;
; Every 4 counts (1, 5, 9, ...), this will load
; tile data.
;
; INPUTS:
;
Screen_ScrollHorizLoadCounter:
; The load counter for blocks.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_LoadAttrsHoriz
;
Screen_LoadBlocksHoriz
;
; XREFS:
;
Screen_HandleScrollRight
;============================================================================
[d4dc]Screen_LoadDataRight:
[d4dc]a2 00LDX #$00; 0 = Load Tiles mode
[d4de]86 6eSTX Screen_ScrollLoadMode; Set as the default mode.
[d4e0]e8INX; X++ (reuse as "is right" flag, set to 1).
[d4e1]a5 45LDA Screen_ScrollHorizLoadCounter; A = Horizontal blocks counter
[d4e3]29 0fAND #$0f; Keep the lower nibble.
[d4e5]c9 02CMP #$02; Is it 2 (every 4 blocks, starting at 2)?
[d4e7]f0 eeBEQ Screen_LoadAttrsHoriz; If so, load attributes.
[d4e9]29 07AND #$07; Else, keep the lower 3 bits.
[d4eb]c9 01CMP #$01; Is it 1 (every 4 blocks, starting at 1)?
[d4ed]f0 14BEQ Screen_LoadBlocksHoriz; If so, load tiles.
[d4ef]60RTS; Else, return.
;============================================================================
; Load screen data for the screen to the left.
;
; Every 16 counts (values 15, 31, 47, ...), this will load
; attribute data.
;
; Every 8 counts (values 6, 14, 22, ...), this will load
; tile data.
;
; INPUTS:
;
Screen_ScrollHorizLoadCounter:
; The load counter for blocks.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_LoadAttrsHoriz
;
Screen_LoadBlocksHoriz
;
; XREFS:
;
Screen_HandleScrollLeft
;============================================================================
[d4f0]Screen_LoadDataLeft:
[d4f0]a2 00LDX #$00; 0 = Load Tiles mode
[d4f2]86 6eSTX Screen_ScrollLoadMode; Set as the default mode.
[d4f4]a5 45LDA Screen_ScrollHorizLoadCounter; A = Horizontal blocks counter
[d4f6]29 0fAND #$0f; Keep the lower nibble.
[d4f8]c9 0fCMP #$0f; Is it 0xF (every 16, starting at 15)?
[d4fa]f0 dbBEQ Screen_LoadAttrsHoriz; If so, load attributes.
[d4fc]29 07AND #$07; Else, keep the lower 3 bits.
[d4fe]c9 06CMP #$06; Is it 6 (every 8 blocks, starting at 6)?
[d500]f0 01BEQ Screen_LoadBlocksHoriz; If so, load tiles.
[d502]60RTS; Else, return.
;============================================================================
; TODO: Document Screen_LoadBlocksHoriz
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_LoadAttrsHoriz
;
Screen_LoadDataLeft
;
Screen_LoadDataRight
;============================================================================
[d503]Screen_LoadBlocksHoriz:
;
; Store a copy of the load counter, and set carry if scrolling
; right.
;
; This will add from
;
SCREEN_SCROLL_HORIZ_COUNTER_INCS, but this won't
; measurably change the value. It adds 0 when scrolling left
; (no-op) and 0xFF when scrolling right (no change, but sets
; carry).
;
[d503]a5 45LDA Screen_ScrollHorizLoadCounter; Load the scrolling counter.
[d505]18CLC
[d506]7d 19 d6ADC a:SCREEN_SCROLL_HORIZ_COUNTER_INCS,X; Add a value to increment by. This won't effectively change the value, but will set carry if scrolling right.
[d509]85 00STA Temp_00; Store the load counter in temp.
;
; Update the horizontal screen scroll amount.
;
; This will add any carry to it from the counter. It also
; appears to add from a table, but the table is all 0 values,
; so that does nothing special.
;
[d50b]a5 59LDA PPU_ScrollScreenHoriz; Load the PPU horizontal screen offset.
[d50d]7d 1b d6ADC a:SCREEN_SCROLL_HORIZ_DELTAS,X; Add carry (no value from the table -- all empty).
[d510]85 47STA Screen_Maybe_ScrollHorizDirection; Store it.
[d512]a5 63LDA Area_CurrentScreen; Load the current screen.
[d514]85 6dSTA MaybeUnused_006d; DEADCODE: Store it (but this is never used).
;
; Load the scroll counter value, divide by 16, and set as
; the load offset.
;
[d516]a5 00LDA Temp_00; Load the counter value.
[d518]4aLSR A; Divide by 16.
[d519]4aLSR A
[d51a]4aLSR A
[d51b]4aLSR A
[d51c]85 46STA Screen_ScrollHorizLoadOffset; Store as the new horizontal load offset.
;
; Set an address to read block data from.
;
; This will be $600.
;
[d51e]a9 00LDA #$00; Lower byte == 0.
[d520]85 08STA Temp_08; Store it.
[d522]a9 06LDA #$06; Upper byte == 6.
[d524]85 09STA Temp_09; Store it.
;
; Choose the operation based on the current load mode:
;
; 0x00: Load block data.
; 0x01: Draw to the screen.
;
[d526]a5 6eLDA Screen_ScrollLoadMode; Check the load/draw mode.
[d528]d0 5dBNE @_drawBlocks; If 0, jump to draw.
;
; We're in the block loading mode.
;
; Load 30 blocks to render in the next draw phase.
;
[d52a]a2 00LDX #$00; X = 0 (loop counter)
;
; Determine the offset for the blocks to load.
;
; This is data populated by
Area_LoadBlocks.
;
[d52c]@_loadBlocksLoop:
[d52c]a4 46LDY Screen_ScrollHorizLoadOffset; Y = Load offset.
[d52e]b1 08LDA (Temp_08),Y; A = Block data at $600 + Y.
;
; Load the blocks information.
;
[d530]a8TAY; Y = A (block data).
[d531]b1 80LDA (CurrentArea_BlockData1StartAddr),Y; A = Block data 1 address at Y.
[d533]85 6fSTA CurrentArea_BlockData1CurAddr; Store it for processing.
[d535]b1 84LDA (CurrentArea_BlockData3StartAddr),Y; A = Block data 3 address at Y.
[d537]85 70STA CurrentArea_BlockData2CurAddr; Store it for processing.
[d539]b1 82LDA (CurrentArea_BlockData2StartAddr),Y; A = Block data 2 address at Y.
[d53b]85 71STA CurrentArea_BlockData3CurAddr; Store it for processing.
[d53d]b1 86LDA (CurrentArea_BlockData4StartAddr),Y; A = Block data 4 address at Y.
[d53f]85 72STA CurrentArea_BlockData4CurAddr; Store it for processing.
;
; Convert the block counter to a tile width and use it
; to determine which part of the block data to load.
;
; Every 8 ticks of the counter, blocks will load. This
; will convert to a value of 0 or 8, and then divide by
; 4 to convert to a block loading index of 0 or 2.
;
; That will be our offset into the loaded block data.
;
; We'll either be working off of block data lists (1, 2)
; or (3, 4). This loads a column of tiles in the block
; at a time.
;
[d541]a5 45LDA Screen_ScrollHorizLoadCounter; A = Horizontal load counter.
[d543]29 08AND #$08; Every 8...
[d545]4aLSR A; Divide by 4 (convert to 0 or 2).
[d546]4aLSR A
[d547]a8TAY; Y = result.
[d548]b9 6f 00LDA a:CurrentArea_BlockData1CurAddr,Y; A = Block data 1 (top-left) or 3 (top-right).
[d54b]9d 00 02STA a:Temp_0200,X; Queue for loading into the PPU.
[d54e]b9 70 00LDA a:CurrentArea_BlockData2CurAddr,Y; A = Block data 2 (bottom-left) or 4 (bottom-right).
[d551]9d 01 02STA a:Temp_0201,X; Queue for loading into the PPU.
[d554]a5 08LDA Temp_08; Load the dest screen buffer address.
[d556]18CLC
[d557]69 10ADC #$10; Increment by 16.
[d559]85 08STA Temp_08; Store the lower byte of the new address.
[d55b]a5 09LDA Temp_09; Load the upper byte.
[d55d]69 00ADC #$00; Add carry, if wrapped.
[d55f]85 09STA Temp_09; Store the upper byte.
;
; Advance the loop counter by 2.
;
[d561]e8INX; X++ (loop counter).
[d562]e8INX; X++ (loop counter).
[d563]e0 1eCPX #$1e
[d565]90 c5BCC @_loadBlocksLoop
[d567]a9 01LDA #$01
[d569]85 74STA Screen_ScrollHorizBlocksLoaded
[d56b]98TYA
[d56c]4aLSR A
[d56d]48PHA
[d56e]a5 46LDA Screen_ScrollHorizLoadOffset
[d570]0aASL A
[d571]85 46STA Screen_ScrollHorizLoadOffset
[d573]68PLA
[d574]18CLC
[d575]65 46ADC Screen_ScrollHorizLoadOffset
[d577]18CLC
[d578]69 80ADC #$80
[d57a]85 4cSTA Screen_ScrollHorizBlocks_PPUTileMapAddr_L
[d57c]a5 47LDA Screen_Maybe_ScrollHorizDirection
[d57e]29 01AND #$01
[d580]0aASL A
[d581]0aASL A
[d582]09 20ORA #$20
[d584]85 4dSTA Screen_ScrollHorizBlocks_PPUTileMapAddr_U
[d586]60RTS
;
; Clear 8 bytes of memory.
;
; This isn't used for any data management purposes. It
; may be to delay some aspect of screen scrolling.
;
[d587]@_drawBlocks:
[d587]a0 f8LDY #$f8; Y = 0xF8 (loop counter)
[d589]a9 00LDA #$00; A = 0 (value to write)
[d58b]@_clearLoop:
[d58b]99 8a 01STA a:Screen_ScrollHoriz_ZeroData,Y; Write a 0 to this empty block.
[d58e]c8INY; Y++
[d58f]d0 faBNE @_clearLoop; If != 0, loop.
;
; Begin loading PPU attribute data and scheduling to draw.
;
[d591]a2 00LDX #$00; X = 0 (loop counter)
;
; Start by storing the data load offset and the block
; attribute value from area data, storing in temp.
;
; The screen buffer data will contain the offset into the
; block attributes data.
;
[d593]@_loadAttrDataLoop:
[d593]a4 46LDY Screen_ScrollHorizLoadOffset; Y = Attr data load offset
[d595]b1 08LDA (Temp_08),Y; A = Screen buffer data at offset (block attribute offset)
[d597]a8TAY; Y = A
[d598]b1 7eLDA (CurrentArea_BlockAttributesAddr),Y; A = block attribute at resulting screen buffer data offset value
[d59a]85 00STA Temp_00; Store it temporarily.
;
; Generate an index 0-3 into a lookup table. This will be:
;
; 0 = Bits 0-1 -- Even loop counter, even load offset
; 1 = Bits 2-3 -- Even loop counter, odd load offset
; 2 = Bits 4-5 -- Odd loop counter, even load offset
; 3 = Bits 6-7 -- Odd loop counter, odd load offset
;
[d59c]a5 46LDA Screen_ScrollHorizLoadOffset; A = Attr data load offset
[d59e]29 01AND #$01; Keep the least-significant bit (even/odd).
[d5a0]85 01STA Temp_01; Store it temporarily.
[d5a2]8aTXA; A = X (loop counter)
[d5a3]29 01AND #$01; Keep the loop counter's least-significant bit (even/odd).
[d5a5]0aASL A; A *= 2
[d5a6]05 01ORA Temp_01; OR with the load offset's even/odd bit.
[d5a8]a8TAY; Y = A (result)
;
; Mask 2 bits of the block attributes based on that index.
;
[d5a9]a5 00LDA Temp_00; A = Loaded block attribute
[d5ab]39 d3 d4AND a:BLOCK_ATTRS_BITMASKS,Y; AND with the bitmask based on our computed index.
[d5ae]48PHA; Push the result to the stack.
;
; Generate the destination index into the PPU attribute data.
;
; This will be the loop counter / 2.
;
[d5af]8aTXA; A = X (loop counter)
[d5b0]4aLSR A; A = A / 2
[d5b1]a8TAY; Y = A
;
; OR the result with the value already loaded at this loop
; index, and store it back.
;
[d5b2]68PLA; Pop the masked block attribute from stack.
[d5b3]19 82 02ORA a:Screen_ScrollHorizPPUAttrData,Y; OR with the data already set.
[d5b6]99 82 02STA a:Screen_ScrollHorizPPUAttrData,Y; And store it.
;
; Advance the screen buffer address by 16 bytes.
;
[d5b9]a5 08LDA Temp_08; A = Lower byte of Current screen buffer address.
[d5bb]18CLC
[d5bc]69 10ADC #$10; A += 16
[d5be]85 08STA Temp_08; Store as the new lower byte.
[d5c0]a5 09LDA Temp_09; A = Upper byte of Current screen buffer address.
[d5c2]69 00ADC #$00; A += Carry from lower increment
[d5c4]85 09STA Temp_09; Store as the new upper byte.
;
; Advance the loop counter, and loop if < 15.
;
[d5c6]e8INX; X++ (loop counter)
[d5c7]e0 0fCPX #$0f; Is this < 15?
[d5c9]90 c8BCC @_loadAttrDataLoop; If so, loop.
;
; Generate a bitmask that will be ANDed to screen
; buffer data and OR'd back to scroll PPU attribute
; data futher below.
;
[d5cb]a2 ccLDX #$cc; Default bitmask to enable bits 2-3, 6-7 (11001100)
[d5cd]a5 46LDA Screen_ScrollHorizLoadOffset; Load the data load offset.
[d5cf]29 01AND #$01; Is it even?
[d5d1]f0 02BEQ @_updatePPUAttrAddr; If so, jump to skip.
[d5d3]a2 33LDX #$33; Set bitmask to enable bits 0-1, 4-5 (00110011)
[d5d5]@_updatePPUAttrAddr:
[d5d5]86 06STX Temp_06; Store for later.
[d5d7]a5 46LDA Screen_ScrollHorizLoadOffset; A = Data load offset.
[d5d9]4aLSR A; A = A / 2
[d5da]18CLC
[d5db]69 08ADC #$08; A += 8
[d5dd]a8TAY; Y = A (new screen buffer write offset)
[d5de]18CLC
[d5df]69 c0ADC #$c0; A += 0xC0
[d5e1]85 50STA Screen_ScrollHorizBlocks_PPUAttrAddr_L; Set as the lower byte of the PPU attr address.
[d5e3]a5 47LDA Screen_Maybe_ScrollHorizDirection
[d5e5]29 01AND #$01
[d5e7]aaTAX
[d5e8]0aASL A
[d5e9]0aASL A
[d5ea]09 23ORA #$23
[d5ec]85 51STA Screen_ScrollHorizBlocks_PPUAttrAddr_U; Set as the upper byte of the PPU attr address.
[d5ee]bd cf d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_L,X
[d5f1]85 08STA Temp_08
[d5f3]bd d1 d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_U,X
[d5f6]85 09STA Temp_09
;
; Update 64 blocks of PPU attribute data.
;
; This will OR the bitmask above to each of the 64
; bytes of PPU attribute data already computed.
;
[d5f8]a2 00LDX #$00; X = 0 (loop counter)
[d5fa]@_updatePPUAttrDataLoop:
[d5fa]b1 08LDA (Temp_08),Y; Load the value from the screen buffer write offset at Y.
[d5fc]25 06AND Temp_06; AND with the bitmask computed above.
[d5fe]1d 82 02ORA a:Screen_ScrollHorizPPUAttrData,X; OR with the existing PPU attribute data
[d601]9d 82 02STA a:Screen_ScrollHorizPPUAttrData,X; And store it.
[d604]91 08STA (Temp_08),Y; Store it back in the screen buffer at write offset Y.
[d606]e8INX; X++ (loop counter)
;
; The next three instructions are effectively unused.
; Legacy code?
;
[d607]8aTXA; <unused>
[d608]29 07AND #$07; <unused>
[d60a]8aTXA; <unused>
;
; Advance the earlier loop counter (which should start at 15)
; by 8 each loop iteration.
;
[d60b]98TYA; A = Y (loop counter from initial attribute loading)
[d60c]18CLC
[d60d]69 08ADC #$08; A += 8
[d60f]a8TAY; Y = A
[d610]c0 40CPY #$40; Is it < 64?
[d612]90 e6BCC @_updatePPUAttrDataLoop; If so, loop.
;
; Mark the attribute data as loaded.
;
[d614]a9 01LDA #$01
[d616]85 76STA Screen_ScrollHorizAttrsLoaded; Set attribute data loaded.
[d618]60RTS
;============================================================================
; A table mapping horizontal scroll directions to counter increments.
;
; When scrolling left, there's no increment.
;
; When scrolling right, it increments a full 0xFF, keeping
; the value the same but setting the carry flag.
;
; XREFS:
;
Screen_LoadBlocksHoriz
;============================================================================
[d619]SCREEN_SCROLL_HORIZ_COUNTER_INCS:
[d619]00.byte $00; [0]:
[d61a]ff.byte $ff; [1]:
;============================================================================
; A table of deltas to add to the horizontal scroll offset.
;
; NOTE: All values are 0. This may be a remnant of an earlier
; design. Changing values here largely seems to do
; nothing, but if you set to 0xFF you'll end up with
; some incorrect screens loaded.
;
; XREFS:
;
Screen_LoadBlocksHoriz
;============================================================================
[d61b]SCREEN_SCROLL_HORIZ_DELTAS:
[d61b]00.byte $00; [0]:
[d61c]00.byte $00; [1]:
;============================================================================
; Run a handler used for the next stage of screen scroll.
;
; This will check which handlers have been scheduled to run
; during screen scroll and run the handlers in the order
; found.
;
; Handlers are built to write a single row or column of
; tiles or set a row or column of PPU attributes during
; screen scroll.
;
; This is designed to store state from one call to another.
; Once a handler is found to run, state will be stored on
; the index of the next handler to check against the next
; time this is called. That reduces how many iterations are
; required during screen scroll.
;
; Callers set flags in a mapping table
; (
Screen_ScrollHandlersPending) to mark which they need to
; run for this operation. Only these will be considered.
;
; INPUTS:
;
Screen_ScrollHandlersPending:
; The table of scroll handlers the callers have
; requested to run for the scrolling process.
;
;
Screen_NextScrollDataHandlerIndex:
; The index of the next scroll handler in the
; table to begin checking in the loop.
;
;
SCREEN_WRITESCROLL_HANDLERS_U:
;
SCREEN_WRITESCROLL_HANDLERS_L:
; The table of available scroll handlers.
;
; OUTPUTS:
;
Temp_06:
; Clobbered.
;
; CALLS:
; The resulting handler.
;
; XREFS:
;
Area_ScrollScreenRight
;
PPU_HandleOnInterrupt
;
Screen_UpdateForScroll
;============================================================================
[d61d]Screen_RunWriteScrollDataHandler:
;
; Begin looking through our handler tables from last handler run
; to last in the table.
;
; This will start at
Screen_NextScrollDataHandlerIndex
; (which may have been set from a last call to this function)
; and begin checking the table from that point, wrapping the
; index back around to 0 as it gets to the end of the table if
; necessary.
;
; Up to 4 entries will be checked.
;
; If starting from index 0, this will end up running tile
; handlers before attribute handlers, if both are requested
; during a screen scroll.
;
[d61d]a9 03LDA #$03; 3 = Last entry in handler table.
[d61f]85 06STA Temp_06; Set as our lookup index.
[d621]a6 77LDX Screen_NextScrollDataHandlerIndex; Load the handler scheduled to check next during this process.
[d623]@_loop:
[d623]b5 73LDA Screen_ScrollHandlersPending,X; Check if this handler needs to be run.
[d625]d0 0aBNE @_runHandler; If so, run the handler.
;
; Check the next entry in the tables.
;
[d627]e8INX; X++ (handler index).
[d628]8aTXA; A = incremented handler index.
[d629]29 03AND #$03; Cap to a valid entry in the tables (wrapping if needed).
[d62b]aaTAX; X = resulting index.
[d62c]c6 06DEC Temp_06; Decrement our lookup index.
[d62e]10 f3BPL @_loop; If >= 0, loop.
[d630]60RTS
;
; The handler needs to be run. Clear the pending state and
; queue running the handler.
;
[d631]@_runHandler:
[d631]a9 00LDA #$00; A = 0 (clear the pending state).
[d633]95 73STA Screen_ScrollHandlersPending,X; Clear the pending flag.
[d635]86 06STX Temp_06; Store the handler index.
;
; Set the next handler in the table to check the next time
; this is called, so we don't have to loop through the
; whole table.
;
[d637]e8INX; X++ (next index).
[d638]8aTXA; A = resulting index.
[d639]29 03AND #$03; Cap to a valid entry in the tables.
[d63b]aaTAX; X = A (resulting capped handler index).
[d63c]86 77STX Screen_NextScrollDataHandlerIndex; Store as the currently-running handler index.
;
; Load the index of the current handler to run.
;
[d63e]a5 06LDA Temp_06; Load the handler index.
[d640]29 03AND #$03; Cap to a valid entry in the table.
[d642]aaTAX; X = resulting index.
;
; Load the address of the handler and put it on the stack
; to run it.
;
[d643]bd 50 d6LDA a:SCREEN_WRITESCROLL_HANDLERS_U,X; Load the upper byte of the handler address.
[d646]48PHA; Push to the stack.
[d647]bd 4c d6LDA a:SCREEN_WRITESCROLL_HANDLERS_L,X; Load the lower byte of the handler address.
[d64a]48PHA; Push to the stack.
[d64b]60RTS; And run it.
;============================================================================
; Lower byte address table for screen scroll handlers.
;
; These cover both the block tile writes and the PPU attributes
; writes.
;
; XREFS:
;
Screen_RunWriteScrollDataHandler
;============================================================================
[d64c]SCREEN_WRITESCROLL_HANDLERS_L:
[d64c]53.byte <(Screen_WriteBlockTilesRowToPPU-1); [0]:
[d64d]72.byte <(Screen_WriteBlockTilesColumnToPPU-1); [1]:
[d64e]98.byte <(Screen_WriteBlockAttrsRowToPPU-1); [2]:
[d64f]b0.byte <(Screen_WriteBlockAttrsColumnToPPU-1); [3]:
;============================================================================
; Upper byte address table for screen scroll handlers.
;
; These cover both the block tile writes and the PPU attributes
; writes.
;
; XREFS:
;
Screen_RunWriteScrollDataHandler
;============================================================================
[d650]SCREEN_WRITESCROLL_HANDLERS_U:
[d650]d6.byte >(Screen_WriteBlockTilesRowToPPU-1); [0]:
[d651]d6.byte >(Screen_WriteBlockTilesColumnToPPU-1); [1]:
[d652]d6.byte >(Screen_WriteBlockAttrsRowToPPU-1); [2]:
[d653]d6.byte >(Screen_WriteBlockAttrsColumnToPPU-1); [3]:
[d654]Screen_WriteBlockTilesRowToPPU:
;
; Tell the PPU we want to write 1 byte at a time, going across.
;
[d654]a5 0aLDA PPU_ControlFlags; Load the current PPU control flags.
[d656]29 fbAND #$fb; Switch to add 1, going across.
[d658]8d 00 20STA a:PPUCTRL; Write it to the PPU.
;
; Set the address in the PPU where tiles should be written to.
;
[d65b]a5 4fLDA Screen_ScrollVertBlocks_PPUTileMapAddr_U; Load the upper byte of the tilemap address to write to.
[d65d]8d 06 20STA a:PPUADDR; Write as the upper byte for the data.
[d660]a5 4eLDA Screen_ScrollVertBlocks_PPUTileMapAddr_L; Load the lower byte.
[d662]8d 06 20STA a:PPUADDR; Write as the lower byte.
;
; Begin our tile write loop.
;
; This will write up to 32 tiles (width of the screen).
;
[d665]a2 00LDX #$00; X = 0 (loop counter).
[d667]@_loop:
[d667]bd 20 02LDA a:DataArray,X; Load a tile from the queue.
[d66a]8d 07 20STA a:PPUDATA; Write it to the PPU.
[d66d]e8INX; X++ (loop counter).
[d66e]e0 20CPX #$20; Are we under 32 columns?
[d670]90 f5BCC @_loop; If so, loop.
[d672]60RTS
[d673]Screen_WriteBlockTilesColumnToPPU:
;
; Tell the PPU we want to write 32 bytes at a time, going down.
;
[d673]a5 0aLDA PPU_ControlFlags; Load the current PPU control flags.
[d675]09 04ORA #$04; Switch to add 32, going down.
[d677]8d 00 20STA a:PPUCTRL; Write it to the PPU.
;
; Set the address in the PPU where tiles should be written to.
;
[d67a]a5 4dLDA Screen_ScrollHorizBlocks_PPUTileMapAddr_U; Load the upper byte of the tilemap address to write to.
[d67c]8d 06 20STA a:PPUADDR; Write as the upper byte for the data.
[d67f]a5 4cLDA Screen_ScrollHorizBlocks_PPUTileMapAddr_L; Load the lower byte.
[d681]8d 06 20STA a:PPUADDR; Write as the lower byte.
;
; Begin our tile write loop.
;
; This will write up to 26 tiles (height of the screen).
;
[d684]a2 00LDX #$00; X = 0 (loop counter).
[d686]@_loop:
[d686]bd 00 02LDA a:Temp_0200,X; Load a tile from the block queue.
[d689]8d 07 20STA a:PPUDATA; Write it to the PPU.
[d68c]e8INX; X++ (loop counter).
[d68d]e0 1aCPX #$1a; Are we under 26 rows?
[d68f]90 f5BCC @_loop; If so, loop.
;
; Restore the PPU control flags.
;
[d691]a5 0aLDA PPU_ControlFlags; Load the current PPU control flags.
[d693]29 fbAND #$fb; Switch back to add 1, going across.
[d695]8d 00 20STA a:PPUCTRL; Write it to the PPU.
[d698]60RTS
[d699]Screen_WriteBlockAttrsRowToPPU:
;
; Set the address in the PPU where attributes should be
; written to.
;
[d699]a5 53LDA Screen_ScrollVertBlocks_PPUAttrAddr_U; Load the upper byte of the attribute address to write to.
[d69b]8d 06 20STA a:PPUADDR; Write as the upper byte for the attributes.
[d69e]a5 52LDA Screen_ScrollVertBlocks_PPUAttrAddr_L; Load the lower byte.
[d6a0]8d 06 20STA a:PPUADDR; Write as the lower byte.
;
; Begin our attribute write loop.
;
; This will write up to 8 attributes in a single row.
;
[d6a3]a2 00LDX #$00; X = 0 (loop counter).
[d6a5]@_loop:
[d6a5]bd 8a 02LDA a:Screen_ScrollVertPPUAttrData,X; Load the attribute to write.
[d6a8]8d 07 20STA a:PPUDATA; Write it to the PPU.
[d6ab]e8INX; X++ (loop counter).
[d6ac]e0 08CPX #$08; Are we under 8 attribute columns?
[d6ae]90 f5BCC @_loop; If so, loop.
[d6b0]60RTS
[d6b1]Screen_WriteBlockAttrsColumnToPPU:
;
; Begin our attribute write loop.
;
; This will write up to 7 attributes one row at a time along
; the same column.
;
; The first row of attributes in the PPU is skipped, since it's
; not part Of screen data.
;
[d6b1]a2 00LDX #$00; X = 0 (loop counter).
[d6b3]@_loop:
[d6b3]a5 51LDA Screen_ScrollHorizBlocks_PPUAttrAddr_U; Load the upper byte of the attribute address to write to.
[d6b5]8d 06 20STA a:PPUADDR; Write as the upper byte for the attributes.
[d6b8]8aTXA; A = loop counter.
[d6b9]0aASL A; Multiply by 8 (attributes per row) to get the target row.
[d6ba]0aASL A
[d6bb]0aASL A
[d6bc]18CLC
[d6bd]65 50ADC Screen_ScrollHorizBlocks_PPUAttrAddr_L; Add the lower byte of the PPU address to write to.
[d6bf]8d 06 20STA a:PPUADDR; Write it.
[d6c2]bd 82 02LDA a:Screen_ScrollHorizPPUAttrData,X; Load the attribute to write.
[d6c5]8d 07 20STA a:PPUDATA; Write it.
[d6c8]e8INX; X++ (loop counter).
[d6c9]e0 07CPX #$07; Are we under 7 attribute rows?
[d6cb]90 e6BCC @_loop; If so, loop.
[d6cd]60RTS
;============================================================================
; Handle a breakable floor block the player is standing on.
;
; This is called when the game determines the player is
; standing on a breakable floor block.
;
; The block will be taken through a transition phase,
; depending on the duration in which the player has been
; on the block. This will go through 3 transitions.
;
; The transitions go from:
;
; 0x34 -> 0x2C -> 0x5C -> 0x13
;
; INPUTS:
; X:
; The block position to update.
;
;
Blocks_Result:
; The block type at the offset.
;
;
BREAKABLE_FLOOR_TRANSITIONS:
; Sequence of breakable floor block types.
;
; OUTPUTS:
;
ScreenBuffer:
; The updated screen buffer.
;
;
Arg_BlockAttributesIndex:
; Updated block type.
;
;
Temp_00:
; Clobbered.
;
; CALLS:
;
Area_SetBlocks
;
; XREFS:
;
Player_CheckOnBreakableBlock
;============================================================================
[d6ce]Area_HandleBreakableFloor:
[d6ce]86 00STX Temp_00; Temporarily store the block position.
[d6d0]a2 00LDX #$00; X = 0 (loop counter)
[d6d2]a5 b7LDA Blocks_Result; A = block type (value to search for)
[d6d4]@_checkBlockLoop:
[d6d4]dd ef d6CMP a:BREAKABLE_FLOOR_TRANSITIONS,X; Does the block match the table at this index?
[d6d7]f0 07BEQ @_finishLoop; If so, jump.
[d6d9]e8INX; Else, X++
[d6da]e0 03CPX #$03; Is X < 3?
[d6dc]90 f6BCC @_checkBlockLoop; If so, loop.
[d6de]b0 01BCS @_updateBlocks; Else, jump to update the block.
[d6e0]@_finishLoop:
[d6e0]e8INX; X++ (table index of block to set)
[d6e1]@_updateBlocks:
[d6e1]bd ef d6LDA a:BREAKABLE_FLOOR_TRANSITIONS,X; Load the block to set to.
[d6e4]8d c9 03STA a:Arg_BlockAttributesIndex; DEADCODE: This is overridden in Area_SetBlocks
[d6e7]a6 00LDX Temp_00; X = Block position (stored earlier)
[d6e9]9d 00 06STA a:ScreenBuffer,X; Store in the screen buffer as the new block.
[d6ec]4c c5 d7JMP Area_SetBlocks; And set it for the area data.
;============================================================================
; Sequence of block types for breakable floor transitions.
;
; XREFS:
;
Area_HandleBreakableFloor
;============================================================================
[d6ef]BREAKABLE_FLOOR_TRANSITIONS:
[d6ef]34.byte $34; [0]:
[d6f0]BREAKABLE_FLOOR_TRANSITIONS_1_:
[d6f0]2c.byte $2c; [1]:
[d6f1]BREAKABLE_FLOOR_TRANSITIONS_2_:
[d6f1]5c.byte $5c; [2]:
[d6f2]13.byte $13; [3]:
;============================================================================
; Screen PPU start addresses for updating blocks.
;
; This maps a value to the upper byte of the PPU
; start address for use when updating blocks on the
; screen.
;
; This is used in
Area_SetBlocks_SetAttributes.
;
; XREFS:
;
Area_SetBlocks_SetAttributes
;============================================================================
[d6f3]SET_BLOCKS_SCREEN_TILEMAP_ADDRS_U:
[d6f3]20.byte $20; [0]: Screen 0
[d6f4]24.byte $24; [1]: Screen 1
[d6f5]Game_OpenPathToMascon:
;
; Push the stone covering block offset to the stack for later.
;
[d6f5]8aTXA; A = X
[d6f6]48PHA; Push A to the stack.
;
; Clear the top stone covering.
;
[d6f7]a6 d5LDX PathToMascon_FountainCoverPos; X = Block position (in $YX form).
[d6f9]ad 68 d7LDA a:MASCON_FOUNTAIN_BLOCK_1_AIR; A = Block ID to place.
[d6fc]9d 00 06STA a:ScreenBuffer,X; Store it in the screen buffer at the target location.
[d6ff]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Clear the bottom stone covering.
;
[d702]a5 d5LDA PathToMascon_FountainCoverPos; X = Block position (in $YX form).
[d704]18CLC
[d705]69 10ADC #$10; X += 16 (next row down; YPos += 1).
[d707]aaTAX; X = A
[d708]ad 69 d7LDA a:MASCON_FOUNTAIN_BLOCK_2_AIR; A = Block ID to place.
[d70b]9d 00 06STA a:ScreenBuffer,X; Store it in the screen buffer at the target location.
[d70e]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Pop the original stone covering block offset.
;
[d711]68PLA; Pull A from the stack (original block position)
[d712]aaTAX; X = A
[d713]bd 6c d7LDA a:GAME_LADDER_TO_MASCON_BLOCK_OFFSETS,X; A = Block position offset, based on the arguments.
[d716]18CLC
[d717]65 d5ADC PathToMascon_FountainCoverPos; A += Target block position.
[d719]aaTAX; X = A
[d71a]86 d5STX PathToMascon_FountainCoverPos; Update the block position.
;
; Place the top stone covering.
;
[d71c]ad 6a d7LDA a:MASCON_FOUNTAIN_BLOCK_1_STONE; A = Stone block.
[d71f]9d 00 06STA a:ScreenBuffer,X; Place it in the screen buffer at the offset.
[d722]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Place the bottom stone covering.
;
[d725]a5 d5LDA PathToMascon_FountainCoverPos; A = Block position.
[d727]18CLC
[d728]69 10ADC #$10; A += 16 (next row).
[d72a]aaTAX; X = A
[d72b]ad 6b d7LDA a:MASCON_FOUNTAIN_BLOCK_2_STONE; A = Stone block.
[d72e]9d 00 06STA a:ScreenBuffer,X; Place it in the screen buffer.
[d731]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Prepare to animate the dropping of the ladder.
;
[d734]a9 07LDA #$07
[d736]85 d9STA PathToMascon_LadderBlocksRemaining; Set the ladder block count to 7.
[d738]a9 22LDA #$22
[d73a]85 d8STA PathToMascon_LadderPos; Set the position of the top of the ladder to X=2, Y=2.
;
; Check if the Path to Mascon quest is not yet complete.
;
[d73c]ad 2d 04LDA a:Quests; Load the completed quests.
[d73f]29 20AND #$20; Is Path to Mascon completed?
[d741]d0 08BNE @_clearCoverings; If it is, jump.
;
; Path to Mascon is not complete. Wait for 30 interrupts.
;
[d743]a2 1eLDX #$1e; X = 30 (loop counter)
[d745]@_waitforInterruptLoop:
[d745]20 2e caJSR WaitForInterrupt; Wait for an interrupt.
[d748]caDEX; X--;
[d749]d0 faBNE @_waitforInterruptLoop; If not 0, loop.
;
; Clear the top stone block on the fountain.
;
[d74b]@_clearCoverings:
[d74b]a6 d5LDX PathToMascon_FountainCoverPos; X = Fountain cover block position.
[d74d]ad 68 d7LDA a:MASCON_FOUNTAIN_BLOCK_1_AIR; A = Air block.
[d750]9d 00 06STA a:ScreenBuffer,X; Set in the screen buffer at the cover position.
[d753]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Clear the bottom stone block on the fountain.
;
[d756]a5 d5LDA PathToMascon_FountainCoverPos; A = Fountain cover block position.
[d758]18CLC
[d759]69 10ADC #$10; A += 16 (next row).
[d75b]aaTAX; X = A
[d75c]ad 69 d7LDA a:MASCON_FOUNTAIN_BLOCK_2_AIR; A = Air block.
[d75f]9d 00 06STA a:ScreenBuffer,X; Set in the screen buffer at the cover position.
[d762]20 c5 d7JSR Area_SetBlocks; Update the block on the screen.
;
; Drop the ladder to the path to Mascon.
;
[d765]4c 6e d7JMP Game_DropLadderToMascon; Drop the ladder.
;============================================================================
; Two cleared fountain stone block coverings.
;
; XREFS:
;
Game_OpenPathToMascon
;============================================================================
[d768]MASCON_FOUNTAIN_BLOCK_1_AIR:
[d768]42.byte $42; Air
[d769]MASCON_FOUNTAIN_BLOCK_2_AIR:
[d769]42.byte $42; Air
;============================================================================
; Two set fountain stone block coverings.
;
; XREFS:
;
Game_OpenPathToMascon
;============================================================================
[d76a]MASCON_FOUNTAIN_BLOCK_1_STONE:
[d76a]88.byte $88; Stone cover
[d76b]MASCON_FOUNTAIN_BLOCK_2_STONE:
[d76b]88.byte $88; Stone cover
;============================================================================
; Lookup table for quickly calculating a relative X or Y position for placing
; a block.
;
; At index 0, X will be incremented by 1.
;
; At index 1, Y will be incremented by 1 (screen wrap-around).
;
; XREFS:
;
Game_OpenPathToMascon
;============================================================================
[d76c]GAME_LADDER_TO_MASCON_BLOCK_OFFSETS:
[d76c]01.byte $01; [0]: X + 1
[d76d]ff.byte $ff; [1]: Y + 1
[d76e]Game_DropLadderToMascon:
;
; Check if we're on the screen with the path to Mascon.
;
; NOTE: This is only ever called from this screen,
; so it's interesting that this check exists.
;
[d76e]ad 35 04LDA a:Area_Region; Load the current region.
[d771]c9 01CMP #$01; Are we in Trunk?
[d773]d0 0eBNE @_dropLadderLoop; If not, jump.
[d775]a5 63LDA Area_CurrentScreen; Load the current screen.
[d777]c9 28CMP #$28; Is it the screen with the blocked path?
[d779]d0 08BNE @_dropLadderLoop; If not, jump.
;
; We're on the right screen. Mark the path as opened.
;
[d77b]ad 2d 04LDA a:Quests; Load the quests.
[d77e]09 20ORA #$20; Mark the path to Mascon opened.
[d780]8d 2d 04STA a:Quests; Store it.
;
; Wait for 4 interrupts.
;
[d783]@_dropLadderLoop:
[d783]20 2e caJSR WaitForInterrupt
[d786]20 2e caJSR WaitForInterrupt
[d789]20 2e caJSR WaitForInterrupt
[d78c]20 2e caJSR WaitForInterrupt
;
; Play the Drop Ladder sound effect.
;
[d78f]a9 17LDA #$17; 0x17 == Drop ladder sound effect.
[d791]20 e4 d0JSR Sound_PlayEffect; Play the sound effect.
;
; Place the ladder block.
;
[d794]a6 d8LDX PathToMascon_LadderPos; X = Next ladder block position.
[d796]ad af d7LDA a:DROP_LADDER_TO_MASCON_LADDER_BLOCK; A = Ladder block.
[d799]9d 00 06STA a:ScreenBuffer,X; Store in the screen buffer at X.
[d79c]20 c5 d7JSR Area_SetBlocks; Update blocks on the screen.
;
; Increment the ladder position, decrement the number of blocks
; to place, and loop.
;
[d79f]a5 d8LDA PathToMascon_LadderPos; A = Ladder position.
[d7a1]18CLC
[d7a2]69 10ADC #$10; A += 16 (next row)
[d7a4]85 d8STA PathToMascon_LadderPos; Store as the new position.
[d7a6]c6 d9DEC PathToMascon_LadderBlocksRemaining; Decrement the number of ladder blocks to place.
[d7a8]d0 d9BNE @_dropLadderLoop; If there are still blocks remaining, loop.
;
; Clear the "Opening Path" flag.
;
[d7aa]a9 00LDA #$00
[d7ac]85 d4STA PathToMascon_Opening; Set opening to 0.
[d7ae]60RTS
[d7af]DROP_LADDER_TO_MASCON_LADDER_BLOCK:
[d7af]20.byte $20; Ladder block
;============================================================================
; TODO: Document SpriteBehavior_EnemyUnused18_SomethingSetBlocks
;
; INPUTS:
; X
;
; OUTPUTS:
; A
;
; XREFS:
;
SpriteBehavior_EnemyUnused18__9991
;============================================================================
[d7b0]SpriteBehavior_EnemyUnused18_SomethingSetBlocks:
[d7b0]8aTXA
[d7b1]48PHA
[d7b2]98TYA
[d7b3]48PHA
[d7b4]ae cf 03LDX a:Something_UnusedSprite_ScreenBufferOffset
[d7b7]ad ce 03LDA a:Something_UnusedSprite_BlockOffset
[d7ba]9d 00 06STA a:ScreenBuffer,X
[d7bd]20 c5 d7JSR Area_SetBlocks
[d7c0]68PLA
[d7c1]a8TAY
[d7c2]68PLA
[d7c3]aaTAX
[d7c4]60RTS
[d7c5]Area_SetBlocks:
[d7c5]8d c9 03STA a:Arg_BlockAttributesIndex; Set the index of the block attribute to load.
[d7c8]86 00STX Temp_00; Store the destination index of the block to write.
[d7ca]8aTXA; A = X (write index).
[d7cb]48PHA; Push it to the stack.
;
; Switch to the area metadata bank.
;
[d7cc]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[d7cf]48PHA; Push it to the stack.
[d7d0]a2 03LDX #$03; Bank 3 = Area metadata.
[d7d2]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
;
; Restore our state for the block writes.
;
[d7d5]a5 00LDA Temp_00; Load the destination index.
[d7d7]48PHA; Push it to the stack.
;
; Set the PPU address for the block.
;
[d7d8]aaTAX; X = Loaded destination index.
[d7d9]20 ac d8JSR Area_SetPPUAddrForBlockIndex; Set the PPU address for this block.
[d7dc]20 ff d7JSR Area_SetBlocks_WriteBlockDataColumn1; Write column 1 of the block's tiles.
[d7df]ad e8 00LDA a:PPU_TargetAddr; Load the PPU target address we set before.
[d7e2]18CLC
[d7e3]69 20ADC #$20; Increment by 32 bytes (2 tiles).
[d7e5]8d e8 00STA a:PPU_TargetAddr; Set as the new lower byte of the PPU address.
[d7e8]ad e9 00LDA a:PPU_TargetAddr_U; Load the upper byte.
[d7eb]69 00ADC #$00; Add carry, if the lower byte wrapped.
[d7ed]8d e9 00STA a:PPU_TargetAddr_U; Set as the new upper byte.
[d7f0]20 16 d8JSR Area_SetBlocks_WriteBlockDataColumn2; Write column 2 of the block's tiles.
;
; Begin updating the PPU attributes.
;
[d7f3]68PLA; Pop the write index from the stack.
[d7f4]20 2d d8JSR Area_SetBlocks_SetAttributes; Write the PPU attributes for the block.
;
; Switch back to the previous bank.
;
[d7f7]68PLA; Pop the previous bank.
[d7f8]aaTAX
[d7f9]20 1a ccJSR MMC1_UpdateROMBank; Switch back to that bank.
[d7fc]68PLA; Pop the write index from the stack.
[d7fd]aaTAX; Set it back on X.
[d7fe]60RTS; Return it.
[d7ff]Area_SetBlocks_WriteBlockDataColumn1:
;
; Set the tile length to 2.
;
[d7ff]a9 02LDA #$02
[d801]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Write length of 2.
;
; Load the first block's tile and write to the PPU.
;
[d804]ac c9 03LDY a:Arg_BlockAttributesIndex; Load the requested block index within the area data.
[d807]b1 80LDA (CurrentArea_BlockData1StartAddr),Y; Load the top-left tile for the block.
[d809]9d 00 05STA a:PPUBuffer,X; Store it the PPU buffer.
;
; Load the second block's tile and write to the PPU.
;
[d80c]e8INX; X++ (PPU buffer offset).
[d80d]b1 82LDA (CurrentArea_BlockData2StartAddr),Y; Load the bottom-left tile for the block.
[d80f]9d 00 05STA a:PPUBuffer,X; Store it the PPU buffer.
[d812]e8INX; X++ (PPU buffer offset).
;
; Set the new upper bounds of the buffer.
;
[d813]86 20STX PPUBuffer_WriteOffset; Set X as the new upper bounds of the PPU buffer.
[d815]60RTS
[d816]Area_SetBlocks_WriteBlockDataColumn2:
;
; Set the tile length to 2.
;
[d816]a9 02LDA #$02
[d818]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Write length of 2.
;
; Load the first block and write to the PPU.
;
[d81b]ac c9 03LDY a:Arg_BlockAttributesIndex; Load the requested block index within the area data.
[d81e]b1 84LDA (CurrentArea_BlockData3StartAddr),Y; Load the top-right tile for the block.
[d820]9d 00 05STA a:PPUBuffer,X; Store it the PPU buffer.
;
; Load the second block and write to the PPU.
;
[d823]e8INX; X++ (PPU buffer offset).
[d824]b1 86LDA (CurrentArea_BlockData4StartAddr),Y; Load the bottom-right tile for the block.
[d826]9d 00 05STA a:PPUBuffer,X; Store it the PPU buffer.
[d829]e8INX; X++ (PPU buffer offset).
;
; Set the new upper bounds of the buffer.
;
[d82a]86 20STX PPUBuffer_WriteOffset; Set X as the new upper bounds of the PPU buffer.
[d82c]60RTS
[d82d]Area_SetBlocks_SetAttributes:
;
; Create a normalized offset from the index.
;
; This will be the index + 32.
;
; Effectively, this is ensuring we always have bit 5
; set, and are starting at a value of >= 32 for the
; bit math.
;
[d82d]18CLC
[d82e]69 20ADC #$20; A = index + 32
[d830]85 00STA Temp_00; Store it.
;
; Create a floored offset from the index.
;
; This will be one of 0, 8, 16, 24, 32, 40, 48, or 56.
; It will be stored in
PPU_TargetAddr for later.
;
[d832]4aLSR A; A = A / 4
[d833]4aLSR A
[d834]29 38AND #$38; Round down to a multiple of 8.
[d836]8d e8 00STA a:PPU_TargetAddr; Store it.
;
; Compute the lower starting byte for the PPU address.
;
; This is where the block will be written to on the
; screen.
;
; Result is the floored offset (multiple of 8) OR'd
; with half the lower nibble of the normalized offset.
;
[d839]a5 00LDA Temp_00; A = normalized offset.
[d83b]29 0fAND #$0f; A = lower nibble of A.
[d83d]4aLSR A; A = A / 2
[d83e]0d e8 00ORA a:PPU_TargetAddr; OR with the floored offset.
[d841]8d e8 00STA a:PPU_TargetAddr; And store it.
;
; Compute a corner index (0..3) based on the offset.
;
; The corner is composed of bits 0 and 4, packed into
; 2 bits. So:
;
; 0000 0000 == 0
; 0000 0001 == 1
; 0001 0000 == 2
; 0001 0001 == 3
;
[d844]a5 00LDA Temp_00; A = normalized offset.
[d846]29 10AND #$10; Keep bit 4 (result = 0 or 16)
[d848]4aLSR A; Convert to value 0 or 2.
[d849]4aLSR A
[d84a]4aLSR A
[d84b]85 06STA Temp_06; Store the result.
[d84d]a5 00LDA Temp_00; A = floored offset.
[d84f]29 01AND #$01; Keep bit 0 (result = 0 or 1).
[d851]05 06ORA Temp_06; OR with the value we just calculated (result = 0..3)
[d853]85 06STA Temp_06; Store it.
;
; Load the block attributes for this block.
;
[d855]ac c9 03LDY a:Arg_BlockAttributesIndex; Y = block attributes index for the new block.
[d858]b1 7eLDA (CurrentArea_BlockAttributesAddr),Y; A = block attributes for that index.
[d85a]a6 06LDX Temp_06; X = corner (0..3 value from above).
[d85c]3d a4 d8AND a:SET_BLOCKS_TILE_CORNER_MASK,X; A = block attributes for the given corner.
[d85f]48PHA; Push it to the stack.
;
; Get the screen index we're drawing to.
;
[d860]a5 0dLDA PPU_ScrollScreen; A = Scroll screen to update.
[d862]29 01AND #$01; Keep the right-most bit (0 or 1)
[d864]aaTAX; X = result as an index for the lookup tables.
;
; Compute a start address for that screen.
;
; NOTE: Despite using a lookup table, this will always
; be $0242 (
TextBox_AttributeData), since all
; values in the tables are the same for all indexes.
;
; This is code that could be optimized away.
;
[d865]bd cf d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_L,X; A = lower byte of PPU address
[d868]85 02STA Temp_Addr_L; Store it.
[d86a]bd d1 d4LDA a:SET_BLOCKS_TILEMAP_OFFSETS_U,X; A = upper byte of PPU address
[d86d]85 03STA Temp_Addr_U; Store it.
;
; Compute the upper starting byte for the PPU address.
;
[d86f]bd f3 d6LDA a:SET_BLOCKS_SCREEN_TILEMAP_ADDRS_U,X; A = tilemap address for the screen.
[d872]8d e9 00STA a:PPU_TargetAddr_U; Store as the upper byte of the tilemap address.
;
; Combine the new data for this corner with the existing
; data.
;
[d875]ac e8 00LDY a:PPU_TargetAddr; Y = lower byte of tilemap address computed above.
[d878]b1 02LDA (Temp_Addr_L),Y; Load the data for this block.
[d87a]a6 06LDX Temp_06; X = corner value (0..3)
[d87c]3d a8 d8AND a:SET_BLOCKS_TILE_CORNER_MASK_INVERT,X; Invert the corner mask (keep all non-updated corner data).
[d87f]85 06STA Temp_06; Store as the new value.
[d881]68PLA; Pull the updated block attributes for the corner.
[d882]05 06ORA Temp_06; OR it with the non-updating corner values.
[d884]91 02STA (Temp_Addr_L),Y; And store it.
[d886]48PHA; And push it to the stack.
;
; Create a final PPU attribute target address to draw to.
;
; This is going to be an address starting at $23C0.
;
[d887]ad e9 00LDA a:PPU_TargetAddr_U; Load the upper byte of the PPU address.
[d88a]09 03ORA #$03; Ensure it starts at 0x03 (address >= $03XX).
Realistically, $23C0 (we guarantee 0x20 or 0x24 above for the screen).
[d88c]8d e9 00STA a:PPU_TargetAddr_U; Store as the new upper byte.
[d88f]ad e8 00LDA a:PPU_TargetAddr; Load the lower byte.
[d892]09 c0ORA #$c0; Ensure it starts at 0xC0 (address >= $XXC0)
[d894]8d e8 00STA a:PPU_TargetAddr; Store it.
;
; Write the attribute data to the PPU buffer.
;
[d897]a9 01LDA #$01
[d899]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue 1 byte.
[d89c]68PLA; Pull the attribute data to write from stack.
[d89d]9d 00 05STA a:PPUBuffer,X; Store it in the buffer at X.
[d8a0]e8INX; X++ (new offset)
[d8a1]86 20STX PPUBuffer_WriteOffset; Store it.
[d8a3]60RTS
;============================================================================
; A mapping table for masking a corner's bits.
;
; XREFS:
;
Area_SetBlocks_SetAttributes
;============================================================================
[d8a4]SET_BLOCKS_TILE_CORNER_MASK:
[d8a4]03.byte $03; [0]:
[d8a5]0c.byte $0c; [1]:
[d8a6]30.byte $30; [2]:
[d8a7]c0.byte $c0; [3]:
;============================================================================
; A mapping table for masking all but a corner's bits.
;
; XREFS:
;
Area_SetBlocks_SetAttributes
;============================================================================
[d8a8]SET_BLOCKS_TILE_CORNER_MASK_INVERT:
[d8a8]fc.byte $fc; [0]:
[d8a9]f3.byte $f3; [1]:
[d8aa]cf.byte $cf; [2]:
[d8ab]3f.byte $3f; [3]:
;============================================================================
; TODO: Document Area_SetPPUAddrForBlockIndex
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_SetBlocks
;============================================================================
[d8ac]Area_SetPPUAddrForBlockIndex:
;
; Build an address based on the block index.
;
; The upper byte will always be 0. The lower byte will be
; the lower nibble * 2.
;
[d8ac]a9 00LDA #$00; 0 = Upper byte of address.
[d8ae]8d e9 00STA a:PPU_TargetAddr_U; Set as the PPU target address.
;
; Compute the lower byte of the target PPU address based off
; the block index.
;
; This will be the lower nibble * 2.
;
[d8b1]8aTXA; A = Block index.
[d8b2]29 0fAND #$0f; Keep the lower nibble.
[d8b4]0aASL A; Multiply by 2.
[d8b5]8d e8 00STA a:PPU_TargetAddr; Set as the lower byte of the PPU target address.
;
; Compute the upper byte of the target PPU address.
;
[d8b8]8aTXA; A = Block index.
[d8b9]29 f0AND #$f0; Keep the upper nibble.
[d8bb]0aASL A; Multiply by 2.
[d8bc]2e e9 00ROL a:PPU_TargetAddr_U
[d8bf]0aASL A
[d8c0]2e e9 00ROL a:PPU_TargetAddr_U
[d8c3]18CLC
[d8c4]6d e8 00ADC a:PPU_TargetAddr; Add the lower byte of the PPU target address.
[d8c7]8d e8 00STA a:PPU_TargetAddr; Store as the lower byte of the PPU target address.
[d8ca]a5 0dLDA PPU_ScrollScreen; Load the scroll screen.
[d8cc]29 01AND #$01; Convert to a 0 = Left, 1 = Right value.
[d8ce]a8TAY; Y = result.
[d8cf]ad e9 00LDA a:PPU_TargetAddr_U; Load the upper byte of the PPU target address.
[d8d2]19 ea d8ORA a:AREA_SETPPUADDRFORBLOCK_U_BITS,Y; Apply bits to the value of the upper byte of the address.
[d8d5]8d e9 00STA a:PPU_TargetAddr_U; Store as the new upper byte of the adress.
[d8d8]ad e8 00LDA a:PPU_TargetAddr; Load the lower byte of the address.
[d8db]18CLC
[d8dc]69 80ADC #$80; Add 0x80 to it.
[d8de]8d e8 00STA a:PPU_TargetAddr; Store as the new lower byte.
[d8e1]ad e9 00LDA a:PPU_TargetAddr_U; Load the upper byte.
[d8e4]69 00ADC #$00; Add carry, if the lower byte wrapped.
[d8e6]8d e9 00STA a:PPU_TargetAddr_U; Store as the new upper byte.
[d8e9]60RTS
[d8ea]AREA_SETPPUADDRFORBLOCK_U_BITS:
[d8ea]20.byte $20; [0]: Scroll screen 0
[d8eb]24.byte $24; [1]: Scroll screen 1
[d8ec]Player_HandleDeath:
[d8ec]a2 ffLDX #$ff; X = 0xFF
[d8ee]9aTXS; Set as stack pointer
;
; Switch to bank 14 (Logic)
;
[d8ef]a2 0eLDX #$0e
[d8f1]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 14.
;
; Clear all player bits but the facing direction.
;
[d8f4]a5 a4LDA Player_Flags; Load the player's flags.
[d8f6]29 40AND #$40; Remove all but the facing direction bit.
[d8f8]85 a4STA Player_Flags; Store it back.
;
; Clear out more player state.
;
[d8fa]a9 00LDA #$00
[d8fc]85 a5STA Player_StatusFlag; Set player status to 0.
[d8fe]85 a3STA Player_MovementTick; Set the movement tick to 0.
[d900]85 adSTA Player_InvincibilityPhase; Set invincibility phase to 0.
;
; Push the inventory state to the stack.
;
[d902]ad bd 03LDA a:SelectedWeapon; Load the selected weapon.
[d905]48PHA; Push to the stack.
[d906]ad be 03LDA a:SelectedArmor; Load the selected armor.
[d909]48PHA; Push to the stack.
[d90a]ad bf 03LDA a:SelectedShield; Load the selected shield.
[d90d]48PHA; Push to the stack.
[d90e]ad c0 03LDA a:SelectedMagic; Load the selected magic.
[d911]48PHA; Push to the stack.
[d912]ad c1 03LDA a:SelectedItem; Load the selected item.
[d915]48PHA; Push to the stack.
;
; Wait for interrupt and call other functions to
; reset state.
;
[d916]20 2e caJSR WaitForInterrupt; Wait for interrupt.
[d919]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset animations.
[d91c]20 bf b7JSR PlayerDeath_ResetSelectedItemState; Reset selected item state.
[d91f]20 45 edJSR Player_DrawSprite; Update player sprite.
;
; Prepare state for screen transitions.
;
[d922]a9 00LDA #$00
[d924]8d db 03STA a:Screen_TransitionCounter; Clear the screen transition counter.
;
; Reset the player death state.
;
[d927]8d 38 04STA a:PlayerIsDead; Clear the dead flag.
[d92a]a9 ffLDA #$ff
[d92c]8d dd 03STA a:Player_DeathAnimationPhase; Clear the death animation phase.
[d92f]8d de 03STA a:Player_DeathAnimationCounter; Clear the death animation counter.
[d932]8d 30 04STA a:Screen_FadeOutStage; Clear the palette index.
;
; Clear the sprites and music.
;
[d935]20 30 c1JSR Screen_ClearSprites; Clear sprites from the screen.
[d938]a9 00LDA #$00
[d93a]85 faSTA Music_Current; Clear the music.
;
; Play the death sound effect.
;
[d93c]a9 16LDA #$16; 0x16 == Player death sound.
[d93e]20 e4 d0JSR Sound_PlayEffect; Play the sound effect.
;
; Begin our death animation loop.
;
; This will dissolve the user's sprite.
;
[d941]@_dissolvePlayerLoop:
[d941]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[d944]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset animations.
[d947]20 42 daJSR Screen_NextTransitionState; Prepare for the next screen state.
[d94a]20 ee ebJSR Player_DrawBody; Draw the player.
[d94d]20 d6 d9JSR Player_DrawDeathAnimation; Draw the next cycle of the death animation.
;
; Check whether to reset the animation state.
;
[d950]ad dd 03LDA a:Player_DeathAnimationPhase; Load the animation phase.
[d953]c9 ffCMP #$ff; Is it 0xFF (complete)?
[d955]d0 0fBNE @_prepareNextLoop; If not, prepare for the next loop.
[d957]ad db 03LDA a:Screen_TransitionCounter; Load the screen transition counter.
[d95a]c9 10CMP #$10; Is it >= 16?
[d95c]90 08BCC @_prepareNextLoop; If so, prepare for next loop.
;
; Clear animation state.
;
[d95e]a9 00LDA #$00
[d960]8d dd 03STA a:Player_DeathAnimationPhase; Clear the animation phase.
[d963]8d de 03STA a:Player_DeathAnimationCounter; Clear the animation counter.
[d966]@_prepareNextLoop:
[d966]ee db 03INC a:Screen_TransitionCounter; Increment the screen transition counter.
[d969]d0 3eBNE @_continueDissolvePlayerLoop; If transition counter != 0, loop.
;
; We're past the player death animation.
;
; Load the palette and update the screen.
;
[d96b]ad d0 03LDA a:Screen_PaletteIndex
[d96e]20 3b d0JSR Screen_LoadUIPalette; Load the palette.
[d971]20 90 d0JSR PPUBuffer_WritePalette; Append 0 to the PPU buffer.
;
; Restore the player's inventory.
;
[d974]68PLA; Pop the selected item from the stack.
[d975]8d c1 03STA a:SelectedItem; Set it.
[d978]68PLA; Pop the magic from the stack.
[d979]8d c0 03STA a:SelectedMagic; Set it.
[d97c]68PLA; Pop the shield from the stack.
[d97d]8d bf 03STA a:SelectedShield; Set it.
[d980]68PLA; Pop the armor from the stack.
[d981]8d be 03STA a:SelectedArmor; Set it.
[d984]68PLA; Pop the weapon from the stack.
[d985]8d bd 03STA a:SelectedWeapon; Set it.
;
; Set the death music.
;
[d988]a9 08LDA #$08; 0x08 == Death music.
[d98a]85 faSTA Music_Current; Set as the current music.
;
; Clear the sprite loaded state.
;
[d98c]a9 ffLDA #$ff
[d98e]85 0eSTA Screen_ReadyState; Set loaded state to 0xFF.
[d990]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[d993]0c.byte BANK_12_LOGIC; Bank = 12
[d994]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[d996]@_afterIScriptFarJump:
[d996]a9 00LDA #$00; 0 = No music
[d998]85 faSTA Music_Current; Set it.
[d99a]20 59 f8JSR MMC1_LoadBankAndJump; Run:
[d99d]0c.byte BANK_12_LOGIC; Bank = 12
[d99e]b0 95pointer Player_SetInitialExpAndGold-1; Address = Player_SetInitialExpAndGold
[d9a0]@_afterSetExpGoldFarJump:
[d9a0]20 2f daJSR Screen_FadeToBlack; Fade the screen to black.
[d9a3]20 7d daJSR Game_InitStateForSpawn; Reset state for spawn.
[d9a6]4c 0a dbJMP Player_Spawn; Spawn the player.
[d9a9]@_continueDissolvePlayerLoop:
[d9a9]4c 41 d9JMP @_dissolvePlayerLoop
[d9ac]EndGame_Begin:
;
; Clear all sprites from the screen.
;
[d9ac]20 30 c1JSR Screen_ClearSprites; Clear sprites.
;
; Wait 120 frames before beginning the fade-out.
;
[d9af]a2 78LDX #$78; X = 120 (loop counter)
[d9b1]@_beforeFadeLoop:
[d9b1]8aTXA; A = X
[d9b2]48PHA; Push to the stack.
[d9b3]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[d9b6]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset for this frame.
[d9b9]20 46 dcJSR Game_DrawScreenInFrozenState; Draw the screen and player in a semi-paused state.
[d9bc]68PLA; Pop our loop counter.
[d9bd]aaTAX; X = A
[d9be]caDEX; X--
[d9bf]d0 f0BNE @_beforeFadeLoop; If > 0, loop.
;
; Fade the screen to black.
;
[d9c1]20 2f daJSR Screen_FadeToBlack; Fade to black.
;
; Wait another 120 frames before switching screens.
;
[d9c4]a2 78LDX #$78; X = 120 (loop counter)
[d9c6]@_beforeTransitionLoop:
[d9c6]8aTXA; A = X (loop counter)
[d9c7]48PHA; Push to the stack.
[d9c8]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[d9cb]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset for this frame.
[d9ce]68PLA; Pop our loop counter.
[d9cf]aaTAX; X = A (loop counter)
[d9d0]caDEX; X--
[d9d1]d0 f3BNE @_beforeTransitionLoop; If > 0, loop.
;
; Move the player to the King's room and begin the
; end-game dialogue sequence.
;
[d9d3]4c 04 dbJMP EndGame_BeginKingsRoomSequence; Begin the next transition into the King's room.
[d9d6]Player_DrawDeathAnimation:
[d9d6]ad dd 03LDA a:Player_DeathAnimationPhase; Load the death animation phase.
[d9d9]c9 08CMP #$08; Is it >= 8?
[d9db]b0 43BCS @_return; If so, return.
[d9dd]ad de 03LDA a:Player_DeathAnimationCounter; Load the animation death counter.
[d9e0]10 0bBPL @_dissolveSprite; If >= 0, jump to dissolve the sprite.
[d9e2]a5 1aLDA InterruptCounter; Else, load the interrupt counter.
[d9e4]29 03AND #$03; Every 3 out of 4 ticks...
[d9e6]d0 38BNE @_return; Return.
[d9e8]a9 00LDA #$00
[d9ea]8d de 03STA a:Player_DeathAnimationCounter; Clear the death counter.
[d9ed]@_dissolveSprite:
[d9ed]a6 20LDX PPUBuffer_WriteOffset; X = PPU buffer upper bounds.
[d9ef]a9 faLDA #$fa; A = 0xFA (Remove Vertical Lines draw command).
[d9f1]9d 00 05STA a:PPUBuffer,X; Set that as the command to execute.
;
; Set the current player phase for the draw.
;
[d9f4]e8INX; X++
[d9f5]ad dd 03LDA a:Player_DeathAnimationPhase; Load the animation phase.
[d9f8]9d 00 05STA a:PPUBuffer,X; Set that as the phase parameter.
;
; Set the upper byte of the address to 0.
;
[d9fb]e8INX; X++
[d9fc]a9 00LDA #$00
[d9fe]9d 00 05STA a:PPUBuffer,X; Set 0 as the upper address.
;
; Set the lower byte of the address based on the counter.
;
[da01]e8INX; X++
[da02]ac de 03LDY a:Player_DeathAnimationCounter; Load the animation counter.
[da05]b9 21 daLDA a:DEATH_ANIMATION_DRAW_ADDR_LOWER,Y; Load the lower address for Y.
[da08]9d 00 05STA a:PPUBuffer,X; Set it as the lower address.
;
; Store the new upper bounds of the PPU buffer.
;
[da0b]e8INX; X++
[da0c]86 20STX PPUBuffer_WriteOffset; Set as the new upper bounds for the PPU buffer.
;
; Update the animation counter.
;
[da0e]ee de 03INC a:Player_DeathAnimationCounter; Increment the animation counter.
;
; If >= 8, reset the counter and increment the phase.
;
[da11]ad de 03LDA a:Player_DeathAnimationCounter; Load the new animation counter.
[da14]c9 08CMP #$08; Is it < 8?
[da16]90 08BCC @_return; If so, return.
;
; Reset the animation counter and increment the phase.
;
Player_HandleDeath will see this and prepare the
; next
; round of vertical line removals.
;
[da18]a9 ffLDA #$ff
[da1a]8d de 03STA a:Player_DeathAnimationCounter; Set the animation counter to 0xFF.
[da1d]ee dd 03INC a:Player_DeathAnimationPhase; Increment the animation phase.
[da20]@_return:
[da20]60RTS
;============================================================================
; Lower bytes for the player's tiles to update during death animation.
;
; XREFS:
;
Player_DrawDeathAnimation
;============================================================================
[da21]DEATH_ANIMATION_DRAW_ADDR_LOWER:
[da21]00.byte $00; [0]:
[da22]10.byte $10; [1]:
[da23]20.byte $20; [2]:
[da24]30.byte $30; [3]:
[da25]40.byte $40; [4]:
[da26]50.byte $50; [5]:
[da27]60.byte $60; [6]:
[da28]70.byte $70; [7]:
;============================================================================
; DEADCODE: Clear the fade-out stage for the screen transitions.
;============================================================================
[da29]UNUSED_Screen_ClearFadeOutStage:
[da29]a9 ffLDA #$ff
[da2b]8d 30 04STA a:Screen_FadeOutStage
[da2e]60RTS
[da2f]Screen_FadeToBlack:
[da2f]a9 00LDA #$00; X = 0 (loop counter)
[da31]8d 30 04STA a:Screen_FadeOutStage; Set as the index into the screen palette lookup.
[da34]@_loop:
[da34]20 2e caJSR WaitForInterrupt; Wait for the next interrupt.
[da37]20 42 daJSR Screen_NextTransitionState; Update the screen for this palete.
[da3a]ad 30 04LDA a:Screen_FadeOutStage; Load the palette index.
[da3d]c9 04CMP #$04; Is it < 4?
[da3f]90 f3BCC @_loop; If so, loop.
[da41]60RTS
;============================================================================
; Handle the next state for a screen transition.
;
; This may be used when entering a building or when dying.
; An outer loop will call this each tick. It won't begin
; the fadeout until
Screen_FadeOutStage is a value 0-4.
;
; The palette will increase at periodic intervals,
; eventually ending with a black screen.
;
; INPUTS:
;
Screen_FadeOutStage:
; The current palette index.
;
;
Screen_FadeOutCounter:
; The counter for this loop.
;
;
Screen_PaletteIndex:
; The palette index to apply for the transition.
;
; OUTPUTS:
;
Screen_FadeOutStage:
; The new palette index.
;
;
Screen_FadeOutCounter:
; The fade-out counter.
;
; XREFS:
;
Player_HandleDeath
;
Screen_FadeToBlack
;============================================================================
[da42]Screen_NextTransitionState:
[da42]ad 30 04LDA a:Screen_FadeOutStage; Load the current palette index.
[da45]c9 04CMP #$04; Is it >= 4?
[da47]b0 14BCS @_doneFadeOut; If so, jump to finish up.
[da49]ad dc 03LDA a:Screen_FadeOutCounter; A = fadeout counter.
[da4c]18CLC
[da4d]69 32ADC #$32; A += 0x32 (50)
[da4f]8d dc 03STA a:Screen_FadeOutCounter; Store as the new value.
[da52]90 09BCC @_doneFadeOut; If 0, don't alter the palette.
;
; Fade out to the next palette towards black.
;
[da54]ad d0 03LDA a:Screen_PaletteIndex; Load the screen palette index.
[da57]20 ad d0JSR Screen_SetFadeOutPalette; Load the palette into memory.
[da5a]ee 30 04INC a:Screen_FadeOutStage; Increment the palette index to the next transition state.
[da5d]@_doneFadeOut:
[da5d]ad db 03LDA a:Screen_TransitionCounter; Load the screen transition counter.
[da60]c9 50CMP #$50; Is it < 80?
[da62]d0 05BNE @_return; If so, return.
;
; We're doing player death, and have finished dissolving
; the player. We can now begin fading to black.
;
[da64]a9 00LDA #$00
[da66]8d 30 04STA a:Screen_FadeOutStage; Set the palette to 0.
[da69]@_return:
[da69]60RTS
[da6a]Game_InitStateForStartScreen:
[da6a]a2 ffLDX #$ff; X = 0xFF
[da6c]9aTXS; Copy to stack pointer.
[da6d]a9 00LDA #$00; A = 0
[da6f]85 24STA Area_CurrentArea; Set as the current area (Eolis)
[da71]8d 35 04STA a:Area_Region; Set as the current region (Eolis)
[da74]20 ae b7JSR Player_InitInventoryState; Initialize the player inventory.
[da77]20 7d daJSR Game_InitStateForSpawn; Initiate the spawn state.
[da7a]4c 65 fcJMP Game_ShowStartScreen; Show the start screen.
[da7d]Game_InitStateForSpawn:
[da7d]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[da80]a9 00LDA #$00
[da82]8d 38 04STA a:PlayerIsDead; Set the player as alive.
[da85]85 20STA PPUBuffer_WriteOffset; Reset PPU buffer upper bounds to 0.
[da87]85 1fSTA PPUBuffer_ReadOffset; Reset PPU buffer offset to 0.
[da89]85 0eSTA Screen_ReadyState; Mark the screen as loaded and ready.
[da8b]20 aa e0JSR Player_SetInitialState; Initialize the player's initial state.
[da8e]20 55 baJSR Player_ClearVisibleMagic; Clear any visible magic on screen.
[da91]20 80 ceJSR Sprites_LoadCommon; Load sprites from bank 8.
[da94]a9 01LDA #$01
[da96]8d 2f 04STA a:Maybe_Game_Ready; Set that the game is ready to play.
[da99]a9 00LDA #$00
[da9b]85 0cSTA PPU_ScrollX; Clear the scrolling screen pixel state.
[da9d]85 0dSTA PPU_ScrollScreen; Clear the scrolling screen state.
[da9f]60RTS
[daa0]Game_SetupEnterScreen:
[daa0]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[daa3]20 46 ddJSR Screen_Load; Load the screen state.
[daa6]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[daa9]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[daac]4c 8d c2JMP GameLoop_LoadSpriteImages; Load the sprite images.
[daaf]Game_SetupEnterBuilding:
[daaf]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dab2]20 06 deJSR Game_EnterBuilding; Load state for the building.
[dab5]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dab8]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[dabb]4c 8d c2JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dabe]Game_SetupExitBuilding:
[dabe]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dac1]20 66 deJSR Game_ExitBuilding; Load state for the area outside the building.
[dac4]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dac7]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[daca]4c 8d c2JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dacd]Game_SetupNewArea:
[dacd]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dad0]20 64 dfJSR Game_EnterAreaHandler; Load state for the new area.
[dad3]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dad6]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[dad9]4c 8d c2JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dadc]Game_SetupAndLoadOutsideArea:
[dadc]20 f7 caJSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dadf]ae 35 04LDX a:Area_Region; Load the region.
[dae2]bd fe daLDA a:DOOR_OUTSIDE_REGION_INDEXES,X; Load the area of the game to place the player for this region.
[dae5]85 24STA Area_CurrentArea; Store as the new area.
[dae7]20 f5 deJSR Game_LoadCurrentArea; Load data for the area.
[daea]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[daed]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[daf0]20 8d c2JSR GameLoop_LoadSpriteImages; Load the sprite images.
[daf3]4c 45 dbJMP Game_MainLoop; Run the game mainloop.
;============================================================================
; DEADCODE
;
; Would assign the area based on transitioning from another.
;
; This isn't even a complete function. It runs right into the
; data table below.
;============================================================================
[daf6]FUN_PRG15_MIRROR__daf6:
[daf6]ae 35 04LDX a:Area_Region
[daf9]bd fe daLDA a:DOOR_OUTSIDE_REGION_INDEXES,X
[dafc]85 24STA Area_CurrentArea
[dafe]DOOR_OUTSIDE_REGION_INDEXES:
[dafe]00.byte AREA_EOLIS; [0]: Eolis
[daff]01.byte AREA_TRUNK; [1]: Trunk
[db00]02.byte AREA_MIST; [2]: Mist
[db01]05.byte AREA_BRANCH; [3]: Branch
[db02]06.byte AREA_DARTMOOR; [4]: Dartmoor
[db03]07.byte AREA_ZENIS; [5]: Evil Fortress
;============================================================================
; Place the player in the King's room and move toward the King.
;
; This is part of the end-game sequence, and takes place
; immediately after the final boss is killed and the screen
; fades out.
;
; The player will be placed back in the King's room, and
; will then begin moving automatically toward the King.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; CALLS:
;
EndGame_MoveToKingsRoom
;
EndGame_MainLoop
;
; XREFS:
;
EndGame_Begin
;============================================================================
[db04]EndGame_BeginKingsRoomSequence:
[db04]20 dd ddJSR EndGame_MoveToKingsRoom; Place the player in the King's room.
[db07]4c ef dbJMP EndGame_MainLoop; Move the player toward the King.
[db0a]Player_Spawn:
[db0a]a9 50LDA #$50
[db0c]8d 31 04STA a:Player_HP_U; Set the initial player HP to 80.
[db0f]8d 9a 03STA a:Player_MP; Set the initial player MP to 80.
[db12]ad 2d 04LDA a:Quests; Load the completed quests.
[db15]29 efAND #$ef; Disable the Mattock barrier to Mascon quest.
[db17]8d 2d 04STA a:Quests; Store it.
[db1a]20 af c6JSR Game_ClearTimedItems; Cleared all timed items.
[db1d]20 61 ddJSR Game_SpawnInTemple; Spawn in the temple.
[db20]20 af daJSR Game_SetupEnterBuilding; Change the state to be inside a building.
[db23]4c 45 dbJMP Game_MainLoop; Start the mainloop.
[db26]Game_Start:
[db26]20 af c6JSR Game_ClearTimedItems; Clear all timed items.
[db29]20 f7 caJSR PPU_WaitUntilFlushed; Wait until everything is drawn to the screen.
[db2c]20 a7 deJSR Game_LoadFirstLevel; Load the first level.
;
; Clear the player's MP.
;
[db2f]a9 00LDA #$00
[db31]8d 9a 03STA a:Player_MP; Set MP to 0.
;
; Clear the player's experience.
;
[db34]a9 00LDA #$00
[db36]8d 90 03STA a:Experience; Set lower byte of experience to 0.
[db39]8d 91 03STA a:Experience_U; And upper byte.
;
; Prepare state for the first screen of Eolis, including the
; intro trigger sprite entity.
;
[db3c]20 17 cbJSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[db3f]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[db42]20 8d c2JSR GameLoop_LoadSpriteImages; Load images for the current sprite entities (which will be the intro trigger).
;
; v-- Fall through --v
;
;============================================================================
; Mainloop for the game.
;
; This controls all input, rendering, and eventing while
; playing the game.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; XREFS:
;
Game_MainLoop
;
Game_SetupAndLoadOutsideArea
;
Player_Spawn
;============================================================================
[db45]Game_MainLoop:
[db45]a2 ffLDX #$ff; X = 0xFF
[db47]9aTXS; Transfer to the stack.
;
; Switch to bank 14 to run sprite logic.
;
[db48]a2 0eLDX #$0e; Bank 14 = Logic
[db4a]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Prepare to draw sprites.
;
[db4d]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[db50]20 47 cbJSR Screen_ResetSpritesForGamePlay; Reset the sprite state for the screen.
[db53]20 ca e0JSR GameLoop_UpdatePlayer
[db56]20 82 b9JSR Player_DrawShield; Draw the player's shield sprite.
[db59]20 ee ebJSR Player_DrawBody; Draw the player's body sprite.
[db5c]20 d6 b7JSR Player_DrawWeapon; Draw the player's weapon sprite.
[db5f]20 5b baJSR Player_CastMagic; Check and handle casting magic.
[db62]20 78 c4JSR GameLoop_CheckUseCurrentItem; Active selected item?
[db65]20 00 80JSR Sprites_UpdateAll; Update all sprites.
[db68]20 8f c8JSR GameLoop_CountdownItems
[db6b]20 c5 dfJSR Fog_OnTick
[db6e]20 16 e0JSR GameLoop_CheckShowPlayerMenu
[db71]20 4b efJSR GameLoop_RunScreenEventHandlers
[db74]20 2b e0JSR GameLoop_CheckPauseGame
[db77]ad 38 04LDA a:PlayerIsDead
[db7a]f0 03BEQ @_playerIsNotDead
[db7c]4c ec d8JMP Player_HandleDeath
[db7f]@_playerIsNotDead:
[db7f]a5 54LDA Screen_ScrollDirection
[db81]30 c2BMI Game_MainLoop
[db83]20 f4 cfJSR PPUBuffer_WaitUntilClear
[db86]ad 2f 04LDA a:Maybe_Game_Ready
[db89]f0 06BEQ @LAB_PRG15_MIRROR__db91
[db8b]a5 54LDA Screen_ScrollDirection
[db8d]c9 02CMP #$02
[db8f]90 1eBCC @LAB_PRG15_MIRROR__dbaf
[db91]@LAB_PRG15_MIRROR__db91:
[db91]20 47 cbJSR Screen_ResetSpritesForGamePlay
[db94]20 25 caJSR WaitForNextFrame
[db97]20 30 c1JSR Screen_ClearSprites
[db9a]20 b4 c1JSR Screen_LoadSpriteInfo
[db9d]20 25 caJSR WaitForNextFrame
[dba0]20 8d c2JSR GameLoop_LoadSpriteImages
[dba3]20 f7 caJSR PPU_WaitUntilFlushed
[dba6]20 0f ddJSR Game_UpdateForScroll
[dba9]20 17 cbJSR Screen_ResetForGamePlay
[dbac]4c 45 dbJMP Game_MainLoop
[dbaf]@LAB_PRG15_MIRROR__dbaf:
[dbaf]20 25 caJSR WaitForNextFrame
[dbb2]20 47 cbJSR Screen_ResetSpritesForGamePlay
[dbb5]20 82 b9JSR Player_DrawShield
[dbb8]20 ee ebJSR Player_DrawBody
[dbbb]20 d6 b7JSR Player_DrawWeapon
[dbbe]20 30 c1JSR Screen_ClearSprites
[dbc1]20 b4 c1JSR Screen_LoadSpriteInfo
[dbc4]20 25 caJSR WaitForNextFrame
[dbc7]20 8d c2JSR GameLoop_LoadSpriteImages
[dbca]@_scrolling:
[dbca]20 25 caJSR WaitForNextFrame
[dbcd]20 47 cbJSR Screen_ResetSpritesForGamePlay
[dbd0]20 48 e0JSR Game_UpdatePlayerOnScroll
[dbd3]20 82 b9JSR Player_DrawShield
[dbd6]20 ee ebJSR Player_DrawBody
[dbd9]20 d6 b7JSR Player_DrawWeapon
[dbdc]20 e7 d2JSR Screen_HandleScroll
[dbdf]20 e7 d2JSR Screen_HandleScroll
[dbe2]20 e7 d2JSR Screen_HandleScroll
[dbe5]20 e7 d2JSR Screen_HandleScroll
[dbe8]a5 54LDA Screen_ScrollDirection
[dbea]10 deBPL @_scrolling
[dbec]4c 45 dbJMP Game_MainLoop
[dbef]EndGame_MainLoop:
[dbef]a2 ffLDX #$ff; X = 0xFF (unused -- noop)
;
; Switch to bank 14.
;
[dbf1]a2 0eLDX #$0e
[dbf3]20 1a ccJSR MMC1_UpdateROMBank; Set bank to 14 (Logic).
;
; Wait for a frame and prepare for renders.
;
[dbf6]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[dbf9]20 47 cbJSR Screen_ResetSpritesForGamePlay
;
; Move the player a step/jump toward the King, or begin
; interaction, depending on position.
;
[dbfc]20 23 dcJSR EndGame_MovePlayerTowardKing; Simulate the player's button press.
[dbff]20 ca e0JSR GameLoop_UpdatePlayer; Update the player's position/state.
;
; Perform all the standard mainloop operations.
;
[dc02]20 82 b9JSR Player_DrawShield; Draw the shield.
[dc05]20 ee ebJSR Player_DrawBody; Draw the armor.
[dc08]20 d6 b7JSR Player_DrawWeapon; Draw the weapon.
[dc0b]20 5b baJSR Player_CastMagic; No-op
[dc0e]20 78 c4JSR GameLoop_CheckUseCurrentItem; No-op
[dc11]20 00 80JSR Sprites_UpdateAll; Update sprites.
[dc14]20 8f c8JSR GameLoop_CountdownItems; No-op
[dc17]20 c5 dfJSR Fog_OnTick; No-op
[dc1a]20 16 e0JSR GameLoop_CheckShowPlayerMenu; No-op
[dc1d]20 4b efJSR GameLoop_RunScreenEventHandlers; No-op
[dc20]4c ef dbJMP EndGame_MainLoop; Loop.
;============================================================================
; Move the player toward the King in the end-game.
;
; This will move the player by simulating holding down the
; Left joypad button, pressing the A button when at the
; stairs, and pressing Up when at the King.
;
; This is all done by factoring in the current player position:
;
; 1. Press Left until at X=79.
;
; 2. At X=79, keep pressing Jump and left.
;
; 3. At X=68, release and press Up.
;
; INPUTS:
;
Player_PosX_Block:
; The current player X position.
;
; OUTPUTS:
;
Joy1_ButtonMask:
;
Joy1_ChangedButtonMask:
; The simulated button presses.
;
; XREFS:
;
EndGame_MainLoop
;============================================================================
[dc23]EndGame_MovePlayerTowardKing:
[dc23]a5 9eLDA Player_PosX_Block; A = player X.
[dc25]c9 61CMP #$61; Is it >= 97?
[dc27]b0 0fBCS @_setButtonsLeft; If so, jump to just press Left.
[dc29]c9 50CMP #$50; Is it >= 80?
[dc2b]b0 12BCS @_setJumpLeft; If so, jump to press Left+A.
[dc2d]c9 44CMP #$44; Is it >= 68?
[dc2f]b0 07BCS @_setButtonsLeft; If so, jump to press Left.
;
; Simulate pressing Up to engage with the King.
;
[dc31]a9 08LDA #$08; 0x08 == Up button bitmask.
[dc33]85 16STA Joy1_ButtonMask; Set as the current button mask.
[dc35]85 19STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc37]60RTS
;
; Simulate pressing Left.
;
[dc38]@_setButtonsLeft:
[dc38]a9 02LDA #$02; 0x02 == Left button bitmask.
[dc3a]85 16STA Joy1_ButtonMask; Set as the current button mask.
[dc3c]85 19STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc3e]60RTS
;
; Simulate pressing Left+A to jump left.
;
[dc3f]@_setJumpLeft:
[dc3f]a9 82LDA #$82; 0x82 == Left + A button bitmask.
[dc41]85 16STA Joy1_ButtonMask; Set as the current button mask.
[dc43]85 19STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc45]RETURN_DC45:
[dc45]60RTS
[dc46]Game_DrawScreenInFrozenState:
[dc46]a5 0eLDA Screen_ReadyState; Load the loaded state.
[dc48]c9 ffCMP #$ff; Is it 0xFF?
[dc4a]f0 f9BEQ RETURN_DC45; If so, then return.
;
; Switch to bank 14.
;
[dc4c]ad 00 01LDA a:CurrentROMBank; Load the current bank.
[dc4f]48PHA; Push to the stack.
[dc50]a2 0eLDX #$0e
[dc52]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 14.
;
; Temporarily clear Wing Boots state.
;
[dc55]a5 a5LDA Player_StatusFlag; Load the player's status flag.
[dc57]48PHA; Push to the stack so it can be restored later.
[dc58]29 7fAND #$7f; Keep all but the Wing Boots flag.
[dc5a]85 a5STA Player_StatusFlag; Store it.
;
; Draw the player.
;
[dc5c]20 82 b9JSR Player_DrawShield; Draw the shield.
[dc5f]20 ee ebJSR Player_DrawBody; Draw the armor.
[dc62]20 d6 b7JSR Player_DrawWeapon; Draw the weapon.
;
; Restore the player's status flags.
;
[dc65]68PLA; Pop the status flag.
[dc66]85 a5STA Player_StatusFlag; Store it.
;
; Draw all the sprites, but with sprites paused.
;
; This flag will will disable all behavioral updates for
; sprites.
;
[dc68]a9 01LDA #$01; A = 1
[dc6a]8d 26 04STA a:Sprites_UpdatesPaused; Set as the paused state for sprites.
[dc6d]20 70 80JSR Sprites_UpdateAllStates; Update all sprites.
[dc70]a9 00LDA #$00; A = 0
[dc72]8d 26 04STA a:Sprites_UpdatesPaused; Clear the paused state.
[dc75]4c d6 f3JMP MMC1_UpdatePRGBankToStackA; Restore the previous bank.
[dc78]Game_LoadAreaTable:
;
; Set the address for the area in the areas table.
;
; This will convert the offset to an address within the
; current areas bank by taking the given offset, multiplying
; by 2, and then using it as an index into the current bank
; to load 2 bytes.
;
; These 2 bytes form an upper and lower byte offset into
; bank 3.
;
[dc78]0aASL A; Normalize the offset to a word boundary.
[dc79]a8TAY; Set as Y.
[dc7a]b9 00 80LDA a:ROMBankStart,Y; Load as the lower byte in the current bank.
[dc7d]85 7aSTA Area_ScreenBlocksOffset; Store that for lookup.
[dc7f]b9 01 80LDA a:ROMBankStart+1,Y; Load the next byte as the upper byte in the current bank.
[dc82]18CLC
[dc83]69 80ADC #$80; Add 0x80, converting to an absolute address.
[dc85]85 7bSTA Area_ScreenBlocksOffset_U; Store it.
;
; Save the current ROM bank and switch to bank 3, where the
; areas table lives.
;
[dc87]ad 00 01LDA a:CurrentROMBank; Load the current bank.
[dc8a]48PHA; Push it to the stack.
[dc8b]a2 03LDX #$03; X = bank 3 (area metadata).
[dc8d]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
;
; Set the start of the area's pointer table in
; 0x{@address 007C}.
;
; This will be
AREAS_TABLE_PTR in Bank 3.
;
[dc90]ad 00 80LDA a:AREAS_TABLE_PTR; A = first byte from the bank.
[dc93]85 02STA Temp_Addr_L; Store it temporarily as a lower byte.
[dc95]ad 01 80LDA a:AREAS_TABLE_PTR+1; Load the next byte.
[dc98]18CLC
[dc99]69 80ADC #$80; Add 0x80 (making it an absolute address).
[dc9b]85 03STA Temp_Addr_U; Store it temporarily as an upper byte.
;
; Load the address of the current area's metadata table.
;
[dc9d]a5 24LDA Area_CurrentArea; A = Current area.
[dc9f]0aASL A; Normalize to a word boundary.
[dca0]a8TAY; Y = result.
[dca1]b1 02LDA (Temp_Addr_L),Y; Load this offset from the area metadata table.
[dca3]85 7cSTA CurrentArea_TableAddr; Store as the lower byte of the current area's metadata table address.
[dca5]c8INY; Y++ (upper byte index).
[dca6]b1 02LDA (Temp_Addr_L),Y; Load that offset.
[dca8]18CLC
[dca9]69 80ADC #$80; Add 0x80 to the address, making it an absolute address.
[dcab]85 7dSTA CurrentArea_TableAddr_U; Store as the upper byte of the current area's metadata table address.
;
; Load the address of the block attributes pointer (not the
; block attributes data) from the metadata table. We'll read
; the first entry to get that address.
;
[dcad]a0 00LDY #$00; Y = 0.
[dcaf]b1 7cLDA (CurrentArea_TableAddr),Y; Load the first byte from the current area's metadata table.
[dcb1]85 02STA Temp_Addr_L; Store temporarily as the lower byte of an address.
[dcb3]c8INY; Y++
[dcb4]b1 7cLDA (CurrentArea_TableAddr),Y; Load the upper byte.
[dcb6]18CLC
[dcb7]69 80ADC #$80; Normalize to an absolute address.
[dcb9]85 03STA Temp_Addr_U; Store it.
;
; Store the addresses of the block attributes and 4 groups of
; block data in addresses {@address 007E}. This is done with
; a simple load loop, which corresponds to the order in which
; the data is represented in RAM.
;
[dcbb]a0 00LDY #$00; Y = 0.
[dcbd]a2 05LDX #$05; X = 5.
[dcbf]@_loadBlockInfoLoop:
[dcbf]b1 02LDA (Temp_Addr_L),Y; Load the lower byte of the address at the current index.
[dcc1]99 7e 00STA a:CurrentArea_BlockAttributesAddr,Y; Store it in RAM at the current index.
[dcc4]c8INY; Y++ (upper byte).
[dcc5]b1 02LDA (Temp_Addr_L),Y; Load the upper byte.
[dcc7]18CLC
[dcc8]69 80ADC #$80; Normalize to an absolute address.
[dcca]99 7e 00STA a:CurrentArea_BlockAttributesAddr,Y; Store it in RAM.
[dccd]c8INY; Y++ (next entry).
[dcce]caDEX; X-- (loop counter).
[dccf]d0 eeBNE @_loadBlockInfoLoop; If > 0, loop.
;
; Load the block properties (entry 2).
;
[dcd1]a0 02LDY #$02; Y = 2 (entry index).
[dcd3]b1 7cLDA (CurrentArea_TableAddr),Y; Load the lower byte of the data address.
[dcd5]85 88STA CurrentArea_BlockPropertiesAddr; Store it.
[dcd7]c8INY; Y++ (upper byte).
[dcd8]b1 7cLDA (CurrentArea_TableAddr),Y; Load the upper byte.
[dcda]18CLC
[dcdb]69 80ADC #$80; Normalize to an absolute address.
[dcdd]85 89STA CurrentArea_BlockPropertiesAddr_U; Store it.
;
; Load the scrolling data (entry 4).
;
[dcdf]a0 04LDY #$04; Y = 4 (entry index).
[dce1]b1 7cLDA (CurrentArea_TableAddr),Y; Load the lower byte of the data address.
[dce3]85 8aSTA CurrentArea_ScrollingDataAddr; Store it.
[dce5]c8INY; Y++ (upper byte).
[dce6]b1 7cLDA (CurrentArea_TableAddr),Y; Load the upper byte.
[dce8]18CLC
[dce9]69 80ADC #$80; Normalize to an absolute address.
[dceb]85 8bSTA ScrollingData_U; Store it.
;
; Load the door locations (entry 6).
;
[dced]a0 06LDY #$06; Y = 6 (entry index).
[dcef]b1 7cLDA (CurrentArea_TableAddr),Y; Load the lower byte of the data address.
[dcf1]85 8dSTA CurrentArea_DoorLocationsAddr; Store it.
[dcf3]c8INY; Y++ (upper byte).
[dcf4]b1 7cLDA (CurrentArea_TableAddr),Y; Load the upper byte.
[dcf6]18CLC
[dcf7]69 80ADC #$80; Normalize to an absolute address.
[dcf9]85 8eSTA CurrentArea_DoorLocationsAddr_U; Store it.
;
; Load the door destinations (entry 8).
;
[dcfb]a0 08LDY #$08; Y = 8 (entry index).
[dcfd]b1 7cLDA (CurrentArea_TableAddr),Y; Load the lower byte of the data address.
[dcff]85 8fSTA CurrentArea_DoorDestinationsAddr; Store it.
[dd01]c8INY; Y++ (upper byte).
[dd02]b1 7cLDA (CurrentArea_TableAddr),Y; Load the upper byte.
[dd04]18CLC
[dd05]69 80ADC #$80; Normalize to an absolute address.
[dd07]85 90STA CurrentArea_DoorDestinationsAddr_U; Store it.
;
; Switch back to the previous bank.
;
[dd09]68PLA; Restore the previous bank.
[dd0a]aaTAX; X = result.
[dd0b]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
[dd0e]60RTS
;============================================================================
; Update the scroll state during a mainloop iteration.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_UpdateForScroll
;
; XREFS:
;
Game_MainLoop
;============================================================================
[dd0f]Game_UpdateForScroll:
[dd0f]20 ce d2JSR Screen_UpdateForScroll
[dd12]60RTS
[dd13]Screen_SetupNew:
[dd13]20 12 edJSR Player_DrawSpriteImmediately; Draw the player sprite.
[dd16]20 80 ceJSR Sprites_LoadCommon; Load all common sprites.
[dd19]a5 64LDA Area_LoadingScreenIndex
[dd1b]85 63STA Area_CurrentScreen
[dd1d]20 f6 d0JSR Area_ScrollScreenRight
[dd20]20 eb f8JSR UI_DrawHUDSprites
[dd23]a5 65LDA Screen_DestPaletteOrIndex
[dd25]8d d0 03STA a:Screen_PaletteIndex
[dd28]20 3b d0JSR Screen_LoadUIPalette
;
; Reset the screen scrolls.
;
[dd2b]a9 00LDA #$00
[dd2d]85 59STA PPU_ScrollScreenHoriz; Clear the horizontal scroll.
[dd2f]85 58STA PPU_ScrollScreenVert; Clear the vertical scroll.
[dd31]85 9fSTA Screen_Maybe_ScrollXCounter
[dd33]85 a2STA Player_Something_ScrollPosY
[dd35]a5 6cLDA Screen_StartPosYX
[dd37]29 f0AND #$f0
[dd39]85 a1STA Player_PosY
[dd3b]a5 6cLDA Screen_StartPosYX
[dd3d]0aASL A
[dd3e]0aASL A
[dd3f]0aASL A
[dd40]0aASL A
[dd41]85 9eSTA Player_PosX_Block
[dd43]4c 00 c0JMP UI_SetHUDPPUAttributes
[dd46]Screen_Load:
[dd46]20 13 ddJSR Screen_SetupNew; Draw the HUD and prepare setup for the screen.
[dd49]a9 00LDA #$00
[dd4b]20 62 d0JSR Screen_LoadSpritePalette; Load the palette.
;
; v-- Fall through --v
;
;============================================================================
; Set up sprites and palette for the screen.
;
; This will clear out the existing sprites and then load
; in new ones.
;
; After load, the screen will be set as ready and the
; palette written.
;
; INPUTS:
;
Screen_ReadyState:
; The screen's current ready state.
;
; OUTPUTS:
;
Screen_ReadyState:
; The new state, set to ready.
;
; CALLS:
;
Screen_ClearSprites
;
Screen_LoadSpriteInfo
;
PPUBuffer_WritePalette
;
; XREFS:
;
Game_EnterBuilding
;============================================================================
[dd4e]Screen_SetupSprites:
;
; Clear all the sprite data for the screen and load in
; new sprites.
;
; Note that this code checks
Screen_ReadyState
; and only loads if it's not 1, but in the shipped game the
; only values are 0 and 0xFF, so the path always runs.
;
[dd4e]20 30 c1JSR Screen_ClearSprites; Clear current sprite data.
[dd51]a5 0eLDA Screen_ReadyState; Get the screen state.
[dd53]c9 01CMP #$01; Is it 1? (DEADCODE: It never will be).
[dd55]f0 03BEQ @_setReady; If not, skip loading sprites.
;
; Load sprite information. Despite the conditional, this
; will always execute, since the screen state is never 1
; in the shipped again.
;
[dd57]20 b4 c1JSR Screen_LoadSpriteInfo; Load information for the screen.
;
; Mark the screen as ready, and write the palette.
;
[dd5a]@_setReady:
[dd5a]a9 00LDA #$00
[dd5c]85 0eSTA Screen_ReadyState; Set the state to ready.
[dd5e]4c 90 d0JMP PPUBuffer_WritePalette; Write the palette to the PPU buffer.
;============================================================================
; Spawn the player in a temple.
;
; This is called when starting up a new life for the player.
; The player will be spawned in a temple, placed in the
; specified area and screen, using the correct palette and
; player positions.
;
; INPUTS:
;
TempleSpawnPoint:
; The index of the template in which the player will
; be spawned.
;
; OUTPUTS:
;
Area_CurrentScreen:
; The starting screen outside the template.
;
;
Area_Region:
; The area in which the player will be placed outside
; the template.
;
;
Player_PosX_Block:
; The X position the player will be in outside the
; temple.
;
;
Player_PosY:
; The Y position the player will be in outside the
; temple.
;
;
Area_DestScreen:
; TODO
;
;
Area_CurrentArea:
; TODO
;
;
Screen_PaletteIndex:
; TODO: The index of the palette outside the temple?
;
;
Area_Music_Outside:
; The music to play outside the temple.
;
;
Screen_DestPaletteOrIndex:
; The palette inside the temple.
;
;
Building_TilesIndex:
; Index for the building tiles.
;
;
Screen_StartPosYX:
; The start position inside the temple.
;
;
Areas_DefaultMusic:
; The music to play inside the temple.
;
;
Area_LoadingScreenIndex:
; TODO
;
;
Player_Flags:
; The new player flags (set to face left inside
; the temple).
;
; XREFS:
;
Player_Spawn
;============================================================================
[dd61]Game_SpawnInTemple:
;
; Set the starting area/screen/position information outside the
; temple.
;
[dd61]ae 39 04LDX a:TempleSpawnPoint; X = index of the temple spawn point.
[dd64]bd d5 ddLDA a:START_SCREEN_FOR_TEMPLE_SPAWN,X; Set the current screen for outside the temple.
[dd67]85 63STA Area_CurrentScreen
[dd69]bd cd ddLDA a:START_MAYBE_NEXT_AREA_FOR_TEMPLE_SPAWN,X
[dd6c]8d 35 04STA a:Area_Region
[dd6f]bd b5 ddLDA a:START_PLAYERPOSX_FULL_FOR_TEMPLE_SPAWN,X
[dd72]85 9eSTA Player_PosX_Block
[dd74]bd bd ddLDA a:MAYBE_START_PLAYERPOSY_FOR_TEMPLE_SPAWN,X
[dd77]85 a1STA Player_PosY
[dd79]bd c5 ddLDA a:DEST_SCREEN_FOR_TEMPLE_SPAWN,X
[dd7c]8d da 03STA a:Area_DestScreen
[dd7f]bc ad ddLDY a:CURRENT_REGION_FOR_TEMPLE_SPAWN,X
[dd82]84 24STY Area_CurrentArea
[dd84]b9 4c dfLDA a:AREA_PALETTE_INDEXES,Y
[dd87]8d d0 03STA a:Screen_PaletteIndex
[dd8a]b9 5c dfLDA a:AREA_TO_MUSIC_TABLE,Y
[dd8d]8d d2 03STA a:Area_Music_Outside
[dd90]a9 12LDA #$12
[dd92]85 65STA Screen_DestPaletteOrIndex
[dd94]a9 06LDA #$06
[dd96]8d d9 03STA a:Building_TilesIndex
[dd99]a9 9eLDA #$9e
[dd9b]85 6cSTA Screen_StartPosYX
[dd9d]a9 0eLDA #$0e
[dd9f]8d d1 03STA a:Areas_DefaultMusic
[dda2]a9 01LDA #$01
[dda4]85 64STA Area_LoadingScreenIndex
;
; Set the player to face left inside the temple.
;
[dda6]a5 a4LDA Player_Flags
[dda8]29 bfAND #$bf
[ddaa]85 a4STA Player_Flags
[ddac]60RTS
;============================================================================
; A mapping of Temple spawn positions to regions.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddad]CURRENT_REGION_FOR_TEMPLE_SPAWN:
[ddad]00.byte REGION_EOLIS; [0]: First town
[ddae]03.byte REGION_BRANCH; [1]: Apolune
[ddaf]03.byte REGION_BRANCH; [2]: Forepaw
[ddb0]02.byte REGION_MIST; [3]: Fog
[ddb1]03.byte REGION_BRANCH; [4]: Victim
[ddb2]03.byte REGION_BRANCH; [5]: Conflate
[ddb3]03.byte REGION_BRANCH; [6]: Daybreak
[ddb4]03.byte REGION_BRANCH; [7]: Final town
;============================================================================
; A mapping of Temple spawn positions to outside X positions.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddb5]START_PLAYERPOSX_FULL_FOR_TEMPLE_SPAWN:
[ddb5]50.byte $50; [0]: First town
[ddb6]50.byte $50; [1]: Apolune
[ddb7]30.byte $30; [2]: Forepaw
[ddb8]90.byte $90; [3]: Fog
[ddb9]30.byte $30; [4]: Victim
[ddba]90.byte $90; [5]: Conflate
[ddbb]60.byte $60; [6]: Daybreak
[ddbc]30.byte $30; [7]: Final town
;============================================================================
; A mapping of Temple spawn positions to outside Y positions.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddbd]MAYBE_START_PLAYERPOSY_FOR_TEMPLE_SPAWN:
[ddbd]90.byte $90; [0]: First town
[ddbe]90.byte $90; [1]: Apolune
[ddbf]90.byte $90; [2]: Forepaw
[ddc0]80.byte $80; [3]: Fog
[ddc1]90.byte $90; [4]: Victim
[ddc2]90.byte $90; [5]: Conflate
[ddc3]90.byte $90; [6]: Daybreak
[ddc4]90.byte $90; [7]: Final town
;============================================================================
; TODO: A mapping of Temple spawn positions to <TODO>.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddc5]DEST_SCREEN_FOR_TEMPLE_SPAWN:
[ddc5]02.byte $02; [0]: First town
[ddc6]0b.byte $0b; [1]: Apolune
[ddc7]10.byte $10; [2]: Forepaw
[ddc8]1e.byte $1e; [3]: Fog
[ddc9]23.byte $23; [4]: Victim
[ddca]2b.byte $2b; [5]: Conflate
[ddcb]33.byte $33; [6]: Daybreak
[ddcc]3c.byte $3c; [7]: Final town
;============================================================================
; A mapping of Temple spawn positions to next areas.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddcd]START_MAYBE_NEXT_AREA_FOR_TEMPLE_SPAWN:
[ddcd]00.byte REGION_EOLIS; [0]: First town
[ddce]01.byte REGION_TRUNK; [1]: Apolune
[ddcf]01.byte REGION_TRUNK; [2]: Forepaw
[ddd0]02.byte REGION_MIST; [3]: Fog
[ddd1]02.byte REGION_MIST; [4]: Victim
[ddd2]03.byte REGION_BRANCH; [5]: Conflate
[ddd3]03.byte REGION_BRANCH; [6]: Daybreak
[ddd4]04.byte REGION_DARTMOOR; [7]: Final town
;============================================================================
; A mapping of Temple spawn positions to start screens.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddd5]START_SCREEN_FOR_TEMPLE_SPAWN:
[ddd5]02.byte $02; [0]: First town
[ddd6]01.byte $01; [1]: Apolune
[ddd7]03.byte $03; [2]: Forepaw
[ddd8]06.byte $06; [3]: Fog
[ddd9]06.byte $06; [4]: Victim
[ddda]08.byte $08; [5]: Conflate
[dddb]0b.byte $0b; [6]: Daybreak
[dddc]0c.byte $0c; [7]: Final town
[dddd]EndGame_MoveToKingsRoom:
;
; Move the player back to Eolis.
;
[dddd]a9 00LDA #$00
[dddf]85 64STA Area_LoadingScreenIndex; Set the screen index to 0.
[dde1]8d 35 04STA a:Area_Region; Set the region to Eolis.
;
; Play the Temple music.
;
[dde4]a9 0dLDA #$0d
[dde6]8d d1 03STA a:Areas_DefaultMusic; Play the Temple music.
;
; Move the player to the right of the room.
;
[dde9]a9 9dLDA #$9d
[ddeb]85 6cSTA Screen_StartPosYX; Set the start position to X=13, Y=9.
;
; Load the palette for the King's room.
;
[dded]a9 11LDA #$11
[ddef]85 65STA Screen_DestPaletteOrIndex; Set the Palette for the King's room.
;
; Set the King's Room screen.
;
[ddf1]a9 44LDA #$44
[ddf3]85 63STA Area_CurrentScreen; Set the screen to 68.
[ddf5]8d da 03STA a:Area_DestScreen
[ddf8]a9 06LDA #$06
[ddfa]8d d9 03STA a:Building_TilesIndex; Set the tiles to 6.
;
; Face the player to the left.
;
[ddfd]a5 a4LDA Player_Flags; Load the player flags.
[ddff]29 bfAND #$bf; Set to facing left.
[de01]85 a4STA Player_Flags; Store it.
[de03]4c af daJMP Game_SetupEnterBuilding; Trigger the Enter Building logic.
;============================================================================
; TODO: Document Game_EnterBuilding
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_SetupEnterBuilding
;============================================================================
[de06]Game_EnterBuilding:
;
; Unset the player's current weapon. Players can't attack in
; buildings.
;
[de06]a9 ffLDA #$ff
[de08]8d c8 03STA a:Player_CurWeapon; Clear the weapon.
;
; Set the music for this area.
;
[de0b]ad d1 03LDA a:Areas_DefaultMusic; Load the default music for the area.
[de0e]85 faSTA Music_Current; Set as the current music.
;
; Push a buch of state to return to when exiting the door.
;
[de10]a5 24LDA Area_CurrentArea; Load the current area.
[de12]8d d5 03STA a:Area_SavedArea; Save it for when exiting the building.
[de15]a5 63LDA Area_CurrentScreen; Load the current screen in the area.
[de17]8d d6 03STA a:Area_SavedScreen; Save it.
[de1a]ad d0 03LDA a:Screen_PaletteIndex; Load the current palette.
[de1d]8d d7 03STA a:Screen_SavedPalette; Save it.
;
; Save the player's X/Y position (rounded to the nearest
; block).
;
[de20]a5 a1LDA Player_PosY; Load the player's Y position.
[de22]29 f0AND #$f0; Normalize to a block offset.
[de24]8d d8 03STA a:Player_SavedPosXY; Save it.
[de27]a5 9eLDA Player_PosX_Block; Load the player's block X position.
[de29]29 f0AND #$f0; Normalize it to a block offset.
[de2b]4aLSR A; Shift to the lower byte.
[de2c]4aLSR A
[de2d]4aLSR A
[de2e]4aLSR A
[de2f]0d d8 03ORA a:Player_SavedPosXY; Combine it with the saved X/Y position.
[de32]8d d8 03STA a:Player_SavedPosXY; And save it.
;
; Set the new area to enter and load it.
;
[de35]a2 04LDX #$04
[de37]86 24STX Area_CurrentArea; Set the current area to 4 (inside buildings)
[de39]bd 34 dfLDA a:AREA_TO_SCREEN_DATA_BANKS,X; Load the bank for this area.
[de3c]85 8cSTA CurrentArea_ROMBank; Store as the current area ROM bank.
[de3e]a6 8cLDX CurrentArea_ROMBank
[de40]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo; Push the current bank and switch to the new one.
[de43]a6 24LDX Area_CurrentArea; Load the current area again.
[de45]bd 3c dfLDA a:AREA_TO_BANK_OFFSET,X; Get the index into the screen data table in this bank.
[de48]20 78 dcJSR Game_LoadAreaTable; Load the area table for the screen.
[de4b]20 e7 ccJSR MMC1_RestorePrevROMBank; Restore the previous bank.
;
; Set the index into the tiles for this area and load them.
;
[de4e]ad d9 03LDA a:Building_TilesIndex
[de51]85 95STA Area_TilesIndex
[de53]20 b8 ceJSR Area_LoadTiles
;
; Load the palette.
;
[de56]a9 01LDA #$01
[de58]20 62 d0JSR Screen_LoadSpritePalette
;
; Set up the screen and the sprites.
;
[de5b]20 13 ddJSR Screen_SetupNew
[de5e]ad da 03LDA a:Area_DestScreen
[de61]85 63STA Area_CurrentScreen
[de63]4c 4e ddJMP Screen_SetupSprites
;============================================================================
; TODO: Document Game_ExitBuilding
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_SetupExitBuilding
;============================================================================
[de66]Game_ExitBuilding:
[de66]ad bd 03LDA a:SelectedWeapon
[de69]8d c8 03STA a:Player_CurWeapon
[de6c]ad d1 03LDA a:Areas_DefaultMusic
[de6f]85 faSTA Music_Current
[de71]ae d5 03LDX a:Area_SavedArea
[de74]86 24STX Area_CurrentArea
[de76]bd 34 dfLDA a:AREA_TO_SCREEN_DATA_BANKS,X
[de79]85 8cSTA CurrentArea_ROMBank
[de7b]a6 8cLDX CurrentArea_ROMBank
[de7d]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo
[de80]a6 24LDX Area_CurrentArea
[de82]bd 3c dfLDA a:AREA_TO_BANK_OFFSET,X
[de85]20 78 dcJSR Game_LoadAreaTable
[de88]20 e7 ccJSR MMC1_RestorePrevROMBank
[de8b]a6 24LDX Area_CurrentArea
[de8d]bd 44 dfLDA a:AREA_TO_TILES_INDEXES,X
[de90]85 95STA Area_TilesIndex
[de92]20 b8 ceJSR Area_LoadTiles
[de95]ad d6 03LDA a:Area_SavedScreen
[de98]85 64STA Area_LoadingScreenIndex
[de9a]ad d7 03LDA a:Screen_SavedPalette
[de9d]85 65STA Screen_DestPaletteOrIndex
[de9f]ad d8 03LDA a:Player_SavedPosXY
[dea2]85 6cSTA Screen_StartPosYX
[dea4]4c 46 ddJMP Screen_Load
[dea7]Game_LoadFirstLevel:
[dea7]a9 00LDA #$00; A = 0
;
; Set this as the current town.
;
[dea9]85 24STA Area_CurrentArea; Set the current area to 0 (Eolis).
;
; Set the initial state of the player, and hard-code their
; health to the starting amount of 16HP.
;
[deab]20 aa e0JSR Player_SetInitialState; Set the initial player state.
[deae]a9 10LDA #$10
[deb0]8d 31 04STA a:Player_HP_U; Set the HP to 16.
[deb3]a6 24LDX Area_CurrentArea; X = Current area
;
; Load the block properties for this area.
;
; The bank containing the properties for the area will
; be loaded temporarily while fetching the block properties.
;
[deb5]bd 34 dfLDA a:AREA_TO_SCREEN_DATA_BANKS,X; A = PRG bank for the area.
[deb8]85 8cSTA CurrentArea_ROMBank; Store as the current bank.
[deba]a6 8cLDX CurrentArea_ROMBank; X = current bank.
[debc]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo; Push the current bank and update to this area's bank.
;
; Load the area information.
;
; NOTE: There's a bug here. This *should* be passing in the
; area index, but it's mistakenly passing in the resulting
; ROM bank. In the shipped game, this is 0 in both cases,
; but it means that any shuffling around of banks will do
; the wrong thing if not starting with area 0, bank 0.
;
[debf]bd 3c dfLDA a:AREA_TO_BANK_OFFSET,X; A = sprite bank for the area.
[dec2]20 78 dcJSR Game_LoadAreaTable; Load the area information.
[dec5]20 e7 ccJSR MMC1_RestorePrevROMBank; Restore the previous bank.
;
; Start on the current screen.
;
[dec8]a6 24LDX Area_CurrentArea; X = current area.
[deca]a9 00LDA #$00
[decc]85 63STA Area_CurrentScreen; Current screen = 0
[dece]85 64STA Area_LoadingScreenIndex; Screen index = 0
;
; Load the tiles and images for the area.
;
[ded0]a6 24LDX Area_CurrentArea; X = current area.
[ded2]bd 44 dfLDA a:AREA_TO_TILES_INDEXES,X; A = tiles index.
[ded5]85 95STA Area_TilesIndex; Store that.
[ded7]20 b8 ceJSR Area_LoadTiles; Load the tileset pages for that index.
;
; Load the palette for the area.
;
[deda]a9 00LDA #$00
[dedc]20 62 d0JSR Screen_LoadSpritePalette; Load the palette for that index.
[dedf]a6 24LDX Area_CurrentArea; X = current area.
[dee1]bd 4c dfLDA a:AREA_PALETTE_INDEXES,X; Load the palette.
[dee4]85 65STA Screen_DestPaletteOrIndex; And store it as the current one for the area.
;
; Load the music for this area.
;
[dee6]bd 5c dfLDA a:AREA_TO_MUSIC_TABLE,X; Load the music for the area.
[dee9]85 faSTA Music_Current; And store it as the current music.
[deeb]8d d1 03STA a:Areas_DefaultMusic; And as the default for the area.
;
; Set the start X/Y position for the player as X=1, Y=9.
;
[deee]a9 91LDA #$91
[def0]85 6cSTA Screen_StartPosYX; Set start position.
[def2]4c 46 ddJMP Screen_Load; Load this screen.
[def5]Game_LoadCurrentArea:
[def5]20 aa e0JSR Player_SetInitialState; Set the initial player state.
;
; Load the bank for this area.
;
[def8]a6 24LDX Area_CurrentArea; X = current area.
[defa]bd 34 dfLDA a:AREA_TO_SCREEN_DATA_BANKS,X; Load the ROM bank for the area.
[defd]85 8cSTA CurrentArea_ROMBank; Store that as the current bank.
[deff]a6 8cLDX CurrentArea_ROMBank; X = current bank.
[df01]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo; Save the current bank and switch to it.
;
; Load the bank for the sprites for this area.
;
[df04]a6 24LDX Area_CurrentArea; X = current area.
[df06]bd 3c dfLDA a:AREA_TO_BANK_OFFSET,X; A = bank for the sprites.
;
; Load information on the area.
;
[df09]20 78 dcJSR Game_LoadAreaTable; Load the table of information on this area.
[df0c]20 e7 ccJSR MMC1_RestorePrevROMBank; Restore to the previous bank.
[df0f]a5 64LDA Area_LoadingScreenIndex; A = current screen index.
[df11]85 63STA Area_CurrentScreen; Store as the current screen.
;
; Load the tiles for the area.
;
[df13]a6 24LDX Area_CurrentArea; X = current area.
[df15]bd 44 dfLDA a:AREA_TO_TILES_INDEXES,X; A = tiles index for the area.
[df18]85 95STA Area_TilesIndex; Store as the current tiles index.
[df1a]20 b8 ceJSR Area_LoadTiles; Load all tileset images for that index.
;
; Load the palette for the area.
;
[df1d]a9 00LDA #$00
[df1f]20 62 d0JSR Screen_LoadSpritePalette; Load palette 0.
[df22]a6 24LDX Area_CurrentArea; X = current area.
[df24]bd 4c dfLDA a:AREA_PALETTE_INDEXES,X; Load the palette for the area.
[df27]85 65STA Screen_DestPaletteOrIndex; Store as the current palette.
;
; Load the music for the area.
;
[df29]bd 5c dfLDA a:AREA_TO_MUSIC_TABLE,X; Load the music for the area.
[df2c]85 faSTA Music_Current; Set as the current music.
[df2e]8d d1 03STA a:Areas_DefaultMusic; Store as the default.
[df31]4c 46 ddJMP Screen_Load; Load this screen.
[df34]AREA_TO_SCREEN_DATA_BANKS:
[df34]00.byte BANK_0_AREA_DATA; [0]: Eolis
[df35]01.byte BANK_1_AREA_DATA; [1]: Trunk
[df36]00.byte BANK_0_AREA_DATA; [2]: Mist
[df37]00.byte BANK_0_AREA_DATA; [3]: Towns
[df38]AREA_TO_SCREEN_DATA_BANKS_4_:
[df38]02.byte BANK_2_AREA_DATA; [4]: Buildings
[df39]01.byte BANK_1_AREA_DATA; [5]: Branch
[df3a]02.byte BANK_2_AREA_DATA; [6]: Dartmoor
[df3b]02.byte BANK_2_AREA_DATA; [7]: Zenis
[df3c]AREA_TO_BANK_OFFSET:
[df3c]00.byte $00; [0]: Eolis
[df3d]00.byte $00; [1]: Trunk
[df3e]01.byte $01; [2]: Mist
[df3f]02.byte $02; [3]: Towns
[df40]AREA_TO_BANK_OFFSET_4_:
[df40]01.byte $01; [4]: Buildings
[df41]01.byte $01; [5]: Branch
[df42]00.byte $00; [6]: Dartmoor
[df43]02.byte $02; [7]: Zenis
[df44]AREA_TO_TILES_INDEXES:
[df44]00.byte $00; [0]: Eolis
[df45]02.byte $02; [1]: Trunk
[df46]03.byte $03; [2]: Mist
[df47]05.byte $05; [3]: Towns
[df48]06.byte $06; [4]: Buildings
[df49]01.byte $01; [5]: Branch
[df4a]04.byte $04; [6]: Dartmoor
[df4b]04.byte $04; [7]: Zenis / Evil Fortress
[df4c]AREA_PALETTE_INDEXES:
[df4c]00.byte PALETTE_EOLIS; [0]: Eolis
[df4d]06.byte PALETTE_OUTSIDE; [1]: Trunk
[df4e]0a.byte PALETTE_MIST; [2]: Mist
[df4f]1b.byte PALETTE_TOWN; [3]: Towns
[df50]1b.byte PALETTE_TOWN; [4]: Buildings
[df51]08.byte PALETTE_BRANCH; [5]: Branch
[df52]0c.byte PALETTE_DARTMOOR; [6]: Dartmoor
[df53]0f.byte PALETTE_EVIL_FORTRESS; [7]: Zenis / Evil Fortress
;============================================================================
; MOD NOTES: 8 bytes of unused data.
;============================================================================
[df54]00 00 00 00 00 00 00 00.byte $00,$00,$00,$00,$00,$00,$00,$00; undefined
[df5c]AREA_TO_MUSIC_TABLE:
[df5c]07.byte MUSIC_EOLIS; [0]: Eolis
[df5d]03.byte MUSIC_APOLUNE; [1]: Trunk
[df5e]05.byte MUSIC_FOREPAW; [2]: Mist
[df5f]09.byte MUSIC_MASCON_VICTIM; [3]: Towns
[df60]09.byte MUSIC_MASCON_VICTIM; [4]: Buildings
[df61]04.byte MUSIC_CONFLATE; [5]: Branch
[df62]02.byte MUSIC_DAYBREAK; [6]: Dartmoor
[df63]10.byte MUSIC_EVIL_FORTRESS; [7]: Zenith / Evil Fortress
[df64]Game_EnterAreaHandler:
;
; Load the bank containing the screen data for this area.
;
[df64]a6 24LDX Area_CurrentArea; Load the current area.
[df66]bd 34 dfLDA a:AREA_TO_SCREEN_DATA_BANKS,X; Get the bank for the area's screen data.
[df69]85 8cSTA CurrentArea_ROMBank; Store as the new area bank.
[df6b]a6 8cLDX CurrentArea_ROMBank; Load that area bank back into X.
[df6d]20 15 ccJSR MMC1_SaveROMBankAndUpdateTo; Push the bank to the stack and switch to it.
;
; Load the area table.
;
[df70]a6 24LDX Area_CurrentArea; Load the current area.
[df72]bd 3c dfLDA a:AREA_TO_BANK_OFFSET,X; Load the starting pointer index in the loaded bank.
[df75]20 78 dcJSR Game_LoadAreaTable; Load the area table at that index.
[df78]20 e7 ccJSR MMC1_RestorePrevROMBank; Pop the previous bank.
;
; Set the current screen to the target.
;
[df7b]a5 64LDA Area_LoadingScreenIndex; Load the target screen index.
[df7d]85 63STA Area_CurrentScreen; Set as the current screen.
;
; Load the music for the area.
;
[df7f]a6 24LDX Area_CurrentArea; Load the current area.
[df81]bd 5c dfLDA a:AREA_TO_MUSIC_TABLE,X; Load the index of the music for this area.
[df84]85 faSTA Music_Current; Store as the new current music.
[df86]8d d1 03STA a:Areas_DefaultMusic; Store as the default music for this area.
;
; Load the tiles for the area.
;
[df89]bd 44 dfLDA a:AREA_TO_TILES_INDEXES,X; Load the index for the tile data for this area.
[df8c]85 95STA Area_TilesIndex; Store as the current tiles index.
[df8e]20 b8 ceJSR Area_LoadTiles; Load the tiles for the area into CHR RAM.
;
; Load the sprite palette.
;
[df91]a9 00LDA #$00
[df93]20 62 d0JSR Screen_LoadSpritePalette; Populate the sprite palette.
;
; Load the screen state.
;
[df96]4c 46 ddJMP Screen_Load; Load the screen data.
;============================================================================
; Debug code to switch the current area region.
;
; This allows the player to control which part of the game
; they're in.
;
; By pressing Up on the second controller, the player will
; be moved up a region.
;
; By pressing Down, they player will be moved down a region.
;
; This isn't activated by default, but can be swapped in
; through the following Game Genie codes (overriding the
; ability to pause):
;
; OPYISU
; NIYIVU
;
; INPUTS:
;
Joy2_ButtonMask:
; The second controller's button mask.
;
; OUTPUTS:
;
Area_Region:
; The updated region.
;
;
Area_CurrentScreen:
; Set to 0.
;
; CALLS:
;
Game_SetupAndLoadOutsideArea
;============================================================================
[df99]Debug_ChooseArea:
[df99]a5 17LDA Joy2_ButtonMask; Load the player 2 controller buttons.
[df9b]29 0cAND #$0c; Is Up or Down pressed?
[df9d]f0 3eBEQ RETURN_DFDD; If not, return.
;
; Figure out which region to switch to based on button press.
;
[df9f]29 04AND #$04; Is Down pressed?
[dfa1]d0 11BNE @_nextArea; If so, switch to the next area.
;
; Up was pressed. Go forward a region.
;
[dfa3]ee 35 04INC a:Area_Region; Increment the region.
[dfa6]ad 35 04LDA a:Area_Region; A = incremented region.
[dfa9]c9 06CMP #$06; Is it in bounds?
[dfab]90 11BCC @_switchArea; If so, switch to the area.
[dfad]a9 00LDA #$00; Else...
[dfaf]8d 35 04STA a:Area_Region; Set the region to (Eolis).
[dfb2]f0 0aBEQ @_switchArea; And switch to it.
;
; Down was pressed. Go back a region.
;
[dfb4]@_nextArea:
[dfb4]ce 35 04DEC a:Area_Region; Decrement the region.
[dfb7]10 05BPL @_switchArea; If it's in bounds, switch to the previous area.
[dfb9]a9 05LDA #$05; Else, set the region to the final fortress.
[dfbb]8d 35 04STA a:Area_Region; Store as the new region.
;
; Set the current screen to 0 and load it.
;
[dfbe]@_switchArea:
[dfbe]a9 00LDA #$00
[dfc0]85 63STA Area_CurrentScreen; Set current screen to 0.
[dfc2]4c dc daJMP Game_SetupAndLoadOutsideArea; Load the area.
;============================================================================
; Update the fog state on tick.
;
; This will first check if the area supports fog (Forepaw
; area with the Mist palette).
;
; If fog is allowed, it will proceed to animate fog
; every odd tick and update fog state every even tick.
;
; INPUTS:
;
Area_CurrentArea:
; The current area.
;
;
Screen_PaletteIndex:
; The screen's palette.
;
;
Fog_StateIndex:
; The current fog index.
;
;
Fog_TileIndex:
; The current fog generator value.
;
; OUTPUTS:
;
Fog_StateIndex:
; The updated fog index.
;
;
Fog_TileIndex:
; The updated fog generator value.
;
; CALLS:
;
Fog_UpdateTiles
;
; XREFS:
;
EndGame_MainLoop
;
Game_MainLoop
;============================================================================
[dfc5]Fog_OnTick:
[dfc5]a5 24LDA Area_CurrentArea; Load the current area.
[dfc7]c9 02CMP #$02; Is it Forepaw?
[dfc9]d0 46BNE RETURN_E011; If not, return.
[dfcb]ad d0 03LDA a:Screen_PaletteIndex; Load the current palette.
[dfce]c9 0aCMP #$0a; Is it Mist?
[dfd0]d0 3fBNE RETURN_E011; If not, return.
[dfd2]a5 1eLDA Fog_StateIndex; Load the fog index.
[dfd4]4aLSR A; Is it an odd value?
[dfd5]90 07BCC Fog_UpdateTiles; If so, jump to rotate tiles.
;
; Decrement the fog generator and check if it reached 0.
; If so, increment the fog index.
;
[dfd7]c6 1dDEC Fog_TileIndex; Decrement the fog generator.
[dfd9]d0 36BNE RETURN_E011; If != 0, return.
[dfdb]e6 1eINC Fog_StateIndex; Else, increment the fog index.
[dfdd]RETURN_DFDD:
[dfdd]60RTS
;============================================================================
; Animate a fog tile and update fog generation state.
;
; This will take a fog tile represented by the current
; fog generation value and rotate each row of its contents
; one pixel to the right, wrapping around to the left.
;
; The fog generation value is then incremented by 2,
; wrapping around from 7 to 0. If it hits 0, the fog
; index is inremented and a new starting fog generation
; value is chosen based on that index.
;
; INPUTS:
;
Fog_TileIndex:
; The current fog generator value.
;
;
Fog_StateIndex:
; The current fog index value.
;
;
PPUBuffer_WriteOffset:
; The current upper bounds of the PPU buffer.
;
; OUTPUTS:
;
Fog_TileIndex:
; The updated fog generator value.
;
;
Fog_StateIndex:
; The updated fog index value.
;
;
PPUBuffer:
; The updated PPU buffer, with the rotate-right
; command scheduled.
;
;
PPUBuffer_WriteOffset:
; The updated upper bounds of the PPU buffer.
;
;
; XREFS:
;
Fog_OnTick
;============================================================================
[dfde]Fog_UpdateTiles:
[dfde]a6 20LDX PPUBuffer_WriteOffset; Load the upper bounds of the fog generator.
;
; Queue drawing command "Rotate tiles right."
;
[dfe0]a9 fcLDA #$fc; 0xFC == Rotate Tiles Right draw command.
[dfe2]9d 00 05STA a:PPUBuffer,X; Set as the command.
;
; Set the upper byte of the tile address to 0x18.
;
[dfe5]e8INX; X++
[dfe6]a9 18LDA #$18; 0x18 == Upper byte of the sprite tile address.
[dfe8]9d 00 05STA a:PPUBuffer,X; Set it as the upper byte for the draw command.
;
; Calculate a lower byte for the tile based on the
; fog tile index.
;
; This will be the existing value * 16 (tile data size).
;
[dfeb]e8INX; X++
[dfec]a5 1dLDA Fog_TileIndex; Load the fog generator value.
[dfee]e6 1dINC Fog_TileIndex; Increment for later (not involved in the math).
[dff0]0aASL A; Multiply the value we loaded by 16.
[dff1]0aASL A
[dff2]0aASL A
[dff3]0aASL A
[dff4]9d 00 05STA a:PPUBuffer,X; Set as the lower byte for the draw command.
;
; Set the new upper bounds for the PPU buffer.
;
; This will increment by 3.
;
[dff7]e8INX; X++
[dff8]86 20STX PPUBuffer_WriteOffset; Set as the new upper bounds.
;
; Update the fog tile index.
;
; It'll increment by 2 (from above and here), keeping and
; wrapping around with a value range of 0..7.
;
[dffa]e6 1dINC Fog_TileIndex; Increment the fog generator value.
[dffc]a5 1dLDA Fog_TileIndex; Load the result.
[dffe]29 07AND #$07; Keep it in range 0..7.
[e000]85 1dSTA Fog_TileIndex; Store it back.
[e002]d0 0dBNE RETURN_E011; If != 0, we're done.
;
; The fog generator value hit 0. Increment the fog
; index and look up a new starting fog generator value.
;
[e004]e6 1eINC Fog_StateIndex; Increment the fog index.
[e006]a5 1eLDA Fog_StateIndex; Load it.
[e008]4aLSR A; Divide by 2.
[e009]29 03AND #$03; And convert to an index in the lookup table.
[e00b]aaTAX; X = A
[e00c]bd 12 e0LDA a:FOG_START_TILE_INDEXES,X; Load a starting fog generator value at that index.
[e00f]85 1dSTA Fog_TileIndex; And set it.
[e011]RETURN_E011:
[e011]60RTS
[e012]FOG_START_TILE_INDEXES:
[e012]18.byte $18; [0]:
[e013]06.byte $06; [1]:
[e014]30.byte $30; [2]:
[e015]0c.byte $0c; [3]:
;============================================================================
; Check whether the Player Menu can and should be shown.
;
; The menu can be shown anywhere other than the first
; screen of the first town (where the game begins).
;
; If the menu can be shown, then it will be shown if
; the Select button has been pressed.
;
; INPUTS:
;
Area_CurrentArea:
; The current area.
;
;
Area_CurrentScreen:
; The current screen of the current area.
;
;
Joy1_ChangedButtonMask:
; The bitmask of newly-pressed buttons.
;
; OUTPUTS:
; None
;
; CALLS:
;
MMC1_LoadBankAndJump
;
; XREFS:
;
EndGame_MainLoop
;
Game_MainLoop
;============================================================================
[e016]GameLoop_CheckShowPlayerMenu:
;
; Check to make sure we're not outside the walls of Eolis.
;
[e016]a5 24LDA Area_CurrentArea; A = current area.
[e018]d0 04BNE @_allowInventory; If it's > Eolis, the player can open the inventory. Jump.
[e01a]a5 63LDA Area_CurrentScreen; A = current screen.
[e01c]f0 0cBEQ RETURN_E02A; If it's 0, then we're outside the walls. No inventory allowed. Return.
[e01e]@_allowInventory:
[e01e]a5 19LDA Joy1_ChangedButtonMask; A = controller 1 button mask.
[e020]29 20AND #$20; Is Select pressed?
[e022]f0 06BEQ RETURN_E02A; If not, return.
[e024]20 59 f8JSR MMC1_LoadBankAndJump; Open the Player Menu.
[e027]0c.byte BANK_12_LOGIC; Bank = 12
[e028]92 8apointer PlayerMenu_Show-1; Address = PlayerMenu_Show
[e02a]RETURN_E02A:
[e02a]60RTS
;============================================================================
; Check whether the player has paused.
;
; If they have, then stay paused until the player
; unpauses.
;
; This function won't return until the player unpaused.
; While paused,
Game_PausedState will be set.
;
; INPUTS:
;
Joy1_ChangedButtonMask:
; The changed button mask to check.
;
; OUTPUTS:
;
Game_PausedState:
; 1 while paused. 0 on return.
;
; CALLS:
;
WaitForNextFrame
;
Sprites_FlipRanges
;
; XREFS:
;
Game_MainLoop
;============================================================================
[e02b]GameLoop_CheckPauseGame:
;
; Check if the player pressed Start.
;
[e02b]a5 19LDA Joy1_ChangedButtonMask; Load the button state.
[e02d]29 10AND #$10; Check if the Pause button is pressed.
[e02f]f0 f9BEQ RETURN_E02A; If paused...
;
; The player paused. Set the flag and wait until unpaused.
;
[e031]a9 01LDA #$01
[e033]8d 20 01STA a:Game_PausedState; Set the Paused flag.
[e036]@_waitForUnpause:
[e036]20 25 caJSR WaitForNextFrame; Wait...
[e039]20 a8 cbJSR Sprites_FlipRanges
;
; Check if the player unpaused.
;
[e03c]a5 19LDA Joy1_ChangedButtonMask; Check if the Pause button was pressed again.
[e03e]29 10AND #$10
[e040]f0 f4BEQ @_waitForUnpause; Until pause is pressed again, loop.
;
; Unpause the game.
;
[e042]a9 00LDA #$00; Clear the Paused flag.
[e044]8d 20 01STA a:Game_PausedState
[e047]60RTS
;============================================================================
; Update player state during a screen scroll.
;
; This will preserve the controller buttons that were
; pressed at the beginning of the screen scroll, regardless
; of any changes made to those buttons since.
;
; It will then position the player based off of a scroll
; transition counter, and increment that counter. This
; counter helps coordinate at which points the player's
; sprite appears to move and where it ends up.
;
; INPUTS:
;
Joy1_PrevButtonMask:
; The controller 1 buttons pressed at the beginning
; of the screen scroll.
;
;
Screen_ScrollPlayerTransitionCounter:
; The counter used to control player position during
; scroll.
;
; OUTPUTS:
;
Joy1_ButtonMask:
; The restored button mask.
;
;
Screen_ScrollPlayerTransitionCounter:
; The updated counter.
;
; CALLS:
;
Game_MovePlayerOnScroll
;
; XREFS:
;
Game_MainLoop
;
Screen_UpdateForScroll
;============================================================================
[e048]Game_UpdatePlayerOnScroll:
;
; Preserve the previous pressed buttons while scrolling.
;
[e048]a5 18LDA Joy1_PrevButtonMask; Load the previous button mask.
[e04a]85 16STA Joy1_ButtonMask; Set it as the current button mask.
[e04c]20 6a e0JSR Game_MovePlayerOnScroll; Update the player position.
[e04f]e6 b4INC Screen_ScrollPlayerTransitionCounter; Increment the screen scroll transition counter.
[e051]60RTS
;============================================================================
; TODO: Document Game_MovePlayerOnScrollLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_MovePlayerOnScroll
;============================================================================
[e052]Game_MovePlayerOnScrollLeft:
;
; The screen is scrolling to the screen to the left.
;
; If we're in the first 4 frames of the screen transition,
; move the player left 4px per frame until it wraps the
; screen.
;
[e052]a5 b4LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e054]c9 04CMP #$04; Is it >= 4?
[e056]b0 0dBCS @_updateScrollX; If so, jump.
[e058]a5 9eLDA Player_PosX_Block; Load the player X position.
[e05a]38SEC
[e05b]e9 04SBC #$04; Subtract 4.
[e05d]85 9eSTA Player_PosX_Block; Store it.
[e05f]a5 9fLDA Screen_Maybe_ScrollXCounter; Load our screen scroll counter.
[e061]e9 00SBC #$00; Subtract carry.
[e063]85 9fSTA Screen_Maybe_ScrollXCounter; Store it.
[e065]@_updateScrollX:
[e065]a5 9eLDA Player_PosX_Block
[e067]85 b2STA Maybe_PlayerX_ForScroll
[e069]60RTS
;============================================================================
; Move the player while the screen is scrolling.
;
; This will position the player on the screen based on the
; state of the screen scroll.
;
; When scrolling horizontally, it will inch the player
; toward the nearest screen boundary until it wraps.
;
; When scrolling vertically, nothing useful happens.
;
; NOTE: The function is full of useless code that's not
; needed. Conditionals and variable settings that
; never get used by anything else.
;
; Y scrolling effects aren't in Faxanadu, and the
; code managing that doesn't end up doing anything
; useful.
;
; For X scrolling, we set some variables that also
; either don't get used or don't have any true
; impact on the logic in which they're used.
;
; INPUTS:
;
Screen_ScrollDirection:
; The direction in which the screen is scrolling.
;
;
Screen_ScrollPlayerTransitionCounter:
; The transition counter used during screen scroll
; to help position the player.
;
;
Player_PosX_Block:
; The player's current X position.
;
; OUTPUTS:
;
Player_PosX_Block:
; The updated player position.
;
;
Screen_Maybe_ScrollXCounter:
; An updated X value used during screen scroll.
; This is set in the first 4 frames of scrolling
; left or right, but is thought to ultimately be
; useless.
;
;
Maybe_PlayerX_ForScroll:
;
Maybe_PlayerY_ForScroll
; Ultimately unused values. Remnants of dead code.
;
; XREFS:
;
Game_UpdatePlayerOnScroll
;============================================================================
[e06a]Game_MovePlayerOnScroll:
;
; Check which direction the screen is scrolling.
;
[e06a]a6 54LDX Screen_ScrollDirection; Load the screen scroll direction.
[e06c]f0 e4BEQ Game_MovePlayerOnScrollLeft; If 0, jump to handle scrolling left.
[e06e]caDEX
[e06f]f0 21BEQ @_scrollRight; If 1, jump to handle scrolling right.
[e071]caDEX
[e072]f0 0fBEQ @_scrollUp; If 2, jump to handle scrolling up.
;
; The screen is scrolling to the screen below.
;
[e074]a5 b4LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e076]c9 08CMP #$08; Is it < 8?
[e078]90 08BCC @_return1; If so, return.
[e07a]a5 b3LDA Maybe_PlayerY_ForScroll; Load the player's Y position during scroll.
[e07c]38SEC
[e07d]e9 04SBC #$04; Subtract 4
[e07f]85 b3STA Maybe_PlayerY_ForScroll; Store it.
[e081]38SEC
[e082]@_return1:
[e082]60RTS
;
; The screen is scrolling to the screen above.
;
[e083]@_scrollUp:
[e083]a5 b4LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e085]c9 08CMP #$08; Is it < 8?
[e087]90 08BCC @_return2; If so, return.
[e089]a5 b3LDA Maybe_PlayerY_ForScroll; Load the player Y position during scroll.
[e08b]18CLC
[e08c]69 04ADC #$04; Add 4.
[e08e]85 b3STA Maybe_PlayerY_ForScroll; Store it.
[e090]38SEC
[e091]@_return2:
[e091]60RTS
;
; The screen is scrolling to the screen to the right.
;
; If we're in the first 4 frames of the screen transition,
; move the player right 4px per frame until it wraps the
; screen.
;
[e092]@_scrollRight:
[e092]a5 b4LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e094]c9 04CMP #$04; Is it >= 4?
[e096]b0 0dBCS @_finishScrollRight; If so, jump.
[e098]a5 9eLDA Player_PosX_Block; Load the player X position.
[e09a]18CLC
[e09b]69 04ADC #$04; Add 4.
[e09d]85 9eSTA Player_PosX_Block; Store it.
[e09f]a5 9fLDA Screen_Maybe_ScrollXCounter; Load our screen scroll counter.
[e0a1]69 00ADC #$00; Add carry.
[e0a3]85 9fSTA Screen_Maybe_ScrollXCounter; Store it.
[e0a5]@_finishScrollRight:
[e0a5]a5 9eLDA Player_PosX_Block
[e0a7]85 b2STA Maybe_PlayerX_ForScroll
[e0a9]60RTS
[e0aa]Player_SetInitialState:
;
; Clear scroll state.
;
[e0aa]a9 00LDA #$00
[e0ac]85 9fSTA Screen_Maybe_ScrollXCounter
[e0ae]85 a2STA Player_Something_ScrollPosY
[e0b0]85 58STA PPU_ScrollScreenVert; DEADCODE: Overridden below.
[e0b2]85 59STA PPU_ScrollScreenHoriz
;
; Set the starting speed, status, and iframes.
;
[e0b4]85 a9STA Player_MoveAcceleration; Set the player speed to 0.
[e0b6]85 a5STA Player_StatusFlag; Set the player status to 0.
[e0b8]85 adSTA Player_InvincibilityPhase; Set the invincibility phase to 0.
[e0ba]85 58STA PPU_ScrollScreenVert; Set the vertical scroll to 0.
;
; Face the player to the right.
;
[e0bc]a9 40LDA #$40; 0x04 = Face player right bit.
[e0be]85 a4STA Player_Flags; Set that on the player's flags.
;
; Set the accessory information.
;
[e0c0]a9 ffLDA #$ff
[e0c2]85 a8STA Player_DrawTileReadOffset
;
; Unused value. This is written to but never read from.
;
[e0c4]a9 02LDA #$02
[e0c6]85 abSTA Player_Unused_00ab
[e0c8]60RTS
[e0c9]RETURN_E0C9:
[e0c9]60RTS
[e0ca]GameLoop_UpdatePlayer:
[e0ca]20 03 e1JSR Player_CheckHandleAttack; Check if the player has attacked and handle it.
[e0cd]20 c8 e2JSR Player_CheckHandleVertMovement
[e0d0]20 74 e1JSR Player_CheckHandleJump; Check if the player has jumped and handle it.
[e0d3]20 e8 e0JSR Player_HandleIFrames; Handle any pending iframes.
;
; Check if screen scrolling is occurring.
;
[e0d6]a5 54LDA Screen_ScrollDirection; A = scrolling activity.
[e0d8]c9 04CMP #$04; Is the screen scrolling in either direction?
[e0da]90 edBCC RETURN_E0C9; If so, return.
;
; The screen is not scrolling.
;
[e0dc]20 26 e5JSR Player_CheckHandleEnterDoor; Check if the player is entering a door.
[e0df]20 05 e9JSR Player_CheckOnBreakableBlock
[e0e2]20 5d e9JSR Player_CheckPushingBlock; Check if the player is pushing the block to open a path to Mascon.
[e0e5]4c c0 e9JMP Player_CheckSwitchScreen; Check the block the player is on and handle it.
;============================================================================
; Handle any invincibility frames that may be set.
;
; Invincibility frames start at 0x3C.
;
; If the player has invincibility frames, the frame counter
; will be reduced by 1.
;
; The user's knockback will be reset when the counter hits
; 0x38, at which point the player can safely move.
;
; The user is invincible until the counter hits 0.
;
; INPUTS:
;
Player_InvincibilityPhase:
; The current invincibility phase, from 0x3C to 0.
;
;
Player_StatusFlag:
; The player's current status flag.
;
; OUTPUTS:
;
Player_StatusFlag:
; The new status flag with knockback removed, when
; hitting 0x39 or lower.
;
; CALLS:
;
Player_SetStandardAcceleration
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e0e8]Player_HandleIFrames:
[e0e8]a5 adLDA Player_InvincibilityPhase; Check the current invincibility phase.
[e0ea]f0 10BEQ @_removeIFrames; If the phase is now 1, remove iframe status.
;
; Invincibility is active. Reduce it by 1.
;
[e0ec]c6 adDEC Player_InvincibilityPhase; Reduce our invincibility phase by 1.
;
; Check where we are in the phase. It starts at 0x3C, and
; until it hits 0x39, it will be in knockback phase.
;
[e0ee]a5 adLDA Player_InvincibilityPhase; Check the invincibility phase.
[e0f0]c9 39CMP #$39; Is it 0x39?
[e0f2]f0 05BEQ @_resetSpeed; If so, reset the speed but don't change the player status.
[e0f4]90 06BCC @_removeIFrames; If not over, update the player status.
;
; Set the knockback speed and return, leaving the knockback
; state intact.
;
[e0f6]4c 7f e2JMP Player_SetStandardAcceleration; Set standard acceleration.
;
; Set the knockback speed, but then clear the knockback.
;
[e0f9]@_resetSpeed:
[e0f9]20 7f e2JSR Player_SetStandardAcceleration; Reset the player speed.
;
; Clear knockback from the player flags.
;
[e0fc]@_removeIFrames:
[e0fc]a5 a5LDA Player_StatusFlag; Load the player's flags.
[e0fe]29 fdAND #$fd; Clear the knockback bit.
[e100]85 a5STA Player_StatusFlag; Save them.
[e102]60RTS
;============================================================================
; Check if the player is attacking and handle it.
;
; If the player is not yet attacking, and is triggering
; an attack ("B" button hit) while in a position to attack
; (not climbing), this will begin the attack phase.
;
; If the player is attacking, this will manage the current
; animation frame based on the attack phase (defined as
;
PLAYER_ATTACK_ANIMATION_PHASES).
;
; INPUTS:
;
Joy1_ChangedButtonMask:
; The controller 1 button mask.
;
;
Player_Flags:
; The player's current flags.
;
;
Player_StatusFlag:
; The player's status flags.
;
;
PLAYER_ATTACK_ANIMATION_PHASES:
; The table of animation phases to timer lengths.
;
; OUTPUTS:
;
Player_AttackPhaseTimer:
; The timer for the current attack phase.
;
;
Player_AttackPhaseAnimFrame:
; The current animation frame for the phase.
;
;
Player_Flags:
; Player flags with the attacking bit set/cleared.
;
;
Player_StatusFlag:
; Player stauts flags with the attacking bit
; set/cleared.
;
; CALLS:
;
Player_IsClimbing
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e103]Player_CheckHandleAttack:
[e103]a5 a4LDA Player_Flags; Load the player's flags.
[e105]30 2bBMI @_updateHitPhase; If already attacking, jump.
;
; The player is not yet attacking. Check first if the
; player is allowed to attack.
;
[e107]20 f6 ecJSR Player_IsClimbing; Is the player climbing?
[e10a]b0 0dBCS @_clearAttacking; If so, clear any attacking state and return.
;
; The player isn't climbing. Check if they've attacked.
;
[e10c]a5 19LDA Joy1_ChangedButtonMask; Load the controller 1 button mask.
[e10e]29 40AND #$40; Is the B button set?
[e110]f0 07BEQ @_clearAttacking; If not, clear any attacking state and return.
[e112]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[e114]29 01AND #$01; Was the player in attack mode?
[e116]f0 08BEQ @_setAttacking; If so, set the attacking state.
[e118]60RTS
;
; Clear the attacking state for the player.
;
[e119]@_clearAttacking:
[e119]a5 a5LDA Player_StatusFlag; Load the player's flags.
[e11b]29 feAND #$fe; Clear the Attacking bit.
[e11d]85 a5STA Player_StatusFlag; Store it.
[e11f]60RTS
;
; Set the attacking state for the player.
;
; This will update the flags and begin the animation
; phase timer.
;
[e120]@_setAttacking:
[e120]a5 a4LDA Player_Flags; Load the player's flags.
[e122]09 80ORA #$80; Set the Attacking bit.
[e124]85 a4STA Player_Flags; Store it.
[e126]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[e128]09 01ORA #$01; Set the Attacking bit.
[e12a]85 a5STA Player_StatusFlag; Store it.
;
; Clear the phase state.
;
[e12c]a9 00LDA #$00
[e12e]85 acSTA Player_AttackPhaseTimer; Set phase timer to 0.
[e130]85 aeSTA Player_AttackPhaseAnimFrame; Set phase counter to 0.
[e132]@_updateHitPhase:
[e132]e6 acINC Player_AttackPhaseTimer; Increment the current phase timer.
[e134]a5 acLDA Player_AttackPhaseTimer; A = updated phase timer.
[e136]a6 aeLDX Player_AttackPhaseAnimFrame; X = phase counter.
[e138]dd 50 e1CMP a:PLAYER_ATTACK_ANIMATION_PHASES,X; Check against the phase lookup table.
[e13b]90 0bBCC @_return; If we're not transitioning to the next phase count, return.
;
; We're done with that phase. Move to the next and
; reset the timer.
;
[e13d]a9 00LDA #$00
[e13f]85 acSTA Player_AttackPhaseTimer; Reset the current phase's timer to 0.
[e141]e8INX; Phase counter++
[e142]e0 03CPX #$03; Is it >= 3 (end of phases)?
[e144]b0 03BCS @_finishAttacking; If so, finish the attacking state.
[e146]86 aeSTX Player_AttackPhaseAnimFrame; Else, increment the phase counter for the next frame.
[e148]@_return:
[e148]60RTS
[e149]@_finishAttacking:
[e149]a5 a4LDA Player_Flags; Load the player's flags.
[e14b]29 7fAND #$7f; Clear the Attacking flag.
[e14d]85 a4STA Player_Flags; Store it.
[e14f]RETURN_E14F:
[e14f]60RTS
;============================================================================
; Mapping of player phase indexes to total animation frames.
;
; XREFS:
;
Player_CheckHandleAttack
;============================================================================
[e150]PLAYER_ATTACK_ANIMATION_PHASES:
[e150]08.byte $08; [0]: 8 frames thrusting out weapon
[e151]03.byte $03; [1]: 3 frames of waiting
[e152]08.byte $08; [2]: 8 frames withdrawing weapon
;============================================================================
; Handle knockback when getting hit.
;
; This will knock the player back in the direction opposite
; where the player was facing.
;
; It will temporarily flip the facing bit and then move in
; that direction. The original bit will then be restored.
;
; INPUTS:
;
Player_Flags:
; The player's flags.
;
; OUTPUTS:
;
Player_Flags:
; The updated flags, with any new state applied
; from the move but the flipped bit retained.
;
;
Temp_00:
; Clobbered.
;
; CALLS:
;
Player_KnockbackHoriz
;
; XREFS:
;
Player_CheckHandleJump
;============================================================================
[e153]Player_HandleKnockback:
;
; Temporarily store the player's current flags.
;
[e153]a5 a4LDA Player_Flags; Load the player's flags.
[e155]29 40AND #$40; Take only the facing bit.
[e157]48PHA; Push it to the stack.
;
; Flip the facing bit and knock back in the flipped direction.
;
[e158]49 40EOR #$40; Flip the facing bit temporarily for the purpose of knockback.
[e15a]85 a4STA Player_Flags; Store as the new flags.
[e15c]20 6b e1JSR Player_KnockbackHoriz; Move based on the facing bit.
;
; Restore the original flags, merging with any updated bits
; from the move.
;
[e15f]68PLA; Pop the original flags.
[e160]85 00STA Temp_00; Store it.
[e162]a5 a4LDA Player_Flags; Load the flags set above.
[e164]29 bfAND #$bf; Clear the facing bit.
[e166]05 00ORA Temp_00; OR with the original facing bit.
[e168]85 a4STA Player_Flags; Store it.
[e16a]60RTS
;============================================================================
; Move the player left or right due to knockback.
;
; The player will be knocked back based on the direction
; they're facing.
;
; INPUTS:
;
Player_Flags:
; The player's current flags.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Player_TryMoveLeft
;
Player_TryMoveRight
;
; XREFS:
;
Player_HandleKnockback
;============================================================================
[e16b]Player_KnockbackHoriz:
[e16b]a5 a4LDA Player_Flags; Load the player's flags.
[e16d]29 40AND #$40; Is the player facing right?
[e16f]d0 5eBNE Player_TryMoveRight; If so, move right.
[e171]4c 20 e2JMP Player_TryMoveLeft
;============================================================================
; TODO: Document Player_CheckHandleJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e174]Player_CheckHandleJump:
;
; Check if the screen is scrolling. If so, bail.
;
[e174]a5 54LDA Screen_ScrollDirection; Load the scroll direction.
[e176]10 d7BPL RETURN_E14F; If set, return.
;
; Check if knockback is active. Handle that if so.
;
[e178]a5 a5LDA Player_StatusFlag; Load the player's status flag.
[e17a]29 02AND #$02; Check for knockback.
[e17c]d0 d5BNE Player_HandleKnockback; If knockback is set, handle that and return.
;
; Check if the player's flying.
;
[e17e]a5 a5LDA Player_StatusFlag; Load the player's status flag.
[e180]30 15BMI @_playerCanFly; If the player is flying,
[e182]a5 a4LDA Player_Flags; Load the player's flags.
[e184]29 05AND #$05
[e186]f0 0fBEQ @_playerCanFly
[e188]a5 a4LDA Player_Flags
[e18a]29 20AND #$20
[e18c]f0 1eBEQ @_playerNotMoving
[e18e]a5 a4LDA Player_Flags
[e190]29 40AND #$40
[e192]d0 3bBNE Player_TryMoveRight
[e194]4c 20 e2JMP Player_TryMoveLeft
[e197]@_playerCanFly:
[e197]a5 a4LDA Player_Flags
[e199]30 23BMI @LAB_PRG15_MIRROR__e1be
[e19b]a5 16LDA Joy1_ButtonMask
[e19d]29 03AND #$03
[e19f]f0 0bBEQ @_playerNotMoving
[e1a1]e6 a3INC Player_MovementTick
[e1a3]4aLSR A
[e1a4]b0 29BCS Player_TryMoveRight
[e1a6]4aLSR A
[e1a7]90 03BCC @_playerNotMoving
[e1a9]4c 20 e2JMP Player_TryMoveLeft
[e1ac]@_playerNotMoving:
[e1ac]a5 16LDA Joy1_ButtonMask
[e1ae]29 0cAND #$0c
[e1b0]f0 0cBEQ @LAB_PRG15_MIRROR__e1be
[e1b2]a5 9eLDA Player_PosX_Block
[e1b4]29 0fAND #$0f
[e1b6]f0 06BEQ @LAB_PRG15_MIRROR__e1be
[e1b8]a5 a4LDA Player_Flags
[e1ba]29 08AND #$08
[e1bc]d0 07BNE @LAB_PRG15_MIRROR__e1c5
[e1be]@LAB_PRG15_MIRROR__e1be:
[e1be]a5 a4LDA Player_Flags
[e1c0]29 dfAND #$df
[e1c2]85 a4STA Player_Flags
[e1c4]60RTS
[e1c5]@LAB_PRG15_MIRROR__e1c5:
[e1c5]e6 a3INC Player_MovementTick
[e1c7]a5 9eLDA Player_PosX_Block
[e1c9]29 0fAND #$0f
[e1cb]c9 08CMP #$08
[e1cd]90 51BCC Player_TryMoveLeft
[e1cf]Player_TryMoveRight:
;
; Check whether the player is moving.
;
[e1cf]a5 a4LDA Player_Flags; Load the player's flags.
[e1d1]29 20AND #$20; Check the moving bit.
[e1d3]d0 03BNE @_setMoving; If set, jump to handle movement.
;
; The player is not moving. Reset the movement speed.
;
[e1d5]20 7f e2JSR Player_SetStandardAcceleration; Start with the standard acceleration.
;
; Calculate movement speed and set the player to move to
; the right.
;
[e1d8]@_setMoving:
[e1d8]20 91 e2JSR Player_UpdateAcceleration; Calculate the new walking speed.
[e1db]a5 a4LDA Player_Flags; Load the player's flags.
[e1dd]09 60ORA #$60; Set moving and to the right.
[e1df]85 a4STA Player_Flags; Store as the new flags.
;
; Update the player's X position to factor in acceleration.
;
[e1e1]a5 9dLDA Player_PosX_Frac; Load the fractional X position.
[e1e3]18CLC
[e1e4]65 a9ADC Player_MoveAcceleration; Add the move acceleration.
[e1e6]85 9dSTA Player_PosX_Frac; Store as the new fractional position.
[e1e8]a5 9eLDA Player_PosX_Block; Load the full X position.
[e1ea]65 aaADC Player_MoveAcceleration_U; Add the move acceleration.
[e1ec]85 9eSTA Player_PosX_Block; And store it.
;
; Check if the block there is passable.
;
[e1ee]a2 01LDX #$01
[e1f0]20 c8 e6JSR Player_CheckIfPassable; Is the block to the right passable?
[e1f3]f0 09BEQ @_checkAtRightEdge; If not, jump to check for an edge or transition boundary.
;
; The block is not passable. Cap the position.
;
[e1f5]a5 9eLDA Player_PosX_Block; Load the player X position.
[e1f7]29 f0AND #$f0; Cap it.
[e1f9]85 9eSTA Player_PosX_Block; And store it.
[e1fb]4c 1a e2JMP @_return
;
; Check if the player is far enough to the right for
; any screen transition logic.
;
[e1fe]@_checkAtRightEdge:
[e1fe]a5 9eLDA Player_PosX_Block; X = Player X position.
[e200]c9 f1CMP #$f1; Is it too far left to perform a screen transition?
[e202]90 16BCC @_return; If so, return.
;
; Check if the player is at the edge and can scroll.
;
[e204]a0 01LDY #$01
[e206]20 27 e6JSR Screen_IsEdge; Check if the screen is the right-most edge.
[e209]b0 10BCS Player_SetPosAtRightEdge; If so, jump to set the position there.
;
; There's a screen to the right. Transition there.
;
[e20b]a5 67LDA Area_ScreenToTheRight; A = Neighboring screen to the right.
[e20d]85 63STA Area_CurrentScreen; Store as the new current screen.
[e20f]a2 01LDX #$01
[e211]20 e9 e8JSR Area_ScrollTo; Begin scrolling to the right.
[e214]e6 9fINC Screen_Maybe_ScrollXCounter; Increment the scroll counter.
[e216]a9 00LDA #$00
[e218]85 b4STA Screen_ScrollPlayerTransitionCounter; Clear the transition counter.
[e21a]@_return:
[e21a]60RTS
;============================================================================
; Position the player at the right edge of the screen.
;
; The player will be positioned at 0xF0.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
Player_PosX_Block:
; The updated X position.
;
; XREFS:
;
Player_TryMoveRight
;============================================================================
[e21b]Player_SetPosAtRightEdge:
[e21b]a9 f0LDA #$f0
[e21d]85 9eSTA Player_PosX_Block; Set the X position to 0xF0.
[e21f]60RTS
[e220]Player_TryMoveLeft:
;
; Check whether the player is moving.
;
[e220]a5 a4LDA Player_Flags; Load the player's flags.
[e222]29 20AND #$20; Check the moving bit.
[e224]d0 03BNE @_setMoving; If set, jump to handle movement.
;
; The player is not moving. Reset the movement speed.
;
[e226]20 7f e2JSR Player_SetStandardAcceleration; Start with the standard acceleration.
;
; Calculate movement speed and set the player to move to
; the left.
;
[e229]@_setMoving:
[e229]20 91 e2JSR Player_UpdateAcceleration; Calculate the new walking speed.
[e22c]a5 a4LDA Player_Flags; Load the player's flags.
[e22e]29 bfAND #$bf; Set the player to face left.
[e230]09 20ORA #$20; Set the player to moving.
[e232]85 a4STA Player_Flags; Store as the new flags.
;
; Update the player's X position to factor in acceleration.
;
[e234]a5 9dLDA Player_PosX_Frac; Load the fractional X position.
[e236]38SEC
[e237]e5 a9SBC Player_MoveAcceleration; Subtract the move acceleration.
[e239]85 9dSTA Player_PosX_Frac; Store as the new fractional position.
[e23b]a5 9eLDA Player_PosX_Block; Load the full X position.
[e23d]e5 aaSBC Player_MoveAcceleration_U; Subtract the move acceleration.
[e23f]08PHP; Push all flags.
[e240]b0 02BCS @_saveFlags; If >= 0, jump.
[e242]a9 00LDA #$00; Else, cap at 0, so it doesn't wrap.
[e244]@_saveFlags:
[e244]85 9eSTA Player_PosX_Block; Store the new X position.
;
; Check if the block there is passable.
;
[e246]a2 00LDX #$00
[e248]20 c8 e6JSR Player_CheckIfPassable; Is the block to the left passable?
[e24b]f0 11BEQ @_checkAtLeftEdge; If not, jump to check for an edge or transition boundary.
;
; The block is not passable. Cap the position.
;
[e24d]28PLP; Pop the flags from the acceleration calculation.
[e24e]a5 9eLDA Player_PosX_Block; Load the player X position.
[e250]29 0fAND #$0f; Is this in the first 16 pixels?
[e252]f0 09BEQ @_return1; If so, we're done.
[e254]a5 9eLDA Player_PosX_Block; Load the player X position.
[e256]29 f0AND #$f0; Keep all but pixels 0-16.
[e258]18CLC
[e259]69 10ADC #$10; Add 16 pixels.
[e25b]85 9eSTA Player_PosX_Block; Store as the new position.
[e25d]@_return1:
[e25d]60RTS; And we're done.
;
; Check if the player is far enough to the left for
; any screen transition logic.
;
[e25e]@_checkAtLeftEdge:
[e25e]28PLP; Pop the flags from the acceleration calculation.
[e25f]b0 16BCS @_return2; If it didn't overflow off the left, return.
;
; Check if the player is at the edge and can scroll.
;
[e261]a0 00LDY #$00
[e263]20 27 e6JSR Screen_IsEdge; Check if the screen is the left-most edge.
[e266]b0 10BCS @_setAtLeftEdge; If at the edge, jump to cap the position.
;
; There's a screen to the left. Transition there.
;
[e268]a5 66LDA Area_ScreenToTheLeft; A = Neighboring screen to the left.
[e26a]85 63STA Area_CurrentScreen; Store as the new current screen.
[e26c]a2 00LDX #$00
[e26e]20 e9 e8JSR Area_ScrollTo; Begin scrolling to the left.
[e271]a9 00LDA #$00
[e273]85 9eSTA Player_PosX_Block; Set the X position to 0.
[e275]85 b4STA Screen_ScrollPlayerTransitionCounter; Clear the transition counter.
[e277]@_return2:
[e277]60RTS
;
; The player is at the left-most edge, but didn't pass it.
;
[e278]@_setAtLeftEdge:
[e278]a9 00LDA #$00
[e27a]85 9eSTA Player_PosX_Block; Set the X position to 0.
[e27c]60RTS; And return.
[e27d]00.byte $00; [0]:
[e27e]0f.byte $0f; [1]:
[e27f]Player_SetStandardAcceleration:
[e27f]a9 c0LDA #$c0
[e281]85 a9STA Player_MoveAcceleration; Set the lower byte of movement acceleation to 0xC0.
[e283]a9 00LDA #$00
[e285]85 aaSTA Player_MoveAcceleration_U; Set the upper byte to 0.
[e287]60RTS
;============================================================================
; Update the player's position based on knockback.
;
; This will accelerate the player by 8 pixels when its
; position is next updated.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
Player_MoveAcceleration:
;
Player_MoveAcceleration+1:
; The updated acceleration.
;
; XREFS:
;
Player_UpdateAcceleration
;============================================================================
[e288]Player_UpdatePosFromKnockback:
;
; The player's in knockback state. Bump the player
; back a bunch.
;
[e288]a9 00LDA #$00
[e28a]85 a9STA Player_MoveAcceleration; Set lower acceleration byte to 0.
[e28c]a9 08LDA #$08
[e28e]85 aaSTA Player_MoveAcceleration_U; Set upper to 8.
[e290]60RTS
;============================================================================
; Update the player's acceleration.
;
; This will determine if the player can accelerate and, if
; so, update the acceleration value based on their
; title/rank.
;
; If the player's in a knockback state, acceleration will
; stop and the position will be updated.
;
; If the player pressed Up or Down, or is in front of a
; climbable block, acceleration will reset.
;
; If the player's hit max acceleration (384), acceleration
; will not increase.
;
; Otherwise, acceleration will be updated based on their
; rank/title. Values are in 4 groups of 4 ranks each,
; meaning every 4 ranks, their acceleration will increase.
;
; INPUTS:
;
Player_Flags:
; The player's current flags.
;
;
Player_StatusFlag:
; The player's current status flags.
;
;
PlayerTitle:
; The player's current title/rank.
;
;
Joy1_ButtonMask:
; Controller 1's button mask.
;
;
Player_MoveAcceleration:
; The current move acceleration.
;
;
PLAYER_ACCELERATION_DELTA_BY_RANK_GROUP:
; The table of acceleration values by rank group.
;
; OUTPUTS:
;
Player_MoveAcceleration:
; The updated move acceleration.
;
; CALLS:
;
Player_SetStandardAcceleration
;
Player_UpdatePosFromKnockback
;
; XREFS:
;
Player_TryMoveLeft
;
Player_TryMoveRight
;============================================================================
[e291]Player_UpdateAcceleration:
;
; First, check if the player is in a knockback state. If so,
; instead of accelerating, set their position from knockback.
;
[e291]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[e293]29 02AND #$02; Keep the knockback bit.
[e295]d0 f1BNE Player_UpdatePosFromKnockback; If set, update the position from knockback and return.
;
; The player is not in a knockback state, so proceed with
; acceleration.
;
[e297]a5 16LDA Joy1_ButtonMask; Load the pressed buttons.
[e299]29 0cAND #$0c; Check if Up or Down is pressed.
[e29b]f0 06BEQ @_updateAcceleration; If not, jump to update acceleration.
[e29d]a5 a4LDA Player_Flags; Load the player's flags.
[e29f]29 08AND #$08; Check if the player can currently climb.
[e2a1]d0 dcBNE Player_SetStandardAcceleration; If so, switch to standard acceleration and return.
;
; The player is moving and can accelerate. Calculate the
; acceleration.
;
; First, check if the acceleration >= 384, which I believe
; represents max acceleration.
;
[e2a3]@_updateAcceleration:
[e2a3]a5 a9LDA Player_MoveAcceleration; Load the lower byte of the current acceleration.
[e2a5]c9 80CMP #$80; If >= 128, C=1. Else, C=0.
[e2a7]a5 aaLDA Player_MoveAcceleration_U; And load the upper byte.
[e2a9]e9 01SBC #$01; If >= 128 above, subtract (1 + C). That equals 1 if C=0, 2 if C=1.
[e2ab]b0 16BCS @_return; If acceleration >= 384 (-128 unsigned), return.
;
; Max acceleration wasn't hit, so calculate acceleration.
;
; This will be done based on the player's title and how much
; they've moved.
;
; Every 4 ranks, acceleration will change, so we'll take the
; current one and divide by 4 to get that grouping.
;
[e2ad]ad 37 04LDA a:PlayerTitle; Load the player's title.
[e2b0]4aLSR A; Divide by 4 (acceleration changes every 4 ranks).
[e2b1]4aLSR A
[e2b2]29 03AND #$03; Cap to the size of the lookup table.
[e2b4]aaTAX; X = resulting table index.
[e2b5]a5 a9LDA Player_MoveAcceleration; A = Current acceleration (lower byte).
[e2b7]18CLC
[e2b8]7d c4 e2ADC a:PLAYER_ACCELERATION_DELTA_BY_RANK_GROUP,X; Add acceleration for the current rank.
[e2bb]85 a9STA Player_MoveAcceleration; Store as the new lower byte value.
[e2bd]a5 aaLDA Player_MoveAcceleration_U; Load the upper byte.
[e2bf]69 00ADC #$00; Increment if lower byte wrapped.
[e2c1]85 aaSTA Player_MoveAcceleration_U; Store it.
[e2c3]@_return:
[e2c3]60RTS
;============================================================================
; Mapping table of rank groups to acceleration deltas.
;
; Each entry is indexed by a 4-span of ranks, mapping to
; a value that should be added to the player's acceleration
; every update tick.
;
;
; XREFS:
;
Player_UpdateAcceleration
;============================================================================
[e2c4]PLAYER_ACCELERATION_DELTA_BY_RANK_GROUP:
[e2c4]02.byte $02; [0]: Novice - Aspirant
[e2c5]04.byte $04; [1]: Adept - Warrior
[e2c6]06.byte $06; [2]: Swordman - Myrmidon
[e2c7]08.byte $08; [3]: Champion - Lord
;============================================================================
; TODO: Document Player_CheckHandleVertMovement
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e2c8]Player_CheckHandleVertMovement:
;
; First, check if the Wing Boots are active or the A
; button is pressed.
;
[e2c8]a5 a5LDA Player_StatusFlag; A = Player status flags.
[e2ca]10 0aBPL @_checkClimbLadder; If Wing Boots are not active, jump.
[e2cc]a5 16LDA Joy1_ButtonMask; A = Controller 1 button mask.
[e2ce]10 06BPL @_checkClimbLadder; If A is not pressed, jump.
;
; Wing Boots are active and the A button is pressed.
;
[e2d0]20 52 e7JSR Player_CheckIfOnLadder; Set state based on whether the user's in front of a ladder/climbable block.
[e2d3]4c f4 e2JMP @_checkClimbUpDown
[e2d6]@_checkClimbLadder:
[e2d6]20 52 e7JSR Player_CheckIfOnLadder; Set state based on whether the user's in front of a ladder/climbable block.
[e2d9]a5 a4LDA Player_Flags; Load the player's flags.
[e2db]29 08AND #$08; Is the Can Climb flag set?
[e2dd]f0 1fBEQ @_checkClimbSide; If not, jump to check climbing to the side.
[e2df]a5 16LDA Joy1_ButtonMask; Load the controller 1 button mask.
[e2e1]29 0cAND #$0c; Check if Up or Down is pressed.
[e2e3]f0 19BEQ @_checkClimbSide; If not, check climbing to the side.
;
; The player is climbing. Set the state.
;
[e2e5]a5 a4LDA Player_Flags; Load the player's flags.
[e2e7]09 10ORA #$10; Set the climbing bit.
[e2e9]85 a4STA Player_Flags; Save it.
;
; Check if the player is evenly on a ladder (aligned with the
; sides of the ladder, and not in-between ladders or falling
; off).
;
; If evenly on a ladder, they'll climb up or down. If not,
; they'll be re-aligned first, or will fall off, depending
; on direction and nearby blocks.
;
[e2eb]a5 9eLDA Player_PosX_Block; Load the block X position the player is on.
[e2ed]29 0fAND #$0f; Round it.
[e2ef]f0 03BEQ @_checkClimbUpDown; If the player's evenly on the ladder, jump.
;
; The player is not evenly on the ladder.
;
[e2f1]4c 99 e3JMP Player_HandleVertLadderOrJump; Handle ladder movement.
;
; Check if the player is climbing up a ladder.
;
[e2f4]@_checkClimbUpDown:
[e2f4]a5 16LDA Joy1_ButtonMask; Load the Controller 1 button mask.
[e2f6]4aLSR A; Shift the "Down" bit into bit 0.
[e2f7]4aLSR A
[e2f8]4aLSR A
[e2f9]b0 4eBCS Player_CheckHandleClimbDown; If set, climb down.
;
; Check if the player is climbing down.
;
[e2fb]4aLSR A; Else, shift "Up" into bit 0.
[e2fc]b0 03BCS Player_CheckHandleClimbUp; If set, climb up.
[e2fe]@_checkClimbSide:
[e2fe]4c 99 e3JMP Player_HandleVertLadderOrJump; Else, handle general ladder movement.
;============================================================================
; TODO: Document Player_CheckHandleClimbUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleVertMovement
;============================================================================
[e301]Player_CheckHandleClimbUp:
;
; Clear the flags for jumping, falling, and moving.
;
[e301]a5 a4LDA Player_Flags; Load the player's flags.
[e303]29 daAND #$da; Clear the Jumping, Falling Off, and Moving flags.
[e305]85 a4STA Player_Flags; Save it.
;
; Check if the player is able to climb up a block.
;
[e307]a2 02LDX #$02; X = 2 (Up direction).
[e309]20 c8 e6JSR Player_CheckIfPassable; Check if the block up is passable.
[e30c]d0 35BNE @_return; If not, return.
[e30e]e6 a3INC Player_MovementTick; Increment the movement tick.
[e310]a5 a5LDA Player_StatusFlag; Load the player's status flags.
[e312]10 0aBPL @_wingBootsActive; If Wing Boots are active, jump.
;
; Wing Boots are not active, and the block is climbable,
; so move the player up 1.
;
[e314]a5 a1LDA Player_PosY; Load the player's Y position.
[e316]38SEC
[e317]e9 01SBC #$01; Subtract 1.
[e319]85 a1STA Player_PosY; Save it.
[e31b]4c 2b e3JMP @_checkScreenEdge
;
; Wing Boots are active.
;
[e31e]@_wingBootsActive:
[e31e]a5 a0LDA Player_ClimbFlySpeedModifier
[e320]38SEC
[e321]e9 a0SBC #$a0
[e323]85 a0STA Player_ClimbFlySpeedModifier
[e325]a5 a1LDA Player_PosY; Load the player's Y position.
[e327]e9 00SBC #$00; Subtract carry from above calculation, if set.
[e329]85 a1STA Player_PosY; Save it.
;
; Check the screen edge for wrapping.
;
; If subtracting 1 from the player's Y position did not wrap
; to negative, then there's no reason to check the edge, in
; which case we can bail early.
;
[e32b]@_checkScreenEdge:
[e32b]b0 16BCS @_return; If subtraction above did not wrap, return.
[e32d]a0 02LDY #$02; Y = 2 (Up direction).
[e32f]20 27 e6JSR Screen_IsEdge; Check if there's a screen above or if this is the edge.
[e332]b0 10BCS @_atEdge; If at the edge, jump.
;
; There's a screen above.
;
[e334]a5 68LDA Area_ScreenAbove; Load the index of the screen above.
[e336]85 63STA Area_CurrentScreen; Set as the new current screen.
;
; Scroll up.
;
[e338]a2 02LDX #$02; X = 2 (Up direction).
[e33a]20 e9 e8JSR Area_ScrollTo; Scroll up.
[e33d]c6 a2DEC Player_Something_ScrollPosY; Decrement the scrolling Y position by 1.
;
; Move the player to the bottom of the new screen.
;
[e33f]a9 c0LDA #$c0; Set the player's Y position to the bottom of the screen.
[e341]85 a1STA Player_PosY; Save it.
[e343]@_return:
[e343]60RTS
;
; The player's at the screen edge and can't go up, so cap
; the Y position to 0 so it doesn't wrap.
;
[e344]@_atEdge:
[e344]a9 00LDA #$00; A = 0.
[e346]85 a1STA Player_PosY; Set as the Y position.
[e348]60RTS
;============================================================================
; TODO: Document Player_CheckHandleClimbDown
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleVertMovement
;============================================================================
[e349]Player_CheckHandleClimbDown:
[e349]a5 a4LDA Player_Flags
[e34b]29 daAND #$da
[e34d]85 a4STA Player_Flags
[e34f]a2 03LDX #$03
[e351]20 c8 e6JSR Player_CheckIfPassable
[e354]d0 3dBNE RETURN_E393
[e356]e6 a3INC Player_MovementTick
[e358]a5 a5LDA Player_StatusFlag
[e35a]10 10BPL @LAB_PRG15_MIRROR__e36c
[e35c]a5 a0LDA Player_ClimbFlySpeedModifier
[e35e]18CLC
[e35f]69 80ADC #$80
[e361]85 a0STA Player_ClimbFlySpeedModifier
[e363]a5 a1LDA Player_PosY
[e365]69 01ADC #$01
[e367]85 a1STA Player_PosY
[e369]4c 79 e3JMP Player_MoveDownScreen
[e36c]@LAB_PRG15_MIRROR__e36c:
[e36c]a5 a0LDA Player_ClimbFlySpeedModifier
[e36e]18CLC
[e36f]69 c0ADC #$c0
[e371]85 a0STA Player_ClimbFlySpeedModifier
[e373]a5 a1LDA Player_PosY
[e375]69 00ADC #$00
[e377]85 a1STA Player_PosY
;============================================================================
; TODO: Document Player_MoveDownScreen
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbDown
;============================================================================
[e379]Player_MoveDownScreen:
[e379]c9 c1CMP #$c1
[e37b]90 16BCC RETURN_E393
[e37d]a0 03LDY #$03
[e37f]20 27 e6JSR Screen_IsEdge
[e382]b0 10BCS Player_SetPosAtBottomEdge
[e384]a5 69LDA Area_ScreenBelow
[e386]85 63STA Area_CurrentScreen
[e388]a2 03LDX #$03
[e38a]20 e9 e8JSR Area_ScrollTo
[e38d]e6 a2INC Player_Something_ScrollPosY
[e38f]a9 00LDA #$00
[e391]85 a1STA Player_PosY
[e393]RETURN_E393:
[e393]60RTS
;============================================================================
; Set the player at the bottom-most position of the screen.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
Player_PosY:
; The updated player position (set to 0xC0).
;
; XREFS:
;
Player_MoveDownScreen
;============================================================================
[e394]Player_SetPosAtBottomEdge:
[e394]a9 c0LDA #$c0
[e396]85 a1STA Player_PosY; Set player Y position to 0xC0 (192).
[e398]60RTS
;============================================================================
; TODO: Document Player_HandleVertLadderOrJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleVertMovement
;============================================================================
[e399]Player_HandleVertLadderOrJump:
;
; Check if there's a jump curve, and if it's in bounds.
;
[e399]a5 a6LDA Player_JumpCurveIndex; Load the jump curve index.
[e39b]c9 20CMP #$20; Is it within bounds of the jump curve?
[e39d]90 06BCC @_checkJumping; If so, check the jumping state.
;
; The jump curve is not in bounds. Clear the player's
; jumping state.
;
[e39f]a5 a4LDA Player_Flags; Load the player's flags.
[e3a1]29 feAND #$fe; Clear the Jumping bit.
[e3a3]85 a4STA Player_Flags; Save it back.
;
; Check the jumping state.
;
; If jumping, continue processing the jump.
;
; If not jumping, handle ladder movement.
;
[e3a5]@_checkJumping:
[e3a5]a5 a4LDA Player_Flags; Load the player's flags.
[e3a7]4aLSR A; Shift the Jumping bit into bit 0.
[e3a8]90 03BCC @_checkDownPassable; If not jumping, check if the block below is passable.
;
; The player is jumping. Stay on the ladder.
;
[e3aa]4c 3a e4JMP Player_GrabClimbLadderOrJump; Else, stay on the ladder.
;
; BUG: This checks if a block in the Down direction is passable,
; but sets the wrong variable. It sets A instead of X. So
; instead, this ends up checking whatever X might be set to
; at this moment.
;
; Due to the way
Player_CheckIfPassable
; works, this
; will check the block below unless X just happens to be 0,
; 1, or 2. Which is what "A" was going for above, checking
; the Down direction. So most likely, this will check Down,
; but it can mess up depending on what's in the X register.
;
[e3ad]@_checkDownPassable:
[e3ad]a9 03LDA #$03; Should be LDX -- Fail to set the direction to Up.
[e3af]20 c8 e6JSR Player_CheckIfPassable; Check if the block in whatever direction might be in X is passable.
[e3b2]f0 03BEQ @_checkAdjacent; If it's passable, jump.
;
; The block below is impassable (well, in theory.. the bug).
;
; Stay on the ladder and continue.
;
[e3b4]4c 3a e4JMP Player_GrabClimbLadderOrJump
;
; Check if the player is moving left or right at the bottom of
; the screen and has a solid block in the direction they're
; moving. If so, keep the player firmly in place
;
; NOTE:
; has a solid block off-screen. If so, a counter
; will start
;
; NOTE: I'm
;
[e3b7]@_checkAdjacent:
[e3b7]20 f6 e4JSR Area_CheckClimbAdjacentImpassable; Check if attempting to climb left/right to an impassable block.
[e3ba]a5 b7LDA Blocks_Result; Check the result.
[e3bc]f0 0fBEQ @_checkWingBoots; If passable, check if Wing Boots are active.
;
; The player tried climbing left or right, and the block there
; was impassable.
;
; This code is going to try to check what I think is a hang
; time to go from climbing to falling, but in practice, I'm
; not sure this is ever used.
;
; Seems we'd get here if moving left or right into an
; impassable block at the bottom of the screen, but I can't
; make this happen. I think other code paths get to that case
; first.
;
[e3be]a5 b1LDA Player_ClimbLadderHangTime
[e3c0]c9 08CMP #$08
[e3c2]b0 05BCS @_resetHangTime
[e3c4]e6 b1INC Player_ClimbLadderHangTime
[e3c6]4c 44 e4JMP Player_UpdateClimbOrJump
[e3c9]@_resetHangTime:
[e3c9]a9 00LDA #$00
[e3cb]85 b1STA Player_ClimbLadderHangTime
;
; DEADCODE: This checks if Wing Boots are active, and then
; jumps to the very next line. It effectively does
; nothing.
;
[e3cd]@_checkWingBoots:
[e3cd]a5 a5LDA Player_StatusFlag
[e3cf]10 00BPL @_setFalling
;
; Mark the player as falling off the ladder.
;
[e3d1]@_setFalling:
[e3d1]a5 a4LDA Player_Flags; Load the player's flags.
[e3d3]09 04ORA #$04; Set the Falling bit.
[e3d5]85 a4STA Player_Flags; Save it.
[e3d7]a5 a4LDA Player_Flags; Load the player's flags again.
[e3d9]29 08AND #$08; Check if the Can Climb bit is set.
[e3db]d0 5dBNE Player_GrabClimbLadderOrJump; If so, stay on the ladder.
;
; The player can no longer climb.
;
; Update the Y position based on whether Wing Boots are
; being used or not.
;
[e3dd]a5 a5LDA Player_StatusFlag; Load the player's status flag.
[e3df]10 14BPL @_climbDown; If Wing Boots are inactive, jump to climb down.
[e3e1]a5 16LDA Joy1_ButtonMask; Load the controller 1 button mask.
[e3e3]10 10BPL @_climbDown; If the A button isn't pressed, jump to climb down.
;
; Wing Boots are active, and "A" is pressed.
;
;
; DEADCODE: This next block doesn't do anything anymore. It
; loads the modifier, adds 0, and saves it back. It
; was probably used at one point to do the same kind
; of work that
Player_CheckHandleClimbUp
; does.
;
[e3e5]a5 a0LDA Player_ClimbFlySpeedModifier
[e3e7]18CLC
[e3e8]69 00ADC #$00
[e3ea]85 a0STA Player_ClimbFlySpeedModifier
;
; Increase the Y position by 1.
;
[e3ec]a5 a1LDA Player_PosY; Load the player's Y position.
[e3ee]69 01ADC #$01; Add 1.
[e3f0]85 a1STA Player_PosY; Save it.
[e3f2]4c fc e3JMP @_checkScrollScreenDown; Check if the screen should now scroll down.
;
; Move the player down 8 pixels.
;
[e3f5]@_climbDown:
[e3f5]a5 a1LDA Player_PosY; Load the player's Y position.
[e3f7]18CLC
[e3f8]69 08ADC #$08; Add 8.
[e3fa]85 a1STA Player_PosY; Save it.
;
; Check if the screen should scroll down.
;
[e3fc]@_checkScrollScreenDown:
[e3fc]c9 c1CMP #$c1; Check if the player's hit the bottom of the screen.
[e3fe]90 1eBCC @_checkCapFall; If not, check whether the player's fell on a block.
;
; The player hit the bottom of the screen.
;
; Check if there's a screen below.
;
[e400]a0 03LDY #$03; Y = 3 (Down direction).
[e402]20 27 e6JSR Screen_IsEdge; Check if there's a screen or edge there.
[e405]90 07BCC @_scrollScreenDown; If there's a screen, handle scrolling down.
;
; There's no screen below. Cap the player's position to the
; bottom of the screen.
;
[e407]a9 c0LDA #$c0; A = 192 (last Y position on the screen).
[e409]85 a1STA Player_PosY; Set that as the player's Y position.
[e40b]4c 2c e4JMP @_clearFalling; Clear the falling flag.
;
; Begin scrolling the screen down.
;
[e40e]@_scrollScreenDown:
[e40e]a5 69LDA Area_ScreenBelow; Load the screen below.
[e410]85 63STA Area_CurrentScreen; Store as the new current screen.
[e412]a2 03LDX #$03; X = 3 (Down direction).
[e414]20 e9 e8JSR Area_ScrollTo; Scroll there.
[e417]e6 a2INC Player_Something_ScrollPosY; Increment the scroll Y position.
[e419]a9 00LDA #$00; A = 0 (new player position, top of screen).
[e41b]85 a1STA Player_PosY; Set as the player's new Y position.
[e41d]60RTS
;
; Check if the block below is passable. If not, cap the Y
; position so the player doesn't fall through it.
;
[e41e]@_checkCapFall:
[e41e]a2 03LDX #$03; X = 3 (Down direction).
[e420]20 c8 e6JSR Player_CheckIfPassable; Check if there's a passable block there.
[e423]f0 06BEQ @_return; If so, return.
;
; It's not passable, so we can cap now.
;
[e425]a5 a1LDA Player_PosY; Load the player's Y position.
[e427]29 f0AND #$f0; Round to the top of the block.
[e429]85 a1STA Player_PosY; Save it.
[e42b]@_return:
[e42b]60RTS
;
; Clear the falling state.
;
[e42c]@_clearFalling:
[e42c]a5 a4LDA Player_Flags; Load the player's flags.
[e42e]29 fbAND #$fb; Clear the falling bit.
[e430]85 a4STA Player_Flags; Store it.
[e432]60RTS
;============================================================================
; Clear the player's Jumping and Holding to Climb flags.
;
; INPUTS:
;
Player_Flags:
; The player's current flags.
;
; OUTPUTS:
;
Player_Flags:
; The flags with Jumping and Holding To Climb cleared.
;
; XREFS:
;
Player_UpdateClimbOrJump
;============================================================================
[e433]Player_ClearJumpingAndHoldingToClimb:
[e433]a5 a4LDA Player_Flags; Load the player flags.
[e435]29 fcAND #$fc; Clear the Jumping and Holding To Climb flags.
[e437]85 a4STA Player_Flags; Save it.
[e439]RETURN_E439:
[e439]60RTS
;============================================================================
; TODO: Document Player_GrabClimbLadderOrJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_HandleVertLadderOrJump
;============================================================================
[e43a]Player_GrabClimbLadderOrJump:
;
; Clear the Falling Off state.
;
[e43a]a5 a4LDA Player_Flags; Load the player's flags.
[e43c]29 fbAND #$fb; Clear the Falling Off flag.
[e43e]85 a4STA Player_Flags; Save it back.
;
; Reset the hang time.
;
[e440]a9 00LDA #$00
[e442]85 b1STA Player_ClimbLadderHangTime
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Player_UpdateClimbOrJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_HandleVertLadderOrJump
;============================================================================
[e444]Player_UpdateClimbOrJump:
;
; Check if the player is jumping.
;
[e444]a5 a4LDA Player_Flags; Load the player's flags.
[e446]4aLSR A; Shift the Jump flag into Carry.
[e447]b0 1aBCS @_isJumping; If set (player is jumping), then jump (branch).
;
; The player is not jumping.
;
[e449]a6 19LDX Joy1_ChangedButtonMask; X = Controller 1 changed button mask.
[e44b]10 e6BPL Player_ClearJumpingAndHoldingToClimb; If the player is holding down A, then check grabbing for a ladder and return.
;
; Check if the player is climbing. If so, return.
;
[e44d]20 f6 ecJSR Player_IsClimbing
[e450]b0 e7BCS RETURN_E439
;
; Check if the block above is passable. If not, return.
;
[e452]a2 02LDX #$02
[e454]20 c8 e6JSR Player_CheckIfPassable
[e457]d0 5dBNE @_return
;
; Set the Jumping and Holding To Climb flags.
;
[e459]a5 a4LDA Player_Flags; Load the player's flags.
[e45b]09 03ORA #$03; Set the Jumping and Holding To Climb flags.
[e45d]85 a4STA Player_Flags; Save it.
[e45f]a9 00LDA #$00
[e461]85 a6STA Player_JumpCurveIndex
[e463]@_isJumping:
[e463]a6 a6LDX Player_JumpCurveIndex; Load the jump timer.
[e465]e0 10CPX #$10; Check if we're in a rising (< 16) or falling (>= 16) state.
[e467]90 02BCC @_jumpRising; Jump if rising.
[e469]b0 37BCS @_jumpFalling; Jump if falling.
;
; Reduce the player's Y position based on the current position
; in the jump curve.
;
[e46b]@_jumpRising:
[e46b]a5 a1LDA Player_PosY; Load the player's Y position.
[e46d]38SEC
[e46e]fd d6 e4SBC a:PLAYER_JUMP_CURVE,X; Subtract the delta from the jump curve table.
[e471]85 a1STA Player_PosY; Store as the new Y position.
[e473]b0 0cBCS @_checkBlockAbove; If Y >= 0, jump.
;
; The Y position wrapped around. Check if we should switch
; screens to the screen above.
;
[e475]a0 02LDY #$02; Y = 2 (Up direction).
[e477]20 27 e6JSR Screen_IsEdge; Check if there's an edge above.
[e47a]90 3bBCC @_scrollScreenUp; If not, scroll the screen up.
;
; We're not switching screens, so cap at Y=0.
;
[e47c]a9 00LDA #$00; A = 0 (new player Y position).
[e47e]4c 9a e4JMP @_setJumpCurveMidpoint; Jump to set the position and wrap up.
;
; Check if there's an impassable block above the player. If not,
; the player's Y position will be capped below the block.
;
[e481]@_checkBlockAbove:
[e481]a2 02LDX #$02; X = 2 (Up direction).
[e483]20 c8 e6JSR Player_CheckIfPassable; Check if the block there is passable.
[e486]f0 29BEQ @_continueJumpCurve; If impassable, continue the jump.
;
; The player hit against an impassable block. That means
; they've hit the top of the jump curve and can't go any
; higher.
;
; This will set the jump curve to the midpoint so they'll
; begin to fall, and cap the player's Y so they're just
; below the block they hit. Unless they hit the top of
; the screen, in which case they'll just be capped there.
;
[e488]a6 a6LDX Player_JumpCurveIndex; X = Jump curve index.
[e48a]a5 a1LDA Player_PosY; A = Current player Y position.
[e48c]29 0fAND #$0f; Cap it to <= the midpoint of the curve.
[e48e]aaTAX; X = result.
[e48f]a5 a1LDA Player_PosY; A = Current player Y position.
[e491]29 f0AND #$f0; Cap it to a rounded block position.
[e493]e0 00CPX #$00; Is it 0?
[e495]f0 03BEQ @_setJumpCurveMidpoint; If so, jump to avoid updating the Y position.
[e497]18CLC
[e498]69 10ADC #$10; Shift down one block (below the obstacle hit).
;
; Set the jump curve index to 0xF, one just before the
; midpoint, to begin the process of putting the player
; into a downward arc.
;
; Just before the midpoint is used because after this,
; the index will advance by one.
;
[e49a]@_setJumpCurveMidpoint:
[e49a]85 a1STA Player_PosY; Set the new player Y position.
[e49c]a9 0fLDA #$0f; 0xF == Jump curve midpoint (- 1).
[e49e]85 a6STA Player_JumpCurveIndex; Set to the midpoint to begin falling.
[e4a0]d0 0fBNE @_continueJumpCurve; Continue the jump curve, advancing the index.
;
; Increase the player's Y position based on the current position
; in the jump curve.
;
[e4a2]@_jumpFalling:
[e4a2]a5 a1LDA Player_PosY; Load the player's Y position.
[e4a4]18CLC
[e4a5]7d d6 e4ADC a:PLAYER_JUMP_CURVE,X; Add the delta from the jump curve table.
[e4a8]85 a1STA Player_PosY; Store as the new Y position.
[e4aa]a2 03LDX #$03; Y = 2 (Down direction).
[e4ac]20 c8 e6JSR Player_CheckIfPassable; Check if the block there is passable.
[e4af]d0 18BNE @_setLandedOnBlock; If impassable, land the player.
;
; Increase the index into the jump curve table.
;
[e4b1]@_continueJumpCurve:
[e4b1]a6 a6LDX Player_JumpCurveIndex; Load the current jump timer.
[e4b3]e8INX; Increase by 1.
[e4b4]86 a6STX Player_JumpCurveIndex; Save it.
[e4b6]@_return:
[e4b6]60RTS
;
; Scroll to the screen above.
;
[e4b7]@_scrollScreenUp:
[e4b7]a5 68LDA Area_ScreenAbove; Load the screen above.
[e4b9]85 63STA Area_CurrentScreen; Set as the new screen.
[e4bb]a2 02LDX #$02; X = 2 (Up direction).
[e4bd]20 e9 e8JSR Area_ScrollTo; Scroll the screen up.
[e4c0]c6 a2DEC Player_Something_ScrollPosY
;
; Place the player at the bottom of the screen and clear the
; jump state.
;
[e4c2]a9 c0LDA #$c0; Y = 0xC0 (first valid Y position at the bottom of a screen).
[e4c4]85 a1STA Player_PosY; Set as the player's Y position.
[e4c6]4c c9 e4JMP @_setLandedOnBlock; Mark the player as landed.
;
; Cap the player's Y position to the nearest rounded block
; position.
;
[e4c9]@_setLandedOnBlock:
[e4c9]a5 a1LDA Player_PosY; Load the player's Y position.
[e4cb]29 f0AND #$f0; Cap it to a rounded block position.
[e4cd]85 a1STA Player_PosY; Set as the new Y position.
;
; Clear the Jumping flag.
;
[e4cf]a5 a4LDA Player_Flags; Load the player's flags.
[e4d1]29 feAND #$fe; Clear the jumping flag.
[e4d3]85 a4STA Player_Flags; Set the flags.
[e4d5]60RTS
;============================================================================
; A table mapping the jump curve.
;
; This maps a position into the jump curve
; (
Player_JumpCurveIndex) to a Y delta to apply to the
; player's Y position.
;
; XREFS:
;
Player_UpdateClimbOrJump
;============================================================================
[e4d6]PLAYER_JUMP_CURVE:
[e4d6]08.byte $08; [0]: Start of the jump.
[e4d7]04.byte $04; [1]:
[e4d8]04.byte $04; [2]:
[e4d9]04.byte $04; [3]:
[e4da]04.byte $04; [4]:
[e4db]02.byte $02; [5]:
[e4dc]02.byte $02; [6]:
[e4dd]01.byte $01; [7]:
[e4de]01.byte $01; [8]:
[e4df]01.byte $01; [9]:
[e4e0]01.byte $01; [10]:
[e4e1]00.byte $00; [11]:
[e4e2]00.byte $00; [12]:
[e4e3]00.byte $00; [13]:
[e4e4]00.byte $00; [14]:
[e4e5]00.byte $00; [15]:
[e4e6]00.byte $00; [16]: Midpoint of the jump.
[e4e7]00.byte $00; [17]:
[e4e8]00.byte $00; [18]:
[e4e9]00.byte $00; [19]:
[e4ea]00.byte $00; [20]:
[e4eb]01.byte $01; [21]:
[e4ec]01.byte $01; [22]:
[e4ed]01.byte $01; [23]:
[e4ee]01.byte $01; [24]:
[e4ef]02.byte $02; [25]:
[e4f0]02.byte $02; [26]:
[e4f1]04.byte $04; [27]:
[e4f2]04.byte $04; [28]:
[e4f3]04.byte $04; [29]:
[e4f4]04.byte $04; [30]:
[e4f5]08.byte $08; [31]: End of the jump.
;============================================================================
; TODO: Document Area_CheckClimbAdjacentImpassable
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_HandleVertLadderOrJump
;============================================================================
[e4f6]Area_CheckClimbAdjacentImpassable:
;
; Check if Left or Right is pressed.
;
[e4f6]a2 00LDX #$00; X = 0 (mapping table index).
[e4f8]a5 19LDA Joy1_ChangedButtonMask; Load the controller 1 button mask.
[e4fa]29 03AND #$03; If Left or Right pressed?
[e4fc]f0 21BEQ @_returnFalse; If not, jump to return a false result.
[e4fe]29 01AND #$01; Keep the Right Button bit.
[e500]f0 01BEQ @_buildArgs
[e502]e8INX; X = 1 (mapping table index).
;
; Determine an argument value for the X position to check.
;
; If pressing Left, this will be the player's rounded X
; block position.
;
; If pressing Right, this will be the next block to the
; right.
;
[e503]@_buildArgs:
[e503]a5 9eLDA Player_PosX_Block; Load the player's X block position.
[e505]18CLC
[e506]7d 24 e5ADC a:CLIMB_ADJACENT_DIRECTION_TO_OFFSET,X; Add the offset based on the direction. No offset for Left, one block over for Right.
[e509]85 b5STA Arg_PixelPosX; Store as the X position argument.
;
; Determine an argument for the Y position to check.
;
; This will be 2 blocks below the player's Y position, so
; the block under the player.
;
[e50b]a5 a1LDA Player_PosY; Load the player's Y position.
[e50d]18CLC
[e50e]69 20ADC #$20; Add 32 (2 blocks down).
[e510]85 b6STA Arg_PixelPosY; Store as the Y position argument.
;
; Only perform the check if the result is the second row of
; blocks off-screen.
;
; NOTE: Which should not be possible. Let's break this down:
;
; 1. The last viable Y player position is 0xC0. Which, normalized
; with the + 0x20, is 0xE0.
;
; 2. Sticking the player's head just below the boundary of the
; screen would be 0xD0, or 0xF0 normalized.
;
; This is where the player would need to be, but...
;
; 3. The user would have already hit a screen transition
; in this case, or been capped at the boundary of the screen.
;
; So unless there's a particular case somewhere that allows the
; player to move under the screen (some kind of easter egg? Some
; kind of mechanic I can't think of?), this should never be able
; to pass the check.
;
; This makes me think that this boundary value was to turn off
; this logic as other climbing code was written.
;
[e512]c9 f0CMP #$f0; Is it < 0xF0?
[e514]90 09BCC @_returnFalse; If so, return false.
;
; The player's within the sufficient bounds for a block check.
; Get the block and return the passable/impassable flag.
;
[e516]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert the argument X/Y position to a block position.
[e519]20 7c e8JSR ScreenBuffer_IsBlockImpassable; Check if the block there is impassable.
[e51c]85 b7STA Blocks_Result; Store it as the result to return.
[e51e]60RTS
[e51f]@_returnFalse:
[e51f]a9 00LDA #$00; A = 0 (false).
[e521]85 b7STA Blocks_Result; Store it as the result to return.
[e523]60RTS
;============================================================================
; Mapping of directions to block X offsets to check for adjacent climbing.
;
; XREFS:
;
Area_CheckClimbAdjacentImpassable
;============================================================================
[e524]CLIMB_ADJACENT_DIRECTION_TO_OFFSET:
[e524]00.byte $00; [0]: Left
[e525]CLIMB_ADJACENT_DIRECTION_TO_OFFSET_1_:
[e525]0f.byte $0f; [1]: Right
;============================================================================
; TODO: Document Player_CheckHandleEnterDoor
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e526]Player_CheckHandleEnterDoor:
[e526]a5 19LDA Joy1_ChangedButtonMask
[e528]29 08AND #$08
[e52a]f0 3cBEQ @_return
[e52c]20 c5 e7JSR Area_SetStateFromDoorDestination; Set the new state based on the other end of the door.
[e52f]a5 b7LDA Blocks_Result; Check the result of that.
[e531]f0 35BEQ @_return
[e533]a5 6bLDA Temp_BlockPos2
[e535]c9 feCMP #$fe
[e537]b0 79BCS Player_EnterDoorToOutside
;
; The player is going inside.
;
[e539]20 2f ebJSR Game_RunDoorRequirementHandler
[e53c]ad 2b 04LDA a:CurrentDoor_KeyRequirement
[e53f]d0 27BNE @_return
;
; The door is not locked.
;
[e541]a5 6bLDA Temp_BlockPos2
[e543]c9 20CMP #$20
[e545]b0 30BCS Player_EnterDoorToInside
[e547]20 2f daJSR Screen_FadeToBlack
[e54a]a2 06LDX #$06
[e54c]@_paletteCheckLoop:
[e54c]a5 65LDA Screen_DestPaletteOrIndex
[e54e]dd 69 e5CMP a:Palette_ARRAY_PRG15_MIRROR__e569,X
[e551]f0 05BEQ @_setupArea
[e553]caDEX
[e554]10 f6BPL @_paletteCheckLoop
[e556]30 0dBMI @_enterScreen
[e558]@_setupArea:
[e558]bd 70 e5LDA a:Music_ARRAY_PRG15_MIRROR__e570,X
[e55b]cd d1 03CMP a:Areas_DefaultMusic
[e55e]f0 05BEQ @_enterScreen
[e560]85 faSTA Music_Current
[e562]8d d1 03STA a:Areas_DefaultMusic
[e565]@_enterScreen:
[e565]4c a0 daJMP Game_SetupEnterScreen
[e568]@_return:
[e568]60RTS
;============================================================================
; Mapping of palettes to accompanying music.
;
; These two tables work together, along with a loop, to
; match up palettes to the music that should play when
; switching screens.
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e569]Palette_ARRAY_PRG15_MIRROR__e569:
[e569]06.byte PALETTE_OUTSIDE; [0]:
[e56a]07.byte PALETTE_TOWER; [1]:
[e56b]0a.byte PALETTE_MIST; [2]:
[e56c]0b.byte PALETTE_SUFFER; [3]:
[e56d]0c.byte PALETTE_DARTMOOR; [4]:
[e56e]0d.byte PALETTE_FRATERNAL; [5]:
[e56f]0e.byte PALETTE_KING_GRIEVES_ROOM; [6]:
[e570]Music_ARRAY_PRG15_MIRROR__e570:
[e570]03.byte MUSIC_APOLUNE; [0]:
[e571]06.byte MUSIC_MAYBE_TOWER; [1]:
[e572]05.byte MUSIC_FOREPAW; [2]:
[e573]06.byte MUSIC_MAYBE_TOWER; [3]:
[e574]02.byte MUSIC_DAYBREAK; [4]:
[e575]06.byte MUSIC_MAYBE_TOWER; [5]:
[e576]06.byte MUSIC_MAYBE_TOWER; [6]:
;============================================================================
; TODO: Document Player_EnterDoorToInside
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e577]Player_EnterDoorToInside:
[e577]a6 64LDX Area_LoadingScreenIndex
[e579]8e da 03STX a:Area_DestScreen
[e57c]a5 65LDA Screen_DestPaletteOrIndex
[e57e]85 64STA Area_LoadingScreenIndex
[e580]aaTAX
[e581]bd 09 e6LDA a:BUILDING_PALETTES,X
[e584]85 65STA Screen_DestPaletteOrIndex
[e586]bd 13 e6LDA a:BUILDING_MAYBE_TILES_INDEXES,X
[e589]8d d9 03STA a:Building_TilesIndex
[e58c]bd 1d e6LDA a:BUILDING_START_POSITIONS,X
[e58f]85 6cSTA Screen_StartPosYX
[e591]ad d1 03LDA a:Areas_DefaultMusic
[e594]8d d2 03STA a:Area_Music_Outside
[e597]bd ff e5LDA a:BUILDING_MUSIC,X
[e59a]8d d1 03STA a:Areas_DefaultMusic
[e59d]a5 24LDA Area_CurrentArea
;
; Where does this door take the player?
;
[e59f]c9 04CMP #$04
[e5a1]d0 03BNE @LAB_PRG15_MIRROR__e5a6
[e5a3]4c 50 eaJMP Game_BeginExitBuilding
[e5a6]@LAB_PRG15_MIRROR__e5a6:
[e5a6]a5 a4LDA Player_Flags
[e5a8]29 bfAND #$bf
[e5aa]85 a4STA Player_Flags
[e5ac]20 2f daJSR Screen_FadeToBlack
[e5af]4c af daJMP Game_SetupEnterBuilding
;============================================================================
; TODO: Document Player_EnterDoorToOutside
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e5b2]Player_EnterDoorToOutside:
;
; The player opened a door back to the outside.
;
[e5b2]4aLSR A
[e5b3]ad 35 04LDA a:Area_Region
[e5b6]2aROL A
[e5b7]48PHA
[e5b8]a8TAY
;
; Check if the door is locked.
;
[e5b9]b9 db e5LDA a:DOOR_LOOKUP_REQUIREMENTS,Y
[e5bc]8d 2b 04STA a:CurrentDoor_KeyRequirement
[e5bf]20 2f ebJSR Game_RunDoorRequirementHandler
[e5c2]68PLA
[e5c3]a8TAY
[e5c4]ad 2b 04LDA a:CurrentDoor_KeyRequirement
[e5c7]d0 11BNE SUB_RETURN_E5DA
;
; This door is not locked.
;
[e5c9]b9 e7 e5LDA a:MAYBE_REGION_TRANSITION_MAP,Y
[e5cc]8d 35 04STA a:Area_Region
[e5cf]b9 f3 e5LDA a:MAYBE_SCREEN_TRANSITION_MAP,Y
[e5d2]85 64STA Area_LoadingScreenIndex
[e5d4]20 2f daJSR Screen_FadeToBlack
[e5d7]4c dc daJMP Game_SetupAndLoadOutsideArea
[e5da]SUB_RETURN_E5DA:
[e5da]60RTS
[e5db]DOOR_LOOKUP_REQUIREMENTS:
[e5db]00.byte $00; [0]: No key required
[e5dc]04.byte $04; [1]: "J" key required
[e5dd]00.byte $00; [2]: No key required
[e5de]03.byte $03; [3]: "Q" key required
[e5df]00.byte $00; [4]: No key required
[e5e0]01.byte $01; [5]: "A" key required
[e5e1]00.byte $00; [6]: No key required
[e5e2]07.byte $07; [7]: Ring of Dworf required
[e5e3]00.byte $00; [8]: No key required
[e5e4]08.byte $08; [9]: Demon's Ring required
[e5e5]00.byte $00; [10]: No key required
[e5e6]00.byte $00; [11]: No key required
[e5e7]MAYBE_REGION_TRANSITION_MAP:
[e5e7]00.byte REGION_EOLIS; [0]:
[e5e8]01.byte REGION_TRUNK; [1]:
[e5e9]00.byte REGION_EOLIS; [2]:
[e5ea]02.byte REGION_MIST; [3]:
[e5eb]01.byte REGION_TRUNK; [4]:
[e5ec]03.byte REGION_BRANCH; [5]:
[e5ed]02.byte REGION_MIST; [6]:
[e5ee]04.byte REGION_DARTMOOR; [7]:
[e5ef]03.byte REGION_BRANCH; [8]:
[e5f0]05.byte REGION_EVIL_FORTRESS; [9]:
[e5f1]04.byte REGION_DARTMOOR; [10]:
[e5f2]05.byte REGION_EVIL_FORTRESS; [11]:
[e5f3]MAYBE_SCREEN_TRANSITION_MAP:
[e5f3]00.byte $00; [0]:
[e5f4]00.byte $00; [1]:
[e5f5]08.byte $08; [2]:
[e5f6]11.byte $11; [3]:
[e5f7]28.byte $28; [4]:
[e5f8]00.byte $00; [5]:
[e5f9]1f.byte $1f; [6]:
[e5fa]00.byte $00; [7]:
[e5fb]27.byte $27; [8]:
[e5fc]08.byte $08; [9]:
[e5fd]0e.byte $0e; [10]:
[e5fe]0e.byte $0e; [11]:
[e5ff]BUILDING_MUSIC:
[e5ff]0d.byte MUSIC_KINGS_ROOM; [0]: King's Room
[e600]0e.byte MUSIC_TEMPLE; [1]: Temple
[e601]0f.byte MUSIC_SHOP; [2]: Hospital
[e602]0f.byte MUSIC_SHOP; [3]: Tavern
[e603]0f.byte MUSIC_SHOP; [4]: Tool Shop
[e604]0f.byte MUSIC_SHOP; [5]: Key Shop
[e605]0f.byte MUSIC_SHOP; [6]: House
[e606]0f.byte MUSIC_SHOP; [7]: Meat Shop
[e607]0f.byte MUSIC_SHOP; [8]: Martial Arts
[e608]0f.byte MUSIC_SHOP; [9]: Magic Shop
;============================================================================
; Mapping of building indexes to palette indexes.
;
; XREFS:
;
Player_EnterDoorToInside
;============================================================================
[e609]BUILDING_PALETTES:
[e609]11.byte PALETTE_KINGS_ROOM; [0]: King's Room
[e60a]12.byte PALETTE_TEMPLE; [1]: Temple
[e60b]13.byte PALETTE_HOSPITAL; [2]: Hospital
[e60c]14.byte PALETTE_TAVERN; [3]: Tavern
[e60d]15.byte PALETTE_TOOL_SHOP; [4]: Tool Shop
[e60e]16.byte PALETTE_KEY_SHOP; [5]: Key Shop
[e60f]17.byte PALETTE_HOUSE; [6]: House
[e610]18.byte PALETTE_MEAT_SHOP; [7]: Meat Shop
[e611]19.byte PALETTE_MARTIAL_ARTS; [8]: Martial Arts
[e612]1a.byte PALETTE_MAGIC_SHOP; [9]: Magic Shop
[e613]BUILDING_MAYBE_TILES_INDEXES:
[e613]06.byte $06; [0]: King's Room
[e614]06.byte $06; [1]: Temple
[e615]06.byte $06; [2]: Hospital
[e616]07.byte $07; [3]: Tavern
[e617]07.byte $07; [4]: Tool Shop
[e618]07.byte $07; [5]: Key Shop
[e619]07.byte $07; [6]: House
[e61a]07.byte $07; [7]: Meat Shop
[e61b]08.byte $08; [8]: Martial Arts
[e61c]08.byte $08; [9]: Magic Shop
[e61d]BUILDING_START_POSITIONS:
[e61d]9e.byte $9e; [0]: King's Room
[e61e]9e.byte $9e; [1]: Temple
[e61f]9e.byte $9e; [2]: Hospital
[e620]8e.byte $8e; [3]: Tavern
[e621]7e.byte $7e; [4]: Tool Shop
[e622]7e.byte $7e; [5]: Key Shop
[e623]7e.byte $7e; [6]: House
[e624]7e.byte $7e; [7]: Meat Shop
[e625]8e.byte $8e; [8]: Martial Arts
[e626]8e.byte $8e; [9]: Magic Shop
[e627]Screen_IsEdge:
[e627]b9 66 00LDA a:Area_ScreenToTheLeft,Y; Load the screen ID at the given neighbor.
[e62a]c9 ffCMP #$ff; If 0xFF, C = 1. Else, C = 0
[e62c]60RTS
;============================================================================
; TODO: Document Area_CanMoveUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveRight
;============================================================================
[e62d]Area_CanMoveUp:
[e62d]a5 a1LDA Player_PosY
[e62f]4aLSR A
[e630]4aLSR A
[e631]4aLSR A
[e632]4aLSR A
[e633]aaTAX
[e634]bc f6 03LDY a:FirstColumnInRightScreen,X
[e637]20 7f e8JSR Area_IsBlockImpassable
[e63a]85 b7STA Blocks_Result
[e63c]e8INX
[e63d]bc f6 03LDY a:FirstColumnInRightScreen,X
[e640]20 7f e8JSR Area_IsBlockImpassable
[e643]05 b7ORA Blocks_Result
[e645]85 b7STA Blocks_Result
[e647]a5 a1LDA Player_PosY
[e649]29 0fAND #$0f
[e64b]f0 0bBEQ @LAB_PRG15_MIRROR__e658
[e64d]e8INX
[e64e]bc f6 03LDY a:FirstColumnInRightScreen,X
[e651]20 7f e8JSR Area_IsBlockImpassable
[e654]05 b7ORA Blocks_Result
[e656]85 b7STA Blocks_Result
[e658]@LAB_PRG15_MIRROR__e658:
[e658]a5 b7LDA Blocks_Result
[e65a]60RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e65b]Area_CanPlayerMoveRight:
[e65b]a5 9eLDA Player_PosX_Block
[e65d]18CLC
[e65e]69 10ADC #$10
[e660]85 b5STA Arg_PixelPosX
[e662]b0 c9BCS Area_CanMoveUp
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Area_CanPlayerMoveAtY
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveLeft
;============================================================================
[e664]Area_CanPlayerMoveAtY:
[e664]a5 a1LDA Player_PosY
[e666]85 b6STA Arg_PixelPosY
[e668]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e66b]20 7c e8JSR ScreenBuffer_IsBlockImpassable
[e66e]85 b7STA Blocks_Result
[e670]8aTXA
[e671]18CLC
[e672]69 10ADC #$10
[e674]aaTAX
[e675]20 7c e8JSR ScreenBuffer_IsBlockImpassable
[e678]05 b7ORA Blocks_Result
[e67a]85 b7STA Blocks_Result
[e67c]a5 a1LDA Player_PosY
[e67e]29 0fAND #$0f
[e680]f0 0cBEQ @_returnResult
[e682]8aTXA
[e683]18CLC
[e684]69 10ADC #$10
[e686]aaTAX
[e687]20 7c e8JSR ScreenBuffer_IsBlockImpassable
[e68a]05 b7ORA Blocks_Result
[e68c]85 b7STA Blocks_Result
[e68e]@_returnResult:
[e68e]a5 b7LDA Blocks_Result
[e690]60RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e691]Area_CanPlayerMoveLeft:
[e691]a5 9eLDA Player_PosX_Block
[e693]38SEC
[e694]e9 01SBC #$01
[e696]85 b5STA Arg_PixelPosX
[e698]b0 caBCS Area_CanPlayerMoveAtY
[e69a]a5 a1LDA Player_PosY
[e69c]4aLSR A
[e69d]4aLSR A
[e69e]4aLSR A
[e69f]4aLSR A
[e6a0]aaTAX
[e6a1]bc e6 03LDY a:LastColumnLeftScreen,X
[e6a4]20 7f e8JSR Area_IsBlockImpassable
[e6a7]85 b7STA Blocks_Result
[e6a9]e8INX
[e6aa]bc e6 03LDY a:LastColumnLeftScreen,X
[e6ad]20 7f e8JSR Area_IsBlockImpassable
[e6b0]05 b7ORA Blocks_Result
[e6b2]85 b7STA Blocks_Result
[e6b4]a5 a1LDA Player_PosY
[e6b6]29 0fAND #$0f
[e6b8]f0 0bBEQ @LAB_PRG15_MIRROR__e6c5
[e6ba]e8INX
[e6bb]bc e6 03LDY a:LastColumnLeftScreen,X
[e6be]20 7f e8JSR Area_IsBlockImpassable
[e6c1]05 b7ORA Blocks_Result
[e6c3]85 b7STA Blocks_Result
[e6c5]@LAB_PRG15_MIRROR__e6c5:
[e6c5]a5 b7LDA Blocks_Result
[e6c7]60RTS
[e6c8]Player_CheckIfPassable:
[e6c8]8aTXA
[e6c9]f0 c6BEQ Area_CanPlayerMoveLeft
[e6cb]caDEX
[e6cc]f0 8dBEQ Area_CanPlayerMoveRight
[e6ce]caDEX
[e6cf]f0 53BEQ Area_CanPlayerMoveUp
[e6d1]a5 a1LDA Player_PosY
[e6d3]18CLC
[e6d4]69 20ADC #$20
[e6d6]85 b6STA Arg_PixelPosY
[e6d8]c9 d0CMP #$d0
[e6da]90 23BCC Area_CanMoveImmediatelyRight
[e6dc]a5 9eLDA Player_PosX_Block
[e6de]4aLSR A
[e6df]4aLSR A
[e6e0]4aLSR A
[e6e1]4aLSR A
[e6e2]aaTAX
[e6e3]bc 16 04LDY a:FirstRowBelowScreen,X
[e6e6]20 7f e8JSR Area_IsBlockImpassable
[e6e9]85 b7STA Blocks_Result
[e6eb]a5 9eLDA Player_PosX_Block
[e6ed]29 0fAND #$0f
[e6ef]f0 0bBEQ @_returnResult
[e6f1]e8INX
[e6f2]bc 16 04LDY a:FirstRowBelowScreen,X
[e6f5]20 7f e8JSR Area_IsBlockImpassable
[e6f8]05 b7ORA Blocks_Result
[e6fa]85 b7STA Blocks_Result
[e6fc]@_returnResult:
[e6fc]a5 b7LDA Blocks_Result
[e6fe]60RTS
;============================================================================
; TODO: Document Area_CanMoveImmediatelyRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveUp
;
Player_CheckIfPassable
;============================================================================
[e6ff]Area_CanMoveImmediatelyRight:
[e6ff]a5 9eLDA Player_PosX_Block
[e701]18CLC
[e702]69 04ADC #$04
[e704]85 b5STA Arg_PixelPosX
[e706]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e709]20 7c e8JSR ScreenBuffer_IsBlockImpassable
[e70c]85 b7STA Blocks_Result
[e70e]a5 9eLDA Player_PosX_Block
[e710]29 0fAND #$0f
[e712]38SEC
[e713]e9 04SBC #$04
[e715]c9 08CMP #$08
[e717]b0 08BCS @_returnResult
[e719]e8INX
[e71a]20 7c e8JSR ScreenBuffer_IsBlockImpassable
[e71d]05 b7ORA Blocks_Result
[e71f]85 b7STA Blocks_Result
[e721]@_returnResult:
[e721]a5 b7LDA Blocks_Result
[e723]60RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e724]Area_CanPlayerMoveUp:
[e724]a5 a1LDA Player_PosY
[e726]38SEC
[e727]e9 01SBC #$01
[e729]85 b6STA Arg_PixelPosY
[e72b]c9 f0CMP #$f0
[e72d]90 d0BCC Area_CanMoveImmediatelyRight
[e72f]a5 9eLDA Player_PosX_Block
[e731]4aLSR A
[e732]4aLSR A
[e733]4aLSR A
[e734]4aLSR A
[e735]aaTAX
[e736]bc 06 04LDY a:LastRowAboveScreen,X
[e739]20 7f e8JSR Area_IsBlockImpassable
[e73c]85 b7STA Blocks_Result
[e73e]a5 9eLDA Player_PosX_Block
[e740]29 0fAND #$0f
[e742]f0 0bBEQ @_returnResult
[e744]e8INX
[e745]bc 06 04LDY a:LastRowAboveScreen,X
[e748]20 7f e8JSR Area_IsBlockImpassable
[e74b]05 b7ORA Blocks_Result
[e74d]85 b7STA Blocks_Result
[e74f]@_returnResult:
[e74f]a5 b7LDA Blocks_Result
[e751]60RTS
;============================================================================
; TODO: Document Player_CheckIfOnLadder
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleVertMovement
;============================================================================
[e752]Player_CheckIfOnLadder:
;
; Build the target block pixel position to look up.
;
; This will offset the X by +7, which helps grab onto the block
; if just to the left of it.
;
[e752]a5 9eLDA Player_PosX_Block; A = player X position
[e754]18CLC
[e755]69 07ADC #$07; Add 7.
[e757]85 b5STA Arg_PixelPosX; Store the player's new X pixel position
[e759]a5 a1LDA Player_PosY; Load the player's Y position
[e75b]85 b6STA Arg_PixelPosY; Store the player's Y pixel position
;
; Check if the block at this position is climbable.
;
[e75d]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[e760]bc 00 06LDY a:ScreenBuffer,X; Load the block at that position.
[e763]20 c6 e8JSR Area_GetBlockProperty; Load the block property for that block.
[e766]85 b7STA Blocks_Result; Store it.
[e768]20 b2 e8JSR Area_IsBlockClimbable; Check if it's climbable.
[e76b]b0 45BCS @_resetCanClimb
;
; The block is not climbable. Check if the player is
; overlapping a block to the right, and check that.
;
[e76d]a5 9eLDA Player_PosX_Block; A = player X position.
[e76f]29 0fAND #$0f
[e771]c9 08CMP #$08
[e773]d0 0eBNE @_checkBlockBelow
[e775]e8INX
[e776]bc 00 06LDY a:ScreenBuffer,X
[e779]20 c6 e8JSR Area_GetBlockProperty
[e77c]85 b7STA Blocks_Result
[e77e]20 b2 e8JSR Area_IsBlockClimbable
[e781]b0 2fBCS @_resetCanClimb
[e783]@_checkBlockBelow:
[e783]a5 9eLDA Player_PosX_Block
[e785]18CLC
[e786]69 07ADC #$07
[e788]85 b5STA Arg_PixelPosX
[e78a]a5 a1LDA Player_PosY
[e78c]18CLC
[e78d]69 1fADC #$1f
[e78f]85 b6STA Arg_PixelPosY
[e791]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e794]bc 00 06LDY a:ScreenBuffer,X
[e797]20 c6 e8JSR Area_GetBlockProperty
[e79a]85 b7STA Blocks_Result
[e79c]20 b2 e8JSR Area_IsBlockClimbable
[e79f]b0 11BCS @_resetCanClimb
[e7a1]a5 9eLDA Player_PosX_Block
[e7a3]29 0fAND #$0f
[e7a5]c9 08CMP #$08
[e7a7]d0 09BNE @_resetCanClimb
[e7a9]e8INX
[e7aa]bc 00 06LDY a:ScreenBuffer,X
[e7ad]20 c6 e8JSR Area_GetBlockProperty
[e7b0]85 b7STA Blocks_Result
;
; Reset the can-climb bit.
;
[e7b2]@_resetCanClimb:
[e7b2]a5 a4LDA Player_Flags; Load the player's flags.
[e7b4]29 f7AND #$f7; Clear the can-climb bit.
[e7b6]20 b2 e8JSR Area_IsBlockClimbable; Is the current block climbable?
[e7b9]90 05BCC @_clearBit5AndReturn; If not, jump.
;
; Set the can-climb bit.
;
[e7bb]09 08ORA #$08; Else, set the can-climbable bit.
[e7bd]85 a4STA Player_Flags; Set the ladder flag bit
[e7bf]60RTS
[e7c0]@_clearBit5AndReturn:
[e7c0]29 efAND #$ef; Clear bit 5.
[e7c2]85 a4STA Player_Flags; Store it.
[e7c4]60RTS
;============================================================================
; Set state from the area at the other end of the door.
;
; This checks if the player is in front of a door (being
; careful to check any overlapping blocks) and then looks up
; the area at the other end of the door.
;
; If a matching door is found (one that maps to the current
; screen and block position), then a new screen index, start
; position, palette, and door key requirement for the
; current screen will be set. This prepares for switching to
; the new area.
;
; INPUTS:
;
Player_PosX_Block:
; The X position of the player.
;
;
Player_PosY:
; The Y position of the player.
;
; OUTPUTS:
;
Blocks_Result:
; 1 if a door could be entered.
; 0 if one could not.
;
;
Screen_StartPosYX:
; The start position when switching to the new area.
;
;
Area_LoadingScreenIndex:
; The new screen when switching to the new area.
;
;
Screen_DestPaletteOrIndex:
; The new palette when switching to the new area.
;
;
CurrentDoor_KeyRequirement:
; The key requirement for the matching doro when
; switching to the new area.
;
;
Temp_Addr_L
;
Temp_Addr_U
;
Arg_PixelPosX:
;
Arg_PixelPosX:
;
Temp_BlockPos2
; Clobbered.
;
; CALLS:
;
Area_ConvertPixelsToBlockPos
;
Area_GetBlockProperty
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e7c5]Area_SetStateFromDoorDestination:
;
; Convert the player position to a normalized block position.
;
[e7c5]a5 9eLDA Player_PosX_Block; Load the player's full X position.
[e7c7]18CLC
[e7c8]69 07ADC #$07; Add 7.
[e7ca]85 b5STA Arg_PixelPosX; Store as the X pixel position argument.
[e7cc]a5 a1LDA Player_PosY; Load the player's full Y position.
[e7ce]85 b6STA Arg_PixelPosY; Store as the Y pixel position argument.
[e7d0]20 6c e8JSR Area_ConvertPixelsToBlockPos; Conver to block positions.
;
; Load the block at this position from the screen and
; check what it is.
;
[e7d3]bc 00 06LDY a:ScreenBuffer,X; Load the block from the screen buffer.
[e7d6]20 c6 e8JSR Area_GetBlockProperty; Get the block property.
[e7d9]c9 03CMP #$03; Is it a door?
[e7db]f0 18BEQ @_blockIsDoor
;
; It's not a door. The player may still be near a door.
; Let's round to the nearest block to the right and check
; again.
;
[e7dd]a5 9eLDA Player_PosX_Block; Load the player's full X position.
[e7df]29 0fAND #$0f; Check the lower nibble.
[e7e1]c9 08CMP #$08; Is it on a full block boundary?
[e7e3]d0 0bBNE @_blockIsNotDoor; _blockIsAir
[e7e5]e8INX; Increment the block position.
;
; We normalized to the next overlapped block. Now check
; to see if it's a door.
;
[e7e6]bc 00 06LDY a:ScreenBuffer,X; Load the block at that position.
[e7e9]20 c6 e8JSR Area_GetBlockProperty; Load the block property.
[e7ec]c9 03CMP #$03; Is it a door?
[e7ee]f0 05BEQ @_blockIsDoor; If so, jump.
;
; The player was not standing in front of a door.
; Consider this block passable.
;
[e7f0]@_blockIsNotDoor:
[e7f0]a9 00LDA #$00
[e7f2]85 b7STA Blocks_Result; Set the resulting block property as air.
[e7f4]60RTS
;
; The player is standing in front of a door.
;
; Check to see where they are.
;
[e7f5]@_blockIsDoor:
[e7f5]a5 24LDA Area_CurrentArea; Load the current region.
[e7f7]c9 04CMP #$04; Is this the area around Victim(?) ?
[e7f9]d0 03BNE @LAB_PRG15_MIRROR__e7fe; If not, jump and continue on.
;
; We won't be handling anything in this area. We're done here.
;
[e7fb]4c da e5JMP SUB_RETURN_E5DA; Else, we're done.
[e7fe]@LAB_PRG15_MIRROR__e7fe:
[e7fe]a9 01LDA #$01
[e800]85 b7STA Blocks_Result; Mark the resulting block as solid by default.
[e802]86 6aSTX Temp_BlockPos; Store our block X position temporarily.
;
; Get the location of the doors for this area and store
; the address for access below.
;
[e804]a5 8dLDA CurrentArea_DoorLocationsAddr; Load the lower byte of the doors address.
[e806]85 02STA Temp_Addr_L; Store it temporarily.
[e808]a5 8eLDA CurrentArea_DoorLocationsAddr_U; Load the upper byte.
[e80a]85 03STA Temp_Addr_U; Store it temporarily.
;
; Switch to Bank 3 (level data).
;
[e80c]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[e80f]48PHA; Push it to the stack.
[e810]a2 03LDX #$03
[e812]20 1a ccJSR MMC1_UpdateROMBank; Load bank 3.
;
; Check the byte stored in address stored starting at
;
Temp_Addr_L to see if it's an air block or the screen
; index.
;
[e815]@_doorCheckLoop:
[e815]a0 00LDY #$00; Y = 0
[e817]b1 02LDA (Temp_Addr_L),Y; Load the first byte from the referenced address.
[e819]c9 ffCMP #$ff; Is it 0xFF?
[e81b]f0 45BEQ @_returnAirBlock; If so, consider this air and return.
[e81d]c5 63CMP Area_CurrentScreen; Is it the current screen index?
[e81f]d0 31BNE @LAB_PRG15_MIRROR__e852; If not, jump.
;
; That was a match.
;
; Next, check the next byte in the address referenced by
;
Temp_Addr_L to see if it's the stored block position
; from above (
Temp_BlockPos).
;
[e821]c8INY; Y++
[e822]b1 02LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e824]c5 6aCMP Temp_BlockPos; Is it the block position we stored?
[e826]d0 2aBNE @LAB_PRG15_MIRROR__e852; If not, jump.
;
; That was a match too.
;
; Load the next byte referenced in
Temp_Addr_L and store
; that
; as a block position in
Temp_BlockPos2.
;
[e828]c8INY; Y++
[e829]b1 02LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e82b]85 6bSTA Temp_BlockPos2; Store that byte as a temporary block position.
[e82d]c8INY; Y++
[e82e]b1 02LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e830]85 6cSTA Screen_StartPosYX; Store as the area's starting X/Y position.
[e832]a5 6bLDA Temp_BlockPos2; A = Byte we had just stored up above.
;
; Check the current area. If it's around Mascon somewhere,
; we'll need to reduce the value by 32.
;
; TODO: Check why this is.
;
[e834]a4 24LDY Area_CurrentArea; Y = current area
[e836]c0 03CPY #$03; Is the current area 3?
[e838]d0 03BNE @LAB_PRG15_MIRROR__e83d; If not, jump.
;
; We'll need to normalize the block position.
;
[e83a]38SEC
[e83b]e9 20SBC #$20; Reduce it by 32.
[e83d]@LAB_PRG15_MIRROR__e83d:
[e83d]0aASL A; Multiply the block position by 4.
[e83e]0aASL A
;
; Load state from the door destination at this block.
;
; We'll load the screen index, palette, and the key
; requirement.
;
[e83f]a8TAY; Y = A (block position)
[e840]b1 8fLDA (CurrentArea_DoorDestinationsAddr),Y; Load the screen index from the door destination.
[e842]85 64STA Area_LoadingScreenIndex; Store it as the new screen index.
[e844]c8INY; Y++
[e845]b1 8fLDA (CurrentArea_DoorDestinationsAddr),Y; Load the palette from the door destination.
[e847]85 65STA Screen_DestPaletteOrIndex; Store it as the new palette.
[e849]c8INY; Y++
[e84a]b1 8fLDA (CurrentArea_DoorDestinationsAddr),Y; Load the key requirement.
[e84c]8d 2b 04STA a:CurrentDoor_KeyRequirement; Store it.
[e84f]4c 66 e8JMP @_restoreBankAndReturn; We're done. Restore our bank and set our results.
;
; Skip the 4 bytes of information of the area at the other end
; of the door destination. We'll set up to process the next one.
;
[e852]@LAB_PRG15_MIRROR__e852:
[e852]a5 02LDA Temp_Addr_L; Load the lower byte of the door destination address we stored.
[e854]18CLC
[e855]69 04ADC #$04; Add 4 (skip the information found on the other side of that door).
[e857]85 02STA Temp_Addr_L; Store it back out.
[e859]a5 03LDA Temp_Addr_U; Load the upper byte of the address.
[e85b]69 00ADC #$00; Add the carry flag, if the lower byte overflowed.
[e85d]85 03STA Temp_Addr_U; Store it.
[e85f]4c 15 e8JMP @_doorCheckLoop
;
; Consider this air.
;
[e862]@_returnAirBlock:
[e862]a9 00LDA #$00
[e864]85 b7STA Blocks_Result; Set the resulting block property to 0 (air).
;
; Restore the previous ROM bank and return.
;
[e866]@_restoreBankAndReturn:
[e866]68PLA; Pop the previous bank from the stack.
[e867]aaTAX; X = A (bank)
[e868]20 1a ccJSR MMC1_UpdateROMBank; Update to the bank.
[e86b]60RTS
[e86c]Area_ConvertPixelsToBlockPos:
[e86c]a5 b6LDA Arg_PixelPosY; Load the stored Y coordinate for comparison
[e86e]29 f0AND #$f0; Clear out the lower nibble
[e870]85 00STA Temp_00
[e872]a5 b5LDA Arg_PixelPosX; Load the stored X coordinate for comparison
[e874]4aLSR A; Convert X to block positions
[e875]4aLSR A
[e876]4aLSR A
[e877]4aLSR A
[e878]05 00ORA Temp_00; Include the Y block position
[e87a]aaTAX; Store in X and return
[e87b]60RTS
[e87c]ScreenBuffer_IsBlockImpassable:
[e87c]bc 00 06LDY a:ScreenBuffer,X; Load the block at the given index, and fall through.
;
; v-- Fall through --v
;
[e87f]Area_IsBlockImpassable:
[e87f]20 c6 e8JSR Area_GetBlockProperty; Get the property of the provided block, and fall through.
;
; v-- Fall through --v
;
;============================================================================
; Return whether a block property is an impassable type.
;
; INPUTS:
; A:
; The block property type.
;
;
BLOCK_PROPERTY_IMPASSABLE_MAP:
; The map of impassable block properties.
;
; OUTPUTS:
; A:
; 1 if the block is impassable.
; 0 if it is passable.
;
; XREFS:
;
Area_IsBlockImpassableOrLadder
;============================================================================
[e882]Area_GetFromImpassableMap:
[e882]a8TAY; Y = A
[e883]b9 d9 e8LDA a:BLOCK_PROPERTY_IMPASSABLE_MAP,Y; A = Impassable flag for the block property type.
[e886]60RTS; And return it.
;============================================================================
; Return whether a block at the given screen buffer index is impassable or a
; ladder.
;
; INPUTS:
; X:
; The index within the screen buffer, for
; getting the block property index.
;
;
BLOCK_PROPERTY_IMPASSABLE_MAP:
; A map of impassable block types.
;
; OUTPUTS:
; A:
; 1 if the block is impassable or a ladder.
; 0 if it is passable.
;
; CALLS:
;
Area_GetBlockProperty
;
; XREFS:
;
Sprites_CheckPlacementInvalidAtY
;============================================================================
[e887]Area_IsBlockImpassableOrLadder:
[e887]bc 00 06LDY a:ScreenBuffer,X; Load the block property offset from the screen buffer at X.
[e88a]20 c6 e8JSR Area_GetBlockProperty; Load the block property.
[e88d]c9 02CMP #$02; Is it a ladder?
[e88f]d0 f1BNE Area_GetFromImpassableMap; If not a ladder, look up from the passable map.
;
; This is a ladder.
;
[e891]a9 01LDA #$01; Return true.
[e893]60RTS
;============================================================================
; DEADCODE: Set whether a block is truly air.
;
; This loads the specified block property and then
; determines if it's actually air. This will be stored in
;
Blocks_Result.
;
; INPUTS:
; X:
; The index into the screen buffer.
;
; OUTPUTS:
;
Blocks_Result:
; The result of the check.
;
; 1 for solid-like.
; 0 for air-like.
;
; CALLS:
;
Area_GetBlockProperty
;============================================================================
[e894]Area_StoreBlockIsAir:
;
; Load the block property at this screen buffer offset.
;
[e894]bc 00 06LDY a:ScreenBuffer,X; Y = block property at screen buffer X
[e897]20 c6 e8JSR Area_GetBlockProperty; Load the block property.
[e89a]85 b7STA Blocks_Result; Store that in a temporary variable.
;
; Check if this is a solid block.
;
[e89c]c9 01CMP #$01; Is it solid?
[e89e]f0 0dBEQ @_blockIsSolid; If so, then jump.
;
; Check if this is a ladder.
;
[e8a0]c9 02CMP #$02; Is it a ladder?
[e8a2]f0 09BEQ @_blockIsSolid; If so, then jump.
;
; Check if this is a block transitioning to another screen.
;
[e8a4]c9 0aCMP #$0a; Is this a transition to a screen?
[e8a6]f0 05BEQ @_blockIsSolid; If so, then jump.
;
; It's not solid. Store a result of 0 (air).
;
[e8a8]a9 00LDA #$00
[e8aa]85 b7STA Blocks_Result; Set result = 0.
[e8ac]60RTS
;
; It is solid. Store a result of 1 (solid).
;
[e8ad]@_blockIsSolid:
[e8ad]a9 01LDA #$01
[e8af]85 b7STA Blocks_Result; Set result = 1.
[e8b1]60RTS
;============================================================================
; Determine if the current block is climbable.
;
; This will check the provided block property (stored in
; a temp variable) and check if it can be climbed.
;
; INPUTS:
;
Blocks_Result:
; The block property to check.
;
; OUTPUTS:
; C:
; 1 if it's climbable.
; 0 if it is not.
;
; XREFS:
;
Player_CheckIfOnLadder
;============================================================================
[e8b2]Area_IsBlockClimbable:
[e8b2]48PHA; Push A to the stack.
[e8b3]a5 b7LDA Blocks_Result; Load the block property provided in the temp variable.
[e8b5]c9 02CMP #$02; Is it a ladder?
[e8b7]f0 04BEQ @_isLadder; If so, jump.
[e8b9]c9 0aCMP #$0a; TODO: Is it... something else ladder-like?
[e8bb]d0 03BNE @_isNotLadder; If not, jump.
;
; This is a ladder.
;
[e8bd]@_isLadder:
[e8bd]68PLA; Pop A.
[e8be]38SEC; Set carry to 1 as a truthy result.
[e8bf]60RTS
[e8c0]@_isNotLadder:
[e8c0]68PLA; Pop A.
[e8c1]18CLC; Set carry to 0 as a falsy result.
[e8c2]60RTS
;============================================================================
; Return the block property referenced at the given screen buffer index.
;
; This will load the block property referenced in the screen
; buffer and then fall through to
;
Area_GetBlockProperty.
;
; Block properties are stored with upper and lower nibbles
; representing different blocks. This will look up the
; appropriate block property based on whether the provided
; index is even or odd, and return a value with just a lower
; nibble set based on the properties.
;
; INPUTS:
; X:
; The index into the screen buffer.
;
;
BlockProperties:
; The block properties to load from.
;
; OUTPUTS:
; A:
; A byte containing the property.
;
; XREFS:
;
CurrentSprite_CalculateVisibility
;
Player_CalculateVisibility
;
SpriteBehavior_NecronAides
;
CastMagic_CalculateVisibility
;
Player_CheckSwitchScreen
;============================================================================
[e8c3]ScreenBuffer_LoadBlockProperty:
[e8c3]bc 00 06LDY a:ScreenBuffer,X; Load the block property index from the screen buffer.
;
; v-- Fall through --v
;
[e8c6]Area_GetBlockProperty:
[e8c6]98TYA; A = Y
[e8c7]4aLSR A; Y = even block offset based on the index.
[e8c8]a8TAY; Y = A
[e8c9]90 08BCC @_isEven; Check whether this is even or odd.
;
; Load the block property at the index, moving the data in
; the upper nibble to the lower nibble.
;
[e8cb]b9 3c 04LDA a:BlockProperties,Y; Load the block property at the index.
[e8ce]4aLSR A; Shift to the lower nibble.
[e8cf]4aLSR A
[e8d0]4aLSR A
[e8d1]4aLSR A
[e8d2]60RTS
;
; Load the block property, masking out the upper nibble.
;
[e8d3]@_isEven:
[e8d3]b9 3c 04LDA a:BlockProperties,Y; Load the block property at the index.
[e8d6]29 0fAND #$0f; Mask out the upper nibble, giving just the lower.
[e8d8]60RTS
;============================================================================
; Map of block property IDs to impassibility flags.
;
; XREFS:
;
Area_GetFromImpassableMap
;============================================================================
[e8d9]BLOCK_PROPERTY_IMPASSABLE_MAP:
[e8d9]00.byte $00; [0]: Air
[e8da]01.byte $01; [1]: Solid
[e8db]00.byte $00; [2]: Ladder
[e8dc]00.byte $00; [3]: Door
[e8dd]00.byte $00; [4]: Foreground
[e8de]01.byte $01; [5]: Breakable floor
[e8df]01.byte $01; [6]: Pushable
[e8e0]01.byte $01; [7]: ??
[e8e1]01.byte $01; [8]: ??
[e8e2]00.byte $00; [9]: Maybe: Transition down
[e8e3]00.byte $00; [10]: Maybe: Transition up
[e8e4]01.byte $01; [11]: Breakable by Mattock
[e8e5]00.byte $00; [12]: Area transition left-to-right
[e8e6]00.byte $00; [13]: Area transition right-to-left
[e8e7]00.byte $00; [14]: ??
[e8e8]00.byte $00; [15]: ??
[e8e9]Area_ScrollTo:
[e8e9]ad 2e 04LDA a:CurrentScreen_SpecialEventID; Load the current screen's event ID.
[e8ec]29 7fAND #$7f; Keep the numeric ID.
[e8ee]c9 01CMP #$01; Is it 1 (boss room)?
[e8f0]d0 05BNE @_clearStates; If not, jump.
;
; The player is scrolling from a boss room. Reset the music
; back to the default music for the area.
;
[e8f2]ad d1 03LDA a:Areas_DefaultMusic; Load the default music for the area.
[e8f5]85 faSTA Music_Current; Set as the current music.
[e8f7]@_clearStates:
[e8f7]a9 00LDA #$00; A = 0
[e8f9]85 a3STA Player_MovementTick; Set player movement tick to 0.
[e8fb]85 1aSTA InterruptCounter; Set interrupt counter to 0.
;
; Clear any cast magic.
;
[e8fd]a9 ffLDA #$ff; A = 0xFF (unset)
[e8ff]8d b3 02STA a:CastMagic_Type; Set as the magic type on screen.
[e902]4c 27 d1JMP Area_LoadScrollData; Now scroll to the room.
[e905]Player_CheckOnBreakableBlock:
;
; Only check for falling periodically.
;
[e905]a5 1aLDA InterruptCounter; Load the interrupt counter.
[e907]29 07AND #$07; Are we ready to check for falling?
[e909]d0 4fBNE @_return; If not, return.
;
; Begin checking against a block 32 pixels down.
;
[e90b]a5 a1LDA Player_PosY; A = Player's Y position.
[e90d]18CLC
[e90e]69 20ADC #$20; A += 32
[e910]85 b6STA Arg_PixelPosY; Store as the Y argument for block checks.
[e912]c9 f0CMP #$f0; Is it >= 0xEF? (one block below the screen)
[e914]b0 44BCS @_return; If so, stop falling.
;
; The player may be permitted to fall. Check the block
; it's on.
;
[e916]a5 a4LDA Player_Flags; Load the player's flags.
[e918]29 40AND #$40; Keep only the facing bit.
[e91a]2aROL A; Shift this into bit 0.
[e91b]2aROL A
[e91c]2aROL A
[e91d]29 01AND #$01; Mask out everything else.
[e91f]48PHA; Push it to the stack.
;
; Check the first block the player is overlapping.
;
[e920]aaTAX; X = facing value.
[e921]a5 9eLDA Player_PosX_Block; A = Player X position.
[e923]18CLC
[e924]7d 5b e9ADC a:@_CHECK_BREAKABLE_BLOCK_X_OFFSETS,X; Load a block offset to round to the nearest overlapping blocks.
[e927]85 b5STA Arg_PixelPosX; Store as the X position to check.
[e929]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert it to a block position.
[e92c]bc 00 06LDY a:ScreenBuffer,X; Load the block value from the screen.
[e92f]84 b7STY Blocks_Result; Store as the block result.
[e931]20 c6 e8JSR Area_GetBlockProperty; And get its corresponding property.
[e934]c9 05CMP #$05; Is it 5 (breakable floor)?
[e936]d0 04BNE @_checkNextBlock; If not, check the next overlapping block.
[e938]68PLA; Pop the facing bit from the stack.
[e939]4c ce d6JMP Area_HandleBreakableFloor; Handle breakable floor logic.
;
; Check the second block the player is overlapping.
;
[e93c]@_checkNextBlock:
[e93c]68PLA; Pop the facing bit from the stack.
[e93d]49 01EOR #$01; XOR the facing bit to check the other direction.
[e93f]aaTAX; X = A
[e940]a5 9eLDA Player_PosX_Block; A = Player X position.
[e942]18CLC
[e943]7d 5b e9ADC a:@_CHECK_BREAKABLE_BLOCK_X_OFFSETS,X; Load a block offset to round to the next overlapping blocks.
[e946]85 b5STA Arg_PixelPosX; Store as the X position to check.
[e948]20 6c e8JSR Area_ConvertPixelsToBlockPos; Convert it to a block position.
[e94b]bc 00 06LDY a:ScreenBuffer,X; Load the block value from the screen.
[e94e]84 b7STY Blocks_Result; Store as the block result.
[e950]20 c6 e8JSR Area_GetBlockProperty
[e953]c9 05CMP #$05; Is it 5 (breakable floor)?
[e955]d0 03BNE @_return; If not, return.
[e957]4c ce d6JMP Area_HandleBreakableFloor; Handle breakable floor logic.
[e95a]@_return:
[e95a]60RTS
;============================================================================
; Map of facing bit values to block X offsets for checking breakable blocks.
;
; XREFS:
;
Player_CheckOnBreakableBlock
;============================================================================
[e95b]@_CHECK_BREAKABLE_BLOCK_X_OFFSETS:
[e95b]04.byte $04; [0]: Facing left
[e95c]0c.byte $0c; [1]: Facing right
;============================================================================
; TODO: Document Player_CheckPushingBlock
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e95d]Player_CheckPushingBlock:
[e95d]a5 16LDA Joy1_ButtonMask
[e95f]29 03AND #$03
[e961]f0 56BEQ @LAB_PRG15_MIRROR__e9b9
[e963]ad 2c 04LDA a:SpecialItems
[e966]29 40AND #$40
[e968]f0 4fBEQ @LAB_PRG15_MIRROR__e9b9
[e96a]ad 2d 04LDA a:Quests
[e96d]29 07AND #$07
[e96f]c9 07CMP #$07
[e971]d0 46BNE @LAB_PRG15_MIRROR__e9b9
[e973]a5 a4LDA Player_Flags
[e975]29 40AND #$40
[e977]2aROL A
[e978]2aROL A
[e979]2aROL A
[e97a]29 01AND #$01
[e97c]aaTAX
[e97d]a5 a1LDA Player_PosY
[e97f]85 b6STA Arg_PixelPosY
[e981]a5 9eLDA Player_PosX_Block
[e983]18CLC
[e984]7d be e9ADC a:BYTE_ARRAY_PRG15_MIRROR__e9be,X
[e987]85 b5STA Arg_PixelPosX
;
; Fetch the block property at this position.
;
[e989]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e98c]bc 00 06LDY a:ScreenBuffer,X
[e98f]20 c6 e8JSR Area_GetBlockProperty
;
; Check if the block is pushable. If so, we'll handle it.
;
[e992]c9 06CMP #$06
[e994]d0 23BNE @LAB_PRG15_MIRROR__e9b9
;
; This is pushable. Increase the push counter.
;
; We'll play a sound when we hit 0x3F, and finish pushing
; after 0x60.
;
[e996]e6 d7INC BlockPushCounter
[e998]a5 d7LDA BlockPushCounter
[e99a]c9 3fCMP #$3f
[e99c]d0 05BNE @LAB_PRG15_MIRROR__e9a3
[e99e]a9 0fLDA #$0f
[e9a0]20 e4 d0JSR Sound_PlayEffect
[e9a3]@LAB_PRG15_MIRROR__e9a3:
[e9a3]a5 d7LDA BlockPushCounter
[e9a5]c9 60CMP #$60
[e9a7]90 0fBCC @_return
[e9a9]a9 01LDA #$01
[e9ab]85 d4STA PathToMascon_Opening
[e9ad]86 d5STX PathToMascon_FountainCoverPos
[e9af]a5 16LDA Joy1_ButtonMask
[e9b1]4aLSR A
[e9b2]29 01AND #$01
[e9b4]aaTAX
[e9b5]4c f5 d6JMP Game_OpenPathToMascon
[e9b8]@_return:
[e9b8]60RTS
[e9b9]@LAB_PRG15_MIRROR__e9b9:
[e9b9]a9 00LDA #$00
[e9bb]85 d7STA BlockPushCounter
[e9bd]60RTS
[e9be]BYTE_ARRAY_PRG15_MIRROR__e9be:
[e9be]ff.byte $ff; [0]:
[e9bf]10.byte $10; [1]:
;============================================================================
; TODO: Document Player_CheckSwitchScreen
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e9c0]Player_CheckSwitchScreen:
;
; Check the current block the player is on.
;
[e9c0]a5 a1LDA Player_PosY
[e9c2]85 b6STA Arg_PixelPosY
[e9c4]a5 9eLDA Player_PosX_Block
[e9c6]85 b5STA Arg_PixelPosX
[e9c8]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e9cb]20 c3 e8JSR ScreenBuffer_LoadBlockProperty
[e9ce]c9 09CMP #$09
[e9d0]f0 0cBEQ @LAB_PRG15_MIRROR__e9de
[e9d2]c9 0aCMP #$0a
[e9d4]f0 08BEQ @LAB_PRG15_MIRROR__e9de
[e9d6]c9 0dCMP #$0d
[e9d8]f0 04BEQ @LAB_PRG15_MIRROR__e9de
[e9da]c9 0cCMP #$0c
[e9dc]d0 58BNE @_return
;
; Check the next block.
;
[e9de]@LAB_PRG15_MIRROR__e9de:
[e9de]a5 9eLDA Player_PosX_Block
[e9e0]18CLC
[e9e1]69 0fADC #$0f
[e9e3]85 b5STA Arg_PixelPosX
[e9e5]20 6c e8JSR Area_ConvertPixelsToBlockPos
[e9e8]20 c3 e8JSR ScreenBuffer_LoadBlockProperty
[e9eb]c9 09CMP #$09
[e9ed]f0 0cBEQ @LAB_PRG15_MIRROR__e9fb
[e9ef]c9 0aCMP #$0a
[e9f1]f0 08BEQ @LAB_PRG15_MIRROR__e9fb
[e9f3]c9 0dCMP #$0d
[e9f5]f0 04BEQ @LAB_PRG15_MIRROR__e9fb
[e9f7]c9 0cCMP #$0c
[e9f9]d0 3bBNE @_return
;
; Check if this is a horizontal transition block. If so,
; switch the area.
;
[e9fb]@LAB_PRG15_MIRROR__e9fb:
[e9fb]c9 0cCMP #$0c
[e9fd]f0 60BEQ Player_CheckSwitchScreen_SwitchAreaHoriz
[e9ff]c9 0dCMP #$0d
[ea01]f0 5cBEQ Player_CheckSwitchScreen_SwitchAreaHoriz
;
; This is a vertical transition block. Switch the
; area.
;
[ea03]a5 24LDA Area_CurrentArea
[ea05]0aASL A
[ea06]aaTAX
[ea07]bd 37 eaLDA a:AREA_SCREEN_COMPARATORS,X
[ea0a]85 02STA Temp_Addr_L
[ea0c]bd 38 eaLDA a:AREA_SCREEN_COMPARATORS+1,X
[ea0f]85 03STA Temp_Addr_U
[ea11]a0 00LDY #$00
[ea13]@LAB_PRG15_MIRROR__ea13:
[ea13]b1 02LDA (Temp_Addr_L),Y
[ea15]c9 ffCMP #$ff
[ea17]f0 1dBEQ @_return
[ea19]c5 63CMP Area_CurrentScreen
[ea1b]d0 12BNE @LAB_PRG15_MIRROR__ea2f
[ea1d]c8INY
[ea1e]b1 02LDA (Temp_Addr_L),Y
[ea20]85 64STA Area_LoadingScreenIndex
[ea22]c8INY
[ea23]b1 02LDA (Temp_Addr_L),Y
[ea25]85 6cSTA Screen_StartPosYX
[ea27]c8INY
[ea28]b1 02LDA (Temp_Addr_L),Y
[ea2a]85 65STA Screen_DestPaletteOrIndex
[ea2c]4c a0 daJMP Game_SetupEnterScreen
[ea2f]@LAB_PRG15_MIRROR__ea2f:
[ea2f]c8INY
[ea30]c8INY
[ea31]c8INY
[ea32]c8INY
[ea33]4c 13 eaJMP @LAB_PRG15_MIRROR__ea13
[ea36]@_return:
[ea36]60RTS
[ea37]AREA_SCREEN_COMPARATORS:
[ea37]4f eapointer AREA_SCREEN_COMPARATOR_END; Eolis
[ea39]47 eapointer AREA_SCREEN_COMPARATOR_APOLUNE; Apolune
[ea3b]4f eapointer AREA_SCREEN_COMPARATOR_END; Forepaw
[ea3d]4f eapointer AREA_SCREEN_COMPARATOR_END; Mascon
[ea3f]4f eapointer AREA_SCREEN_COMPARATOR_END; Victim
[ea41]4f eapointer AREA_SCREEN_COMPARATOR_END; Conflate
[ea43]4f eapointer AREA_SCREEN_COMPARATOR_END; Daybreak
[ea45]4f eapointer AREA_SCREEN_COMPARATOR_END; Evil Fortress
[ea47]AREA_SCREEN_COMPARATOR_APOLUNE:
[ea47]0c.byte $0c; Current screen comparator
[ea48]16.byte $16; New screen index
[ea49]b3.byte $b3; Y, X
[ea4a]06.byte $06; Area
[ea4b]16.byte $16; Current screen comparator
[ea4c]0c.byte $0c; New screen index
[ea4d]2d.byte $2d; Y, X
[ea4e]06.byte $06; Area
[ea4f]AREA_SCREEN_COMPARATOR_END:
[ea4f]ff.byte $ff; byte
[ea50]Game_BeginExitBuilding:
;
; Face the player to the right.
;
[ea50]a5 a4LDA Player_Flags; Load the player flags.
[ea52]09 40ORA #$40; Face the player right.
[ea54]85 a4STA Player_Flags; Store the flags.
[ea56]ad d2 03LDA a:Area_Music_Outside; Load the music used outside the building.
[ea59]8d d1 03STA a:Areas_DefaultMusic; Set as the music to play.
[ea5c]4c be daJMP Game_SetupExitBuilding; Jump to finish the exit-building logic.
;============================================================================
; TODO: Document Player_CheckSwitchScreen_SwitchAreaHoriz
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckSwitchScreen
;============================================================================
[ea5f]Player_CheckSwitchScreen_SwitchAreaHoriz:
;
; Check if the player is in the area of Victim.
;
[ea5f]a5 24LDA Area_CurrentArea
[ea61]c9 04CMP #$04
[ea63]f0 ebBEQ Game_BeginExitBuilding
[ea65]0aASL A
[ea66]a8TAY
[ea67]b9 9c eaLDA a:AREA_TOWN_TRANSITIONS_DATA,Y
[ea6a]85 02STA Temp_Addr_L
[ea6c]b9 9d eaLDA a:AREA_TOWN_TRANSITIONS_DATA+1,Y
[ea6f]85 03STA Temp_Addr_U
[ea71]a0 00LDY #$00
[ea73]@LAB_PRG15_MIRROR__ea73:
[ea73]b1 02LDA (Temp_Addr_L),Y
[ea75]c9 ffCMP #$ff
[ea77]f0 22BEQ @_return
[ea79]c5 63CMP Area_CurrentScreen
[ea7b]d0 17BNE @LAB_PRG15_MIRROR__ea94
[ea7d]c8INY
[ea7e]b1 02LDA (Temp_Addr_L),Y
[ea80]85 24STA Area_CurrentArea
[ea82]c8INY
[ea83]b1 02LDA (Temp_Addr_L),Y
[ea85]85 64STA Area_LoadingScreenIndex
[ea87]c8INY
[ea88]b1 02LDA (Temp_Addr_L),Y
[ea8a]85 6cSTA Screen_StartPosYX
[ea8c]c8INY
[ea8d]b1 02LDA (Temp_Addr_L),Y
[ea8f]85 65STA Screen_DestPaletteOrIndex
[ea91]4c cd daJMP Game_SetupNewArea
[ea94]@LAB_PRG15_MIRROR__ea94:
[ea94]98TYA
[ea95]18CLC
[ea96]69 05ADC #$05
[ea98]a8TAY
[ea99]90 d8BCC @LAB_PRG15_MIRROR__ea73
[ea9b]@_return:
[ea9b]60RTS
[ea9c]AREA_TOWN_TRANSITIONS_DATA:
[ea9c]2e ebpointer TOWN_TRANSITIONS_EMPTY; [0]: Eolis
[ea9e]ac eapointer TOWN_TRANSITIONS_TRUNK; [1]: Trunk
[eaa0]03 ebpointer TOWN_TRANSITIONS_MIST; [2]: Mist
[eaa2]c6 eapointer TOWN_TRANSITIONS_TOWNS; [3]: Towns
[eaa4]2e ebpointer TOWN_TRANSITIONS_EMPTY; [4]: Buildings
[eaa6]18 ebpointer TOWN_TRANSITIONS_BRANCH; [5]: Branch
[eaa8]28 ebpointer TOWN_TRANSITIONS_DARTMOOR; [6]: Dartmoor
[eaaa]2e ebpointer TOWN_TRANSITIONS_EMPTY; [7]: Zenis
[eaac]TOWN_TRANSITIONS_TRUNK:
[eaac]00.byte $00; byte
[eaad]03.byte AREA_TOWNS; Area
[eaae]02 92.byte $02,$92; byte
[eab0]1b.byte PALETTE_TOWN; Palette
[eab1]07.byte $07; Entering Trunk from Trunk (screen 7)
[eab2]03.byte AREA_TOWNS; |-> Switch to area 3
[eab3]00.byte $00; |-> Screen index 0
[eab4]92.byte $92; |-> Start position Y=9, X=2
[eab5]1b.byte PALETTE_TOWN; '-> Palette 27
[eab6]08.byte $08; byte
[eab7]03.byte AREA_TOWNS; Area
[eab8]01 9e.byte $01,$9e; byte
[eaba]1b.byte PALETTE_TOWN; Palette
[eabb]1a.byte $1a; Entering Trunk from Trunk (screen 26)
[eabc]03.byte AREA_TOWNS; |-> Switch to area 3
[eabd]02.byte $02; |-> Screen index 2
[eabe]92.byte $92; |-> Start position X=2, Y=9
[eabf]1b.byte PALETTE_TOWN; '-> Palette 27
[eac0]1d.byte $1d; byte
[eac1]03.byte AREA_TOWNS; Area
[eac2]03 9e.byte $03,$9e; byte
[eac4]1b.byte PALETTE_TOWN; Palette
[eac5]ff.byte $ff; byte
[eac6]TOWN_TRANSITIONS_TOWNS:
[eac6]00.byte $00; Exiting left from Apolune screen 0
[eac7]01.byte AREA_TRUNK; |-> Switch to area 1
[eac8]07.byte $07; |-> Screen index 6
[eac9]7e.byte $7e; |-> Start position Y=7, X=14
[eaca]06.byte PALETTE_OUTSIDE; '-> Palette 6
[eacb]01.byte $01; Exit right from Apolune screen 1
[eacc]01.byte AREA_TRUNK; |-> Switch to area 1
[eacd]08.byte $08; |-> Screen index 8
[eace]71.byte $71; |-> Start position Y=7, X=1
[eacf]06.byte PALETTE_OUTSIDE; '-> Palette 6
[ead0]02.byte $02; Exiting left from Forepaw to Trunk (screen 2)
[ead1]01.byte AREA_TRUNK; |-> Switch to area 1
[ead2]1a.byte $1a; |-> Screen index 26
[ead3]7e.byte $7e; |-> Start position X=14, Y=7
[ead4]06.byte PALETTE_OUTSIDE; '-> Palette 6
[ead5]03.byte $03; Exiting right from Forepaw to Branch (screen 3)
[ead6]01.byte AREA_TRUNK; |-> Switch to area 3
[ead7]1d.byte $1d; |-> Screen index 29
[ead8]71.byte $71; |-> Start position X=1, Y=7
[ead9]06.byte PALETTE_OUTSIDE; |-> Palette 6
[eada]04.byte $04; Exiting left from Mascon to Mist (screen 4)
[eadb]02.byte AREA_MIST; |-> Switch to area 2
[eadc]09.byte $09; |-> Screen index 9
[eadd]9e.byte $9e; |-> Start position X=14, Y=9
[eade]0a.byte PALETTE_MIST; '-> Palette 10
[eadf]05.byte $05; Exiting right from Mascon to Mist (screen 5)
[eae0]02.byte AREA_MIST; |-> Switch to area 2
[eae1]0c.byte $0c; |-> Screen index 12
[eae2]91.byte $91; |-> Start position X=1, Y=9
[eae3]0a.byte PALETTE_MIST; '-> Palette 10
[eae4]06.byte $06; Exit left from Victim to Mist from screen 6
[eae5]02.byte AREA_MIST; |-> Switch to area 2
[eae6]22.byte $22; |-> Screen index 34
[eae7]9e.byte $9e; |-> Start position X=14, Y=9
[eae8]0a.byte PALETTE_MIST; '-> Palette 10
[eae9]07.byte $07; Exit right from Victim to Mist on screen 7
[eaea]02.byte AREA_MIST; |-> Switch to area 7
[eaeb]25.byte $25; |-> Screen index = 37
[eaec]91.byte $91; |-> Start position X=1, Y=9
[eaed]0a.byte PALETTE_MIST; '-> Palette 10
[eaee]08.byte $08; Exit left from Conflate to Branch
[eaef]05.byte AREA_BRANCH; |-> Switch to area 5
[eaf0]0d.byte $0d; |-> Screen index 13
[eaf1]7e.byte $7e; |-> Start position X=14, Y=7
[eaf2]08.byte PALETTE_BRANCH; '-> Palette 8
[eaf3]0a.byte $0a; Exit left from Daybreak to Branch
[eaf4]05.byte AREA_BRANCH; |-> Switch to area 5
[eaf5]23.byte $23; |-> Screen index 35
[eaf6]7e.byte $7e; |-> Start position X=14, Y=7
[eaf7]08.byte PALETTE_BRANCH; '-> Palette 8
[eaf8]0b.byte $0b; Exit right from Daybreak to Branch
[eaf9]05.byte AREA_BRANCH; |-> Switch to area 5
[eafa]24.byte $24; |-> Screen index 36
[eafb]71.byte $71; |-> Start position X=1, Y=7
[eafc]08.byte PALETTE_BRANCH; '-> Palette 8
[eafd]0c.byte $0c; Exit left from Dartmoor to Branch (screen 12)
[eafe]06.byte AREA_DARTMOOR; |-> Switch to area 6
[eaff]03.byte $03; |-> Screen index 3
[eb00]7e.byte $7e; |-> Start position X=14, Y=7
[eb01]0c.byte PALETTE_DARTMOOR; '-> Palette 12
[eb02]ff.byte $ff; byte
[eb03]TOWN_TRANSITIONS_MIST:
[eb03]09.byte $09; Entering Mascon from Mist (screen 9)
[eb04]03.byte AREA_TOWNS; |-> Switch to area 3
[eb05]04.byte $04; |-> Screen index 4
[eb06]91.byte $91; |-> Start position X=1, Y=9
[eb07]1b.byte PALETTE_TOWN; '-> Palette 27
[eb08]0c.byte $0c; Entering left to Mascon from Mist
[eb09]03.byte AREA_TOWNS; |-> Switch to area 3
[eb0a]05.byte $05; |-> Screen index 5
[eb0b]9e.byte $9e; |-> Start position X=14, Y=9
[eb0c]1b.byte PALETTE_TOWN; '-> Palette 27
[eb0d]22.byte $22; Enering right to Victim from Mist (screen 34)
[eb0e]03.byte AREA_TOWNS; |-> Switch to area 3
[eb0f]06.byte $06; |-> Screen index 6
[eb10]91.byte $91; |-> Start position X=1, Y=9
[eb11]1b.byte PALETTE_TOWN; '-> Palette 27
[eb12]25.byte $25; Entering left to Victim from Mist (screen 37)
[eb13]03.byte AREA_TOWNS; |-> Switch to area 3
[eb14]07.byte $07; |-> Screen index 7
[eb15]9e.byte $9e; |-> Start position X=14, Y=9
[eb16]1b.byte PALETTE_TOWN; '-> Palette 27
[eb17]ff.byte $ff; byte
[eb18]TOWN_TRANSITIONS_BRANCH:
[eb18]0d.byte $0d; Entering right to Conflate from Branch (screen 13)
[eb19]03.byte AREA_TOWNS; |-> Switch to area 3
[eb1a]08.byte $08; |-> Screen index 8
[eb1b]91.byte $91; |-> Start position X=1, Y=9
[eb1c]1b.byte PALETTE_TOWN; '-> Palette 27
[eb1d]23.byte $23; Entering Daybreak from Branch (screen 35)
[eb1e]03.byte AREA_TOWNS; |-> Switch to area 3
[eb1f]0a.byte $0a; |-> Screen index 10
[eb20]92.byte $92; |-> Start position X=2, Y=9
[eb21]1b.byte PALETTE_TOWN; '-> Palette 27
[eb22]24.byte $24; Entering Daybreak from Branch (screen 36)
[eb23]03.byte AREA_TOWNS; |-> Switch to area 3
[eb24]0b.byte $0b; |-> Screen index 11
[eb25]9e.byte $9e; |-> Start position X=14, Y=9
[eb26]1b.byte PALETTE_TOWN; '-> Palette 27
[eb27]ff.byte $ff; byte
[eb28]TOWN_TRANSITIONS_DARTMOOR:
[eb28]03.byte $03; Entering right to Dartmoor from Branch (screen 3)
[eb29]03.byte AREA_TOWNS; |-> Switch to area 3
[eb2a]0c.byte $0c; |-> Screen index 12
[eb2b]92.byte $92; |-> Start position X=2, Y=9
[eb2c]1b.byte PALETTE_TOWN; '-> Palette 27
[eb2d]ff.byte $ff; byte
[eb2e]TOWN_TRANSITIONS_EMPTY:
[eb2e]ff.byte $ff; byte
;============================================================================
; Run the door requirement handler function for the current door.
;
; This will look up the door requirement handler function
; corresponding to
CurrentDoor_KeyRequirement and run it,
; checking that the requirements for the door have been met.
; The Checks and behavior are entirely up to that function.
;
; INPUTS:
;
CurrentDoor_KeyRequirement:
; The key requirement for the current door.
;
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS:
; The lookup table of door requirement handlers.
;
; OUTPUTS:
; None
;
; XREFS:
;
Player_CheckHandleEnterDoor
;
Player_EnterDoorToOutside
;============================================================================
[eb2f]Game_RunDoorRequirementHandler:
[eb2f]ad 2b 04LDA a:CurrentDoor_KeyRequirement
[eb32]f0 0aBEQ RETURN_EB3E
[eb34]0aASL A
[eb35]a8TAY
[eb36]b9 40 ebLDA a:DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS+1,Y
[eb39]48PHA
[eb3a]b9 3f ebLDA a:DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS,Y
[eb3d]48PHA
;============================================================================
; TODO: Document RETURN_EB3E
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb3f]
;
Game_RunDoorRequirementHandler
;============================================================================
[eb3e]RETURN_EB3E:
[eb3e]60RTS
;============================================================================
; A mapping of door requirement lookup function addresses.
;
; XREFS:
;
Game_RunDoorRequirementHandler
;============================================================================
[eb3f]DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS:
[eb3f]3d ebpointer RETURN_EB3E-1; [0]: [0]: No key, return
[eb41]50 ebpointer Game_OpenDoorWithAKey-1; [0]: [1]: "A" Key
[eb43]60 ebpointer Game_OpenDoorWithKKey-1; [0]: [2]: "K" Key
[eb45]70 ebpointer Game_OpenDoorWithQKey-1; [0]: [3]: "Q" Key
[eb47]80 ebpointer Game_OpenDoorWithJKey-1; [0]: [4]: "J" Key
[eb49]90 ebpointer Game_OpenDoorWithJoKey-1; [0]: [5]: "Jo" Key
[eb4b]a0 ebpointer Game_OpenDoorWithRingOfElf-1; [0]: [6]: Ring of Elf
[eb4d]b0 ebpointer Game_OpenDoorWithRingOfDworf-1; [0]: [7]: Ring of Dworf
[eb4f]c0 ebpointer Game_OpenDoorWithDemonsRing-1; [0]: [8]: Demon's Ring
;============================================================================
; Attempt to open a door marked with the "A" Key.
;
; This will check if the player has the "A" Key, and
; if they do, the door will be unlocked and entered.
; The key will be consumed.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb41]
;============================================================================
[eb51]Game_OpenDoorWithAKey:
[eb51]ad c1 03LDA a:SelectedItem; Load the selected item.
[eb54]c9 04CMP #$04; Is this the "A" Key?
[eb56]f0 79BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb58]a9 7dLDA #$7d; 0x7D == "A" Key required IScript.
[eb5a]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[eb5d]0c.byte BANK_12_LOGIC; Bank = 12
[eb5e]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[eb60]@_afterFarJump:
[eb60]60RTS
;============================================================================
; Attempt to open a door marked with the "K" Key.
;
; This will check if the player has the "K" Key, and
; if they do, the door will be unlocked and entered.
; The key will be consumed.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb43]
;============================================================================
[eb61]Game_OpenDoorWithKKey:
[eb61]ad c1 03LDA a:SelectedItem; Load the selected item.
[eb64]c9 05CMP #$05; Is this the "K" Key?
[eb66]f0 69BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb68]a9 7cLDA #$7c; 0x7C == "K" Key required IScript.
[eb6a]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[eb6d]0c.byte BANK_12_LOGIC; Bank = 12
[eb6e]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[eb70]@_afterFarJump:
[eb70]60RTS
;============================================================================
; Attempt to open a door marked with the "Q" Key.
;
; This will check if the player has the "Q" Key, and
; if they do, the door will be unlocked and entered.
; The key will be consumed.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb45]
;============================================================================
[eb71]Game_OpenDoorWithQKey:
[eb71]ad c1 03LDA a:SelectedItem; Load the selected item.
[eb74]c9 06CMP #$06; Is this the "Q" Key?
[eb76]f0 59BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb78]a9 7bLDA #$7b; 0x7B == "Q" Key required IScript.
[eb7a]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[eb7d]0c.byte BANK_12_LOGIC; Bank = 12
[eb7e]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[eb80]@_afterFarJump:
[eb80]60RTS
;============================================================================
; Attempt to open a door marked with the "J" Key.
;
; This will check if the player has the "J" Key, and
; if they do, the door will be unlocked and entered.
; The key will be consumed.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb47]
;============================================================================
[eb81]Game_OpenDoorWithJKey:
[eb81]ad c1 03LDA a:SelectedItem; Load the selected item.
[eb84]c9 07CMP #$07; Is this the "J" Key?
[eb86]f0 49BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb88]a9 02LDA #$02; 0x02 == "J" Key required IScript.
[eb8a]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[eb8d]0c.byte BANK_12_LOGIC; Bank = 12
[eb8e]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[eb90]@_afterFarJump:
[eb90]60RTS
;============================================================================
; Attempt to open a door marked with the "Jo" Key.
;
; This will check if the player has the "Jo" Key, and
; if they do, the door will be unlocked and entered.
; The key will be consumed.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb49]
;============================================================================
[eb91]Game_OpenDoorWithJoKey:
[eb91]ad c1 03LDA a:SelectedItem; Load the selected item.
[eb94]c9 08CMP #$08; Is this the "Jo" Key?
[eb96]f0 39BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb98]a9 7eLDA #$7e; 0x7E == "Jo" Key required IScript.
[eb9a]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[eb9d]0c.byte BANK_12_LOGIC; Bank = 12
[eb9e]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[eba0]@_afterFarJump:
[eba0]60RTS
;============================================================================
; Attempt to open a door marked with the Ring of Elf.
;
; This will check if the player has the Ring of Elf, and
; if they do, the door will be unlocked and entered.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb4b]
;============================================================================
[eba1]Game_OpenDoorWithRingOfElf:
[eba1]ad 2c 04LDA a:SpecialItems; Load the special items.
[eba4]29 80AND #$80; Does the player have the Ring of Elf?
[eba6]d0 39BNE Game_UnlockDoor; If so, unlock the door and enter.
[eba8]a9 7fLDA #$7f; 0x7F == Ring required IScript.
[ebaa]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[ebad]0c.byte BANK_12_LOGIC; Bank = 12
[ebae]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[ebb0]@_afterFarJump:
[ebb0]60RTS
;============================================================================
; Attempt to open a door marked with the Ring of Dworf.
;
; This will check if the player has the Ring of Dworf, and
; if they do, the door will be unlocked and entered.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb4d]
;============================================================================
[ebb1]Game_OpenDoorWithRingOfDworf:
[ebb1]ad 2c 04LDA a:SpecialItems; Load the special items.
[ebb4]29 20AND #$20; Does the player have the Ring of Dworf?
[ebb6]d0 29BNE Game_UnlockDoor; If so, unlock the door and enter.
[ebb8]a9 7fLDA #$7f; 0x7F == Ring required IScript.
[ebba]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[ebbd]0c.byte BANK_12_LOGIC; Bank = 12
[ebbe]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[ebc0]@_afterFarJump:
[ebc0]60RTS
;============================================================================
; Attempt to open a door marked with the Demon's Ring.
;
; This will check if the player has the Demon's Ring, and
; if they do, the door will be unlocked and entered.
;
; If the door can't be unlocked, a message will be
; displayed.
;
; INPUTS:
;
SelectedItem:
; The selected item.
;
; CALLS:
;
Game_UnlockDoorWithUsableItem
;
MMC1_LoadBankAndJump
;
; XREFS:
;
DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS
; [$PRG15_MIRROR::eb4f]
;============================================================================
[ebc1]Game_OpenDoorWithDemonsRing:
[ebc1]ad 2c 04LDA a:SpecialItems; Load the special items.
[ebc4]29 10AND #$10; Does the player have the Demon's Ring?
[ebc6]d0 19BNE Game_UnlockDoor; If so, unlock the door and enter.
[ebc8]a9 7fLDA #$7f; 0x7F == Ring required IScript.
[ebca]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[ebcd]0c.byte BANK_12_LOGIC; Bank = 12
[ebce]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[ebd0]@_afterFarJump:
[ebd0]60RTS
[ebd1]Game_UnlockDoorWithUsableItem:
[ebd1]a9 84LDA #$84; 0x84 == Used key IScript.
[ebd3]20 59 f8JSR MMC1_LoadBankAndJump; Run the IScript:
[ebd6]0c.byte BANK_12_LOGIC; Bank = 12
[ebd7]41 82pointer IScripts_Begin-1; Address = IScripts_Begin
[ebd9]@_afterFarJump:
[ebd9]a9 ffLDA #$ff
[ebdb]8d c1 03STA a:SelectedItem
[ebde]20 dc c8JSR UI_ClearSelectedItemPic
[ebe1]Game_UnlockDoor:
[ebe1]a9 00LDA #$00
[ebe3]8d 2b 04STA a:CurrentDoor_KeyRequirement
[ebe6]a9 84LDA #$84
[ebe8]a9 06LDA #$06
[ebea]20 e4 d0JSR Sound_PlayEffect
[ebed]60RTS
[ebee]Player_DrawBody:
[ebee]ad c7 03LDA a:IScript_PortraitID
[ebf1]30 01BMI @LAB_PRG15_MIRROR__ebf4
[ebf3]60RTS
;
; Check the loaded state for the screen to determine
; the arguments to use.
;
; NOTE: This seems to be old code. Nothing sets
;
Screen_ReadyState to anything but 0x00 or 0xFF.
; It appears that at some point they simplified this, which
; makes all of this dead code.
;
[ebf4]@LAB_PRG15_MIRROR__ebf4:
[ebf4]a5 0eLDA Screen_ReadyState
[ebf6]c9 01CMP #$01
[ebf8]f0 17BEQ @LAB_PRG15_MIRROR__ec11
[ebfa]c9 05CMP #$05
[ebfc]f0 13BEQ @LAB_PRG15_MIRROR__ec11
;
; Ths code path should always be hit when this function is
; called.
;
[ebfe]a5 9eLDA Player_PosX_Block
[ec00]85 27STA Arg_DrawSprite_PosX
[ec02]a5 9fLDA Screen_Maybe_ScrollXCounter
[ec04]85 2bSTA Unused_Sprite_ScrollPosX
[ec06]a5 a1LDA Player_PosY
[ec08]85 28STA Arg_DrawSprite_PosY
[ec0a]a5 a2LDA Player_Something_ScrollPosY
[ec0c]85 2aSTA Unused_Sprite_ScrollPosY
[ec0e]4c 21 ecJMP @LAB_PRG15_MIRROR__ec21
;
; This whole section seems unreachable.
Screen_ReadyState
; should never reach these values. This may be dead code.
;
[ec11]@LAB_PRG15_MIRROR__ec11:
[ec11]a5 b2LDA Maybe_PlayerX_ForScroll
[ec13]85 27STA Arg_DrawSprite_PosX
[ec15]a5 59LDA PPU_ScrollScreenHoriz
[ec17]85 2bSTA Unused_Sprite_ScrollPosX
[ec19]a5 b3LDA Maybe_PlayerY_ForScroll
[ec1b]85 28STA Arg_DrawSprite_PosY
[ec1d]a5 58LDA PPU_ScrollScreenVert
[ec1f]85 2aSTA Unused_Sprite_ScrollPosY
[ec21]@LAB_PRG15_MIRROR__ec21:
[ec21]20 ed b9JSR Player_CalculateVisibility
[ec24]20 51 ecJSR Player_SetFacingLeft
[ec27]20 ac ecJSR Player_GetBodySpriteFrameOffset
[ec2a]48PHA
[ec2b]ad be 03LDA a:SelectedArmor
[ec2e]0aASL A
[ec2f]85 00STA Temp_00
[ec31]ad bf 03LDA a:SelectedShield
[ec34]c9 03CMP #$03
[ec36]a9 00LDA #$00
[ec38]2aROL A
[ec39]49 01EOR #$01
[ec3b]05 00ORA Temp_00
[ec3d]aaTAX
[ec3e]68PLA
[ec3f]18CLC
[ec40]7d 49 ecADC a:PLAYER_BODY_TILEINFO_START_OFFSETS,X
[ec43]20 39 f0JSR Sprite_SetPlayerAppearanceAddr
[ec46]4c 58 ecJMP FUN_PRG15_MIRROR__ec58
[ec49]PLAYER_BODY_TILEINFO_START_OFFSETS:
[ec49]00.byte $00; [0]: Leather Armor
[ec4a]08.byte $08; [1]: Leather Armor + Shield
[ec4b]10.byte $10; [2]: Studded Mail
[ec4c]18.byte $18; [3]: Studded Mail + Shield
[ec4d]20.byte $20; [4]: Full Plate
[ec4e]28.byte $28; [5]: Full Plate + Shield
[ec4f]30.byte $30; [6]: Battle Suit
[ec50]38.byte $38; [7]: Battle Suit + Shield
;============================================================================
; TODO: Document Player_SetFacingLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ec51]Player_SetFacingLeft:
[ec51]a5 a4LDA Player_Flags
[ec53]29 40AND #$40
[ec55]85 29STA CurrentSprite_FlipMask
[ec57]60RTS
;============================================================================
; TODO: Document FUN_PRG15_MIRROR__ec58
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ec58]FUN_PRG15_MIRROR__ec58:
[ec58]a5 a4LDA Player_Flags
[ec5a]10 45BPL @_return
[ec5c]a5 aeLDA Player_AttackPhaseAnimFrame
[ec5e]c9 02CMP #$02
[ec60]d0 3fBNE @_return
[ec62]a0 00LDY #$00
[ec64]a5 a4LDA Player_Flags
[ec66]29 40AND #$40
[ec68]f0 01BEQ @LAB_PRG15_MIRROR__ec6b
[ec6a]c8INY
[ec6b]@LAB_PRG15_MIRROR__ec6b:
[ec6b]b9 a2 ecLDA a:BYTE_ARRAY_PRG15_MIRROR__eca2,Y
[ec6e]20 80 b8JSR Player_CalcValueAndFFForNeg
[ec71]a5 9eLDA Player_PosX_Block
[ec73]18CLC
[ec74]65 04ADC Temp_04
[ec76]85 27STA Arg_DrawSprite_PosX
[ec78]a5 9eLDA Player_PosX_Block
[ec7a]65 05ADC Temp_05
[ec7c]c5 9eCMP Player_PosX_Block
[ec7e]d0 21BNE @_return
[ec80]a5 a1LDA Player_PosY
[ec82]85 28STA Arg_DrawSprite_PosY
[ec84]20 ed b9JSR Player_CalculateVisibility
[ec87]ad be 03LDA a:SelectedArmor
[ec8a]0aASL A
[ec8b]85 00STA Temp_00
[ec8d]a0 00LDY #$00
[ec8f]ad bf 03LDA a:SelectedShield
[ec92]c9 03CMP #$03
[ec94]b0 01BCS @LAB_PRG15_MIRROR__ec97
[ec96]c8INY
[ec97]@LAB_PRG15_MIRROR__ec97:
[ec97]98TYA
[ec98]05 00ORA Temp_00
[ec9a]a8TAY
[ec9b]b9 a4 ecLDA a:BYTE_ARRAY_PRG15_MIRROR__eca4,Y
[ec9e]4c 39 f0JMP Sprite_SetPlayerAppearanceAddr
[eca1]@_return:
[eca1]60RTS
[eca2]BYTE_ARRAY_PRG15_MIRROR__eca2:
[eca2]f8.byte $f8; [0]:
[eca3]BYTE_ARRAY_PRG15_MIRROR__eca2_1_:
[eca3]10.byte $10; [1]:
[eca4]BYTE_ARRAY_PRG15_MIRROR__eca4:
[eca4]63.byte $63; [0]: Leather Armor
[eca5]67.byte $67; [1]: Leather Armor + Shield
[eca6]64.byte $64; [2]: Studded Mail
[eca7]68.byte $68; [3]: Studded Mail + Shield
[eca8]65.byte $65; [4]: Full Plate
[eca9]69.byte $69; [5]: Full Plate + Shield
[ecaa]66.byte $66; [6]: Battle Suit
[ecab]6a.byte $6a; [7]: Battle Suit + Shield
;============================================================================
; TODO: Document Player_GetBodySpriteFrameOffset
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ecac]Player_GetBodySpriteFrameOffset:
[ecac]a5 a4LDA Player_Flags
[ecae]4aLSR A
[ecaf]90 07BCC @LAB_PRG15_MIRROR__ecb8
[ecb1]a5 a4LDA Player_Flags
[ecb3]30 17BMI @LAB_PRG15_MIRROR__eccc
[ecb5]a9 03LDA #$03
[ecb7]60RTS
[ecb8]@LAB_PRG15_MIRROR__ecb8:
[ecb8]20 f6 ecJSR Player_IsClimbing
[ecbb]90 0bBCC @LAB_PRG15_MIRROR__ecc8
[ecbd]a5 a3LDA Player_MovementTick
[ecbf]29 10AND #$10
[ecc1]0aASL A
[ecc2]0aASL A
[ecc3]85 29STA CurrentSprite_FlipMask
[ecc5]a9 07LDA #$07
[ecc7]60RTS
[ecc8]@LAB_PRG15_MIRROR__ecc8:
[ecc8]a5 a4LDA Player_Flags
[ecca]10 06BPL @LAB_PRG15_MIRROR__ecd2
[eccc]@LAB_PRG15_MIRROR__eccc:
[eccc]a6 aeLDX Player_AttackPhaseAnimFrame
[ecce]bd f3 ecLDA a:BYTE_ARRAY_PRG15_MIRROR__ecf3,X
[ecd1]60RTS
[ecd2]@LAB_PRG15_MIRROR__ecd2:
[ecd2]a5 a5LDA Player_StatusFlag
[ecd4]10 07BPL @LAB_PRG15_MIRROR__ecdd
[ecd6]a5 16LDA Joy1_ButtonMask
[ecd8]10 03BPL @LAB_PRG15_MIRROR__ecdd
[ecda]a9 03LDA #$03
[ecdc]60RTS
[ecdd]@LAB_PRG15_MIRROR__ecdd:
[ecdd]a5 a4LDA Player_Flags
[ecdf]29 20AND #$20
[ece1]f0 07BEQ @LAB_PRG15_MIRROR__ecea
[ece3]a5 a3LDA Player_MovementTick
[ece5]4aLSR A
[ece6]4aLSR A
[ece7]4aLSR A
[ece8]29 03AND #$03
[ecea]@LAB_PRG15_MIRROR__ecea:
[ecea]aaTAX
[eceb]bd ef ecLDA a:BYTE_ARRAY_PRG15_MIRROR__ecef,X
[ecee]60RTS
[ecef]BYTE_ARRAY_PRG15_MIRROR__ecef:
[ecef]00.byte $00; [0]:
[ecf0]01.byte $01; [1]:
[ecf1]02.byte $02; [2]:
[ecf2]01.byte $01; [3]:
[ecf3]BYTE_ARRAY_PRG15_MIRROR__ecf3:
[ecf3]04.byte $04; [0]:
[ecf4]05.byte $05; [1]:
[ecf5]06.byte $06; [2]:
;============================================================================
; Return whether the player is climbing a ladder.
;
; This will depend on the following conditions:
;
; 1. Whether the player is in front of a ladder.
;
; 2. Whether the player is either:
;
; 1. Directly on a ladder block, or
;
; 2. Not falling off the ladder.
;
; 3. Whether the climbing bit is set.
;
; INPUTS:
;
Player_Flags:
; The player's current flags.
;
;
Player_PosX_Block:
; The full X position of the player.
;
; OUTPUTS:
; C:
; 1 if the player is climbing.
; 0 if the player is not.
;
; XREFS:
;
Player_CastMagic
;
Player_CheckHandleAttack
;
Player_GetBodySpriteFrameOffset
;
Player_UpdateClimbOrJump
;============================================================================
[ecf6]Player_IsClimbing:
;
; Check if the player can currently climb an available ladder.
;
[ecf6]a5 a4LDA Player_Flags; Load the player's flags.
[ecf8]29 08AND #$08; Can the player currently climb?
[ecfa]f0 14BEQ @_returnFalse; If not, return false.
;
; A ladder is available to climb.
;
; Check if the player's X position is on an even block boundary.
;
[ecfc]a5 9eLDA Player_PosX_Block; Load the player's X position.
[ecfe]29 0fAND #$0f; Is it on a block boundary?
[ed00]f0 06BEQ @_checkClimbing; If not, then jump to check if the player is climbing.
;
; The player's X position is on an even byte boundary.
;
; Check if the player is currently falling off a ledge or
; ladder.
;
[ed02]a5 a4LDA Player_Flags; Load the player's flags.
[ed04]29 04AND #$04; Is the player currently falling?
[ed06]d0 08BNE @_returnFalse; If so, return false.
;
; Check if the player is currently climbing.
;
[ed08]@_checkClimbing:
[ed08]a5 a4LDA Player_Flags; Load the player's flags.
[ed0a]29 10AND #$10; Is the player climbing?
[ed0c]f0 02BEQ @_returnFalse; If not, return false.
[ed0e]38SEC; Set C = 1 to return true.
[ed0f]60RTS
[ed10]@_returnFalse:
[ed10]18CLC; Set C = 0 to return false.
[ed11]60RTS
[ed12]Player_DrawSpriteImmediately:
;
; Load and draw the player's sprite.
;
[ed12]20 72 edJSR Player_LoadArmorTilesToPPU; Load the armor sprite information.
[ed15]@_drawArmorLoop:
[ed15]20 a9 eeJSR Player_LoadArmorTile; Draw a tile of armor.
[ed18]20 3c cfJSR PPUBuffer_Draw; Draw to the PPU.
[ed1b]e6 a8INC Player_DrawTileReadOffset; Increment the read offset.
[ed1d]c6 00DEC Temp_00; Decrement the tile count.
[ed1f]10 f4BPL @_drawArmorLoop; If >= 0, loop.
;
; Load and draw the weapon sprite, if one is equipped.
;
[ed21]20 9d edJSR Player_LoadWeaponTilesToPPU; Load the weapon sprite information.
[ed24]a5 a8LDA Player_DrawTileReadOffset; Get the tile read offset.
[ed26]30 0cBMI @_loadShield; If < 0, skip weapon drawing.
;
; The player has a weapon equipped. Draw the tiles.
;
[ed28]@_drawWeaponLoop:
[ed28]20 bf eeJSR Player_LoadWeaponTile; Draw a tile of the weapon.
[ed2b]20 3c cfJSR PPUBuffer_Draw; Draw to the PPU.
[ed2e]e6 a8INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed30]c6 00DEC Temp_00; Decrement the tile count.
[ed32]10 f4BPL @_drawWeaponLoop; If >= 0, loop.
;
; Load and draw the shield sprite.
;
[ed34]@_loadShield:
[ed34]20 cd edJSR Player_LoadShieldTilesToPPU; Load the shield sprite information.
[ed37]a5 a8LDA Player_DrawTileReadOffset; Load the tile read offset.
[ed39]30 09BMI @_return; If < 0 (unset), return.
[ed3b]@_drawShieldLoop:
[ed3b]20 93 eeJSR Player_LoadShieldTile; Draw a tile of the shield.
[ed3e]e6 a8INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed40]c6 00DEC Temp_00; Decrement the tile count.
[ed42]10 f7BPL @_drawShieldLoop; If >= 0, loop.
[ed44]@_return:
[ed44]60RTS
[ed45]Player_DrawSprite:
;
; Load and draw the player's sprite.
;
[ed45]20 72 edJSR Player_LoadArmorTilesToPPU; Load the armor sprite information.
[ed48]@_drawArmorLoop:
[ed48]20 a9 eeJSR Player_LoadArmorTile; Draw a tile of armor.
[ed4b]e6 a8INC Player_DrawTileReadOffset; Increment the read offset.
[ed4d]c6 00DEC Temp_00; Decrement the tile count.
[ed4f]10 f7BPL @_drawArmorLoop; If >= 0, loop.
;
; Load and draw the weapon sprite, if one is equipped.
;
[ed51]20 9d edJSR Player_LoadWeaponTilesToPPU; Load the weapon sprite information.
[ed54]a5 a8LDA Player_DrawTileReadOffset; Get the tile read offset.
[ed56]30 09BMI @_loadShield; If < 0, skip weapon drawing.
;
; The player has a weapon equipped. Draw the tiles.
;
[ed58]@_drawWeaponLoop:
[ed58]20 bf eeJSR Player_LoadWeaponTile; Draw a tile of the weapon.
[ed5b]e6 a8INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed5d]c6 00DEC Temp_00; Decrement the tile count.
[ed5f]10 f7BPL @_drawWeaponLoop; If >= 0, loop.
;
; Load and draw the shield sprite.
;
[ed61]@_loadShield:
[ed61]20 cd edJSR Player_LoadShieldTilesToPPU; Load the shield sprite information.
[ed64]a5 a8LDA Player_DrawTileReadOffset; Load the tile read offset.
[ed66]30 09BMI @_return; If < 0 (unset), return.
[ed68]@_drawShieldLoop:
[ed68]20 93 eeJSR Player_LoadShieldTile; Draw a tile of the shield.
[ed6b]e6 a8INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed6d]c6 00DEC Temp_00; Decrement the tile count.
[ed6f]10 f7BPL @_drawShieldLoop; If >= 0, loop.
[ed71]@_return:
[ed71]60RTS
[ed72]Player_LoadArmorTilesToPPU:
;
; Clear initial state for drawing the armor.
;
[ed72]a9 00LDA #$00
[ed74]85 a8STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[ed76]85 91STA Player_SpriteSegmentPPUAddr_L; Set the sprite segment PPU addresses to 0.
[ed78]85 92STA Player_SpriteSegmentPPUAddr_U
;
; Calculate an index into the tile information tables for the
; armor (and possibly armor + shield, if the Battle Suit isn't
; equipped).
;
[ed7a]ad be 03LDA a:SelectedArmor; Load the selected armor.
[ed7d]0aASL A; Convert to a word boundary.
[ed7e]ac bf 03LDY a:SelectedShield; Load the selected shield.
[ed81]c0 03CPY #$03; Is it the Battle Helmet?
[ed83]b0 02BCS @_loadArmor; If so, jump (skip special armor + shield logic).
;
; There's a shield equipped, and the player isn't wielding
; the full Battle Suit + Battle Helmet. Set the index to be
; odd so we look up a sprite entry compatible with a shield.
;
[ed85]09 01ORA #$01; Offset the lookup index to include armor + shield tiles.
[ed87]@_loadArmor:
[ed87]85 a7STA Player_ArmorLookupIndex; Store the lookup index based on the armor word boundary and possibly the shield bit.
[ed89]aaTAX; X = result.
;
; Determine the number of tiles to load from the lookup table.
;
; The caller will use this to iterate through the tiles to
; load.
;
[ed8a]bd 95 edLDA a:PLAYER_ARMOR_TILE_COUNTS,X; Load the tile count.
[ed8d]85 00STA Temp_00; Store it for the parent caller to use.
;
; Normalize the lookup index to be based on a 4-byte
; boundary (2 bytes for the armor address, 2 for the
; armor + shield address), and then load the sprite
; tiles.
;
[ed8f]8aTXA; A = Lookup index.
[ed90]0aASL A; Multiply by 2 (so index considers armor and armor + shield entries).
[ed91]a8TAY; Y = result.
[ed92]4c 15 eeJMP Player_LoadArmorSpriteTilesAddr; Load the sprite tiles information.
;============================================================================
; The number of available tiles used for all player poses
; for a given armor/shield combination.
;
; XREFS:
;
Player_LoadArmorTilesToPPU
;============================================================================
[ed95]PLAYER_ARMOR_TILE_COUNTS:
[ed95]33.byte $33; [0]: Leather Armor (51 tiles)
[ed96]27.byte $27; [1]: Leather Armor + Shield (39 tiles)
[ed97]33.byte $33; [2]: Studded Mail (51 tiles)
[ed98]27.byte $27; [3]: Studded Mail + Shield (39 tiles)
[ed99]34.byte $34; [4]: Full Plate (52 tiles)
[ed9a]28.byte $28; [5]: Full Plate + Shield (40 tiles)
[ed9b]32.byte $32; [6]: Battle Helmet (50 tiles)
[ed9c]32.byte $32; [7]: Battle Helmet + Shield (50 tiles)
[ed9d]Player_LoadWeaponTilesToPPU:
[ed9d]a9 00LDA #$00
[ed9f]85 a8STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[eda1]ad c8 03LDA a:Player_CurWeapon; Load the selected weapon.
[eda4]c9 ffCMP #$ff; Is it 0xFF (unset)?
[eda6]d0 03BNE @_loadWeaponTiles; If not, jump to load tiles.
;
; No sword is equipped. Set the offset to 0xFF, to disable
; drawing.
;
[eda8]85 a8STA Player_DrawTileReadOffset; Set the read offset to 0xFF.
[edaa]60RTS
;
; Determine the number of tiles to load from the lookup table.
;
; The caller will use this to iterate through the tiles to
; load.
;
[edab]@_loadWeaponTiles:
[edab]aaTAX; X = weapon.
[edac]bd c1 edLDA a:PLAYER_WEAPON_TILE_COUNTS,X; Load the tile count.
[edaf]85 00STA Temp_00; Store it for the parent caller to use.
;
; Set the PPU address to load tiles into and load them.
;
[edb1]8aTXA; X = tile count.
[edb2]0aASL A; Convert to a word boundary.
[edb3]a8TAY; Y = resulting index.
[edb4]b9 c5 edLDA a:PLAYER_WEAPON_TILE_PPU_ADDRS,Y; Load the lower byte of the target PPU address for weapons.
[edb7]85 91STA Player_SpriteSegmentPPUAddr_L; Set it for load.
[edb9]b9 c6 edLDA a:PLAYER_WEAPON_TILE_PPU_ADDRS+1,Y; Load the upper byte of the target PPU address for weapons.
[edbc]85 92STA Player_SpriteSegmentPPUAddr_U; Set it for load.
[edbe]4c 3f eeJMP Player_LoadWeaponSpriteTileAddrs; Load the sprite tiles information.
;============================================================================
; The number of available tiles used for all player poses
; for a given weapon.
;
; XREFS:
;
Player_LoadWeaponTilesToPPU
;============================================================================
[edc1]PLAYER_WEAPON_TILE_COUNTS:
[edc1]02.byte $02; [0]: Dagger
[edc2]05.byte $05; [1]: Long Sword
[edc3]06.byte $06; [2]: Giant Blade
[edc4]08.byte $08; [3]: Dragon Slayer
;============================================================================
; PPU target addresses for each weapon type's loaded tiles.
;
; XREFS:
;
Player_LoadWeaponTilesToPPU
;============================================================================
[edc5]PLAYER_WEAPON_TILE_PPU_ADDRS:
[edc5]80 03.word $0380; [0]: Dagger
[edc7]80 03.word $0380; [1]: Long Sword
[edc9]80 03.word $0380; [2]: Giant Blade
[edcb]40 03.word $0340; [3]: Dragon Slayer
[edcd]Player_LoadShieldTilesToPPU:
[edcd]a9 00LDA #$00
[edcf]85 a8STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[edd1]ad bf 03LDA a:SelectedShield; Load the selected shield.
[edd4]c9 03CMP #$03; Is it the Battle Helmet (or unset -- 0xFF)?
[edd6]90 03BCC @_loadShieldTiles; If not, then jump to handle standard shields.
;
; This is the Battle Helmet or there's no helmet equipped.
; In either case, set the loaded value as the result.
;
[edd8]85 a8STA Player_DrawTileReadOffset; Set the read offset to the shield value. This will be 3 or 0xFF.
[edda]60RTS; And return.
;
; An actual shield is equipped. Begin setting the lookup
; index for the shield and a tile count of 5.
;
[eddb]@_loadShieldTiles:
[eddb]0aASL A; Convert the shield index to a word value for lookup.
[eddc]a8TAY; Y = resulting index.
[eddd]a9 05LDA #$05; 5 tiles.
[eddf]85 00STA Temp_00; Set as the tile count for the load loop.
;
; Set the PPU address to load into to 0x0300.
;
[ede1]a9 00LDA #$00
[ede3]85 91STA Player_SpriteSegmentPPUAddr_L; Set the lower byte of the PPU address to 0.
[ede5]a9 03LDA #$03
[ede7]85 92STA Player_SpriteSegmentPPUAddr_U; And upper to 3.
[ede9]4c 69 eeJMP Player_LoadShieldSpriteTileAddrs; Load the sprite tiles information.
;============================================================================
; Set the player's current weapon.
;
; If the player is in an area where the weapon can be
; equipped, then equip and redraw the player sprite.
;
; INPUTS:
; A:
; The weapon to set:
;
; 0 = Hand Dagger
; 1 = Long Sword
; 2 = Giant Blade
; 3 = Dragon Slayer
;
; OUTPUTS:
;
SelectedWeapon:
; The updated selected weapon.
;
;
Player_CurWeapon:
; The weapon currently equipped by the player,
; if in an area allowing that.
;
; CALLS:
;
Player_DrawSprite
;
; XREFS:
;
Player_Equip
;============================================================================
[edec]Player_SetWeapon:
[edec]48PHA; Push the weapon to the stack.
;
; Check that the player is not in a building.
;
; A weapon cannot be set while in a building.
;
[eded]a5 24LDA Area_CurrentArea; Set A = current area.
[edef]c9 04CMP #$04; Is this the building?
[edf1]f0 0cBEQ @_cannotEquip; If so, branch.
;
; The weapon can be equipped. Equip it and redraw the player.
;
[edf3]68PLA; Pop the weapon from the stack.
[edf4]8d bd 03STA a:SelectedWeapon; Set as the current weapon in the inventory.
[edf7]8d c8 03STA a:Player_CurWeapon; Store as the current weapon.
[edfa]20 45 edJSR Player_DrawSprite; Draw the player sprite.
[edfd]18CLC; Clear Carry.
[edfe]60RTS
;
; The weapon cannot currently be equipped. Set the weapon
; but do not draw.
;
[edff]@_cannotEquip:
[edff]68PLA; Pop the weapon from the stack.
[ee00]8d bd 03STA a:SelectedWeapon; Set as the current weapon.
[ee03]18CLC; Clear Carry.
[ee04]60RTS
;============================================================================
; Set the player's current armor.
;
; The armor will be equipped and the player sprite redrawn.
;
; INPUTS:
; A:
; 0 = Leather
; 1 = Studded Mail
; 2 = Full Plate
; 3 = Battle Suit
;
; OUTPUTS:
;
SelectedArmor:
; The new selected armor.
;
; CALLS:
;
Player_DrawSprite
;
; XREFS:
;
Player_Equip
;============================================================================
[ee05]Player_SetArmor:
[ee05]8d be 03STA a:SelectedArmor; Set the selected armor.
[ee08]20 45 edJSR Player_DrawSprite; Draw the player sprite.
[ee0b]18CLC; Clear Carry.
[ee0c]60RTS
;============================================================================
; Set the player's current shield.
;
; The shield will be equipped and the player sprite redrawn.
;
; INPUTS:
; A:
; The shield to set:
;
; 0 = Small
; 1 = Large
; 2 = Magic
; 3 = Battle Helmet
;
; OUTPUTS:
;
SelectedShield:
; The new selected armor
;
; CALLS:
;
Player_DrawSprite
;
; XREFS:
;
Player_Equip
;============================================================================
[ee0d]Player_SetShield:
[ee0d]8d bf 03STA a:SelectedShield; Set the selected shield.
[ee10]20 45 edJSR Player_DrawSprite; Draw the player sprite.
[ee13]18CLC; Clear Carry.
[ee14]60RTS
;============================================================================
; Load the start address for the current armor's tiles.
;
; This will locate the address in bank 8 where the tiles
; can be read, in preparation for drawing to the screen.
;
; The initial address in bank 8 ({@address PRG8::8000}) points to an
; index table mapping armor IDs to tile start addresses.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank.
;
; OUTPUTS:
;
Player_SpriteTileReadAddr_U:
;
Player_SpriteTileReadAddr_L:
; Address where tile data can be read.
;
; CALLS:
;
MMC1_UpdateROMBank
;
; XREFS:
;
Player_LoadArmorTilesToPPU
;============================================================================
[ee15]Player_LoadArmorSpriteTilesAddr:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee15]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ee18]48PHA; Push the bank to the stack.
[ee19]a2 08LDX #$08; 8 = Sprites bank.
[ee1b]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of armor types to tile IDs.
;
[ee1e]ad 00 80LDA a:TILES_ARMOR_ADDRS_INDEX_REF; Load the lower byte of the address of the armor index.
[ee21]85 61STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee23]ad 01 80LDA a:TILES_ARMOR_ADDRS_INDEX_REF+1; Load the upper byte.
[ee26]18CLC
[ee27]69 80ADC #$80; Add 0x80 to the upper byte.
[ee29]85 62STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the armor type from the index.
;
[ee2b]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee2d]48PHA; Push it to the stack.
[ee2e]c8INY; Increment the offset.
[ee2f]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee31]18CLC
[ee32]69 80ADC #$80; Add 0x80 to it.
[ee34]85 62STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee36]68PLA; Pop the lower byte.
[ee37]85 61STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee39]68PLA; Pop the saved ROM bank from the stack.
[ee3a]aaTAX; Set it to X
[ee3b]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
[ee3e]60RTS
;============================================================================
; Load the start address for the current weapon's tiles.
;
; This will locate the address in bank 8 where the tiles
; can be read, in preparation for drawing to the screen.
;
; The initial address in bank 8 ({@address PRG8::8002}) points to an
; index table mapping weapon IDs to tile start addresses.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank.
;
; OUTPUTS:
;
Player_SpriteTileReadAddr_U:
;
Player_SpriteTileReadAddr_L:
; Address where tile data can be read.
;
; CALLS:
;
MMC1_UpdateROMBank
;
; XREFS:
;
Player_LoadWeaponTilesToPPU
;============================================================================
[ee3f]Player_LoadWeaponSpriteTileAddrs:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee3f]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ee42]48PHA; Push the bank to the stack.
[ee43]a2 08LDX #$08; 8 = Sprites bank.
[ee45]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of weapon types to tile IDs.
;
[ee48]ad 02 80LDA a:TILES_WEAPON_ADDRS_INDEX_REF; Load the lower byte of the address of the weapon index.
[ee4b]85 61STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee4d]ad 03 80LDA a:TILES_WEAPON_ADDRS_INDEX_REF+1; Load the upper byte.
[ee50]18CLC
[ee51]69 80ADC #$80; Add 0x80 to the upper byte.
[ee53]85 62STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the weapon type from the
; index.
;
[ee55]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee57]48PHA; Push it to the stack.
[ee58]c8INY; Increment the offset.
[ee59]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee5b]18CLC
[ee5c]69 80ADC #$80; Add 0x80 to it.
[ee5e]85 62STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee60]68PLA; Pop the lower byte.
[ee61]85 61STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee63]68PLA; Pop the saved ROM bank from the stack.
[ee64]aaTAX; Set it to X
[ee65]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
[ee68]60RTS
;============================================================================
; Load the start address for the current shield's tiles.
;
; This will locate the address in bank 8 where the tiles
; can be read, in preparation for drawing to the screen.
;
; The initial address in bank 8 ({@address PRG8::800C}) points to an
; index table mapping shield IDs to tile start addresses.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank.
;
; OUTPUTS:
;
Player_SpriteTileReadAddr_U:
;
Player_SpriteTileReadAddr_L:
; Address where tile data can be read.
;
; CALLS:
;
MMC1_UpdateROMBank
;
; XREFS:
;
Player_LoadShieldTilesToPPU
;============================================================================
[ee69]Player_LoadShieldSpriteTileAddrs:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee69]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ee6c]48PHA; Push the bank to the stack.
[ee6d]a2 08LDX #$08; 8 = Sprites bank.
[ee6f]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of shield types to tile IDs.
;
[ee72]ad 0c 80LDA a:TILES_SHIELDS_ADDRS_INDEX_REF; Load the lower byte of the address of the shield index.
[ee75]85 61STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee77]ad 0d 80LDA a:TILES_SHIELDS_ADDRS_INDEX_REF+1; Load the upper byte.
[ee7a]18CLC
[ee7b]69 80ADC #$80; Add 0x80 to the upper byte.
[ee7d]85 62STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the shield type from the index.
;
[ee7f]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee81]48PHA; Push it to the stack.
[ee82]c8INY; Increment the offset.
[ee83]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee85]18CLC
[ee86]69 80ADC #$80; Add 0x80 to it.
[ee88]85 62STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee8a]68PLA; Pop the lower byte.
[ee8b]85 61STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee8d]68PLA; Pop the saved ROM bank from the stack.
[ee8e]aaTAX; Set it to X.
[ee8f]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
[ee92]60RTS
[ee93]Player_LoadShieldTile:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee93]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[ee96]48PHA; Push the bank to the stack.
[ee97]a2 08LDX #$08; 8 = Sprites bank.
[ee99]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the shield tile IDs.
;
[ee9c]ad 0a 80LDA a:TILES_SHIELDS_START_REF; Load the lower byte of the tile IDs start address.
[ee9f]85 02STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eea1]ad 0b 80LDA a:TILES_SHIELDS_START_REF+1; Load the upper byte of the tile IDs start address.
[eea4]85 03STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; Draw the shield tile.
;
[eea6]4c d2 eeJMP Player_LoadSpriteTile; Draw the tile.
[eea9]Player_LoadArmorTile:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[eea9]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[eeac]48PHA; Push the bank to the stack.
[eead]a2 08LDX #$08; 8 = Sprites bank.
[eeaf]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the armor tile IDs.
;
[eeb2]ad 04 80LDA a:TILES_ARMOR_START_REF; Load the lower byte of the tile IDs start address.
[eeb5]85 02STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eeb7]ad 05 80LDA a:TILES_ARMOR_START_REF+1; Load the upper byte of the tile IDs start address.
[eeba]85 03STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; Draw the armor tile.
;
[eebc]4c d2 eeJMP Player_LoadSpriteTile; Draw the tile.
[eebf]Player_LoadWeaponTile:
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[eebf]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[eec2]48PHA; Push the bank to the stack.
[eec3]a2 08LDX #$08; 8 = Sprites bank.
[eec5]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the weapon tile IDs.
;
[eec8]ad 06 80LDA a:TILES_WEAPONS_START_REF; Load the lower byte of the tile IDs start address.
[eecb]85 02STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eecd]ad 07 80LDA a:TILES_WEAPONS_START_REF+1; Load the upper byte of the tile IDs start address.
[eed0]85 03STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; v-- Fall through --v
;
[eed2]Player_LoadSpriteTile:
[eed2]68PLA; Pop the saved ROM bank from the stack.
[eed3]aaTAX; Set it to X.
[eed4]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
;
; And then save it again and switch away again, back to
; bank 8.
;
[eed7]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[eeda]48PHA; Push it to the stack.
[eedb]a2 08LDX #$08; 8 = Sprites bank.
[eedd]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Prepare the read address for the tile.
;
[eee0]a4 a8LDY Player_DrawTileReadOffset; Load the tile read offset.
[eee2]a9 00LDA #$00; A = 0
[eee4]85 05STA Temp_05; Store it temporarily.
;
; Convert to a 2-byte value. The lower nibble will be stored
; as the lower byte in
Temp_04, and the upper nibble
; as the upper byte in
Temp_05. Effectively, splitting
; the nibbles into two bytes.
;
[eee6]b1 61LDA (Player_SpriteTileReadAddr_L),Y; Load the tile ID at the offset.
[eee8]0aASL A; Shift left, upper bit into Carry.
[eee9]26 05ROL Temp_05; Rotate left, carry into bit 0, bit 7 into carry.
[eeeb]0aASL A; Repeat 3 more times, moving lower nibble into A, upper nibble into Temp_05.
[eeec]26 05ROL Temp_05
[eeee]0aASL A
[eeef]26 05ROL Temp_05
[eef1]0aASL A
[eef2]26 05ROL Temp_05; Final storage of the upper byte.
[eef4]85 04STA Temp_04; Final storage of the lower byte.
;
; Restore the saved bank.
;
[eef6]68PLA; Pop the saved ROM bank from the stack.
[eef7]aaTAX; Set it to X.
[eef8]20 1a ccJSR MMC1_UpdateROMBank; And switch back to it.
;
; Convert the normalized 16-bit tile ID to an address.
;
; 0x80 + the upper byte of the tiles address will be
; added to the upper byte from the tile ID.
;
; The lower byte of the tiles address will be added to the
; lower byte from the tile ID.
;
; The result is the tile data address.
;
; Start with the lower byte of the address.
;
[eefb]a5 04LDA Temp_04; Load the lower byte from the normalized tile ID.
[eefd]18CLC
[eefe]65 02ADC Temp_Addr_L; Add to the start of the tile IDs.
[ef00]85 04STA Temp_04; Store as the new lower byte.
;
; Now handle the upper byte of the address.
;
[ef02]a5 05LDA Temp_05; Load the upper byte from the normalized tile ID.
[ef04]65 03ADC Temp_Addr_U; Add the upper byte of the address.
[ef06]18CLC
[ef07]69 80ADC #$80; Add 0x80.
[ef09]85 05STA Temp_05; Store as the new upper byte.
;
; Switch bank to bank 8 (where this probably should have
; stayed all along).
;
[ef0b]ad 00 01LDA a:CurrentROMBank; Load the current bank again.
[ef0e]48PHA; Push it to the stack.
[ef0f]a2 08LDX #$08; 8 = Sprites bank
[ef11]20 1a ccJSR MMC1_UpdateROMBank; Switch to it again.
;
; Set the target PPU address to write the tiles to for the
; PPU buffer draw command.
;
[ef14]a5 92LDA Player_SpriteSegmentPPUAddr_U; Load the upper byte of the PPU address of the sprite data.
[ef16]8d e9 00STA a:PPU_TargetAddr_U; Store as the upper byte to write to for the PPU draw command.
[ef19]a5 91LDA Player_SpriteSegmentPPUAddr_L; Load the lower byte.
[ef1b]8d e8 00STA a:PPU_TargetAddr; And store it.
;
; Queue 16 bytes (1 tile) to draw to the PPU.
;
[ef1e]a9 10LDA #$10; A = 16 bytes.
[ef20]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue it as the draw length.
;
; Prepare a loop for writing bytes for the tile.
;
[ef23]a0 00LDY #$00; Y = 0 (loop counter/read offset).
[ef25]@_copyLoop:
[ef25]b1 04LDA (Temp_04),Y; Load a byte of the tile.
[ef27]9d 00 05STA a:PPUBuffer,X; Write it to the PPU buffer.
[ef2a]e8INX; X++ (buffer position).
[ef2b]c8INY; Y++ (loop counter).
[ef2c]c0 10CPY #$10; Is it < 16?
[ef2e]90 f5BCC @_copyLoop; If so, loop.
;
; 16 bytes are written. Update the PPU buffer write offset
; and restore the bank.
;
[ef30]86 20STX PPUBuffer_WriteOffset; Update the PPU buffer write offset.
[ef32]68PLA; Pop the saved bank from the stack.
[ef33]aaTAX; Set in X.
[ef34]20 1a ccJSR MMC1_UpdateROMBank; And update to the bank.
;
; Increment the PPU address for the next tile.
;
[ef37]a5 91LDA Player_SpriteSegmentPPUAddr_L; Load the lower byte of the PPU address.
[ef39]18CLC
[ef3a]69 10ADC #$10; Add 16 (next tile).
[ef3c]85 91STA Player_SpriteSegmentPPUAddr_L; Store it.
[ef3e]a5 92LDA Player_SpriteSegmentPPUAddr_U; Load the upper byte.
[ef40]69 00ADC #$00; Add Carry (if the lower byte wrapped).
[ef42]85 92STA Player_SpriteSegmentPPUAddr_U; And store it.
[ef44]60RTS
;============================================================================
; DEADCODE: Unset the special event ID.
;
; This is not used in the game.
;============================================================================
[ef45]UNUSED_ClearScreenSpecialEventID:
[ef45]a9 ffLDA #$ff
[ef47]8d 2e 04STA a:CurrentScreen_SpecialEventID
[ef4a]60RTS
;============================================================================
; Run special event handlers for the current screen.
;
; This will run code specific to some screens in the game,
; as indicated by the special event ID stored in a screen's
; metadata.
;
; See
Screen_LoadSpecialEventID.
;
; If a handler is found, it will be run directly following
; this function.
;
; INPUTS:
;
CurrentScreen_SpecialEventID:
; The loaded special event ID for the current
; screen.
;
;
SPECIAL_SCREEN_EVENT_LOOKUP_TABLE:
; Table mapping special event IDs to handler
; functions.
;
; OUTPUTS:
; Stack (A):
; The address of the handler to run, if any.
;
; XREFS:
;
EndGame_MainLoop
;
Game_MainLoop
;============================================================================
[ef4b]GameLoop_RunScreenEventHandlers:
;
; Check if this screen has a special event ID.
;
[ef4b]ad 2e 04LDA a:CurrentScreen_SpecialEventID; Load the screen's special event ID.
[ef4e]c9 ffCMP #$ff; Is it 0xFF?
[ef50]f0 10BEQ @_return; If so, nothing to run. Return.
;
; Check if there's an entry in the table.
;
[ef52]29 7fAND #$7f; Take the lower 7 bits.
[ef54]0aASL A; Convert to a word boundary for the lookup table.
[ef55]c9 06CMP #$06; Is it >= 6 (out of bounds)?
[ef57]b0 09BCS @_return; If so, return.
;
; Load the address of the handler and push as the new address
; to run.
;
[ef59]a8TAY; Y = A
[ef5a]b9 64 efLDA a:SPECIAL_SCREEN_EVENT_LOOKUP_TABLE+1,Y; Load the lower byte of the handler address.
[ef5d]48PHA; Push it to the stack.
[ef5e]b9 63 efLDA a:SPECIAL_SCREEN_EVENT_LOOKUP_TABLE,Y; Load the upper byte.
[ef61]48PHA; Push it.
[ef62]@_return:
[ef62]60RTS
;============================================================================
; Special screen events lookup table.
;
; Each maps a special event ID to a handler that will run
; on each tick while on the screen.
;
; MOD NOTES:
; By moving this table later into the bank and updating
; both the address in
;
GameLoop_RunScreenEventHandlers
; and the table length in that function (6), you could
; likely add new special handlers for screens
;
; XREFS:
;
GameLoop_RunScreenEventHandlers
;============================================================================
[ef63]SPECIAL_SCREEN_EVENT_LOOKUP_TABLE:
[ef63]68 efpointer ScreenEvents_HandlePathToMasconEvent-1; [0]: Handle pushable block on path to Mascon.
[ef65]9d efpointer ScreenEvents_HandleBoss-1; [1]: Handle a standard boss battle.
[ef67]d3 efpointer ScreenEvents_HandleFinalBossKilled-1; [2]: Handle end-game sequence after killing the final boss.
;============================================================================
; Handle checking the path to Mascon at the final fountain.
;
; This is a special screen event handler that checks whether
; the player has completed the quests to reach Mascon.
;
; This is only executed on the first tick for the screen,
; and disabled immediately after. If the fountains were
; completed, this will remove the block obscuring the
; ladder to Mascon and then drop down the ladder.
;
; INPUTS:
;
Quests:
; The quests completed.
;
; OUTPUTS:
;
CurrentScreen_SpecialEventID:
; The unset special event ID, to prevent this from
; running more than once.
;
;
PathToMascon_FountainCoverPos:
; Set to the block position to clear.
;
;
PathToMascon_Opening:
; Set to 1.
;
; CALLS:
;
Game_OpenPathToMascon
;
; XREFS:
;
SPECIAL_SCREEN_EVENT_LOOKUP_TABLE
; [$PRG15_MIRROR::ef63]
;============================================================================
[ef69]ScreenEvents_HandlePathToMasconEvent:
;
; Check if the path to Mascon has been opened.
;
[ef69]ad 2d 04LDA a:Quests; Load the list of completed quests.
[ef6c]29 20AND #$20; Is the Mascon path completed?
[ef6e]f0 12BEQ @_notCompleted; If not, jump to return.
;
; The quest has been completed. Turn off this handler (clear
; the event ID) and manage the transition for the path to
; Mascon.
;
[ef70]a9 ffLDA #$ff
[ef72]8d 2e 04STA a:CurrentScreen_SpecialEventID; Unset the special event ID.
[ef75]a9 01LDA #$01
[ef77]85 d4STA PathToMascon_Opening; Set the path to Mascon as opened.
[ef79]a9 56LDA #$56
[ef7b]85 d5STA PathToMascon_FountainCoverPos; Set the pushable block to X=6, Y=5.
[ef7d]a2 00LDX #$00
[ef7f]4c f5 d6JMP Game_OpenPathToMascon; Drop the ladder down to reach Mascon.
;
; The criteria for the path wasn't set. Clear out this event
; ID so it won't be run until the next time the player is
; on the screen.
;
[ef82]@_notCompleted:
[ef82]a9 ffLDA #$ff
[ef84]8d 2e 04STA a:CurrentScreen_SpecialEventID; Unset the special event ID.
[ef87]60RTS
;============================================================================
; DEADCODE: Check if there are sprites not of a given entity.
;
; This does not appear to be used anywhere. It only
; references itself.
;============================================================================
[ef88]Sprites_HasSpritesNotOfType:
[ef88]85 00STA Temp_00
[ef8a]a2 07LDX #$07
[ef8c]@_loop:
[ef8c]bd cc 02LDA a:CurrentSprites_Entities,X
[ef8f]c9 ffCMP #$ff
[ef91]f0 06BEQ @_prepareNextIter
[ef93]c5 00CMP Temp_00
[ef95]f0 02BEQ @_prepareNextIter
[ef97]38SEC
[ef98]60RTS
[ef99]@_prepareNextIter:
[ef99]caDEX
[ef9a]10 f0BPL @_loop
[ef9c]18CLC
[ef9d]60RTS
;============================================================================
; Handle checking for a boss on the screen.
;
; This is a special event handler used for boss rooms.
; It will default the music to boss battle music, and then
; check for the presence of a boss on every game tick.
;
; Once a boss has been defeated, the music will be set back
; to the default for the area and the handler will be shut
; down.
;
; INPUTS:
;
CurrentScreen_SpecialEventID:
; The special event ID for the screen, used to
; determine which bits of logic should run.
;
;
Areas_DefaultMusic:
; The default music for the area.
;
; OUTPUTS:
;
Music_Current:
; The current music being played.
;
;
CurrentScreen_SpecialEventID:
; The updated special event ID.
;
; CALLS:
;
Sprites_HasBoss
;
; XREFS:
;
SPECIAL_SCREEN_EVENT_LOOKUP_TABLE
; [$PRG15_MIRROR::ef65]
;============================================================================
[ef9e]ScreenEvents_HandleBoss:
;
; Check if bit 7 is set. If not, prepare the boss battle music
; and initial state. This would only be done once on the screen.
;
[ef9e]ad 2e 04LDA a:CurrentScreen_SpecialEventID
[efa1]30 0cBMI @_checkForBoss
;
; This is the first tick on a boss battle screen. Take the
; opportunity to cap any item durations, and default the music
; to the boss battle music.
;
[efa3]09 80ORA #$80; Set bit 7 to only run this conditional on the first game tick.
[efa5]8d 2e 04STA a:CurrentScreen_SpecialEventID; Save it.
[efa8]20 bf efJSR LimitItemDurations; Cap item durations within bounds.
[efab]a9 0aLDA #$0a
[efad]85 faSTA Music_Current; Set the music to the boss battle music.
;
; Check if there's a boss on screen.
;
[efaf]@_checkForBoss:
[efaf]20 f8 efJSR Sprites_HasBoss; Check for a boss.
[efb2]b0 0aBCS @_return; If there is one, we're done. Return.
;
; The boss has been defeated. Restore the game state.
;
[efb4]ad d1 03LDA a:Areas_DefaultMusic; Load the default music for the area.
[efb7]85 faSTA Music_Current; Set it as the current music.
[efb9]a9 ffLDA #$ff
[efbb]8d 2e 04STA a:CurrentScreen_SpecialEventID; Clear the special event ID so this doesn't run again.
[efbe]@_return:
[efbe]60RTS
;============================================================================
; Ensure Ointment and Hour Glass durations don't go below 0.
;
; If either goes below 0, it will be set to 0.
;
; INPUTS:
;
DurationOintment:
; The Ointment duration.
;
;
DurationHourGlass:
; The Hour Glass duration.
;
; OUTPUTS:
;
DurationOintment:
; The updated Ointment duration.
;
;
DurationHourGlass:
; The updated Hour Glass duration.
;
; XREFS:
;
ScreenEvents_HandleBoss
;============================================================================
[efbf]LimitItemDurations:
[efbf]ad 27 04LDA a:DurationOintment; Load the Ointment duration.
[efc2]30 05BMI @_checkHourGlass; If >= 0, move on to the Hour Glass.
[efc4]a9 00LDA #$00
[efc6]8d 27 04STA a:DurationOintment; Set the Ointment duration to 0.
[efc9]@_checkHourGlass:
[efc9]ad 2a 04LDA a:DurationHourGlass; Load the Hour Glass duration.
[efcc]30 05BMI @_return; If >= 0, we're done.
[efce]a9 00LDA #$00
[efd0]8d 2a 04STA a:DurationHourGlass; Set the Hour Glass duration to 0.
[efd3]@_return:
[efd3]60RTS
;============================================================================
; Handle checking for the death of the final boss.
;
; This is a special event handler used for the final boss
; room.
;
; If the boss dies (rather, if all sprite entities are
; cleared from the screen), this will switch back to the
; opening music and area, placing the player back in the
; King's room for the end game outro.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
Music_Current:
; Set to the Eolis music.
;
; CALLS:
;
Sound_PlayEffect
;
Sprites_HasCurrentSprites
;
EndGame_Begin
;
; XREFS:
;
SPECIAL_SCREEN_EVENT_LOOKUP_TABLE
; [$PRG15_MIRROR::ef67]
;============================================================================
[efd4]ScreenEvents_HandleFinalBossKilled:
[efd4]20 0b f0JSR Sprites_HasCurrentSprites; Are there sprites on screen?
[efd7]b0 0cBCS @_return; If so, return.
;
; The final boss is dead. Transition toward the end game
; sequence.
;
; Start by switching the music to the Eolis music.
;
[efd9]a9 07LDA #$07; 0x07 == Eolis music
[efdb]85 faSTA Music_Current; Set as the current music.
;
; Play the "Player got hit" sound.
;
[efdd]a9 04LDA #$04; 0x04 == Player hit sound effect.
[efdf]20 e4 d0JSR Sound_PlayEffect; Play it.
[efe2]4c ac d9JMP EndGame_Begin; Begin the end-game sequence.
[efe5]@_return:
[efe5]60RTS
[efe6]Maybe_IsSpriteEntityNotOnScreen:
[efe6]85 00STA Temp_00; Temp_00 = Sprite type for comparison.
[efe8]a0 07LDY #$07; Y = 7
;
; Check the entity for this index in the current sprites list.
;
[efea]@_spriteLoop:
[efea]b9 cc 02LDA a:CurrentSprites_Entities,Y; Load the entity of the sprite at loop index.
[efed]c5 00CMP Temp_00; Is it the entity the caller is checking for?
[efef]d0 02BNE @_prepareNextLoopIter; If not, prepare for the next loop iteration.
;
; The sprite entity is on the screen. Clear carry as the result.
;
[eff1]18CLC; Return false.
[eff2]60RTS
[eff3]@_prepareNextLoopIter:
[eff3]88DEY; Y--
[eff4]10 f4BPL @_spriteLoop; If Y >= 0, loop.
;
; The sprite entity is not on the screen. Set carry as the
; result.
;
[eff6]38SEC; Return true.
[eff7]60RTS
;============================================================================
; Return whether there's a boss on screen.
;
; This loops through all the sprite entities on the screen,
; looks up the category for each, and checks if that
; category is a boss.
;
; INPUTS:
;
CurrentSprites_Entities:
; The list of current sprite entities on screen.
;
;
SPRITE_CATEGORIES_BY_ENTITY:
; Lookup table of entities to categories.
;
; OUTPUTS:
; C:
; 0 if there is no boss on screen.
; 1 if there is a boss on screen.
;
; XREFS:
;
ScreenEvents_HandleBoss
;============================================================================
[eff8]Sprites_HasBoss:
[eff8]a2 07LDX #$07; X = 7 (loop counter).
;
; Check if this sprite is a boss.
;
[effa]@_loop:
[effa]bc cc 02LDY a:CurrentSprites_Entities,X; Load the sprite entity at that index.
[effd]b9 44 b5LDA a:SPRITE_CATEGORIES_BY_ENTITY,Y; Load the category for the entity.
[f000]c9 07CMP #$07; Is it 7 (boss)?
[f002]d0 02BNE @_notBoss; If not, jump.
;
; There is a boss on screen. Return true.
;
[f004]38SEC; Return true.
[f005]60RTS
[f006]@_notBoss:
[f006]caDEX; X--
[f007]10 f1BPL @_loop; If X >= 0, loop.
;
; There is no boss on screen. Return false.
;
[f009]18CLC; Return false.
[f00a]60RTS
;============================================================================
; Return whether any sprites are currently on screen.
;
; INPUTS:
;
CurrentSprites_Entities:
; The types of sprites currently on the screen.
;
; OUTPUTS:
; C:
; 1 if there are sprites on the screen.
; 0 if there are no sprites.
;
; XREFS:
;
ScreenEvents_HandleFinalBossKilled
;============================================================================
[f00b]Sprites_HasCurrentSprites:
[f00b]a0 07LDY #$07; Y = 7 (loop counter).
[f00d]@_loop:
[f00d]b9 cc 02LDA a:CurrentSprites_Entities,Y; Load the sprite entity at that index.
[f010]c9 ffCMP #$ff; Is it unset?
[f012]f0 02BEQ @_prepareNextLoopIter; If yes, jump to prepare for the next loop iteration.
;
; A sprite entity was found. Return true.
;
[f014]38SEC; Return true.
[f015]60RTS
[f016]@_prepareNextLoopIter:
[f016]88DEY; Y--
[f017]10 f4BPL @_loop; If Y >= 0, loop.
;
; A sprite entity was not found. Return false.
;
[f019]18CLC; Return false.
[f01a]60RTS
[f01b]Sprite_DrawPortraitPartAppearance:
[f01b]a8TAY; Y = A (appearance offset).
;
; Save the current ROM bank and switch to bank 7.
;
[f01c]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[f01f]48PHA; Push it to the stack.
[f020]a2 07LDX #$07; Bank 7 = Sprites
[f022]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
[f025]98TYA; A = Y (appearance offset).
[f026]0aASL A; Convert to a word boundary for an index into the tileinfo table.
[f027]a8TAY; Y = A (index).
[f028]08PHP; Push flags to the stack.
[f029]ad 0a 80LDA a:PORTRAIT_APPEARANCES_OFFSET; Load the lower byte of the portraits tileinfo address.
[f02c]85 02STA Temp_Addr_L; Store as the lower byte in the read address.
[f02e]ad 0b 80LDA a:PORTRAIT_APPEARANCES_OFFSET+1; Load the upper byte of the portraits tileinfo address.
[f031]28PLP; Pop flags from the stack.
[f032]69 80ADC #$80; Add 0x80 to the upper byte.
[f034]85 03STA Temp_Addr_U; And store it.
[f036]4c 72 f0JMP Sprite_Draw; Draw the portrait sprite.
[f039]Sprite_SetPlayerAppearanceAddr:
[f039]a8TAY
[f03a]ad 00 01LDA a:CurrentROMBank
[f03d]48PHA
[f03e]a2 07LDX #$07
[f040]20 1a ccJSR MMC1_UpdateROMBank
[f043]98TYA
;
; Load the address to load sprite images from.
;
; With the addresses stored in
PLAYER_APPEARANCES_OFFSET
; and
;
PLAYER_APPEARANCES_OFFSET+1, this should put us at a
; starting
; point of {@address PRG7::A9F1}, or {@address PRG7::AAF1} if offset
; overflowed.
;
[f044]0aASL A
[f045]a8TAY
[f046]08PHP
[f047]ad 08 80LDA a:PLAYER_APPEARANCES_OFFSET
[f04a]85 02STA Temp_Addr_L
[f04c]ad 09 80LDA a:PLAYER_APPEARANCES_OFFSET+1
[f04f]28PLP
[f050]69 80ADC #$80
[f052]85 03STA Temp_Addr_U
[f054]4c 72 f0JMP Sprite_Draw
[f057]Sprite_SetAppearanceAddrFromOffset:
;
; Switch to ROM bank 7 (PRG).
;
[f057]a8TAY; Y = A (offset)
[f058]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[f05b]48PHA; Push it to the stack.
[f05c]a2 07LDX #$07
[f05e]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 7 (PRG).
;
; Load the address to load sprite images from.
;
; With the addresses stored in
SPRITE_APPEARANCES_OFFSET
; and
;
SPRITE_APPEARANCES_OFFSET+1, this should put us at a
; starting
; point of {@address PRG7::9036}, or {@address PRG7::9136} if offset
; overflowed.
;
[f061]98TYA; A = Y (restore offset)
[f062]0aASL A; Multiply offset by 2, to get word boundaries in the lookup table.
[f063]a8TAY; Y = A (updated offset)
[f064]08PHP; Push flags and stack pointer.
[f065]ad 06 80LDA a:SPRITE_APPEARANCES_OFFSET; Load the lower byte of the address to read from.
[f068]85 02STA Temp_Addr_L; Store it for processing.
[f06a]ad 07 80LDA a:SPRITE_APPEARANCES_OFFSET+1; Load the upper byte.
[f06d]28PLP; Pop the flags and stack pointer.
[f06e]69 80ADC #$80; Add 0x80 + carry to the upper byte.
[f070]85 03STA Temp_Addr_U; Store it.
;
; v-- Fall through --v
;
;============================================================================
; Begin drawing a sprite.
;
; This will load the tiles information for a sprite,
; determining the main bounding box, the extents, the
; pivot point when flipping, and generating a suitable
; X and Y position to begin drawing tiles.
;
; Tile information begins with a header in the following
; form:
;
; Byte 0: Upper nibble = Row count - 1
; Lower nibble = Column count -1
; Byte 1: Offset X in pixels (if positive, extends the
; sprite right; if negative, extends left)
; Byte 2: Offset Y in pixels (if positive, extends the
; sprite downward; if negative, extends upward)
; Byte 3: Block-aligned X pixel pivot position used to
; render the main body of the sprite in a stable
; position when flipping horizontally (only used
; in
Sprite_Draw_FlippedHoriz.
;
; This is followed by bytes pointing to tile indexes for the
; list of available tiles for the sprite (with 0xFF meaning
; "empty tile").
Sprite_Draw_Standard and
;
Sprite_Draw_FlippedHoriz handles this.
;
; INPUTS:
;
;
; XREFS:
;
Sprite_DrawPortraitPartAppearance
;
Sprite_SetPlayerAppearanceAddr
;============================================================================
[f072]Sprite_Draw:
;
; Load the offset within our starting address stored in
;
Temp_Addr_L.
;
[f072]b1 02LDA (Temp_Addr_L),Y; Load the offset for the lower byte.
[f074]85 3aSTA Sprite_TileInfo_ReadAddr_L; Store it.
[f076]c8INY; Y++ (offset)
[f077]b1 02LDA (Temp_Addr_L),Y; Load the upper byte.
[f079]69 80ADC #$80; Add 0x80 + carry to it.
[f07b]85 3bSTA Sprite_TileInfo_ReadAddr_U; Store it.
[f07d]a0 00LDY #$00; Y = 0 (read offset)
[f07f]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the value at that address.
[f081]29 0fAND #$0f; Discard the upper nibble.
[f083]85 40STA Temp_Sprites_ColsRemaining; Store it.
[f085]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load it again.
[f087]4aLSR A; Move upper nibble to lower.
[f088]4aLSR A
[f089]4aLSR A
[f08a]4aLSR A
[f08b]85 41STA Temp_Sprites_RowsRemaining; Store it.
[f08d]a9 00LDA #$00; A = 0
[f08f]85 3eSTA Sprite_TileInfo_OffsetX; Set to 0.
[f091]85 3fSTA Sprite_TileInfo_OffsetY; Set to 0.
;
; Load the X offset extending from the main bounding box
; to fill with tiles.
;
; This may be negative (filling tiles left of the main bounding
; box of the sprite) or positive (adding padding left of the
; sprite).
;
[f093]c8INY; Y++ (read offset)
[f094]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the X offset byte.
[f096]10 02BPL @_calcX; If > 0, jump.
[f098]c6 3eDEC Sprite_TileInfo_OffsetX; Set to 0xFF. This will set C=1 for the add below.
[f09a]@_calcX:
[f09a]18CLC
[f09b]65 27ADC Arg_DrawSprite_PosX; Add Arg_DrawSprite_PosX to the X offset (which may be negative, extending left).
[f09d]85 3cSTA Temp_Sprites_NormXPos; Store it.
[f09f]a9 00LDA #$00; A = 0
[f0a1]65 3eADC Sprite_TileInfo_OffsetX; Add our stored value.
[f0a3]85 3eSTA Sprite_TileInfo_OffsetX; Store it back out.
;
; Update the drawn X position to factor in any screen scrolling
; offsets.
;
[f0a5]a5 3cLDA Temp_Sprites_NormXPos; Load the new calculated X position.
[f0a7]38SEC
[f0a8]e5 0cSBC PPU_ScrollX; Subtract any scrolling offset that may be set.
[f0aa]85 3cSTA Temp_Sprites_NormXPos; Store it back.
;
; Load the Y offset extending from the main bounding box
; to fill with tiles.
;
; This may be negative (filling tiles above the main bounding
; box of the sprite) or positive (adding padding above the
; sprite).
;
[f0ac]c8INY; Y++ (read offset)
[f0ad]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the Y offset byte.
[f0af]10 02BPL @_calcY; If > 0, jump.
[f0b1]c6 3fDEC Sprite_TileInfo_OffsetY; Set to 0xFF. This will set C=1 for the add below.
[f0b3]@_calcY:
[f0b3]18CLC
[f0b4]65 28ADC Arg_DrawSprite_PosY; Add Arg_DrawSprite_PosY to the Y offset (which may be negative, extending up).
[f0b6]85 3dSTA Temp_Sprites_NormYPos; Store it.
;
; Add Carry to the offset, if it's set.
;
[f0b8]a9 00LDA #$00
[f0ba]65 3fADC Sprite_TileInfo_OffsetY; A = Y offset + carry (from above).
[f0bc]85 3fSTA Sprite_TileInfo_OffsetY; Store it.
;
; And then always offset by 32 pixels (the HUD height).
;
[f0be]a5 3dLDA Temp_Sprites_NormYPos; Load the normalized Y position.
[f0c0]18CLC
[f0c1]69 20ADC #$20; Add 32 (HUD height in pixels).
[f0c3]85 3dSTA Temp_Sprites_NormYPos; Store it.
;
; Add Carry again to the Y offset, if set. This happens if
; the HUD offset causes the value to overflow.
;
[f0c5]a5 3fLDA Sprite_TileInfo_OffsetY; Load the Y offset.
[f0c7]69 00ADC #$00; Add Carry, if set.
[f0c9]85 3fSTA Sprite_TileInfo_OffsetY; Store it.
;
; Load the block-aligned X pixel pivot point into the sprite
; used to anchor the sprite when flipping. This is generally
; the left-most tile of the main body of the sprite. When
; the sprite turns around, it pivots at this point.
;
; Only
Sprite_Draw_FlippedHoriz uses this.
;
[f0cb]c8INY; Y++ (read offset).
[f0cc]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the pivot point in pixels.
[f0ce]85 02STA Temp_Addr_L; Store it for use if drawing flipped.
;
; Advance our start read address for this sprite to skip the 4
; bytes we just read.
;
[f0d0]a5 3aLDA Sprite_TileInfo_ReadAddr_L; Load the lower byte of the sprite address.
[f0d2]18CLC
[f0d3]69 04ADC #$04; Advance by 4 (skipping loaded state).
[f0d5]85 3aSTA Sprite_TileInfo_ReadAddr_L; Set as the new lower byte of the address.
[f0d7]a5 3bLDA Sprite_TileInfo_ReadAddr_U; Load the upper byte.
[f0d9]69 00ADC #$00; Add the carry, if lower overflowed.
[f0db]85 3bSTA Sprite_TileInfo_ReadAddr_U; Set it.
;
; We'll continue on based on whether the sprite is flipped.
;
[f0dd]a5 29LDA CurrentSprite_FlipMask; Load the flip mask.
[f0df]29 c0AND #$c0; Keep only the horiz/vert flip bits.
[f0e1]85 29STA CurrentSprite_FlipMask; Store as a new flip mask.
[f0e3]a5 29LDA CurrentSprite_FlipMask; Load the resulting flip mask.
[f0e5]29 40AND #$40; Check if it's flipped horizontally.
[f0e7]f0 03BEQ Sprite_Draw_Standard; If not, draw in standard orientation.
[f0e9]4c 81 f1JMP Sprite_Draw_FlippedHoriz; Else, draw horizontally flipped.
;============================================================================
; Draw all tiles for a sprite in the standard orientation.
;
; This will loop through all rows of the sprite, then all
; columns in a row, drawing each tile of the sprite.
;
; Tiles are read from the sprite information in bank 7,
; which is in the following form:
;
; Byte 0: Upper = Row count - 1
; Lower = Column count -1
; Byte 1: Offset X (between first tile and "front" tile
; used for alignment)
; Byte 2: Offset Y
; Byte 3: Block-aligned pixel position of the "front"
; tile for alignment when flipping horizontally
;
; Followed by bytes pointing to tile indexes for the list
; of available tiles for the sprite (with 0xFF meaning
; "empty tile").
;
; INPUTS:
;
;
; XREFS:
;
Sprite_Draw
;============================================================================
[f0ec]Sprite_Draw_Standard:
;
; The sprite is not flipped.
;
[f0ec]a9 00LDA #$00; A = 0.
[f0ee]85 43STA Temp_Sprites_TileYOffset; Store it as the starting tiles Y offset.
[f0f0]a8TAY; Y = A (0, loop counter).
[f0f1]@_drawRowLoop:
[f0f1]a5 40LDA Temp_Sprites_ColsRemaining; Load the number of tile columns remaining.
[f0f3]48PHA; Push it to the stack.
[f0f4]a9 00LDA #$00; A = 0.
[f0f6]85 42STA Temp_Sprites_TileXOffset; Store as the start tiles X offset.
;
; Check if the current sprite data value is not 0xFF (unset).
;
[f0f8]@_drawColLoop:
[f0f8]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the value at the sprite data read address.
[f0fa]c9 ffCMP #$ff; Is it 0xFF?
[f0fc]f0 64BEQ @_prepareNextCol; If so, skip this row.
;
; It's not unset.
;
; Begin checking if we can add to the PPU.
;
; First we'll check if we can add sprites, or if the slots
; are full.
;
[f0fe]a5 39LDA Sprites_SlotsFull; Can we add sprites this frame?
[f100]d0 5fBNE @_endColLoop; If not, jump, try the next row.
[f102]a6 42LDX Temp_Sprites_TileXOffset; X = tile X offset.
[f104]a5 3cLDA Temp_Sprites_NormXPos; A = normalized X position.
[f106]7d 3d f2ADC a:SPRITE_TILE_BLOCK_POSITIONS,X; A += the offset from the lookup table at X.
[f109]85 00STA Temp_00; Store it temporarily.
;
; Check if this tile would fit on screen in the X direction.
;
[f10b]a5 3eLDA Sprite_TileInfo_OffsetX; Load the default X position.
[f10d]69 00ADC #$00; Add carry (from overflow), if set.
[f10f]d0 50BNE @_endColLoop; If it doesn't fit on screen, jump.
[f111]a6 43LDX Temp_Sprites_TileYOffset; X = tile Y offset.
[f113]a5 3dLDA Temp_Sprites_NormYPos; A = normalized Y position.
[f115]7d 3d f2ADC a:SPRITE_TILE_BLOCK_POSITIONS,X; A += the offset from the lookup table at X.
[f118]85 01STA Temp_01; Store it temporarily.
;
; Check if this tile would fit on screen in the Y direction.
;
[f11a]a5 3fLDA Sprite_TileInfo_OffsetY; Load the default Y position.
[f11c]69 00ADC #$00; Add carry (from overflow), if set.
[f11e]d0 41BNE @_endColLoop; If it doesn't fit on screen, jump.
;
; The sprite fits on screen.
;
; We'll begin writing the sprite to DMA.
;
[f120]98TYA; A = Y (loop counter)
[f121]48PHA; Push our loop counter to the stack.
[f122]a5 25LDA Sprites_SpriteSlot; Load the slot for this sprite.
[f124]0aASL A; Multiply by 4 (for Y, tile ID, attributes, X bytes).
[f125]0aASL A
[f126]45 1cEOR Sprites_StartSlotRange
;
; Write the sprite Y position to DMA.
;
[f128]aaTAX; X = A (read offset)
[f129]a5 01LDA Temp_01; Load the Y position.
[f12b]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Write the Y position to DMA.
;
; Write the sprite tile ID to DMA.
;
; The byte value is an index into the list of PPU tile IDs.
;
[f12e]e8INX; X++ (read offset)
[f12f]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the tile index.
[f131]18CLC
[f132]65 33ADC Sprites_PPUOffset; Add the PPU starting offset for the list of tiles.
[f134]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Write the tile ID to DMA.
;
; Write the sprite attributes.
;
; This will primarily be the palette, but it may also contain
; a flip mask. The flip mask will be normalized based on
; whether the full sprite is flipped.
;
[f137]e8INX; X++ (read offset)
[f138]c8INY; Y++ (loop counter)
[f139]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y; Load the attribute.
[f13b]45 29EOR CurrentSprite_FlipMask; XOR with the flip mask.
[f13d]85 01STA Temp_01; And store it.
;
; Calculate visibility for this tile.
;
; This will check if the tile is even or odd, using that
; as an index into
DRAW_SPRITE_VISIBILITY_MASK.
;
; If the tile is not visible, it will be set to background
; priority.
;
[f13f]a5 42LDA Temp_Sprites_TileXOffset; Load the tile X offset.
[f141]29 01AND #$01; Convert to an even/odd value for the visibility lookup table.
[f143]a8TAY; Y = result.
[f144]a5 26LDA MovingSpriteVisibility; Load the moving visibility of the sprite.
[f146]39 24 f2AND a:DRAW_SPRITE_VISIBILITY_MASK,Y; AND with the lookup table, determining priority.
[f149]f0 06BEQ @_storeAttribute; If non-0, keep foreground priority.
Else...
;
; The tile is obscured. Set the sprite to be a
; background sprite.
;
[f14b]a5 01LDA Temp_01; Load the attribute calculated above.
[f14d]09 20ORA #$20; Set background priority.
[f14f]85 01STA Temp_01; And store it.
;
; Write the tile attribute to DMA.
;
[f151]@_storeAttribute:
[f151]a5 01LDA Temp_01; Load the calculated attribute.
[f153]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Store the tile attributes to DMA.
;
; Write the sprite X position to DMA.
;
[f156]e8INX; X++ (read offset)
[f157]a5 00LDA Temp_00; Load the X position.
[f159]9d 00 07STA a:SPRITE_0_RANGE_1_START,X; Write the X coordinate to DMA.
;
; Prepare for the next column.
;
[f15c]20 28 f2JSR Sprites_IncNextSpriteSlot; Finish up with this sprite's state.
[f15f]68PLA; Pull A (loop counter) from stack.
[f160]a8TAY; Y = A (loop counter)
[f161]@_endColLoop:
[f161]c8INY; Y++ (loop counter)
[f162]@_prepareNextCol:
[f162]c8INY; Y++ (loop counter)
[f163]e6 42INC Temp_Sprites_TileXOffset; Increment the tile X offset.
[f165]c6 40DEC Temp_Sprites_ColsRemaining; Decrement the number of columns remaining.
[f167]10 8fBPL @_drawColLoop; If there are columns remaining, loop.
;
; We're on the next row. Reset the column count and advance
; the row counter.
;
[f169]68PLA; Pop the original total columns remaining.
[f16a]85 40STA Temp_Sprites_ColsRemaining; Store as the new columns count for the next row.
[f16c]e6 43INC Temp_Sprites_TileYOffset; Increment the tile Y offset.
[f16e]c6 41DEC Temp_Sprites_RowsRemaining; Decrement the number of rows remaining.
[f170]30 03BMI Sprite_Draw_Finish; If there are no more rows, finish the drawing.
[f172]4c f1 f0JMP @_drawRowLoop; Else, loop to draw the next row.
;============================================================================
; TODO: Document Sprite_Draw_Finish
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Sprite_Draw_FlippedHoriz
;
Sprite_Draw_Standard
;============================================================================
[f175]Sprite_Draw_Finish:
[f175]68PLA
[f176]aaTAX
[f177]20 1a ccJSR MMC1_UpdateROMBank
[f17a]a9 00LDA #$00
[f17c]85 33STA Sprites_PPUOffset
[f17e]85 26STA MovingSpriteVisibility
[f180]60RTS
;============================================================================
; TODO: Document Sprite_Draw_FlippedHoriz
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Sprite_Draw
;============================================================================
[f181]Sprite_Draw_FlippedHoriz:
;
; The sprite is flipped.
;
[f181]a9 00LDA #$00
[f183]85 05STA Temp_05; Set Temp_05 = 0.
[f185]a4 40LDY Temp_Sprites_ColsRemaining; Load the number of columns remaining for a row.
[f187]c8INY; Y++ (convert from 0-based count)
[f188]98TYA; A = result
[f189]0aASL A; Multiply the columns by 8.
[f18a]0aASL A
[f18b]0aASL A
[f18c]85 04STA Temp_04; Store for temporary use.
[f18e]a5 02LDA Temp_Addr_L; A = 4th byte from the sprite information.
[f190]0aASL A; A = A * 2
[f191]38SEC
[f192]e5 04SBC Temp_04; A = A - normalized column value.
[f194]10 02BPL @LAB_PRG15_MIRROR__f198; If negative, jump.
[f196]c6 05DEC Temp_05; Else, it's positive. Decrement Temp_05.
[f198]@LAB_PRG15_MIRROR__f198:
[f198]18CLC
[f199]65 3cADC Temp_Sprites_NormXPos; A = A + normalized X position
[f19b]85 3cSTA Temp_Sprites_NormXPos; Store as the new position.
[f19d]a5 3eLDA Sprite_TileInfo_OffsetX
[f19f]65 05ADC Temp_05
[f1a1]85 3eSTA Sprite_TileInfo_OffsetX
[f1a3]a9 00LDA #$00
[f1a5]85 43STA Temp_Sprites_TileYOffset
[f1a7]a8TAY
[f1a8]@LAB_PRG15_MIRROR__f1a8:
[f1a8]a5 40LDA Temp_Sprites_ColsRemaining
[f1aa]85 42STA Temp_Sprites_TileXOffset
[f1ac]@LAB_PRG15_MIRROR__f1ac:
[f1ac]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y
[f1ae]c9 ffCMP #$ff
[f1b0]f0 64BEQ @LAB_PRG15_MIRROR__f216
[f1b2]a5 39LDA Sprites_SlotsFull
[f1b4]d0 5fBNE @LAB_PRG15_MIRROR__f215
[f1b6]a6 42LDX Temp_Sprites_TileXOffset
[f1b8]a5 3cLDA Temp_Sprites_NormXPos
[f1ba]7d 3d f2ADC a:SPRITE_TILE_BLOCK_POSITIONS,X
[f1bd]85 00STA Temp_00
[f1bf]a5 3eLDA Sprite_TileInfo_OffsetX
[f1c1]69 00ADC #$00
[f1c3]d0 50BNE @LAB_PRG15_MIRROR__f215
[f1c5]a6 43LDX Temp_Sprites_TileYOffset
[f1c7]a5 3dLDA Temp_Sprites_NormYPos
[f1c9]7d 3d f2ADC a:SPRITE_TILE_BLOCK_POSITIONS,X
[f1cc]85 01STA Temp_01
[f1ce]a5 3fLDA Sprite_TileInfo_OffsetY
[f1d0]69 00ADC #$00
[f1d2]d0 41BNE @LAB_PRG15_MIRROR__f215
[f1d4]98TYA
[f1d5]48PHA
[f1d6]a5 25LDA Sprites_SpriteSlot
[f1d8]0aASL A
[f1d9]0aASL A
[f1da]45 1cEOR Sprites_StartSlotRange
[f1dc]aaTAX
[f1dd]a5 01LDA Temp_01
[f1df]9d 00 07STA a:SPRITE_0_RANGE_1_START,X
[f1e2]e8INX
[f1e3]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y
[f1e5]18CLC
[f1e6]65 33ADC Sprites_PPUOffset
[f1e8]9d 00 07STA a:SPRITE_0_RANGE_1_START,X
[f1eb]e8INX
[f1ec]c8INY
[f1ed]b1 3aLDA (Sprite_TileInfo_ReadAddr_L),Y
[f1ef]45 29EOR CurrentSprite_FlipMask
[f1f1]85 01STA Temp_01
[f1f3]a5 42LDA Temp_Sprites_TileXOffset
[f1f5]29 01AND #$01
[f1f7]a8TAY
[f1f8]a5 26LDA MovingSpriteVisibility
[f1fa]39 26 f2AND a:BYTE_ARRAY_PRG15_MIRROR__f226,Y
[f1fd]f0 06BEQ @LAB_PRG15_MIRROR__f205
[f1ff]a5 01LDA Temp_01
[f201]09 20ORA #$20
[f203]85 01STA Temp_01
[f205]@LAB_PRG15_MIRROR__f205:
[f205]a5 01LDA Temp_01
[f207]9d 00 07STA a:SPRITE_0_RANGE_1_START,X
[f20a]e8INX
[f20b]a5 00LDA Temp_00
[f20d]9d 00 07STA a:SPRITE_0_RANGE_1_START,X
[f210]20 28 f2JSR Sprites_IncNextSpriteSlot
[f213]68PLA
[f214]a8TAY
[f215]@LAB_PRG15_MIRROR__f215:
[f215]c8INY
[f216]@LAB_PRG15_MIRROR__f216:
[f216]c8INY
[f217]c6 42DEC Temp_Sprites_TileXOffset
[f219]10 91BPL @LAB_PRG15_MIRROR__f1ac
[f21b]e6 43INC Temp_Sprites_TileYOffset
[f21d]c6 41DEC Temp_Sprites_RowsRemaining
[f21f]10 87BPL @LAB_PRG15_MIRROR__f1a8
[f221]4c 75 f1JMP Sprite_Draw_Finish
[f224]DRAW_SPRITE_VISIBILITY_MASK:
[f224]01.byte $01; [0]: Trailing visibility
[f225]02.byte $02; [1]: Leading visibility
[f226]BYTE_ARRAY_PRG15_MIRROR__f226:
[f226]02.byte $02; [0]:
[f227]01.byte $01; [1]:
;============================================================================
; Increment the sprite slot, and mark when full.
;
; This alternates the sprite slots between 0-31 and 32-63.
; When populating 0-31, it will track whether all slots are
; full, and if so,
Sprites_SlotsFull will be set
; to prevent further sprites from being added.
;
; INPUTS:
;
Sprites_SpriteSlot:
; The current slot for the next sprite.
;
; OUTPUTS:
;
Sprites_SpriteSlot:
; The next slot for the next sprite, if updated.
;
;
Sprites_SlotsFull:
; Set to 1 (rather, incremented, but practically)
; if the slots are full.
;
; XREFS:
;
Sprite_Draw_FlippedHoriz
;
Sprite_Draw_Standard
;============================================================================
[f228]Sprites_IncNextSpriteSlot:
;
; Alternate between sprites 0-31 and sprites 32-63.
;
; The bit being flipped is bit 5, value 32.
;
[f228]a5 25LDA Sprites_SpriteSlot; Load the sprite slot ID.
[f22a]49 20EOR #$20; Cap the value to < 32.
[f22c]85 25STA Sprites_SpriteSlot; Store the result.
;
; If we're populating sprites 0-31, we'll check if we've
; hit the cap. If we're populating 32-63, we're done.
;
[f22e]29 20AND #$20; Check if we're in 32-63.
[f230]d0 0aBNE @_return; If so, return.
;
; It's in sprite range 0-31.
;
[f232]e6 25INC Sprites_SpriteSlot; Increase the sprite slot.
[f234]a5 25LDA Sprites_SpriteSlot; Load the new slot.
[f236]c9 20CMP #$20; Are there slots left?
[f238]90 02BCC @_return; If so, return.
;
; The slots are full. Mark it.
;
[f23a]e6 39INC Sprites_SlotsFull; Mark slots as full.
[f23c]@_return:
[f23c]60RTS
[f23d]SPRITE_TILE_BLOCK_POSITIONS:
[f23d]00.byte $00; [0]:
[f23e]08.byte $08; [1]:
[f23f]10.byte $10; [2]:
[f240]18.byte $18; [3]:
[f241]20.byte $20; [4]:
[f242]28.byte $28; [5]:
[f243]30.byte $30; [6]:
[f244]38.byte $38; [7]:
[f245]40.byte $40; [8]:
[f246]48.byte $48; [9]:
[f247]50.byte $50; [10]:
[f248]58.byte $58; [11]:
[f249]60.byte $60; [12]:
[f24a]68.byte $68; [13]:
[f24b]70.byte $70; [14]:
[f24c]78.byte $78; [15]:
[f24d]IScripts_LoadPortraitTiles:
;
; Check if a portrait ID is set.
;
[f24d]8d c7 03STA a:IScript_PortraitID; Set the current portrait ID.
[f250]aaTAX; X = result.
[f251]30 2aBMI @_return; If unset, return.
;
; The portrait ID is set. Load the address and wait for an
; OAM reset.
;
[f253]20 e3 f2JSR IScripts_LoadPortraitTilesAddress; Load the tiles address for this portrait ID.
[f256]20 9a cbJSR Game_WaitForOAMReset; Wait for OAM reset.
;
; Save the sprite palette and switch to palette 2 for the
; portrait.
;
[f259]ad d4 03LDA a:Palette_SpritePaletteIndex; Load the current sprite palette index
[f25c]8d d3 03STA a:Palette_SavedIndex; And save it for later.
[f25f]a9 02LDA #$02
[f261]20 62 d0JSR Screen_LoadSpritePalette; Load palette 2.
[f264]20 90 d0JSR PPUBuffer_WritePalette; Write the palette to the PPU.
;
; Set the target PPU address to draw the portrait to.
; This will be $0900.
;
[f267]a9 09LDA #$09; Upper byte = 0x09.
[f269]85 e9STA PPU_TargetAddr_U; Set it.
[f26b]a9 00LDA #$00; Lower byte = 0x00.
[f26d]85 e8STA PPU_TargetAddr; Set it.
;
; Begin drawing the portrait. This will loop up to
; 256 times, drawing 16 tiles each.
;
[f26f]a0 00LDY #$00; Y = 0 (loop counter).
[f271]@_drawLoop:
[f271]98TYA; A = Y (loop counter).
[f272]48PHA; Push it to the stack.
[f273]20 16 f3JSR IScripts_DrawPortraitTileToPPU; Draw a 16-byte tile to the PPU.
[f276]b0 06BCS @_finishDrawing
[f278]68PLA; Pop the loop counter from the stack.
[f279]a8TAY; Set back in Y.
[f27a]c8INY; Y++
[f27b]d0 f4BNE @_drawLoop; If not wrapped to 0, loop.
[f27d]@_return:
[f27d]60RTS
;
; The portrait is finished.
;
[f27e]@_finishDrawing:
[f27e]68PLA; Pop the loop counter from the stack.
[f27f]a8TAY; Set back in Y.
[f280]60RTS
[f281]IScripts_ClearPortraitImage:
[f281]a9 ffLDA #$ff
[f283]8d c7 03STA a:IScript_PortraitID; Clear the portrait ID.
[f286]20 9a cbJSR Game_WaitForOAMReset; Wait for OAM reset.
[f289]ad d3 03LDA a:Palette_SavedIndex; Load the saved sprite palette ID.
[f28c]20 62 d0JSR Screen_LoadSpritePalette; Load that palette's data.
[f28f]20 90 d0JSR PPUBuffer_WritePalette; Write to the PPU buffer.
[f292]20 59 f8JSR MMC1_LoadBankAndJump; Load sprite imagesin bank 14.
[f295]0e.byte BANK_14_LOGIC; Bank = 14
Address = GameLoop_LoadSpriteImages
[f296]8c c2pointer GameLoop_LoadSpriteImages-1; GameLoop_LoadSpriteImages
[f298]@_afterFarJump:
[f298]60RTS
[f299]IScripts_DrawPortraitAnimationFrame_Unset:
[f299]68PLA; Clean up the stack, popping the pushed frame.
[f29a]60RTS
[f29b]IScripts_DrawPortraitAnimationFrame:
[f29b]48PHA; Push the frame to the stack.
[f29c]ad c7 03LDA a:IScript_PortraitID; Load the current portrait ID.
[f29f]30 f8BMI IScripts_DrawPortraitAnimationFrame_Unset; If 0xFF, jump to finish.
;
; There's an active portrait. Begin preparing by clearing
; the flip mask state.
;
[f2a1]a9 00LDA #$00
[f2a3]85 29STA CurrentSprite_FlipMask; Set the flip mask to 0 (not flipped).
;
; Update the general portrait image.
;
[f2a5]a9 90LDA #$90
[f2a7]85 33STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2a9]20 d5 f2JSR IScripts_GetPortraitOffset; Get the portrait offset into the tileinfo for the ID.
[f2ac]20 1b f0JSR Sprite_DrawPortraitPartAppearance; Draw as the portrait.
;
; Update the eye animation.
;
; This will switch to new tiles every frame.
;
[f2af]a9 90LDA #$90
[f2b1]85 33STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2b3]68PLA; Pull the frame from the stack.
[f2b4]48PHA; Push it again. We still have it in A.
[f2b5]29 01AND #$01; Generate an even/odd index from it.
[f2b7]aaTAX; X = result.
[f2b8]20 d5 f2JSR IScripts_GetPortraitOffset; Get the offset into the table for the portrait ID.
[f2bb]18CLC
[f2bc]7d df f2ADC a:PORTRAIT_EYE_APPEARANCE_OFFSETS,X; Add the eyes animation offset for this even/odd frame.
[f2bf]20 1b f0JSR Sprite_DrawPortraitPartAppearance; Draw as the eyes.
;
; Update the mouth animation.
;
; This will switch to new tiles every other frame.
;
[f2c2]a9 90LDA #$90
[f2c4]85 33STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2c6]68PLA; Pull the frame from the stack.
[f2c7]4aLSR A; Divide the frame by 2 (reducing the animation rate to switch every other frame).
[f2c8]29 01AND #$01; Convert that to an even/odd value.
[f2ca]aaTAX; X = result.
[f2cb]20 d5 f2JSR IScripts_GetPortraitOffset; Get the offset into the table for the portrait ID.
[f2ce]18CLC
[f2cf]7d e1 f2ADC a:PORTRAIT_MOUTH_APPEARANCE_OFFSETS,X; Add the mouth animation offset for this even/odd frame.
[f2d2]4c 1b f0JMP Sprite_DrawPortraitPartAppearance; Draw as the mouth.
;============================================================================
; Return the offset into the portraits table for the ID.
;
; This takes a portrait ID and returns an offset into
;
TILEINFO_PORTRAITS_ADDRS.
;
; INPUTS:
;
IScript_PortraitID:
; The current portrait ID.
;
; OUTPUTS:
; A:
; The offset into the table.
;
; XREFS:
;
IScripts_DrawPortraitAnimationFrame
;============================================================================
[f2d5]IScripts_GetPortraitOffset:
[f2d5]ad c7 03LDA a:IScript_PortraitID; Load the current portrait ID.
[f2d8]0aASL A; Multiply by 4.
[f2d9]0aASL A
[f2da]18CLC
[f2db]6d c7 03ADC a:IScript_PortraitID; And add the ID again (effectively multiplying by a total of 5).
[f2de]60RTS; Return it as A.
;============================================================================
; Offsets for the portrait eye blinking animation frames.
;
; XREFS:
;
IScripts_DrawPortraitAnimationFrame
;============================================================================
[f2df]PORTRAIT_EYE_APPEARANCE_OFFSETS:
[f2df]01.byte $01; [0]:
[f2e0]02.byte $02; [1]:
;============================================================================
; Offsets for the portrait mouth movement animation frames.
;
; XREFS:
;
IScripts_DrawPortraitAnimationFrame
;============================================================================
[f2e1]PORTRAIT_MOUTH_APPEARANCE_OFFSETS:
[f2e1]03.byte $03; [0]:
[f2e2]04.byte $04; [1]:
[f2e3]IScripts_LoadPortraitTilesAddress:
;
; Switch to bank 8 for the portrait sprites.
;
[f2e3]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[f2e6]48PHA; Push it to the stack.
[f2e7]a2 08LDX #$08; Bank 8 = Sprites
[f2e9]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Convert a portrait ID to a word boundary for later lookup.
;
[f2ec]ad c7 03LDA a:IScript_PortraitID; Load the portrait ID.
[f2ef]0aASL A; Convert to a word boundary.
[f2f0]a8TAY; Y = result
;
; Load the address for the portrait tiles table.
;
[f2f1]ad 0e 80LDA a:TILES_PORTRAITS_ADDRS_INDEX_REF; Load the lower byte of the start address for the portrait tiles.
[f2f4]85 02STA Temp_Addr_L; Store as the lower byte.
[f2f6]ad 0f 80LDA a:TILES_PORTRAITS_ADDRS_INDEX_REF+1; Load the upper byte of the portrait address.
[f2f9]18CLC
[f2fa]69 80ADC #$80; Add 0x80 to it.
[f2fc]85 03STA Temp_Addr_U; And store as the upper byte.
;
; Load the portrait from the sprite tiles list, using the
; portrait ID as the index.
;
[f2fe]b1 02LDA (Temp_Addr_L),Y; Load the lower byte of the address based on the potrait ID.
[f300]48PHA; Push it to the stack.
[f301]c8INY; Y++ (next offset)
[f302]b1 02LDA (Temp_Addr_L),Y; Load the upper byte of the tile.
[f304]18CLC
[f305]69 80ADC #$80; Add 0x80 to it.
[f307]85 03STA Temp_Addr_U; Store as the new upper byte.
[f309]68PLA; Pop the lower byte.
[f30a]85 02STA Temp_Addr_L; Store as the lower byte.
;
; Switch back to the stored bank.
;
[f30c]4c d6 f3JMP MMC1_UpdatePRGBankToStackA; Switch to the previous bank.
;
; The byte for the tile was 0xFF. Restore the bank
; and set a result of C=1, causing drawing to finish.
;
;
; XREFS:
;
IScripts_DrawPortraitTileToPPU
;
[f30f]IScripts_DrawPortraitTileToPPU_IsDone:
[f30f]68PLA; Pop the saved bank from the stack.
[f310]aaTAX; X = A (bank)
[f311]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
[f314]38SEC; Set C=1 for the result.
[f315]60RTS; Return it.
;============================================================================
; Draw a portrait tile to the PPU.
;
; This will load the next byte for the portrait, which
; represents an offset into the portrait tile list.
;
; If this is 0xFF, then drawing is complete, and this will
; return with C=1.
;
; Otherwise, the tile will be drawn to the PPU and state
; will be prepared for the next tile.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank to return to.
;
;
Temp_Addr_U:
;
Temp_Addr_L:
; The tile information address to read from.
;
; OUTPUTS:
; C:
; 1 = Drawing is finished (byte was 0xFF).
; 0 = There are more tiles to draw.
;
;
PPUBuffer:
;
PPUBuffer_WriteOffset:
; The updated PPU buffer state.
;
;
PPU_TargetAddr:
; The updated PPU target address (incremented
; by 16).
;
; CALLS:
;
MMC1_UpdateROMBank
;
PPUBuffer_QueueCommandOrLength
;
; XREFS:
;
IScripts_LoadPortraitTiles
;============================================================================
[f316]IScripts_DrawPortraitTileToPPU:
;
; Save the current bank and switch to bank 8.
;
[f316]ad 00 01LDA a:CurrentROMBank; Load the current ROM bank.
[f319]48PHA; Push it to the stack.
[f31a]a2 08LDX #$08; Bank 8 = Portrait sprites
[f31c]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the tile and see if it's set or if it's 0xFF.
;
[f31f]b1 02LDA (Temp_Addr_L),Y; Load the tile index from the tiles address.
[f321]c9 ffCMP #$ff; Is it 0xFF?
[f323]f0 eaBEQ IScripts_DrawPortraitTileToPPU_IsDone; If so, jump to finish.
;
; Generate an offset into the tiles list, turning each nibble
; of the loaded byte into a 16-bit value.
;
; This is equivalent to:
;
; A (lower) = (byte << 4) & 0xFF
;
Temp_05 (upper) = (byte >> 4) & 0xFF
;
[f325]a9 00LDA #$00; A = 0 (offset).
[f327]85 05STA Temp_05
[f329]b1 02LDA (Temp_Addr_L),Y
[f32b]0aASL A
[f32c]26 05ROL Temp_05
[f32e]0aASL A
[f32f]26 05ROL Temp_05
[f331]0aASL A
[f332]26 05ROL Temp_05
[f334]0aASL A
[f335]26 05ROL Temp_05
[f337]18CLC
[f338]6d 10 80ADC a:TILES_PORTRAITS_START_REF; Add the tile ID to the lower byte.
[f33b]85 04STA Temp_04; Store in Temp_04.
[f33d]a5 05LDA Temp_05; Load the calculated upper byte.
[f33f]6d 11 80ADC a:TILES_PORTRAITS_START_REF+1; Add the upper byte for the relative start address.
[f342]18CLC
[f343]69 80ADC #$80; Add 0x80.
[f345]85 05STA Temp_05; And store as a new upper byte.
;
; Begin writing to the PPU buffer.
;
[f347]a9 10LDA #$10; Length = 16 (tile bytes).
[f349]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue that as the PPU buffer length.
;
; Begin populating the buffer with the tile's bytes.
;
[f34c]a0 00LDY #$00; Y = 0 (loop counter).
[f34e]@_drawLoop:
[f34e]b1 04LDA (Temp_04),Y; Load the next byte for the tile.
[f350]9d 00 05STA a:PPUBuffer,X; Store it in the PPU buffer.
[f353]e8INX; X++ (destination).
[f354]c8INY; Y++ (loop counter).
[f355]c0 10CPY #$10; Have we hit 16 bytes?
[f357]90 f5BCC @_drawLoop; If not, loop.
;
; All tiles have been drawn. Update the write offset and
; advance the PPU address.
;
[f359]86 20STX PPUBuffer_WriteOffset; Store the new PPU buffer write offset.
[f35b]a5 e8LDA PPU_TargetAddr; Load the lower byte of the target address.
[f35d]18CLC
[f35e]69 10ADC #$10; Advance by 16 bytes.
[f360]85 e8STA PPU_TargetAddr; Store it.
[f362]a5 e9LDA PPU_TargetAddr_U; Load the upper byte of the target address.
[f364]69 00ADC #$00; Add carry, if lower wrapped.
[f366]85 e9STA PPU_TargetAddr_U; Store it.
;
; Restore the previous bank.
;
[f368]68PLA; Pop the bank from the stack.
[f369]aaTAX; X = A (bank).
[f36a]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
[f36d]18CLC; Set C=0 for the result.
[f36e]60RTS
;============================================================================
; Set the ID of the sound effect to play.
;
; This assigns the sound effect to play during the next
; hardware interrupt.
;
; In theory, it also works off of a priority table to
; determine which sound effect should be playing, but
; in practice this was either disabled or never fully
; implemented. See the notes in the code.
;
; INPUTS:
; A:
; The ID of the sound effect to set.
;
;
SoundEffect_Unused_PriorityID:
; The currently-played sound effect.
;
;
SOUND_PRIORITIES:
; The table of sound effect priorities.
;
; OUTPUTS:
;
SoundEffect_Unused_PriorityID:
; The new value for the current sound ID, if set.
;
;
SoundEffect_Current:
; The new value for the next sound effect, if set.
;
; XREFS:
;
Sound_PlayEffect
;============================================================================
[f36f]SoundEffect_SetCurrent:
[f36f]c9 1dCMP #$1d; Length of sound IDs array
[f371]b0 14BCS @_return; If out of bounds, return.
;
; DEADCODE: Check the priority level of this sound effect.
;
; This code is run, but nothing really happens. Let's explore
; what it's doing:
;
; 1. It's taking
SoundEffect_Unused_PriorityID and
; comparing it to the ID of the sound effect to play.
;
; 2. If the new sound effect has a lower value, the variable
; will be set to the new sound effect ID.
;
; 3. It then returns.
;
; This seems to work like a priority level. I'm guessing it's
; to choose a higher-priority ID.
;
; HOWEVER, this ID never gets used anywhere else, and it's only
; ever set to 0. That means any time this is called, the current
; sound effect ID's value in the table is compared against the
; first (ID=0) entry, which is 0, and therefore the sound effect's
; value will never be less (nothing in there has bit 7 set).
;
; This makes all this code a bit worthless, and a candidate for
; removal.
;
[f373]aaTAX; X = new sound ID
[f374]ac 21 01LDY a:SoundEffect_Unused_PriorityID; Y = current sound ID
[f377]b9 88 f3LDA a:SOUND_PRIORITIES,Y; A = Sound type for current sound from map
[f37a]dd 88 f3CMP a:SOUND_PRIORITIES,X; Is this already the current sound?
[f37d]f0 06BEQ @_queueNext; If yes, then jump
[f37f]90 04BCC @_queueNext; If it's a lower priority (larger number), then jump.
[f381]8e 21 01STX a:SoundEffect_Unused_PriorityID; Set as the current sound.
[f384]60RTS
;
; Set the sound effect to play at the next hardware interrupt.
;
[f385]@_queueNext:
[f385]86 fbSTX SoundEffect_Current; Queue this up as the next effect.
[f387]@_return:
[f387]60RTS
;============================================================================
; DEADCODE: Table of sound effect priorities, indexed by sound ID.
;
; While code does reference this, no ID but 0 will ever be
; used. See the note in
SoundEffect_SetCurrent.
;
; XREFS:
;
SoundEffect_SetCurrent
;============================================================================
[f388]SOUND_PRIORITIES:
[f388]00.byte $00; [0]:
[f389]08.byte $08; [1]:
[f38a]15.byte $15; [2]: Hit enemy with weapon
[f38b]14.byte $14; [3]: Enemy is dead
[f38c]04.byte $04; [4]: Player hit
[f38d]19.byte $19; [5]: Magic
[f38e]0b.byte $0b; [6]: Open door
[f38f]0d.byte $0d; [7]:
[f390]10.byte $10; [8]: Item picked up
[f391]11.byte $11; [9]: Touched coin
[f392]17.byte $17; [10]: Cast magic hit an obstacle
[f393]09.byte $09; [11]: Cursor moved
[f394]13.byte $13; [12]: Text input
[f395]0a.byte $0a; [13]:
[f396]07.byte $07; [14]: Password character entered
[f397]0c.byte $0c; [15]: Block pushed
[f398]12.byte $12; [16]: Coin dropped
[f399]0f.byte $0f; [17]:
[f39a]0e.byte $0e; [18]:
[f39b]06.byte $06; [19]: Filling HP or MP
[f39c]18.byte $18; [20]: Tilte magic cast
[f39d]04.byte $04; [21]: Player taking step
[f39e]01.byte $01; [22]:
[f39f]02.byte $02; [23]:
[f3a0]03.byte $03; [24]:
[f3a1]05.byte $05; [25]: Gold changed
[f3a2]03.byte $03; [26]: Item used
[f3a3]10.byte $10; [27]: Touched meat
[f3a4]02.byte $02; [28]:
;============================================================================
; Load glyph data for all strings into the PPU tile map.
;
; Glyph data is located in Bank 13 at $8000.
;
; INPUTS:
;
CurrentROMBank:
; The current ROM bank.
;
; OUTPUTS:
;
PPUADDR:
; Updated with the write position.
;
;
Temp_Int24:
; Clobbered.
;
; CALLS:
;
MMC1_UpdateROMBank
;
PPU_WriteGlyphTile
;
; XREFS:
;
PasswordScreen_Show
;============================================================================
[f3a5]PPU_LoadGlyphsForStrings:
;
; Save the current bank and switch to bank 13 (strings).
;
[f3a5]ad 00 01LDA a:CurrentROMBank; Get the current ROM bank.
[f3a8]48PHA; Push it to the stack.
[f3a9]a2 0dLDX #$0d; 13 = Strings bank.
[f3ab]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
;
; Set the starting address to load from ($8000).
;
[f3ae]a9 00LDA #<TILE_GLYPHS_START; Lower byte of the starting address.
[f3b0]85 ecSTA Temp_Int24; Store it.
[f3b2]a9 80LDA #$80; Upper byte of the starting address.
[f3b4]85 edSTA Temp_Int24_M
;
; Set the starting PPU address to write glyphs to.
;
[f3b6]a9 12LDA #$12; Lower byte of the PPU address.
[f3b8]8d 06 20STA a:PPUADDR; Set it.
[f3bb]a9 00LDA #$00; Upper byte of the PPU address.
[f3bd]8d 06 20STA a:PPUADDR; Set it.
[f3c0]a2 60LDX #$60
[f3c2]@_loadGlyphsLoop:
[f3c2]20 dc f3JSR PPU_WriteGlyphTile; Write the glyph as plane 1.
[f3c5]20 dc f3JSR PPU_WriteGlyphTile; Write the glyph as plane 2.
[f3c8]a5 ecLDA Temp_Int24; Load the lower byte of the load address.
[f3ca]18CLC
[f3cb]69 08ADC #$08; Increment by 8.
[f3cd]90 02BCC @_prepareNextLoop; If it didn't wrap, jump to avoid setting the upper byte.
[f3cf]e6 edINC Temp_Int24_M; Increment the upper byte of the load address.
[f3d1]@_prepareNextLoop:
[f3d1]85 ecSTA Temp_Int24; Store as the new lower byte of the load address.
[f3d3]caDEX; X--
[f3d4]d0 ecBNE @_loadGlyphsLoop; If X > 0, loop.
;
; v-- Fall through --v
;
[f3d6]MMC1_UpdatePRGBankToStackA:
[f3d6]68PLA; Pop the bank from the stack.
[f3d7]aaTAX; X = Resulting bank.
[f3d8]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
[f3db]60RTS
;============================================================================
; Write a glyph to the list of tiles in the PPU.
;
; This is used by
PPU_LoadGlyphsForStrings to
; write a glyph tile for later use.
;
; INPUTS:
;
Temp_Int24:
; The temporary address to read bytes from.
;
; OUTPUTS:
;
PPUDATA:
; Data will be written to the PPU.
;
; XREFS:
;
PPU_LoadGlyphsForStrings
;============================================================================
[f3dc]PPU_WriteGlyphTile:
[f3dc]a0 00LDY #$00; Y = 0
[f3de]@_loop:
[f3de]b1 ecLDA (Temp_Int24),Y; Load the byte from the temp string address.
[f3e0]8d 07 20STA a:PPUDATA; Write to the PPU.
[f3e3]c8INY; Y++
[f3e4]c0 08CPY #$08; Have we written 8 bytes?
[f3e6]d0 f6BNE @_loop; If not, loop.
[f3e8]60RTS
;============================================================================
; Load the specified message and progressively show it.
;
; Each character in the message will be shown one-by-one.
;
; INPUTS:
; A:
; The message ID to show.
;
; OUTPUTS:
; None
;
; CALLS:
;
Messages_Load
;
TextBox_LoadAndShowMessage
;
; XREFS:
;
IScriptAction_ShowBuySellMenu
;============================================================================
[f3e9]IScripts_LoadAndShowMessage:
[f3e9]20 f5 f3JSR Messages_Load; Load the message.
[f3ec]@_loop:
[f3ec]20 7d f4JSR TextBox_LoadAndShowMessage; Show the next character from the message.
[f3ef]ad 13 02LDA a:MessageID; Is it 0xFF (end of message)?
[f3f2]d0 f8BNE @_loop; If not, loop.
[f3f4]60RTS
[f3f5]Messages_Load:
[f3f5]8d 13 02STA a:MessageID
;
; Save our current bank.
;
[f3f8]ad 00 01LDA a:CurrentROMBank; A = Our current bank
[f3fb]48PHA; Push A to the stack.
;
; Switch to Bank 13 for the strings.
;
[f3fc]a2 0dLDX #$0d; Update to Bank 13.
[f3fe]20 1a ccJSR MMC1_UpdateROMBank
[f401]a2 00LDX #<MESSAGE_STRINGS; Set the lower byte of the address for the strings.
[f403]86 ecSTX Temp_Int24
[f405]a2 83LDX #>MESSAGE_STRINGS; Set the upper byte of the address for the strings.
[f407]86 edSTX Temp_Int24_M
;
; Reset the display timer.
;
[f409]a0 00LDY #$00; Y = 0 (start of our lookup offset)
[f40b]8c 1d 02STY a:TextBox_Timer; Set the timer counter to 0.
;
; Prepare to loop through the message characters, starting
; at Message ID - 1 and going through 0.
;
; This will effectively from message ID 0 to the end of the
; provided message ID. We'll be doing a full string scan of
; every string along the way (there's no lookup table).
;
[f40e]ae 13 02LDX a:MessageID; X = MessageID
[f411]caDEX; X--
[f412]f0 0eBEQ @_afterLoops; If 0, don't do the message string index loop.
;
; Loop through characters in the current message string.
;
; We'll loop until we hit the end of a string.
;
[f414]@_messageLoop:
[f414]b1 ecLDA (Temp_Int24),Y; A = Character at Y from the current string.
[f416]c8INY; Y++
;
; If Y == 0, increment our upper byte position. This
; will happen when Y wraps around from 0xFF to 0.
;
[f417]d0 02BNE @_checkChar; If Y != 0, jump.
[f419]e6 edINC Temp_Int24_M; Y is 0. Increment upper byte position.
;
; Check if we hit a string terminator (0xFF). If so,
; decrement the local message ID index.
;
[f41b]@_checkChar:
[f41b]c9 ffCMP #$ff; Is A (the character) 0xFF?
[f41d]d0 f5BNE @_messageLoop; If not 0xFF, loop.
[f41f]caDEX; A is 0xFF. X-- to the previous message ID.
[f420]d0 f2BNE @_messageLoop; If X != 0, loop.
;
; We found the start position of our string (or were there
; already). We're done. Begin storing state.
;
[f422]@_afterLoops:
[f422]8c 14 02STY a:Message_ProcessedLength; Message length = Y
[f425]a5 ecLDA Temp_Int24; Load the new lower byte of the string position.
[f427]8d 18 02STA a:Message_StartAddr; Store it.
[f42a]a5 edLDA Temp_Int24_M; Load the new upper byte of the string position.
[f42c]8d 19 02STA a:Message_StartAddr_U; Store it.
;
; Restore our previous bank.
;
[f42f]68PLA; Pop A (our previous bank) off the stack.
[f430]aaTAX; X = A
[f431]20 1a ccJSR MMC1_UpdateROMBank; Switch bank to the bank.
;
; v-- Fall through --v
;
[f434]TextBox_ClearPasswordSize:
[f434]a9 00LDA #$00
[f436]8d 1a 02STA a:TextBox_LineScrollOffset; Set line scroll offset to 0.
[f439]8d 16 02STA a:TextBox_CharPosForLine; Set character position in line to 0.
[f43c]8d 17 02STA a:TextBox_DrawnLineNum; Set the drawn line number to 0.
[f43f]8d 15 02STA a:TextBox_MessagePaused; Clear the message paused state.
[f442]8d 1c 02STA a:Textbox_TitleCharOffset; Set the title character offset to 0.
[f445]a9 04LDA #$04
[f447]8d 1b 02STA a:Unused_Arg_Text_NumLines; Set the textbo height to 4.
;
; v-- Fall through --v
;
;============================================================================
; Clear the text tiles from the textbox.
;
; This will loop through the text tiles and clear them out.
; Tiles $1400 through $1800 will be cleared.
;
; INPUTS:
; None.
;
; OUTPUTS:
;
PPU_TargetAddr:
; The PPU target address ($1400).
;
; CALLS:
;
PPU_IncrementAddrBy32
;
PPUBuffer_WriteValueMany
;
; XREFS:
;
IScripts_FillPlaceholderText
;============================================================================
[f44a]TextBox_ClearTextTiles:
;
; Set the PPU target address to $1400. These are the
; text tiles.
;
[f44a]a9 14LDA #$14; 0x14 == upper byte of the PPU address.
[f44c]85 e9STA PPU_TargetAddr_U; Store it.
[f44e]a9 00LDA #$00; 0x00 == lower byte.
[f450]85 e8STA PPU_TargetAddr; Store it.
[f452]a0 20LDY #$20; Y = 32 (loop counter).
;
; Write the value 0x00 for bytes $1400 through $1800.
;
[f454]@_writeLoop:
[f454]98TYA; A = Y (loop counter)
[f455]48PHA; Push to the stack.
[f456]a9 00LDA #$00; A = 0
[f458]a0 20LDY #$20; Y = 32
[f45a]20 4a f8JSR PPUBuffer_WriteValueMany; Write 0x00 32 times to the PPU buffer.
[f45d]20 26 f8JSR PPU_IncrementAddrBy32; Address += 32
[f460]68PLA; Pull A from the stack.
[f461]a8TAY; Y = A (loop counter)
[f462]88DEY; Y--
[f463]d0 efBNE @_writeLoop; If > 0, loop.
[f465]TextBox_ClearShopSizeAtOffset_return:
[f465]60RTS
[f466]TextBox_ShowNextChar:
;
; Save the current bank and switch to bank 13 (strings).
;
[f466]ad 00 01LDA a:CurrentROMBank; A = current bank
[f469]48PHA; Push it to the stack.
[f46a]a2 0dLDX #$0d; X = 13 (bank)
[f46c]20 1a ccJSR MMC1_UpdateROMBank; Switch to bank 13.
;
; Show the next character.
;
[f46f]ee 1d 02INC a:TextBox_Timer; Increment the timer delay.
[f472]a9 01LDA #$01; A = 1
[f474]8d 12 02STA a:TextBox_PlayTextSound; Store it.
[f477]20 91 f4JSR TextBox_ShowNextChar_IfReady; Show this character in the message.
;
; Restore the bank and return.
;
[f47a]4c d6 f3JMP MMC1_UpdatePRGBankToStackA; Restore the pushed bank.
;============================================================================
; TODO: Document TextBox_LoadAndShowMessage
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
IScripts_LoadAndShowMessage
;============================================================================
[f47d]TextBox_LoadAndShowMessage:
;
; Save the current ROM.
;
[f47d]ad 00 01LDA a:CurrentROMBank
[f480]48PHA
;
; Switch to Bank 13, where the strings are.
;
[f481]a2 0dLDX #$0d
[f483]20 1a ccJSR MMC1_UpdateROMBank
[f486]a9 00LDA #$00
[f488]8d 12 02STA a:TextBox_PlayTextSound
[f48b]20 a2 f4JSR TextBox_DisplayMessage
;
; Restore the previous bank.
;
[f48e]4c d6 f3JMP MMC1_UpdatePRGBankToStackA
;============================================================================
; Show the next character in a message if the state allows it.
;
; A character will be shown if:
;
; 1. A message is loaded.
; 2. The message is not paused.
; 3. The delay timer is a multiple of 3.
;
; This falls through to
TextBox_DisplayMessage.
;
; INPUTS:
;
MessageID:
; The loaded message ID.
;
;
TextBox_MessagePaused:
; Whether the message is currently paused.
;
;
TextBox_Timer:
; The character display timer.
;
; OUTPUTS:
; None.
;
; XREFS:
;
TextBox_ShowNextChar
;============================================================================
[f491]TextBox_ShowNextChar_IfReady:
[f491]ad 13 02LDA a:MessageID; Load the message ID to show.
[f494]f0 cfBEQ TextBox_ClearShopSizeAtOffset_return; If unset, return.
[f496]ad 15 02LDA a:TextBox_MessagePaused; Is the message paused?
[f499]d0 caBNE TextBox_ClearShopSizeAtOffset_return; If so, return.
[f49b]ad 1d 02LDA a:TextBox_Timer; Load the character display timer.
[f49e]29 03AND #$03; Is it 3?
[f4a0]d0 c3BNE TextBox_ClearShopSizeAtOffset_return; If not, we're not ready to display the next character. Return.
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_DisplayMessage
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_LoadAndShowMessage
;============================================================================
[f4a2]TextBox_DisplayMessage:
[f4a2]ad 1c 02LDA a:Textbox_TitleCharOffset
[f4a5]f0 21BEQ TextBox_ShowMessage_LoadMessage
[f4a7]c9 90CMP #$90
[f4a9]f0 18BEQ @LAB_PRG15_MIRROR__f4c3
[f4ab]29 0fAND #$0f
[f4ad]85 ecSTA Temp_Int24
;
; Convert the player's title to an index in the string list.
;
[f4af]ad 37 04LDA a:PlayerTitle; Load the player's title.
[f4b2]0aASL A; Shift left 4, converting to an index.
[f4b3]0aASL A
[f4b4]0aASL A
[f4b5]0aASL A
[f4b6]05 ecORA Temp_Int24
[f4b8]aaTAX; X = A
[f4b9]bd 49 f6LDA a:PLAYER_TITLE_STRINGS,X; Look up the string.
[f4bc]ee 1c 02INC a:Textbox_TitleCharOffset
[f4bf]c9 20CMP #$20
[f4c1]d0 30BNE TextBox_ShowMessageWithSound
[f4c3]@LAB_PRG15_MIRROR__f4c3:
[f4c3]a9 00LDA #$00
[f4c5]8d 1c 02STA a:Textbox_TitleCharOffset
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_LoadMessage
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_DisplayMessage
;
TextBox_ShowMessage_Newline
;============================================================================
[f4c8]TextBox_ShowMessage_LoadMessage:
[f4c8]ad 18 02LDA a:Message_StartAddr
[f4cb]85 ecSTA Temp_Int24
[f4cd]ad 19 02LDA a:Message_StartAddr_U
[f4d0]85 edSTA Temp_Int24_M
[f4d2]ac 14 02LDY a:Message_ProcessedLength
[f4d5]ee 14 02INC a:Message_ProcessedLength
[f4d8]d0 03BNE @LAB_PRG15_MIRROR__f4dd
[f4da]ee 19 02INC a:Message_StartAddr_U
[f4dd]@LAB_PRG15_MIRROR__f4dd:
[f4dd]b1 ecLDA (Temp_Int24),Y
[f4df]c9 ffCMP #$ff
[f4e1]f0 75BEQ TextBox_ShowMessage_EndOfMessage
[f4e3]c9 feCMP #$fe
[f4e5]f0 3cBEQ TextBox_ShowMessage_Newline
[f4e7]c9 fdCMP #$fd
[f4e9]f0 17BEQ TextBox_ShowMessage_HandleSpaceOrEndOfLine
[f4eb]c9 fbCMP #$fb
[f4ed]f0 6fBEQ TextBox_ShowMessage_ShowPlayerTitle
[f4ef]c9 fcCMP #$fc
[f4f1]f0 4cBEQ TextBox_ShowMessage_Pause
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessageWithSound
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_DisplayMessage
;
TextBox_ShowMessage_ShowPlayerTitle
;============================================================================
[f4f3]TextBox_ShowMessageWithSound:
[f4f3]ae 12 02LDX a:TextBox_PlayTextSound
[f4f6]f0 07BEQ TextBox_ShowMessage
;
; Play the message sound effect.
;
[f4f8]48PHA; Push A to the stack.
[f4f9]a9 01LDA #$01; 0x01 == Message sound effect.
[f4fb]20 e4 d0JSR Sound_PlayEffect; Play it.
[f4fe]68PLA; Pop A.
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
PasswordScreen_ShowNextChar
;
TextBox_ShowMessageWithSound
;============================================================================
[f4ff]TextBox_ShowMessage:
[f4ff]20 f3 f5JSR TextBox_WriteChar
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_HandleSpaceOrEndOfLine
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_LoadMessage
;============================================================================
[f502]TextBox_ShowMessage_HandleSpaceOrEndOfLine:
;
; If it's 0xFD, it's a space.
;
[f502]ee 16 02INC a:TextBox_CharPosForLine
[f505]ad 16 02LDA a:TextBox_CharPosForLine
[f508]c9 10CMP #$10
[f50a]90 4bBCC TextBox_ShowMessage_Return
;
; Load the next character in the message string.
;
[f50c]ad 18 02LDA a:Message_StartAddr
[f50f]85 ecSTA Temp_Int24
[f511]ad 19 02LDA a:Message_StartAddr_U
[f514]85 edSTA Temp_Int24_M
[f516]ac 14 02LDY a:Message_ProcessedLength
[f519]b1 ecLDA (Temp_Int24),Y
;
; If it's 0xFF, the message ends.
;
[f51b]c9 ffCMP #$ff
[f51d]f0 39BEQ TextBox_ShowMessage_EndOfMessage
;
; If it's 0xFC, finish this part of the message.
;
[f51f]c9 fcCMP #$fc
[f521]f0 34BEQ TextBox_ShowMessage_Return
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_Newline
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_LoadMessage
;============================================================================
[f523]TextBox_ShowMessage_Newline:
;
; If it's 0xFE, it's a newline.
;
[f523]ad 16 02LDA a:TextBox_CharPosForLine
[f526]f0 a0BEQ TextBox_ShowMessage_LoadMessage
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_IncLineAndReset
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_Prepare4Lines
;============================================================================
[f528]TextBox_ShowMessage_IncLineAndReset:
;
; Increment the line and reset the character position.
;
[f528]a9 00LDA #$00; A = 0
[f52a]8d 16 02STA a:TextBox_CharPosForLine; Set as the character position.
[f52d]ac 17 02LDY a:TextBox_DrawnLineNum; Y = line number
[f530]c8INY; Y++
;
; Check if we've reached the end of the textbox.
; If so, prepare the state for the next 4 lines.
;
[f531]c0 04CPY #$04; Is the line number 4?
[f533]f0 3cBEQ TextBox_ShowMessage_Fill4Lines; If so, jump.
;
; We're not at the end of the textbox. Increment and return.
;
[f535]8c 17 02STY a:TextBox_DrawnLineNum; Els, set as the number of drawn lines.
[f538]60RTS
[f539]TextBox_ShowMessage_Prepare4Lines:
;
; Check if anything's ready to be written for this line.
;
[f539]ad 16 02LDA a:TextBox_CharPosForLine; Load the character position (1-based; 0 for not ready).
[f53c]d0 eaBNE TextBox_ShowMessage_IncLineAndReset; If > 0, the line can be drawn.
[f53e]60RTS
;============================================================================
; Handle a pause in the message the player must acknowledge.
;
; This is message code 0xFC.
;
; If the very next code is 0xFF, this will simply end the
; message.
;
; INPUTS:
;
Message_ProcessedLength:
; The processed number of bytes of the message.
;
;
Message_StartAddr:
;
Message_StartAddr+1:
; The address of the loaded message string.
;
; OUTPUTS:
;
MessageID:
; Set to 0 if the message ends.
;
;
TextBox_MessagePaused:
; Set to 0xFF if paused.
;
;
Temp_Int24:
;
Temp_Int24+1:
; Clobbered.
;
; XREFS:
;
TextBox_ShowMessage_LoadMessage
;============================================================================
[f53f]TextBox_ShowMessage_Pause:
[f53f]ad 18 02LDA a:Message_StartAddr; Load the lower byte of the message string address.
[f542]85 ecSTA Temp_Int24; Store it for length calculation.
[f544]ad 19 02LDA a:Message_StartAddr_U; Load the upper byte.
[f547]85 edSTA Temp_Int24_M; Store it.
;
; Check if the next byte is 0xFF, in which case we just want
; to end the message immediately.
;
[f549]ac 14 02LDY a:Message_ProcessedLength; Load the message length.
[f54c]b1 ecLDA (Temp_Int24),Y; Load the next byte of the message.
[f54e]c9 ffCMP #$ff; Is it 0xFF (end of message)?
[f550]f0 06BEQ TextBox_ShowMessage_EndOfMessage; If true, then end message.
;
; The message isn't ending. We're paused instead.
;
[f552]a9 ffLDA #$ff
[f554]8d 15 02STA a:TextBox_MessagePaused; Set the message as paused.
[f557]TextBox_ShowMessage_Return:
[f557]60RTS
[f558]TextBox_ShowMessage_EndOfMessage:
[f558]a9 00LDA #$00; A = 0
[f55a]8d 13 02STA a:MessageID; Store as the message ID (terminating message).
[f55d]60RTS
[f55e]TextBox_ShowMessage_ShowPlayerTitle:
;
; If it's 0xFB, the title rank will be inserted.
;
[f55e]a9 81LDA #$81; A = 0x81
[f560]8d 1c 02STA a:Textbox_TitleCharOffset; Store it.
[f563]ad 37 04LDA a:PlayerTitle; Load the player's title.
[f566]0aASL A; Convert to an index in the Player Titles table.
[f567]0aASL A
[f568]0aASL A
[f569]0aASL A
[f56a]aaTAX; X = A
[f56b]bd 49 f6LDA a:PLAYER_TITLE_STRINGS,X; Load the title string at X.
[f56e]4c f3 f4JMP TextBox_ShowMessageWithSound; Show this title in the textbox.
;============================================================================
; TODO: Document TextBox_ShowMessage_Fill4Lines
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_IncLineAndReset
;============================================================================
[f571]TextBox_ShowMessage_Fill4Lines:
;
; Set the start position where text will be drawn.
;
[f571]ad 08 02LDA a:TextBox_X; X = textbox tile X coordinate.
[f574]18CLC
[f575]69 02ADC #$02; X += 2
[f577]85 eaSTA TextBox_ContentsX; Store as the text X start position.
[f579]ad 09 02LDA a:TextBox_Y; Y = textbox tile Y coordinate.
[f57c]18CLC
[f57d]69 02ADC #$02; Y += 2
[f57f]85 ebSTA TextBox_ContentsY; Store as the text Y start position.
;
; Write 3 lines.
;
; This will be the available text characters, filling
; up the buffer. It will contain:
;
; @ABCDEFGHIJKLMNO
; PQRSTUVWXYZ[\]^_
; `abcdefghijklmno
; pqrstuvwxyz{}|~.
;
; This will effectively reserve space in the buffer.
;
[f581]20 04 f8JSR PPU_SetAddrForTextBoxPos; Set the PPU address for the text position.
[f584]ae 1a 02LDX a:TextBox_LineScrollOffset; X = scroll line offset
[f587]20 cd f5JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
[f58a]20 cd f5JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
[f58d]20 cd f5JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
;
; Clear the final line.
;
[f590]a9 00LDA #$00; A = 0 (start offset)
[f592]a0 10LDY #$10; Y = 16 (line length)
[f594]20 4a f8JSR PPUBuffer_WriteValueMany; Write '0' 16 times, clearing the line.
;
; Set the starting text position to the beginning text char/pos
; of the text box we just wrote to.
;
[f597]a9 00LDA #$00; A = 0
[f599]85 e8STA PPU_TargetAddr; Store A as the lower byte of the PPU target address.
[f59b]ad 1a 02LDA a:TextBox_LineScrollOffset; A = scroll-aware line offset.
[f59e]18CLC
[f59f]69 14ADC #$14; A += 20
[f5a1]85 e9STA PPU_TargetAddr_U; Store as the upper byte of the PPU target addres.
;
; Clear out each of the lines that were just written.
;
[f5a3]a2 10LDX #$10; X = 16
[f5a5]@LAB_PRG15_MIRROR__f5a5:
[f5a5]8aTXA; A = X
[f5a6]48PHA; Push A to the stack.
[f5a7]a9 00LDA #$00; A = 0
[f5a9]a0 10LDY #$10; Y = 16
[f5ab]20 4a f8JSR PPUBuffer_WriteValueMany; Write '0' 16 times, clearing the line.
[f5ae]20 1e f8JSR PPU_IncrementAddrBy16; Increment the offset by 16.
[f5b1]68PLA; Pop A from the
stack (now 16).
[f5b2]aaTAX; X = A
[f5b3]caDEX; X--
[f5b4]d0 efBNE @LAB_PRG15_MIRROR__f5a5; If X > 0, loop.
;
; Increase the Y position to draw to by 3, after these lines.
;
[f5b6]e6 ebINC TextBox_ContentsY; TextBox_ContentsY += 3
[f5b8]e6 ebINC TextBox_ContentsY
[f5ba]e6 ebINC TextBox_ContentsY
;
; Draw a line of text to fill the address.
;
; This will be:
;
; PQRSTUVWXYZ[\]^_
;
[f5bc]20 04 f8JSR PPU_SetAddrForTextBoxPos; Set the text draw position back.
[f5bf]ad 1a 02LDA a:TextBox_LineScrollOffset; A = line scroll offset.
[f5c2]20 d1 f5JSR TextBox_FillPlaceholderTextForLine; Write a line of text.
;
; Store the new line number offset.
;
[f5c5]e8INX; X++
[f5c6]8aTXA; A = X
[f5c7]29 03AND #$03; Cap the line number from 0..4.
[f5c9]8d 1a 02STA a:TextBox_LineScrollOffset; Store it.
[f5cc]60RTS
;============================================================================
; Write a line of arbitrary text, taking into account textbox scrolling.
;
; The text will be placeholder text, intended to be
; cleared later.
;
; This is used when a maximum of 4 lines will be shown.
; The provided line number will be capped to a value
; between 0 and 4 and written to the textbox draw address.
;
; INPUTS:
; X:
; The line number to write to.
;
; This will be capped to a displayed line number.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; The new upper bounds of the PPU buffer.
;
; CALLS:
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
PPU_IncrementAddrBy32
;
; XREFS:
;
TextBox_ShowMessage_Fill4Lines
;============================================================================
[f5cd]TextBox_FillPlaceholderTextForLineCapped:
[f5cd]e8INX; lineNum++
[f5ce]8aTXA; A = X
;
; Cap the linenum to lines 0-4.
;
[f5cf]29 03AND #$03
;
; v-- Fall through --v
;
[f5d1]TextBox_FillPlaceholderTextForLine:
[f5d1]aaTAX; X = A
;
; Calculate a character offset for the line number.
; Lines are 16 characters per line.
;
[f5d2]0aASL A; Multiply by 16.
[f5d3]0aASL A
[f5d4]0aASL A
[f5d5]0aASL A
;
; Add 64, giving us a value of "A".
;
[f5d6]69 40ADC #$40; Add 64 (character code of "A").
[f5d8]a8TAY; Y = A
;
; v-- Fall through --v
;
;============================================================================
; Write a line of arbitrary text, starting with a character.
;
; The text will be placeholder text, intended to be
; cleared later.
;
; INPUTS:
; A:
; The line number to write to.
;
; This will be a 0-based index into the textbox.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; The new upper bounds of the PPU buffer.
;
; CALLS:
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
PPU_IncrementAddrBy32
;
; XREFS:
;
IScripts_FillPlaceholderText
;============================================================================
[f5d9]TextBox_FillPlaceholderTextAtLineWithStartChar:
[f5d9]8aTXA; A = X (offset)
[f5da]48PHA; Push A to the stack.
;
; Set the write length to 16 characters (max for a line).
;
[f5db]a9 10LDA #$10; A = 16
[f5dd]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue this as the write length.
[f5e0]98TYA; A = Y
;
; Begin the write loop.
;
[f5e1]a0 10LDY #$10; Y = 16
[f5e3]@_loop:
[f5e3]20 45 f8JSR PPUBuffer_Set; Write the value to the buffer.
[f5e6]18CLC
[f5e7]69 01ADC #$01; A++ (next character value)
[f5e9]88DEY; Y--
[f5ea]d0 f7BNE @_loop; If Y > 0, loop.
[f5ec]86 20STX PPUBuffer_WriteOffset; Store the new offset as the PPU upper bounds.
[f5ee]68PLA; Pop A from the stack.
[f5ef]aaTAX; X = A
[f5f0]4c 26 f8JMP PPU_IncrementAddrBy32; Increment the PPU address by 32.
[f5f3]TextBox_WriteChar:
;
; Normalize the ASCII value into an index in the characters
; table.
;
[f5f3]38SEC; C = 1
[f5f4]e9 20SBC #$20; A -= 32 (normalize the value to a character lookup table)
;
; Set the address of the string address.
;
; Each glyph is 8 bytes, so this will normalize the character
; index to the proper byte offset.
;
[f5f6]a2 00LDX #$00; X = 0
[f5f8]86 edSTX Temp_Int24_M; Set middle byte of string address to 0.
[f5fa]0aASL A; A = A * 8 (each glyph is 8 bytes)
[f5fb]0aASL A
[f5fc]26 edROL Temp_Int24_M; Rotate upper byte left and add carry.
[f5fe]0aASL A
[f5ff]26 edROL Temp_Int24_M
[f601]69 00ADC #$00
[f603]85 ecSTA Temp_Int24
;
; Make the address relative to $8000.
;
[f605]a5 edLDA Temp_Int24_M; Load the upper byte of of the address.
[f607]69 80ADC #$80; Add 0x80 to it.
[f609]85 edSTA Temp_Int24_M; Store it back.
;
; Compute the PPU target address.
;
[f60b]a9 00LDA #$00; A = 0
[f60d]85 e9STA PPU_TargetAddr_U; Store as the starting upper byte of the PPU target address.
[f60f]ad 16 02LDA a:TextBox_CharPosForLine; A = character position.
[f612]0aASL A; Convert to a 16 byte address boundary.
[f613]0aASL A
[f614]0aASL A
[f615]0aASL A
[f616]85 e8STA PPU_TargetAddr; Store as the lower byte of the PPU target address.
[f618]ad 17 02LDA a:TextBox_DrawnLineNum; A = drawn line number.
[f61b]18CLC
[f61c]6d 1a 02ADC a:TextBox_LineScrollOffset; A += line scroll offset
[f61f]29 03AND #$03; Cap to a 0..4 line number.
[f621]69 14ADC #$14; A += 20
[f623]85 e9STA PPU_TargetAddr_U; Set as the new upper byte of the PPU target address.
;
; Save the current bank and switch to bank 13 (strings).
;
[f625]ad 00 01LDA a:CurrentROMBank; A = current bank
[f628]48PHA; Push A to the stack.
[f629]a2 0dLDX #$0d; A = bank 13
[f62b]20 1a ccJSR MMC1_UpdateROMBank; Switch to it.
;
; Queue up the drawn line.
;
[f62e]a9 10LDA #$10; A = 16 (line length)
[f630]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue as the line length.
[f633]20 3e f6JSR TextBox_Write8BytesFromTemp; Write the first half of the line.
[f636]20 3e f6JSR TextBox_Write8BytesFromTemp; Write the second half.
;
; Finish up and restore the bank.
;
[f639]86 20STX PPUBuffer_WriteOffset; Store the new upper bounds for the PPU buffer.
[f63b]4c d6 f3JMP MMC1_UpdatePRGBankToStackA; Restore to the previous bank.
;============================================================================
; Write values from temp to the PPU buffer.
;
; This will write all values from
Temp_Int24
; until a value 128-255 are hit.
;
; INPUTS:
; X:
; The destination index into the buffer to write to.
;
; OUTPUTS:
; None
;
; CALLS:
;
PPUBuffer_WriteFromTemp
;
; XREFS:
;
TextBox_WriteChar
;============================================================================
[f63e]TextBox_Write8BytesFromTemp:
[f63e]a0 00LDY #$00; Y = 0
[f640]@_loop:
[f640]20 42 f8JSR PPUBuffer_WriteFromTemp; Write a value from temp.
[f643]98TYA; A = Y
[f644]29 07AND #$07; Consider values 0-127.
[f646]d0 f8BNE @_loop; If not 0, loop.
[f648]60RTS
[f649]PLAYER_TITLE_STRINGS:
[f649]4e 6f 76 69 63 65 20 20.byte "Novice "; char
[f651]20 20 20 20 20 20 20 20.byte " "; char
[f659]41 73 70 69 72 61 6e 74.byte "Aspirant"; char
[f661]20 20 20 20 20 20 20 20.byte " "; char
[f669]42 61 74 74 6c 65 72 20.byte "Battler "; char
[f671]20 20 20 20 20 20 20 20.byte " "; char
[f679]46 69 67 68 74 65 72 20.byte "Fighter "; char
[f681]20 20 20 20 20 20 20 20.byte " "; char
[f689]41 64 65 70 74 20 20 20.byte "Adept "; char
[f691]20 20 20 20 20 20 20 20.byte " "; char
[f699]43 68 65 76 61 6c 69 65.byte "Chevalie"; char
[f6a1]72 20 20 20 20 20 20 20.byte "r "; char
[f6a9]56 65 74 65 72 61 6e 20.byte "Veteran "; char
[f6b1]20 20 20 20 20 20 20 20.byte " "; char
[f6b9]57 61 72 72 69 6f 72 20.byte "Warrior "; char
[f6c1]20 20 20 20 20 20 20 20.byte " "; char
[f6c9]53 77 6f 72 64 6d 61 6e.byte "Swordman"; char
[f6d1]20 20 20 20 20 20 20 20.byte " "; char
[f6d9]48 65 72 6f 20 20 20 20.byte "Hero "; char
[f6e1]20 20 20 20 20 20 20 20.byte " "; char
[f6e9]53 6f 6c 64 69 65 72 20.byte "Soldier "; char
[f6f1]20 20 20 20 20 20 20 20.byte " "; char
[f6f9]4d 79 72 6d 69 64 6f 6e.byte "Myrmidon"; char
[f701]20 20 20 20 20 20 20 20.byte " "; char
[f709]43 68 61 6d 70 69 6f 6e.byte "Champion"; char
[f711]20 20 20 20 20 20 20 20.byte " "; char
[f719]53 75 70 65 72 68 65 72.byte "Superher"; char
[f721]6f 20 20 20 20 20 20 20.byte "o "; char
[f729]50 61 6c 61 64 69 6e 20.byte "Paladin "; char
[f731]20 20 20 20 20 20 20 20.byte " "; char
[f739]4c 6f 72 64 20 20 20 20.byte "Lord "; char
[f741]20 20 20 20 20 20.byte " "; char
[f747]PLAYER_TITLE_STRINGS_15__14_:
[f747]20.byte " "; char
[f748]PLAYER_TITLE_STRINGS_15__15_:
[f748]20.byte " "; char
;============================================================================
; Experience requirements for each title.
;
; XREFS:
;
PlayerMenu_ShowStatusMenu
;
Player_CheckReachedNextTitle
;============================================================================
[f749]PLAYER_TITLE_EXP_NEEDED:
[f749]e8 03.word $03e8; [0]: Aspirant (1,000 exp)
[f74b]98 08.word $0898; [1]: Battler (2,200 exp)
[f74d]ac 0d.word $0dac; [2]: Fighter (3,500 exp)
[f74f]c0 12.word $12c0; [3]: Adept (4,800 exp)
[f751]38 18.word $1838; [4]: Chevalier (6,200 exp)
[f753]40 1f.word $1f40; [5]: Veteran (8,000 exp)
[f755]10 27.word $2710; [6]: Warrior (10,000 exp)
[f757]d4 30.word $30d4; [7]: Swordman (12,500 exp)
[f759]98 3a.word $3a98; [8]: Hero (15,000 exp)
[f75b]50 46.word $4650; [9]: Soldier (18,000 exp)
[f75d]f0 55.word $55f0; [10]: Myrmidon (22,000 exp)
[f75f]90 65.word $6590; [11]: Champion (26,000 exp)
[f761]30 75.word $7530; [12]: Superhero (30,000 exp)
[f763]b8 88.word $88b8; [13]: Paladin (35,000 exp)
[f765]PLAYER_TITLE_EXP_NEEDED_14_:
[f765]c8 af.word $afc8; [14]: Lord (45,000 exp)
;============================================================================
; Starting gold at each player title.
;============================================================================
[f767]PLAYER_TITLE_GOLD:
[f767]f4 01.word $01f4; [0]:
[f769]20 03.word $0320; [1]:
[f76b]b0 04.word $04b0; [2]:
[f76d]40 06.word $0640; [3]:
[f76f]34 08.word $0834; [4]:
[f771]f0 0a.word $0af0; [5]:
[f773]ac 0d.word $0dac; [6]:
[f775]cc 10.word $10cc; [7]:
[f777]50 14.word $1450; [8]:
[f779]38 18.word $1838; [9]:
[f77b]4c 1d.word $1d4c; [10]:
[f77d]28 23.word $2328; [11]:
[f77f]04 29.word $2904; [12]:
[f781]c8 32.word $32c8; [13]:
[f783]98 3a.word $3a98; [14]:
[f785]Player_GetInventoryIndexForItem:
[f785]4aLSR A; Shift right 5, turning inventory bits into an index.
[f786]4aLSR A
[f787]4aLSR A
[f788]4aLSR A
[f789]4aLSR A
[f78a]60RTS
;============================================================================
; Convert an inventory index to a bit for an item.
;
; This takes an inventory index and shifts it left 5,
; which turns it into an inventory bitmask that can be
; OR'd with an item ID.
;
; INPUTS:
; A:
; The inventory index.
;
; 0 = Weapons
; 1 = Armor
; 2 = Shields
; 3 = Magic
; 4 = Special
;
; OUTPUTS:
; A:
; The inventory bitmask.
;
; XREFS:
;
IScriptAction_ShowSellMenu
;
PlayerMenu_DrawInventoryItems
;
PlayerMenu_EquipItem
;
PlayerMenu_ShowStatusMenu
;============================================================================
[f78b]Player_GetInventoryBitForIndex:
[f78b]0aASL A; Shift left 5 (here and the next 4 after falling through).
[f78c]Math_MultiplyBy16:
[f78c]0aASL A; Shift left 4.
[f78d]0aASL A
[f78e]0aASL A
[f78f]0aASL A
[f790]60RTS
[f791]TextBox_GetBackingAttributeData:
;
; Preserve X and Y on the stack.
;
[f791]98TYA; A = Y
[f792]48PHA; Push it to the stack.
[f793]8aTXA; A = X
[f794]48PHA; Push it to the stack.
;
; Preserve the current ROM bank.
;
[f795]ad 00 01LDA a:CurrentROMBank; A = Current ROM bank.
[f798]48PHA; Push it to the stack.
;
; Switch to bank 3 (area metadata).
;
[f799]a2 03LDX #$03; X = 3 (Area metadata bank)
[f79b]20 1a ccJSR MMC1_UpdateROMBank; Switch to the bank.
[f79e]ad eb 00LDA a:TextBox_ContentsY; A = Tile Y position for the text in the textbox.
[f7a1]20 8c f7JSR Math_MultiplyBy16; Convert to an index in the block attributes data.
[f7a4]29 f0AND #$f0; Keep the upper nibble.
[f7a6]85 ecSTA Temp_Int24; Store for the final result.
[f7a8]0d ea 00ORA a:TextBox_ContentsX; OR the index with the tile X position for the text.
[f7ab]38SEC
[f7ac]e9 20SBC #$20; Subtract 32.
;
; Load the data attribute from the screen buffer at this index.
;
[f7ae]aaTAX; X = A (screen buffer lookup index)
[f7af]bc 00 06LDY a:ScreenBuffer,X; Y = Screen buffer data at the index.
[f7b2]b1 7eLDA (CurrentArea_BlockAttributesAddr),Y; Load the block attributes at the given offset.
[f7b4]4c f6 f7JMP textBox_GetAreaBehindTextBox_restoreAndReturn; Jump to finish loading.
;============================================================================
; TODO: Document Textbox_Maybe_GetAreaBehindTextbox
;
; INPUTS:
; Y
;
; OUTPUTS:
; A
;
; XREFS:
;
TextBox_Close
;============================================================================
[f7b7]Textbox_Maybe_GetAreaBehindTextbox:
[f7b7]98TYA
[f7b8]48PHA
[f7b9]8aTXA
[f7ba]48PHA
[f7bb]ad 00 01LDA a:CurrentROMBank
[f7be]48PHA
[f7bf]a2 03LDX #$03
[f7c1]20 1a ccJSR MMC1_UpdateROMBank
[f7c4]ad eb 00LDA a:TextBox_ContentsY
[f7c7]0aASL A
[f7c8]0aASL A
[f7c9]0aASL A
[f7ca]29 f0AND #$f0
[f7cc]85 ecSTA Temp_Int24
[f7ce]ad ea 00LDA a:TextBox_ContentsX
[f7d1]4aLSR A
[f7d2]05 ecORA Temp_Int24
[f7d4]38SEC
[f7d5]e9 20SBC #$20
[f7d7]aaTAX
[f7d8]bc 00 06LDY a:ScreenBuffer,X
[f7db]ad ea 00LDA a:TextBox_ContentsX
[f7de]29 01AND #$01
[f7e0]85 ecSTA Temp_Int24
[f7e2]ad eb 00LDA a:TextBox_ContentsY
[f7e5]0aASL A
[f7e6]29 02AND #$02
[f7e8]05 ecORA Temp_Int24
[f7ea]0aASL A
[f7eb]aaTAX
[f7ec]b5 80LDA CurrentArea_BlockData1StartAddr,X
[f7ee]85 ecSTA Temp_Int24
[f7f0]b5 81LDA CurrentArea_BlockData1StartAddr+1,X
[f7f2]85 edSTA Temp_Int24_M
[f7f4]b1 ecLDA (Temp_Int24),Y
[f7f6]textBox_GetAreaBehindTextBox_restoreAndReturn:
[f7f6]85 ecSTA Temp_Int24; Store the result temporarily.
;
; Restore the previous ROM bank.
;
[f7f8]68PLA; Pull the save ROM bank from the stack.
[f7f9]aaTAX; X = A (ROM bank).
[f7fa]20 1a ccJSR MMC1_UpdateROMBank; Restore the bank.
;
; Restore the original X and Y registers from the stack.
;
[f7fd]68PLA; Pull the original X from the stack.
[f7fe]aaTAX; Set back as X.
[f7ff]68PLA; Pull the original Y from the stack.
[f800]a8TAY; Set back as Y.
;
; Load the result we calculated and return it.
;
[f801]a5 ecLDA Temp_Int24; Load the calculated result.
[f803]60RTS; And return it.
[f804]PPU_SetAddrForTextBoxPos:
[f804]a5 0dLDA PPU_ScrollScreen; A = Scroll screen used for drawing.
[f806]29 01AND #$01; Convert to 0/1 (even/odd).
[f808]09 08ORA #$08; A += 8
[f80a]85 e9STA PPU_TargetAddr_U; Store as the PPU target address.
[f80c]ad eb 00LDA a:TextBox_ContentsY; Load the text Y position.
[f80f]0aASL A; A = A * 16
[f810]0aASL A
[f811]0aASL A
[f812]0aASL A
[f813]26 e9ROL PPU_TargetAddr_U; Rotate the upper byte of the target address left.
[f815]0aASL A; A << 1
[f816]26 e9ROL PPU_TargetAddr_U; Rotate the upper byte of the target address left.
[f818]0d ea 00ORA a:TextBox_ContentsX; OR it with the X position.
[f81b]85 e8STA PPU_TargetAddr; Store as the lower byte of the target address.
[f81d]60RTS
[f81e]PPU_IncrementAddrBy16:
[f81e]a9 10LDA #$10; A = 16
[f820]d0 06BNE PPU_IncrementAddrBy; Unconditionally jump to PPU_IncrementAddrBy.
;============================================================================
; Increment the PPU target address by 8.
;
; INPUTS:
;
PPU_TargetAddr:
;
PPU_TargetAddr+1:
; The existing PPU target address.
;
; OUTPUTS:
;
PPU_TargetAddr:
;
PPU_TargetAddr+1:
; The updated PPU target address.
;
; XREFS:
;
TextBox_FillBackground
;============================================================================
[f822]PPU_IncrementAddrBy8:
[f822]a9 08LDA #$08; A = 8
[f824]d0 02BNE PPU_IncrementAddrBy; Unconditionally jump to PPU_IncrementAddrBy.
[f826]PPU_IncrementAddrBy32:
[f826]a9 20LDA #$20; Set the offset to 32.
;
; v-- Fall through --v
;
[f828]PPU_IncrementAddrBy:
[f828]18CLC
[f829]65 e8ADC PPU_TargetAddr; A += PPU_TargetAddr
[f82b]90 02BCC @_storeAndReturn; If it overflowed, jump. No need to increment the upper byte.
[f82d]e6 e9INC PPU_TargetAddr_U; Else, increment the upper byte by 1.
[f82f]@_storeAndReturn:
[f82f]85 e8STA PPU_TargetAddr; Set the new target address.
[f831]60RTS
;============================================================================
; Queue a write to the PPU buffer for the textbox width.
;
; INPUTS:
;
TextBox_Width:
; The width of the textbox.
;
; OUTPUTS:
; Y:
; The textbox width.
;
; CALLS:
;
PPUBuffer_QueueCommandOrLength
;
; XREFS:
;
TextBox_Open
;============================================================================
[f832]TextBox_QueuePPUBufferTextBoxLength:
[f832]ad 0a 02LDA a:TextBox_Width; A = Textbox width
[f835]a8TAY; Y = A
[f836]4c dc cfJMP PPUBuffer_QueueCommandOrLength; Queue as the length.
;============================================================================
; Fill a line of text within a textbox with a value.
;
; This will write to the PPU buffer for a given width. The
; width is expected to be at the last text position within
; the textbox. It will stop before it hits the padding
; before the text on the line.
;
; INPUTS:
; A:
; The value to write.
;
; X:
; The offset to write to.
;
; Y:
; The text position (+ 2).
;
; OUTPUTS:
; None.
;
; CALLS:
;
PPUBuffer_Set
;
; XREFS:
;
IScriptAction_AddInventoryItem_ClearTextBox
;
TextBox_Open
;
TextBox_FillPPUBufferForTextWidth
;============================================================================
[f839]TextBox_FillPPUBufferForTextWidth:
[f839]20 45 f8JSR PPUBuffer_Set; Set the value in the buffer at the given offset.
[f83c]88DEY; Y--
[f83d]c0 02CPY #$02; Is it 2?
[f83f]d0 f8BNE TextBox_FillPPUBufferForTextWidth; If not, loop.
[f841]60RTS
;============================================================================
; Write a value from a temp location to the PPU buffer.
;
; This will load a byte from
Temp_Int24 and write
; it to the PPU buffer. The source offset will be
; advanced by 1.
;
; INPUTS:
; X:
; The destination index within the PPU buffer.
;
; Y:
; The offset from the address to load from.
;
;
Temp_Int24:
; The location of the address to load from.
;
; OUTPUTS:
; X:
; The new offset to write to (X + 1).
;
; XREFS:
;
PasswordScreen_DrawMessage
;
TextBox_LoadItemSourceTiles
;
TextBox_Write8BytesFromTemp
;============================================================================
[f842]PPUBuffer_WriteFromTemp:
[f842]b1 ecLDA (Temp_Int24),Y; Load the value from Temp_Int24 at offset Y.
[f844]c8INY; Y++
;
; v-- Fall through --v
;
[f845]PPUBuffer_Set:
[f845]9d 00 05STA a:PPUBuffer,X; Store A in the PPU buffer at X.
[f848]e8INX; X++
[f849]60RTS
;============================================================================
; Write a value many types to the PPU buffer.
;
; This will write the provided value the specified number
; of times, starting at a given offset.
;
; The first byte written will be the length, and the
; remaining bytes will be the provided value repeated.
;
; INPUTS:
; A:
; The value to write.
;
; Y:
; The number of times to write the value.
;
; OUTPUTS:
; X:
; The new offset in the buffer after these values.
;
; XREFS:
;
TextBox_ClearTextTiles
;
TextBox_ShowMessage_Fill4Lines
;============================================================================
[f84a]PPUBuffer_WriteValueMany:
[f84a]48PHA; Push A to the stack
[f84b]98TYA; A = Y (length)
[f84c]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Write the length to the buffer.
[f84f]68PLA; Pop A from the stack
[f850]@_loop:
[f850]20 45 f8JSR PPUBuffer_Set; Set the value in the PPU buffer.
[f853]88DEY; Y--
[f854]d0 faBNE @_loop; If Y > 0, loop
[f856]86 20STX PPUBuffer_WriteOffset; Set the upper bounds to write.
[f858]60RTS; Return with the new offset.
[f859]MMC1_LoadBankAndJump:
;
; Save out the X, Y, and Z values to temporary variables.
;
[f859]85 deSTA BankedCallSetup_SavedA; Set BankedCallSetup_SavedA = A
[f85b]86 dfSTX BankedCallSetup_SavedX; Set BankedCallSetup_SavedX = X
[f85d]84 e0STY BankedCallSetup_SavedY; Set BankedCallSetup_SavedY = Y
;
; Pop two bytes of A from the stack and transfer to the lower
; and middle bytes (in order) of
Temp_Int24.
;
[f85f]68PLA; Pop A from the stack.
[f860]85 ecSTA Temp_Int24; Lower byte of Temp_Int24 = A
[f862]68PLA; Pull A from the stack.
[f863]85 edSTA Temp_Int24_M; Middle byte of Temp_Int24 = A
;
; Save A to X. We'll increment this below if we overflow
; the add.
;
[f865]aaTAX; X = A
;
; Compute a new return low =
Temp_Int24.L + 3.
;
[f866]a5 ecLDA Temp_Int24; A = lower byte of Temp_Int24.
[f868]18CLC; C = 0
[f869]69 03ADC #$03; A = A + 3
[f86b]85 eeSTA Temp_Int24_U; Upper byte of Temp_Int24 = A
;
; If the upper byte overflowed, increment X.
;
[f86d]90 01BCC @_storeCurrentAddress; If carry is cleared, jump.
[f86f]e8INX; X = X + 1
;
; Preserve the target address and ROM bank on the stack.
;
[f870]@_storeCurrentAddress:
[f870]8aTXA; A = X (our adjusted offset)
[f871]48PHA; Push A to stack
[f872]a5 eeLDA Temp_Int24_U; A = upper byte of Temp_Int24
[f874]48PHA; Push A to stack
[f875]ad 00 01LDA a:CurrentROMBank; A = CurrentROMBank
[f878]48PHA; Push A to stack
;
; Store the loaded address and bank to jump to.
;
[f879]a0 03LDY #$03; Y = 3
[f87b]b1 ecLDA (Temp_Int24),Y; A = Upper byte of the address to jump to.
[f87d]85 efSTA Temp_0xEF; Temp_0xEF = A
[f87f]88DEY; Y-- (2)
[f880]b1 ecLDA (Temp_Int24),Y; A = Lower byte of the address to jump to.
[f882]85 eeSTA Temp_Int24_U; Upper byte of Temp_Int24 = A
[f884]88DEY; Y-- (1)
[f885]b1 ecLDA (Temp_Int24),Y; A = Bank to load
[f887]aaTAX; X = A
[f888]20 1a ccJSR MMC1_UpdateROMBank; Update the ROM bank to X.
[f88b]a9 f8LDA #>MMC1_PopBank; Upper byte of the trampoline address.
.
[f88d]48PHA; Push A to stack
[f88e]a9 c5LDA #<MMC1_PopBank-1; Upper byte of the trampoline address.
.
[f890]48PHA; Push A to stack
;
; Push the target address for the trampoline.
;
[f891]a5 efLDA Temp_0xEF; A = Temp_0xEF
[f893]48PHA; Push A to stack
[f894]a5 eeLDA Temp_Int24_U; A = upper byte of Temp_Int24.
[f896]48PHA; Push A to stack
;
; Restore the original values for A, X, and Y.
;
[f897]a5 deLDA BankedCallSetup_SavedA; A = BankedCallSetup_SavedA
[f899]a6 dfLDX BankedCallSetup_SavedX; X = BankedCallSetup_SavedX
[f89b]a4 e0LDY BankedCallSetup_SavedY; Y = BankedCallSetup_SavedY
[f89d]60RTS
;============================================================================
; TODO: Document PPU_WriteTilesFromCHRRAM
;
; INPUTS:
; X
; Y
;
; OUTPUTS:
; TODO
;
; XREFS:
;
SplashAnimation_DrawScenery
;
StartScreen_Draw
;
UI_DrawHUDSprites
;============================================================================
[f89e]PPU_WriteTilesFromCHRRAM:
[f89e]ad 00 01LDA a:CurrentROMBank
[f8a1]48PHA
[f8a2]20 1a ccJSR MMC1_UpdateROMBank
[f8a5]84 ecSTY Temp_Int24
[f8a7]a5 e9LDA PPU_TargetAddr_U
[f8a9]8d 06 20STA a:PPUADDR
[f8ac]a5 e8LDA PPU_TargetAddr
[f8ae]8d 06 20STA a:PPUADDR
[f8b1]a0 00LDY #$00
[f8b3]@LAB_PRG15_MIRROR__f8b3:
[f8b3]a2 10LDX #$10
[f8b5]@LAB_PRG15_MIRROR__f8b5:
[f8b5]b1 dbLDA (IScriptOrCHRAddr),Y
[f8b7]8d 07 20STA a:PPUDATA
[f8ba]c8INY
[f8bb]d0 02BNE @LAB_PRG15_MIRROR__f8bf
[f8bd]e6 dcINC IScriptOrCHRAddr_U
[f8bf]@LAB_PRG15_MIRROR__f8bf:
[f8bf]caDEX
[f8c0]d0 f3BNE @LAB_PRG15_MIRROR__f8b5
[f8c2]c6 ecDEC Temp_Int24
[f8c4]d0 edBNE @LAB_PRG15_MIRROR__f8b3
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document MMC1_PopBank
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;============================================================================
[f8c6]MMC1_PopBank:
[f8c6]68PLA
[f8c7]aaTAX
[f8c8]4c 1a ccJMP MMC1_UpdateROMBank
;============================================================================
; PPU address locations where statuc symbols are positioned.
;
; XREFS:
;
UI_DrawHUDSprites
;============================================================================
[f8cb]UI_STATUS_SYMBOL_PPU_ADDR_L:
[f8cb]41.byte $41; [0]: 0x2041 -- "M" (Magic)
[f8cc]UI_STATUS_SYMBOL_PPU_ADDR_L_1_:
[f8cc]61.byte $61; [1]: 0x2061 -- "P" (Power)
[f8cd]6e.byte $6e; [2]: 0x206E -- "G" (Gold)
[f8ce]4e.byte $4e; [3]: 0x204E -- "E" (Experience)
[f8cf]56.byte $56; [4]: 0x2056 -- "T" (Time)
[f8d0]5b.byte $5b; [5]: 0x205B -- Top-left of "[" (Selected item)
[f8d1]7b.byte $7b; [6]: 0x207B -- Bottom-left of "[" (Selected item)
;============================================================================
; Tile indexes to display in the status area.
;
; Each of these will be placed horizontally at a start
; location (by
UI_STATUS_SYMBOL_PPU_ADDR_L).
;
; A 0x00 means to end the run.
;
; XREFS:
;
UI_DrawHUDSprites
;============================================================================
[f8d2]UI_STATUS_TILES:
[f8d2]1c.byte $1c; [0]: "M" (Magic)
[f8d3]UI_STATUS_TILES_1_:
[f8d3]0a.byte $0a; [1]: ":"
[f8d4]UI_STATUS_TILES_2_:
[f8d4]00.byte $00; [2]:
[f8d5]1f.byte $1f; [3]: "P" (Power)
[f8d6]0a.byte $0a; [4]: ":"
[f8d7]00.byte $00; [5]:
[f8d8]16.byte $16; [6]: "G" (Gold)
[f8d9]3a.byte $3a; [7]: ":"
[f8da]00.byte $00; [8]:
[f8db]14.byte $14; [9]: "E" (Experience)
[f8dc]3a.byte $3a; [10]: ":"
[f8dd]00.byte $00; [11]:
[f8de]23.byte $23; [12]: "T" (Time)
[f8df]3a.byte $3a; [13]: ":"
[f8e0]00.byte $00; [14]:
[f8e1]2c.byte $2c; [15]: Top of "[" (current item)
[f8e2]3c.byte $3c; [16]: <blank space>
[f8e3]3d.byte $3d; [17]: <blank space>
[f8e4]2e.byte $2e; [18]: Top of "]" (current item)
[f8e5]00.byte $00; [19]:
[f8e6]2d.byte $2d; [20]: Bottom of "[" (current item)
[f8e7]3e.byte $3e; [21]: <blank space>
[f8e8]3f.byte $3f; [22]: <blank space>
[f8e9]2f.byte $2f; [23]: Bottom of "]" (current item)
[f8ea]00.byte $00; [24]:
;============================================================================
; Draw the status symbols on the screen.
;
; This will draw the following:
;
; "P" (Power)
; "M" (Magic)
; "E" (Experience)
; "G" (Gold)
; "T" (Time)
; Parts of "[ ]" for item selection
;
; It will also draw the selected item.
;
; INPUTS:
; None
;
; OUTPUTS:
; None
;
; XREFS:
;
Screen_SetupNew
;============================================================================
[f8eb]UI_DrawHUDSprites:
[f8eb]a9 0aLDA #$0a
[f8ed]8d ee 04STA a:UI_MPAndHPBarWidth
;
; Fill the entire status area with 0 (blank).
; This is 4 rows of tiles.
;
[f8f0]a9 20LDA #$20; Set the upper byte for the draw position.
[f8f2]8d 06 20STA a:PPUADDR; Store it.
[f8f5]a9 00LDA #$00; Set the lower byte for the draw position.
[f8f7]8d 06 20STA a:PPUADDR; Store it.
[f8fa]a0 80LDY #$80; Set Y = 128 (number of tiles to draw)
[f8fc]a9 00LDA #$00; Set the value to draw (blank tile).
[f8fe]20 b2 fcJSR PPU_FillData; Clear all 128 tiles.
;
; Prepare to draw the symbols.
;
; This will loop through all positions, and then all tiles
; for that position. These are done as two separate lookup
; tables:
;
; 1.
UI_STATUS_SYMBOL_PPU_ADDR_L: The lower
; addresses
; of the PPU draw positions for placing each set of tiles.
;
; This is incremented by 1 every time we finish placing tiles
; for that area.
;
; 2.
UI_STATUS_TILES: The tiles to draw at the
; current
;
; Each run of tiles terminates with a 0x00. The next index would
; then match the next position. This is incremented by 1 every
; time we place a tile.
;
[f901]a2 00LDX #$00; Set X = 0 (start index in UI_STATUS_SYMBOL_PPU_ADDR_L.
[f903]a0 00LDY #$00; Set Y = 0 (start index at UI_STATUS_TILES.
;
; Set the PPUADDR to $20XX, where XX comes from the
;
UI_STATUS_SYMBOL_PPU_ADDR_L lookup table.
;
; Each of these will be the location of a static symbol
; to show in the status area.
;
[f905]@_goToNextPosition:
[f905]a9 20LDA #$20
[f907]8d 06 20STA a:PPUADDR; Store the upper draw position byte as 0x20.
[f90a]bd cb f8LDA a:UI_STATUS_SYMBOL_PPU_ADDR_L,X; Load the lower byte from the lookup table based on the index.
[f90d]8d 06 20STA a:PPUADDR; Store it as the lower byte.
;
; Lay out tiles for UI elements starting at the current symbol
; address.
;
; This will write data from the
UI_STATUS_TILES
; table
; until it hits an end market (0x00).
;
; This is generally 2-4 tiles per address.
;
[f910]@_drawTiles:
[f910]b9 d2 f8LDA a:UI_STATUS_TILES,Y; Load the tile to draw at the current tile index.
[f913]f0 06BEQ @_advanceIndexes; If this is 0x00, we've reached the end of the run. Advance indexes.
[f915]8d 07 20STA a:PPUDATA; Else, write the tile.
[f918]c8INY; tileIndex++
[f919]d0 f5BNE @_drawTiles; Loop.
;
; Advance to the next set of tiles and the next lower address
; (draw position) in the lookup tables.
;
[f91b]@_advanceIndexes:
[f91b]c8INY; tileIndex++
[f91c]e8INX; position++
;
; Check if we've completed the lookup table.
;
; There's only 7 addresses/positions to work with.
;
[f91d]e0 07CPX #$07; Have we reached the end (draw position 7)?
[f91f]d0 e4BNE @_goToNextPosition; If not, loop.
[f921]20 af fbJSR UI_DrawSelectedItem; Draw the selected item.
;
; Set the location in bank 10 to load HUD tiles from
; (
HUD_TILES_START).
;
[f924]a9 40LDA #<HUD_TILES_START; Lower byte of the start of HUD tiles.
[f926]85 dbSTA IScriptOrCHRAddr; Set it for reading.
[f928]a9 81LDA #>HUD_TILES_START; Upper byte of the start of HUD tiles.
[f92a]85 dcSTA IScriptOrCHRAddr_U; Set it for reading.
;
; Set the address in the PPU to write tiles to ($1000).
;
[f92c]a9 00LDA #$00; Lower byte of PPU address.
[f92e]85 e8STA PPU_TargetAddr; Set it for writing.
[f930]a9 10LDA #$10; Upper byte of PPU address.
[f932]85 e9STA PPU_TargetAddr_U; Set it for writing.
;
; Begin writing 60 tiles from bank 10.
;
[f934]a2 0aLDX #$0a; X = bank 10.
[f936]a0 3cLDY #$3c; Y = number of tiles to write.
[f938]20 9e f8JSR PPU_WriteTilesFromCHRRAM
[f93b]60RTS
;============================================================================
; Check if the player reached the next title.
;
; If the player's experience passed the necessary threshold,
; the title will be increased by 1. It will never go up more
; than 1.
;
; INPUTS:
;
Experience_U:
; The upper byte of the experience to check.
;
;
Experience:
; The lower byte of the experience to check.
;
;
NextPlayerTitle:
; The current player title is checked.
;
; OUTPUTS:
;
NextPlayerTitle:
; The player title is updated as needed.
;
; XREFS:
;
Player_UpdateExperience
;============================================================================
[f93c]Player_CheckReachedNextTitle:
[f93c]ad ed 04LDA a:NextPlayerTitle
[f93f]c9 0fCMP #$0f; Have we hit the max title? (There are 15)
[f941]f0 13BEQ @_return; If yes, exit.
[f943]0aASL A; Convert to an index in the lookup table.
;
; Load the 16-bit experience level for the title.
;
[f944]aaTAX
[f945]ad 90 03LDA a:Experience; Load the lower byte from the lookup table.
[f948]dd 49 f7CMP a:PLAYER_TITLE_EXP_NEEDED,X; Compare it.
[f94b]ad 91 03LDA a:Experience_U; Load the upper byte from the lookup table.
[f94e]fd 4a f7SBC a:PLAYER_TITLE_EXP_NEEDED+1,X; Compare it.
[f951]90 03BCC @_return; If we're under, exit.
;
; The player has met the next title's requirements.
;
[f953]ee ed 04INC a:NextPlayerTitle; We have enough. Increase the player's next title.
[f956]@_return:
[f956]60RTS
[f957]Player_UpdateExperience:
;
; Update the lower byte of experience.
;
[f957]ad 90 03LDA a:Experience; Load the current lower byte of experience.
[f95a]18CLC
[f95b]65 ecADC Temp_Int24
[f95d]8d 90 03STA a:Experience; Set the new lower byte of experience.
;
; Update the upper byte of experience.
;
[f960]ad 91 03LDA a:Experience_U; Load the current upper byte of experience.
[f963]65 edADC Temp_Int24_M
[f965]8d 91 03STA a:Experience_U; Set the new upper byte of experience.
[f968]90 08BCC @LAB_PRG15_MIRROR__f972; Can we add to the experience, or did we hit a max?
;
; We hit the maximum amount of experience. Make sure
; this is capped.
;
[f96a]a9 ffLDA #$ff
[f96c]8d 90 03STA a:Experience; Set lower byte of experience to max.
[f96f]8d 91 03STA a:Experience_U; Set upper byte of experience to max.
[f972]@LAB_PRG15_MIRROR__f972:
[f972]20 3c f9JSR Player_CheckReachedNextTitle; Check if the next title has been hit.
;
; Fall through to the next function.
;
;============================================================================
; Render the player experience.
;
; This will set the draw position to 0x2050 (the location of
; the first digit of the experience), store out the experience
; to render, and then trigger the render.
;
; INPUTS:
;
Experience_U:
; The upper byte of the experience.
;
;
Experience:
; The lower byte of the experience.
;
; OUTPUTS:
;
PPU_TargetAddr+1:
; The upper byte of the location to render to.
;
;
PPU_TargetAddr:
; The lower byte of the location to render to.
;
; $ee:
; TODO: Currently unknown.
;
; CALLS:
;
UI_DrawDigitsZeroPadded
;
; XREFS:
;
UI_DrawHUD
;============================================================================
[f975]UI_DrawPlayerExperience:
;
; Set the address of the first digit of the experience to write
; (0x2050).
;
[f975]a9 20LDA #$20
[f977]85 e9STA PPU_TargetAddr_U; Store 0x20 as the upper byte.
[f979]a9 50LDA #$50
[f97b]85 e8STA PPU_TargetAddr; Store 0x50 as the upper byte.
[f97d]ad 90 03LDA a:Experience; Load the lower byte of the experience.
[f980]85 ecSTA Temp_Int24; Save to Temp_Int24.
[f982]ad 91 03LDA a:Experience_U; Load the upper byte of the experience.
[f985]85 edSTA Temp_Int24_M; Save to Temp_Int24+1.
[f987]a9 00LDA #$00
[f989]85 eeSTA Temp_Int24_U
[f98b]a0 05LDY #$05
[f98d]4c 06 faJMP UI_DrawDigitsZeroPadded; Trigger the render.
[f990]UI_DrawTimeValue:
;
; Set the number of seconds remaining as a 24-bit integer.
;
; Only the lower byte is used. The rest are 0'd out.
;
[f990]85 ecSTA Temp_Int24; Set number of seconds as the lower byte.
[f992]a9 00LDA #$00
[f994]85 edSTA Temp_Int24_M; Set middle byte = 0.
[f996]85 eeSTA Temp_Int24_U; Set upper byte = 0.
;
; Set the draw position (first digit of time remaining).
; This is 0x2058.
;
[f998]a9 20LDA #$20
[f99a]85 e9STA PPU_TargetAddr_U; Set high byte of position as 0x20.
[f99c]a9 58LDA #$58
[f99e]85 e8STA PPU_TargetAddr; Set high byte of position as 0x58.
;
; Draw the digits with zero-padding.
;
[f9a0]a0 02LDY #$02; Set the number of digits to draw.
[f9a2]4c 06 faJMP UI_DrawDigitsZeroPadded; Draw the digits with zero-padding.
;============================================================================
; Subtract gold from the player.
;
; This takes in gold as the 24-bit
; $ee:
Temp_Int24+1:
Temp_Int24
; and reduces those values from the player's gold.
;
; After subtracting, this will be drawn to the screen.
;
; INPUTS:
; $ee:
; The upper byte of the 24-bit gold value to subtract.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit gold value to
; subtract.
;
;
Temp_Int24:
; The lower byte of the 24-bit gold value to
; subtract.
;
; OUTPUTS:
;
Gold_U:
; The new upper byte of the current gold value.
;
;
Gold_M:
; The new middle byte of the current gold value.
;
;
Gold:
; The new lower byte of the current gold value.
;
; CALLS:
;
UI_DrawGoldValue
;
; XREFS:
;
IScripts_ProgressivelySubtractGold
;============================================================================
[f9a5]Player_SubtractGold:
;
; Subtract from the lower byte.
;
[f9a5]ad 92 03LDA a:Gold; Load the lower byte of the current amount.
[f9a8]38SEC
[f9a9]e5 ecSBC Temp_Int24; Subtract the provided value.
[f9ab]8d 92 03STA a:Gold; Store as the new lower byte.
;
; Subtract from the middle byte.
;
[f9ae]ad 93 03LDA a:Gold_M; Load the middle byte of the current amount.
[f9b1]e5 edSBC Temp_Int24_M; Subtract the provided value and the carry.
[f9b3]8d 93 03STA a:Gold_M; Store as the new middle byte.
;
; Subtract from the upper byte.
;
[f9b6]ad 94 03LDA a:Gold_U; Load the upper byte of the current amount.
[f9b9]e9 00SBC #$00; Subtract the carry.
[f9bb]8d 94 03STA a:Gold_U; Store as the new upper byte.
;
; Draw the new amount.
;
[f9be]4c e7 f9JMP UI_DrawGoldValue; Draw the new amount of gold.
;============================================================================
; Add gold to the player.
;
; This takes in gold as the 24-bit
; $ee:
Temp_Int24+1:
Temp_Int24
; and adds those values from the player's gold.
;
; After adding, this will be drawn to the screen.
;
; INPUTS:
; $ee:
; The upper byte of the 24-bit gold value to add.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit gold value to add.
;
;
Temp_Int24:
; The lower byte of the 24-bit gold value to add.
;
; OUTPUTS:
;
Gold_U:
; The new upper byte of the current gold value.
;
;
Gold_M:
; The new middle byte of the current gold value.
;
;
Gold:
; The new lower byte of the current gold value.
;
; CALLS:
;
UI_DrawDigitsZeroPadded
;
; XREFS:
;
IScripts_ProgressivelyAddGold
;============================================================================
[f9c1]Player_AddGold:
;
; Add to the lower byte.
;
[f9c1]ad 92 03LDA a:Gold; Load the lower byte of the current amount.
[f9c4]18CLC
[f9c5]65 ecADC Temp_Int24; Add the new amount.
[f9c7]8d 92 03STA a:Gold; Store as the new lower byte.
;
; Add to the middle byte.
;
[f9ca]ad 93 03LDA a:Gold_M; Load the middle byte of the current amount.
[f9cd]65 edADC Temp_Int24_M; Add the new amount + carry.
[f9cf]8d 93 03STA a:Gold_M; Store as the new middle byte.
;
; Add to the upper byte.
;
[f9d2]ad 94 03LDA a:Gold_U; Load the upper byte of the current amount.
[f9d5]69 00ADC #$00; Add the carry.
[f9d7]8d 94 03STA a:Gold_U; Store as the new upper byte.
;
; If there's no carry, draw the value. Otherwise, we've
; maxed out the gold, so set that explicitly and then draw.
;
[f9da]90 0bBCC UI_DrawGoldValue; If no carry, draw the current amount.
;
; Carry was set. We're maxed. Make it official.
;
[f9dc]a9 ffLDA #$ff
[f9de]8d 92 03STA a:Gold; Set lower to max.
[f9e1]8d 93 03STA a:Gold_M; Set middle to max.
[f9e4]8d 94 03STA a:Gold_U; Set upper to max.
;
; v-- Fall through --v
;
[f9e7]UI_DrawGoldValue:
;
; Set the draw position for the first digit of gold.
; This is 0x2070.
;
[f9e7]a9 20LDA #$20
[f9e9]85 e9STA PPU_TargetAddr_U; Set the upper byte to 0x20.
[f9eb]a9 70LDA #$70
[f9ed]85 e8STA PPU_TargetAddr; Set the lower byte to 0x70.
;
; Load the gold into the 24-bit temporary integer used for
; drawing digits.
;
[f9ef]ad 92 03LDA a:Gold; Load the lower byte of the gold value.
[f9f2]85 ecSTA Temp_Int24; Set a the lower byte for the digits to draw.
[f9f4]ad 93 03LDA a:Gold_M; Load the middle byte of the gold value.
[f9f7]85 edSTA Temp_Int24_M; Set a the middle byte for the digits to draw.
[f9f9]ad 94 03LDA a:Gold_U; Load the upper byte of the gold value.
[f9fc]85 eeSTA Temp_Int24_U; Set a the upper byte for the digits to draw.
;
; Draw the new amount as 7 digits, zero-padded.
;
[f9fe]a0 07LDY #$07
[fa00]4c 06 faJMP UI_DrawDigitsZeroPadded; Draw 7 digits of gold.
;============================================================================
; Draw a 0-padded 24-bit number at the textbox draw position.
;
; This will draw a number up to a specific number of digits
; (maximum of 7) at the current textbox draw position,
; filling the width with leading zeros.
;
; It starts by populating all zeros, and then follows up
; by filling in any values from the number.
;
; INPUTS:
; Y:
; The number of digits in total.
;
; $ee:
; The upper byte of the 24-bit value.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit value.
;
;
Temp_Int24:
; The lower byte of the 24-bit value.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; The new write offset into the PPU buffer.
;
; CALLS:
;
PPU_SetAddrForTextBoxPos
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
UI_PopulateDigits
;
; XREFS:
;
PlayerMenu_ShowStatusMenu
;============================================================================
[fa03]TextBox_DrawZeroPaddedNumber:
[fa03]20 04 f8JSR PPU_SetAddrForTextBoxPos; Set the PPU position address for the text.
;
; v-- Fall through --v
;
;============================================================================
; Draw a 0-padded 24-bit number to the screen.
;
; This will draw a number up to a specific number of digits
; (maximum of 7) at the current PPU target address, filling
; the width with leading zeros.
;
; It starts by populating all zeros, and then follows up
; by filling in any values from the number.
;
; INPUTS:
; Y:
; The number of digits in total.
;
; $ee:
; The upper byte of the 24-bit value.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit value.
;
;
Temp_Int24:
; The lower byte of the 24-bit value.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; The new write offset into the PPU buffer.
;
; CALLS:
;
PPU_SetAddrForTextBoxPos
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
UI_PopulateDigits
;
; XREFS:
;
UI_DrawGoldValue
;
UI_DrawPlayerExperience
;
UI_DrawTimeValue
;============================================================================
[fa06]UI_DrawDigitsZeroPadded:
[fa06]98TYA; A = Y (number of digits).
[fa07]48PHA; Push it to the stack.
[fa08]20 47 faJSR UI_PopulateDigits; Populate the ASCII digits to render.
[fa0b]UI_DrawDigitsZeroPadded_Populated:
[fa0b]68PLA; Pull the number of digits from the stack.
[fa0c]a8TAY; Y = A
[fa0d]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Set as the number of tiles to draw in the PPU buffer.
[fa10]84 ecSTY Temp_Int24; Store the number temporarily.
[fa12]a9 07LDA #$07; A = 7 (maximum number of digits).
[fa14]38SEC
[fa15]e5 ecSBC Temp_Int24; Subtract the number of tiles to draw.
[fa17]a8TAY; Y = result (loop counter).
[fa18]@_drawDigitsLoop:
[fa18]b9 38 02LDA a:UI_DigitsToRender,Y; Load the digit to draw at this index.
[fa1b]20 45 f8JSR PPUBuffer_Set; Set it in the PPU buffer.
[fa1e]c8INY; Y++
[fa1f]c0 07CPY #$07; Is it 7 (max)?
[fa21]d0 f5BNE @_drawDigitsLoop; If not, loop.
[fa23]86 20STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
[fa25]60RTS
;============================================================================
; Draw a space-padded 24-bit number to the screen.
;
; This will draw a number up to a specific number of digits
; (maximum of 7) at the current PPU target address, filling
; the width with spaces.
;
; It starts by populating all spaces, and then follows up
; by filling in any values from the number.
;
; INPUTS:
; Y:
; The number of digits in total.
;
; $ee:
; The upper byte of the 24-bit value.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit value.
;
;
Temp_Int24:
; The lower byte of the 24-bit value.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; The new write offset into the PPU buffer.
;
; CALLS:
;
PPU_SetAddrForTextBoxPos
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
UI_PopulateDigitsNoLeadingZeroes
;
; XREFS:
;
Shop_Draw
;============================================================================
[fa26]UI_DrawDigitsNoLeadingZeroes:
[fa26]20 04 f8JSR PPU_SetAddrForTextBoxPos; Set the PPU position address for the text.
[fa29]98TYA; A = Y (number of digits)
[fa2a]48PHA; Push it to the stack.
[fa2b]20 31 faJSR UI_PopulateDigitsNoLeadingZeroes; Populate the digits with no leading zeros.
[fa2e]4c 0b faJMP UI_DrawDigitsZeroPadded_Populated; Draw the digits.
;============================================================================
; Populate digits but with leading "0"s empty.
;
; This will populate the digits and then loop through them
; (up to 6 digits), NULing out all leading "0" digits so
; they don't display.
;
; INPUTS:
; X:
; The starting index into the digits.
;
; OUTPUTS:
;
UI_DigitsToRender:
; The updated digits.
;
; CALLS:
;
UI_PopulateDigits
;
; XREFS:
;
UI_DrawDigitsNoLeadingZeroes
;============================================================================
[fa31]UI_PopulateDigitsNoLeadingZeroes:
[fa31]20 47 faJSR UI_PopulateDigits; Populate the ASCII digits.
[fa34]e8INX; Start at the provided index + 1.
;
; Check if the digit displays "0". If not, we're done.
;
[fa35]@_loop:
[fa35]bd 38 02LDA a:UI_DigitsToRender,X; Load the current ASCII digit.
[fa38]c9 30CMP #$30; Is it an ASCII "0"?
[fa3a]d0 0aBNE @_return; If not, we're done.
;
; Clear out the digit entirely.
;
[fa3c]a9 00LDA #$00; NUL out this digit.
[fa3e]9d 38 02STA a:UI_DigitsToRender,X; Store that as the new digit value.
[fa41]e8INX; i++
[fa42]e0 06CPX #$06; Are we at the end of the loop?
[fa44]d0 efBNE @_loop; If not, loop again.
[fa46]@_return:
[fa46]60RTS
;============================================================================
; Populate RAM with the digits to set based in the status UI.
;
; This will take the 24-bit value stored in
; $ee:
Temp_Int24+1:
Temp_Int24.
;
; It will loop through every digit, convert to ASCII, and
; store in
UI_DigitsToRender for render.
;
; INPUTS:
; $ee:
; The upper byte of the 24-bit value.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit value.
;
;
Temp_Int24:
; The lower byte of the 24-bit value.
;
; OUTPUTS:
;
UI_DigitsToRender:
; The resulting digits in ASCII form.
;
; CALLS:
;
UI_GetValueForDigit
;
; XREFS:
;
UI_DrawDigitsZeroPadded
;
UI_PopulateDigitsNoLeadingZeroes
;============================================================================
[fa47]UI_PopulateDigits:
;
; Prepare to loop 7 times (the max number of digits).
;
[fa47]a2 06LDX #$06; Set the upper bound for the loop. We'll count down.
;
; Get the value for the next digit and convert to ASCII.
;
[fa49]@_loop:
[fa49]20 55 faJSR UI_GetValueForDigit; Get the value for this digit.
[fa4c]09 30ORA #$30; Normalize the value to an ASCII digit.
[fa4e]9d 38 02STA a:UI_DigitsToRender,X; Store in the target position in RAM.
[fa51]caDEX; i--
[fa52]10 f5BPL @_loop; Loop if we're not done.
[fa54]60RTS
;============================================================================
; Return a numeric value for status line UI display.
;
; This takes a 24-bit value (representing the gold or
; experience, in practice) and converts it to a numeric
; value between 0 and 9.
;
; It does this by considering the unsigned integer value
; (stored as
; $ee:
Temp_Int24+1:
Temp_Int24).
;
; It loops for each of the 24 bits, left-shifting and
; rotating the most-significant bit into A (the resulting
; value). If A >= 10, it will subtract 10 and set the
; quotient bit incrementing @{symbol Temp3_L}. Rinse and
; repeat.
;
; After 24 iterations, the the remainder will be in A, and
; this will be a value between 0 and 9.
;
; The upper, middle, and lower values will be modified to
; divide by 10, which allows this to be called repeatedly
; to get every digit.
;
; INPUTS:
; $ee:
; The upper byte of the 24-bit value.
;
;
Temp_Int24+1:
; The middle byte of the 24-bit value.
;
;
Temp_Int24:
; The lower byte of the 24-bit value.
;
; OUTPUTS:
; A:
; The resulting digit (between 0 and 9).
;
; $ee:
; Upper byte of the floor of the value / 10.
;
;
Temp_Int24+1:
; Middle byte of the floor of the value / 10.
;
;
Temp_Int24:
; Lower byte of the floor of the value / 10.
;
; XREFS:
;
UI_PopulateDigits
;============================================================================
[fa55]UI_GetValueForDigit:
[fa55]a0 18LDY #$18; Prepare to loop over the 24 bits.
[fa57]a9 00LDA #$00; Set A (our value) = 0.
[fa59]@_loop:
[fa59]06 ecASL Temp_Int24; Shift the low byte << 1;
Set C as the out going bit.
[fa5b]26 edROL Temp_Int24_M; Set mid byte = (mid << 1) | C
[fa5d]26 eeROL Temp_Int24_U; Set high byte = (high << 1) | C
[fa5f]2aROL A; Set A = (A << 1) | outgoing bit of high byte
;
; If the remainder (A) >= 10, subtract 10 and set the
; quotient bit (by incrementing
Temp_Int24).
;
[fa60]c9 0aCMP #$0a; Is A >= 10?
[fa62]90 04BCC @_finishLoopIter; Branch if not.
[fa64]e9 0aSBC #$0a; A >= 10, so subtract 10.
[fa66]e6 ecINC Temp_Int24; Set quotient bit to 1.
[fa68]@_finishLoopIter:
[fa68]88DEY; i-- (process next bit)
[fa69]d0 eeBNE @_loop; Loop until we hit 0.
[fa6b]60RTS
[fa6c]STATUS_BAR_PPU_ADDR_L:
[fa6c]63.byte $63; [0]: Power bar
[fa6d]43.byte $43; [1]: Magic bar
[fa6e]STATUS_BAR_FILL_TILE_INDEXES:
[fa6e]08.byte $08; [0]: Power bar
[fa6f]09.byte $09; [1]: Magic bar
[fa70]STATUS_BAR_PARTIAL_TILE_INDEXES:
[fa70]0c.byte $0c; [0]: Power bar
[fa71]0d.byte $0d; [1]: Magic bar
[fa72]STATUS_BAR_PARTIAL_PPU_ADDR_L:
[fa72]c0.byte $c0; [0]: Power bar
[fa73]d0.byte $d0; [1]: Magic bar
[fa74]60.byte $60; DEADCODE: Leftover byte. RTS? Extra bar type?
;============================================================================
; TODO: Document UI_DrawPlayerHPValue
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
IScriptAction_AddHP
;
Player_AddHP
;
UI_DrawPlayerHP
;============================================================================
[fa75]UI_DrawPlayerHPValue:
;
; Cap the amount of HP to 80, if over.
;
[fa75]c9 51CMP #$51
[fa77]90 02BCC @LAB_PRG15_MIRROR__fa7b
[fa79]a9 50LDA #$50
[fa7b]@LAB_PRG15_MIRROR__fa7b:
[fa7b]8d 99 03STA a:Temp_AddedHPValue
[fa7e]8d 31 04STA a:Player_HP_U
[fa81]a0 00LDY #$00
[fa83]f0 0bBEQ UI_SetHUDBarValue
;
; v-- Fall through --v
;
;============================================================================
; Set the MP value for the player.
;
; Once set, the magic bar will be drawn to reflect the new amount.
;
; INPUT:
; A:
; The MP to set.
;
; OUTPUT:
; None
;
; CALLS:
;
UI_SetHUDBarValue
;
; XREFS:
;
IScriptAction_AddMP
;
Player_AddMP
;
Player_ReduceMP
;
UI_DrawHUD
;============================================================================
[fa85]Player_SetMP:
;
; Cap the MP to 80.
;
[fa85]c9 51CMP #$51; Check if over 80 MP.
[fa87]90 02BCC @_setMP; If not over, we'll store what was passed in.
[fa89]a9 50LDA #$50; Cap to 80 MP.
;
; Proceed to set the MP on the player and then draw the
; Magic bar.
;
[fa8b]@_setMP:
[fa8b]8d 9a 03STA a:Player_MP; Set the player's current MP.
[fa8e]a0 01LDY #$01; Set 1 = Magic Bar and fall through to update.
;
; v-- Fall through --v
;
;============================================================================
; Set the Power or Magic bar's value.
;
; This fills up the bar to the specified value. It's done by
; considering three types of tiles:
;
; 1. Zero or more "fill" tiles
; 2. An optional single "partial fill" tile
; 3. Zero or more "empty" tiles
;
; The number of each type of tile needed is calculated. Any
; fill tiles will be drawn across the bar up to the maximum
; width, followed by a partial tile (if the value isn't at
; an even tile boundary), followed by any empty tiles needed
; to fill up the bar.
;
; There's a "partial fill" tile for each possible value,
; listed immediately after this function.
;
; INPUTS:
; A:
; The value to set for the bar.
;
; This will always be capped to the maximum value
; allowed by the bar.
;
; Y:
; The index of the bar.
;
; 0 = Power Bar
; 1 = Magic Bar
;
;
UI_MPAndHPBarWidth:
; The width of the MP/HP bar in pixels.
;
;
STATUS_BAR_PPU_ADDR_L:
; Table of lower PPU address bytes for drawing, by
; bar type.
;
;
STATUS_BAR_FILL_TILE_INDEXES:
; Indexes of the fill tiles in the PPU to set for
; drawing, by bar type.
;
;
STATUS_BAR_PARTIAL_TILE_INDEXES:
; Indexes of the partial fill tiles in the PPU to
; set for drawing, by bar type.
;
;
STATUS_BAR_PARTIAL_PPU_ADDR_L:
; Table of lower PPU address bytes for partial
; tiles, by bar type.
;
;
STATUS_BAR_PARTIAL_PPU_TILES:
; Start of the list of partial fill tiles.
;
; OUTPUTS:
;
PPUBuffer_WriteOffset:
; Next offset into the PPU buffer for writing.
;
;
PPU_TargetAddr:
;
PPU_TargetAddr+1:
;
Temp_Int24:
; $ee:
;
Temp_0xEF:
; Clobbered.
;
; CALLS:
;
Math_MultiplyBy16
;
PPUBuffer_QueueCommandOrLength
;
PPUBuffer_Set
;
; XREFS:
;
UI_DrawPlayerHPValue
;============================================================================
[fa90]UI_SetHUDBarValue:
;
; Cap the value to be <= the max for the bar.
;
; The cap is the bar's width * 8.
;
[fa90]85 eeSTA Temp_Int24_U; Store the value temporarily.
[fa92]ad ee 04LDA a:UI_MPAndHPBarWidth; Load the total width of the bar.
[fa95]0aASL A; Multiply by 8, yielding the max available tile-bound value.
[fa96]0aASL A
[fa97]0aASL A
[fa98]c5 eeCMP Temp_Int24_U; Is the passed-in value less than the max?
[fa9a]b0 02BCS @_setBar; If so, jump to draw.
[fa9c]85 eeSTA Temp_Int24_U; Else, cap to the max.
;
; Set the draw position for the bar to update. This will be
; based on the lower address from the
;
STATUS_BAR_PPU_ADDR_L
; lookup table.
;
; Bar 0 is the Mana bar.
; Bar 1 is the Power bar.
;
[fa9e]@_setBar:
[fa9e]84 efSTY Temp_0xEF; Set the bar index in a temp variable
[faa0]a9 20LDA #$20; Upper byte of the PPU address.
[faa2]85 e9STA PPU_TargetAddr_U; Set it.
[faa4]b9 6c faLDA a:STATUS_BAR_PPU_ADDR_L,Y; Lower byte of the PPU address, based on the bar index.
[faa7]85 e8STA PPU_TargetAddr; Set it.
;
; Set the number of tiles to draw as the bar width + 1.
;
[faa9]ae ee 04LDX a:UI_MPAndHPBarWidth; Load the width of the bar.
[faac]e8INX; Increment by 1.
[faad]8aTXA; A = result.
[faae]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Queue that as the number of tiles to draw.
;
; Calculate the numer of pixels to draw.
;
[fab1]a5 eeLDA Temp_Int24_U; Load the value to draw.
[fab3]4aLSR A; Multiply by 8 to get a tile-bound value.
[fab4]4aLSR A
[fab5]4aLSR A
[fab6]f0 0eBEQ @_drawPartialTile; If 0, jump.
[fab8]48PHA; Push the normalized value to the stack.
;
; Begin the loop for drawing fill tiles.
;
[fab9]85 ecSTA Temp_Int24; Store it as a temp variable.
[fabb]b9 6e faLDA a:STATUS_BAR_FILL_TILE_INDEXES,Y; Load the fill tile index to draw for this bar.
[fabe]@_fillTileLoop:
[fabe]20 45 f8JSR PPUBuffer_Set; Queue drawing the tile.
[fac1]c6 ecDEC Temp_Int24; Decrement the loop counter.
[fac3]d0 f9BNE @_fillTileLoop; If > 0, loop.
;
; The loop is complete.
;
; See if the bar is full. If not, the rest will need to be
; filled in.
;
[fac5]68PLA; Pop the normalized value.
[fac6]@_drawPartialTile:
[fac6]cd ee 04CMP a:UI_MPAndHPBarWidth; Was the bar maxed out?
[fac9]f0 17BEQ @_drawEndBarDeco; If so, jump.
;
; The bar was not maxed out. Draw a partial tile before
; the empty tiles.
;
; A partial tile has the fill in the left and empty space
; in the right.
;
[facb]48PHA; Else, push the normalized value back to the stack.
[facc]b9 70 faLDA a:STATUS_BAR_PARTIAL_TILE_INDEXES,Y; Load the partial tile index to draw for this bar.
[facf]20 45 f8JSR PPUBuffer_Set; Queue drawing the tile.
[fad2]68PLA; Pop the normalized value from the stack.
[fad3]a8TAY; Y = result.
;
; Now fill in the rest with empty tiles.
;
; This will be tile index 7 for either bar.
;
[fad4]a9 07LDA #$07
[fad6]@_emptyTileLoop:
[fad6]c8INY; Y++ (next value for the bar).
[fad7]cc ee 04CPY a:UI_MPAndHPBarWidth; Would this max out the bar?
[fada]f0 06BEQ @_drawEndBarDeco; If so, break out of the loop.
[fadc]20 45 f8JSR PPUBuffer_Set; Else, queue drawing the tile.
[fadf]4c d6 faJMP @_emptyTileLoop; Loop.
;
; Draw the ending decoration for the bar.
;
[fae2]@_drawEndBarDeco:
[fae2]a9 0bLDA #$0b; 0x0B == Ending bar decoration.
[fae4]20 45 f8JSR PPUBuffer_Set; Queue drawing it.
[fae7]86 20STX PPUBuffer_WriteOffset; X = resulting write offset.
;
; Next, the partial tile will be updated to fill in the
; number of pixels remaining in the bar value.
;
[fae9]a5 eeLDA Temp_Int24_U; Load the raw value to set for the bar.
[faeb]29 07AND #$07; Convert to a partial tile index below.
[faed]20 8c f7JSR Math_MultiplyBy16; Multiply by 16 (2 tiles).
[faf0]a8TAY; Y = result.
;
; Compute the PPU address where the partial tile resides
; for this bar.
;
[faf1]a9 10LDA #$10; Upper byte of PPU address.
[faf3]85 e9STA PPU_TargetAddr_U; Set it.
[faf5]a6 efLDX Temp_0xEF; Load the bar index.
[faf7]bd 72 faLDA a:STATUS_BAR_PARTIAL_PPU_ADDR_L,X; Lower byte of the PPU address, based on bar index.
[fafa]85 e8STA PPU_TargetAddr; Set it.
[fafc]a9 10LDA #$10; A = Length of 16 tiles (2 pattern draws).
[fafe]20 dc cfJSR PPUBuffer_QueueCommandOrLength; Set as the draw length.
;
; Figure out which bar is being drawn, so the appropriate
; partial tile can be updated.
;
[fb01]a5 efLDA Temp_0xEF; Load the bar index.
[fb03]d0 0fBNE @_updateMagicBarPartialTilePattern1Loop; If not the Power bar, jump.
;
; This is the Power bar. Queue drawing each tile in range.
;
[fb05]@_updatePowerBarPartialTileLoop:
[fb05]b9 2f fbLDA a:STATUS_BAR_PARTIAL_PPU_TILES,Y; Load the partial tile to set.
[fb08]c8INY; Y++ (next partial tile to draw).
[fb09]20 45 f8JSR PPUBuffer_Set; Queue drawing the loaded tile.
[fb0c]98TYA; A = Next tile index.
[fb0d]29 0fAND #$0f; Cap to 16.
[fb0f]d0 f4BNE @_updatePowerBarPartialTileLoop; If the tile index is within bounds (> 0, < 16), loop.
;
; We're done. Finish up and exit.
;
[fb11]86 20STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
[fb13]60RTS; And return.
;
; This is the Magic bar. Queue drawing each tile in range.
;
; Start with drawing pattern 1 based on pattern 2 in the
; selected tile. This is half-way through the tile. (Tiles
; consist of 2 patterns).
;
; Note that the second half of all these bar tiles are the same.
;
[fb14]@_updateMagicBarPartialTilePattern1Loop:
[fb14]b9 37 fbLDA a:STATUS_BAR_PARTIAL_PPU_TILES+8,Y; Load the partial tile to set.
[fb17]c8INY; Y++ (next partial tile to draw).
[fb18]20 45 f8JSR PPUBuffer_Set; Queue drawing the loaded tile.
[fb1b]98TYA; A = Next tile index.
[fb1c]29 07AND #$07; Cap to 8 (pattern length).
[fb1e]d0 f4BNE @_updateMagicBarPartialTilePattern1Loop; If the tile index is within bounds (> 0, < 8), loop.
[fb20]@_updateMagicBarPartialTilePattern2Loop:
[fb20]b9 27 fbLDA a:STATUS_BAR_PARTIAL_PPU_TILES-8,Y; Load the first half as pattern 1.
[fb23]c8INY; Y++ (next partial tile to draw).
[fb24]20 45 f8JSR PPUBuffer_Set; Queue drawing the loaded tile.
[fb27]@LAB_PRG15_MIRROR__fb27:
[fb27]98TYA; A = loop counter.
[fb28]29 07AND #$07; Cap to 8 (pattern length).
[fb2a]d0 f4BNE @_updateMagicBarPartialTilePattern2Loop; If the tile index is within bounds (> 0, < 8), loop.
;
; We're done. Finish up and exit.
;
[fb2c]86 20STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
[fb2e]60RTS; And return.
;============================================================================
; The collection of partial tiles for the Power/Magic bars.
;
; When drawing the Power bar, the tiles are used as normal.
; The first half is pattern 1, the second half is pattern 2.
;
; When drawing the Magic bar, that's reversed. Pattern 1
; comes from the second half, and pattern 2 comes from the
; first half.
;
; XREFS:
;
_updatePowerBarPartialTileLoop
; [$PRG15_MIRROR::fb05]
;============================================================================
;
; ........
; ████████
; ........
; ........
; ........
; ........
; ████████
; ........
;
; XREFS:
;
_updatePowerBarPartialTileLoop
; [$PRG15_MIRROR::fb05]
;
[fb2f]STATUS_BAR_PARTIAL_PPU_TILES:
[fb2f]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
[fb37]STATUS_BAR_PARTIAL_PPU_TILES_row0_1:
[fb37]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘.......
; â–‘.......
; â–‘.......
; â–‘.......
; ████████
; ........
;
[fb3f]00.byte $00; ........
[fb40]ff 80 80 80 80 ff 00 00.byte $ff,$80,$80,$80,$80,$ff,$00,$00; byte
[fb48]ff 00 00 00 00 ff 00.byte $ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘......
; â–‘â–‘......
; â–‘â–‘......
; â–‘â–‘......
; ████████
; ........
;
[fb4f]00 ff c0 c0 c0 c0 ff 00.byte $00,$ff,$c0,$c0,$c0,$c0,$ff,$00; byte
[fb57]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘â–‘.....
; â–‘â–‘â–‘.....
; â–‘â–‘â–‘.....
; â–‘â–‘â–‘.....
; ████████
; ........
;
[fb5f]00 ff e0 e0 e0 e0 ff 00.byte $00,$ff,$e0,$e0,$e0,$e0,$ff,$00; byte
[fb67]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘â–‘â–‘....
; â–‘â–‘â–‘â–‘....
; â–‘â–‘â–‘â–‘....
; â–‘â–‘â–‘â–‘....
; ████████
; ........
;
[fb6f]00 ff f0 f0 f0 f0 ff 00.byte $00,$ff,$f0,$f0,$f0,$f0,$ff,$00; byte
[fb77]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘â–‘â–‘â–‘...
; â–‘â–‘â–‘â–‘â–‘...
; â–‘â–‘â–‘â–‘â–‘...
; â–‘â–‘â–‘â–‘â–‘...
; ████████
; ........
;
[fb7f]00 ff f8 f8 f8 f8 ff 00.byte $00,$ff,$f8,$f8,$f8,$f8,$ff,$00; byte
[fb87]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘â–‘â–‘â–‘â–‘..
; â–‘â–‘â–‘â–‘â–‘â–‘..
; â–‘â–‘â–‘â–‘â–‘â–‘..
; â–‘â–‘â–‘â–‘â–‘â–‘..
; ████████
; ........
;
[fb8f]00 ff fc fc fc fc ff 00.byte $00,$ff,$fc,$fc,$fc,$fc,$ff,$00; byte
[fb97]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;
; ........
; ████████
; â–‘â–‘â–‘â–‘â–‘â–‘â–‘.
; â–‘â–‘â–‘â–‘â–‘â–‘â–‘.
; â–‘â–‘â–‘â–‘â–‘â–‘â–‘.
; â–‘â–‘â–‘â–‘â–‘â–‘â–‘.
; ████████
; ........
;
[fb9f]00 ff fe fe fe fe ff 00.byte $00,$ff,$fe,$fe,$fe,$fe,$ff,$00; byte
[fba7]00 ff 00 00 00 00 ff 00.byte $00,$ff,$00,$00,$00,$00,$ff,$00; byte
;============================================================================
; TODO: Document UI_DrawSelectedItem
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
UI_DrawHUDSprites
;============================================================================
[fbaf]UI_DrawSelectedItem:
[fbaf]a9 13LDA #$13
[fbb1]8d 06 20STA a:PPUADDR
[fbb4]a9 c0LDA #$c0
[fbb6]8d 06 20STA a:PPUADDR
[fbb9]ad c1 03LDA a:SelectedItem
[fbbc]10 07BPL @LAB_PRG15_MIRROR__fbc5
[fbbe]a0 40LDY #$40
[fbc0]a9 00LDA #$00
[fbc2]4c b2 fcJMP PPU_FillData
[fbc5]@LAB_PRG15_MIRROR__fbc5:
[fbc5]0aASL A
[fbc6]0aASL A
[fbc7]a8TAY
[fbc8]ad 00 01LDA a:CurrentROMBank
[fbcb]48PHA
[fbcc]a2 0aLDX #$0a
[fbce]20 1a ccJSR MMC1_UpdateROMBank
[fbd1]@LAB_PRG15_MIRROR__fbd1:
[fbd1]b9 e4 b4LDA a:ITEM_TILEMAP_INDEXES,Y
[fbd4]20 f0 fbJSR UI_Maybe_GetItemSpritePPUTileAddr
[fbd7]98TYA
[fbd8]48PHA
[fbd9]a0 00LDY #$00
[fbdb]@LAB_PRG15_MIRROR__fbdb:
[fbdb]b1 ecLDA (Temp_Int24),Y
[fbdd]8d 07 20STA a:PPUDATA
[fbe0]c8INY
[fbe1]c0 10CPY #$10
[fbe3]d0 f6BNE @LAB_PRG15_MIRROR__fbdb
[fbe5]68PLA
[fbe6]a8TAY
[fbe7]c8INY
[fbe8]98TYA
[fbe9]29 03AND #$03
[fbeb]d0 e4BNE @LAB_PRG15_MIRROR__fbd1
[fbed]4c d6 f3JMP MMC1_UpdatePRGBankToStackA
;============================================================================
; TODO: Document UI_Maybe_GetItemSpritePPUTileAddr
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_LoadItemSourceTiles
;
UI_DrawSelectedItem
;============================================================================
[fbf0]UI_Maybe_GetItemSpritePPUTileAddr:
[fbf0]85 edSTA Temp_Int24_M
[fbf2]a9 00LDA #$00
[fbf4]46 edLSR Temp_Int24_M
[fbf6]6aROR A
[fbf7]46 edLSR Temp_Int24_M
[fbf9]6aROR A
[fbfa]46 edLSR Temp_Int24_M
[fbfc]6aROR A
[fbfd]46 edLSR Temp_Int24_M
[fbff]6aROR A
[fc00]69 00ADC #$00
[fc02]85 ecSTA Temp_Int24
[fc04]a5 edLDA Temp_Int24_M
[fc06]69 85ADC #$85
[fc08]85 edSTA Temp_Int24_M
[fc0a]60RTS
;============================================================================
; TODO: Document Player_SetItem
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_Equip
;============================================================================
[fc0b]Player_SetItem:
[fc0b]8d c1 03STA a:SelectedItem
[fc0e]a2 13LDX #$13
[fc10]86 e9STX PPU_TargetAddr_U
[fc12]a2 c0LDX #$c0
[fc14]86 e8STX PPU_TargetAddr
[fc16]09 80ORA #$80
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_LoadItemSourceTiles
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_DrawItemImage
;============================================================================
[fc18]TextBox_LoadItemSourceTiles:
[fc18]48PHA
[fc19]20 85 f7JSR Player_GetInventoryIndexForItem
[fc1c]aaTAX
[fc1d]bd 5b fcLDA a:FUN_PRG15_MIRROR__fc0b__LOWER_ADDR_TABLE,X
[fc20]85 eeSTA Temp_Int24_U
[fc22]bd 60 fcLDA a:FUN_PRG15_MIRROR__fc0b__UPPER_ADDR_TABLE,X
[fc25]85 efSTA Temp_0xEF
[fc27]68PLA
[fc28]29 1fAND #$1f
[fc2a]0aASL A
[fc2b]0aASL A
[fc2c]a8TAY
[fc2d]ad 00 01LDA a:CurrentROMBank
[fc30]48PHA
[fc31]a2 0aLDX #$0a
[fc33]20 1a ccJSR MMC1_UpdateROMBank
[fc36]@LAB_PRG15_MIRROR__fc36:
[fc36]b1 eeLDA (Temp_Int24_U),Y
[fc38]20 f0 fbJSR UI_Maybe_GetItemSpritePPUTileAddr
[fc3b]98TYA
[fc3c]48PHA
[fc3d]a9 10LDA #$10
[fc3f]20 dc cfJSR PPUBuffer_QueueCommandOrLength
[fc42]a0 00LDY #$00
[fc44]@LAB_PRG15_MIRROR__fc44:
[fc44]20 42 f8JSR PPUBuffer_WriteFromTemp
[fc47]c0 10CPY #$10
[fc49]d0 f9BNE @LAB_PRG15_MIRROR__fc44
[fc4b]86 20STX PPUBuffer_WriteOffset
[fc4d]20 1e f8JSR PPU_IncrementAddrBy16
[fc50]68PLA
[fc51]a8TAY
[fc52]c8INY
[fc53]98TYA
[fc54]29 03AND #$03
[fc56]d0 deBNE @LAB_PRG15_MIRROR__fc36
[fc58]4c d6 f3JMP MMC1_UpdatePRGBankToStackA
[fc5b]FUN_PRG15_MIRROR__fc0b__LOWER_ADDR_TABLE:
[fc5b]a0.byte $a0; [0]:
[fc5c]b0.byte $b0; [1]:
[fc5d]c0.byte $c0; [2]:
[fc5e]d0.byte $d0; [3]:
[fc5f]e4.byte $e4; [4]:
[fc60]FUN_PRG15_MIRROR__fc0b__UPPER_ADDR_TABLE:
[fc60]b4.byte $b4; [0]:
[fc61]b4.byte $b4; [1]:
[fc62]b4.byte $b4; [2]:
[fc63]b4.byte $b4; [3]:
[fc64]b4.byte $b4; [4]:
[fc65]Game_ShowStartScreen:
[fc65]a2 ffLDX #$ff; X = 0xFF
[fc67]9aTXS; Store X in memory.
[fc68]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fc6b]0c.byte BANK_12_LOGIC; Bank = 12
[fc6c]20 9epointer StartScreen_Draw-1; Address = StartScreen_Draw
;
; Wait for a choice at the game's start screen.
;
[fc6e]@_waitForInput:
[fc6e]20 25 caJSR WaitForNextFrame; Wait for the next frame.
[fc71]20 4f cbJSR Screen_ResetSpritesForNonGame
[fc74]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fc77]0c.byte BANK_12_LOGIC; Bank = 12
[fc78]43 9fpointer StartScreen_CheckHandleInput-1; Address = StartScreen_CheckHandleInput
[fc7a]@_afterCheckHandleInputFarJump:
[fc7a]a5 19LDA Joy1_ChangedButtonMask; Check the changed controller 1 button mask.
[fc7c]29 10AND #$10; Was the Start button pressed?
[fc7e]f0 eeBEQ @_waitForInput; If not, loop.
;
; The player pressed Start. Check what they chose.
;
; But first, play the titlescreen music.
;
[fc80]a9 08LDA #$08
[fc82]85 faSTA Music_Current
[fc84]ad 87 06LDA a:DAT_0687; Check the chosen option.
[fc87]f0 0fBEQ @_startGame; If 0, they chose to start the game.
Else, fall through.
;
; The player chose to enter the Password screen.
;
; Switch to bank 12 and run
PasswordScreen_Show.
;
[fc89]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fc8c]0c.byte BANK_12_LOGIC; Bank = 12
[fc8d]9c 90pointer PasswordScreen_Show-1; Address = PasswordScreen_Show
[fc8f]@_afterPasswordScreenShow:
[fc8f]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fc92]0c.byte BANK_12_LOGIC; Bank = 12
[fc93]b0 95pointer Player_SetInitialExpAndGold-1; Address = Player_SetInitialExpAndGold
[fc95]@_afterSetExpGoldFarJump:
[fc95]4c 0a dbJMP Player_Spawn
[fc98]@_startGame:
[fc98]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fc9b]0c.byte BANK_12_LOGIC; Bank = 12
[fc9c]99 a7pointer SplashAnimation_RunIntro-1; Address = SplashAnimation_RunIntro
[fc9e]@_afterRunIntroFarJump:
[fc9e]20 59 f8JSR MMC1_LoadBankAndJump; Jump to:
[fca1]0c.byte BANK_12_LOGIC; Bank = 12
[fca2]6f 95pointer Player_SetStartGameState-1; Address = Player_SetStartGameState
;
; Begin the game.
;
[fca4]@_afterSetStartGameStateFarJump:
[fca4]4c 26 dbJMP Game_Start
[fca7]UI_DrawPromptInputSymbol:
[fca7]86 27STX Arg_DrawSprite_PosX; Set the X argument.
[fca9]84 28STY Arg_DrawSprite_PosY; Set the Y argument.
[fcab]a2 00LDX #$00
[fcad]86 29STX CurrentSprite_FlipMask; Clear the flip mask.
[fcaf]4c 57 f0JMP Sprite_SetAppearanceAddrFromOffset; Set the animation frame and offset of the sprite.
;============================================================================
; Fill the PPU with a single byte repeated `count` times.
;
; This will always write the value at least once and then
; decrement. If a count of 0 is passed, it will decrement
; to 0xFF, and then loop back down to 0.
;
; INPUTS:
; A:
; The value to write.
;
; Y:
; The number of times to write the value.
;
; If 0, this will write 256 times.
;
; OUTPUTS:
;
PPUDATA:
; This will be filled with values.
;
; XREFS:
;
PasswordScreen_Show
;
PPU_ClearAllTilemaps
;
PPU_FillData
;
UI_DrawHUDSprites
;
UI_DrawSelectedItem
;============================================================================
[fcb2]PPU_FillData:
[fcb2]8d 07 20STA a:PPUDATA; Store the value in PPUDATA.
[fcb5]88DEY; Y--
[fcb6]d0 faBNE PPU_FillData; If not 0, loop.
[fcb8]60RTS
;============================================================================
; Clear all tiles across all nametables.
;
; This will fill every tile with 0.
;
; INPUTS:
; None
;
; OUTPUTS:
;
PPUADDR:
; The updated address.
;
; CALLS:
;
PPU_FillData
;
; XREFS:
;
PasswordScreen_Show
;
StartScreen_Draw
;============================================================================
[fcb9]PPU_ClearAllTilemaps:
;
; Set the start position to the top-left ($2000).
;
[fcb9]a9 20LDA #$20
[fcbb]8d 06 20STA a:PPUADDR
[fcbe]a9 00LDA #$00
[fcc0]8d 06 20STA a:PPUADDR
;
; Prepare the value and loop counter.
;
[fcc3]a8TAY; Y = 0 (value to write)
[fcc4]a2 04LDX #$04; X = 4 (loop counter)
;
; Begin the loop.
;
[fcc6]@_loop:
[fcc6]20 b2 fcJSR PPU_FillData; Fill 256 bytes of data.
[fcc9]caDEX; X--
[fcca]d0 faBNE @_loop; If not 0, loop.
[fccc]60RTS
[fccd]bd.byte $bd; [0]:
[fcce]ff.byte $ff; [1]:
[fccf]ff.byte $ff; [2]:
[fcd0]ff.byte $ff; [3]:
[fcd1]ff.byte $ff; [4]:
[fcd2]ff.byte $ff; [5]:
[fcd3]ff.byte $ff; [6]:
[fcd4]ff.byte $ff; [7]:
[fcd5]ff.byte $ff; [8]:
[fcd6]ff.byte $ff; [9]:
[fcd7]ff.byte $ff; [10]:
[fcd8]ff.byte $ff; [11]:
[fcd9]ff.byte $ff; [12]:
[fcda]ff.byte $ff; [13]:
[fcdb]ff.byte $ff; [14]:
[fcdc]ff.byte $ff; [15]:
[fcdd]ff.byte $ff; [16]:
[fcde]ff.byte $ff; [17]:
[fcdf]ff.byte $ff; [18]:
[fce0]ff.byte $ff; [19]:
[fce1]ff.byte $ff; [20]:
[fce2]ff.byte $ff; [21]:
[fce3]ff.byte $ff; [22]:
[fce4]ff.byte $ff; [23]:
[fce5]ff.byte $ff; [24]:
[fce6]ff.byte $ff; [25]:
[fce7]ff.byte $ff; [26]:
[fce8]ff.byte $ff; [27]:
[fce9]ff.byte $ff; [28]:
[fcea]ff.byte $ff; [29]:
[fceb]ff.byte $ff; [30]:
[fcec]ff.byte $ff; [31]:
[fced]ff.byte $ff; [32]:
[fcee]ff.byte $ff; [33]:
[fcef]ff.byte $ff; [34]:
[fcf0]ff.byte $ff; [35]:
[fcf1]ff.byte $ff; [36]:
[fcf2]ff.byte $ff; [37]:
[fcf3]ff.byte $ff; [38]:
[fcf4]ff.byte $ff; [39]:
[fcf5]ff.byte $ff; [40]:
[fcf6]ff.byte $ff; [41]:
[fcf7]ff.byte $ff; [42]:
[fcf8]ff.byte $ff; [43]:
[fcf9]ff.byte $ff; [44]:
[fcfa]ff.byte $ff; [45]:
[fcfb]ff.byte $ff; [46]:
[fcfc]ff.byte $ff; [47]:
[fcfd]ff.byte $ff; [48]:
[fcfe]ff.byte $ff; [49]:
[fcff]ff.byte $ff; [50]:
[fd00]ff.byte $ff; [51]:
[fd01]ff.byte $ff; [52]:
[fd02]ff.byte $ff; [53]:
[fd03]ff.byte $ff; [54]:
[fd04]ff.byte $ff; [55]:
[fd05]ff.byte $ff; [56]:
[fd06]ff.byte $ff; [57]:
[fd07]ff.byte $ff; [58]:
[fd08]ff.byte $ff; [59]:
[fd09]ff.byte $ff; [60]:
[fd0a]ff.byte $ff; [61]:
[fd0b]ff.byte $ff; [62]:
[fd0c]ff.byte $ff; [63]:
[fd0d]ff.byte $ff; [64]:
[fd0e]ff.byte $ff; [65]:
[fd0f]ff.byte $ff; [66]:
[fd10]ff.byte $ff; [67]:
[fd11]ff.byte $ff; [68]:
[fd12]ff.byte $ff; [69]:
[fd13]ff.byte $ff; [70]:
[fd14]ff.byte $ff; [71]:
[fd15]ff.byte $ff; [72]:
[fd16]ff.byte $ff; [73]:
[fd17]ff.byte $ff; [74]:
[fd18]ff.byte $ff; [75]:
[fd19]ff.byte $ff; [76]:
[fd1a]ff.byte $ff; [77]:
[fd1b]ff.byte $ff; [78]:
[fd1c]ff.byte $ff; [79]:
[fd1d]ff.byte $ff; [80]:
[fd1e]ff.byte $ff; [81]:
[fd1f]ff.byte $ff; [82]:
[fd20]ff.byte $ff; [83]:
[fd21]ff.byte $ff; [84]:
[fd22]ff.byte $ff; [85]:
[fd23]ff.byte $ff; [86]:
[fd24]ff.byte $ff; [87]:
[fd25]ff.byte $ff; [88]:
[fd26]ff.byte $ff; [89]:
[fd27]ff.byte $ff; [90]:
[fd28]ff.byte $ff; [91]:
[fd29]ff.byte $ff; [92]:
[fd2a]ff.byte $ff; [93]:
[fd2b]ff.byte $ff; [94]:
[fd2c]ff.byte $ff; [95]:
[fd2d]ff.byte $ff; [96]:
[fd2e]ff.byte $ff; [97]:
[fd2f]ff.byte $ff; [98]:
[fd30]ff.byte $ff; [99]:
[fd31]ff.byte $ff; [100]:
[fd32]ff.byte $ff; [101]:
[fd33]ff.byte $ff; [102]:
[fd34]ff.byte $ff; [103]:
[fd35]ff.byte $ff; [104]:
[fd36]ff.byte $ff; [105]:
[fd37]ff.byte $ff; [106]:
[fd38]ff.byte $ff; [107]:
[fd39]ff.byte $ff; [108]:
[fd3a]ff.byte $ff; [109]:
[fd3b]ff.byte $ff; [110]:
[fd3c]ff.byte $ff; [111]:
[fd3d]ff.byte $ff; [112]:
[fd3e]ff.byte $ff; [113]:
[fd3f]ff.byte $ff; [114]:
[fd40]ff.byte $ff; [115]:
[fd41]ff.byte $ff; [116]:
[fd42]ff.byte $ff; [117]:
[fd43]ff.byte $ff; [118]:
[fd44]ff.byte $ff; [119]:
[fd45]ff.byte $ff; [120]:
[fd46]ff.byte $ff; [121]:
[fd47]ff.byte $ff; [122]:
[fd48]ff.byte $ff; [123]:
[fd49]ff.byte $ff; [124]:
[fd4a]ff.byte $ff; [125]:
[fd4b]ff.byte $ff; [126]:
[fd4c]ff.byte $ff; [127]:
[fd4d]ff.byte $ff; [128]:
[fd4e]ff.byte $ff; [129]:
[fd4f]ff.byte $ff; [130]:
[fd50]ff.byte $ff; [131]:
[fd51]ff.byte $ff; [132]:
[fd52]ff.byte $ff; [133]:
[fd53]ff.byte $ff; [134]:
[fd54]ff.byte $ff; [135]:
[fd55]ff.byte $ff; [136]:
[fd56]ff.byte $ff; [137]:
[fd57]ff.byte $ff; [138]:
[fd58]ff.byte $ff; [139]:
[fd59]ff.byte $ff; [140]:
[fd5a]ff.byte $ff; [141]:
[fd5b]ff.byte $ff; [142]:
[fd5c]ff.byte $ff; [143]:
[fd5d]ff.byte $ff; [144]:
[fd5e]ff.byte $ff; [145]:
[fd5f]ff.byte $ff; [146]:
[fd60]ff.byte $ff; [147]:
[fd61]ff.byte $ff; [148]:
[fd62]ff.byte $ff; [149]:
[fd63]ff.byte $ff; [150]:
[fd64]ff.byte $ff; [151]:
[fd65]ff.byte $ff; [152]:
[fd66]ff.byte $ff; [153]:
[fd67]ff.byte $ff; [154]:
[fd68]ff.byte $ff; [155]:
[fd69]ff.byte $ff; [156]:
[fd6a]ff.byte $ff; [157]:
[fd6b]ff.byte $ff; [158]:
[fd6c]ff.byte $ff; [159]:
[fd6d]ff.byte $ff; [160]:
[fd6e]ff.byte $ff; [161]:
[fd6f]ff.byte $ff; [162]:
[fd70]ff.byte $ff; [163]:
[fd71]ff.byte $ff; [164]:
[fd72]ff.byte $ff; [165]:
[fd73]ff.byte $ff; [166]:
[fd74]ff.byte $ff; [167]:
[fd75]ff.byte $ff; [168]:
[fd76]ff.byte $ff; [169]:
[fd77]ff.byte $ff; [170]:
[fd78]ff.byte $ff; [171]:
[fd79]ff.byte $ff; [172]:
[fd7a]ff.byte $ff; [173]:
[fd7b]ff.byte $ff; [174]:
[fd7c]ff.byte $ff; [175]:
[fd7d]ff.byte $ff; [176]:
[fd7e]ff.byte $ff; [177]:
[fd7f]ff.byte $ff; [178]:
[fd80]ff.byte $ff; [179]:
[fd81]ff.byte $ff; [180]:
[fd82]ff.byte $ff; [181]:
[fd83]ff.byte $ff; [182]:
[fd84]ff.byte $ff; [183]:
[fd85]ff.byte $ff; [184]:
[fd86]ff.byte $ff; [185]:
[fd87]ff.byte $ff; [186]:
[fd88]ff.byte $ff; [187]:
[fd89]ff.byte $ff; [188]:
[fd8a]ff.byte $ff; [189]:
[fd8b]ff.byte $ff; [190]:
[fd8c]ff.byte $ff; [191]:
[fd8d]ff.byte $ff; [192]:
[fd8e]ff.byte $ff; [193]:
[fd8f]ff.byte $ff; [194]:
[fd90]ff.byte $ff; [195]:
[fd91]ff.byte $ff; [196]:
[fd92]ff.byte $ff; [197]:
[fd93]ff.byte $ff; [198]:
[fd94]ff.byte $ff; [199]:
[fd95]ff.byte $ff; [200]:
[fd96]ff.byte $ff; [201]:
[fd97]ff.byte $ff; [202]:
[fd98]ff.byte $ff; [203]:
[fd99]ff.byte $ff; [204]:
[fd9a]ff.byte $ff; [205]:
[fd9b]ff.byte $ff; [206]:
[fd9c]ff.byte $ff; [207]:
[fd9d]ff.byte $ff; [208]:
[fd9e]ff.byte $ff; [209]:
[fd9f]ff.byte $ff; [210]:
[fda0]ff.byte $ff; [211]:
[fda1]ff.byte $ff; [212]:
[fda2]ff.byte $ff; [213]:
[fda3]ff.byte $ff; [214]:
[fda4]ff.byte $ff; [215]:
[fda5]ff.byte $ff; [216]:
[fda6]ff.byte $ff; [217]:
[fda7]ff.byte $ff; [218]:
[fda8]ff.byte $ff; [219]:
[fda9]ff.byte $ff; [220]:
[fdaa]ff.byte $ff; [221]:
[fdab]ff.byte $ff; [222]:
[fdac]ff.byte $ff; [223]:
[fdad]ff.byte $ff; [224]:
[fdae]ff.byte $ff; [225]:
[fdaf]ff.byte $ff; [226]:
[fdb0]ff.byte $ff; [227]:
[fdb1]ff.byte $ff; [228]:
[fdb2]ff.byte $ff; [229]:
[fdb3]ff.byte $ff; [230]:
[fdb4]ff.byte $ff; [231]:
[fdb5]ff.byte $ff; [232]:
[fdb6]ff.byte $ff; [233]:
[fdb7]ff.byte $ff; [234]:
[fdb8]ff.byte $ff; [235]:
[fdb9]ff.byte $ff; [236]:
[fdba]ff.byte $ff; [237]:
[fdbb]ff.byte $ff; [238]:
[fdbc]ff.byte $ff; [239]:
[fdbd]ff.byte $ff; [240]:
[fdbe]ff.byte $ff; [241]:
[fdbf]ff.byte $ff; [242]:
[fdc0]ff.byte $ff; [243]:
[fdc1]ff.byte $ff; [244]:
[fdc2]ff.byte $ff; [245]:
[fdc3]ff.byte $ff; [246]:
[fdc4]ff.byte $ff; [247]:
[fdc5]ff.byte $ff; [248]:
[fdc6]ff.byte $ff; [249]:
[fdc7]ff.byte $ff; [250]:
[fdc8]ff.byte $ff; [251]:
[fdc9]ff.byte $ff; [252]:
[fdca]ff.byte $ff; [253]:
[fdcb]ff.byte $ff; [254]:
[fdcc]ff.byte $ff; [255]:
[fdcd]ff.byte $ff; [256]:
[fdce]ff.byte $ff; [257]:
[fdcf]ff.byte $ff; [258]:
[fdd0]ff.byte $ff; [259]:
[fdd1]ff.byte $ff; [260]:
[fdd2]ff.byte $ff; [261]:
[fdd3]ff.byte $ff; [262]:
[fdd4]ff.byte $ff; [263]:
[fdd5]ff.byte $ff; [264]:
[fdd6]ff.byte $ff; [265]:
[fdd7]ff.byte $ff; [266]:
[fdd8]ff.byte $ff; [267]:
[fdd9]ff.byte $ff; [268]:
[fdda]ff.byte $ff; [269]:
[fddb]ff.byte $ff; [270]:
[fddc]ff.byte $ff; [271]:
[fddd]ff.byte $ff; [272]:
[fdde]ff.byte $ff; [273]:
[fddf]ff.byte $ff; [274]:
[fde0]ff.byte $ff; [275]:
[fde1]ff.byte $ff; [276]:
[fde2]ff.byte $ff; [277]:
[fde3]ff.byte $ff; [278]:
[fde4]ff.byte $ff; [279]:
[fde5]ff.byte $ff; [280]:
[fde6]ff.byte $ff; [281]:
[fde7]ff.byte $ff; [282]:
[fde8]ff.byte $ff; [283]:
[fde9]ff.byte $ff; [284]:
[fdea]ff.byte $ff; [285]:
[fdeb]ff.byte $ff; [286]:
[fdec]ff.byte $ff; [287]:
[fded]ff.byte $ff; [288]:
[fdee]ff.byte $ff; [289]:
[fdef]ff.byte $ff; [290]:
[fdf0]ff.byte $ff; [291]:
[fdf1]ff.byte $ff; [292]:
[fdf2]ff.byte $ff; [293]:
[fdf3]ff.byte $ff; [294]:
[fdf4]ff.byte $ff; [295]:
[fdf5]ff.byte $ff; [296]:
[fdf6]ff.byte $ff; [297]:
[fdf7]ff.byte $ff; [298]:
[fdf8]ff.byte $ff; [299]:
[fdf9]ff.byte $ff; [300]:
[fdfa]ff.byte $ff; [301]:
[fdfb]ff.byte $ff; [302]:
[fdfc]ff.byte $ff; [303]:
[fdfd]ff.byte $ff; [304]:
[fdfe]ff.byte $ff; [305]:
[fdff]ff.byte $ff; [306]:
[fe00]ff.byte $ff; [307]:
[fe01]ff.byte $ff; [308]:
[fe02]ff.byte $ff; [309]:
[fe03]ff.byte $ff; [310]:
[fe04]ff.byte $ff; [311]:
[fe05]ff.byte $ff; [312]:
[fe06]ff.byte $ff; [313]:
[fe07]ff.byte $ff; [314]:
[fe08]ff.byte $ff; [315]:
[fe09]ff.byte $ff; [316]:
[fe0a]ff.byte $ff; [317]:
[fe0b]ff.byte $ff; [318]:
[fe0c]ff.byte $ff; [319]:
[fe0d]ff.byte $ff; [320]:
[fe0e]ff.byte $ff; [321]:
[fe0f]ff.byte $ff; [322]:
[fe10]ff.byte $ff; [323]:
[fe11]ff.byte $ff; [324]:
[fe12]ff.byte $ff; [325]:
[fe13]ff.byte $ff; [326]:
[fe14]ff.byte $ff; [327]:
[fe15]ff.byte $ff; [328]:
[fe16]ff.byte $ff; [329]:
[fe17]ff.byte $ff; [330]:
[fe18]ff.byte $ff; [331]:
[fe19]ff.byte $ff; [332]:
[fe1a]ff.byte $ff; [333]:
[fe1b]ff.byte $ff; [334]:
[fe1c]ff.byte $ff; [335]:
[fe1d]ff.byte $ff; [336]:
[fe1e]ff.byte $ff; [337]:
[fe1f]ff.byte $ff; [338]:
[fe20]ff.byte $ff; [339]:
[fe21]ff.byte $ff; [340]:
[fe22]ff.byte $ff; [341]:
[fe23]ff.byte $ff; [342]:
[fe24]ff.byte $ff; [343]:
[fe25]ff.byte $ff; [344]:
[fe26]ff.byte $ff; [345]:
[fe27]ff.byte $ff; [346]:
[fe28]ff.byte $ff; [347]:
[fe29]ff.byte $ff; [348]:
[fe2a]ff.byte $ff; [349]:
[fe2b]ff.byte $ff; [350]:
[fe2c]ff.byte $ff; [351]:
[fe2d]ff.byte $ff; [352]:
[fe2e]ff.byte $ff; [353]:
[fe2f]ff.byte $ff; [354]:
[fe30]ff.byte $ff; [355]:
[fe31]ff.byte $ff; [356]:
[fe32]ff.byte $ff; [357]:
[fe33]ff.byte $ff; [358]:
[fe34]ff.byte $ff; [359]:
[fe35]ff.byte $ff; [360]:
[fe36]ff.byte $ff; [361]:
[fe37]ff.byte $ff; [362]:
[fe38]ff.byte $ff; [363]:
[fe39]ff.byte $ff; [364]:
[fe3a]ff.byte $ff; [365]:
[fe3b]ff.byte $ff; [366]:
[fe3c]ff.byte $ff; [367]:
[fe3d]ff.byte $ff; [368]:
[fe3e]ff.byte $ff; [369]:
[fe3f]ff.byte $ff; [370]:
[fe40]ff.byte $ff; [371]:
[fe41]ff.byte $ff; [372]:
[fe42]ff.byte $ff; [373]:
[fe43]ff.byte $ff; [374]:
[fe44]ff.byte $ff; [375]:
[fe45]ff.byte $ff; [376]:
[fe46]ff.byte $ff; [377]:
[fe47]ff.byte $ff; [378]:
[fe48]ff.byte $ff; [379]:
[fe49]ff.byte $ff; [380]:
[fe4a]ff.byte $ff; [381]:
[fe4b]ff.byte $ff; [382]:
[fe4c]ff.byte $ff; [383]:
[fe4d]ff.byte $ff; [384]:
[fe4e]ff.byte $ff; [385]:
[fe4f]ff.byte $ff; [386]:
[fe50]ff.byte $ff; [387]:
[fe51]ff.byte $ff; [388]:
[fe52]ff.byte $ff; [389]:
[fe53]ff.byte $ff; [390]:
[fe54]ff.byte $ff; [391]:
[fe55]ff.byte $ff; [392]:
[fe56]ff.byte $ff; [393]:
[fe57]ff.byte $ff; [394]:
[fe58]ff.byte $ff; [395]:
[fe59]ff.byte $ff; [396]:
[fe5a]ff.byte $ff; [397]:
[fe5b]ff.byte $ff; [398]:
[fe5c]ff.byte $ff; [399]:
[fe5d]ff.byte $ff; [400]:
[fe5e]ff.byte $ff; [401]:
[fe5f]ff.byte $ff; [402]:
[fe60]ff.byte $ff; [403]:
[fe61]ff.byte $ff; [404]:
[fe62]ff.byte $ff; [405]:
[fe63]ff.byte $ff; [406]:
[fe64]ff.byte $ff; [407]:
[fe65]ff.byte $ff; [408]:
[fe66]ff.byte $ff; [409]:
[fe67]ff.byte $ff; [410]:
[fe68]ff.byte $ff; [411]:
[fe69]ff.byte $ff; [412]:
[fe6a]ff.byte $ff; [413]:
[fe6b]ff.byte $ff; [414]:
[fe6c]ff.byte $ff; [415]:
[fe6d]ff.byte $ff; [416]:
[fe6e]ff.byte $ff; [417]:
[fe6f]ff.byte $ff; [418]:
[fe70]ff.byte $ff; [419]:
[fe71]ff.byte $ff; [420]:
[fe72]ff.byte $ff; [421]:
[fe73]ff.byte $ff; [422]:
[fe74]ff.byte $ff; [423]:
[fe75]ff.byte $ff; [424]:
[fe76]ff.byte $ff; [425]:
[fe77]ff.byte $ff; [426]:
[fe78]ff.byte $ff; [427]:
[fe79]ff.byte $ff; [428]:
[fe7a]ff.byte $ff; [429]:
[fe7b]ff.byte $ff; [430]:
[fe7c]ff.byte $ff; [431]:
[fe7d]ff.byte $ff; [432]:
[fe7e]ff.byte $ff; [433]:
[fe7f]ff.byte $ff; [434]:
[fe80]ff.byte $ff; [435]:
[fe81]ff.byte $ff; [436]:
[fe82]ff.byte $ff; [437]:
[fe83]ff.byte $ff; [438]:
[fe84]ff.byte $ff; [439]:
[fe85]ff.byte $ff; [440]:
[fe86]ff.byte $ff; [441]:
[fe87]ff.byte $ff; [442]:
[fe88]ff.byte $ff; [443]:
[fe89]ff.byte $ff; [444]:
[fe8a]ff.byte $ff; [445]:
[fe8b]ff.byte $ff; [446]:
[fe8c]ff.byte $ff; [447]:
[fe8d]ff.byte $ff; [448]:
[fe8e]ff.byte $ff; [449]:
[fe8f]ff.byte $ff; [450]:
[fe90]ff.byte $ff; [451]:
[fe91]ff.byte $ff; [452]:
[fe92]ff.byte $ff; [453]:
[fe93]ff.byte $ff; [454]:
[fe94]ff.byte $ff; [455]:
[fe95]ff.byte $ff; [456]:
[fe96]ff.byte $ff; [457]:
[fe97]ff.byte $ff; [458]:
[fe98]ff.byte $ff; [459]:
[fe99]ff.byte $ff; [460]:
[fe9a]ff.byte $ff; [461]:
[fe9b]ff.byte $ff; [462]:
[fe9c]ff.byte $ff; [463]:
[fe9d]ff.byte $ff; [464]:
[fe9e]ff.byte $ff; [465]:
[fe9f]ff.byte $ff; [466]:
[fea0]ff.byte $ff; [467]:
[fea1]ff.byte $ff; [468]:
[fea2]ff.byte $ff; [469]:
[fea3]ff.byte $ff; [470]:
[fea4]ff.byte $ff; [471]:
[fea5]ff.byte $ff; [472]:
[fea6]ff.byte $ff; [473]:
[fea7]ff.byte $ff; [474]:
[fea8]ff.byte $ff; [475]:
[fea9]ff.byte $ff; [476]:
[feaa]ff.byte $ff; [477]:
[feab]ff.byte $ff; [478]:
[feac]ff.byte $ff; [479]:
[fead]ff.byte $ff; [480]:
[feae]ff.byte $ff; [481]:
[feaf]ff.byte $ff; [482]:
[feb0]ff.byte $ff; [483]:
[feb1]ff.byte $ff; [484]:
[feb2]ff.byte $ff; [485]:
[feb3]ff.byte $ff; [486]:
[feb4]ff.byte $ff; [487]:
[feb5]ff.byte $ff; [488]:
[feb6]ff.byte $ff; [489]:
[feb7]ff.byte $ff; [490]:
[feb8]ff.byte $ff; [491]:
[feb9]ff.byte $ff; [492]:
[feba]ff.byte $ff; [493]:
[febb]ff.byte $ff; [494]:
[febc]ff.byte $ff; [495]:
[febd]ff.byte $ff; [496]:
[febe]ff.byte $ff; [497]:
[febf]ff.byte $ff; [498]:
[fec0]ff.byte $ff; [499]:
[fec1]ff.byte $ff; [500]:
[fec2]ff.byte $ff; [501]:
[fec3]ff.byte $ff; [502]:
[fec4]ff.byte $ff; [503]:
[fec5]ff.byte $ff; [504]:
[fec6]ff.byte $ff; [505]:
[fec7]ff.byte $ff; [506]:
[fec8]ff.byte $ff; [507]:
[fec9]ff.byte $ff; [508]:
[feca]ff.byte $ff; [509]:
[fecb]ff.byte $ff; [510]:
[fecc]ff.byte $ff; [511]:
[fecd]ff.byte $ff; [512]:
[fece]ff.byte $ff; [513]:
[fecf]ff.byte $ff; [514]:
[fed0]ff.byte $ff; [515]:
[fed1]ff.byte $ff; [516]:
[fed2]ff.byte $ff; [517]:
[fed3]ff.byte $ff; [518]:
[fed4]ff.byte $ff; [519]:
[fed5]ff.byte $ff; [520]:
[fed6]ff.byte $ff; [521]:
[fed7]ff.byte $ff; [522]:
[fed8]ff.byte $ff; [523]:
[fed9]ff.byte $ff; [524]:
[feda]ff.byte $ff; [525]:
[fedb]ff.byte $ff; [526]:
[fedc]ff.byte $ff; [527]:
[fedd]ff.byte $ff; [528]:
[fede]ff.byte $ff; [529]:
[fedf]ff.byte $ff; [530]:
[fee0]ff.byte $ff; [531]:
[fee1]ff.byte $ff; [532]:
[fee2]ff.byte $ff; [533]:
[fee3]ff.byte $ff; [534]:
[fee4]ff.byte $ff; [535]:
[fee5]ff.byte $ff; [536]:
[fee6]ff.byte $ff; [537]:
[fee7]ff.byte $ff; [538]:
[fee8]ff.byte $ff; [539]:
[fee9]ff.byte $ff; [540]:
[feea]ff.byte $ff; [541]:
[feeb]ff.byte $ff; [542]:
[feec]ff.byte $ff; [543]:
[feed]ff.byte $ff; [544]:
[feee]ff.byte $ff; [545]:
[feef]ff.byte $ff; [546]:
[fef0]ff.byte $ff; [547]:
[fef1]ff.byte $ff; [548]:
[fef2]ff.byte $ff; [549]:
[fef3]ff.byte $ff; [550]:
[fef4]ff.byte $ff; [551]:
[fef5]ff.byte $ff; [552]:
[fef6]ff.byte $ff; [553]:
[fef7]ff.byte $ff; [554]:
[fef8]ff.byte $ff; [555]:
[fef9]ff.byte $ff; [556]:
[fefa]ff.byte $ff; [557]:
[fefb]ff.byte $ff; [558]:
[fefc]ff.byte $ff; [559]:
[fefd]ff.byte $ff; [560]:
[fefe]ff.byte $ff; [561]:
[feff]ff.byte $ff; [562]:
[ff00]ff.byte $ff; [563]:
[ff01]ff.byte $ff; [564]:
[ff02]ff.byte $ff; [565]:
[ff03]ff.byte $ff; [566]:
[ff04]ff.byte $ff; [567]:
[ff05]ff.byte $ff; [568]:
[ff06]ff.byte $ff; [569]:
[ff07]ff.byte $ff; [570]:
[ff08]ff.byte $ff; [571]:
[ff09]ff.byte $ff; [572]:
[ff0a]ff.byte $ff; [573]:
[ff0b]ff.byte $ff; [574]:
[ff0c]ff.byte $ff; [575]:
[ff0d]ff.byte $ff; [576]:
[ff0e]ff.byte $ff; [577]:
[ff0f]ff.byte $ff; [578]:
[ff10]ff.byte $ff; [579]:
[ff11]ff.byte $ff; [580]:
[ff12]ff.byte $ff; [581]:
[ff13]ff.byte $ff; [582]:
[ff14]ff.byte $ff; [583]:
[ff15]ff.byte $ff; [584]:
[ff16]ff.byte $ff; [585]:
[ff17]ff.byte $ff; [586]:
[ff18]ff.byte $ff; [587]:
[ff19]ff.byte $ff; [588]:
[ff1a]ff.byte $ff; [589]:
[ff1b]ff.byte $ff; [590]:
[ff1c]ff.byte $ff; [591]:
[ff1d]ff.byte $ff; [592]:
[ff1e]ff.byte $ff; [593]:
[ff1f]ff.byte $ff; [594]:
[ff20]ff.byte $ff; [595]:
[ff21]ff.byte $ff; [596]:
[ff22]ff.byte $ff; [597]:
[ff23]ff.byte $ff; [598]:
[ff24]ff.byte $ff; [599]:
[ff25]ff.byte $ff; [600]:
[ff26]ff.byte $ff; [601]:
[ff27]ff.byte $ff; [602]:
[ff28]ff.byte $ff; [603]:
[ff29]ff.byte $ff; [604]:
[ff2a]ff.byte $ff; [605]:
[ff2b]ff.byte $ff; [606]:
[ff2c]ff.byte $ff; [607]:
[ff2d]ff.byte $ff; [608]:
[ff2e]ff.byte $ff; [609]:
[ff2f]ff.byte $ff; [610]:
[ff30]ff.byte $ff; [611]:
[ff31]ff.byte $ff; [612]:
[ff32]ff.byte $ff; [613]:
[ff33]ff.byte $ff; [614]:
[ff34]ff.byte $ff; [615]:
[ff35]ff.byte $ff; [616]:
[ff36]ff.byte $ff; [617]:
[ff37]ff.byte $ff; [618]:
[ff38]ff.byte $ff; [619]:
[ff39]ff.byte $ff; [620]:
[ff3a]ff.byte $ff; [621]:
[ff3b]ff.byte $ff; [622]:
[ff3c]ff.byte $ff; [623]:
[ff3d]ff.byte $ff; [624]:
[ff3e]ff.byte $ff; [625]:
[ff3f]ff.byte $ff; [626]:
[ff40]ff.byte $ff; [627]:
[ff41]ff.byte $ff; [628]:
[ff42]ff.byte $ff; [629]:
[ff43]ff.byte $ff; [630]:
[ff44]ff.byte $ff; [631]:
[ff45]ff.byte $ff; [632]:
[ff46]ff.byte $ff; [633]:
[ff47]ff.byte $ff; [634]:
[ff48]ff.byte $ff; [635]:
[ff49]ff.byte $ff; [636]:
[ff4a]ff.byte $ff; [637]:
[ff4b]ff.byte $ff; [638]:
[ff4c]ff.byte $ff; [639]:
[ff4d]ff.byte $ff; [640]:
[ff4e]ff.byte $ff; [641]:
[ff4f]ff.byte $ff; [642]:
[ff50]ff.byte $ff; [643]:
[ff51]ff.byte $ff; [644]:
[ff52]ff.byte $ff; [645]:
[ff53]ff.byte $ff; [646]:
[ff54]ff.byte $ff; [647]:
[ff55]ff.byte $ff; [648]:
[ff56]ff.byte $ff; [649]:
[ff57]ff.byte $ff; [650]:
[ff58]ff.byte $ff; [651]:
[ff59]ff.byte $ff; [652]:
[ff5a]ff.byte $ff; [653]:
[ff5b]ff.byte $ff; [654]:
[ff5c]ff.byte $ff; [655]:
[ff5d]ff.byte $ff; [656]:
[ff5e]ff.byte $ff; [657]:
[ff5f]ff.byte $ff; [658]:
[ff60]ff.byte $ff; [659]:
[ff61]ff.byte $ff; [660]:
[ff62]ff.byte $ff; [661]:
[ff63]ff.byte $ff; [662]:
[ff64]ff.byte $ff; [663]:
[ff65]ff.byte $ff; [664]:
[ff66]ff.byte $ff; [665]:
[ff67]ff.byte $ff; [666]:
[ff68]ff.byte $ff; [667]:
[ff69]ff.byte $ff; [668]:
[ff6a]ff.byte $ff; [669]:
[ff6b]ff.byte $ff; [670]:
[ff6c]ff.byte $ff; [671]:
[ff6d]ff.byte $ff; [672]:
[ff6e]ff.byte $ff; [673]:
[ff6f]ff.byte $ff; [674]:
[ff70]ff.byte $ff; [675]:
[ff71]ff.byte $ff; [676]:
[ff72]ff.byte $ff; [677]:
[ff73]ff.byte $ff; [678]:
[ff74]ff.byte $ff; [679]:
[ff75]ff.byte $ff; [680]:
[ff76]ff.byte $ff; [681]:
[ff77]ff.byte $ff; [682]:
[ff78]ff.byte $ff; [683]:
[ff79]ff.byte $ff; [684]:
[ff7a]ff.byte $ff; [685]:
[ff7b]ff.byte $ff; [686]:
[ff7c]ff.byte $ff; [687]:
[ff7d]ff.byte $ff; [688]:
[ff7e]ff.byte $ff; [689]:
[ff7f]ff.byte $ff; [690]:
[ff80]ff.byte $ff; [691]:
[ff81]ff.byte $ff; [692]:
[ff82]ff.byte $ff; [693]:
[ff83]ff.byte $ff; [694]:
[ff84]ff.byte $ff; [695]:
[ff85]ff.byte $ff; [696]:
[ff86]ff.byte $ff; [697]:
[ff87]ff.byte $ff; [698]:
[ff88]ff.byte $ff; [699]:
[ff89]ff.byte $ff; [700]:
[ff8a]ff.byte $ff; [701]:
[ff8b]ff.byte $ff; [702]:
[ff8c]ff.byte $ff; [703]:
[ff8d]ff.byte $ff; [704]:
[ff8e]ff.byte $ff; [705]:
[ff8f]ff.byte $ff; [706]:
[ff90]ff.byte $ff; [707]:
[ff91]ff.byte $ff; [708]:
[ff92]ff.byte $ff; [709]:
[ff93]ff.byte $ff; [710]:
[ff94]ff.byte $ff; [711]:
[ff95]ff.byte $ff; [712]:
[ff96]ff.byte $ff; [713]:
[ff97]ff.byte $ff; [714]:
[ff98]ff.byte $ff; [715]:
[ff99]ff.byte $ff; [716]:
[ff9a]ff.byte $ff; [717]:
[ff9b]ff.byte $ff; [718]:
[ff9c]ff.byte $ff; [719]:
[ff9d]ff.byte $ff; [720]:
[ff9e]ff.byte $ff; [721]:
[ff9f]ff.byte $ff; [722]:
[ffa0]ff.byte $ff; [723]:
[ffa1]ff.byte $ff; [724]:
[ffa2]ff.byte $ff; [725]:
[ffa3]ff.byte $ff; [726]:
[ffa4]ff.byte $ff; [727]:
[ffa5]ff.byte $ff; [728]:
[ffa6]ff.byte $ff; [729]:
[ffa7]ff.byte $ff; [730]:
[ffa8]ff.byte $ff; [731]:
[ffa9]ff.byte $ff; [732]:
[ffaa]ff.byte $ff; [733]:
[ffab]ff.byte $ff; [734]:
[ffac]ff.byte $ff; [735]:
[ffad]ff.byte $ff; [736]:
[ffae]ff.byte $ff; [737]:
[ffaf]ff.byte $ff; [738]:
[ffb0]ff.byte $ff; [739]:
[ffb1]ff.byte $ff; [740]:
[ffb2]ff.byte $ff; [741]:
[ffb3]ff.byte $ff; [742]:
[ffb4]ff.byte $ff; [743]:
[ffb5]ff.byte $ff; [744]:
[ffb6]ff.byte $ff; [745]:
[ffb7]ff.byte $ff; [746]:
[ffb8]ff.byte $ff; [747]:
[ffb9]ff.byte $ff; [748]:
[ffba]ff.byte $ff; [749]:
[ffbb]ff.byte $ff; [750]:
[ffbc]ff.byte $ff; [751]:
[ffbd]ff.byte $ff; [752]:
[ffbe]ff.byte $ff; [753]:
[ffbf]ff.byte $ff; [754]:
[ffc0]ff.byte $ff; [755]:
[ffc1]ff.byte $ff; [756]:
[ffc2]ff.byte $ff; [757]:
[ffc3]ff.byte $ff; [758]:
[ffc4]ff.byte $ff; [759]:
[ffc5]ff.byte $ff; [760]:
[ffc6]ff.byte $ff; [761]:
[ffc7]ff.byte $ff; [762]:
[ffc8]ff.byte $ff; [763]:
[ffc9]ff.byte $ff; [764]:
[ffca]ff.byte $ff; [765]:
[ffcb]ff.byte $ff; [766]:
[ffcc]ff.byte $ff; [767]:
[ffcd]ff.byte $ff; [768]:
[ffce]ff.byte $ff; [769]:
[ffcf]ff.byte $ff; [770]:
[ffd0]ff.byte $ff; [771]:
[ffd1]ff.byte $ff; [772]:
[ffd2]ff.byte $ff; [773]:
[ffd3]ff.byte $ff; [774]:
[ffd4]ff.byte $ff; [775]:
[ffd5]ff.byte $ff; [776]:
[ffd6]ff.byte $ff; [777]:
[ffd7]ff.byte $ff; [778]:
[ffd8]ff.byte $ff; [779]:
[ffd9]ff.byte $ff; [780]:
[ffda]ff.byte $ff; [781]:
[ffdb]ff.byte $ff; [782]:
[ffdc]ff.byte $ff; [783]:
[ffdd]ff.byte $ff; [784]:
[ffde]ff.byte $ff; [785]:
[ffdf]ff.byte $ff; [786]: