;============================================================================
; Faxanadu (U).nes
;
; PRG15_MIRROR ($c000 - $ffff)
;============================================================================
.segment "PRG15_MIRROR"
.ORG $c000
[c000]UI_SetHUDPPUAttributes:; [$c000]
;
; Set the PPUADDR to 0x23C0, the top row where the status
; bar resides.
;
[c000]LDA #$23
[c002]STA a:PPUADDR; Set upper PPUADDR as 0x23.
[c005]LDA #$c0
[c007]STA a:PPUADDR; Set upper PPUADDR as 0xC0.
[c00a]LDX a:UI_AttributeDataIndex; X = HUD attribute data index, computed when setting up the screen.
[c00d]LDA HUD_ATTRIBUTE_DATA_BY_INDEX,X; Load the value for the attribute data.
[c010]LDX #$08; X = 8 (loop counter).
[c012]@_writeLoop:; [$c012]
[c012]STA a:PPUDATA; Write to the PPU.
[c015]DEX; X--
[c016]BNE @_writeLoop; If X > 0, loop.
;
; Draw the HUD.
;
[c018]JMP 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]
[c01b].byte $00; [0]: Dartmoor, Castle of Fraternal, King Grieve's Room
[c01c].byte $55; [1]: Start Screen
[c01d].byte $aa; [2]: Most exterior areas.
[c01e].byte $ff; [3]: Most interior areas.
[c01f].byte $41; [4]: Here and below are unused.
[c020].byte $20; [5]:
[c021].byte $04; [6]:
[c022].byte $07; [7]:
[c023].byte $08; [8]:
[c024].byte $09; [9]:
[c025].byte $0a; [10]:
[c026].byte $61; [11]:
[c027].byte $20; [12]:
[c028].byte $04; [13]:
[c029].byte $0b; [14]:
[c02a].byte $0c; [15]:
[c02b].byte $0d; [16]:
[c02c].byte $0e; [17]:
[c02d].byte $56; [18]:
[c02e].byte $20; [19]:
[c02f].byte $03; [20]:
[c030].byte $0f; [21]:
[c031].byte $10; [22]:
[c032].byte $11; [23]:
;============================================================================
; DEADCODE
;============================================================================
[c033]DEADCODE_FUN_PRG15_MIRROR__c033:; [$c033]
[c033]LDY #$00
[c035]LDA (Temp_Addr_L),Y
[c037]STA PPU_TargetAddr
[c039]INY
[c03a]LDA (Temp_Addr_L),Y
[c03c]STA PPU_TargetAddr_U
[c03e]INY
[c03f]LDA (Temp_Addr_L),Y
[c041]PHA
[c042]JSR PPUBuffer_QueueCommandOrLength
[c045]PLA
[c046]STA Temp_00
[c048]LDY #$03
[c04a]@LAB_PRG15_MIRROR__c04a:; [$c04a]
[c04a]LDA (Temp_Addr_L),Y
[c04c]STA PPUBuffer,X
[c04f]INX
[c050]INY
[c051]DEC Temp_00
[c053]BNE @LAB_PRG15_MIRROR__c04a
[c055]STX PPUBuffer_WriteOffset
[c057]RTS
[c058]UI_DrawHUD:; [$c058]
[c058]LDA #$00
[c05a]JSR UI_DrawTimeValue; Draw the time value.
[c05d]JSR UI_DrawGoldValue; Draw the gold value.
[c060]JSR PPUBuffer_DrawAll; Flush to the PPU.
[c063]JSR UI_DrawPlayerExperience; Draw the player experience value.
[c066]JSR PPUBuffer_DrawAll; Flush to the PPU.
[c069]JSR PPUBuffer_DrawAll; Flush to the PPU.
[c06c]JSR UI_DrawPlayerHP; Draw the player's HP bar.
[c06f]JSR PPUBuffer_DrawAll; Flush to the PPU.
[c072]LDA a:Player_MP; Load the player's MP.
[c075]JSR Player_SetMP; Set it and draw.
[c078]JMP 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:; [$c07b]
;
; Update the player's HP with the provided value.
;
[c07b]CLC; Clear carry so it's not added.
[c07c]ADC a:Player_HP_U; Add the provided HP to the player's health.
[c07f]STA a:Player_HP_U; Store as the new health.
;
; Check against the cap.
;
[c082]CMP #$50; Cap at 80 HP.
[c084]BCC @_drawHP; Jump if it's under.
[c086]LDA #$50; Else, cap it to 80HP.
[c088]STA a:Player_HP_U; Store as the new health.
[c08b]@_drawHP:; [$c08b]
[c08b]JMP UI_DrawPlayerHPValue
[c08e]Player_ReduceHP:; [$c08e]
;
; Reduce the lower byte of player health.
;
[c08e]LDA a:Player_HP_L; Load the lower byte of the player's health.
[c091]SEC
[c092]SBC a:Arg_PlayerHealthDelta_L; Subtract the specified lower byte of health.
[c095]STA a:Player_HP_L; Store it as the new player health.
;
; Reduce the upper byte of player health.
;
[c098]LDA a:Player_HP_U; Load the upper byte of the player's health.
[c09b]SBC a:Arg_PlayerHealthDelta_U; Subtract the specified upper byte of health.
[c09e]STA a:Player_HP_U; Store it as the new player health.
[c0a1]BCS @_isStillAlive; If there's still health left, the player is still alive.
;
; The player is out of health. They may die.
;
[c0a3]LDA #$00
[c0a5]STA a:Player_HP_U; Set the player's upper byte of health to 0.
[c0a8]JSR UI_DrawPlayerHP; Draw the health.
;
; Check if the player has an elixir before killing them.
;
[c0ab]LDA a:SpecialItems; Load the player's special items.
[c0ae]AND #$08; Check if the player has an elixir.
[c0b0]BEQ @_killPlayer; If not, kill the player.
;
; The player has an elixir. Fill up their health instead
; of killing the player.
;
[c0b2]JMP Player_UseElixir; Use the Elixir.
;
; Mark the player as dead.
;
[c0b5]@_killPlayer:; [$c0b5]
[c0b5]LDA #$01; Mark the player as dead.
[c0b7]STA a:PlayerIsDead; Store that.
;
; Draw the player's health.
;
[c0ba]@_isStillAlive:; [$c0ba]
[c0ba]JMP UI_DrawPlayerHP; Draw player health.
[c0bd]UI_DrawPlayerHP:; [$c0bd]
[c0bd]LDA a:Player_HP_U; Load the player's current health.
[c0c0]JMP 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]
[c0c3]LDX a:SelectedMagic; Load the selected magic spell.
[c0c6]LDA a:Player_MP; Load the player's total MP.
[c0c9]SEC
[c0ca]SBC MAGIC_COSTS,X; Look up the cost of this spell.
[c0cd]BCC @_notEnoughMP; If there's not enough MP for the spell, then jump.
;
; The player has enough MP for the spell.
;
[c0cf]STA a:Player_MP; Else, store the reduced MP.
[c0d2]JSR Player_SetMP; Set that on the player and draw.
[c0d5]CLC; Clear the carry flag to indicate a success.
[c0d6]RTS
;
; The player does not have enough mana.
;
[c0d7]@_notEnoughMP:; [$c0d7]
[c0d7]SEC; Set the carry flag to 1 to indicate not enough MP.
[c0d8]RTS
;============================================================================
; 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:; [$c0d9]
;
; Add the provided MP to the player's MP.
;
[c0d9]CLC; Clear carry so it's not added.
[c0da]ADC a:Player_MP; Add to the player's MP.
[c0dd]STA a:Player_MP; Store it.
[c0e0]CMP #$50; Check if it's > 80 points.
[c0e2]BCC @_drawMP; If we're under the cap, return.
;
; Cap the MP to the total allowed amount.
;
[c0e4]LDA #$50; Cap to 80 points.
;
; Store it.
;
[c0e6]STA a:Player_MP
[c0e9]@_drawMP:; [$c0e9]
[c0e9]JMP 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]
[c0ec]TXA
[c0ed]PHA
[c0ee]LDA #$00
[c0f0]STA a:Temp1_SomethingChangedHP
[c0f3]STA a:Temp2_SomethingChangedHP
[c0f6]LDX #$10
[c0f8]ROL a:Arg_PlayerHealthDelta_L
[c0fb]ROL a:Arg_PlayerHealthDelta_U
[c0fe]@LAB_PRG15_MIRROR__c0fe:; [$c0fe]
[c0fe]ROL a:Temp1_SomethingChangedHP
[c101]ROL a:Temp2_SomethingChangedHP
[c104]LDA a:Temp1_SomethingChangedHP
[c107]CMP a:SpriteBehaviorUnknown20_SomethingXOrY
[c10a]LDA a:Temp2_SomethingChangedHP
[c10d]SBC a:BYTE_04bf
[c110]BCC @LAB_PRG15_MIRROR__c124
[c112]LDA a:Temp1_SomethingChangedHP
[c115]SBC a:SpriteBehaviorUnknown20_SomethingXOrY
[c118]STA a:Temp1_SomethingChangedHP
[c11b]LDA a:Temp2_SomethingChangedHP
[c11e]SBC a:BYTE_04bf
[c121]STA a:Temp2_SomethingChangedHP
[c124]@LAB_PRG15_MIRROR__c124:; [$c124]
[c124]ROL a:Arg_PlayerHealthDelta_L
[c127]ROL a:Arg_PlayerHealthDelta_U
[c12a]DEX
[c12b]BNE @LAB_PRG15_MIRROR__c0fe
[c12d]PLA
[c12e]TAX
[c12f]RTS
[c130]Screen_ClearSprites:; [$c130]
[c130]LDX #$07; X = 7 (loop counter -- looping 8 times)
[c132]@_clearSpritesLoop:; [$c132]
[c132]LDA #$ff
[c134]STA CurrentSprites_Entities,X; Set sprite to 0xFF (cleared).
[c137]LDA #$00
[c139]STA CurrentSprites_HP,X; Set sprite hit points to 0.
[c13c]STA CurrentSprites_HitCounter,X; Set sprite hit counter to 0.
[c13f]STA CurrentSprites_Behaviors,X; Set sprite subtype to 0.
[c142]DEX; X--
[c143]BPL @_clearSpritesLoop; If we're not done, loop.
;
; We're out of the loop. Reset the PPU draw offset and restore
; screen state.
;
[c145]JSR CurrentSprite_ResetPPUTileOffset; Reset the PPU offset.
[c148]LDA #$ff
[c14a]STA a:IScript_PortraitID; Clear any portrait shown on screen.
[c14d]LDA PPU_Mask; Load the current screen color mode.
[c14f]AND #$fe; Set to color mode (clear bit 1)
[c151]STA PPU_Mask; Store the new color mode.
[c153]RTS
[c154]Screen_LoadAllScreenInfo:; [$c154]
;
; Set the address of the sprites-for-screen lookup table
; for the area.
;
[c154]LDA Area_CurrentArea; A = Current area index.
[c156]ASL A; Convert to a word offset for the lookup table.
;
; Set the starting address of the screen data.
;
[c157]TAY; Y = A
[c158]LDA AREA_SPRITE_ADDRESSES,Y; Load the upper byte of the address.
[c15b]STA Temp_Addr_L; Store for reading.
[c15d]LDA AREA_SPRITE_ADDRESSES+1,Y; Load the lower byte of thea ddress.
[c160]STA Temp_Addr_U; Store it.
;
; Check the first byte of the address. If 0xFF,
; nothing will be loaded.
;
[c162]LDY #$01; Y = 1
[c164]LDA (Temp_Addr_L),Y; Load the address for the screens list.
[c166]CMP #$ff; Is it 0xFF?
[c168]BEQ RETURN_C1B3; If so, there's nothing to load. Return.
;
; Set the start address for a screen's sprite and metadata
; information.
;
[c16a]LDA Area_CurrentScreen; A = Current screen index.
[c16c]ASL A; Convert to a word offset for the lookup table.
[c16d]TAY; Y = A
;
; Read the address for the screen information.
;
[c16e]LDA (Temp_Addr_L),Y; Load the lower byte of the screen address.
[c170]STA Sprites_ReadInfoAddr; Store it as the lower byte of the of the sprites read address.
[c172]INY; Y++
[c173]LDA (Temp_Addr_L),Y; Load the upper byte.
[c175]STA 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]LDY #$00; Y = 0
[c179]@_readLoop:; [$c179]
[c179]LDA (Sprites_ReadInfoAddr),Y; Load the next byte from the screen data.
[c17b]CMP #$ff; Is it 0xFF?
[c17d]BNE @_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]INY; Y++
[c180]TYA; A = Y
[c181]CLC
[c182]ADC Sprites_ReadInfoAddr; Increment the read address by 2 bytes.
[c184]STA Screen_ExtraInfoAddr
[c186]LDA Sprites_ReadInfoAddr_U; Load the upper byte.
[c188]ADC #$00; Add carry, if lower byte overflowed.
[c18a]STA Screen_ExtraInfoAddr_U; Store as the new upper byte.
[c18c]JMP Screen_LoadSpecialEventID; Read the extra screen information.
;
; Increment by 2 (the sprite entity and value).
;
[c18f]@_prepareNextLoopIter:; [$c18f]
[c18f]INY; Y++
[c190]INY; Y++
[c191]BNE @_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:; [$c193]
;
; No special event information was found for the screen.
; Clear that state (set to 0xFF).
;
[c193]LDA #$ff
[c195]STA a:CurrentScreen_SpecialEventID; Clear the special event ID (0xFF).
[c198]RTS
;============================================================================
; 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:; [$c199]
;
; 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]LDY #$00; Y = 0 (loop counter)
[c19b]@_scanLoop:; [$c19b]
[c19b]LDA (Screen_ExtraInfoAddr),Y; Load the next byte in the screen info data.
[c19d]CMP #$ff; Is it 0xFF?
[c19f]BNE @_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]INY; Y++
[c1a2]LDA (Screen_ExtraInfoAddr),Y; Load the next byte to see what we're working with.
[c1a4]CMP #$80; Is it 0x80?
[c1a6]BNE 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]INY; Y++
[c1a9]LDA (Screen_ExtraInfoAddr),Y; Load the next byte containing the special event ID.
[c1ab]STA a:CurrentScreen_SpecialEventID; Store it while on the screen.
[c1ae]RTS
[c1af]@_noFFMarker:; [$c1af]
[c1af]INY; Y++
;
; If we hit 256 loops, we're done.
;
[c1b0]BNE @_scanLoop; If we haven't wrapped, then loop.
[c1b2]RTS; Else, we're done.
[c1b3]RETURN_C1B3:; [$c1b3]
[c1b3]RTS
[c1b4]Screen_LoadSpriteInfo:; [$c1b4]
;
; Switch to ROM bank 11, where sprite information lives.
;
[c1b4]LDA a:CurrentROMBank; Load the current ROM bank.
[c1b7]PHA; Push it to the stack.
[c1b8]LDX #$0b
[c1ba]JSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Load all the sprites for this screen.
;
[c1bd]JSR Screen_LoadAllScreenInfo; Load the sprite information for the screen.
;
; Switch bank to our previous bank.
;
[c1c0]PLA; Pop A (our saved bank).
[c1c1]TAX; X = A
[c1c2]JSR MMC1_UpdateROMBank; And switch back to it.
;
; Begin our loop for reading all sprites for the screen.
;
[c1c5]@_readSpriteLoop:; [$c1c5]
[c1c5]LDY #$00; Y = 0 (info read offset)
;
; Switch back to ROM bank 11 (sprite info).
;
[c1c7]LDA a:CurrentROMBank; A = current ROM bank
[c1ca]PHA; Push to the stack.
[c1cb]LDX #$0b
[c1cd]JSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Read information on the next sprite on the screen.
;
[c1d0]LDA (Sprites_ReadInfoAddr),Y; Read the current byte of the screen information.
[c1d2]CMP #$ff; Is it 0xFF (our end-of-sprites list)?
[c1d4]BEQ @_restoreBankAndReturn; If it is, we're done. Restore our bank and return.
;
; Store the sprite's entity ID.
;
[c1d6]STA 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]INY; Y++
[c1da]LDA (Sprites_ReadInfoAddr),Y; Load the next byte.
[c1dc]AND #$f0; Take only the upper nibble.
[c1de]STA 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]LDA (Sprites_ReadInfoAddr),Y; Load the same byte.
[c1e3]ASL A; Shift the X coordinate from the lower nibble to the upper nibble.
[c1e4]ASL A
[c1e5]ASL A
[c1e6]ASL A
[c1e7]STA a:CurrentSprite_XPos; Store as the sprite's X position.
;
; Restore our bank.
;
[c1ea]PLA; Pull A (the saved bank) from the stack.
[c1eb]TAX; X = A
[c1ec]JSR 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]JSR Sprites_PopulateNextAvailableSprite; Add this sprite to the next available slot.
;
; Increment the read address for the next sprite in the
; screen data.
;
[c1f2]LDA Sprites_ReadInfoAddr; A = lower byte of the screen information address
[c1f4]CLC
[c1f5]ADC #$02; Increment by 2 (sprite entity ID and position).
[c1f7]STA Sprites_ReadInfoAddr; Store as the new lower byte of the address.
[c1f9]LDA Sprites_ReadInfoAddr_U; A = upper byte of the screen information address
[c1fb]ADC #$00; Add carry if the lower byte overflowed.
[c1fd]STA Sprites_ReadInfoAddr_U; Store it as the new upper byte of the address.
[c1ff]JMP @_readSpriteLoop; Loop to read the next sprite.
[c202]@_restoreBankAndReturn:; [$c202]
[c202]JMP MMC1_UpdatePRGBankToStackA
[c205]Sprites_PopulateNextAvailableSprite:; [$c205]
;
; Prepare the sprite loop.
;
[c205]LDX #$07; 7 = loop counter
[c207]@_loop:; [$c207]
[c207]STX a:CurrentSpriteIndex; Set it
[c20a]LDA CurrentSprites_Entities,X; Load the sprite entity at this index.
[c20d]CMP #$ff; Is it 0xFF (unset)?
[c20f]BNE @_prepareNextIter; If not, jump to prepare for next loop.
;
; This sprite index is unset. Populate it.
;
[c211]LDA #$00; 0 = unset
[c213]STA CurrentSprites_Phases,X; Clear the sprite phase.
[c216]STA CurrentSprites_Flags,X; Clear the sprite flags.
[c219]LDA #$ff; 0xFF = unset
[c21b]STA CurrentSprites_HitByMagicBehavior,X; Clear the hit by magic behavior.
;
; Set the sprite's starting X/Y position.
;
[c21e]LDA a:CurrentSprite_XPos; Load the current sprite X position to set.
[c221]STA CurrentSprites_XPos_Full,X; Set for the sprite index.
[c223]LDA a:CurrentSprite_YPos; Load the Y position.
[c226]STA CurrentSprites_YPos,X; Set for the sprite index.
[c228]LDA a:CurrentSprite_Entity; Load the sprite entity.
[c22b]STA CurrentSprites_Entities,X; Set it.
[c22e]TAY; Y = A (entity)
[c22f]LDA SPRITE_ENTITIES_HITBOX_TYPES,Y; Load the hitbox type for the entity.
[c232]STA CurrentSprites_HitBoxTypes,X; Set it.
[c235]LDA SPRITE_ENTITIES_HP,Y; Load the HP for the entity.
[c238]STA CurrentSprites_HP,X; Set it.
;
; Prepare the behavior scripts for the entity.
;
[c23b]TYA; A = Y (entity)
[c23c]ASL A; Convert to a word boundary.
[c23d]TAY; Y = A
[c23e]LDA SPRITE_BSCRIPTS,Y; Load the lower byte of the behavior script for the entity.
[c241]STA CurrentSprites_BehaviorAddrs_L,X; Set the lower byte of the address.
[c244]LDA SPRITE_BSCRIPTS+1,Y; Load the upper byte.
[c247]STA CurrentSprites_BehaviorAddrs_U,X; Set the upper byte.
[c24a]LDA #$ff; A = 0xFF (unset)
[c24c]STA CurrentSprites_Behaviors,X; Clear the current behavior state.
[c24f]JMP Sprites_LoadSpriteValue; Load the value for the sprite and return.
[c252]@_prepareNextIter:; [$c252]
[c252]LDX a:CurrentSpriteIndex; X = current sprite index.
[c255]DEX; X--
[c256]BPL @_loop; If >= 0, loop.
[c258]RTS
;============================================================================
; Banks storing the images for a range of sprites.
;
; XREFS:
;
Sprites_StoreBankForCurrentSprite
;============================================================================
[c259]SPRITE_IMAGE_BANKS:; [$c259]
[c259].byte $06; [0]: Bank for sprites 0-54
[c25a]SPRITE_IMAGE_BANKS_1_:; [$c25a]
[c25a].byte $07; [1]: Bank for sprites 55-100
[c25b]Sprites_LoadSpriteValue:; [$c25b]
[c25b]TXA; A = X (sprite index)
[c25c]TAY; Y = A
;
; Save the current ROM bank and switch to bank 11.
;
[c25d]LDA a:CurrentROMBank; Get the current ROM bank.
[c260]PHA; Push to the stack.
[c261]LDX #$0b; X = 11 (bank)
[c263]JSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Restore our sprite index to X so it's not clobbered
; when this returns.
;
[c266]TYA; A = Y
[c267]TAX; X = A
;
; Load the extra information for this sprite and store it
; in the sprite list.
;
[c268]LDY #$00; Y = 0
[c26a]LDA (Screen_ExtraInfoAddr),Y; A = Extra information for the sprite.
[c26c]STA CurrentSprites_Values,X; Store in CurrentSprites_Values.
[c26f]CMP #$ff; Is it 0xFF (unset)?
[c271]BEQ @_restoreBankAndReturn; If so, we're done. Jump.
;
; Increment the position in the info data.
;
[c273]INC Screen_ExtraInfoAddr; Increment the lower byte of the offset in the extra info data.
[c275]BNE @_restoreBankAndReturn; If this didn't wrap to 0, we're done. Jump.
[c277]INC Screen_ExtraInfoAddr_U; Else, increment the upper byte as well.
[c279]@_restoreBankAndReturn:; [$c279]
[c279]JMP 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]
[c27c]LDY #$00; Y = 0
[c27e]LDA a:CurrentSprite_Entity; A = current sprite entity
[c281]CMP #$37; Is it < sprite 55 (Unused Child sprite)?
[c283]BCC @_loadBank; If yes, then load bank from table at index 0.
[c285]INY; Else, load bank from table at index 1.
;
; Load the image for the bank.
;
[c286]@_loadBank:; [$c286]
[c286]LDA SPRITE_IMAGE_BANKS,Y; Load the address for the sprite entity.
[c289]STA a:CurrentSprite_TilesBank; Store as the bank for this sprite.
[c28c]@_return:; [$c28c]
[c28c]RTS
[c28d]GameLoop_LoadSpriteImages:; [$c28d]
;
; Save state and prepare for image loading.
;
[c28d]LDA a:CurrentSpriteIndex; A = current sprite index
[c290]PHA; Push it to the stack.
[c291]JSR CurrentSprite_ResetPPUTileOffset; Reset the PPU drawing coordinates.
;
; Begin our loop for all 7 sprite slots.
;
[c294]LDX #$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]
[c296]STX a:CurrentSpriteIndex; Store it as the currently-processed sprite index.
[c299]LDA CurrentSprites_Entities,X; Load the associated sprite entity ID.
[c29c]CMP #$ff; Is it 0xFF (unset)?
[c29e]BEQ @_nextLoop; If so, jump and prepare for the next loop.
[c2a0]STA a:CurrentSprite_Entity; Store this as the entity at this index.
;
; Load the image for this sprite.
;
[c2a3]JSR Sprites_StoreBankForCurrentSprite; Store the image bank for the current sprite.
[c2a6]JSR 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]LDA a:CurrentSprite_Entity; Load the current entity.
[c2ac]CMP #$30; Is it the Pakukame boss?
[c2ae]BNE @_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]LDA a:CurrentSprite_Entity; Load the Pakukame entity ID
[c2b3]PHA; Push to the stack.
[c2b4]LDA CurrentSprite_StartPPUTileOffset; Load the sprite's starting PPU tile offset.
[c2b6]PHA; Push it to the stack.
[c2b7]LDA #$09; A = 9 (Lilith)
[c2b9]STA a:CurrentSprite_Entity; Store as the new entity.
[c2bc]JSR Sprites_StoreBankForCurrentSprite; Store the bank for Lilith.
[c2bf]JSR Sprites_LoadImageForCurrentSprite; Load Lilth's image from the bank.
[c2c2]PLA; Pop the PPU offset row.
[c2c3]STA CurrentSprite_StartPPUTileOffset; Store it again.
[c2c5]PLA; Pop the current sprite entity.
[c2c6]STA 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]
[c2c9]LDX a:CurrentSpriteIndex; X = current sprite index
[c2cc]LDA CurrentSprite_StartPPUTileOffset; A = Start PPU tile offset
[c2ce]STA 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]LDA CurrentSprites_YPos,X; Load the Y position for this sprite.
[c2d3]CMP #$f0; Is it less than 0xF0?
[c2d5]BCC @_nextLoop; If so, loop.
;
; It's off-screen. Unset it.
;
[c2d7]LDA #$ff; Else, it's off the screen.
[c2d9]STA CurrentSprites_Entities,X; Unset the entity.
;
; Done with any loading required for the sprite. Prepare for
; the next loop, if needed.
;
[c2dc]@_nextLoop:; [$c2dc]
[c2dc]LDX a:CurrentSpriteIndex; X = current sprite index (loop counter).
[c2df]DEX; X--
[c2e0]BPL @_loop; If X >= 0, loop.
;
; We're done. Restore the original current sprite index and
; wait until everything's drawn to the screen.
;
[c2e2]PLA; Pull A (current sprite index) from the stack.
[c2e3]STA a:CurrentSpriteIndex; Store that back as the current sprite index.
[c2e6]JMP PPUBuffer_WaitUntilClear; Wait until everything's drawn to the screen.
[c2e9]CastMagic_RunUpdateSpellHandler:; [$c2e9]
;
; These two lines are deadcode. It might be the developers
; attempting to hard-code Deluge and Thunder during testing?
; Each gets overriden immediately.
;
[c2e9]LDA #$00; A = 0 (deadcode)
[c2eb]LDA #$01; A = 1 (deadcode)
[c2ed]LDA a:CastMagic_Type; A = Cast magic type
;
; If 0xFF, return.
;
[c2f0]BMI @_return
;
; This was an active, cast magic spell. Set state.
;
[c2f2]JSR CastMagic_CalculateVisibility; Calculate visibility for the magic spell based on foreground elements.
;
; Manage the X position of the spell.
;
[c2f5]LDA a:CastMagic_XPos_Full; Load the magic's X position.
[c2f8]STA Arg_DrawSprite_PosX; Set as an argument for future appearance updates.
;
; Set some unused state not used in the finished game.
;
[c2fa]LDA Screen_Maybe_ScrollXCounter
[c2fc]STA Unused_Sprite_ScrollPosX
;
; Manage the Y position of the spell.
;
[c2fe]LDA a:CastMagic_YPos_Full
[c301]STA Arg_DrawSprite_PosY; Set as an argument for future appearance updates.
;
; Set more unused state not used in the finished game.
;
[c303]LDA Player_Something_ScrollPosY
[c305]STA Unused_Sprite_ScrollPosY
;
; Set the finish handler for this type as the next function
; to run.
;
[c307]LDA a:CastMagic_Type; Load the magic type.
[c30a]ASL A; Convert to a word boundary for the lookup table.
[c30b]TAY; Y = A
[c30c]LDA DAT_bb28,Y; Load the lower byte of the finish handler.
[c30f]PHA; Push to the stack.
[c310]LDA DAT_bb27,Y; Load the upper byte.
[c313]PHA; Push.
[c314]@_return:; [$c314]
[c314]RTS; Return and then call the function.
[c315]CastMagic_CalculateVisibility:; [$c315]
;
; Set the default to visible.
;
[c315]LDA #$00
[c317]STA Temp_MovingSpriteVisibility; Set the default visibility to visible.
;
; Convert the cast magic's position to block positions.
;
[c319]LDA a:CastMagic_YPos_Full; Load the magic's Y position.
[c31c]STA Arg_PixelPosY; Set it as an argument for getting the block property.
[c31e]LDA a:CastMagic_XPos_Full; Load the magic's X position.
[c321]CLC
[c322]ADC #$04; Add 4 to that to better check block alignment.
[c324]STA Arg_PixelPosX; Set it as an argument.
[c326]JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[c329]JSR ScreenBuffer_LoadBlockProperty; Get the block property for this position.
;
; Check if a block overlaps the left.
;
[c32c]CMP #$04; Is this block type 4?
[c32e]BEQ @_leftIsObscured; If so, the left is obscured.
[c330]CMP #$0d; Else, is this block type 13?
[c332]BEQ @_leftIsObscured; If so, the left is obscured.
[c334]CMP #$09; Else, is this block type 9?
[c336]BNE @_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]
[c338]LDA Temp_MovingSpriteVisibility; Load the cast magic's visibility state.
[c33a]ORA #$01; Mark the left-hand side as obscured.
[c33c]STA Temp_MovingSpriteVisibility; Store it.
;
; Now check the right-hand side of the magic.
;
[c33e]@_checkRightObscured:; [$c33e]
[c33e]LDA Arg_PixelPosX; Load the pixel X position.
[c340]CLC
[c341]ADC #$08; Add 8 to it, to better check the right side.
[c343]STA Arg_PixelPosX; Store as the new pixel argument.
[c345]JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[c348]JSR ScreenBuffer_LoadBlockProperty; Load the block property.
;
; Check if a block overlaps the right.
;
[c34b]CMP #$04; Is this block 4?
[c34d]BNE @_checkNeedToFlipBits; If not, it's not obscured.
[c34f]CMP #$04; Again, is this block 4?
[c351]BEQ @_rightIsObscured; If so, the right is obscured.
[c353]CMP #$0d; Else, is this block 13?
[c355]BEQ @_rightIsObscured; If so, the right is obscured.
[c357]CMP #$09; Else, is this block 9?
[c359]BNE @_checkNeedToFlipBits; If not, it's not obscured. Move to the next step.
;
; The trailing side of the magic is obscured.
;
[c35b]@_rightIsObscured:; [$c35b]
[c35b]LDA Temp_MovingSpriteVisibility; Load the cast magic's visibility state.
[c35d]ORA #$02; Mark the right-hand side as obscured.
[c35f]STA 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]
[c361]LDX a:CurrentSpriteIndex; X = current sprite index.
[c364]LDA a:CastMagic_Flags; A = cast magic flags.
[c367]AND #$40; Is it facing right?
[c369]BEQ @_facingRight; If so, there's nothing to do. We're done.
[c36b]LDA Temp_MovingSpriteVisibility; A = cast magic visibility
[c36d]BEQ @_storeVisibility; If the block is fully visible, jump.
[c36f]CMP #$03; Else, is it full obscured?
[c371]BEQ @_storeVisibility; If yes, jump.
;
; Swap the bits to face the other direction.
;
[c373]EOR #$03; Swap the visibility of leading and trailing flags.
[c375]JMP @_storeVisibility; Jump to store.
[c378]@_facingRight:; [$c378]
[c378]LDA Temp_MovingSpriteVisibility; Load the previously-calculated visibility from above.
[c37a]@_storeVisibility:; [$c37a]
[c37a]STA MovingSpriteVisibility; Store the new visibility state.
[c37c]RTS
[c37d]CastMagic_SetAppearance:; [$c37d]
[c37d]LDY a:CastMagic_Type; Y = cast magic type
[c380]CLC
[c381]ADC SPRITE_MAGIC_IMAGE_ADDRS_U,Y; Add the value from the lookup table.
[c384]JMP 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]
[c387].byte $95; [0]: Deluge
[c388].byte $99; [1]: Thunder
[c389].byte $9b; [2]: Fire
[c38a].byte $9d; [3]: Death
[c38b].byte $a1; [4]: Tilte
[c38c].byte $a5; [5]: UNUSED: Deluge after first hit
[c38d].byte $99; [6]: Thunder after first hit
[c38e].byte $9b; [7]: Fire after first hit
[c38f].byte $a5; [8]: UNUSED: Death after first hit
[c390].byte $a5; [9]: UNUSED
[c391].byte $a5; [10]: UNUSED: Hit wall effect
[c392].byte $a5; [11]: Tilte after first hit
[c393]CastMagic_UpdateSpriteDirection:; [$c393]
[c393]LDA a:CastMagic_Flags; Load the cast magic's flags.
[c396]AND #$40; Take only the facing direction bit.
[c398]STA CurrentSprite_FlipMask; Set as the sprite's flip mask.
[c39a]RTS
[c39b]CastMagic_FinishHandler_Deluge:; [$c39b]
[c39b]JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c39e]LDA InterruptCounter; Load the interrupt counter.
[c3a0]LSR A; Shift the interrupt counter to generate an appearance index.
[c3a1]LSR A
[c3a2]AND #$03; Keep the 2 right-most bits (effectively, bits 3 and 4 of the interrupt counter).
[c3a4]JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3a7]CastMagic_FinishHandler_Thunder:; [$c3a7]
[c3a7]JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c3aa]LDA InterruptCounter; Load the interrupt counter.
[c3ac]LSR A; Shift the interrupt counter to generate a value to check against.
[c3ad]LSR A
[c3ae]BCS @_return; If this should not update the appearance, return.
[c3b0]AND #$01; Keep the right-most bit (effectively, bit 2 of the interrupt counter).
[c3b2]JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3b5]@_return:; [$c3b5]
[c3b5]RTS
[c3b6]CastMagic_FinishHandler_Fire:; [$c3b6]
[c3b6]LDA InterruptCounter; Load the interrupt counter.
[c3b8]AND #$02; Is bit 1 set?
[c3ba]BNE @_updateFire; If so, jump to update.
[c3bc]RTS
[c3bd]@_updateFire:; [$c3bd]
[c3bd]JSR CastMagic_UpdateSpriteDirection; Set the sprite's direction.
[c3c0]LDA InterruptCounter; Load the interrupt counter.
[c3c2]LSR A; Shift the interrupt counter to generate an appearance index.
[c3c3]LSR A
[c3c4]AND #$01; Keep the right-most bit (effectively, bit 2 of the interrupt counter).
[c3c6]JMP CastMagic_SetAppearance; Set that as the appearance index.
[c3c9]CastMagic_FinishHandler_Death:; [$c3c9]
[c3c9]JSR CastMagic_UpdateSpriteDirection; Update the sprite's direction.
[c3cc]LDA InterruptCounter; Load the interrupt counter.
[c3ce]LSR A; Shift the interrupt counter to generate an appearance index.
[c3cf]LSR A
[c3d0]LSR A
[c3d1]AND #$03; Keep the 2 right-most bits (effectively, bits 3 and 4 of the interrupt counter).
[c3d3]JMP 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]
[c3d6]JSR CastMagic_UpdateSpriteDirection; Update the sprite's direction.
;
; Check if the first bit of the magic's phase is 0 or 1.
;
[c3d9]LDA a:CastMagic_Phase; Load the magic's phase.
[c3dc]LSR A; Shift bit 0 into Carry.
[c3dd]BCS @_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]LDY #$00; Y = 0
[c3e1]LDA InterruptCounter; A = interrupt counter
[c3e3]AND #$08; Keep bit 3.
[c3e5]BEQ @_setAppearance1; If 0, jump to use 0 as the appearance value.
[c3e7]INY; Y++ (appearance value == 1)
[c3e8]@_setAppearance1:; [$c3e8]
[c3e8]TYA; A = 0 or 1 (appearance value)
[c3e9]JMP 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]
[c3ec]LDA InterruptCounter; Load the interrupt counter.
[c3ee]LSR A; Divide by 2.
[c3ef]LSR A; And place in Carry.
[c3f0]BCC @_setAppearance2; If 0, jump to update appearance.
[c3f2]RTS
;
; 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]
[c3f3]AND #$01; Keep bit 0.
[c3f5]CLC
[c3f6]ADC #$02; Add 2.
[c3f8]JMP 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]
[c3fb]JSR CastMagic_UpdateSpriteDirection; Update the sprite direction.
[c3fe]LDA #$00
[c400]JMP 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:; [$c403]
;
; Set the default flip mask of the sprite to 0 (face right).
;
[c403]LDA #$00
[c405]STA 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]LDA a:CastMagic_Unused_HitWallDeltaPosY; A = Delta Y position.
[c40a]ASL A; A *= 2
[c40b]STA Temp_00; Save it temporarily.
;
; Position and draw the top explosion sprite.
;
; This will be MagicY - (DeltaY * 2).
;
[c40d]LDA a:CastMagic_YPos_Full; A = Magic Y position.
[c410]SEC
[c411]SBC Temp_00; A -= Effect delta Y position.
[c413]STA Arg_DrawSprite_PosY; Set as the new sprite's Y position.
[c415]LDA #$02
[c417]JSR 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]LDA a:CastMagic_Unused_HitWallDeltaPosY; A = Delta Y position.
[c41d]ASL A; A *= 2
[c41e]CLC
[c41f]ADC #$10; A += 16
[c421]CLC
[c422]ADC a:CastMagic_YPos_Full; A += Magic Y position.
[c425]STA Arg_DrawSprite_PosY; Save it as the new sprite's Y position.
[c427]LDA #$03
[c429]JMP CastMagic_SetAppearance; Draw the bottom explosion sprite at that position.
;============================================================================
; TODO: Document CastMagic_FinishHandler_TilteAfterFirstHit
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
CAST_MAGIC_UPDATE_FINISH_HANDLERS [$PRG14::bb3d]
;============================================================================
[c42c]CastMagic_FinishHandler_TilteAfterFirstHit:; [$c42c]
[c42c]LDA #$00
[c42e]STA CurrentSprite_FlipMask
[c430]LDX #$03
[c432]@_loop:; [$c432]
[c432]LDA a:CastMagic_Counter
[c435]ASL A
[c436]PHA
[c437]STA Temp_00
[c439]EOR MAGICHITHANDLER_c42c_ARRAY1,X
[c43c]BPL @LAB_PRG15_MIRROR__c442
[c43e]STA Temp_00
[c440]INC Temp_00
[c442]@LAB_PRG15_MIRROR__c442:; [$c442]
[c442]LDA a:CastMagic_XPos_Full
[c445]CLC
[c446]ADC Temp_00
[c448]STA Arg_DrawSprite_PosX
[c44a]PLA
[c44b]STA Temp_00
[c44d]EOR $c470,X
[c450]BPL @LAB_PRG15_MIRROR__c456
[c452]STA Temp_00
[c454]INC Temp_00
[c456]@LAB_PRG15_MIRROR__c456:; [$c456]
[c456]LDA a:CastMagic_YPos_Full
[c459]CLC
[c45a]ADC Temp_00
[c45c]STA Arg_DrawSprite_PosY
[c45e]TXA
[c45f]PHA
[c460]LDA $c474,X
[c463]JSR CastMagic_SetAppearance
[c466]PLA
[c467]TAX
[c468]DEX
[c469]BPL @_loop
[c46b]RTS
[c46c]MAGICHITHANDLER_c42c_ARRAY1:; [$c46c]
[c46c].byte $ff; [0]:
[c46d].byte $00; [1]:
[c46e]MAGICHITHANDLER_c42c_ARRAY1_2_:; [$c46e]
[c46e].byte $ff; [2]:
[c46f]MAGICHITHANDLER_c42c_ARRAY1_3_:; [$c46f]
[c46f].byte $00; [3]:
[c470].byte $ff; [0]:
[c471].byte $ff; [1]:
[c472].byte $00; [2]:
[c473]BYTE_ARRAY_PRG15_MIRROR__c470_3_:; [$c473]
[c473].byte $00; [3]:
[c474].byte $00; [0]:
[c475].byte $02; [1]:
[c476].byte $01; [2]:
[c477]BYTE_ARRAY_PRG15_MIRROR__c474_3_:; [$c477]
[c477].byte $03; [3]:
;============================================================================
; 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]
[c478]LDA 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]AND #$05
[c47c]BNE GameLoop_UseItem_Return
;
; Check if the Down button has been pressed. If not, return.
;
[c47e]LDA Joy1_ButtonMask; Load the button mask.
[c480]AND #$04; Check if the Down button is pressed.
[c482]BEQ GameLoop_UseItem_Return; If not, return.
;
; Check if the B button has been pressed. If not, return.
;
[c484]LDA a:Joy1_ChangedButtonMask; Load the changed button mask.
[c487]AND #$40; Check if the B button is pressed.
[c489]BEQ 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]LDA a:SelectedItem; Load the selected item.
[c48e]ASL A; Multiply by 2 to get a word boundary for address lookup.
[c48f]TAY; Y = A
[c490]CPY #$22; Is it a valid item?
[c492]BCS GameLoop_UseItem_Return; If not, return.
;
; Look up this item in the jump table and run it.
;
[c494]LDA USE_ITEM_JUMP_TABLE+1,Y; Load the lower byte of the item handler address.
[c497]PHA; Push it.
[c498]LDA USE_ITEM_JUMP_TABLE,Y; Load the upper byte.
[c49b]PHA; Push it.
[c49c]GameLoop_UseItem_Return:; [$c49c]
[c49c]RTS
;============================================================================
; Handler functions for using specific items.
;
; XREFS:
;
GameLoop_CheckUseCurrentItem
;============================================================================
[c49d]USE_ITEM_JUMP_TABLE:; [$c49d]
[c49d].word GameLoop_UseItem_Return-1; [0]: Ring of Elf
[c49f].word GameLoop_UseItem_Return-1; [1]: Ring of Ruby
[c4a1].word GameLoop_UseItem_Return-1; [2]: Ring of Dworf
[c4a3].word GameLoop_UseItem_Return-1; [3]: Demon's Ring
[c4a5].word GameLoop_UseItem_Return-1; [4]: "A" Key
[c4a7].word GameLoop_UseItem_Return-1; [5]: "K" Key
[c4a9].word GameLoop_UseItem_Return-1; [6]: "Q" Key
[c4ab].word GameLoop_UseItem_Return-1; [7]: "J" Key
[c4ad].word GameLoop_UseItem_Return-1; [8]: "Jo" Key
[c4af].word Player_UseMattock-1; [9]: Mattock
[c4b1].word GameLoop_UseItem_Return-1; [10]: Magical Rod
[c4b3].word GameLoop_UseItem_Return-1; [11]: Crystal
[c4b5].word GameLoop_UseItem_Return-1; [12]: Lamp
[c4b7].word Player_UseHourGlass-1; [13]: Hour Glass
[c4b9].word GameLoop_UseItem_Return-1; [14]: Book
[c4bb].word Player_UseWingBoots-1; [15]: Wing Boots
[c4bd].word Player_UseRedPotion-1; [16]: Red Potion
[c4bf]Player_ClearSelectedItem:; [$c4bf]
[c4bf]PHA; Push A to the stack.
[c4c0]LDA #$ff
[c4c2]STA a:SelectedItem; Set selected item to 0xFF (unset).
[c4c5]JSR UI_ClearSelectedItemPic; Update the UI for the cleared image.
[c4c8]PLA; Pop A from the stack.
[c4c9]RTS
[c4ca]Player_UseElixir:; [$c4ca]
[c4ca]LDA a:SpecialItems; Load the special items.
[c4cd]AND #$f7; Clear the elixir bit.
[c4cf]STA a:SpecialItems; Store it.
;
; Run the "Use Elixir" IScript.
;
; IScript 0x85 via jump to
IScripts_Begin.
;
[c4d2]LDA #$85; 0x85 == Use Elixir.
[c4d4]JSR MMC1_LoadBankAndJump; Run IScript:
[c4d7].byte BANK_12_LOGIC; Bank = 12
[c4d8].word IScripts_Begin-1; Address = IScripts_Begin
[c4da]Player_FillHPAndMP:; [$c4da]
;
; Progressively fill the HP, 4 units at a time.
;
; Start by playing the sound.
;
[c4da]LDA #$13; Set sound 0x13 (fill HP/MP bar).
[c4dc]JSR Sound_PlayEffect; Play it.
;
; Add the HP.
;
[c4df]LDA #$04; Set the HP to 4 units.
[c4e1]JSR Player_AddHP; Add it to the player.
[c4e4]JSR 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]JSR WaitForNextFrame; Wait a bit.
[c4ea]JSR Sprites_FlipRanges
[c4ed]JSR WaitForNextFrame
[c4f0]JSR Sprites_FlipRanges
[c4f3]JSR WaitForNextFrame
[c4f6]JSR Sprites_FlipRanges
[c4f9]JSR WaitForNextFrame
[c4fc]JSR Sprites_FlipRanges
;
; Check if we're done filling up HP.
;
[c4ff]LDA a:Player_HP_U; Load the player's HP.
[c502]CMP #$50; Is it below the cap of 80HP?
[c504]BCC Player_FillHPAndMP; If so, loop.
;
; Progressively fill the MP, 4 units at a time.
;
; Start by playing the sound.
;
[c506]@_fillMPLoop:; [$c506]
[c506]LDA #$13; Set sound 0x13 (fill HP/MP bar).
[c508]JSR Sound_PlayEffect; Play it.
;
; Add the MP.
;
[c50b]LDA #$04; Set the MP to 4 units.
[c50d]JSR Player_AddMP; Add it to the player.
[c510]JSR 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]JSR WaitForNextFrame; Wait a bit.
[c516]JSR Sprites_FlipRanges
[c519]JSR WaitForNextFrame
[c51c]JSR Sprites_FlipRanges
[c51f]JSR WaitForNextFrame
[c522]JSR Sprites_FlipRanges
[c525]JSR WaitForNextFrame
[c528]JSR Sprites_FlipRanges
;
; Check if we're done filling up MP.
;
[c52b]LDA a:Player_MP; Load the player's MP.
[c52e]CMP #$50; Is it below the cap of 80MP?
[c530]BCC @_fillMPLoop; If so, loop.
[c532]RTS
[c533]Player_UseRedPotion:; [$c533]
;
; Run the "Used Red Potion" IScript.
;
; IScript 0x80 via jump to
IScripts_Begin.
;
[c533]LDA #$80; 0x80 == Use Red Potion IScript.
[c535]JSR MMC1_LoadBankAndJump; Run IScript:
[c538].byte BANK_12_LOGIC; Bank = 12
[c539].word IScripts_Begin-1; Address = IScripts_Begin
[c53b]@_afterFarJump:; [$c53b]
[c53b]JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play the initial sound effect.
;
[c53e]LDA #$1a; 0x1A == Initial item used sound effect.
[c540]JSR Sound_PlayEffect; Play it.
;
; Set up the HP fill loop. This will fill up to 80.
;
[c543]LDX #$50; X = Maximum HP / our loop counter.
[c545]@_loop:; [$c545]
[c545]TXA; A = X
[c546]PHA; Push it to the stack.
;
; Play the unit fill sound effect.
;
[c547]LDA #$13; 0x13 = Unit of HP fill sound effect.
[c549]JSR Sound_PlayEffect; Play the sound.
;
; Add 2HP.
;
[c54c]LDA #$02
[c54e]JSR Player_AddHP; Add the 2HP to the player.
[c551]JSR Game_DrawScreenInFrozenState
;
; Wait 4 frames worth of updates before filling the next 2HP.
;
[c554]JSR WaitForNextFrame
[c557]JSR Sprites_FlipRanges
[c55a]JSR WaitForNextFrame
[c55d]JSR Sprites_FlipRanges
[c560]JSR WaitForNextFrame
[c563]JSR Sprites_FlipRanges
[c566]JSR WaitForNextFrame
[c569]JSR Sprites_FlipRanges
;
; Check if we're done.
;
[c56c]PLA; Pop the max HP.
[c56d]TAX; X = A
[c56e]LDA a:Player_HP_U; Load the player's current HP.
[c571]CMP #$50; Is the player at max HP?
[c573]BCS @_return; If so, return.
[c575]DEX; X--
[c576]BNE @_loop; If X > 0, loop.
[c578]@_return:; [$c578]
[c578]RTS
;============================================================================
; 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:; [$c579]
;
; Run the "Wing Boots Used" IScript.
;
; IScript 0x83 via jump to
IScripts_Begin.
;
[c579]LDA #$83; 0x83 == Wing Boots used IScript.
[c57b]JSR MMC1_LoadBankAndJump; Run IScript:
[c57e].byte BANK_12_LOGIC; Bank = 12
[c57f].word IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the item.
;
[c581]@_afterFarJump:; [$c581]
[c581]JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play a sound effect for the item usage.
;
[c584]LDA #$1a; Set the sound to play to 0x1A (special item sound).
[c586]JSR Sound_PlayEffect; Play the sound.
;
; Calculate the index into the duration table based on
; the player's title.
;
[c589]LDA a:PlayerTitle; Load the player's title.
[c58c]LSR A; Divide by 4.
[c58d]LSR A
[c58e]TAX; X = A (index)
[c58f]LDA TITLE_TO_WINGBOOTS_DURATION,X; Look up in the duration table.
[c592]STA a:DurationWingBoots; Set that as the starting duration.
[c595]JSR UI_DrawTimeValue; Draw the time on the HUD.
[c598]RTS
;============================================================================
; Wing Boots durations by player title.
;
; The duration decreases every 4 player titles.
;
; XREFS:
;
Player_UseWingBoots
;============================================================================
[c599]TITLE_TO_WINGBOOTS_DURATION:; [$c599]
[c599].byte $28; [0]: 40 seconds: Novice, Aspirant, Battler, Fighter
[c59a].byte $1e; [1]: 30 seconds: Adept, Chevalier, Veteran, Warrior
[c59b].byte $14; [2]: 20 seconds: Swordman, Hero, Soldier, Myrmidon
[c59c].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:; [$c59d]
;
; Check if there's any time remaining on the Wing Boots.
;
[c59d]LDA a:DurationWingBoots; Load the remaining time on the Wing Boots.
[c5a0]BMI @_return; If the counter is unset (0xFF), return.
;
; Update the player's status to mark Wing Boots as on.
;
[c5a2]LDA Player_StatusFlag; Load the player's status flags.
[c5a4]ORA #$80; Enable the Wing Boots bit.
[c5a6]STA Player_StatusFlag; Store it.
;
; Update at most once every second.
;
[c5a8]LDA InterruptCounter; Load the interrupt counter.
[c5aa]AND #$3f; Check if we're at the 1 second mark.
[c5ac]BNE @_return; If not, return.
;
; Reduce time on the Wing Boots by a second.
;
[c5ae]DEC a:DurationWingBoots; Decrement the remaining time for the Wing Boots.
[c5b1]LDA a:DurationWingBoots; Load that duration
[c5b4]BMI @_wingBootsDone; If it wrapped from 0 to 0xFF, it's used up. Jump.
[c5b6]JMP UI_DrawTimeValue; Else, draw the time remaining.
;
; The Wing Boots ran out. Remove the flag from the player's
; status.
;
[c5b9]@_wingBootsDone:; [$c5b9]
[c5b9]LDA Player_StatusFlag; Load the player's status flags.
[c5bb]AND #$7f; Clear the Wing Boots flag.
[c5bd]STA Player_StatusFlag; Store it.
;
; Run "Wing Boots are gone" IScript.
;
; IScript 0x96 via jump to
IScripts_Begin.
;
[c5bf]LDA #$96; 0x96 == Wing Boots are gone IScript.
[c5c1]JSR MMC1_LoadBankAndJump; Run the IScript:
[c5c4].byte BANK_12_LOGIC; Bank = 12
[c5c5].word IScripts_Begin-1; Address = IScripts_Begin
;
; It will resume here.
;
[c5c7]@_return:; [$c5c7]
[c5c7]RTS
;============================================================================
; 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:; [$c5c8]
;
; Run the "Hour Glass Used" IScript.
;
; IScript 0x82 via jump to
IScripts_Begin.
;
[c5c8]LDA #$82; Set the IScript to run to 0x82.
[c5ca]JSR MMC1_LoadBankAndJump; Run IScript:
[c5cd].byte BANK_12_LOGIC; Bank = 12
[c5ce].word IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the item.
;
[c5d0]@_afterFarJump:; [$c5d0]
[c5d0]JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play a sound effect for the item usage.
;
[c5d3]LDA #$1a; Set the sound to play to 0x1A (special item sound).
[c5d5]JSR Sound_PlayEffect; Play the sound.
;
; Reduce the player's health by half. This is the cost of
; the Hour Glass.
;
[c5d8]LSR a:Player_HP_U; Divide the upper value of the player's HP by half.
[c5db]ROR a:Player_HP_L; Divide the lower byte of the player's HP by half, and add carry from the upper.
[c5de]JSR UI_DrawPlayerHP; Draw the new HP.
;
; Set the duration of the Hour Glass to 15 seconds.
;
[c5e1]LDA #$0f; Set the duration to 15 seconds.
[c5e3]STA a:DurationHourGlass
;
; Change the music.
;
[c5e6]LDA #$0b; Set the new music to play.
[c5e8]STA Music_Current; And play it.
[c5ea]RTS
[c5eb]Game_DecHourGlassDuration:; [$c5eb]
;
; Check if there's any time remaining on the Hour Glass.
;
[c5eb]LDA a:DurationHourGlass; Load the remaining time on the Hour Glass.
[c5ee]BMI @_return; If the counter is unset (0xFF), return.
;
; Update at most once every second.
;
[c5f0]LDA InterruptCounter; Load the interrupt counter.
[c5f2]AND #$3f; Check if we're at the 1 second mark.
[c5f4]BNE @_return; If not, return.
;
; Reduce time on the Hour Glass by a second.
;
[c5f6]DEC a:DurationHourGlass; Decrement the remaining time for the Hour Glass.
[c5f9]BPL @_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]LDA #$97; 0x97 == Hour Glass is gone IScript.
[c5fd]JSR MMC1_LoadBankAndJump; Run the IScript:
[c600].byte BANK_12_LOGIC; Bank = 12
[c601].word IScripts_Begin-1; Address = IScripts_Begin
;
; Load the area's default music.
;
[c603]@_afterFarJump:; [$c603]
[c603]LDA a:Areas_DefaultMusic; Load the music for the area.
[c606]STA Music_Current; Store it.
[c608]@_return:; [$c608]
[c608]RTS
[c609]Player_Add100XP:; [$c609]
[c609]LDA #$64; A = 100 (lower byte of XP)
[c60b]STA a:Temp_Int24; Store it as an argument.
[c60e]LDA #$00; A = 0 (upper byte of XP).
[c610]STA a:Temp_Int24_M; Store it as an argument.
[c613]JMP Player_UpdateExperience; Update the player's experience using those values.
[c616]Player_UseMattock:; [$c616]
;
; 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]LDY #$00; Lookup table index = 0 (default)
[c618]LDA Player_Flags; Load the player's flags.
[c61a]AND #$40; Is the player facing right?
[c61c]BEQ @LAB_PRG15_MIRROR__c61f; If not, jump.
;
; The player is facing right. Set our offset into the block
; distances table to 1.
;
[c61e]INY; Lookup table index = 1
[c61f]@LAB_PRG15_MIRROR__c61f:; [$c61f]
[c61f]LDA Player_PosX_Block; Get the player's X position.
[c621]CLC
[c622]ADC USE_MATTOCK_BLOCK_DISTANCES,Y; Add the block distance for the facing direction.
[c625]CMP #$f0; Is the player too near to the right edge of the screen?
[c627]BCS @_return; If so, return.
;
; Begin fetching the block at this offset.
;
; Same logic as above for block calculation.
;
[c629]STA Arg_PixelPosX; Store the calculated position as the X argument.
[c62b]LDA Player_PosY; Load the player's current Y position.
[c62d]STA Arg_PixelPosY; Store that as the Y argument.
[c62f]JSR 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]LDA Area_CurrentArea; Load the current area.
[c634]ASL A; Convert to a 4 byte index.
[c635]ASL A
[c636]TAY; Y = index
[c637]LDA ScreenBuffer,X; Load the value from the screen buffer.
[c63a]CMP USE_MATTOCK_BLOCK_TRANSITIONS,Y; Is this block already cleared by the Mattock?
[c63d]BEQ @_useMattock; If not, use the Mattock to clear them.
[c63f]RTS; Else, return.
;
; The Mattock will be used. Store the offsets into the
; screen buffer and array on the stack for later.
;
[c640]@_useMattock:; [$c640]
[c640]TXA; A = X
[c641]PHA; Push to the stack.
[c642]TYA; A = Y
[c643]PHA; Push to the stack.
;
; Run the "Used Mattock" IScript.
;
; IScript 0x81 via jump to
IScripts_Begin.
;
[c644]LDA #$81
[c646]JSR MMC1_LoadBankAndJump; Run IScript:
[c649].byte BANK_12_LOGIC; Bank = 12
[c64a].word IScripts_Begin-1; Address = IScripts_Begin
;
; Remove the Mattock.
;
[c64c]@_afterFarJump:; [$c64c]
[c64c]JSR Player_ClearSelectedItem; Clear the selected item.
;
; Play the sound effect for the Mattock usage.
;
[c64f]LDA #$1a; 0x1A == Special item sound effect.
[c651]JSR 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]PLA; Pull A from the stack.
[c655]TAY; Y = A
[c656]PLA; Pull A from the stack.
[c657]TAX; X = A
;
; Only update every 4 frames, to get a quick animation effect.
;
[c658]@_loop:; [$c658]
[c658]JSR WaitForNextFrame; Wait for 4 frames.
[c65b]JSR WaitForNextFrame
[c65e]JSR WaitForNextFrame
[c661]JSR WaitForNextFrame
;
; Update the screen to place the next block in the sequence.
;
[c664]TYA; A = Y
[c665]PHA; Push to the stack.
[c666]TXA; A = X
[c667]PHA; Push to the stack.
[c668]LDA USE_MATTOCK_BLOCK_TRANSITIONS,Y; Load the block for this position.
[c66b]STA ScreenBuffer,X; And store it in the screen buffer.
[c66e]JSR Area_SetBlocks; Update the level data.
;
; Update to the next block.
;
[c671]TXA; A = X
[c672]CLC
[c673]ADC #$10; A += 32
[c675]TAX; X = A
[c676]LDA a:Arg_BlockAttributesIndex; Load a value.
[c679]STA ScreenBuffer,X; And write it to the screen buffer.
[c67c]JSR Area_SetBlocks; Upate the level data.
;
; Prepare for the next loop iteration.
;
[c67f]PLA; Pull A from the stack.
[c680]TAX; X = A
[c681]PLA; Pull A from the stack.
[c682]TAY; Y = A
[c683]INY; Y++
[c684]TYA; A = Y
[c685]AND #$03; Have we finished updating the blocks?
[c687]BEQ @_return; If so, return.
[c689]JMP @_loop; Else, loop.
[c68c]@_return:; [$c68c]
[c68c]RTS
;============================================================================
; 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]
[c68d].byte $ff; [0]:
[c68e]USE_MATTOCK_BLOCK_DISTANCES_1_:; [$c68e]
[c68e].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]
[c68f].byte $00; [0]: Eolis
[c690].byte $00; [1]:
[c691].byte $00; [2]:
[c692].byte $00; [3]:
[c693].byte $63; [4]: Apolune
[c694].byte $85; [5]:
[c695].byte $86; [6]:
[c696].byte $42; [7]:
[c697].byte $00; [8]: Forepaw
[c698].byte $00; [9]:
[c699].byte $00; [10]:
[c69a].byte $00; [11]:
[c69b].byte $00; [12]: Mascon
[c69c].byte $00; [13]:
[c69d].byte $00; [14]:
[c69e].byte $00; [15]:
[c69f].byte $00; [16]: Victim
[c6a0].byte $00; [17]:
[c6a1].byte $00; [18]:
[c6a2].byte $00; [19]:
[c6a3].byte $00; [20]: Conflate
[c6a4].byte $00; [21]:
[c6a5].byte $00; [22]:
[c6a6].byte $00; [23]:
[c6a7].byte $00; [24]: Daybreak
[c6a8].byte $00; [25]:
[c6a9].byte $00; [26]:
[c6aa].byte $00; [27]:
[c6ab].byte $00; [28]: Evil Fortress
[c6ac].byte $00; [29]:
[c6ad].byte $00; [30]:
[c6ae].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]
[c6af]LDA #$ff
[c6b1]STA a:DurationGlove; Clear the Glove duration.
[c6b4]STA a:DurationOintment; Clear the Ointment duration.
[c6b7]STA a:DurationWingBoots; Clear the Wing Boots duration.
[c6ba]STA a:DurationHourGlass; Clear the Hour Glass duration.
[c6bd]RTS
;============================================================================
; 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:; [$c6be]
;
; Run the "Got Hour Glass" IScript.
;
; IScript 0x8A via jump to
IScripts_Begin.
;
[c6be]LDA #$8a; 0x8A == Hour Glass picked up IScript.
[c6c0]JSR MMC1_LoadBankAndJump; Run IScript:
[c6c3].byte BANK_12_LOGIC; Bank = 12
[c6c4].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6c6]@_afterFarJump:; [$c6c6]
[c6c6]LDA #$08; 0x08 == Item picked up sound.
[c6c8]JSR Sound_PlayEffect; Play it.
;
; Pick up the item.
;
[c6cb]LDA #$0d; 0xD == Hour Glass.
[c6cd]JMP 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]
[c6d0]LDA a:Quests; Load the completed quests.
[c6d3]ORA #$08; Mark this quest as completed.
[c6d5]STA 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:; [$c6d8]
;
; Run the "Got Wing Boots" IScript.
;
; IScript 0x89 via jump to
IScripts_Begin.
;
[c6d8]LDA #$89; 0x89 == Wing boots picked up IScript.
[c6da]JSR MMC1_LoadBankAndJump; Run IScript:
[c6dd].byte BANK_12_LOGIC; Bank = 12
[c6de].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6e0]@_afterFarJump:; [$c6e0]
[c6e0]LDA #$08; 0x8 == Item pick-up sound.
[c6e2]JSR Sound_PlayEffect; Play it.
;
; Pick up the item.
;
[c6e5]LDA #$0f; 0xF == Wing Boots.
[c6e7]JMP 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:; [$c6ea]
;
; Run the "Got Battle Suit" IScript.
;
; IScript 0x8B via jump to
IScripts_Begin.
;
[c6ea]LDA #$8b; 0x8B == Battle Suit picked up IScript.
[c6ec]JSR MMC1_LoadBankAndJump; Run IScript:
[c6ef].byte BANK_12_LOGIC; Bank = 12
[c6f0].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c6f2]@_afterFarJump:; [$c6f2]
[c6f2]LDA #$08; 0x08 == Item picked up sound.
[c6f4]JSR 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]LDX a:NumberOfWeapons; X = Number of weapons.
[c6fa]CPX #$04; If < 4
[c6fc]BCC @_addItem; Then, add it to the latest slot.
[c6fe]LDX #$03; Else, cap X to 3.
;
; Add the Battle Suit to the inventory.
;
[c700]@_addItem:; [$c700]
[c700]LDA #$03; 0x03 == Battle Suit
[c702]STA ArmorInventory,X; Set in the inventory slot
;
; Increase the number of armors collected.
;
[c705]INX; X++
[c706]STX a:NumberOfArmors; Store as the number of armors.
[c709]RTS
;============================================================================
; 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:; [$c70a]
;
; Run the "Got Battle Helmet" IScript.
;
; IScript 0x8C via jump to
IScripts_Begin.
;
[c70a]LDA #$8c; 0x8C == Battle Helmet picked up IScript.
[c70c]JSR MMC1_LoadBankAndJump; Run it.
[c70f].byte BANK_12_LOGIC; Bank = 12
[c710].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c712]@_afterFarJump:; [$c712]
[c712]LDA #$08; 0x08 == Item picked up sound.
[c714]JSR Sound_PlayEffect; Play it.
;
; Cap the number of shields in the inventory.
;
[c717]LDX a:NumberOfShields; X = Number of shields.
[c71a]CPX #$04; If X < 4:
[c71c]BCC @_addItem; Then, add to the latest slot.
[c71e]LDX #$03; Else, cap X to 3.
;
; Add the Battle Helmet to the inventory.
;
[c720]@_addItem:; [$c720]
[c720]LDA #$03; 0x03 == Battle Helmet.
[c722]STA ShieldInventory,X; Set in the inventory slot.
;
; Increase the number of shields collected.
;
[c725]INX; X++
[c726]STX a:NumberOfShields; Store as the number of shields.
[c729]RTS
;============================================================================
; 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:; [$c72a]
;
; Run the "Got Dragon Slayer" IScript.
;
; IScript 0x8D via jump to
IScripts_Begin.
;
[c72a]LDA #$8d; 0x8D == Dragon Slayer picked up IScript.
[c72c]JSR MMC1_LoadBankAndJump; Run it.
[c72f].byte BANK_12_LOGIC; Bank = 12
[c730].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c732]@_afterFarJump:; [$c732]
[c732]LDA #$08; 0x08 == Item picked up sound.
[c734]JSR 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]LDX a:NumberOfArmors; X = number of armors.
[c73a]CPX #$04; If < 4
[c73c]BCC @_addItem; Then, add it to the latest slot.
[c73e]LDX #$03; Else, cap X to 3.
;
; Add the Dragon Slayer to the inventory.
;
[c740]@_addItem:; [$c740]
[c740]LDA #$03; 0x03 == Dragon Slayer
[c742]STA WeaponInventory,X; Set in the inventory slot.
;
; Increase the number of weapons collected.
;
[c745]INX; X++
[c746]STX a:NumberOfWeapons; Store as the number of weapons.
[c749]RTS
;============================================================================
; 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]
[c74a]LDA a:Quests; Load the quests bitmask.
[c74d]ORA #$10; Bit 0x10 == Mattock
[c74f]STA 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:; [$c752]
;
; Run the "Got Mattock" IScript.
;
; IScript 0x88 via jump to
IScripts_Begin.
;
[c752]LDA #$88; 0x88 == Mattock picked up IScript.
[c754]JSR MMC1_LoadBankAndJump; Run IScript:
[c757].byte BANK_12_LOGIC; Bank = 12
[c758].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c75a]@_afterFarJump:; [$c75a]
[c75a]LDA #$08; 0x08 == Item picked up sound.
[c75c]JSR Sound_PlayEffect; Play it.
;
; Load the Mattock into the inventory.
;
[c75f]LDA #$09; 0x09 == Mattock
[c761]JMP Player_PickUpItem; Pick up the Mattock.
[c764]Player_PickUp:; [$c764]
;
; Check if this is the Mattock.
;
[c764]CMP #$50; 0x50 == Mattock
[c766]BEQ Player_PickUpMattock; If 0x50, this is the Mattock. Pick it up.
;
; Check if this is the Magical Rod.
;
[c768]CMP #$57; 0x57 == Magical Rod
[c76a]BNE @_checkBattleSuit
[c76c]JMP Player_PickUpMagicalRod; This is the Magical Rod. Pick it up.
;
; Check if this is the battle suit.
;
[c76f]@_checkBattleSuit:; [$c76f]
[c76f]CMP #$58; 0x58 == Battle Suit
[c771]BNE @_checkBattleHelmet
[c773]JMP Player_PickUpBattleSuit; This is the Battle Suit. Pick it up.
;
; Check if this is the battle helmet.
;
[c776]@_checkBattleHelmet:; [$c776]
[c776]CMP #$59; 0x59 == Battle Helmet
[c778]BEQ Player_PickUpBattleHelmet; This is the Battle Helmet. Pick it up.
;
; Check if this is the Dragon Slayer.
;
[c77a]CMP #$5a; 0x5A == Dragon Slayer
[c77c]BEQ Player_PickUpDragonSlayer; This is the Dragon Slayer. Pick it up.
;
; Check if this is the Mattock.
;
[c77e]CMP #$5b; 0x5B == Mattock
[c780]BEQ Player_PickUpMattockWithQuest; This is the Mattock. Pick it up.
;
; Check if this is the Wing Boots.
;
[c782]CMP #$55; 0x55 == Wing Boots
[c784]BNE @_checkWingBootsQuest8
[c786]JMP Player_PickUpWingBoots; This is the Wing Boots. Pick it up.
;
; Check if this is the Wing Boots (with Quest 0x08).
;
[c789]@_checkWingBootsQuest8:; [$c789]
[c789]CMP #$5c; 0x5C == Wing Boots for Quest 8
[c78b]BNE @_checkHourGlass
[c78d]JMP Player_PickUpWingBootsWithQuest; This is the Wing Boots for Quest 8. Pick it up.
;
; Check if this is the Hour Glass.
;
[c790]@_checkHourGlass:; [$c790]
[c790]CMP #$56; 0x56 == Hour Glass
[c792]BNE @_checkRedPotion
[c794]JMP Player_PickUpHourGlass; This is the Hour Glass. Pick it up.
;
; Check if this is the Red Potion.
;
[c797]@_checkRedPotion:; [$c797]
[c797]CMP #$5d; 0x5D == Red Potion (1)
[c799]BNE @_checkPoison
[c79b]JMP Player_PickUpRedPotion; This is the Red Potion (1). Pick it up.
;
; Check if this is the Poison.
;
[c79e]@_checkPoison:; [$c79e]
[c79e]CMP #$5e; 0x5E == Poison
[c7a0]BNE @_checkGlove1
[c7a2]JMP Player_PickUpPoison; This is the Poison. Pick it up.
[c7a5]@_checkGlove1:; [$c7a5]
[c7a5]CMP #$5f; 0x5F == Glove
[c7a7]BEQ Player_PickUpGlove; This is the Glove. Pick it up.
;
; Check if this is the Ointment.
;
[c7a9]CMP #$60; 0x60 == Ointment
[c7ab]BNE @_checkGlove2
[c7ad]JMP Player_PickUpOintment; This is the Ointment. Pick it up.
[c7b0]@_checkGlove2:; [$c7b0]
[c7b0]SEC
[c7b1]SBC #$48
[c7b3]TAY
[c7b4]BEQ Player_PickUpGlove; This is the Glove. Pick it up.
;
; Check if this is the Black Onyx.
;
[c7b6]DEY; 0x49 == Black Onyx
[c7b7]BEQ Player_PickUpBlackOnyx; If 0x49, this is the Black Onyx. Pick it up.
;
; Check if this is the Pendant.
;
[c7b9]DEY; 0x4A == Pendant
[c7ba]BEQ Player_PickUpPendant; If 0x4A, this is the pendant. Pick it up.
;
; Check if this is the Red Potion (another variant).
;
[c7bc]DEY; 0x4B == Red Potion (2)
[c7bd]BEQ Player_PickUpRedPotion; If 0x4B, this is the Red Potion (2). Pick it up.
;
; Check if this is the Poison.
;
[c7bf]DEY; 0x4C == Poison
[c7c0]BEQ Player_PickUpPoison; If 0x4C, this is the Poison. Pick it up.
;
; Check if this is the Elixir.
;
[c7c2]DEY; 0x4D == Elixir
[c7c3]BNE @_checkOintment
[c7c5]JMP Player_PickUpElixir; This is the Elixir. Pick it up.
;
; Check if this is the Ointment.
;
[c7c8]@_checkOintment:; [$c7c8]
[c7c8]DEY; 0x4E == Ointment
[c7c9]BNE @_exit
[c7cb]JMP Player_PickUpOintment; This is the Ointment. Pick it up.
[c7ce]@_exit:; [$c7ce]
[c7ce]RTS
;============================================================================
; 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:; [$c7cf]
;
; Run the "Got Glove" IScript.
;
; IScript 0x92 via jump to
IScripts_Begin.
;
[c7cf]LDA #$92; 0x92 == Glove picked up IScript.
[c7d1]JSR MMC1_LoadBankAndJump; Run IScript:
[c7d4].byte BANK_12_LOGIC; Bank = 12
[c7d5].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c7d7]@_afterFarJump:; [$c7d7]
[c7d7]LDA #$08; 0x08 == Item picked up sound.
[c7d9]JSR Sound_PlayEffect; Play it.
;
; Activate the Glove by setting its duration, and then
; add 100XP to the player.
;
[c7dc]LDA #$14; A = 20 seconds.
[c7de]STA a:DurationGlove; Set as the Glove duration.
[c7e1]JMP 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:; [$c7e4]
;
; Run the "Got Black Onyx" IScript.
;
; IScript 0x8E via jump to
IScripts_Begin.
;
[c7e4]LDA #$8e; 0x8E == Black Onyx picked up IScript.
[c7e6]JSR MMC1_LoadBankAndJump; Run IScript:
[c7e9].byte BANK_12_LOGIC; Bank = 12
[c7ea].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c7ec]@_afterFarJump:; [$c7ec]
[c7ec]LDA #$08; 0x08 == Item picked up sound.
[c7ee]JSR Sound_PlayEffect; Play it.
;
; Add the item to the player's special items list.
;
[c7f1]LDA a:SpecialItems; Load the current special items bitmask.
[c7f4]ORA #$01; Bit 0x01 == Black Onyx.
[c7f6]STA a:SpecialItems; Save it back out.
[c7f9]RTS
;============================================================================
; 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:; [$c7fa]
;
; Run the "Got Glove" IScript.
;
; IScript 0x8F via jump to
IScripts_Begin.
;
[c7fa]LDA #$8f; 0x8F = Pendant picked up IScript.
[c7fc]JSR MMC1_LoadBankAndJump; Run IScript:
[c7ff].byte BANK_12_LOGIC; Bank = 12
[c800].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c802]@_afterFarJump:; [$c802]
[c802]LDA #$08; 0x08 = Item picked up sound.
[c804]JSR Sound_PlayEffect; Play it.
;
; Add the item to the player's special items list.
;
[c807]LDA a:SpecialItems; Load the curent special items bitmask.
[c80a]ORA #$02; Bit 0x02 == Pendant.
[c80c]STA a:SpecialItems; Save it back out.
[c80f]RTS
;============================================================================
; 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:; [$c810]
;
; Run the "Got Magical Rod" IScript.
;
; IScript 0x90 via jump to
IScripts_Begin.
;
[c810]LDA #$90; 0x90 == Magical Rod picked up IScript.
[c812]JSR MMC1_LoadBankAndJump; Run IScript:
[c815].byte BANK_12_LOGIC; Bank = 12
[c816].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c818]@_afterFarJump:; [$c818]
[c818]LDA #$08; 0x08 == Item picked up sound.
[c81a]JSR Sound_PlayEffect; Play it.
;
; Add the Magical Rod to the inventory.
;
[c81d]LDA a:SpecialItems; Load the current special items bitmask.
[c820]ORA #$04; Bit 0x04 == Magical Rod.
[c822]STA a:SpecialItems; Store it.
[c825]RTS
;============================================================================
; 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:; [$c826]
;
; Run the "Got Red Potion" IScript.
;
; IScript 0x87 via jump to
IScripts_Begin.
;
[c826]LDA #$87; 0x87 == Red Potion picked up IScript.
[c828]JSR MMC1_LoadBankAndJump; Run IScript:
[c82b].byte BANK_12_LOGIC; Bank = 12
[c82c].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c82e]@_afterFarJump:; [$c82e]
[c82e]LDA #$08; 0x08 == Item picked up sound.
[c830]JSR Sound_PlayEffect; Play it.
;
; Add the Red Potion to the inventory.
;
[c833]LDA #$10; 0x10 == Red Potion.
[c835]JSR Player_PickUpItem; Pick up the item.
[c838]LDX a:CurrentSpriteIndex; X = current sprite index (restored?)
[c83b]RTS
;============================================================================
; 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:; [$c83c]
;
; Run the "Got Poison" IScript.
;
; IScript 0x91 via jump to
IScripts_Begin.
;
[c83c]LDA #$91; 0x91 = Poison picked up IScript.
[c83e]JSR MMC1_LoadBankAndJump; Run IScript:
[c841].byte BANK_12_LOGIC; Bank = 12
[c842].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for the player taking damage.
;
[c844]@_afterFarJump:; [$c844]
[c844]LDA #$04; 0x084 = Player took damage sound.
[c846]JSR Sound_PlayEffect; Play it.
;
; Set temporary invincibility frames for the player.
;
[c849]LDA #$3c; A = 60 iframes
[c84b]STA Player_InvincibilityPhase; Set it as the starting invincibility phase.
;
; Mark the player as hit.
;
[c84d]LDA Player_StatusFlag; Load the player's status flags.
[c84f]ORA #$02; Bit 0x02 == Player was hit.
[c851]STA Player_StatusFlag; Save it.
;
; Remove 16 points from the player's health.
;
[c853]LDA #$00; A = 0
[c855]STA a:Arg_PlayerHealthDelta_L; Store as the lower byte of HP to remove.
[c858]LDA #$10; A = 16
[c85a]STA a:Arg_PlayerHealthDelta_U; Store as the upper byte of HP to remove.
[c85d]JSR Player_ReduceHP; Reduce the HP by that amount.
[c860]LDX a:CurrentSpriteIndex; X = current sprite index (restored?)
[c863]RTS
;============================================================================
; 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:; [$c864]
;
; Run the "Got Elixir" IScript.
;
; IScript 0x86 via jump to
IScripts_Begin.
;
[c864]LDA #$86; 0x86 == Elixir picked up IScript.
[c866]JSR MMC1_LoadBankAndJump; Run IScript:
[c869].byte BANK_12_LOGIC; Bank = 12
[c86a].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c86c]@_afterFarJump:; [$c86c]
[c86c]LDA #$08; 0x08 == Item picked up sound.
[c86e]JSR Sound_PlayEffect; Play it.
;
; Add the Elixir to the inventory.
;
[c871]LDA a:SpecialItems; Load the current special items bitmask.
[c874]ORA #$08; Bit 0x08 == Elixir.
[c876]STA a:SpecialItems; Save it back out.
[c879]RTS
;============================================================================
; 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:; [$c87a]
;
; Run "Got Ointment" IScript.
;
; IScript 0x94 via jump to
IScripts_Begin.
;
[c87a]LDA #$94; 0x94 == Ointment picked up IScript.
[c87c]JSR MMC1_LoadBankAndJump; Run IScript:
[c87f].byte BANK_12_LOGIC; Bank = 12
[c880].word IScripts_Begin-1; Address = IScripts_Begin
;
; Play the sound effect for picking up an item.
;
[c882]@_afterFarJump:; [$c882]
[c882]LDA #$08; 0x08 == Item picked up sound.
[c884]JSR 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]LDA #$1e; A = 30 seconds.
[c889]STA a:DurationOintment; Set as the Ointment duration.
[c88c]JMP Player_Add100XP; Give the player 100XP.
[c88f]GameLoop_CountdownItems:; [$c88f]
[c88f]JSR Game_DecHourGlassDuration
[c892]JSR Game_DecWingBootsDuration
[c895]JSR Game_DecGloveDuration
[c898]JMP 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:; [$c89b]
;
; Check if the Glove is active.
;
[c89b]LDA a:DurationGlove; Load the Glove's duration.
[c89e]BMI @_return; If it's 0xFF (not activated), return.
;
; Check if a second has passed.
;
[c8a0]LDA InterruptCounter; Load the interrupt counter.
[c8a2]AND #$3f; Check if it's an even second boundary.
[c8a4]BNE @_return; If not, return.
;
; Decrement and check if the Glove is still active.
;
[c8a6]DEC a:DurationGlove; Decrement the Glove's duration by 1 second.
[c8a9]BPL @_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]LDA #$93; 0x93 == Glove is gone IScript.
[c8ad]JSR MMC1_LoadBankAndJump; Run the IScript:
[c8b0].byte BANK_12_LOGIC; Bank = 12
[c8b1].word IScripts_Begin-1; Address = IScripts_Begin
[c8b3]@_return:; [$c8b3]
[c8b3]RTS
;============================================================================
; 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:; [$c8b4]
;
; Check if the Ointment is active.
;
[c8b4]LDA a:DurationOintment; Load the Ointment's duration.
[c8b7]BMI @_return; If it's 0xFF (not activated), return.
;
; Check if a second has passed.
;
[c8b9]LDA InterruptCounter; Load the interrupt counter.
[c8bb]AND #$3f; Check if it's an even second boundary.
[c8bd]BNE @_return; If not, return.
;
; Decrement and check if the Ointment is still active.
;
[c8bf]DEC a:DurationOintment; Decrement the Ointment's duration by 1 second.
[c8c2]BPL @_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]LDA #$95; 0x95 == Ointment is gone IScript.
[c8c6]JSR MMC1_LoadBankAndJump; Run the IScript:
[c8c9].byte BANK_12_LOGIC; Bank = 12
[c8ca].word IScripts_Begin-1; Address = IScripts_Begin
[c8cc]@_return:; [$c8cc]
[c8cc]RTS
;============================================================================
; 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:; [$c8cd]
;
; Check first if the inventory is full.
;
[c8cd]LDX a:NumberOfItems; Load the number of items in the inventory.
[c8d0]CPX #$08; Is there room?
[c8d2]BCS @_return; If full, exit.
;
; There's room. Set the item in the inventory and increment
; the item counter.
;
[c8d4]STA ItemInventory,X; Store the item in the next available slot.
[c8d7]INX; Set the next available slot.
[c8d8]STX a:NumberOfItems
[c8db]@_return:; [$c8db]
[c8db]RTS
[c8dc]UI_ClearSelectedItemPic:; [$c8dc]
;
; Set the draw position to 0x13C0 (top-left of selected
; item tiles).
;
[c8dc]LDA #$13; A = 0x13
[c8de]STA a:PPU_TargetAddr_U; Set as upper byte.
[c8e1]LDA #$c0; A = 0xC0
[c8e3]STA 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]LDX #$04; X = 4 (counter).
[c8e8]@_vertLoop:; [$c8e8]
[c8e8]TXA; A = X
[c8e9]PHA; Push A to the stack.
;
; Set the draw length as 16 bytes (this tile).
;
[c8ea]LDA #$10; A = 16
[c8ec]JSR PPUBuffer_QueueCommandOrLength; Write as the length.
[c8ef]LDY #$10; Y = 16
[c8f1]LDA #$00; A = 0
[c8f3]@_horizLoop:; [$c8f3]
[c8f3]STA PPUBuffer,X; Write a 0 to the PPU buffer.
[c8f6]INX; X++
[c8f7]DEY; Y--
[c8f8]BNE @_horizLoop; If not 0, loop.
;
; This row is finished. Prepare to draw the next (if any).
;
[c8fa]STX PPUBuffer_WriteOffset; Store X as the new upper bounds.
[c8fc]LDA a:PPU_TargetAddr; A = target address to draw to.
[c8ff]CLC
[c900]ADC #$10; Increment by 16 (next tile).
[c902]STA a:PPU_TargetAddr; Store as the new lower byte of the target address.
[c905]LDA a:PPU_TargetAddr_U; Load the upper byte.
[c908]ADC #$00; Add the carry flag, if any.
[c90a]STA a:PPU_TargetAddr_U; And save it.
[c90d]PLA; Pull A from the stack (full 0..4 loop counter).
[c90e]TAX; X = A
[c90f]DEX; X--
[c910]BNE @_vertLoop; If > 0, loop.
[c912]RTS
[c913]Game_Init:; [$c913]
[c913]SEI
[c914]CLD
[c915]LDX #$ff; X = 0xFF
[c917]TXS; 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]LDA #$00; A = 0
[c91a]STA a:PPUMASK; Set as the PPU mask.
[c91d]LDA #$18; A = 0x18
[c91f]STA 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]LDA a:PPUSTATUS; Read PPU status.
[c925]@_readPPUStatus1:; [$c925]
[c925]LDA a:PPUSTATUS; Read PPU status.
[c928]BPL @_readPPUStatus1; If > -1, loop.
[c92a]@_readPPUStatus2:; [$c92a]
[c92a]LDA a:PPUSTATUS; Read PPU status.
[c92d]BPL @_readPPUStatus2; If > -1, loop.
;
; Set PPUMASK to 0 to allow transferring large amounts of
; Data to VRAM.
;
[c92f]LDA #$00; A = 0
[c931]STA a:PPUMASK; Set as the PPU mask.
[c934]LDX #$ff; X = 0xFF
[c936]TXS; Transfer to stack.
;
; Clear RAM, setting all ranges of bytes used for state to 0.
;
[c937]LDX #$00; X = 0 (loop counter)
[c939]@_loop:; [$c939]
[c939]CPX #$fc; Is X >= 252?
[c93b]BCS @_clearState; If so, jump.
[c93d]STA Temp_00,X; Clear memory at address X.
[c93f]@_clearState:; [$c93f]
[c93f]STA Temp_0200,X; Clear memory at $0200 + X
[c942]STA CurrentSprites_InternalBehaviorStates_4_,X; Clear memory at $0300 + X
[c945]STA FirstColumnInRightScreen_10_,X; Clear memory at $0400 + X
[c948]STA PPUBuffer,X; Clear memory at $0500 + X
[c94b]STA ScreenBuffer,X; Clear memory at $0600 + X
[c94e]STA SPRITE_0_RANGE_1_START,X; Clear memory at $0700 + X
[c951]INX; X++
[c952]BNE @_loop; If X != 0, loop.
[c954]JSR Game_InitMMCAndBank; Initialize the MMC1 chip and bank.
[c957]JSR Game_InitScreenAndMusic; Initialize the screen and music.
[c95a]JMP Game_InitStateForStartScreen; Initialize the start screen.
;
; Check whether the OAM needs to be reset.
;
;
; XREFS:
;
OnInterrupt
;
[c95d]OnInterrupt__updatePPUOAMAndAudio:; [$c95d]
[c95d]LDA Game_NeedOAMReset; Check the reset flag.
[c95f]BEQ OnInterrupt__updatePPUAndAudio; If 0, don't reset. Jump to PPU/audio management.
[c961]LDA #$00
[c963]STA a:OAMADDR; Reset OAMADDR to 0.
[c966]STA Game_NeedOAMReset; Clear the reset flag.
[c968]LDA #$07
[c96a]STA a:OAMDMA; Initialize the OAM, writing from $0700.
[c96d]OnInterrupt__updatePPUAndAudio:; [$c96d]
[c96d]JSR PPU_HandleOnInterrupt; Run PPU on-interrupt handlers.
;
; Update the audio so music keeps playing.
;
;
; XREFS:
;
OnInterrupt
;
[c970]OnInterrupt__updateAudio:; [$c970]
[c970]INC InterruptCounter; Increment the interrupt counter.
[c972]LDA a:CurrentROMBank; Load the current ROM bank.
[c975]PHA; Push it to the stack.
[c976]LDX #$05
[c978]JSR MMC1_EnsurePRG; Switch to bank 5.
[c97b]JSR thunk_Music_HandleOnInterrupt; Run audio interrupt handlers.
[c97e]JSR thunk_SoundEffects_HandleOnInterrupt; Run sound playback interrupt handlers.
[c981]PLA; Pop the bank from the stack.
[c982]TAX
[c983]JSR MMC1_EnsurePRG; Switch back to the bank.
[c986]JMP 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]
[c989]LDX PauseInterruptCounter; Load the interrupt counter.
[c98b]CPX #$02; Is it < 2?
[c98d]BCS @_clearPPUMask; If so, jump.
[c98f]INC PauseInterruptCounter; Increment the counter.
[c991]@_clearPPUMask:; [$c991]
[c991]LDA #$00
[c993]STA a:PPUMASK; Clear PPUMASK.
[c996]JMP OnInterrupt__updateAudio; Update the audio.
[c999]OnInterrupt:; [$c999]
;
; Push all registers to the stack.
;
[c999]PHA; Push A to the stack.
[c99a]TXA; Set A = X.
[c99b]PHA; Push A (X) to the stack.
[c99c]TYA; Set A = Y.
[c99d]PHA; Push A (Y) to the stack.
;
; Check what will be handled this interrupt.
;
[c99e]LDA Game_InterruptHandlersEnabled; Check whether interrupt handling code should be run.
[c9a0]BEQ OnInterrupt__noProcessInterrupts; If not, jump.
;
; Interrupts may be run. See if it's time to run them.
;
[c9a2]LDA Game_InterruptsHandledLatch; Check if we're on a frame where we should run interrupts.
[c9a4]BNE OnInterrupt__updatePPUOAMAndAudio; Jump to update the PPU, OAM, and Audio.
;
; Ready to handle routine operations.
;
[c9a6]INC Game_InterruptsHandledLatch; Flip the toggle.
;
; Reset the OAM.
;
; This will write $00 to OAMADDR and then use OAMDMA.
;
[c9a8]LDA #$00
[c9aa]STA a:OAMADDR; Clear OAMADDR.
[c9ad]STA Game_NeedOAMReset; Turn off the reset flag.
[c9af]LDA #$07
[c9b1]STA a:OAMDMA; Initialize the OAM, writing from SPRITE_0_RANGE_1_START ($0700).
;
; Update the PPU state.
;
[c9b4]JSR PPU_HandleOnInterrupt
;
; Switch to bank 5 (audio logic) and run audio updates.
;
[c9b7]LDA a:CurrentROMBank; Load the current bank.
[c9ba]PHA; Push it to the stack.
[c9bb]LDX #$05
[c9bd]JSR MMC1_EnsurePRG; Switch to bank 5.
[c9c0]JSR thunk_Music_HandleOnInterrupt; Run audio interrupt handling code.
[c9c3]JSR thunk_SoundEffects_HandleOnInterrupt; Run sound playback interrupt handling code.
[c9c6]PLA; Pop the previous bank from the stack.
[c9c7]TAX
[c9c8]JSR MMC1_EnsurePRG; Switch back to it.
;
; Handle input management and final state.
;
[c9cb]JSR Input_HandleOnInterrupt; Run input interrupt handlers.
[c9ce]INC InterruptCounter; Increment the interrupt counter.
;
; Restore all registers from the stack.
;
;
; XREFS:
;
OnInterrupt
;
[c9d0]OnInterrupt__popAndReturn:; [$c9d0]
[c9d0]PLA; Pull A from the stack.
[c9d1]TAY; Set Y = A
[c9d2]PLA; Pull A from the stack.
[c9d3]TAX; Set X = A
[c9d4]PLA; Pull A from the satck
[c9d5]OnHardwareInterrupt:; [$c9d5]
[c9d5]RTI; 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:; [$c9d6]
;
; Handle any scrolling-related screen updates and flush
; the PPU buffer to the screen.
;
[c9d6]JSR Screen_RunWriteScrollDataHandler; Run the handler for any screen scrolling.
[c9d9]JSR PPUBuffer_Draw; Flush the PPU buffer to the screen.
[c9dc]LDA PPU_Mask; Load the stored PPU mask.
[c9de]STA a:PPUMASK; And set it on the PPU.
[c9e1]LDA Sprites_Sprite0Mode; Load the sprite 0 mode.
[c9e3]BMI @_setScroll; If 0xFF (no sprite 0), jump.
[c9e5]LDA PPU_ForceLowerPatternTables; Load the Force Lower Pattern Tables setting.
[c9e7]BEQ @_clearNametable2400; If not forcing, then jump.
;
; Clear the following flags:
;
; * Nametable 2400
; * Sprite pattern 1000
; * BGTable Addr 1000
;
[c9e9]LDA PPU_ControlFlags; Load the saved PPU control flags.
[c9eb]AND #$e6; Clear flags.
[c9ed]STA a:PPUCTRL; Set it on the PPU.
[c9f0]JMP @_resetScroll; Jump to update scroll positions.
;
; Clear the Nametable 2400 flag.
;
[c9f3]@_clearNametable2400:; [$c9f3]
[c9f3]LDA PPU_ControlFlags
[c9f5]AND #$fe
[c9f7]STA a:PPUCTRL
;
; Reset the screen scroll.
;
[c9fa]@_resetScroll:; [$c9fa]
[c9fa]LDA #$00
[c9fc]STA a:PPUSCROLL; Clear horizontal screen scroll.
[c9ff]STA a:PPUSCROLL; Clear vertical screen scroll.
;
; Wait until sprite 0 isn't hit in the sprite update.
;
[ca02]@_waitSprite0NotHitLoop:; [$ca02]
[ca02]BIT a:PPUSTATUS; Test the Sprite 0 Hit flag.
[ca05]BVS @_waitSprite0NotHitLoop; If set, loop.
;
; Now wait until it is hit.
;
[ca07]@_waitSprite0Hit:; [$ca07]
[ca07]BIT a:PPUSTATUS; Test the Sprite 0 Hit flag.
[ca0a]BVC @_waitSprite0Hit; if not set, loop.
;
; Loop 160 times, giving the screen time to update.
;
[ca0c]LDX #$a0; X = 160 (loop counter).
[ca0e]@_loop160Times:; [$ca0e]
[ca0e]DEX; X--
[ca0f]BNE @_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]
[ca11]LDA PPU_ScrollScreen; Load the current scroll screen.
[ca13]AND #$01; Convert to an even/odd value (0 or 1) to select the nametable.
[ca15]ORA PPU_ControlFlags; OR with the PPU controls.
[ca17]STA a:PPUCTRL; Store as the new PPU control flags.
[ca1a]LDA PPU_ScrollX; Load the calculated scroll X.
[ca1c]STA a:PPUSCROLL; Write as the horizontal scroll offset.
[ca1f]LDA #$00; Hard-code scroll Y as 0.
[ca21]STA a:PPUSCROLL; Write as the vertical scroll offset.
[ca24]RTS
[ca25]WaitForNextFrame:; [$ca25]
[ca25]LDA #$00; A = 0
[ca27]STA Game_InterruptsHandledLatch; Set as the frame toggle state.
[ca29]@_loop:; [$ca29]
[ca29]LDA Game_InterruptsHandledLatch; Load the frame toggle state from the interrupt handler.
[ca2b]BEQ @_loop; If it's 0, loop.
[ca2d]RTS; Else, return.
[ca2e]WaitForInterrupt:; [$ca2e]
[ca2e]LDA InterruptCounter; Load the interrupt count.
[ca30]@_loop:; [$ca30]
[ca30]CMP InterruptCounter; Has the interrupt counter changed?
[ca32]BEQ @_loop; If not, loop.
[ca34]RTS
;============================================================================
; 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:; [$ca35]
;
; Store the current button mask as the new previous button mask.
;
[ca35]LDA Joy1_ButtonMask; A = Stored controller 1 button mask
[ca37]STA Joy1_PrevButtonMask; Store as the previous button mask.
;
; Prepare to read the controller state.
;
[ca39]LDA #$01; A = 1
[ca3b]STA a:JOY1; Trigger loading controller 1 bits.
[ca3e]LDA #$00
[ca40]STA a:JOY1; Prepare for read.
[ca43]STA Joy1_ButtonMask; Reset controller 1 button mask to 0.
[ca45]STA Joy2_ButtonMask; Reset controller 2 button mask to 0.
;
; Read the controller 1 state into the button mask.
;
[ca47]LDX #$08; X = 8 (loop counter)
[ca49]@_loop1:; [$ca49]
[ca49]LDA a:JOY1; A = Current controller 1 state.
[ca4c]AND #$03; Take the first two bits.
[ca4e]LSR A; Shift right 1. Bit 0 goes into Carry.
[ca4f]ROL Joy1_ButtonMask; Rotate the target bitmask left, moving Carry into bit 0.
[ca51]ORA Joy1_ButtonMask; OR to the generated bitmask.
[ca53]STA Joy1_ButtonMask; Store as the new button mask.
[ca55]DEX; X--
[ca56]BNE @_loop1; If > 0, loop.
;
; Read the controller 2 state into the button mask.
;
; This is only used for the debug level skip code.
;
[ca58]LDX #$08; X = 8 (loop counter).
[ca5a]@_loop2:; [$ca5a]
[ca5a]LDA a:JOY2; A = Current controller 2 state.
[ca5d]AND #$01; Take the first bit.
[ca5f]LSR A; Shift right 1. Bit 0 goes into Carry.
[ca60]ROL Joy2_ButtonMask; Rotate the target bitmask left, moving Carry into bit 0.
[ca62]DEX; X--
[ca63]BNE @_loop2; If > 0, loop.
;
; Compute the changed button mask between the last read and now.
;
[ca65]LDA Joy1_ButtonMask; Load the controller 1 button mask.
[ca67]EOR Joy1_PrevButtonMask; XOR with the previously-changed, yielding the button differences.
[ca69]AND Joy1_ButtonMask; AND with the new button mask.
[ca6b]STA Joy1_ChangedButtonMask; And store as the changed button mask.
[ca6d]RTS
;============================================================================
; 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]
[ca6e]LDX Random_Offset; Load the current offset into the bank.
[ca70]LDA Sprites_UpdateAll,X; Load the byte value at that offset.
[ca73]EOR Joy1_ButtonMask; XOR with the controler 1 button mask.
[ca75]INC Random_Offset; Increment the offset.
[ca77]RTS
[ca78]Game_InitScreenAndMusic:; [$ca78]
;
; Initialize PPU settings.
;
[ca78]LDA #$10
[ca7a]STA PPU_ControlFlags; Set PPU control flags to BGTable and Address $1000.
[ca7c]LDA #$00
[ca7e]STA PPU_ScrollX; Clear scroll X.
[ca80]STA PPU_ScrollScreen; Clear scroll screen.
[ca82]STA PPU_ScrollY; Clear scroll Y.
[ca84]LDA #$1e
[ca86]STA PPU_Mask; Set screen color mode to enable sprites, BG, and showing left-most 8 pixels.
[ca88]JSR Screen_ResetSpritesForNonGame; Reset sprites for a non-game screen.
[ca8b]JSR PPU_InitAttributeAndNameTables; Initialize attributes and nametables.
;
; Stop any interrupt handling for the game.
;
[ca8e]LDA #$00
[ca90]STA Game_InterruptHandlersEnabled; Disable interrupt handlers.
[ca92]STA a:Maybe_Game_Ready; Disable the game ready state.
;
; Switch to bank 5 and set up sound.
;
[ca95]LDA a:CurrentROMBank; Load the current ROM bank.
[ca98]PHA; Push it to the stack.
[ca99]LDX #$05; Bank 5 = Sound.
[ca9b]JSR MMC1_UpdateROMBank; Switch to it.
[ca9e]JSR thunk_Audio_InitPlayingState; Initialize the sound play state.
[caa1]JSR thunk_SoundEffects_Init; Initialize sound.
[caa4]PLA; Pop the previous bank from the stack.
[caa5]TAX; X = previous bank.
[caa6]JSR MMC1_UpdateROMBank; And switch back to it.
;
; Re-enable the game.
;
[caa9]LDA #$00
[caab]STA Game_InterruptsHandledLatch; Disable the interrupt handlers latch.
[caad]STA a:Game_PausedState; Disable the pause state.
[cab0]STA PPU_ForceLowerPatternTables; Disable forcing lower pattern tables.
[cab2]JMP 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]
[cab5]STA a:PPUADDR; Set the upper byte.
[cab8]STX a:PPUADDR; Set the lower byte.
[cabb]RTS
;============================================================================
; TODO: Document PPU_InitAttributeAndNameTables
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_InitScreenAndMusic
;============================================================================
[cabc]PPU_InitAttributeAndNameTables:; [$cabc]
[cabc]LDA #$20
[cabe]STA Temp_PPU_NameTableValue
;
; Set address 0x2000: Name table 0.
;
; Write 32 to 8 bytes.
;
[cac0]LDA #$20
[cac2]LDX #$00
[cac4]JSR PPU_SetAddr
[cac7]LDY #$08
[cac9]LDX #$00
[cacb]LDA Temp_PPU_NameTableValue
[cacd]JSR PPU_FillGrid
;
; 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]LDA #$23
[cad2]LDX #$c0
[cad4]JSR PPU_SetAddr
[cad7]LDX #$40
[cad9]LDY #$01
[cadb]LDA #$55
[cadd]JSR PPU_FillGrid
;
; Set address 0x27C0: Attribute table 1.
;
; Write 64 entries here with the same information.
;
[cae0]LDA #$27
[cae2]LDX #$c0
[cae4]JSR PPU_SetAddr
[cae7]LDX #$40
[cae9]LDY #$01
[caeb]LDA #$55
;
; v-- Fall through --v
;
;============================================================================
; Fills a grid in the PPU with a value.
;
; This writes a value to the PPU up to X times, and then
; again 256 * Y times.
;
; This seems a bit strange, but in practice it's never
; really used this way. Always 256 columns for every row,
; or only ever one row.
;
; INPUTS:
; A:
; The value to write.
;
; X:
; Initial number of columns for the first row
; (256 columns per row after).
;
; Y:
; The number of rows.
;
; OUTPUTS:
;
PPUDATA:
; Updated with the new written data.
;
; XREFS:
;
PPU_FillGrid
;
PPU_InitAttributeAndNameTables
;============================================================================
[caed]PPU_FillGrid:; [$caed]
[caed]STA a:PPUDATA; Write the value to PPUDATA.
[caf0]DEX; X--
[caf1]BNE PPU_FillGrid; 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]DEY; Y--
[caf4]BNE PPU_FillGrid; If Y > 0, loop
[caf6]RTS
[caf7]PPU_WaitUntilFlushed:; [$caf7]
;
; Wait until the PPU buffer has been emptied (flushed to the
; PPU).
;
[caf7]LDA PPUBuffer_WriteOffset; Load the upper bounds of the PPU buffer.
[caf9]CMP PPUBuffer_ReadOffset; Has the read offset hit the upper bounds?
[cafb]BNE PPU_WaitUntilFlushed; If not, loop until it's cleared.
;
; Pause interrupt handling for 2 interrupts.
;
[cafd]LDA #$00
[caff]STA PauseInterruptCounter; Reset the paused interrupt counter.
[cb01]STA Game_InterruptHandlersEnabled; Disable processing of interrupts.
[cb03]STA PPU_ForceLowerPatternTables
;
; Wait for 2 interrupts (at least).
;
[cb05]@_waitForInterruptLoop:; [$cb05]
[cb05]LDA PauseInterruptCounter; Load the paused interrupt counter.
[cb07]CMP #$02; Has it reached 2 interrupts?
[cb09]BCC @_waitForInterruptLoop; If not, loop.
[cb0b]RTS
;============================================================================
; DEADCODE
;
; XREFS:
;
DEADCODE_FUN_PRG15_MIRROR__cb0c
;============================================================================
[cb0c]DEADCODE_FUN_PRG15_MIRROR__cb0c:; [$cb0c]
[cb0c]LDA a:PPUSTATUS
[cb0f]BMI DEADCODE_FUN_PRG15_MIRROR__cb0c
[cb11]@LAB_PRG15_MIRROR__cb11:; [$cb11]
[cb11]LDA a:PPUSTATUS
[cb14]BPL @LAB_PRG15_MIRROR__cb11
[cb16]RTS
[cb17]Screen_ResetForGamePlay:; [$cb17]
[cb17]JSR Screen_ResetSpritesForGamePlay; Reset the sprites for game play mode.
[cb1a]LDA #$01
[cb1c]STA Game_InterruptHandlersEnabled; Enable interrupt handlers.
[cb1e]RTS
;============================================================================
; DEADCODE
;============================================================================
[cb1f]DEADCODE_FUN_PRG15_MIRROR__cb1f:; [$cb1f]
[cb1f]JSR DEADCODE_Sprites_ResetForDefaultsMode1
[cb22]LDA #$01
[cb24]STA Game_InterruptHandlersEnabled
[cb26]RTS
[cb27]Screen_ResetForNonGame:; [$cb27]
[cb27]JSR Screen_ResetSpritesForNonGame; Reset the sprites for a non-game mode.
[cb2a]LDA #$01
[cb2c]STA Game_InterruptHandlersEnabled; Enable interrupt handlers.
[cb2e]RTS
;============================================================================
; 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:; [$cb2f]
;
; Enable VBlank NMI.
;
[cb2f]LDA PPU_ControlFlags; Load the PPU control flags.
[cb31]ORA #$80; Enable the VBlank bit.
[cb33]BNE @_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]LDA PPU_ControlFlags
[cb37]AND #$7f
;
; Write our copy of the bitmask and set on the PPU.
;
[cb39]@_savePPUCtrl:; [$cb39]
[cb39]STA PPU_ControlFlags; Store the saved PPU control flags.
[cb3b]STA a:PPUCTRL; Store the flags in the PPU.
[cb3e]RTS
;============================================================================
; DEADCODE
;
; XREFS:
;
DEADCODE_FUN_PRG15_MIRROR__cb1f
;============================================================================
[cb3f]DEADCODE_Sprites_ResetForDefaultsMode1:; [$cb3f]
[cb3f]LDA #$01
[cb41]STA Sprites_Sprite0Mode
[cb43]STA PPU_ForceLowerPatternTables
[cb45]BNE Sprites_Reset
;
; v-- Fall through --v
;
[cb47]Screen_ResetSpritesForGamePlay:; [$cb47]
[cb47]LDA #$00
[cb49]STA Sprites_Sprite0Mode; Set sprite 0 to X=8, Y=23.
[cb4b]STA PPU_ForceLowerPatternTables; Disable forcing lower pattern tables.
[cb4d]BEQ Sprites_Reset; Reset sprites. This will always jump.
[cb4f]Screen_ResetSpritesForNonGame:; [$cb4f]
[cb4f]LDA #$ff; A = 0xFF (no sprite 0)
[cb51]STA Sprites_Sprite0Mode; Store it.
;
; v-- Fall through --v
;
[cb53]Sprites_Reset:; [$cb53]
;
; Reset state.
;
; Some of this seem to be remnants from an older design.
;
[cb53]LDY #$00; Y = 0
[cb55]STY Sprites_PPUOffset; Sprites_PPUOffset = 0
[cb57]STY Sprites_Unused_0034; Sprites_Unused_0034 = 0
[cb59]STY Sprites_Unused_0035; Sprites_Unused_0035 = 0
[cb5b]STY Sprites_Unused_0038; Sprites_Unused_0038 = 0
[cb5d]STY Sprites_Unused_0037; Sprites_Unused_0037 = 0
[cb5f]STY Sprites_SlotsFull; Set sprite slots to not be full.
[cb61]STY Sprites_SpriteSlot; Set the starting sprite slot to 0.
[cb63]LDA Sprites_Sprite0Mode; Load our byte from before.
;
; Check whether sprite 0 should be hard-coded.
;
[cb65]BMI @_beginFillSpriteSlots; If 0xFF, then jump.
[cb67]INC Sprites_SpriteSlot; Increment the sprite slot (start new sprites at sprite 1).
[cb69]TAY; Y = A (index)
[cb6a]LDA SPRITES_START_XY,Y; Load the Y for the sprite.
[cb6d]STA a:SPRITE_0_RANGE_1_START; Store for sprite 0's Y.
[cb70]LDA #$7f; 0x7F == tile ID
[cb72]STA a:SPRITE0_TILE; Store for sprite 0's tile ID.
[cb75]LDA #$23; 0x23 == Palette index 3, behind background
[cb77]STA a:SPRITE0_ATTRS; Store for sprite 0's attributes.
[cb7a]LDA $cb98,Y; Load the X for the sprite.
[cb7d]STA a:SPRITE0_X; Store for sprite 0's X.
[cb80]LDY #$04; Y = 4 (loop counter).
;
; Begin filling all sprite slots with a Y = 240 (off-screen).
;
[cb82]@_beginFillSpriteSlots:; [$cb82]
[cb82]LDA #$f0; A = 0xF0 (default Y for unused slots).
[cb84]@_fillSpriteSlotsLoop:; [$cb84]
[cb84]STA SPRITE_0_RANGE_1_START,Y; Set it for this sprite slot's Y.
[cb87]INY; Increment slot byte by 4.
[cb88]INY
[cb89]INY
[cb8a]INY
[cb8b]BNE @_fillSpriteSlotsLoop; If not 0, loop.
;
; Flip the start of the sprite slot range between 0 and 32.
;
[cb8d]LDA Sprites_StartSlotRange; Load the frame flags.
[cb8f]AND #$80; Cap to 0 or 32.
[cb91]EOR #$80; Flip it between 0 and 32.
[cb93]STA Sprites_StartSlotRange; Store it.
[cb95]RTS
;============================================================================
; Start X/Y locations for the default sprite 0.
;============================================================================
[cb96]SPRITES_START_XY:; [$cb96]
[cb96].byte $17; [0]: SPRITE_0_RANGE_1_START when Sprites_Sprite0Mode == 0
[cb97].byte $48; [1]: SPRITE_0_RANGE_1_START when Sprites_Sprite0Mode == 0xFF
[cb98].byte $08; [2]: SPRITE0_X when Sprites_Sprite0Mode == 0
[cb99].byte $00; [3]: SPRITE0_X when Sprites_Sprite0Mode == 0xFF
[cb9a]Game_WaitForOAMReset:; [$cb9a]
[cb9a]JSR Screen_ResetSpritesForGamePlay
[cb9d]LDA #$01
[cb9f]STA Game_NeedOAMReset
[cba1]LDA InterruptCounter
[cba3]@_waitForInterruptLoop:; [$cba3]
[cba3]CMP InterruptCounter
[cba5]BEQ @_waitForInterruptLoop
[cba7]RTS
;============================================================================
; 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]
[cba8]LDX #$04; X = 4 (sprite data offset)
[cbaa]LDY #$84; Y = 0x84 (start of loop counter)
[cbac]@_loop:; [$cbac]
[cbac]LDA SPRITE_0_RANGE_1_START,X; Load the sprite Y from this offset in range 1.
[cbaf]PHA; Push it to the stack.
[cbb0]LDA SPRITE_0_RANGE_1_START,Y; Load the sprite Y from this offset in range 2.
[cbb3]STA SPRITE_0_RANGE_1_START,X; Copy to range 1.
[cbb6]PLA; Pop the previous Y from the stack.
[cbb7]STA SPRITE_0_RANGE_1_START,Y; Copy it to range 2.
[cbba]INX; X++ (sprite data offset)
[cbbb]INY; Y++ (loop counter)
[cbbc]BNE @_loop; If not wrapped to 0, loop.
[cbbe]RTS
;============================================================================
; 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]
[cbbf]LDA #$00
[cbc1]STA MMC1_ShiftSync
[cbc3]JSR MMC1_Init; Initialize the MMC1 chipset.
[cbc6]LDX #$0e
[cbc8]STX a:CurrentROMBank; Set the previous and current ROM banks to 14.
[cbcb]STX SavedPRGBank
[cbcd]JMP MMC1_UpdateROMBank; Switch to the bank.
;============================================================================
; Initialize the MMC1 chip.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; XREFS:
;
Game_InitMMCAndBank
;============================================================================
[cbd0]MMC1_Init:; [$cbd0]
[cbd0]LDA #$ff
[cbd2]STA a:MMC1_SERIAL
[cbd5]LDA #$0e; Horizontal Mirroring
Regular Mirroring
Swap ROM bank at 0x8000
Swap 8K of VROM at PPU 0x0000
Don't reset
[cbd7]STA a:$9fff
[cbda]LSR A
[cbdb]STA a:$9fff
[cbde]LSR A
[cbdf]STA a:$9fff
[cbe2]LSR A
[cbe3]STA a:$9fff
[cbe6]LSR A
[cbe7]STA a:$9fff; VROM_SIZE_SELECT
[cbea]LDA #$00; Select VROM bank at 0x0000
Switch 4KB only
Don't reset
[cbec]STA a:$bfff
[cbef]LSR A
[cbf0]STA a:$bfff
[cbf3]LSR A
[cbf4]STA a:$bfff
[cbf7]LSR A
[cbf8]STA a:$bfff
[cbfb]LSR A
[cbfc]STA a:$bfff; VROM_PAGE_SELECT_1
[cbff]LDA #$00; Select VROM bank at 0x1000
Switch 4KB only
Don't reset
[cc01]STA a:$dfff
[cc04]LSR A
[cc05]STA a:$dfff
[cc08]LSR A
[cc09]STA a:$dfff
[cc0c]LSR A
[cc0d]STA a:$dfff
[cc10]LSR A
[cc11]STA a:$dfff
[cc14]RTS
[cc15]MMC1_SaveROMBankAndUpdateTo:; [$cc15]
[cc15]LDA a:CurrentROMBank; Get the currently-loaded ROM bank
[cc18]STA SavedPRGBank
;
; v-- Fall through --v
;
[cc1a]MMC1_UpdateROMBank:; [$cc1a]
[cc1a]STX a:CurrentROMBank; Set the bank we're switching to.
[cc1d]@_setupMMC1:; [$cc1d]
[cc1d]LDA #$01
[cc1f]STA MMC1_ShiftSync
;
; Write 5 LSBs of A to $FFFF, one bit per write (MMC1 serial protocol):
;
[cc21]TXA; ROM_PAGE_SELECT parameter in X
[cc22]STA a:MMC1_SERIAL
[cc25]LSR A
[cc26]STA a:MMC1_SERIAL
[cc29]LSR A
[cc2a]STA a:MMC1_SERIAL
[cc2d]LSR A
[cc2e]STA a:MMC1_SERIAL
[cc31]LSR A
[cc32]STA a:MMC1_SERIAL
;
; 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]LDA MMC1_ShiftSync
[cc37]CMP #$01
[cc39]BEQ @_finish
;
; Slow path. Re-initialize the MMC1.
;
[cc3b]LDA #$ff
[cc3d]STA a:MMC1_SERIAL; Reset the MMC1 shifter/controler.
[cc40]LDA #$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]STA a:$9fff
[cc45]LSR A
[cc46]STA a:$9fff
[cc49]LSR A
[cc4a]STA a:$9fff
[cc4d]LSR A
[cc4e]STA a:$9fff
[cc51]LSR A
[cc52]STA a:$9fff
;
; Set CHR bank 0 = 0.
;
[cc55]LDA #$00; Select VROM bank at 0x000
Switch 4K only
Don't reset
[cc57]STA a:$bfff
[cc5a]LSR A
[cc5b]STA a:$bfff
[cc5e]LSR A
[cc5f]STA a:$bfff
[cc62]LSR A
[cc63]STA a:$bfff
[cc66]LSR A
[cc67]STA a:$bfff
;
; Set CHR bank 1 = 0.
;
[cc6a]LDA #$00; Select VROM bank at 0x1000
Switch 4K only
Don't reset
[cc6c]STA a:$dfff
[cc6f]LSR A
[cc70]STA a:$dfff
[cc73]LSR A
[cc74]STA a:$dfff
[cc77]LSR A
[cc78]STA a:$dfff
[cc7b]LSR A
[cc7c]STA a:$dfff
[cc7f]JMP @_setupMMC1
[cc82]@_finish:; [$cc82]
[cc82]DEC MMC1_ShiftSync
[cc84]RTS
;============================================================================
; 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]
[cc85]STX 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]LDA MMC1_ShiftSync
[cc8a]BEQ @_fastPath
;
; The sync state was set, so something is switching banks.
; Increment the sync state and reset the MMC1 chip.
;
[cc8c]INC MMC1_ShiftSync
;
; Reset MMC1.
;
[cc8e]LDA #$ff
[cc90]STA a:MMC1_SERIAL
[cc93]LDA #$0e; Horizontal Mirroring
Regular Mirroring
Swap ROM bank at 0x8000
Swap 8K of VROM at PPU 0x000
Don't reset
[cc95]@_setMMC1State:; [$cc95]
[cc95]STA a:$9fff
[cc98]LSR A
[cc99]STA a:$9fff
[cc9c]LSR A
[cc9d]STA a:$9fff
[cca0]LSR A
[cca1]STA a:$9fff
[cca4]LSR A
[cca5]STA a:$9fff
;
; Set CHR bank 0 = 0
;
[cca8]LDA #$00
[ccaa]STA a:$bfff
[ccad]LSR A
[ccae]STA a:$bfff
[ccb1]LSR A
[ccb2]STA a:$bfff
[ccb5]LSR A
[ccb6]STA a:$bfff
[ccb9]LSR A
[ccba]STA a:$bfff
;
; Set CHR bank 1 = 0.
;
[ccbd]LDA #$00
[ccbf]STA a:$dfff
[ccc2]LSR A
[ccc3]STA a:$dfff
[ccc6]LSR A
[ccc7]STA a:$dfff
[ccca]LSR A
[cccb]STA a:$dfff
[ccce]LSR A
[cccf]STA a:$dfff
;
; Set the bank.
;
[ccd2]@_fastPath:; [$ccd2]
[ccd2]TXA
[ccd3]STA a:MMC1_SERIAL
[ccd6]LSR A
[ccd7]STA a:MMC1_SERIAL
[ccda]LSR A
[ccdb]STA a:MMC1_SERIAL
[ccde]LSR A
[ccdf]STA a:MMC1_SERIAL
[cce2]LSR A
[cce3]STA a:MMC1_SERIAL
[cce6]RTS
[cce7]MMC1_RestorePrevROMBank:; [$cce7]
[cce7]LDX SavedPRGBank
[cce9]JMP MMC1_UpdateROMBank
;============================================================================
; TODO: Document PPUBuffer_DrawCommand_RemoveVerticalLines
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
PPUBUFFER_DRAW_COMMANDS
; [$PRG15_MIRROR::cfc8]
;============================================================================
[ccec]PPUBuffer_DrawCommand_RemoveVerticalLines:; [$ccec]
;
; Read the first byte from the buffer as the phase.
;
[ccec]LDX PPUBuffer_ReadOffset; Load our current PPU buffer offset into X.
[ccee]LDA PPUBuffer,X; Load the first address byte from the buffer into A.
[ccf1]INX; Increment buffer offset (X = X + 1).
[ccf2]STA Temp_08; Store the upper byte (A) into a temporary variable.
;
; Read the upper address from the buffer and set in PPUADDR.
;
[ccf4]LDA PPUBuffer,X; Load the next byte from the buffer into A.
[ccf7]INX; Increment buffer offset.
[ccf8]TAY; Y = lower byte.
[ccf9]STY a:PPUADDR; Write the upper byte to PPUADDR.
;
; Read the lower address from the buffer and set in PPUADDR.
;
[ccfc]LDA PPUBuffer,X
[ccff]INX
[cd00]STX PPUBuffer_ReadOffset; Set the new offset.
[cd02]TAX
[cd03]STX a:PPUADDR; Write the lower byte to PPUADDR.
;
; Begin preparing for the loop. There will be 16 iterations.
;
[cd06]LDA #$10; Set to 16 iterations in Temp_06.
[cd08]STA Temp_06
[cd0a]LDA a:PPUDATA; Read a byte from PPUDATA.
[cd0d]@_loop:; [$cd0d]
[cd0d]STX Temp_07; Store X (offset) into our temp state.
[cd0f]LDA a:PPUDATA; Read the next byte of data.
;
; Set the address again. The PPUDATA read advanced the offset.
;
[cd12]STY a:PPUADDR; Set upper byte of PPU address to Y.
[cd15]STX 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]PHA
[cd19]LDA Temp_06; Load the loop index.
[cd1b]CLC
[cd1c]ADC Temp_08; Add the phase byte to it.
[cd1e]AND #$07; Retain only the 3 least-significant bits.
[cd20]TAX; Store in X.
[cd21]PLA
;
; 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]AND PPUBUFFER_DRAWCOMMAND_0xFA_MASKS,X
[cd25]STA a:PPUDATA; Set as the new PPU data.
;
; Load the offset into the buffer and increment.
;
[cd28]LDX Temp_07; Load the saved offset.
[cd2a]INX; Increment by 1.
;
; Prepare either for the next loop or for termination.
;
[cd2b]LDA a:PPUDATA; Load our next PPU data byte for the next loop.
[cd2e]DEC Temp_06; Decrement our loop index by 1.
[cd30]BNE @_loop; If not 0, loop.
[cd32]RTS
[cd33]PPUBUFFER_DRAWCOMMAND_0xFA_MASKS:; [$cd33]
[cd33].byte $fe; [0]:
[cd34].byte $df; [1]:
[cd35].byte $f7; [2]:
[cd36].byte $fd; [3]:
[cd37].byte $bf; [4]:
[cd38].byte $ef; [5]:
[cd39].byte $7f; [6]:
[cd3a].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:; [$cd3b]
;
; Read the first two bytes from the offset and set as
; the PPUADDR.
;
[cd3b]LDX PPUBuffer_ReadOffset; Load our current PPU buffer offset.
[cd3d]LDA PPUBuffer,X; Load the first address byte from the buffer.
[cd40]INX
[cd41]TAY; Y = byte 1
[cd42]STY a:PPUADDR; Set as the upper byte of the PPU address.
[cd45]LDA PPUBuffer,X; Load the next address byte.
[cd48]INX
[cd49]STX PPUBuffer_ReadOffset; Update the offset to skip these bytes.
[cd4b]TAX
[cd4c]STX a:PPUADDR; Set as the lower byte of the PPU address.
;
; Begin preparing for drawing.
;
; We'll draw 16 bytes from the buffer.
;
[cd4f]LDA #$10; Set our loop for 16 bytes.
[cd51]STA Temp_06
[cd53]LDA a:PPUDATA; Load byte 1 from the PPU.
[cd56]@_loop:; [$cd56]
[cd56]LDA a:PPUDATA; Load a byte from the PPU.
;
; Set the address again. The PPUDATA read advanced the offset.
;
[cd59]STY a:PPUADDR; Set upper byte of PPU address to Y.
[cd5c]STX a:PPUADDR; Set lower byte of PPU address to X.
;
; Rotate the tile data from this address right by 1 pixel.
;
[cd5f]PHA
[cd60]LSR A; Rotate the pixel data right by 1.
[cd61]PLA
[cd62]ROR A; OR it to the data rotated left by 7.
[cd63]STA a:PPUDATA; Store the rotated data.
[cd66]INX; Increment the lower byte of the PPU address.
[cd67]LDA a:PPUDATA
;
; Check if we're at the end of the loop.
;
; If we've processed 16 entries, we're done.
;
[cd6a]DEC Temp_06; Decrement our loop counter.
[cd6c]BNE @_loop; If it's > 0, loop.
[cd6e]RTS
[cd6f]CurrentSprite_ResetPPUTileOffset:; [$cd6f]
[cd6f]LDA #$09
[cd71]STA CurrentSprite_TargetPPUTileAddr_U; Set upper to 0x09.
[cd73]LDA #$00
[cd75]STA CurrentSprite_TargetPPUTileAddr; Set lower to 0x00.
[cd77]RTS
[cd78]CurrentSprite_LoadTilesInfo:; [$cd78]
;
; Save the current ROM bank to the stack.
;
[cd78]LDA a:CurrentROMBank; Load the current bank.
[cd7b]PHA; Push to the stack.
;
; Switch to the bank for the current sprite's images.
;
[cd7c]LDX a:CurrentSprite_TilesBank; Load the bank where the images for the current sprite are found
[cd7f]JSR 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]LDA a:ROMBankStart; Read byte from $8000.
[cd85]STA 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]LDA a:ROMBankStart+1; Read byte from $8001.
[cd8a]CLC
[cd8b]ADC #$80; Add 0x80.
[cd8d]STA 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]LDA a:CurrentSprite_Entity; Load the current sprite ID
[cd92]CMP #$37; Check if we're on the next bank.
[cd94]BCC @_loadImages
[cd96]SBC #$37; We are, so subtract 55 for the new sprite index.
;
; Compute the address for the sprite image.
;
[cd98]@_loadImages:; [$cd98]
[cd98]ASL A; Convert the sprite ID to a word boundary.
[cd99]TAY; Y = A
[cd9a]LDA (Temp_Addr_L),Y; Get the pointer to the sprite data from the bank address computed above.
[cd9c]STA CurrentSprite_LoadTileAddr; A = Lower byte of the image data address.
[cd9e]INY
[cd9f]LDA (Temp_Addr_L),Y; A = Upper byte of the address.
[cda1]CLC
[cda2]ADC #$80; Add 0x80 to the upper address.
[cda4]STA 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]LDA a:CurrentSprite_Entity; Load the current sprite ID
[cda9]TAY
[cdaa]LDA SPRITES_PPU_TILE_COUNTS,Y; Get the number of tiles needed
[cdad]STA CurrentSprite_LoadTileCount; Store that for the render
;
; Restore the previous bank.
;
[cdaf]PLA; Pop the previous bank.
[cdb0]TAX; Transfer to X.
[cdb1]JSR MMC1_UpdateROMBank; Update to the bank.
[cdb4]RTS
[cdb5]Sprites_LoadImageForCurrentSprite:; [$cdb5]
[cdb5]LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte of the current PPU tile address to read from.
[cdb7]STA Temp_00; Store it temporarily.
[cdb9]LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte of the PPU tile address.
[cdbb]ASL Temp_00; Multiply by 16.
[cdbd]ROL A
[cdbe]ASL Temp_00
[cdc0]ROL A
[cdc1]ASL Temp_00
[cdc3]ROL A
[cdc4]ASL Temp_00
[cdc6]ROL A; A = Lower byte of the offset.
[cdc7]STA 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]JSR CurrentSprite_LoadTilesInfo; Look up the sprite data pointer for this.
[cdcc]LDA CurrentSprite_LoadTileCount; A = Tile count.
[cdce]BNE @_loadSpriteToPPUBuffer; If count > 0, jump to load tile data.
[cdd0]RTS
;
; Switch to the bank containing the tiles to load.
;
[cdd1]@_loadSpriteToPPUBuffer:; [$cdd1]
[cdd1]LDA a:CurrentROMBank; Load the current ROM bank.
[cdd4]PHA; Push it to the stack.
[cdd5]LDX a:CurrentSprite_TilesBank; X = Bank for the tiles.
[cdd8]JSR MMC1_UpdateROMBank; Switch to that bank.
;
; Set the PPU address to load tiles into.
;
[cddb]LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte of the target PPU address.
[cddd]STA PPU_TargetAddr_U; Set it.
[cddf]LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte.
[cde1]STA PPU_TargetAddr; Set it.
;
; Queue loading 16 bytes for the current tile.
;
[cde3]LDA #$10; A = 16 (bytes)
[cde5]JSR 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]LDY #$00; Y = 0 (loop counter)
[cdea]@_copySpriteImage:; [$cdea]
[cdea]LDA (CurrentSprite_LoadTileAddr),Y; Load a byte from the tile.
[cdec]STA PPUBuffer,X; Write to the PPU buffer at X.
[cdef]INX; X++ (write offset)
[cdf0]INY; Y++ (loop counter)
[cdf1]CPY #$10; Is X < 16?
[cdf3]BCC @_copySpriteImage; If so, loop.
;
; 16 bytes of sprite data was written to the PPU buffer.
; Update the new write position for the buffer.
;
[cdf5]STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
;
; Restore the previous bank.
;
[cdf7]PLA; Pull the previous bank from the stack.
[cdf8]TAX; Set to X.
[cdf9]JSR MMC1_UpdateROMBank; And switch to it.
;
; Update the source tile address to advance to the next tile.
;
[cdfc]LDA CurrentSprite_LoadTileAddr; Load the lower byte of the tile address.
[cdfe]CLC
[cdff]ADC #$10; Add 16 (the bytes we just read).
[ce01]STA CurrentSprite_LoadTileAddr; And store it.
[ce03]LDA CurrentSprite_LoadTileAddr_U; Load the upper byte.
[ce05]ADC #$00; Add Carry, if the lower byte wrapped.
[ce07]STA CurrentSprite_LoadTileAddr_U; And store it.
;
; Update the target PPU tile address to advance to the next
; tile.
;
[ce09]LDA CurrentSprite_TargetPPUTileAddr; Load the lower byte of the target address.
[ce0b]CLC
[ce0c]ADC #$10; Add 16.
[ce0e]STA CurrentSprite_TargetPPUTileAddr; And store it.
[ce10]LDA CurrentSprite_TargetPPUTileAddr_U; Load the upper byte.
[ce12]ADC #$00; Add Carry, if the lower byte wrapped.
[ce14]STA CurrentSprite_TargetPPUTileAddr_U; And store it.
;
; Decrement the tile count, and loop if there are tiles
; remaining.
;
[ce16]DEC CurrentSprite_LoadTileCount; Decrement the remaining tile count.
[ce18]BNE @_loadSpriteToPPUBuffer; If > 0, loop.
[ce1a]RTS
;============================================================================
; The number of PPU tiles each sprite needs.
;
; XREFS:
;
CurrentSprite_LoadTilesInfo
;============================================================================
[ce1b]SPRITES_PPU_TILE_COUNTS:; [$ce1b]
[ce1b].byte $01; [0]:
[ce1c].byte $01; [1]: Dropped: Bread
[ce1d].byte $01; [2]: Dropped: Coin
[ce1e].byte $01; [3]: Enemy: ?
[ce1f].byte $10; [4]: Enemy: Raiden
[ce20].byte $10; [5]: Enemy: Necron Aides
[ce21].byte $10; [6]: Enemy: Zombie
[ce22].byte $08; [7]: Enemy: Hornet
[ce23].byte $06; [8]: Enemy: Bihoruda
[ce24].byte $06; [9]: Enemy: Lilith
[ce25].byte $07; [10]: Magic: ?
[ce26].byte $06; [11]: Enemy: Yuinaru
[ce27].byte $0c; [12]: Enemy: Snowman
[ce28].byte $10; [13]: Enemy: Nash
[ce29].byte $10; [14]: Enemy: Fire Giant
[ce2a].byte $12; [15]: Enemy: Ishiisu
[ce2b].byte $0d; [16]: Enemy: Execution Hood
[ce2c].byte $26; [17]: Boss: Rokusutahn
[ce2d].byte $10; [18]: Boss: unused (round body of snake boss)
[ce2e].byte $00; [19]: Effect: Enemy death
[ce2f].byte $00; [20]: Effect: Lightning ball
[ce30].byte $16; [21]: Enemy: Charron
[ce31].byte $17; [22]: Enemy: ? (Unused)
[ce32].byte $10; [23]: Enemy: Geributa
[ce33].byte $0e; [24]: Enemy: Sugata
[ce34].byte $12; [25]: Enemy: Grimlock
[ce35].byte $0c; [26]: Enemy: Giant Bees
[ce36].byte $0e; [27]: Enemy: Myconid
[ce37].byte $10; [28]: Enemy: Naga
[ce38].byte $10; [29]: Enemy: Skeleton Knight (unused)
[ce39].byte $12; [30]: Enemy: Giant Strider
[ce3a].byte $12; [31]: Enemy: Sir Gawaine
[ce3b].byte $1f; [32]: Enemy: Maskman
[ce3c].byte $16; [33]: Enemy: Wolfman
[ce3d].byte $0f; [34]: Enemy: Yareeka
[ce3e].byte $10; [35]: Enemy: Magman
[ce3f].byte $13; [36]: Enemy: Curly-tailed guy with spear (unused)
[ce40].byte $10; [37]: Enemy: ? (unused)
[ce41].byte $11; [38]: Enemy: Ikeda
[ce42].byte $10; [39]: Enemy: Muppet guy (unused)
[ce43].byte $10; [40]: Enemy: Lamprey
[ce44].byte $10; [41]: Enemy: ? (unused)
[ce45].byte $13; [42]: Enemy: Monodron
[ce46].byte $0c; [43]: Enemy: Winged skeleton (unused)
[ce47].byte $12; [44]: Enemy: Tamazutsu
[ce48].byte $3e; [45]: Boss: Ripasheiku
[ce49].byte $33; [46]: Boss: Zoradohna
[ce4a].byte $1c; [47]: Boss: Borabohra
[ce4b].byte $0e; [48]: Boss: Pakukame
[ce4c].byte $25; [49]: Boss: Zorugeriru
[ce4d].byte $54; [50]: Boss: King Grieve
[ce4e].byte $69; [51]: Boss: Shadow Eura
[ce4f].byte $10; [52]: NPC: Walking Man 1
[ce50].byte $10; [53]: NPC: Blue lady (unused)
[ce51].byte $09; [54]: NPC: Child (unused)
[ce52].byte $08; [55]: NPC: Armor Salesman
[ce53].byte $0b; [56]: NPC: Martial Artist
[ce54].byte $0b; [57]: NPC: Priest
[ce55].byte $14; [58]: NPC: King
[ce56].byte $0c; [59]: NPC: Magic Teacher
[ce57].byte $08; [60]: NPC: Key Salesman
[ce58].byte $0a; [61]: NPC: Smoking Man
[ce59].byte $0e; [62]: NPC: Man in Chair
[ce5a].byte $0a; [63]: NPC: Sitting Man
[ce5b].byte $0d; [64]: NPC: Meat Salesman
[ce5c].byte $10; [65]: NPC: Lady in Blue Dress with Cup
[ce5d].byte $10; [66]: NPC: Guard
[ce5e].byte $0b; [67]: NPC: Doctor
[ce5f].byte $0e; [68]: NPC: Walking Woman 1
[ce60].byte $0d; [69]: NPC: Walking Woman 2
[ce61].byte $09; [70]: Enemy: Eyeball (unused)
[ce62].byte $08; [71]: Enemy: Zozura
[ce63].byte $02; [72]: Item: Glove
[ce64].byte $02; [73]: Item: Black Onyx
[ce65].byte $04; [74]: Item: Pendant
[ce66].byte $02; [75]: Item: Red Potion
[ce67].byte $02; [76]: Item: Poison
[ce68].byte $04; [77]: Item: Elixir
[ce69].byte $02; [78]: Item: Ointment
[ce6a].byte $00; [79]: Trigger: Intro
[ce6b].byte $02; [80]: Item: Mattock
[ce6c].byte $00; [81]: Magic: ?
[ce6d].byte $0c; [82]: Effect: Fountain
[ce6e].byte $00; [83]: Magic: ?
[ce6f].byte $00; [84]: Magic: Enemy Fireball
[ce70].byte $04; [85]: Item: Wing Boots
[ce71].byte $02; [86]: Item: Hour Glass
[ce72].byte $04; [87]: Item: Magical Rod
[ce73].byte $02; [88]: Item: Battle Suit
[ce74].byte $04; [89]: Item: Battle Helmet
[ce75].byte $04; [90]: Item: Dragon Slayer
[ce76].byte $02; [91]: Item: Mattock
[ce77].byte $04; [92]: Item: Wing Boots (from quest)
[ce78].byte $02; [93]: Item: Red Potion
[ce79].byte $02; [94]: Item: Poison
[ce7a].byte $02; [95]: Item: Glove
[ce7b].byte $02; [96]: Item: Ointment
[ce7c].byte $0c; [97]: Effect: Spring of Trunk
[ce7d].byte $0c; [98]: Effect: Spring of Sky
[ce7e].byte $0c; [99]: Effect: Spring of Tower
[ce7f].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:; [$ce80]
;
; Save the current ROM bank and switch to bank 8.
;
[ce80]LDA a:CurrentROMBank; Load the current ROM bank.
[ce83]PHA; Push it to the stack.
[ce84]LDX #$08; Bank 8 == sprite data.
[ce86]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Set the start address for the sprites to load.
;
[ce89]LDA a:TILES_COMMON_START_REF; Load the lower byte of the address of the first sprite (a coin).
[ce8c]STA Temp_Addr_L; Store it.
[ce8e]LDA a:TILES_COMMON_START_REF+1; Load the upper byte.
[ce91]CLC; Clear Carry.
[ce92]ADC #$80; Add 0x80 to the upper byte.
[ce94]STA 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]LDA #$05; 5 = number of passes of 256 bytes of sprite tiles to load.
[ce98]STA Temp_00; Store it for the loop.
;
; Set the PPU address to $0400 (within the loaded PPU tiles).
;
[ce9a]LDA #$04; 0x04 == Upper byte.
[ce9c]STA a:PPUADDR; Write it.
[ce9f]LDY #$00; 0x00 == Lower byte and loop counter.
[cea1]STY a:PPUADDR; Write it.
;
; Begin loading and writing sprites, 5 passes of 256 bytes.
;
[cea4]@_loadLoop:; [$cea4]
[cea4]LDA (Temp_Addr_L),Y; Load the address of the sprite.
[cea6]STA a:PPUDATA; Write to the PPU data.
[cea9]INY; Y++ (loop counter).
[ceaa]BNE @_loadLoop; If not incremented back up to 0, loop.
[ceac]INC Temp_Addr_U; Increment the upper byte of the write address.
[ceae]DEC Temp_00; Decrement the number of passes remaining.
[ceb0]BNE @_loadLoop; If > 0, loop.
;
; Loading is complete. Restore the previous bank.
;
[ceb2]PLA; Pop the previous bank from the stack.
[ceb3]TAX; X = A (bank)
[ceb4]JSR MMC1_UpdateROMBank; Switch back to the bank.
[ceb7]RTS
[ceb8]Area_LoadTiles:; [$ceb8]
;
; Load the address for the tile data based on the tiles index.
;
[ceb8]LDA Area_TilesIndex; Set A = tileset index
[ceba]ASL A; Set A = tileset index * 2 (word offset)
[cebb]TAY; Set Y = word offset into the pointer table
[cebc]LDA TILE_INDEX_TO_ADDR,Y; Read the low byte from the table
[cebf]STA Area_TilesReadAddress; Save low byte to TilesAddress
[cec1]LDA TILE_INDEX_TO_ADDR+1,Y; Read the high byte from the table
[cec4]STA Area_TilesReadAddress+1; Set high byte to TilesAddress
;
; Set the PPU to increment by +1 after each PPUDATA write.
;
[cec6]JSR 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]LDY Area_TilesIndex; Load the tiles index.
[cecb]LDA TILE_INDEX_TO_PPU_ADDR_UPPER,Y; Convert to a PPU address via lookup table (0x18 or 0x1A).
[cece]STA a:PPUADDR; Write as the upper byte.
[ced1]LDA #$00; 0x00 = Lower byte (start of page).
[ced3]STA 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]STA Temp_08; Set lower byte of the remaining tile count to 0.
[ced8]LDA TILE_INDEX_TO_NUM_CHR_PAGES,Y; Load the number of pages to load.
[cedb]STA Temp_09; Set as the upper byte.
;
; Save the current bank and switch to bank 4 (tile data).
;
[cedd]LDA a:CurrentROMBank; A = Current ROM bank.
[cee0]PHA; Push it to the stack.
[cee1]LDX #$04; 4 = Tiles
[cee3]JSR MMC1_UpdateROMBank; Switch to bank 4 (tile data)
;
; Begin loading tiles into PPUDATA.
;
[cee6]LDY #$00; Set Y = 0 (start page of loop)
;
; Stream 1 byte from tiles to the PPU.
;
[cee8]@_copyLoop:; [$cee8]
[cee8]LDA (Area_TilesReadAddress),Y; Load a byte from the tiles.
[ceea]STA 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]INY; Y++ (source pointer).
[ceee]BNE @_noCarry; If wrapped to 0, jump.
[cef0]INC Area_TilesReadAddress+1; Increment the upper byte of the tiles address.
;
; Decrement the 16-bit remaining tile count.
;
[cef2]@_noCarry:; [$cef2]
[cef2]LDA Temp_08; Load the lower byte of the remaining count.
[cef4]SEC
[cef5]SBC #$01; Subtract 1.
[cef7]STA Temp_08; And set it.
[cef9]LDA Temp_09; Load the upper byte of the remaining count.
[cefb]SBC #$00; Subtract carry, if lower wrapped.
[cefd]STA Temp_09; And set it.
[ceff]BCS @_copyLoop; If count > 0, loop.
;
; Switch back to the previous bank.
;
[cf01]PLA; Pop the previous bank.
[cf02]TAX; Transfer to X.
[cf03]JSR MMC1_UpdateROMBank; And switch to it.
[cf06]RTS
;============================================================================
; Offsets of the tiles in the bank
;
; Index into the table are based on
Area_TilesIndex.
;
; TODO
;
; XREFS:
;
Area_LoadTiles
;============================================================================
[cf07]TILE_INDEX_TO_ADDR:; [$cf07]
[cf07].word AREA_TILESETS_EOLIS; [0]: Eolis
[cf09].word AREA_TILESETS_BRANCHES; [1]: Branches
[cf0b].word AREA_TILESETS_TRUNK; [2]: Trunk
[cf0d].word AREA_TILESETS_MIST; [3]: Mist
[cf0f].word AREA_TILESETS_DARTMOOR_EVIL_LAIR; [4]: Dartmoor Castle
Evil Lair
[cf11].word AREA_TILESETS_TOWNS; [5]: Towns
[cf13].word AREA_TILESETS_KINGSROOM_GURU_HOSPITAL; [6]: King's Room
Guru Room
Hospital
[cf15].word AREA_TILESETS_SHOPS_HOUSE_TAVERN; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf17].word 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]
[cf19].byte $18; [0]: Eolis
[cf1a].byte $18; [1]: Branches
[cf1b].byte $18; [2]: Trunk
[cf1c].byte $18; [3]: Mist
[cf1d].byte $18; [4]: Dartmoor Castle
Evil Lair
[cf1e].byte $18; [5]: Towns
[cf1f].byte $1a; [6]: King's Room
Guru Room
Hospital
[cf20].byte $1a; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf21].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]
[cf22].byte $08; [0]: Eolis
[cf23].byte $08; [1]: Branches
[cf24].byte $08; [2]: Trunk
[cf25].byte $08; [3]: Mist
[cf26].byte $08; [4]: Dartmoor Castle
Evil Lair
[cf27].byte $08; [5]: Towns
[cf28].byte $06; [6]: King's Room
Guru Room
Hospital
[cf29].byte $06; [7]: Tavern
Tool Shop
Key Shop
House
Meat Shop
[cf2a].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]
[cf2b]LDA PPU_ControlFlags; Load the PPU control flags.
[cf2d]AND #$fb; Set VRAM address increment per CPU read/write of PPUDATA to 0: Add 1, going across.
[cf2f]STA PPU_ControlFlags; Set it to SavedPPUCtrl.
[cf31]STA a:PPUCTRL; Set it to PPUCTRL.
[cf34]RTS
;============================================================================
; MAYBE 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]
[cf35]LDA #$00
[cf37]STA PPUBuffer_ReadOffset; Set offset = 0
[cf39]STA PPUBuffer_WriteOffset; Set upper bounds = 0
[cf3b]RETURN_CF3B:; [$cf3b]
[cf3b]RTS
;============================================================================
; 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:; [$cf3c]
;
; Check if there's anything in the buffer to write.
; If not, we're done.
;
[cf3c]LDA PPUBuffer_ReadOffset; A = current buffer offset
[cf3e]CMP PPUBuffer_WriteOffset; Compare against the upper bound of the PPU buffer.
[cf40]BEQ 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]LDX PPUBuffer_ReadOffset; X = current PPU offset
[cf44]LDA #$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]SEC
[cf47]SBC PPUBuffer,X; Get the value from the buffer at this offset.
[cf4a]CMP #$07; Check if it's a command code.
[cf4c]BCS @_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]INC PPUBuffer_ReadOffset; Consume the byte in the buffer.
[cf50]ASL A
[cf51]TAY; Begin looking up in the table.
[cf52]LDA PPUBUFFER_DRAW_COMMANDS+1,Y; High byte of the command address.
[cf55]PHA; Push to the stack.
[cf56]LDA PPUBUFFER_DRAW_COMMANDS,Y; Low byte of the command address.
[cf59]PHA; Push to the stack.
[cf5a]RTS; Return. The next address following will execute.
;
; This is standard data to write to the PPU, not a command.
;
[cf5b]@_beginDraw:; [$cf5b]
[cf5b]LDA #$d0; Total byte length that can be written.
[cf5d]STA PPUBuffer_Temp_TotalByteLength; Set it.
[cf5f]LDA #$06; Max number of entries in the buffer that can be written.
[cf61]STA PPUBuffer_Temp_PendingEntryCount; Set it.
;
; Loop through the buffer until either it's cleared or we've
; processed 6 entries.
;
[cf63]@_loopBuffer:; [$cf63]
[cf63]LDX PPUBuffer_ReadOffset; X = Read offset into the buffer.
[cf65]CPX PPUBuffer_WriteOffset; Compare it to the write offset.
[cf67]BEQ @_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]LDA PPU_ControlFlags; A = PPU control flags.
[cf6b]AND #$fb; Set to Add 1 Across.
[cf6d]TAY; Y = Resulting flags.
[cf6e]LDA PPUBuffer,X; Load the byte from the buffer.
[cf71]BPL @_processData; If bit 7 is set, jump.
[cf73]AND #$7f; Else, set VBLank Disable.
[cf75]INY; And Inc Add 32 Down.
[cf76]INY
[cf77]INY
[cf78]INY
[cf79]@_processData:; [$cf79]
[cf79]STY 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]LDA PPUBuffer,X; A = value from the buffer
[cf7f]AND #$7f; Mask out control bit 7.
[cf81]TAY; Y = A (save until we load the address)
[cf82]INX; Set to the next byte.
[cf83]LDA PPUBuffer,X; Load the upper PPU byte address from the buffer.
[cf86]STA a:PPUADDR; Set as the upper byte in PPUADDR.
[cf89]INX; X = X + 1
[cf8a]LDA PPUBuffer,X; Load the lower PPU byte address from the buffer.
[cf8d]STA a:PPUADDR; Set as the lower byte in PPUADDR.
[cf90]INX; X = X + 1
[cf91]TYA; Y = A (restore from above)
[cf92]CLC
[cf93]ADC PPUBuffer_Temp_TotalByteLength; Add the length to our running total.
[cf95]STA PPUBuffer_Temp_TotalByteLength
;
; Begin writing <length> bytes from the buffer to the PPU.
;
[cf97]@_loopData:; [$cf97]
[cf97]LDA PPUBuffer,X; Load the byte to draw at our current offset.
[cf9a]INX; Increment the offset.
[cf9b]STA a:PPUDATA; Set the byte in our PPUDATA.
[cf9e]DEY; Decrement the nuber of bytes to write.
[cf9f]BNE @_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]STX PPUBuffer_ReadOffset; Increment our offset into the buffer to skip the length
and address bytes and all the data bytes.
[cfa3]DEC PPUBuffer_Temp_PendingEntryCount; Decrement our total allowed entries for this operation.
[cfa5]BEQ @_finishLoop; If we can't do any more, we're done.
[cfa7]LDY PPUBuffer,X; Load the next byte to process from the buffer.
[cfaa]DEY
[cfab]CPY #$f9; Check if it's a command byte.
[cfad]BCS @_finishLoop; If it is, we're done. This will be handled next call.
[cfaf]LDA PPUBuffer_Temp_TotalByteLength; Check the total byte length.
[cfb1]BMI @_loopBuffer; If there's room to go, loop.
;
; We're done with everything. Clear out the PPU addresses and
; return.
;
[cfb3]@_finishLoop:; [$cfb3]
[cfb3]LDA #$00
[cfb5]STA a:PPUADDR; Set upper PPUADDR to 0.
[cfb8]STA 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]
[cfbb]RTS
;============================================================================
; Handlers for special PPU buffer draw commands.
;
; XREFS:
;
PPUBuffer_Draw
;============================================================================
[cfbc]PPUBUFFER_DRAW_COMMANDS:; [$cfbc]
[cfbc].word PPUBuffer_DrawCommand_WritePalette-1; [0]: Command 0x00: Write Palette to PPU
[cfbe].word PPUBuffer_DrawCommand_Noop-1; [1]: Command 0xFF
[cfc0].word PPUBuffer_DrawCommand_Noop-1; [2]: Command 0xFE
[cfc2].word PPUBuffer_DrawCommand_Noop-1; [3]: Command 0xFD
[cfc4].word PPUBuffer_DrawCommand_RotateTilesRight1Pixel-1; [4]: Command 0xFC: Rotate Tiles Right
[cfc6].word PPUBuffer_DrawCommand_Noop-1; [5]: Command 0xFB
[cfc8].word 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]
[cfca]JSR PPUBuffer_HasCapacity; Is there capacity?
[cfcd]BCC PPUBuffer_WaitForCapacity; If not, loop.
[cfcf]RTS
;============================================================================
; 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]
[cfd0]LDA PPUBuffer_ReadOffset; A = PPU buffer offset.
[cfd2]SEC
[cfd3]SBC PPUBuffer_WriteOffset; A = offset - upper
[cfd5]BEQ @_return; If (offset - upper) == 0, jump to return false.
[cfd7]CMP #$24; Set whether there's at least 36 bytes difference.
[cfd9]@_return:; [$cfd9]
[cfd9]RTS
[cfda].byte $09,$80; [$cfda] word
[cfdc]PPUBuffer_QueueCommandOrLength:; [$cfdc]
;
; Wait for capacity in the buffer.
;
[cfdc]PHA; Push the length/command to the stack.
[cfdd]JSR PPUBuffer_WaitForCapacity; Wait for capacity in the buffer.
;
; There's capacity. Set up to write.
;
; Start with the length or command byte.
;
[cfe0]PLA
[cfe1]LDX PPUBuffer_WriteOffset; Load the upper bounds of the buffer.
[cfe3]STA PPUBuffer,X; Store the provided value (A) there.
[cfe6]INX
[cfe7]LDA PPU_TargetAddr_U; Load the upper address byte.
[cfe9]STA PPUBuffer,X; Write it to the buffer.
[cfec]INX
[cfed]LDA PPU_TargetAddr; Load the lower address byte.
[cfef]STA PPUBuffer,X; Write it to the buffer.
[cff2]INX
[cff3]RTS
[cff4]PPUBuffer_WaitUntilClear:; [$cff4]
[cff4]LDA PPUBuffer_WriteOffset; Load the upper bounds of the buffer.
[cff6]CMP PPUBuffer_ReadOffset; Does it == the offset?
[cff8]BNE PPUBuffer_WaitUntilClear; If not, loop.
[cffa]RTS
;============================================================================
; 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]
[cffb]JSR PPUBuffer_Draw; Draw from the buffer.
[cffe]LDA PPUBuffer_WriteOffset; Load the upper bounds.
[d000]CMP PPUBuffer_ReadOffset; Does it match the offset?
[d002]BNE PPUBuffer_DrawAll; If not, loop.
[d004]RTS
;============================================================================
; 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]
[d005]LDA #$04
[d007]JSR Screen_LoadUIPalette
[d00a]JMP 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]
[d00d]LDA a:Screen_PaletteIndex
[d010]JSR Screen_LoadUIPalette
[d013]JMP 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:; [$d016]
;
; Write all 32 palettes to 0x3F00.
;
[d016]LDA #$3f; 0x3F == Upper byte.
[d018]STA a:PPUADDR; Write as the upper PPU address.
[d01b]LDX #$00; 0x00 == Lower byte.
[d01d]STX a:PPUADDR; Write as the lower PPU address.
;
; Loop through 32 bytes of palette data and write to the PPU.
;
[d020]LDY #$20; Y = 32 (loop counter).
[d022]@_paletteLoop:; [$d022]
[d022]LDA Screen_PaletteData_Tiles,X; A = Palette byte at the index.
[d025]STA a:PPUDATA; Write to the PPU.
[d028]INX; Increment the PPU address.
[d029]DEY; Y-- (loop counter).
[d02a]BNE @_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]LDA #$3f; 0x3F == Upper byte.
[d02e]STA a:PPUADDR; Write as the upper PPU address.
[d031]STY a:PPUADDR; Zero out the lower byte.
[d034]STY a:PPUADDR; Now zero out the upper byte.
[d037]STY a:PPUADDR; And the lower again.
[d03a]RTS
[d03b]Screen_LoadUIPalette:; [$d03b]
[d03b]TAY; Y = A (palette index).
;
; Save the current bank and load bank 11 (palette data).
;
[d03c]LDA a:CurrentROMBank; Load the current ROM bank.
[d03f]PHA; Push it to the stack.
[d040]LDX #$0b
[d042]JSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Set the attribute data index for the HUD.
;
[d045]LDA DAT_81f0,Y; Load the HUD attribute data for this index.
[d048]STA a:UI_AttributeDataIndex; Set it for the HUD/textboxes.
;
; Restore our current bank.
;
[d04b]PLA; Pop A (previous bank) from the stack.
[d04c]TAX; X = A
[d04d]JSR MMC1_UpdateROMBank; Switch back to the bank.
;
; Get the offset into the bank.
;
[d050]TYA; A = Y (palette index).
[d051]JSR Palette_IndexToROMOffset16; Convert to a relative offset into the bank.
[d054]ADC #$00; Add 0 + C (also 0) to the lower byte.
[d056]STA Temp_08; Store it.
[d058]LDA Temp_09; Load the computed upper byte.
[d05a]ADC #$80; Add 0x80.
[d05c]STA Temp_09; Store it.
;
; Write that palette into the sprite palette.
;
[d05e]LDY #$0f; Start writing into index 16 (sprite palette).
[d060]BNE Screen_SetPaletteData; Load it.
;
; v-- Fall through --v
;
[d062]Screen_LoadSpritePalette:; [$d062]
[d062]STA 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]JSR Palette_IndexToROMOffset16; Convert the relative address for the palette.
[d068]ADC #$c0; Add 0xC0 to the lower byte.
[d06a]STA Temp_08; Store it.
[d06c]LDA Temp_09; Load the computed upper byte.
[d06e]ADC #$81; Add 0x81 to it.
[d070]STA Temp_09; Store it back.
;
; Save the previous bank and switch to bank 11 (palette data).
;
[d072]LDY #$1f; Y = Destination for the palette (31).
;
; v-- Fall through --v
;
[d074]Screen_SetPaletteData:; [$d074]
;
; Save the current bank.
;
[d074]LDA a:CurrentROMBank; Load the current ROM bank.
[d077]PHA; Push it to the stack.
[d078]LDX #$0b
[d07a]JSR MMC1_UpdateROMBank; Switch to bank 11.
;
; Copy 16 bytes of palette into ROM.
;
[d07d]TYA; A = Y (palette data index)
[d07e]TAX; X = A
[d07f]LDY #$0f; Y = 15 (loop counter)
[d081]@_loadPaletteLoop:; [$d081]
[d081]LDA (Temp_08),Y; Load the byte to copy.
[d083]STA Screen_PaletteData_Tiles,X; Store as the palette data at the index.
[d086]DEX; X-- (destination index)
[d087]DEY; Y-- (loop counter)
[d088]BPL @_loadPaletteLoop; If >= 0, loop.
;
; Switch bank to the previous bank.
;
[d08a]PLA; Pop the previous bank.
[d08b]TAX; X = A
[d08c]JSR MMC1_UpdateROMBank; Switch to the bank.
[d08f]RTS
[d090]PPUBuffer_WritePalette:; [$d090]
[d090]LDA #$00; A = 0
[d092]LDX PPUBuffer_WriteOffset; X = upper bounds of the buffer
[d094]STA PPUBuffer,X; Store the 0 in the buffer at the upper bounds.
[d097]INX; Increment the upper bounds.
[d098]STX PPUBuffer_WriteOffset
[d09a]RTS
;============================================================================
; 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]
[d09b]LDY #$00; Y = 0
[d09d]STY Temp_09; Store it temporarily.
[d09f]ASL A; Shift left, upper bit into Carry.
[d0a0]ROL Temp_09; Rotate left, carry into bit 0, bit 7 into carry.
[d0a2]ASL A; Repeat 3 more times, moving lower nibble into A, upper nibble into Temp_09.
[d0a3]ROL Temp_09
[d0a5]ASL A
[d0a6]ROL Temp_09
[d0a8]ASL A
[d0a9]ROL Temp_09
[d0ab]CLC; Clear carry.
[d0ac]RTS; Return A and Temp_09.
[d0ad]Screen_SetFadeOutPalette:; [$d0ad]
;
; Get an address for the provided palette in bank 11.
;
[d0ad]JSR Palette_IndexToROMOffset16; Convert the palette to a 16-bit integer.
[d0b0]ADC #$00; Add 0 and carry (which is also 0) to the lower byte.
[d0b2]STA Temp_08; Store temporarily as the lower byte.
[d0b4]LDA Temp_09; Load it back into A.
[d0b6]ADC #$80; Add 0x80.
[d0b8]STA Temp_09; Store as the upper byte.
;
; Save the current bank and switch to bank 11.
;
[d0ba]LDA a:CurrentROMBank; Load the current bank.
[d0bd]PHA; Push it to the stack.
[d0be]LDX #$0b; 11 = Sprite info/palettes bank.
[d0c0]JSR MMC1_UpdateROMBank; Switch to that bank.
[d0c3]LDX 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]LDY #$0f; Y = 15 (loop counter).
[d0c8]@_loop:; [$d0c8]
[d0c8]LDA (Temp_08),Y; Load the palette data at Y.
[d0ca]SEC; Set C = 1 so SBC won't subtract C.
[d0cb]SBC FADE_OUT_DELTA_TABLE,X; Subtract the value form the lookup table at the transition index.
[d0ce]BCS @_setPaletteData; If > 0, jump to use this value as the palette.
;
; The subtraction yielded a palette value <= 0, so
; hard-code to 15.
;
[d0d0]LDA #$0f; Set palette = 15.
[d0d2]@_setPaletteData:; [$d0d2]
[d0d2]STA Screen_PaletteData_Tiles,Y; Set the palette data to A at index Y.
[d0d5]DEY; Y--
[d0d6]BPL @_loop; If >= 0, loop.
;
; Restore the previous bank.
;
[d0d8]PLA; Pull the previous bank from the stack.
[d0d9]TAX; Set in X.
[d0da]JSR MMC1_UpdateROMBank; And switch to that bank.
[d0dd]JMP 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]
[d0e0].byte $10; [0]:
[d0e1].byte $20; [1]:
[d0e2].byte $30; [2]:
[d0e3].byte $40; [3]:
[d0e4]Sound_PlayEffect:; [$d0e4]
[d0e4]STA 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]TXA; Push X
[d0e8]PHA
[d0e9]TYA; Push Y
[d0ea]PHA
[d0eb]LDA a:Temp_SoundIDToPlay; Load the sound ID we stored.
[d0ee]JSR SoundEffect_SetCurrent; Play the sound.
;
; Pop X and Y from the stack and restore.
;
[d0f1]PLA; Pop A into Y.
[d0f2]TAY
[d0f3]PLA; Pop A into X.
[d0f4]TAX
[d0f5]RTS
;============================================================================
; TODO: Document Area_ScrollScreenRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_SetupNew
;============================================================================
[d0f6]Area_ScrollScreenRight:; [$d0f6]
[d0f6]LDA #$00
[d0f8]STA Screen_Maybe_ScrollHorizDirection
[d0fa]STA MaybeUnused_006d
[d0fc]STA Screen_Maybe_ScrollHorizDirection
[d0fe]LDA #$00
[d100]STA PPU_ScrollY
[d102]STA PPU_ScrollX
[d104]STA PPU_ScrollScreen
[d106]STA Screen_LoadBlocksStage
[d108]STA Screen_NeighborBlocksLoadedByDirection
[d10a]STA Screen_ScrollHorizBlocksLoaded
[d10c]STA Screen_MaybeUnused_0075
[d10e]STA Screen_ScrollHorizAttrsLoaded
[d110]LDX #$00
[d112]STX PPU_ScrollX
[d114]INX
[d115]STX PPU_ScrollScreen
[d117]LDX #$01
[d119]JSR Area_LoadScrollDataRight
[d11c]@_loop:; [$d11c]
[d11c]JSR Screen_HandleScroll
[d11f]JSR Screen_RunWriteScrollDataHandler
[d122]LDA Screen_ScrollDirection
[d124]BPL @_loop
[d126]RTS
;============================================================================
; TODO: Document Area_LoadScrollDataRight
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenRight
;
Area_ScrollTo
;============================================================================
[d127]Area_LoadScrollDataRight:; [$d127]
[d127]STX Screen_ScrollDirection
[d129]LDA PPU_ScrollScreen
[d12b]STA PPU_ScrollScreenHoriz
[d12d]LDA PPU_ScrollX
[d12f]STA Screen_ScrollHorizLoadCounter
[d131]LDA a:CurrentROMBank
[d134]PHA
[d135]LDX #$03
[d137]JSR MMC1_UpdateROMBank
[d13a]LDA #$00
[d13c]STA Temp_Addr_U
[d13e]LDA Area_CurrentScreen
[d140]ASL A
[d141]ROL Temp_Addr_U
[d143]ASL A
[d144]ROL Temp_Addr_U
[d146]CLC
[d147]ADC CurrentArea_ScrollingDataAddr
[d149]STA Temp_Addr_L
[d14b]LDA Temp_Addr_U
[d14d]ADC ScrollingData_U
[d14f]STA Temp_Addr_U
[d151]LDY #$00
[d153]@_loadScrollData:; [$d153]
[d153]LDA (Temp_Addr_L),Y
[d155]STA Area_ScreenToTheLeft,Y
[d158]INY
[d159]CPY #$04
[d15b]BCC @_loadScrollData
[d15d]PLA
[d15e]TAX
[d15f]JSR MMC1_UpdateROMBank
[d162]LDX CurrentArea_ROMBank
[d164]JSR MMC1_SaveROMBankAndUpdateTo
;
; Load blocks to the left of the screen.
;
[d167]LDA Area_ScreenToTheLeft
[d169]JSR Area_LoadBlocks
[d16c]LDX #$0f
[d16e]LDY #$00
[d170]@_copyLastColumnLeftScreenLoop:; [$d170]
[d170]LDA ScreenBuffer,X
[d173]STA LastColumnLeftScreen,Y
[d176]TXA
[d177]CLC
[d178]ADC #$10
[d17a]TAX
[d17b]INY
[d17c]CPY #$10
[d17e]BCC @_copyLastColumnLeftScreenLoop
;
; Load blocks to the right of the screen.
;
[d180]LDA Area_ScreenToTheRight
[d182]JSR Area_LoadBlocks
[d185]LDX #$00
[d187]LDY #$00
[d189]@_copyRowScreenAboveLoop:; [$d189]
[d189]LDA ScreenBuffer,X
[d18c]STA FirstColumnInRightScreen,Y
[d18f]TXA
[d190]CLC
[d191]ADC #$10
[d193]TAX
[d194]INY
[d195]CPY #$10
[d197]BCC @_copyRowScreenAboveLoop
;
; Load blocks from the screen above.
;
[d199]LDA Area_ScreenAbove
[d19b]JSR Area_LoadBlocks; Load blocks from screen above.
[d19e]LDX #$f0; X = 240
[d1a0]@_copyRowScreenBelowLoop:; [$d1a0]
[d1a0]LDA $05d0,X
[d1a3]STA $0316,X
[d1a6]INX; X++
[d1a7]BNE @_copyRowScreenBelowLoop
;
; Load blocks from the screen below.
;
[d1a9]LDA Area_ScreenBelow
[d1ab]JSR Area_LoadBlocks; Load blocks from screen below.
[d1ae]LDX #$f0
[d1b0]@LAB_PRG15_MIRROR__d1b0:; [$d1b0]
[d1b0]LDA $0510,X
[d1b3]STA $0326,X
[d1b6]INX
[d1b7]BNE @LAB_PRG15_MIRROR__d1b0
;
; Load blocks for the current screen.
;
[d1b9]LDA Area_CurrentScreen
[d1bb]JSR Area_LoadBlocks
[d1be]JSR MMC1_RestorePrevROMBank
[d1c1]LDA Screen_ScrollDirection
[d1c3]CMP #$02
[d1c5]BCS @_VerticalScroll
[d1c7]TAX
[d1c8]LDA #$00
[d1ca]STA Screen_ScrollPlayerTransitionCounter
[d1cc]LDA Player_PosY
[d1ce]STA Maybe_PlayerY_ForScroll
[d1d0]LDA SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE,X
[d1d3]STA Maybe_PlayerX_ForScroll
[d1d5]RTS
[d1d6]@_VerticalScroll:; [$d1d6]
[d1d6]AND #$01
[d1d8]TAX
[d1d9]LDA Player_PosX_Block
[d1db]STA Maybe_PlayerX_ForScroll
[d1dd]LDA #$00
[d1df]STA Screen_ScrollPlayerTransitionCounter
[d1e1]LDA SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE_2_,X
[d1e4]STA Maybe_PlayerY_ForScroll
[d1e6]RTS
[d1e7]SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE:; [$d1e7]
[d1e7].byte $00; [0]:
[d1e8].byte $f0; [1]:
[d1e9]SCREEN_MAYBE_PLAYERX_FOR_SCROLL_MODE_2_:; [$d1e9]
[d1e9].byte $00; [2]:
[d1ea].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]
[d1eb]LDX #$00; X = 0
[d1ed]LDA #$00; A = 0
;
; Begin writing empty blocks.
;
[d1ef]@_loop:; [$d1ef]
[d1ef]STA ScreenBuffer,X; Write 0 to the screen buffer.
[d1f2]INX; X = X + 1
[d1f3]BNE @_loop; If X hasn't wrapped back to 0, loop.
[d1f5]RTS
;============================================================================
; 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_LoadScrollDataRight
;============================================================================
[d1f6]Area_LoadBlocks:; [$d1f6]
[d1f6]CMP #$ff; Check if the screen index is set.
[d1f8]BEQ 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]ASL A; Multiply screen index by 2
[d1fb]TAY; Y = New screen index offset value
[d1fc]LDA (Area_ScreenBlocksOffset),Y; Set the lower block data address for this screen index.
[d1fe]STA Temp_08; Store it.
[d200]INY; Y++
[d201]LDA (Area_ScreenBlocksOffset),Y; Set the upper block data address for this screen index.
[d203]CLC
[d204]ADC #$80; Add 0x80 to the upper byte.
[d206]STA Temp_09; Store this as our total CHR page count.
;
; Set our initial state for compressed block loading.
;
[d208]LDA #$00; A = 0
[d20a]STA LoadCompressedScreenData_ByteOffset; Set byte offset to 0.
[d20c]STA Temp_LoadedBlocksCount; Set count to 0.
[d20e]STA LoadCompressedScreenData_BitOffset; Set bit offset to 0.
;
; Load the control bits for the block. This will dictate
; how loading will proceed.
;
[d210]@_nextBlock:; [$d210]
[d210]LDA #$00
[d212]STA 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]JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d217]ROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
[d219]JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d21c]ROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
;
; Check what bits we just loaded.
;
[d21e]LDA Temp_LoadedBlockValue; Load the current block value.
[d220]AND #$03; Check the 2 least-significant bits we loaded.
[d222]TAX; X = Our loaded bits value
[d223]CPX #$03; Is the value 3?
[d225]BEQ @_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]LDA Temp_LoadedBlocksCount; Else, load our blocks count.
[d229]CLC
[d22a]ADC BLOCK_DATA_OFFSETS_FOR_BIT_VALUES,X; Add that to the value for this bit in the lookup table.
[d22d]TAX; Set as X
[d22e]LDA ScreenBuffer,X; Load the block value from the screen buffer at the offset for this bit value.
[d231]JMP @_storeBlock; Proceed to store the block.
;
; Control 0x03: Read an 8-bit block.
;
[d234]@_read8BitBlock:; [$d234]
[d234]LDX #$08; X = 8 (total bits)
[d236]LDA #$00; A = 0 (new block value)
[d238]STA 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]
[d23a]JSR Area_LoadNextCompressedScreenBit; Load next bit from compressed data.
[d23d]ROL Temp_LoadedBlockValue; Shift our block to the left 1 and add the bit.
[d23f]DEX; X-- (total bits)
[d240]BNE @_load8BitsOfBlockData; If we're not at 0, loop.
[d242]LDA Temp_LoadedBlockValue; Else, load the resulting block value.
;
; Store the block value in the screen buffer at the
; current offset.
;
[d244]@_storeBlock:; [$d244]
[d244]LDX Temp_LoadedBlocksCount; X = loaded block count, as our new offset
[d246]STA ScreenBuffer,X; Store the byte in the screen buffer at that offset.
[d249]INC Temp_LoadedBlocksCount; Increment our block count.
[d24b]BNE @_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]LDX #$f0; X = 0xF0 (offset into the screen buffer to fill)
[d24f]LDA #$00; A = 0 (value to store)
;
; Fill the last line.
;
[d251]@_fillLastLine:; [$d251]
[d251]STA ScreenBuffer,X; Store in the screen buffer.
[d254]INX; X++
[d255]BNE @_fillLastLine; If we haven't hit 16 blocks (value 256 and wrapped around), then loop.
[d257]RTS
[d258]Area_LoadNextCompressedScreenBit:; [$d258]
[d258]LDA LoadCompressedScreenData_BitOffset; Load the current bit.
[d25a]BNE @_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]LDY LoadCompressedScreenData_ByteOffset; Get the offset of the byte we'll be processing.
[d25e]LDA (Temp_08),Y; Load the next byte.
[d260]STA LoadCompressedScreenData_CurByte
;
; Shift the current byte left by 1 and increment the bit
; offset by 1, for comparison.
;
[d262]@_update:; [$d262]
[d262]ASL LoadCompressedScreenData_CurByte; Shift the current byte we're processing.
[d264]PHP; Save all flags.
[d265]INC 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]LDA LoadCompressedScreenData_BitOffset; Check if we've hit the last bit in the byte.
[d269]AND #$07
[d26b]BNE @_return; If we have any data in bits 1-3...
[d26d]STA LoadCompressedScreenData_BitOffset; Reset the current bit to 0.
[d26f]INC LoadCompressedScreenData_ByteOffset; Increase the byte offset.
[d271]@_return:; [$d271]
[d271]PLP; Restore all flags.
[d272]RTS
[d273]BLOCK_DATA_OFFSETS_FOR_BIT_VALUES:; [$d273]
[d273].byte $ff; [0]:
[d274].byte $f0; [1]:
[d275].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:; [$d276]
;
; Switch to bank 3, where the level data is stored.
;
[d276]LDA a:CurrentROMBank
[d279]PHA
[d27a]LDX #$03
[d27c]JSR MMC1_UpdateROMBank
;
; Prepare for the read loop.
;
[d27f]LDY #$00; Y = 0
[d281]LDX #$00; X = 0
;
; Load the lower nibble from the block properties.
;
[d283]@_nextBlockProperty:; [$d283]
[d283]LDA (CurrentArea_BlockPropertiesAddr),Y; A = block property in the current area at offset Y.
[d285]AND #$0f; Retain the lower nibble.
[d287]STA Temp_00; Store that for later lookup.
;
; Load the next block property as the upper nibble.
;
[d289]INY; Increment the index in the block properties table.;
[d28a]LDA (CurrentArea_BlockPropertiesAddr),Y; Load it into A.
[d28c]ASL A; Move the lower nibble to the upper nibble.
[d28d]ASL A
[d28e]ASL A
[d28f]ASL A
;
; Combine the two nibbles for the new value and store it in
; BlockProperties.
;
[d290]ORA Temp_00; OR it to the lower nibble we saved.
[d292]STA BlockProperties,X; Store it.
;
; Keep going while the index is positive.
;
[d295]INY; Y = Y + 1
[d296]INX; X = X + 1
[d297]BPL @_nextBlockProperty; If we haven't overflowed, loop.
;
; We're done. Switch back to our previous bank.
;
[d299]PLA; Pull our saved bank.
[d29a]TAX; Set to X and...
[d29b]JSR MMC1_UpdateROMBank; Switch banks.
[d29e]RTS
[d29f]Screen_StopScrollAndLoadBlockProperties:; [$d29f]
[d29f]LDA #$ff
[d2a1]STA Screen_ScrollDirection
[d2a3]JMP Area_LoadBlockProperties
;============================================================================
; TODO: Document Screen_HandleScrollUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d2a6]Screen_HandleScrollUp:; [$d2a6]
[d2a6]LDA PPU_ScrollY
[d2a8]BNE @LAB_PRG15_MIRROR__d2ac
[d2aa]LDA #$d0
[d2ac]@LAB_PRG15_MIRROR__d2ac:; [$d2ac]
[d2ac]SEC
[d2ad]SBC #$01
[d2af]STA PPU_ScrollY
[d2b1]BNE @LAB_PRG15_MIRROR__d2b8
[d2b3]DEC PPU_ScrollScreenVert
[d2b5]JSR Screen_StopScrollAndLoadBlockProperties
[d2b8]@LAB_PRG15_MIRROR__d2b8:; [$d2b8]
[d2b8]LDA PPU_ScrollY
[d2ba]STA Screen_ScrollVertLoadCounter
[d2bc]LDA a:CurrentROMBank
[d2bf]PHA
[d2c0]LDX #$03
[d2c2]JSR MMC1_UpdateROMBank
[d2c5]JSR Screen_LoadDataUp
[d2c8]PLA
[d2c9]TAX
[d2ca]JSR MMC1_UpdateROMBank
[d2cd]RTS
;============================================================================
; TODO: Document Screen_UpdateForScroll
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_UpdateForScroll
;
Screen_UpdateForScroll
;============================================================================
[d2ce]Screen_UpdateForScroll:; [$d2ce]
[d2ce]JSR Game_UpdatePlayerOnScroll; Update the player position during scroll.
[d2d1]JSR Screen_HandleScroll; Update the screen state/tiles on scroll.
[d2d4]JSR Screen_RunWriteScrollDataHandler; Write data to the PPU based on the scroll direction.
[d2d7]LDA Screen_ScrollDirection; Load the scroll direction.
[d2d9]CMP #$02; Is it left or right?
[d2db]BCS @_isLeftOrRight
;
; The screen is scrolling up, down, or not at all.
;
[d2dd]LDA PPU_ScrollX; Load the scroll X delta.
[d2df]BNE Screen_UpdateForScroll; If it's not 0, loop.
[d2e1]RTS
;
; The screen is scrolling left or right.
;
[d2e2]@_isLeftOrRight:; [$d2e2]
[d2e2]LDA PPU_ScrollY; Load the scroll Y delta.
[d2e4]BNE Screen_UpdateForScroll; If it's not 0, loop.
[d2e6]RETURN_D2E6:; [$d2e6]
[d2e6]RTS; Else, return.
;============================================================================
; TODO: Document Screen_HandleScroll
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenRight
;
Game_MainLoop
;
Screen_UpdateForScroll
;============================================================================
[d2e7]Screen_HandleScroll:; [$d2e7]
[d2e7]LDX Screen_ScrollDirection
[d2e9]BEQ Screen_HandleScrollLeft
[d2eb]DEX
[d2ec]BEQ Screen_HandleScrollRight
[d2ee]DEX
[d2ef]BEQ Screen_HandleScrollUp
[d2f1]DEX
[d2f2]BNE RETURN_D2E6
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Screen_HandleScrollDown
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;============================================================================
[d2f4]Screen_HandleScrollDown:; [$d2f4]
;
; The screen is scrolling down.
;
[d2f4]LDA PPU_ScrollY
[d2f6]CLC
[d2f7]ADC #$01
[d2f9]STA PPU_ScrollY
[d2fb]CMP #$d0
[d2fd]BCC @LAB_PRG15_MIRROR__d308
[d2ff]LDA #$00
[d301]STA PPU_ScrollY
[d303]INC PPU_ScrollScreenVert
[d305]JSR Screen_StopScrollAndLoadBlockProperties
[d308]@LAB_PRG15_MIRROR__d308:; [$d308]
[d308]LDA PPU_ScrollY
[d30a]STA Screen_ScrollVertLoadCounter
[d30c]LDA a:CurrentROMBank
[d30f]PHA
[d310]LDX #$03
[d312]JSR MMC1_UpdateROMBank
[d315]JSR Screen_LoadBlocksDown
[d318]PLA
[d319]TAX
[d31a]JSR MMC1_UpdateROMBank
[d31d]RTS
;============================================================================
; TODO: Document Screen_HandleScrollRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d31e]Screen_HandleScrollRight:; [$d31e]
;
; The screen is scrolling right.
;
[d31e]LDA PPU_ScrollX
[d320]CLC
[d321]ADC #$01
[d323]STA PPU_ScrollX
[d325]PHP
[d326]LDA PPU_ScrollScreenHoriz
[d328]ADC #$00
[d32a]STA PPU_ScrollScreenHoriz
[d32c]STA PPU_ScrollScreen
[d32e]PLP
[d32f]BCC @LAB_PRG15_MIRROR__d334
[d331]JSR Screen_StopScrollAndLoadBlockProperties
[d334]@LAB_PRG15_MIRROR__d334:; [$d334]
[d334]LDA PPU_ScrollX
[d336]STA Screen_ScrollHorizLoadCounter
[d338]LDA a:CurrentROMBank
[d33b]PHA
[d33c]LDX #$03
[d33e]JSR MMC1_UpdateROMBank
[d341]JSR Screen_LoadDataRight
[d344]PLA
[d345]TAX
[d346]JSR MMC1_UpdateROMBank
[d349]RTS
;============================================================================
; TODO: Document Screen_HandleScrollLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_HandleScroll
;============================================================================
[d34a]Screen_HandleScrollLeft:; [$d34a]
;
; The screen is scrolling left.
;
[d34a]LDA Screen_ScrollHorizLoadCounter
[d34c]CMP #$fc
[d34e]BCS @LAB_PRG15_MIRROR__d364
[d350]LDA PPU_ScrollX
[d352]SEC
[d353]SBC #$01
[d355]STA PPU_ScrollX
[d357]PHP
[d358]LDA PPU_ScrollScreen
[d35a]SBC #$00
[d35c]STA PPU_ScrollScreen
[d35e]PLP
[d35f]BNE @LAB_PRG15_MIRROR__d364
[d361]JSR Screen_StopScrollAndLoadBlockProperties
[d364]@LAB_PRG15_MIRROR__d364:; [$d364]
[d364]LDA Screen_ScrollHorizLoadCounter
[d366]SEC
[d367]SBC #$01
[d369]STA Screen_ScrollHorizLoadCounter
[d36b]LDA PPU_ScrollScreenHoriz
[d36d]SBC #$00
[d36f]STA PPU_ScrollScreenHoriz
[d371]CMP PPU_ScrollScreen
[d373]BNE @_finish
[d375]LDA PPU_ScrollX
[d377]CMP #$04
[d379]BCS @_finish
[d37b]RTS
[d37c]@_finish:; [$d37c]
[d37c]LDA a:CurrentROMBank
[d37f]PHA
[d380]LDX #$03
[d382]JSR MMC1_UpdateROMBank
[d385]JSR Screen_LoadDataLeft
[d388]PLA
[d389]TAX
[d38a]JSR MMC1_UpdateROMBank
[d38d]RTS
;============================================================================
; 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]
[d38e]INC Screen_ScrollLoadMode; Set load mode to 1 (load attributes)
[d390]JMP 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]
[d393]LDX #$00; 0 = Load Tiles mode
[d395]STX Screen_ScrollLoadMode; Set as the default mode.
[d397]LDA Screen_ScrollVertLoadCounter; A = Vertical blocks counter
[d399]AND #$0f; Keep the lower nibble.
[d39b]CMP #$07; Is it 7 (every 8 blocks, starting at 7)?
[d39d]BEQ Screen_LoadAttrsVert; If so, load attributes.
[d39f]AND #$07; Else, keep the lower 3 bits.
[d3a1]CMP #$03; Is it 3 (every 8 blocks, starting at 3)?
[d3a3]BEQ Screen_LoadBlockDataVert; If so, load tiles.
[d3a5]RTS; 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]
[d3a6]LDX #$00; 0 = Load Tiles mode
[d3a8]STX Screen_ScrollLoadMode; Set as the default mode.
[d3aa]INX; X++ (reuse as "is down" flag, set to 1).
[d3ab]LDA Screen_ScrollVertLoadCounter; A = Vertical blocks counter
[d3ad]AND #$0f; Keep the lower nibble.
[d3af]CMP #$08; Is it 8 (every 16 blocks, starting at 8)?
[d3b1]BEQ Screen_LoadAttrsVert; If so, load attributes.
[d3b3]AND #$07; Else, keep the lower 3 bits.
[d3b5]CMP #$04; Is it 4 (every 8 blocks, starting at 4)?
[d3b7]BEQ Screen_LoadBlockDataVert; If so, load tiles.
[d3b9]RTS; Else, return.
[d3ba]Screen_LoadBlockDataVert:; [$d3ba]
[d3ba]LDA Screen_ScrollVertLoadCounter
[d3bc]CLC
[d3bd]ADC $d4cb,X
[d3c0]STA Temp_00
[d3c2]LDA PPU_ScrollScreenVert
[d3c4]ADC #$00
[d3c6]STA Unused_Blocks_0049
[d3c8]LDA Area_CurrentScreen
[d3ca]STA MaybeUnused_006d
[d3cc]LDA Temp_00
[d3ce]AND #$f0
[d3d0]STA Temp_Blocks_0048
[d3d2]LDA #$00
[d3d4]CLC
[d3d5]ADC Temp_Blocks_0048
[d3d7]STA Temp_08
[d3d9]LDA #$06
[d3db]ADC #$00
[d3dd]STA Temp_09
[d3df]LDA Screen_ScrollLoadMode
[d3e1]BEQ @LAB_PRG15_MIRROR__d3e6
[d3e3]JMP @LAB_PRG15_MIRROR__d445
[d3e6]@LAB_PRG15_MIRROR__d3e6:; [$d3e6]
[d3e6]LDY #$00
[d3e8]LDX #$00
[d3ea]@LAB_PRG15_MIRROR__d3ea:; [$d3ea]
[d3ea]STY Temp_06
[d3ec]LDA (Temp_08),Y
[d3ee]TAY
[d3ef]LDA (CurrentArea_BlockData1StartAddr),Y
[d3f1]STA CurrentArea_BlockData1CurAddr
[d3f3]LDA (CurrentArea_BlockData2StartAddr),Y
[d3f5]STA CurrentArea_BlockData2CurAddr
[d3f7]LDA (CurrentArea_BlockData3StartAddr),Y
[d3f9]STA CurrentArea_BlockData3CurAddr
[d3fb]LDA (CurrentArea_BlockData4StartAddr),Y
[d3fd]STA CurrentArea_BlockData4CurAddr
[d3ff]LDA Screen_ScrollVertLoadCounter
[d401]AND #$08
[d403]LSR A
[d404]LSR A
[d405]TAY
[d406]LDA CurrentArea_BlockData1CurAddr,Y
[d409]STA DataArray,X
[d40c]LDA CurrentArea_BlockData2CurAddr,Y
[d40f]STA DataArray_1_,X
[d412]INX
[d413]INX
[d414]LDY Temp_06
[d416]INY
[d417]CPY #$10
[d419]BCC @LAB_PRG15_MIRROR__d3ea
[d41b]LDA #$00
[d41d]STA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d41f]LDA Screen_ScrollVertLoadCounter
[d421]AND #$f8
[d423]ASL A
[d424]ROL Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d426]ASL A
[d427]ROL Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d429]CLC
[d42a]ADC #$80
[d42c]STA Screen_ScrollVertBlocks_PPUTileMapAddr_L
[d42e]LDA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d430]ADC #$00
[d432]STA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d434]LDA PPU_ScrollScreenHoriz
[d436]AND #$01
[d438]ASL A
[d439]ASL A
[d43a]ORA #$20
[d43c]ORA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d43e]STA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d440]LDA #$01
[d442]STA Screen_NeighborBlocksLoadedByDirection
[d444]RTS
[d445]@LAB_PRG15_MIRROR__d445:; [$d445]
[d445]LDA Screen_ScrollVertLoadCounter
[d447]AND #$16
[d449]LSR A
[d44a]LSR A
[d44b]LSR A
[d44c]STA Temp_01
[d44e]LDY #$f8
[d450]LDA #$00
[d452]@LAB_PRG15_MIRROR__d452:; [$d452]
[d452]STA $0192,Y
[d455]INY
[d456]BNE @LAB_PRG15_MIRROR__d452
[d458]@LAB_PRG15_MIRROR__d458:; [$d458]
[d458]STY Temp_06
[d45a]LDA (Temp_08),Y
[d45c]TAY
[d45d]LDA (CurrentArea_BlockAttributesAddr),Y
[d45f]STA Temp_00
[d461]LDA Temp_06
[d463]AND #$01
[d465]ORA Temp_01
[d467]TAY
[d468]LDA Temp_00
[d46a]AND BLOCK_ATTRS_BITMASKS,Y
[d46d]PHA
[d46e]LDA Temp_06
[d470]LSR A
[d471]TAY
[d472]PLA
[d473]ORA Screen_ScrollVertPPUAttrData,Y
[d476]STA Screen_ScrollVertPPUAttrData,Y
[d479]LDY Temp_06
[d47b]INY
[d47c]CPY #$10
[d47e]BCC @LAB_PRG15_MIRROR__d458
[d480]LDX #$f0
[d482]LDA Screen_ScrollVertLoadCounter
[d484]AND #$10
[d486]BEQ @LAB_PRG15_MIRROR__d48a
[d488]LDX #$0f
[d48a]@LAB_PRG15_MIRROR__d48a:; [$d48a]
[d48a]STX Temp_06
[d48c]LDA Screen_ScrollVertLoadCounter
[d48e]AND #$e0
[d490]LSR A
[d491]LSR A
[d492]STA Temp_00
[d494]CLC
[d495]ADC #$08
[d497]TAY
[d498]CLC
[d499]ADC #$c0
[d49b]STA Screen_ScrollVertBlocks_PPUAttrAddr_L
[d49d]LDA PPU_ScrollScreenHoriz
[d49f]AND #$01
[d4a1]TAX
[d4a2]ASL A
[d4a3]ASL A
[d4a4]ORA #$23
[d4a6]STA Screen_ScrollVertBlocks_PPUAttrAddr_U
[d4a8]LDA SET_BLOCKS_TILEMAP_OFFSETS_L,X
[d4ab]STA Temp_08
[d4ad]LDA SET_BLOCKS_TILEMAP_OFFSETS_U,X
[d4b0]STA Temp_09
[d4b2]LDX #$00
[d4b4]@LAB_PRG15_MIRROR__d4b4:; [$d4b4]
[d4b4]LDA (Temp_08),Y
[d4b6]AND Temp_06
[d4b8]ORA Screen_ScrollVertPPUAttrData,X
[d4bb]STA (Temp_08),Y
[d4bd]STA Screen_ScrollVertPPUAttrData,X
[d4c0]INY
[d4c1]INX
[d4c2]CPX #$08
[d4c4]BCC @LAB_PRG15_MIRROR__d4b4
[d4c6]LDA #$01
[d4c8]STA Screen_MaybeUnused_0075
[d4ca]RTS
[d4cb].byte $00; [0]:
[d4cc]BYTE_ARRAY_PRG15_MIRROR__d4cb_1_:; [$d4cc]
[d4cc].byte $00; [1]:
[d4cd].byte $00,$08; [$d4cd] byte
[d4cf]SET_BLOCKS_TILEMAP_OFFSETS_L:; [$d4cf]
[d4cf].byte $42; [0]:
[d4d0].byte $42; [1]:
[d4d1]SET_BLOCKS_TILEMAP_OFFSETS_U:; [$d4d1]
[d4d1].byte $02; [0]:
[d4d2].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]
[d4d3].byte $03; [0]: Mask bits 0 and 1
[d4d4].byte $0c; [1]: Mask bits 2 and 3
[d4d5].byte $30; [2]: Mask bits 4 and 5
[d4d6].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]
[d4d7]INC Screen_ScrollLoadMode; Set load mode to 1 (load attributes).
[d4d9]JMP 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]
[d4dc]LDX #$00; 0 = Load Tiles mode
[d4de]STX Screen_ScrollLoadMode; Set as the default mode.
[d4e0]INX; X++ (reuse as "is right" flag, set to 1).
[d4e1]LDA Screen_ScrollHorizLoadCounter; A = Horizontal blocks counter
[d4e3]AND #$0f; Keep the lower nibble.
[d4e5]CMP #$02; Is it 2 (every 4 blocks, starting at 2)?
[d4e7]BEQ Screen_LoadAttrsHoriz; If so, load attributes.
[d4e9]AND #$07; Else, keep the lower 3 bits.
[d4eb]CMP #$01; Is it 1 (every 4 blocks, starting at 1)?
[d4ed]BEQ Screen_LoadBlocksHoriz; If so, load tiles.
[d4ef]RTS; 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]
[d4f0]LDX #$00; 0 = Load Tiles mode
[d4f2]STX Screen_ScrollLoadMode; Set as the default mode.
[d4f4]LDA Screen_ScrollHorizLoadCounter; A = Horizontal blocks counter
[d4f6]AND #$0f; Keep the lower nibble.
[d4f8]CMP #$0f; Is it 0xF (every 16, starting at 15)?
[d4fa]BEQ Screen_LoadAttrsHoriz; If so, load attributes.
[d4fc]AND #$07; Else, keep the lower 3 bits.
[d4fe]CMP #$06; Is it 6 (every 8 blocks, starting at 6)?
[d500]BEQ Screen_LoadBlocksHoriz; If so, load tiles.
[d502]RTS; Else, return.
;============================================================================
; TODO: Document Screen_LoadBlocksHoriz
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Screen_LoadAttrsHoriz
;
Screen_LoadDataLeft
;
Screen_LoadDataRight
;============================================================================
[d503]Screen_LoadBlocksHoriz:; [$d503]
[d503]LDA Screen_ScrollHorizLoadCounter
[d505]CLC
[d506]ADC SCROLL_HORIZ_END_POS,X
[d509]STA Temp_00
[d50b]LDA PPU_ScrollScreenHoriz
[d50d]ADC BYTE_ARRAY_PRG15_MIRROR__d61b,X
[d510]STA Screen_Maybe_ScrollHorizDirection
[d512]LDA Area_CurrentScreen
[d514]STA MaybeUnused_006d
[d516]LDA Temp_00
[d518]LSR A
[d519]LSR A
[d51a]LSR A
[d51b]LSR A
[d51c]STA Screen_ScrollHorizLoadOffset
[d51e]LDA #$00
[d520]STA Temp_08
[d522]LDA #$06
[d524]STA Temp_09
[d526]LDA Screen_ScrollLoadMode
[d528]BNE @_loadPPUData
;
; Load 30 blocks to render in the next draw phase.
;
[d52a]LDX #$00; X = 0 (counter)
;
; Determine the offset for the blocks to load.
;
[d52c]@_loadBlocksLoop:; [$d52c]
[d52c]LDY Screen_ScrollHorizLoadOffset
[d52e]LDA (Temp_08),Y
;
; Load the blocks information.
;
[d530]TAY
[d531]LDA (CurrentArea_BlockData1StartAddr),Y
[d533]STA CurrentArea_BlockData1CurAddr
[d535]LDA (CurrentArea_BlockData3StartAddr),Y
[d537]STA CurrentArea_BlockData2CurAddr
[d539]LDA (CurrentArea_BlockData2StartAddr),Y
[d53b]STA CurrentArea_BlockData3CurAddr
[d53d]LDA (CurrentArea_BlockData4StartAddr),Y
[d53f]STA CurrentArea_BlockData4CurAddr
;
; Convert the block counter to a tile width.
;
[d541]LDA Screen_ScrollHorizLoadCounter
[d543]AND #$08
[d545]LSR A
[d546]LSR A
[d547]TAY
[d548]LDA CurrentArea_BlockData1CurAddr,Y
[d54b]STA Temp_0200,X
[d54e]LDA CurrentArea_BlockData2CurAddr,Y
[d551]STA Temp_0201,X
[d554]LDA Temp_08
[d556]CLC
[d557]ADC #$10
[d559]STA Temp_08
[d55b]LDA Temp_09
[d55d]ADC #$00
[d55f]STA Temp_09
[d561]INX
[d562]INX
[d563]CPX #$1e
[d565]BCC @_loadBlocksLoop
[d567]LDA #$01
[d569]STA Screen_ScrollHorizBlocksLoaded
[d56b]TYA
[d56c]LSR A
[d56d]PHA
[d56e]LDA Screen_ScrollHorizLoadOffset
[d570]ASL A
[d571]STA Screen_ScrollHorizLoadOffset
[d573]PLA
[d574]CLC
[d575]ADC Screen_ScrollHorizLoadOffset
[d577]CLC
[d578]ADC #$80
[d57a]STA Screen_ScrollHorizBlocks_PPUTileMapAddr_L
[d57c]LDA Screen_Maybe_ScrollHorizDirection
[d57e]AND #$01
[d580]ASL A
[d581]ASL A
[d582]ORA #$20
[d584]STA Screen_ScrollHorizBlocks_PPUTileMapAddr_U
[d586]RTS
;
; 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]@_loadPPUData:; [$d587]
[d587]LDY #$f8; Y = 0xF8 (loop counter)
[d589]LDA #$00; A = 0 (value to write)
[d58b]@_clearLoop:; [$d58b]
[d58b]STA Screen_ScrollHoriz_ZeroData,Y; Write a 0 to this empty block.
[d58e]INY; Y++
[d58f]BNE @_clearLoop; If != 0, loop.
;
; Begin loading PPU attribute data and scheduling to draw.
;
[d591]LDX #$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]
[d593]LDY Screen_ScrollHorizLoadOffset; Y = Attr data load offset
[d595]LDA (Temp_08),Y; A = Screen buffer data at offset (block attribute offset)
[d597]TAY; Y = A
[d598]LDA (CurrentArea_BlockAttributesAddr),Y; A = block attribute at resulting screen buffer data offset value
[d59a]STA 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]LDA Screen_ScrollHorizLoadOffset; A = Attr data load offset
[d59e]AND #$01; Keep the least-significant bit (even/odd).
[d5a0]STA Temp_01; Store it temporarily.
[d5a2]TXA; A = X (loop counter)
[d5a3]AND #$01; Keep the loop counter's least-significant bit (even/odd).
[d5a5]ASL A; A *= 2
[d5a6]ORA Temp_01; OR with the load offset's even/odd bit.
[d5a8]TAY; Y = A (result)
;
; Mask 2 bits of the block attributes based on that index.
;
[d5a9]LDA Temp_00; A = Loaded block attribute
[d5ab]AND BLOCK_ATTRS_BITMASKS,Y; AND with the bitmask based on our computed index.
[d5ae]PHA; Push the result to the stack.
;
; Generate the destination index into the PPU attribute data.
;
; This will be the loop counter / 2.
;
[d5af]TXA; A = X (loop counter)
[d5b0]LSR A; A = A / 2
[d5b1]TAY; Y = A
;
; OR the result with the value already loaded at this loop
; index, and store it back.
;
[d5b2]PLA; Pop the masked block attribute from stack.
[d5b3]ORA Screen_ScrollHorizPPUAttrData,Y; OR with the data already set.
[d5b6]STA Screen_ScrollHorizPPUAttrData,Y; And store it.
;
; Advance the screen buffer address by 16 bytes.
;
[d5b9]LDA Temp_08; A = Lower byte of Current screen buffer address.
[d5bb]CLC
[d5bc]ADC #$10; A += 16
[d5be]STA Temp_08; Store as the new lower byte.
[d5c0]LDA Temp_09; A = Upper byte of Current screen buffer address.
[d5c2]ADC #$00; A += Carry from lower increment
[d5c4]STA Temp_09; Store as the new upper byte.
;
; Advance the loop counter, and loop if < 15.
;
[d5c6]INX; X++ (loop counter)
[d5c7]CPX #$0f; Is this < 15?
[d5c9]BCC @_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]LDX #$cc; Default bitmask to enable bits 2-3, 6-7 (11001100)
[d5cd]LDA Screen_ScrollHorizLoadOffset; Load the data load offset.
[d5cf]AND #$01; Is it even?
[d5d1]BEQ @_updatePPUAttrAddr; If so, jump to skip.
[d5d3]LDX #$33; Set bitmask to enable bits 0-1, 4-5 (00110011)
[d5d5]@_updatePPUAttrAddr:; [$d5d5]
[d5d5]STX Temp_06; Store for later.
[d5d7]LDA Screen_ScrollHorizLoadOffset; A = Data load offset.
[d5d9]LSR A; A = A / 2
[d5da]CLC
[d5db]ADC #$08; A += 8
[d5dd]TAY; Y = A (new screen buffer write offset)
[d5de]CLC
[d5df]ADC #$c0; A += 0xC0
[d5e1]STA Screen_ScrollHorizBlocks_PPUAttrAddr_L; Set as the lower byte of the PPU attr address.
[d5e3]LDA Screen_Maybe_ScrollHorizDirection
[d5e5]AND #$01
[d5e7]TAX
[d5e8]ASL A
[d5e9]ASL A
[d5ea]ORA #$23
[d5ec]STA Screen_ScrollHorizBlocks_PPUAttrAddr_U; Set as the upper byte of the PPU attr address.
[d5ee]LDA SET_BLOCKS_TILEMAP_OFFSETS_L,X
[d5f1]STA Temp_08
[d5f3]LDA SET_BLOCKS_TILEMAP_OFFSETS_U,X
[d5f6]STA 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]LDX #$00; X = 0 (loop counter)
[d5fa]@_updatePPUAttrDataLoop:; [$d5fa]
[d5fa]LDA (Temp_08),Y; Load the value from the screen buffer write offset at Y.
[d5fc]AND Temp_06; AND with the bitmask computed above.
[d5fe]ORA Screen_ScrollHorizPPUAttrData,X; OR with the existing PPU attribute data
[d601]STA Screen_ScrollHorizPPUAttrData,X; And store it.
[d604]STA (Temp_08),Y; Store it back in the screen buffer at write offset Y.
[d606]INX; X++ (loop counter)
;
; The next three instructions are effectively unused.
; Legacy code?
;
[d607]TXA; <unused>
[d608]AND #$07; <unused>
[d60a]TXA; <unused>
;
; Advance the earlier loop counter (which should start at 15)
; by 8 each loop iteration.
;
[d60b]TYA; A = Y (loop counter from initial attribute loading)
[d60c]CLC
[d60d]ADC #$08; A += 8
[d60f]TAY; Y = A
[d610]CPY #$40; Is it < 64?
[d612]BCC @_updatePPUAttrDataLoop; If so, loop.
;
; Mark the attribute data as loaded.
;
[d614]LDA #$01
[d616]STA Screen_ScrollHorizAttrsLoaded; Set attribute data loaded.
[d618]RTS
[d619]SCROLL_HORIZ_END_POS:; [$d619]
[d619].byte $00; [0]:
[d61a].byte $ff; [1]:
[d61b]BYTE_ARRAY_PRG15_MIRROR__d61b:; [$d61b]
[d61b].byte $00; [0]:
[d61c].byte $00; [1]:
;============================================================================
; TODO: Document Screen_RunWriteScrollDataHandler
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenRight
;
PPU_HandleOnInterrupt
;
Screen_UpdateForScroll
;============================================================================
[d61d]Screen_RunWriteScrollDataHandler:; [$d61d]
[d61d]LDA #$03
[d61f]STA Temp_06
[d621]LDX Screen_LoadBlocksStage
[d623]@_loop:; [$d623]
[d623]LDA Screen_NeighborBlocksLoadedByDirection,X
[d625]BNE @LAB_PRG15_MIRROR__d631
[d627]INX
[d628]TXA
[d629]AND #$03
[d62b]TAX
[d62c]DEC Temp_06
[d62e]BPL @_loop
[d630]RTS
[d631]@LAB_PRG15_MIRROR__d631:; [$d631]
[d631]LDA #$00
[d633]STA Screen_NeighborBlocksLoadedByDirection,X
[d635]STX Temp_06
[d637]INX
[d638]TXA
[d639]AND #$03
[d63b]TAX
[d63c]STX Screen_LoadBlocksStage
[d63e]LDA Temp_06
[d640]AND #$03
[d642]TAX
[d643]LDA SCREEN_WRITESCROLL_HANDLERS_U,X
[d646]PHA
[d647]LDA SCREEN_WRITESCROLL_HANDLERS_L,X
[d64a]PHA
[d64b]RTS
[d64c]SCREEN_WRITESCROLL_HANDLERS_L:; [$d64c]
[d64c].byte <(Screen_WriteScrollVertPPUTileData-1); [0]:
[d64d].byte <(Screen_WriteScrollHorizPPUTileData-1); [1]:
[d64e].byte <(Screen_WriteScrollVertPPUAttrData-1); [2]:
[d64f].byte <(Screen_WriteScrollHorizPPUAttrData-1); [3]:
[d650]SCREEN_WRITESCROLL_HANDLERS_U:; [$d650]
[d650].byte >(Screen_WriteScrollVertPPUTileData-1); [0]:
[d651].byte >(Screen_WriteScrollHorizPPUTileData-1); [1]:
[d652].byte >(Screen_WriteScrollVertPPUAttrData-1); [2]:
[d653].byte >(Screen_WriteScrollHorizPPUAttrData-1); [3]:
;============================================================================
; TODO: Document Screen_WriteScrollVertPPUTileData
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
SCREEN_WRITESCROLL_HANDLERS_L
; [$PRG15_MIRROR::d64c]
;
SCREEN_WRITESCROLL_HANDLERS_U
; [$PRG15_MIRROR::d650]
;============================================================================
[d654]Screen_WriteScrollVertPPUTileData:; [$d654]
[d654]LDA PPU_ControlFlags
[d656]AND #$fb
[d658]STA a:PPUCTRL
[d65b]LDA Screen_ScrollVertBlocks_PPUTileMapAddr_U
[d65d]STA a:PPUADDR
[d660]LDA Screen_ScrollVertBlocks_PPUTileMapAddr_L
[d662]STA a:PPUADDR
[d665]LDX #$00
[d667]@_loop:; [$d667]
[d667]LDA DataArray,X
[d66a]STA a:PPUDATA
[d66d]INX
[d66e]CPX #$20
[d670]BCC @_loop
[d672]RTS
;============================================================================
; TODO: Document Screen_WriteScrollHorizPPUTileData
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
SCREEN_WRITESCROLL_HANDLERS_L
; [$PRG15_MIRROR::d64d]
;
SCREEN_WRITESCROLL_HANDLERS_U
; [$PRG15_MIRROR::d651]
;============================================================================
[d673]Screen_WriteScrollHorizPPUTileData:; [$d673]
[d673]LDA PPU_ControlFlags
[d675]ORA #$04
[d677]STA a:PPUCTRL
[d67a]LDA Screen_ScrollHorizBlocks_PPUTileMapAddr_U
[d67c]STA a:PPUADDR
[d67f]LDA Screen_ScrollHorizBlocks_PPUTileMapAddr_L
[d681]STA a:PPUADDR
[d684]LDX #$00
[d686]@_loop:; [$d686]
[d686]LDA Temp_0200,X
[d689]STA a:PPUDATA
[d68c]INX
[d68d]CPX #$1a
[d68f]BCC @_loop
[d691]LDA PPU_ControlFlags
[d693]AND #$fb
[d695]STA a:PPUCTRL
[d698]RTS
;============================================================================
; TODO: Document Screen_WriteScrollVertPPUAttrData
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
SCREEN_WRITESCROLL_HANDLERS_L
; [$PRG15_MIRROR::d64e]
;
SCREEN_WRITESCROLL_HANDLERS_U
; [$PRG15_MIRROR::d652]
;============================================================================
[d699]Screen_WriteScrollVertPPUAttrData:; [$d699]
[d699]LDA Screen_ScrollVertBlocks_PPUAttrAddr_U
[d69b]STA a:PPUADDR
[d69e]LDA Screen_ScrollVertBlocks_PPUAttrAddr_L
[d6a0]STA a:PPUADDR
[d6a3]LDX #$00
[d6a5]@_loop:; [$d6a5]
[d6a5]LDA Screen_ScrollVertPPUAttrData,X
[d6a8]STA a:PPUDATA
[d6ab]INX
[d6ac]CPX #$08
[d6ae]BCC @_loop
[d6b0]RTS
;============================================================================
; TODO: Document Screen_WriteScrollHorizPPUAttrData
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
SCREEN_WRITESCROLL_HANDLERS_L
; [$PRG15_MIRROR::d64f]
;
SCREEN_WRITESCROLL_HANDLERS_U
; [$PRG15_MIRROR::d653]
;============================================================================
[d6b1]Screen_WriteScrollHorizPPUAttrData:; [$d6b1]
[d6b1]LDX #$00
[d6b3]@_loop:; [$d6b3]
[d6b3]LDA Screen_ScrollHorizBlocks_PPUAttrAddr_U
[d6b5]STA a:PPUADDR
[d6b8]TXA
[d6b9]ASL A
[d6ba]ASL A
[d6bb]ASL A
[d6bc]CLC
[d6bd]ADC Screen_ScrollHorizBlocks_PPUAttrAddr_L
[d6bf]STA a:PPUADDR
[d6c2]LDA Screen_ScrollHorizPPUAttrData,X
[d6c5]STA a:PPUDATA
[d6c8]INX
[d6c9]CPX #$07
[d6cb]BCC @_loop
[d6cd]RTS
;============================================================================
; 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]
[d6ce]STX Temp_00; Temporarily store the block position.
[d6d0]LDX #$00; X = 0 (loop counter)
[d6d2]LDA Blocks_Result; A = block type (value to search for)
[d6d4]@_checkBlockLoop:; [$d6d4]
[d6d4]CMP BREAKABLE_FLOOR_TRANSITIONS,X; Does the block match the table at this index?
[d6d7]BEQ @_finishLoop; If so, jump.
[d6d9]INX; Else, X++
[d6da]CPX #$03; Is X < 3?
[d6dc]BCC @_checkBlockLoop; If so, loop.
[d6de]BCS @_updateBlocks; Else, jump to update the block.
[d6e0]@_finishLoop:; [$d6e0]
[d6e0]INX; X++ (table index of block to set)
[d6e1]@_updateBlocks:; [$d6e1]
[d6e1]LDA BREAKABLE_FLOOR_TRANSITIONS,X; Load the block to set to.
[d6e4]STA a:Arg_BlockAttributesIndex; DEADCODE: This is overridden in Area_SetBlocks
[d6e7]LDX Temp_00; X = Block position (stored earlier)
[d6e9]STA ScreenBuffer,X; Store in the screen buffer as the new block.
[d6ec]JMP 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]
[d6ef].byte $34; [0]:
[d6f0]BREAKABLE_FLOOR_TRANSITIONS_1_:; [$d6f0]
[d6f0].byte $2c; [1]:
[d6f1]BREAKABLE_FLOOR_TRANSITIONS_2_:; [$d6f1]
[d6f1].byte $5c; [2]:
[d6f2].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]
[d6f3].byte $20; [0]: Screen 0
[d6f4].byte $24; [1]: Screen 1
[d6f5]Game_OpenPathToMascon:; [$d6f5]
;
; Push the stone covering block offset to the stack for later.
;
[d6f5]TXA; A = X
[d6f6]PHA; Push A to the stack.
;
; Clear the top stone covering.
;
[d6f7]LDX PathToMascon_FountainCoverPos; X = Block position (in $YX form).
[d6f9]LDA a:MASCON_FOUNTAIN_BLOCK_1_AIR; A = Block ID to place.
[d6fc]STA ScreenBuffer,X; Store it in the screen buffer at the target location.
[d6ff]JSR Area_SetBlocks; Update the block on the screen.
;
; Clear the bottom stone covering.
;
[d702]LDA PathToMascon_FountainCoverPos; X = Block position (in $YX form).
[d704]CLC
[d705]ADC #$10; X += 16 (next row down; YPos += 1).
[d707]TAX; X = A
[d708]LDA a:MASCON_FOUNTAIN_BLOCK_2_AIR; A = Block ID to place.
[d70b]STA ScreenBuffer,X; Store it in the screen buffer at the target location.
[d70e]JSR Area_SetBlocks; Update the block on the screen.
;
; Pop the original stone covering block offset.
;
[d711]PLA; Pull A from the stack (original block position)
[d712]TAX; X = A
[d713]LDA GAME_LADDER_TO_MASCON_BLOCK_OFFSETS,X; A = Block position offset, based on the arguments.
[d716]CLC
[d717]ADC PathToMascon_FountainCoverPos; A += Target block position.
[d719]TAX; X = A
[d71a]STX PathToMascon_FountainCoverPos; Update the block position.
;
; Place the top stone covering.
;
[d71c]LDA a:MASCON_FOUNTAIN_BLOCK_1_STONE; A = Stone block.
[d71f]STA ScreenBuffer,X; Place it in the screen buffer at the offset.
[d722]JSR Area_SetBlocks; Update the block on the screen.
;
; Place the bottom stone covering.
;
[d725]LDA PathToMascon_FountainCoverPos; A = Block position.
[d727]CLC
[d728]ADC #$10; A += 16 (next row).
[d72a]TAX; X = A
[d72b]LDA a:MASCON_FOUNTAIN_BLOCK_2_STONE; A = Stone block.
[d72e]STA ScreenBuffer,X; Place it in the screen buffer.
[d731]JSR Area_SetBlocks; Update the block on the screen.
;
; Prepare to animate the dropping of the ladder.
;
[d734]LDA #$07
[d736]STA PathToMascon_LadderBlocksRemaining; Set the ladder block count to 7.
[d738]LDA #$22
[d73a]STA 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]LDA a:Quests; Load the completed quests.
[d73f]AND #$20; Is Path to Mascon completed?
[d741]BNE @_clearCoverings; If it is, jump.
;
; Path to Mascon is not complete. Wait for 30 interrupts.
;
[d743]LDX #$1e; X = 30 (loop counter)
[d745]@_waitforInterruptLoop:; [$d745]
[d745]JSR WaitForInterrupt; Wait for an interrupt.
[d748]DEX; X--;
[d749]BNE @_waitforInterruptLoop; If not 0, loop.
;
; Clear the top stone block on the fountain.
;
[d74b]@_clearCoverings:; [$d74b]
[d74b]LDX PathToMascon_FountainCoverPos; X = Fountain cover block position.
[d74d]LDA a:MASCON_FOUNTAIN_BLOCK_1_AIR; A = Air block.
[d750]STA ScreenBuffer,X; Set in the screen buffer at the cover position.
[d753]JSR Area_SetBlocks; Update the block on the screen.
;
; Clear the bottom stone block on the fountain.
;
[d756]LDA PathToMascon_FountainCoverPos; A = Fountain cover block position.
[d758]CLC
[d759]ADC #$10; A += 16 (next row).
[d75b]TAX; X = A
[d75c]LDA a:MASCON_FOUNTAIN_BLOCK_2_AIR; A = Air block.
[d75f]STA ScreenBuffer,X; Set in the screen buffer at the cover position.
[d762]JSR Area_SetBlocks; Update the block on the screen.
;
; Drop the ladder to the path to Mascon.
;
[d765]JMP Game_DropLadderToMascon; Drop the ladder.
;============================================================================
; Two cleared fountain stone block coverings.
;
; XREFS:
;
Game_OpenPathToMascon
;============================================================================
[d768]MASCON_FOUNTAIN_BLOCK_1_AIR:; [$d768]
[d768].byte $42; Air
[d769]MASCON_FOUNTAIN_BLOCK_2_AIR:; [$d769]
[d769].byte $42; Air
;============================================================================
; Two set fountain stone block coverings.
;
; XREFS:
;
Game_OpenPathToMascon
;============================================================================
[d76a]MASCON_FOUNTAIN_BLOCK_1_STONE:; [$d76a]
[d76a].byte $88; Stone cover
[d76b]MASCON_FOUNTAIN_BLOCK_2_STONE:; [$d76b]
[d76b].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]
[d76c].byte $01; [0]: X + 1
[d76d].byte $ff; [1]: Y + 1
[d76e]Game_DropLadderToMascon:; [$d76e]
;
; 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]LDA a:Area_Region; Load the current region.
[d771]CMP #$01; Are we in Trunk?
[d773]BNE @_dropLadderLoop; If not, jump.
[d775]LDA Area_CurrentScreen; Load the current screen.
[d777]CMP #$28; Is it the screen with the blocked path?
[d779]BNE @_dropLadderLoop; If not, jump.
;
; We're on the right screen. Mark the path as opened.
;
[d77b]LDA a:Quests; Load the quests.
[d77e]ORA #$20; Mark the path to Mascon opened.
[d780]STA a:Quests; Store it.
;
; Wait for 4 interrupts.
;
[d783]@_dropLadderLoop:; [$d783]
[d783]JSR WaitForInterrupt
[d786]JSR WaitForInterrupt
[d789]JSR WaitForInterrupt
[d78c]JSR WaitForInterrupt
;
; Play the Drop Ladder sound effect.
;
[d78f]LDA #$17; 0x17 == Drop ladder sound effect.
[d791]JSR Sound_PlayEffect; Play the sound effect.
;
; Place the ladder block.
;
[d794]LDX PathToMascon_LadderPos; X = Next ladder block position.
[d796]LDA a:DROP_LADDER_TO_MASCON_LADDER_BLOCK; A = Ladder block.
[d799]STA ScreenBuffer,X; Store in the screen buffer at X.
[d79c]JSR Area_SetBlocks; Update blocks on the screen.
;
; Increment the ladder position, decrement the number of blocks
; to place, and loop.
;
[d79f]LDA PathToMascon_LadderPos; A = Ladder position.
[d7a1]CLC
[d7a2]ADC #$10; A += 16 (next row)
[d7a4]STA PathToMascon_LadderPos; Store as the new position.
[d7a6]DEC PathToMascon_LadderBlocksRemaining; Decrement the number of ladder blocks to place.
[d7a8]BNE @_dropLadderLoop; If there are still blocks remaining, loop.
;
; Clear the "Opening Path" flag.
;
[d7aa]LDA #$00
[d7ac]STA PathToMascon_Opening; Set opening to 0.
[d7ae]RTS
[d7af]DROP_LADDER_TO_MASCON_LADDER_BLOCK:; [$d7af]
[d7af].byte $20; Ladder block
;============================================================================
; TODO: Document SpriteBehavior_EnemyUnused18_SomethingSetBlocks
;
; INPUTS:
; X
;
; OUTPUTS:
; A
;
; XREFS:
;
SpriteBehavior_EnemyUnused18__9991
;============================================================================
[d7b0]SpriteBehavior_EnemyUnused18_SomethingSetBlocks:; [$d7b0]
[d7b0]TXA
[d7b1]PHA
[d7b2]TYA
[d7b3]PHA
[d7b4]LDX a:Something_UnusedSprite_ScreenBufferOffset
[d7b7]LDA a:Something_UnusedSprite_BlockOffset
[d7ba]STA ScreenBuffer,X
[d7bd]JSR Area_SetBlocks
[d7c0]PLA
[d7c1]TAY
[d7c2]PLA
[d7c3]TAX
[d7c4]RTS
[d7c5]Area_SetBlocks:; [$d7c5]
[d7c5]STA a:Arg_BlockAttributesIndex
[d7c8]STX Temp_00
[d7ca]TXA
[d7cb]PHA
[d7cc]LDA a:CurrentROMBank
[d7cf]PHA
[d7d0]LDX #$03
[d7d2]JSR MMC1_UpdateROMBank
[d7d5]LDA Temp_00
[d7d7]PHA
[d7d8]TAX
[d7d9]JSR Area_SetPPUAddrForBlockIndex
[d7dc]JSR Area_SetBlocks_WriteBlockData12
[d7df]LDA a:PPU_TargetAddr
[d7e2]CLC
[d7e3]ADC #$20
[d7e5]STA a:PPU_TargetAddr
[d7e8]LDA a:PPU_TargetAddr_U
[d7eb]ADC #$00
[d7ed]STA a:PPU_TargetAddr_U
[d7f0]JSR Area_SetBlocks_WriteBlockData34
[d7f3]PLA
[d7f4]JSR Area_SetBlocks_SetAttributes
[d7f7]PLA
[d7f8]TAX
[d7f9]JSR MMC1_UpdateROMBank
[d7fc]PLA
[d7fd]TAX
[d7fe]RTS
[d7ff]Area_SetBlocks_WriteBlockData12:; [$d7ff]
;
; Set the tile length to 2.
;
[d7ff]LDA #$02
[d801]JSR PPUBuffer_QueueCommandOrLength; Write length of 2.
;
; Load the first block and write to the PPU.
;
[d804]LDY a:Arg_BlockAttributesIndex; Load the block position within the level.
[d807]LDA (CurrentArea_BlockData1StartAddr),Y; Load the block at that position.
[d809]STA PPUBuffer,X; Store it the PPU buffer.
;
; Load the second block and write to the PPU.
;
[d80c]INX; X++
[d80d]LDA (CurrentArea_BlockData2StartAddr),Y; Load the block position within the level.
[d80f]STA PPUBuffer,X; Load the block at that position.Store it the PPU buffer.
[d812]INX; X++
;
; Set the new upper bounds of the buffer.
;
[d813]STX PPUBuffer_WriteOffset; Set X as the new upper bounds of the PPU buffer.
[d815]RTS
[d816]Area_SetBlocks_WriteBlockData34:; [$d816]
;
; Set the tile length to 2.
;
[d816]LDA #$02
[d818]JSR PPUBuffer_QueueCommandOrLength
;
; Load the first block and write to the PPU.
;
[d81b]LDY a:Arg_BlockAttributesIndex
[d81e]LDA (CurrentArea_BlockData3StartAddr),Y
[d820]STA PPUBuffer,X
;
; Load the second block and write to the PPU.
;
[d823]INX
[d824]LDA (CurrentArea_BlockData4StartAddr),Y
[d826]STA PPUBuffer,X
[d829]INX
;
; Set the new upper bounds of the buffer.
;
[d82a]STX PPUBuffer_WriteOffset
[d82c]RTS
;============================================================================
; TODO: Document Area_SetBlocks_SetAttributes
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_SetBlocks
;============================================================================
[d82d]Area_SetBlocks_SetAttributes:; [$d82d]
;
; 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]CLC
[d82e]ADC #$20; A = index + 32
[d830]STA 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]LSR A; A = A / 4
[d833]LSR A
[d834]AND #$38; Round down to a multiple of 8.
[d836]STA 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]LDA Temp_00; A = normalized offset.
[d83b]AND #$0f; A = lower nibble of A.
[d83d]LSR A; A = A / 2
[d83e]ORA a:PPU_TargetAddr; OR with the floored offset.
[d841]STA 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]LDA Temp_00; A = normalized offset.
[d846]AND #$10; Keep bit 4 (result = 0 or 16)
[d848]LSR A; Convert to value 0 or 2.
[d849]LSR A
[d84a]LSR A
[d84b]STA Temp_06; Store the result.
[d84d]LDA Temp_00; A = floored offset.
[d84f]AND #$01; Keep bit 0 (result = 0 or 1).
[d851]ORA Temp_06; OR with the value we just calculated (result = 0..3)
[d853]STA Temp_06; Store it.
;
; Load the block attributes for this block.
;
[d855]LDY a:Arg_BlockAttributesIndex; Y = block attributes index for the new block.
[d858]LDA (CurrentArea_BlockAttributesAddr),Y; A = block attributes for that index.
[d85a]LDX Temp_06; X = corner (0..3 value from above).
[d85c]AND SET_BLOCKS_TILE_CORNER_MASK,X; A = block attributes for the given corner.
[d85f]PHA; Push it to the stack.
;
; Get the screen index we're drawing to.
;
[d860]LDA PPU_ScrollScreen; A = Scroll screen to update.
[d862]AND #$01; Keep the right-most bit (0 or 1)
[d864]TAX; 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]LDA SET_BLOCKS_TILEMAP_OFFSETS_L,X; A = lower byte of PPU address
[d868]STA Temp_Addr_L; Store it.
[d86a]LDA SET_BLOCKS_TILEMAP_OFFSETS_U,X; A = upper byte of PPU address
[d86d]STA Temp_Addr_U; Store it.
;
; Compute the upper starting byte for the PPU address.
;
[d86f]LDA SET_BLOCKS_SCREEN_TILEMAP_ADDRS_U,X; A = tilemap address for the screen.
[d872]STA 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]LDY a:PPU_TargetAddr; Y = lower byte of tilemap address computed above.
[d878]LDA (Temp_Addr_L),Y; Load the data for this block.
[d87a]LDX Temp_06; X = corner value (0..3)
[d87c]AND SET_BLOCKS_TILE_CORNER_MASK_INVERT,X; Invert the corner mask (keep all non-updated corner data).
[d87f]STA Temp_06; Store as the new value.
[d881]PLA; Pull the updated block attributes for the corner.
[d882]ORA Temp_06; OR it with the non-updating corner values.
[d884]STA (Temp_Addr_L),Y; And store it.
[d886]PHA; 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]LDA a:PPU_TargetAddr_U; Load the upper byte of the PPU address.
[d88a]ORA #$03; Ensure it starts at 0x03 (address >= $03XX).
Realistically, $23C0 (we guarantee 0x20 or 0x24 above for the screen).
[d88c]STA a:PPU_TargetAddr_U; Store as the new upper byte.
[d88f]LDA a:PPU_TargetAddr; Load the lower byte.
[d892]ORA #$c0; Ensure it starts at 0xC0 (address >= $XXC0)
[d894]STA a:PPU_TargetAddr; Store it.
;
; Write the attribute data to the PPU buffer.
;
[d897]LDA #$01
[d899]JSR PPUBuffer_QueueCommandOrLength; Queue 1 byte.
[d89c]PLA; Pull the attribute data to write from stack.
[d89d]STA PPUBuffer,X; Store it in the buffer at X.
[d8a0]INX; X++ (new offset)
[d8a1]STX PPUBuffer_WriteOffset; Store it.
[d8a3]RTS
[d8a4]SET_BLOCKS_TILE_CORNER_MASK:; [$d8a4]
[d8a4].byte $03; [0]:
[d8a5].byte $0c; [1]:
[d8a6].byte $30; [2]:
[d8a7].byte $c0; [3]:
[d8a8]SET_BLOCKS_TILE_CORNER_MASK_INVERT:; [$d8a8]
[d8a8].byte $fc; [0]:
[d8a9].byte $f3; [1]:
[d8aa].byte $cf; [2]:
[d8ab].byte $3f; [3]:
;============================================================================
; TODO: Document Area_SetPPUAddrForBlockIndex
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_SetBlocks
;============================================================================
[d8ac]Area_SetPPUAddrForBlockIndex:; [$d8ac]
;
; Build an address based on the X, Y position.
;
; X = X * 2
; Y = Y * 4
;
[d8ac]LDA #$00
[d8ae]STA a:PPU_TargetAddr_U
[d8b1]TXA
[d8b2]AND #$0f
[d8b4]ASL A
[d8b5]STA a:PPU_TargetAddr
[d8b8]TXA
[d8b9]AND #$f0
[d8bb]ASL A
[d8bc]ROL a:PPU_TargetAddr_U
[d8bf]ASL A
[d8c0]ROL a:PPU_TargetAddr_U
[d8c3]CLC
[d8c4]ADC a:PPU_TargetAddr
[d8c7]STA a:PPU_TargetAddr
[d8ca]LDA PPU_ScrollScreen
[d8cc]AND #$01
[d8ce]TAY
[d8cf]LDA a:PPU_TargetAddr_U
[d8d2]ORA BYTE_ARRAY_PRG15_MIRROR__d8ea,Y
[d8d5]STA a:PPU_TargetAddr_U
[d8d8]LDA a:PPU_TargetAddr
[d8db]CLC
[d8dc]ADC #$80
[d8de]STA a:PPU_TargetAddr
[d8e1]LDA a:PPU_TargetAddr_U
[d8e4]ADC #$00
[d8e6]STA a:PPU_TargetAddr_U
[d8e9]RTS
[d8ea]BYTE_ARRAY_PRG15_MIRROR__d8ea:; [$d8ea]
[d8ea].byte $20; [0]:
[d8eb].byte $24; [1]:
[d8ec]Player_HandleDeath:; [$d8ec]
[d8ec]LDX #$ff; X = 0xFF
[d8ee]TXS; Set as stack pointer
;
; Switch to bank 14 (Logic)
;
[d8ef]LDX #$0e
[d8f1]JSR MMC1_UpdateROMBank; Switch to bank 14.
;
; Clear all player bits but the facing direction.
;
[d8f4]LDA Player_Flags; Load the player's flags.
[d8f6]AND #$40; Remove all but the facing direction bit.
[d8f8]STA Player_Flags; Store it back.
;
; Clear out more player state.
;
[d8fa]LDA #$00
[d8fc]STA Player_StatusFlag; Set player status to 0.
[d8fe]STA Player_MovementTick; Set the movement tick to 0.
[d900]STA Player_InvincibilityPhase; Set invincibility phase to 0.
;
; Push the inventory state to the stack.
;
[d902]LDA a:SelectedWeapon; Load the selected weapon.
[d905]PHA; Push to the stack.
[d906]LDA a:SelectedArmor; Load the selected armor.
[d909]PHA; Push to the stack.
[d90a]LDA a:SelectedShield; Load the selected shield.
[d90d]PHA; Push to the stack.
[d90e]LDA a:SelectedMagic; Load the selected magic.
[d911]PHA; Push to the stack.
[d912]LDA a:SelectedItem; Load the selected item.
[d915]PHA; Push to the stack.
;
; Wait for interrupt and call other functions to
; reset state.
;
[d916]JSR WaitForInterrupt; Wait for interrupt.
[d919]JSR Screen_ResetSpritesForGamePlay; Reset animations.
[d91c]JSR PlayerDeath_ResetSelectedItemState; Reset selected item state.
[d91f]JSR Player_DrawSprite; Update player sprite.
;
; Prepare state for screen transitions.
;
[d922]LDA #$00
[d924]STA a:Screen_TransitionCounter; Clear the screen transition counter.
;
; Reset the player death state.
;
[d927]STA a:PlayerIsDead; Clear the dead flag.
[d92a]LDA #$ff
[d92c]STA a:Player_DeathAnimationPhase; Clear the death animation phase.
[d92f]STA a:Player_DeathAnimationCounter; Clear the death animation counter.
[d932]STA a:Screen_FadeOutStage; Clear the palette index.
;
; Clear the sprites and music.
;
[d935]JSR Screen_ClearSprites; Clear sprites from the screen.
[d938]LDA #$00
[d93a]STA Music_Current; Clear the music.
;
; Play the death sound effect.
;
[d93c]LDA #$16; 0x16 == Player death sound.
[d93e]JSR Sound_PlayEffect; Play the sound effect.
;
; Begin our death animation loop.
;
; This will dissolve the user's sprite.
;
[d941]@_dissolvePlayerLoop:; [$d941]
[d941]JSR WaitForNextFrame; Wait for the next frame.
[d944]JSR Screen_ResetSpritesForGamePlay; Reset animations.
[d947]JSR Screen_NextTransitionState; Prepare for the next screen state.
[d94a]JSR Player_DrawBody; Draw the player.
[d94d]JSR Player_DrawDeathAnimation; Draw the next cycle of the death animation.
;
; Check whether to reset the animation state.
;
[d950]LDA a:Player_DeathAnimationPhase; Load the animation phase.
[d953]CMP #$ff; Is it 0xFF (complete)?
[d955]BNE @_prepareNextLoop; If not, prepare for the next loop.
[d957]LDA a:Screen_TransitionCounter; Load the screen transition counter.
[d95a]CMP #$10; Is it >= 16?
[d95c]BCC @_prepareNextLoop; If so, prepare for next loop.
;
; Clear animation state.
;
[d95e]LDA #$00
[d960]STA a:Player_DeathAnimationPhase; Clear the animation phase.
[d963]STA a:Player_DeathAnimationCounter; Clear the animation counter.
[d966]@_prepareNextLoop:; [$d966]
[d966]INC a:Screen_TransitionCounter; Increment the screen transition counter.
[d969]BNE @_continueDissolvePlayerLoop; If transition counter != 0, loop.
;
; We're past the player death animation.
;
; Load the palette and update the screen.
;
[d96b]LDA a:Screen_PaletteIndex
[d96e]JSR Screen_LoadUIPalette; Load the palette.
[d971]JSR PPUBuffer_WritePalette; Append 0 to the PPU buffer.
;
; Restore the player's inventory.
;
[d974]PLA; Pop the selected item from the stack.
[d975]STA a:SelectedItem; Set it.
[d978]PLA; Pop the magic from the stack.
[d979]STA a:SelectedMagic; Set it.
[d97c]PLA; Pop the shield from the stack.
[d97d]STA a:SelectedShield; Set it.
[d980]PLA; Pop the armor from the stack.
[d981]STA a:SelectedArmor; Set it.
[d984]PLA; Pop the weapon from the stack.
[d985]STA a:SelectedWeapon; Set it.
;
; Set the death music.
;
[d988]LDA #$08; 0x08 == Death music.
[d98a]STA Music_Current; Set as the current music.
;
; Clear the sprite loaded state.
;
[d98c]LDA #$ff
[d98e]STA Screen_ReadyState; Set loaded state to 0xFF.
[d990]JSR MMC1_LoadBankAndJump; Run the IScript:
[d993].byte BANK_12_LOGIC; Bank = 12
[d994].word IScripts_Begin-1; Address = IScripts_Begin
[d996]@_afterIScriptFarJump:; [$d996]
[d996]LDA #$00; 0 = No music
[d998]STA Music_Current; Set it.
[d99a]JSR MMC1_LoadBankAndJump; Run:
[d99d].byte BANK_12_LOGIC; Bank = 12
[d99e].word Player_SetInitialExpAndGold-1; Address = Player_SetInitialExpAndGold
[d9a0]@_afterSetExpGoldFarJump:; [$d9a0]
[d9a0]JSR Screen_FadeToBlack; Fade the screen to black.
[d9a3]JSR Game_InitStateForSpawn; Reset state for spawn.
[d9a6]JMP Player_Spawn; Spawn the player.
[d9a9]@_continueDissolvePlayerLoop:; [$d9a9]
[d9a9]JMP @_dissolvePlayerLoop
[d9ac]EndGame_Begin:; [$d9ac]
;
; Clear all sprites from the screen.
;
[d9ac]JSR Screen_ClearSprites; Clear sprites.
;
; Wait 120 frames before beginning the fade-out.
;
[d9af]LDX #$78; X = 120 (loop counter)
[d9b1]@_beforeFadeLoop:; [$d9b1]
[d9b1]TXA; A = X
[d9b2]PHA; Push to the stack.
[d9b3]JSR WaitForNextFrame; Wait for the next frame.
[d9b6]JSR Screen_ResetSpritesForGamePlay; Reset for this frame.
[d9b9]JSR Game_DrawScreenInFrozenState; Draw the screen and player in a semi-paused state.
[d9bc]PLA; Pop our loop counter.
[d9bd]TAX; X = A
[d9be]DEX; X--
[d9bf]BNE @_beforeFadeLoop; If > 0, loop.
;
; Fade the screen to black.
;
[d9c1]JSR Screen_FadeToBlack; Fade to black.
;
; Wait another 120 frames before switching screens.
;
[d9c4]LDX #$78; X = 120 (loop counter)
[d9c6]@_beforeTransitionLoop:; [$d9c6]
[d9c6]TXA; A = X (loop counter)
[d9c7]PHA; Push to the stack.
[d9c8]JSR WaitForNextFrame; Wait for the next frame.
[d9cb]JSR Screen_ResetSpritesForGamePlay; Reset for this frame.
[d9ce]PLA; Pop our loop counter.
[d9cf]TAX; X = A (loop counter)
[d9d0]DEX; X--
[d9d1]BNE @_beforeTransitionLoop; If > 0, loop.
;
; Move the player to the King's room and begin the
; end-game dialogue sequence.
;
[d9d3]JMP EndGame_BeginKingsRoomSequence; Begin the next transition into the King's room.
[d9d6]Player_DrawDeathAnimation:; [$d9d6]
[d9d6]LDA a:Player_DeathAnimationPhase; Load the death animation phase.
[d9d9]CMP #$08; Is it >= 8?
[d9db]BCS @_return; If so, return.
[d9dd]LDA a:Player_DeathAnimationCounter; Load the animation death counter.
[d9e0]BPL @_dissolveSprite; If >= 0, jump to dissolve the sprite.
[d9e2]LDA InterruptCounter; Else, load the interrupt counter.
[d9e4]AND #$03; Every 3 out of 4 ticks...
[d9e6]BNE @_return; Return.
[d9e8]LDA #$00
[d9ea]STA a:Player_DeathAnimationCounter; Clear the death counter.
[d9ed]@_dissolveSprite:; [$d9ed]
[d9ed]LDX PPUBuffer_WriteOffset; X = PPU buffer upper bounds.
[d9ef]LDA #$fa; A = 0xFA (Remove Vertical Lines draw command).
[d9f1]STA PPUBuffer,X; Set that as the command to execute.
;
; Set the current player phase for the draw.
;
[d9f4]INX; X++
[d9f5]LDA a:Player_DeathAnimationPhase; Load the animation phase.
[d9f8]STA PPUBuffer,X; Set that as the phase parameter.
;
; Set the upper byte of the address to 0.
;
[d9fb]INX; X++
[d9fc]LDA #$00
[d9fe]STA PPUBuffer,X; Set 0 as the upper address.
;
; Set the lower byte of the address based on the counter.
;
[da01]INX; X++
[da02]LDY a:Player_DeathAnimationCounter; Load the animation counter.
[da05]LDA DEATH_ANIMATION_DRAW_ADDR_LOWER,Y; Load the lower address for Y.
[da08]STA PPUBuffer,X; Set it as the lower address.
;
; Store the new upper bounds of the PPU buffer.
;
[da0b]INX; X++
[da0c]STX PPUBuffer_WriteOffset; Set as the new upper bounds for the PPU buffer.
;
; Update the animation counter.
;
[da0e]INC a:Player_DeathAnimationCounter; Increment the animation counter.
;
; If >= 8, reset the counter and increment the phase.
;
[da11]LDA a:Player_DeathAnimationCounter; Load the new animation counter.
[da14]CMP #$08; Is it < 8?
[da16]BCC @_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]LDA #$ff
[da1a]STA a:Player_DeathAnimationCounter; Set the animation counter to 0xFF.
[da1d]INC a:Player_DeathAnimationPhase; Increment the animation phase.
[da20]@_return:; [$da20]
[da20]RTS
;============================================================================
; Lower bytes for the player's tiles to update during death animation.
;
; XREFS:
;
Player_DrawDeathAnimation
;============================================================================
[da21]DEATH_ANIMATION_DRAW_ADDR_LOWER:; [$da21]
[da21].byte $00; [0]:
[da22].byte $10; [1]:
[da23].byte $20; [2]:
[da24].byte $30; [3]:
[da25].byte $40; [4]:
[da26].byte $50; [5]:
[da27].byte $60; [6]:
[da28].byte $70; [7]:
;============================================================================
; DEADCODE: Clear the fade-out stage for the screen transitions.
;============================================================================
[da29]UNUSED_Screen_ClearFadeOutStage:; [$da29]
[da29]LDA #$ff
[da2b]STA a:Screen_FadeOutStage
[da2e]RTS
[da2f]Screen_FadeToBlack:; [$da2f]
[da2f]LDA #$00; X = 0 (loop counter)
[da31]STA a:Screen_FadeOutStage; Set as the index into the screen palette lookup.
[da34]@_loop:; [$da34]
[da34]JSR WaitForInterrupt; Wait for the next interrupt.
[da37]JSR Screen_NextTransitionState; Update the screen for this palete.
[da3a]LDA a:Screen_FadeOutStage; Load the palette index.
[da3d]CMP #$04; Is it < 4?
[da3f]BCC @_loop; If so, loop.
[da41]RTS
;============================================================================
; 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]
[da42]LDA a:Screen_FadeOutStage; Load the current palette index.
[da45]CMP #$04; Is it >= 4?
[da47]BCS @_doneFadeOut; If so, jump to finish up.
[da49]LDA a:Screen_FadeOutCounter; A = fadeout counter.
[da4c]CLC
[da4d]ADC #$32; A += 0x32 (50)
[da4f]STA a:Screen_FadeOutCounter; Store as the new value.
[da52]BCC @_doneFadeOut; If 0, don't alter the palette.
;
; Fade out to the next palette towards black.
;
[da54]LDA a:Screen_PaletteIndex; Load the screen palette index.
[da57]JSR Screen_SetFadeOutPalette; Load the palette into memory.
[da5a]INC a:Screen_FadeOutStage; Increment the palette index to the next transition state.
[da5d]@_doneFadeOut:; [$da5d]
[da5d]LDA a:Screen_TransitionCounter; Load the screen transition counter.
[da60]CMP #$50; Is it < 80?
[da62]BNE @_return; If so, return.
;
; We're doing player death, and have finished dissolving
; the player. We can now begin fading to black.
;
[da64]LDA #$00
[da66]STA a:Screen_FadeOutStage; Set the palette to 0.
[da69]@_return:; [$da69]
[da69]RTS
[da6a]Game_InitStateForStartScreen:; [$da6a]
[da6a]LDX #$ff; X = 0xFF
[da6c]TXS; Copy to stack pointer.
[da6d]LDA #$00; A = 0
[da6f]STA Area_CurrentArea; Set as the current area (Eolis)
[da71]STA a:Area_Region; Set as the current region (Eolis)
[da74]JSR Player_InitInventoryState; Initialize the player inventory.
[da77]JSR Game_InitStateForSpawn; Initiate the spawn state.
[da7a]JMP Game_ShowStartScreen; Show the start screen.
;============================================================================
; TODO: Document Game_InitStateForSpawn
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_InitStateForStartScreen
;============================================================================
[da7d]Game_InitStateForSpawn:; [$da7d]
[da7d]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[da80]LDA #$00
[da82]STA a:PlayerIsDead; Set the player as alive.
[da85]STA PPUBuffer_WriteOffset; Reset PPU buffer upper bounds to 0.
[da87]STA PPUBuffer_ReadOffset; Reset PPU buffer offset to 0.
[da89]STA Screen_ReadyState; Mark the screen as loaded and ready.
[da8b]JSR Player_SetInitialState; Initialize the player's initial state.
[da8e]JSR Player_ClearVisibleMagic; Clear any visible magic on screen.
[da91]JSR Sprites_LoadCommon; Load sprites from bank 8.
[da94]LDA #$01
[da96]STA a:Maybe_Game_Ready; Set that the game is ready to play.
[da99]LDA #$00
[da9b]STA PPU_ScrollX; Clear the scrolling screen pixel state.
[da9d]STA PPU_ScrollScreen; Clear the scrolling screen state.
[da9f]RTS
[daa0]Game_SetupEnterScreen:; [$daa0]
[daa0]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[daa3]JSR Screen_Load; Load the screen state.
[daa6]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[daa9]JSR WaitForNextFrame; Wait for the next frame.
[daac]JMP GameLoop_LoadSpriteImages; Load the sprite images.
[daaf]Game_SetupEnterBuilding:; [$daaf]
[daaf]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dab2]JSR Game_EnterBuilding; Load state for the building.
[dab5]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dab8]JSR WaitForNextFrame; Wait for the next frame.
[dabb]JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dabe]Game_SetupExitBuilding:; [$dabe]
[dabe]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dac1]JSR Game_ExitBuilding; Load state for the area outside the building.
[dac4]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dac7]JSR WaitForNextFrame; Wait for the next frame.
[daca]JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dacd]Game_SetupNewArea:; [$dacd]
[dacd]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dad0]JSR Game_EnterAreaHandler; Load state for the new area.
[dad3]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[dad6]JSR WaitForNextFrame; Wait for the next frame.
[dad9]JMP GameLoop_LoadSpriteImages; Load the sprite images.
[dadc]Game_SetupAndLoadOutsideArea:; [$dadc]
[dadc]JSR PPU_WaitUntilFlushed; Wait until the PPU is flushed.
[dadf]LDX a:Area_Region; Load the region.
[dae2]LDA DOOR_OUTSIDE_REGION_INDEXES,X; Load the area of the game to place the player for this region.
[dae5]STA Area_CurrentArea; Store as the new area.
[dae7]JSR Game_LoadCurrentArea; Load data for the area.
[daea]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[daed]JSR WaitForNextFrame; Wait for the next frame.
[daf0]JSR GameLoop_LoadSpriteImages; Load the sprite images.
[daf3]JMP Game_MainLoop; Run the game mainloop.
;============================================================================
; DEADCODE
;
; Would assign the area based on transitioning from another.
;============================================================================
[daf6]FUN_PRG15_MIRROR__daf6:; [$daf6]
[daf6]LDX a:Area_Region
[daf9]LDA DOOR_OUTSIDE_REGION_INDEXES,X
[dafc]STA Area_CurrentArea
[dafe]DOOR_OUTSIDE_REGION_INDEXES:; [$dafe]
[dafe].byte AREA_EOLIS; [0]: Eolis
[daff].byte AREA_APOLUNE; [1]: Trunk
[db00].byte AREA_FOREPAW; [2]: Mist
[db01].byte AREA_CONFLATE; [3]: Branch
[db02].byte AREA_DAYBREAK; [4]: Dartmoor
[db03].byte AREA_EVIL_FORTRESS; [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]
[db04]JSR EndGame_MoveToKingsRoom; Place the player in the King's room.
[db07]JMP EndGame_MainLoop; Move the player toward the King.
[db0a]Player_Spawn:; [$db0a]
[db0a]LDA #$50
[db0c]STA a:Player_HP_U; Set the initial player HP to 80.
[db0f]STA a:Player_MP; Set the initial player MP to 80.
[db12]LDA a:Quests; Load the completed quests.
[db15]AND #$ef; Disable the Mattock barrier to Mascon quest.
[db17]STA a:Quests; Store it.
[db1a]JSR Game_ClearTimedItems; Cleared all timed items.
[db1d]JSR Game_SpawnInTemple; Spawn in the temple.
[db20]JSR Game_SetupEnterBuilding; Change the state to be inside a building.
[db23]JMP Game_MainLoop; Start the mainloop.
[db26]Game_Start:; [$db26]
[db26]JSR Game_ClearTimedItems; Clear all timed items.
[db29]JSR PPU_WaitUntilFlushed; Wait until everything is drawn to the screen.
[db2c]JSR Game_LoadFirstLevel; Load the first level.
;
; Clear the player's MP.
;
[db2f]LDA #$00
[db31]STA a:Player_MP; Set MP to 0.
;
; Clear the player's experience.
;
[db34]LDA #$00
[db36]STA a:Experience; Set lower byte of experience to 0.
[db39]STA a:Experience_U; And upper byte.
;
; Prepare state for the first screen of Eolis, including the
; intro trigger sprite entity.
;
[db3c]JSR Screen_ResetForGamePlay; Reset the screen and enable interrupts.
[db3f]JSR WaitForNextFrame; Wait for the next frame.
[db42]JSR 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]
[db45]LDX #$ff; X = 0xFF
[db47]TXS; Transfer to the stack.
;
; Switch to bank 14 to run sprite logic.
;
[db48]LDX #$0e; Bank 14 = Logic
[db4a]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Prepare to draw sprites.
;
[db4d]JSR WaitForNextFrame; Wait for the next frame.
[db50]JSR Screen_ResetSpritesForGamePlay; Reset the sprite state for the screen.
[db53]JSR GameLoop_UpdatePlayer
[db56]JSR Player_DrawShield; Draw the player's shield sprite.
[db59]JSR Player_DrawBody; Draw the player's body sprite.
[db5c]JSR Player_DrawWeapon; Draw the player's weapon sprite.
[db5f]JSR Player_CastMagic; Check and handle casting magic.
[db62]JSR GameLoop_CheckUseCurrentItem; Active selected item?
[db65]JSR Sprites_UpdateAll; Update all sprites.
[db68]JSR GameLoop_CountdownItems
[db6b]JSR Fog_OnTick
[db6e]JSR GameLoop_CheckShowPlayerMenu
[db71]JSR GameLoop_RunScreenEventHandlers
[db74]JSR GameLoop_CheckPauseGame
[db77]LDA a:PlayerIsDead
[db7a]BEQ @_playerIsNotDead
[db7c]JMP Player_HandleDeath
[db7f]@_playerIsNotDead:; [$db7f]
[db7f]LDA Screen_ScrollDirection
[db81]BMI Game_MainLoop
[db83]JSR PPUBuffer_WaitUntilClear
[db86]LDA a:Maybe_Game_Ready
[db89]BEQ @LAB_PRG15_MIRROR__db91
[db8b]LDA Screen_ScrollDirection
[db8d]CMP #$02
[db8f]BCC @LAB_PRG15_MIRROR__dbaf
[db91]@LAB_PRG15_MIRROR__db91:; [$db91]
[db91]JSR Screen_ResetSpritesForGamePlay
[db94]JSR WaitForNextFrame
[db97]JSR Screen_ClearSprites
[db9a]JSR Screen_LoadSpriteInfo
[db9d]JSR WaitForNextFrame
[dba0]JSR GameLoop_LoadSpriteImages
[dba3]JSR PPU_WaitUntilFlushed
[dba6]JSR Game_UpdateForScroll
[dba9]JSR Screen_ResetForGamePlay
[dbac]JMP Game_MainLoop
[dbaf]@LAB_PRG15_MIRROR__dbaf:; [$dbaf]
[dbaf]JSR WaitForNextFrame
[dbb2]JSR Screen_ResetSpritesForGamePlay
[dbb5]JSR Player_DrawShield
[dbb8]JSR Player_DrawBody
[dbbb]JSR Player_DrawWeapon
[dbbe]JSR Screen_ClearSprites
[dbc1]JSR Screen_LoadSpriteInfo
[dbc4]JSR WaitForNextFrame
[dbc7]JSR GameLoop_LoadSpriteImages
[dbca]@_scrolling:; [$dbca]
[dbca]JSR WaitForNextFrame
[dbcd]JSR Screen_ResetSpritesForGamePlay
[dbd0]JSR Game_UpdatePlayerOnScroll
[dbd3]JSR Player_DrawShield
[dbd6]JSR Player_DrawBody
[dbd9]JSR Player_DrawWeapon
[dbdc]JSR Screen_HandleScroll
[dbdf]JSR Screen_HandleScroll
[dbe2]JSR Screen_HandleScroll
[dbe5]JSR Screen_HandleScroll
[dbe8]LDA Screen_ScrollDirection
[dbea]BPL @_scrolling
[dbec]JMP Game_MainLoop
[dbef]EndGame_MainLoop:; [$dbef]
[dbef]LDX #$ff; X = 0xFF (unused -- noop)
;
; Switch to bank 14.
;
[dbf1]LDX #$0e
[dbf3]JSR MMC1_UpdateROMBank; Set bank to 14 (Logic).
;
; Wait for a frame and prepare for renders.
;
[dbf6]JSR WaitForNextFrame; Wait for the next frame.
[dbf9]JSR Screen_ResetSpritesForGamePlay
;
; Move the player a step/jump toward the King, or begin
; interaction, depending on position.
;
[dbfc]JSR EndGame_MovePlayerTowardKing; Simulate the player's button press.
[dbff]JSR GameLoop_UpdatePlayer; Update the player's position/state.
;
; Perform all the standard mainloop operations.
;
[dc02]JSR Player_DrawShield; Draw the shield.
[dc05]JSR Player_DrawBody; Draw the armor.
[dc08]JSR Player_DrawWeapon; Draw the weapon.
[dc0b]JSR Player_CastMagic; No-op
[dc0e]JSR GameLoop_CheckUseCurrentItem; No-op
[dc11]JSR Sprites_UpdateAll; Update sprites.
[dc14]JSR GameLoop_CountdownItems; No-op
[dc17]JSR Fog_OnTick; No-op
[dc1a]JSR GameLoop_CheckShowPlayerMenu; No-op
[dc1d]JSR GameLoop_RunScreenEventHandlers; No-op
[dc20]JMP 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]
[dc23]LDA Player_PosX_Block; A = player X.
[dc25]CMP #$61; Is it >= 97?
[dc27]BCS @_setButtonsLeft; If so, jump to just press Left.
[dc29]CMP #$50; Is it >= 80?
[dc2b]BCS @_setJumpLeft; If so, jump to press Left+A.
[dc2d]CMP #$44; Is it >= 68?
[dc2f]BCS @_setButtonsLeft; If so, jump to press Left.
;
; Simulate pressing Up to engage with the King.
;
[dc31]LDA #$08; 0x08 == Up button bitmask.
[dc33]STA Joy1_ButtonMask; Set as the current button mask.
[dc35]STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc37]RTS
;
; Simulate pressing Left.
;
[dc38]@_setButtonsLeft:; [$dc38]
[dc38]LDA #$02; 0x02 == Left button bitmask.
[dc3a]STA Joy1_ButtonMask; Set as the current button mask.
[dc3c]STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc3e]RTS
;
; Simulate pressing Left+A to jump left.
;
[dc3f]@_setJumpLeft:; [$dc3f]
[dc3f]LDA #$82; 0x82 == Left + A button bitmask.
[dc41]STA Joy1_ButtonMask; Set as the current button mask.
[dc43]STA Joy1_ChangedButtonMask; Set as the changed button mask.
[dc45]RETURN_DC45:; [$dc45]
[dc45]RTS
[dc46]Game_DrawScreenInFrozenState:; [$dc46]
[dc46]LDA Screen_ReadyState; Load the loaded state.
[dc48]CMP #$ff; Is it 0xFF?
[dc4a]BEQ RETURN_DC45; If so, then return.
;
; Switch to bank 14.
;
[dc4c]LDA a:CurrentROMBank; Load the current bank.
[dc4f]PHA; Push to the stack.
[dc50]LDX #$0e
[dc52]JSR MMC1_UpdateROMBank; Switch to bank 14.
;
; Temporarily clear Wing Boots state.
;
[dc55]LDA Player_StatusFlag; Load the player's status flag.
[dc57]PHA; Push to the stack so it can be restored later.
[dc58]AND #$7f; Keep all but the Wing Boots flag.
[dc5a]STA Player_StatusFlag; Store it.
;
; Draw the player.
;
[dc5c]JSR Player_DrawShield; Draw the shield.
[dc5f]JSR Player_DrawBody; Draw the armor.
[dc62]JSR Player_DrawWeapon; Draw the weapon.
;
; Restore the player's status flags.
;
[dc65]PLA; Pop the status flag.
[dc66]STA Player_StatusFlag; Store it.
;
; Draw all the sprites, but with sprites paused.
;
; This flag will will disable all behavioral updates for
; sprites.
;
[dc68]LDA #$01; A = 1
[dc6a]STA a:Sprites_UpdatesPaused; Set as the paused state for sprites.
[dc6d]JSR Sprites_UpdateAllStates; Update all sprites.
[dc70]LDA #$00; A = 0
[dc72]STA a:Sprites_UpdatesPaused; Clear the paused state.
[dc75]JMP MMC1_UpdatePRGBankToStackA; Restore the previous bank.
[dc78]Game_LoadAreaTable:; [$dc78]
;
; XXX Set the address for the area in the areas table.
;
[dc78]ASL A
[dc79]TAY
[dc7a]LDA ROMBankStart,Y
[dc7d]STA Area_ScreenBlocksOffset
[dc7f]LDA ROMBankStart+1,Y
[dc82]CLC
[dc83]ADC #$80
[dc85]STA Area_ScreenBlocksOffset_U
;
; Save the current ROM bank and switch to bank 3, where the
; areas table lives.
;
[dc87]LDA a:CurrentROMBank
[dc8a]PHA
[dc8b]LDX #$03
[dc8d]JSR MMC1_UpdateROMBank
[dc90]LDA a:ROMBankStart
;
; Set the start of the area's pointer table in
; 0x{@address 007C}.
;
; This will be
AREAS_TABLE_PTR in Bank 3.
;
[dc93]STA Temp_Addr_L
[dc95]LDA a:AREAS_TABLE_PTR+1
[dc98]CLC
[dc99]ADC #$80
[dc9b]STA Temp_Addr_U
[dc9d]LDA Area_CurrentArea
[dc9f]ASL A
[dca0]TAY
[dca1]LDA (Temp_Addr_L),Y
[dca3]STA CurrentArea_TableAddr
[dca5]INY
[dca6]LDA (Temp_Addr_L),Y
[dca8]CLC
[dca9]ADC #$80
[dcab]STA CurrentArea_TableAddr_U
[dcad]LDY #$00
[dcaf]LDA (CurrentArea_TableAddr),Y
[dcb1]STA Temp_Addr_L
[dcb3]INY
[dcb4]LDA (CurrentArea_TableAddr),Y
[dcb6]CLC
[dcb7]ADC #$80
[dcb9]STA Temp_Addr_U
;
; Store the addresses of the block attributes and 4 groups of
; block data in addresses {@address 007E}.
;
[dcbb]LDY #$00
[dcbd]LDX #$05
[dcbf]@LAB_PRG15_MIRROR__dcbf:; [$dcbf]
[dcbf]LDA (Temp_Addr_L),Y
[dcc1]STA CurrentArea_BlockAttributesAddr,Y
[dcc4]INY
[dcc5]LDA (Temp_Addr_L),Y
[dcc7]CLC
[dcc8]ADC #$80
[dcca]STA CurrentArea_BlockAttributesAddr,Y
[dccd]INY
[dcce]DEX
[dccf]BNE @LAB_PRG15_MIRROR__dcbf
[dcd1]LDY #$02
;
; Load the block properties, scrolling data, door locations,
; and door destinations addresses.
;
[dcd3]LDA (CurrentArea_TableAddr),Y
[dcd5]STA CurrentArea_BlockPropertiesAddr
[dcd7]INY
[dcd8]LDA (CurrentArea_TableAddr),Y
[dcda]CLC
[dcdb]ADC #$80
[dcdd]STA CurrentArea_BlockPropertiesAddr_U
[dcdf]LDY #$04
[dce1]LDA (CurrentArea_TableAddr),Y
[dce3]STA CurrentArea_ScrollingDataAddr
[dce5]INY
[dce6]LDA (CurrentArea_TableAddr),Y
[dce8]CLC
[dce9]ADC #$80
[dceb]STA ScrollingData_U
[dced]LDY #$06
[dcef]LDA (CurrentArea_TableAddr),Y
[dcf1]STA CurrentArea_DoorLocationsAddr
[dcf3]INY
[dcf4]LDA (CurrentArea_TableAddr),Y
[dcf6]CLC
[dcf7]ADC #$80
[dcf9]STA CurrentArea_DoorLocationsAddr_U
[dcfb]LDY #$08
[dcfd]LDA (CurrentArea_TableAddr),Y
[dcff]STA CurrentArea_DoorDestinationsAddr
[dd01]INY
[dd02]LDA (CurrentArea_TableAddr),Y
[dd04]CLC
[dd05]ADC #$80
[dd07]STA CurrentArea_DoorDestinationsAddr_U
;
; Switch back to the previous bank.
;
[dd09]PLA
[dd0a]TAX
[dd0b]JSR MMC1_UpdateROMBank
[dd0e]RTS
;============================================================================
; Update the scroll state during a mainloop iteration.
;
; INPUTS:
; None.
;
; OUTPUTS:
; None.
;
; CALLS:
;
Screen_UpdateForScroll
;
; XREFS:
;
Game_MainLoop
;============================================================================
[dd0f]Game_UpdateForScroll:; [$dd0f]
[dd0f]JSR Screen_UpdateForScroll
[dd12]RTS
[dd13]Screen_SetupNew:; [$dd13]
[dd13]JSR Player_DrawSpriteImmediately; Draw the player sprite.
[dd16]JSR Sprites_LoadCommon
[dd19]LDA Area_LoadingScreenIndex
[dd1b]STA Area_CurrentScreen
[dd1d]JSR Area_ScrollScreenRight
[dd20]JSR UI_DrawHUDSprites
[dd23]LDA Screen_DestPaletteOrIndex
[dd25]STA a:Screen_PaletteIndex
[dd28]JSR Screen_LoadUIPalette
[dd2b]LDA #$00
[dd2d]STA PPU_ScrollScreenHoriz
[dd2f]STA PPU_ScrollScreenVert
[dd31]STA Screen_Maybe_ScrollXCounter
[dd33]STA Player_Something_ScrollPosY
[dd35]LDA Screen_StartPosYX
[dd37]AND #$f0
[dd39]STA Player_PosY
[dd3b]LDA Screen_StartPosYX
[dd3d]ASL A
[dd3e]ASL A
[dd3f]ASL A
[dd40]ASL A
[dd41]STA Player_PosX_Block
[dd43]JMP UI_SetHUDPPUAttributes
[dd46]Screen_Load:; [$dd46]
[dd46]JSR Screen_SetupNew; Draw the HUD and prepare setup for the screen.
[dd49]LDA #$00
[dd4b]JSR 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:; [$dd4e]
;
; 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]JSR Screen_ClearSprites; Clear current sprite data.
[dd51]LDA Screen_ReadyState; Get the screen state.
[dd53]CMP #$01; Is it 1? (DEADCODE: It never will be).
[dd55]BEQ @_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]JSR Screen_LoadSpriteInfo; Load information for the screen.
;
; Mark the screen as ready, and write the palette.
;
[dd5a]@_setReady:; [$dd5a]
[dd5a]LDA #$00
[dd5c]STA Screen_ReadyState; Set the state to ready.
[dd5e]JMP 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:; [$dd61]
;
; Set the starting area/screen/position information outside the
; temple.
;
[dd61]LDX a:TempleSpawnPoint; X = index of the temple spawn point.
[dd64]LDA START_SCREEN_FOR_TEMPLE_SPAWN,X; Set the current screen for outside the temple.
[dd67]STA Area_CurrentScreen
[dd69]LDA START_MAYBE_NEXT_AREA_FOR_TEMPLE_SPAWN,X
[dd6c]STA a:Area_Region
[dd6f]LDA START_PLAYERPOSX_FULL_FOR_TEMPLE_SPAWN,X
[dd72]STA Player_PosX_Block
[dd74]LDA MAYBE_START_PLAYERPOSY_FOR_TEMPLE_SPAWN,X
[dd77]STA Player_PosY
[dd79]LDA DEST_SCREEN_FOR_TEMPLE_SPAWN,X
[dd7c]STA a:Area_DestScreen
[dd7f]LDY CURRENT_REGION_FOR_TEMPLE_SPAWN,X
[dd82]STY Area_CurrentArea
[dd84]LDA AREA_PALETTE_INDEXES,Y
[dd87]STA a:Screen_PaletteIndex
[dd8a]LDA AREA_TO_MUSIC_TABLE,Y
[dd8d]STA a:Area_Music_Outside
[dd90]LDA #$12
[dd92]STA Screen_DestPaletteOrIndex
[dd94]LDA #$06
[dd96]STA a:Building_TilesIndex
[dd99]LDA #$9e
[dd9b]STA Screen_StartPosYX
[dd9d]LDA #$0e
[dd9f]STA a:Areas_DefaultMusic
[dda2]LDA #$01
[dda4]STA Area_LoadingScreenIndex
;
; Set the player to face left inside the temple.
;
[dda6]LDA Player_Flags
[dda8]AND #$bf
[ddaa]STA Player_Flags
[ddac]RTS
;============================================================================
; A mapping of Temple spawn positions to regions.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddad]CURRENT_REGION_FOR_TEMPLE_SPAWN:; [$ddad]
[ddad].byte REGION_EOLIS; [0]: First town
[ddae].byte REGION_BRANCH; [1]: Apolune
[ddaf].byte REGION_BRANCH; [2]: Forepaw
[ddb0].byte REGION_MIST; [3]: Fog
[ddb1].byte REGION_BRANCH; [4]: Victim
[ddb2].byte REGION_BRANCH; [5]: Conflate
[ddb3].byte REGION_BRANCH; [6]: Daybreak
[ddb4].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]
[ddb5].byte $50; [0]: First town
[ddb6].byte $50; [1]: Apolune
[ddb7].byte $30; [2]: Forepaw
[ddb8].byte $90; [3]: Fog
[ddb9].byte $30; [4]: Victim
[ddba].byte $90; [5]: Conflate
[ddbb].byte $60; [6]: Daybreak
[ddbc].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]
[ddbd].byte $90; [0]: First town
[ddbe].byte $90; [1]: Apolune
[ddbf].byte $90; [2]: Forepaw
[ddc0].byte $80; [3]: Fog
[ddc1].byte $90; [4]: Victim
[ddc2].byte $90; [5]: Conflate
[ddc3].byte $90; [6]: Daybreak
[ddc4].byte $90; [7]: Final town
;============================================================================
; TODO: A mapping of Temple spawn positions to <TODO>.
;
; XREFS:
;
Game_SpawnInTemple
;============================================================================
[ddc5]DEST_SCREEN_FOR_TEMPLE_SPAWN:; [$ddc5]
[ddc5].byte $02; [0]: First town
[ddc6].byte $0b; [1]: Apolune
[ddc7].byte $10; [2]: Forepaw
[ddc8].byte $1e; [3]: Fog
[ddc9].byte $23; [4]: Victim
[ddca].byte $2b; [5]: Conflate
[ddcb].byte $33; [6]: Daybreak
[ddcc].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]
[ddcd].byte REGION_EOLIS; [0]: First town
[ddce].byte REGION_TRUNK; [1]: Apolune
[ddcf].byte REGION_TRUNK; [2]: Forepaw
[ddd0].byte REGION_MIST; [3]: Fog
[ddd1].byte REGION_MIST; [4]: Victim
[ddd2].byte REGION_BRANCH; [5]: Conflate
[ddd3].byte REGION_BRANCH; [6]: Daybreak
[ddd4].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]
[ddd5].byte $02; [0]: First town
[ddd6].byte $01; [1]: Apolune
[ddd7].byte $03; [2]: Forepaw
[ddd8].byte $06; [3]: Fog
[ddd9].byte $06; [4]: Victim
[ddda].byte $08; [5]: Conflate
[dddb].byte $0b; [6]: Daybreak
[dddc].byte $0c; [7]: Final town
[dddd]EndGame_MoveToKingsRoom:; [$dddd]
;
; Move the player back to Eolis.
;
[dddd]LDA #$00
[dddf]STA Area_LoadingScreenIndex; Set the screen index to 0.
[dde1]STA a:Area_Region; Set the region to Eolis.
;
; Play the Temple music.
;
[dde4]LDA #$0d
[dde6]STA a:Areas_DefaultMusic; Play the Temple music.
;
; Move the player to the right of the room.
;
[dde9]LDA #$9d
[ddeb]STA Screen_StartPosYX; Set the start position to X=13, Y=9.
;
; Load the palette for the King's room.
;
[dded]LDA #$11
[ddef]STA Screen_DestPaletteOrIndex; Set the Palette for the King's room.
;
; Set the King's Room screen.
;
[ddf1]LDA #$44
[ddf3]STA Area_CurrentScreen; Set the screen to 68.
[ddf5]STA a:Area_DestScreen
[ddf8]LDA #$06
[ddfa]STA a:Building_TilesIndex; Set the tiles to 6.
;
; Face the player to the left.
;
[ddfd]LDA Player_Flags; Load the player flags.
[ddff]AND #$bf; Set to facing left.
[de01]STA Player_Flags; Store it.
[de03]JMP Game_SetupEnterBuilding; Trigger the Enter Building logic.
;============================================================================
; TODO: Document Game_EnterBuilding
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_SetupEnterBuilding
;============================================================================
[de06]Game_EnterBuilding:; [$de06]
;
; Unset the player's current weapon. Players can't attack in
; buildings.
;
[de06]LDA #$ff
[de08]STA a:Player_CurWeapon; Clear the weapon.
;
; Set the music for this area.
;
[de0b]LDA a:Areas_DefaultMusic; Load the default music for the area.
[de0e]STA Music_Current; Set as the current music.
;
; Push a buch of state to return to when exiting the door.
;
[de10]LDA Area_CurrentArea; Load the current area.
[de12]STA a:Area_SavedArea; Save it for when exiting the building.
[de15]LDA Area_CurrentScreen; Load the current screen in the area.
[de17]STA a:Area_SavedScreen; Save it.
[de1a]LDA a:Screen_PaletteIndex; Load the current palette.
[de1d]STA a:Screen_SavedPalette; Save it.
;
; Save the player's X/Y position (rounded to the nearest
; block).
;
[de20]LDA Player_PosY; Load the player's Y position.
[de22]AND #$f0; Normalize to a block offset.
[de24]STA a:Player_SavedPosXY; Save it.
[de27]LDA Player_PosX_Block; Load the player's block X position.
[de29]AND #$f0; Normalize it to a block offset.
[de2b]LSR A; Shift to the lower byte.
[de2c]LSR A
[de2d]LSR A
[de2e]LSR A
[de2f]ORA a:Player_SavedPosXY; Combine it with the saved X/Y position.
[de32]STA a:Player_SavedPosXY; And save it.
;
; Set the new area to enter and load it.
;
[de35]LDX #$04
[de37]STX Area_CurrentArea; Set the current area to 4 (inside buildings)
[de39]LDA AREA_TO_SCREEN_DATA_BANKS,X; Load the bank for this area.
[de3c]STA CurrentArea_ROMBank; Store as the current area ROM bank.
[de3e]LDX CurrentArea_ROMBank
[de40]JSR MMC1_SaveROMBankAndUpdateTo; Push the current bank and switch to the new one.
[de43]LDX Area_CurrentArea; Load the current area again.
[de45]LDA AREA_TO_BANK_OFFSET,X; Get the index into the screen data table in this bank.
[de48]JSR Game_LoadAreaTable; Load the area table for the screen.
[de4b]JSR MMC1_RestorePrevROMBank; Restore the previous bank.
;
; Set the index into the tiles for this area and load them.
;
[de4e]LDA a:Building_TilesIndex
[de51]STA Area_TilesIndex
[de53]JSR Area_LoadTiles
;
; Load the palette.
;
[de56]LDA #$01
[de58]JSR Screen_LoadSpritePalette
;
; Set up the screen and the sprites.
;
[de5b]JSR Screen_SetupNew
[de5e]LDA a:Area_DestScreen
[de61]STA Area_CurrentScreen
[de63]JMP Screen_SetupSprites
;============================================================================
; TODO: Document Game_ExitBuilding
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_SetupExitBuilding
;============================================================================
[de66]Game_ExitBuilding:; [$de66]
[de66]LDA a:SelectedWeapon
[de69]STA a:Player_CurWeapon
[de6c]LDA a:Areas_DefaultMusic
[de6f]STA Music_Current
[de71]LDX a:Area_SavedArea
[de74]STX Area_CurrentArea
[de76]LDA AREA_TO_SCREEN_DATA_BANKS,X
[de79]STA CurrentArea_ROMBank
[de7b]LDX CurrentArea_ROMBank
[de7d]JSR MMC1_SaveROMBankAndUpdateTo
[de80]LDX Area_CurrentArea
[de82]LDA AREA_TO_BANK_OFFSET,X
[de85]JSR Game_LoadAreaTable
[de88]JSR MMC1_RestorePrevROMBank
[de8b]LDX Area_CurrentArea
[de8d]LDA CURRENT_AREA_TO_TILES_INDEX_TABLE,X
[de90]STA Area_TilesIndex
[de92]JSR Area_LoadTiles
[de95]LDA a:Area_SavedScreen
[de98]STA Area_LoadingScreenIndex
[de9a]LDA a:Screen_SavedPalette
[de9d]STA Screen_DestPaletteOrIndex
[de9f]LDA a:Player_SavedPosXY
[dea2]STA Screen_StartPosYX
[dea4]JMP Screen_Load
[dea7]Game_LoadFirstLevel:; [$dea7]
[dea7]LDA #$00; A = 0
;
; Set this as the current town.
;
[dea9]STA 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]JSR Player_SetInitialState; Set the initial player state.
[deae]LDA #$10
[deb0]STA a:Player_HP_U; Set the HP to 16.
[deb3]LDX 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]LDA AREA_TO_SCREEN_DATA_BANKS,X; A = PRG bank for the area.
[deb8]STA CurrentArea_ROMBank; Store as the current bank.
[deba]LDX CurrentArea_ROMBank; X = current bank.
[debc]JSR 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]LDA AREA_TO_BANK_OFFSET,X; A = sprite bank for the area.
[dec2]JSR Game_LoadAreaTable; Load the area information.
[dec5]JSR MMC1_RestorePrevROMBank; Restore the previous bank.
;
; Start on the current screen.
;
[dec8]LDX Area_CurrentArea; X = current area.
[deca]LDA #$00
[decc]STA Area_CurrentScreen; Current screen = 0
[dece]STA Area_LoadingScreenIndex; Screen index = 0
;
; Load the tiles and images for the area.
;
[ded0]LDX Area_CurrentArea; X = current area.
[ded2]LDA CURRENT_AREA_TO_TILES_INDEX_TABLE,X; A = tiles index.
[ded5]STA Area_TilesIndex; Store that.
[ded7]JSR Area_LoadTiles; Load the tileset pages for that index.
;
; Load the palette for the area.
;
[deda]LDA #$00
[dedc]JSR Screen_LoadSpritePalette; Load the palette for that index.
[dedf]LDX Area_CurrentArea; X = current area.
[dee1]LDA AREA_PALETTE_INDEXES,X; Load the palette.
[dee4]STA Screen_DestPaletteOrIndex; And store it as the current one for the area.
;
; Load the music for this area.
;
[dee6]LDA AREA_TO_MUSIC_TABLE,X; Load the music for the area.
[dee9]STA Music_Current; And store it as the current music.
[deeb]STA 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]LDA #$91
[def0]STA Screen_StartPosYX; Set start position.
[def2]JMP Screen_Load; Load this screen.
[def5]Game_LoadCurrentArea:; [$def5]
[def5]JSR Player_SetInitialState; Set the initial player state.
;
; Load the bank for this area.
;
[def8]LDX Area_CurrentArea; X = current area.
[defa]LDA AREA_TO_SCREEN_DATA_BANKS,X; Load the ROM bank for the area.
[defd]STA CurrentArea_ROMBank; Store that as the current bank.
[deff]LDX CurrentArea_ROMBank; X = current bank.
[df01]JSR MMC1_SaveROMBankAndUpdateTo; Save the current bank and switch to it.
;
; Load the bank for the sprites for this area.
;
[df04]LDX Area_CurrentArea; X = current area.
[df06]LDA AREA_TO_BANK_OFFSET,X; A = bank for the sprites.
;
; Load information on the area.
;
[df09]JSR Game_LoadAreaTable; Load the table of information on this area.
[df0c]JSR MMC1_RestorePrevROMBank; Restore to the previous bank.
[df0f]LDA Area_LoadingScreenIndex; A = current screen index.
[df11]STA Area_CurrentScreen; Store as the current screen.
;
; Load the tiles for the area.
;
[df13]LDX Area_CurrentArea; X = current area.
[df15]LDA CURRENT_AREA_TO_TILES_INDEX_TABLE,X; A = tiles index for the area.
[df18]STA Area_TilesIndex; Store as the current tiles index.
[df1a]JSR Area_LoadTiles; Load all tileset images for that index.
;
; Load the palette for the area.
;
[df1d]LDA #$00
[df1f]JSR Screen_LoadSpritePalette; Load palette 0.
[df22]LDX Area_CurrentArea; X = current area.
[df24]LDA AREA_PALETTE_INDEXES,X; Load the palette for the area.
[df27]STA Screen_DestPaletteOrIndex; Store as the current palette.
;
; Load the music for the area.
;
[df29]LDA AREA_TO_MUSIC_TABLE,X; Load the music for the area.
[df2c]STA Music_Current; Set as the current music.
[df2e]STA a:Areas_DefaultMusic; Store as the default.
[df31]JMP Screen_Load; Load this screen.
[df34]AREA_TO_SCREEN_DATA_BANKS:; [$df34]
[df34].byte BANK_0_AREA_DATA; [0]: Eolis
[df35].byte BANK_1_AREA_DATA; [1]: Apolune
[df36].byte BANK_0_AREA_DATA; [2]: Forepaw
[df37].byte BANK_0_AREA_DATA; [3]: Mascon
[df38]AREA_TO_SCREEN_DATA_BANKS_4_:; [$df38]
[df38].byte BANK_2_AREA_DATA; [4]: Victim
[df39].byte BANK_1_AREA_DATA; [5]: Conflate
[df3a].byte BANK_2_AREA_DATA; [6]: Daybreak
[df3b].byte BANK_2_AREA_DATA; [7]: Evil Fortress
[df3c]AREA_TO_BANK_OFFSET:; [$df3c]
[df3c].byte BANK_0_AREA_DATA; [0]: Eolis
[df3d].byte BANK_0_AREA_DATA; [1]: Apolune
[df3e].byte BANK_1_AREA_DATA; [2]: Forepaw
[df3f].byte BANK_2_AREA_DATA; [3]: Mascon
[df40]AREA_TO_BANK_OFFSET_4_:; [$df40]
[df40].byte BANK_1_AREA_DATA; [4]: Victim
[df41].byte BANK_1_AREA_DATA; [5]: Conflate
[df42].byte BANK_0_AREA_DATA; [6]: Daybreak
[df43].byte BANK_2_AREA_DATA; [7]: Evil Fortress
[df44]CURRENT_AREA_TO_TILES_INDEX_TABLE:; [$df44]
[df44].byte $00; [0]: Eolis
[df45].byte $02; [1]: First town
[df46].byte $03; [2]:
[df47].byte $05; [3]:
[df48].byte $06; [4]:
[df49].byte $01; [5]:
[df4a].byte $04; [6]:
[df4b].byte $04; [7]: Final boss room
[df4c]AREA_PALETTE_INDEXES:; [$df4c]
[df4c].byte PALETTE_EOLIS; [0]: Eolis
[df4d].byte PALETTE_OUTSIDE; [1]:
[df4e].byte PALETTE_MIST; [2]:
[df4f].byte PALETTE_TOWN; [3]:
[df50].byte PALETTE_TOWN; [4]:
[df51].byte PALETTE_BRANCH; [5]:
[df52].byte PALETTE_DARTMOOR; [6]:
[df53].byte PALETTE_EVIL_FORTRESS; [7]:
[df54].byte $00,$00,$00,$00,$00,$00,$00,$00; [$df54] undefined
[df5c]AREA_TO_MUSIC_TABLE:; [$df5c]
[df5c].byte MUSIC_EOLIS; [0]: Eolis
[df5d].byte MUSIC_APOLUNE; [1]: Apolune
[df5e].byte MUSIC_FOREPAW; [2]: Forepaw
[df5f].byte MUSIC_MASCON_VICTIM; [3]: Mascon
[df60].byte MUSIC_MASCON_VICTIM; [4]: Victim
[df61].byte MUSIC_CONFLATE; [5]: Conflate
[df62].byte MUSIC_DAYBREAK; [6]: Daybreak
[df63].byte MUSIC_EVIL_FORTRESS; [7]: Evil Fortress
;============================================================================
; TODO: Document Game_EnterAreaHandler
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_SetupNewArea
;============================================================================
[df64]Game_EnterAreaHandler:; [$df64]
[df64]LDX Area_CurrentArea
[df66]LDA AREA_TO_SCREEN_DATA_BANKS,X
[df69]STA CurrentArea_ROMBank
[df6b]LDX CurrentArea_ROMBank
[df6d]JSR MMC1_SaveROMBankAndUpdateTo
[df70]LDX Area_CurrentArea
[df72]LDA AREA_TO_BANK_OFFSET,X
[df75]JSR Game_LoadAreaTable
[df78]JSR MMC1_RestorePrevROMBank
[df7b]LDA Area_LoadingScreenIndex
[df7d]STA Area_CurrentScreen
[df7f]LDX Area_CurrentArea
[df81]LDA AREA_TO_MUSIC_TABLE,X
[df84]STA Music_Current
[df86]STA a:Areas_DefaultMusic
[df89]LDA CURRENT_AREA_TO_TILES_INDEX_TABLE,X
[df8c]STA Area_TilesIndex
[df8e]JSR Area_LoadTiles
[df91]LDA #$00
[df93]JSR Screen_LoadSpritePalette
[df96]JMP Screen_Load
;============================================================================
; 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]
[df99]LDA Joy2_ButtonMask; Load the player 2 controller buttons.
[df9b]AND #$0c; Is Up or Down pressed?
[df9d]BEQ RETURN_DFDD; If not, return.
;
; Figure out which region to switch to based on button press.
;
[df9f]AND #$04; Is Down pressed?
[dfa1]BNE @_nextArea; If so, switch to the next area.
;
; Up was pressed. Go forward a region.
;
[dfa3]INC a:Area_Region; Increment the region.
[dfa6]LDA a:Area_Region; A = incremented region.
[dfa9]CMP #$06; Is it in bounds?
[dfab]BCC @_switchArea; If so, switch to the area.
[dfad]LDA #$00; Else...
[dfaf]STA a:Area_Region; Set the region to (Eolis).
[dfb2]BEQ @_switchArea; And switch to it.
;
; Down was pressed. Go back a region.
;
[dfb4]@_nextArea:; [$dfb4]
[dfb4]DEC a:Area_Region; Decrement the region.
[dfb7]BPL @_switchArea; If it's in bounds, switch to the previous area.
[dfb9]LDA #$05; Else, set the region to the final fortress.
[dfbb]STA a:Area_Region; Store as the new region.
;
; Set the current screen to 0 and load it.
;
[dfbe]@_switchArea:; [$dfbe]
[dfbe]LDA #$00
[dfc0]STA Area_CurrentScreen; Set current screen to 0.
[dfc2]JMP 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]
[dfc5]LDA Area_CurrentArea; Load the current area.
[dfc7]CMP #$02; Is it Forepaw?
[dfc9]BNE RETURN_E011; If not, return.
[dfcb]LDA a:Screen_PaletteIndex; Load the current palette.
[dfce]CMP #$0a; Is it Mist?
[dfd0]BNE RETURN_E011; If not, return.
[dfd2]LDA Fog_StateIndex; Load the fog index.
[dfd4]LSR A; Is it an odd value?
[dfd5]BCC 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]DEC Fog_TileIndex; Decrement the fog generator.
[dfd9]BNE RETURN_E011; If != 0, return.
[dfdb]INC Fog_StateIndex; Else, increment the fog index.
[dfdd]RETURN_DFDD:; [$dfdd]
[dfdd]RTS
;============================================================================
; 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]
[dfde]LDX PPUBuffer_WriteOffset; Load the upper bounds of the fog generator.
;
; Queue drawing command "Rotate tiles right."
;
[dfe0]LDA #$fc; 0xFC == Rotate Tiles Right draw command.
[dfe2]STA PPUBuffer,X; Set as the command.
;
; Set the upper byte of the tile address to 0x18.
;
[dfe5]INX; X++
[dfe6]LDA #$18; 0x18 == Upper byte of the sprite tile address.
[dfe8]STA 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]INX; X++
[dfec]LDA Fog_TileIndex; Load the fog generator value.
[dfee]INC Fog_TileIndex; Increment for later (not involved in the math).
[dff0]ASL A; Multiply the value we loaded by 16.
[dff1]ASL A
[dff2]ASL A
[dff3]ASL A
[dff4]STA 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]INX; X++
[dff8]STX 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]INC Fog_TileIndex; Increment the fog generator value.
[dffc]LDA Fog_TileIndex; Load the result.
[dffe]AND #$07; Keep it in range 0..7.
[e000]STA Fog_TileIndex; Store it back.
[e002]BNE 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]INC Fog_StateIndex; Increment the fog index.
[e006]LDA Fog_StateIndex; Load it.
[e008]LSR A; Divide by 2.
[e009]AND #$03; And convert to an index in the lookup table.
[e00b]TAX; X = A
[e00c]LDA FOG_START_TILE_INDEXES,X; Load a starting fog generator value at that index.
[e00f]STA Fog_TileIndex; And set it.
[e011]RETURN_E011:; [$e011]
[e011]RTS
[e012]FOG_START_TILE_INDEXES:; [$e012]
[e012].byte $18; [0]:
[e013].byte $06; [1]:
[e014].byte $30; [2]:
[e015].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:; [$e016]
;
; Check to make sure we're not outside the walls of Eolis.
;
[e016]LDA Area_CurrentArea; A = current area.
[e018]BNE @_allowInventory; If it's > Eolis, the player can open the inventory. Jump.
[e01a]LDA Area_CurrentScreen; A = current screen.
[e01c]BEQ RETURN_E02A; If it's 0, then we're outside the walls. No inventory allowed. Return.
[e01e]@_allowInventory:; [$e01e]
[e01e]LDA Joy1_ChangedButtonMask; A = controller 1 button mask.
[e020]AND #$20; Is Select pressed?
[e022]BEQ RETURN_E02A; If not, return.
[e024]JSR MMC1_LoadBankAndJump; Open the Player Menu.
[e027].byte BANK_12_LOGIC; Bank = 12
[e028].word PlayerMenu_Show-1; Address = PlayerMenu_Show
[e02a]RETURN_E02A:; [$e02a]
[e02a]RTS
;============================================================================
; 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:; [$e02b]
;
; Check if the player pressed Start.
;
[e02b]LDA Joy1_ChangedButtonMask; Load the button state.
[e02d]AND #$10; Check if the Pause button is pressed.
[e02f]BEQ RETURN_E02A; If paused...
;
; The player paused. Set the flag and wait until unpaused.
;
[e031]LDA #$01
[e033]STA a:Game_PausedState; Set the Paused flag.
[e036]@_waitForUnpause:; [$e036]
[e036]JSR WaitForNextFrame; Wait...
[e039]JSR Sprites_FlipRanges
;
; Check if the player unpaused.
;
[e03c]LDA Joy1_ChangedButtonMask; Check if the Pause button was pressed again.
[e03e]AND #$10
[e040]BEQ @_waitForUnpause; Until pause is pressed again, loop.
;
; Unpause the game.
;
[e042]LDA #$00; Clear the Paused flag.
[e044]STA a:Game_PausedState
[e047]RTS
;============================================================================
; 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:; [$e048]
;
; Preserve the previous pressed buttons while scrolling.
;
[e048]LDA Joy1_PrevButtonMask; Load the previous button mask.
[e04a]STA Joy1_ButtonMask; Set it as the current button mask.
[e04c]JSR Game_MovePlayerOnScroll; Update the player position.
[e04f]INC Screen_ScrollPlayerTransitionCounter; Increment the screen scroll transition counter.
[e051]RTS
;============================================================================
; TODO: Document Game_MovePlayerOnScrollLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Game_MovePlayerOnScroll
;============================================================================
[e052]Game_MovePlayerOnScrollLeft:; [$e052]
;
; 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]LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e054]CMP #$04; Is it >= 4?
[e056]BCS @LAB_PRG15_MIRROR__e065; If so, jump.
[e058]LDA Player_PosX_Block; Load the player X position.
[e05a]SEC
[e05b]SBC #$04; Subtract 4.
[e05d]STA Player_PosX_Block; Store it.
[e05f]LDA Screen_Maybe_ScrollXCounter; Load our screen scroll counter.
[e061]SBC #$00; Subtract carry.
[e063]STA Screen_Maybe_ScrollXCounter; Store it.
[e065]@LAB_PRG15_MIRROR__e065:; [$e065]
[e065]LDA Player_PosX_Block
[e067]STA Maybe_PlayerX_ForScroll
[e069]RTS
;============================================================================
; 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:; [$e06a]
;
; Check which direction the screen is scrolling.
;
[e06a]LDX Screen_ScrollDirection; Load the screen scroll direction.
[e06c]BEQ Game_MovePlayerOnScrollLeft; If 0, jump to handle scrolling left.
[e06e]DEX
[e06f]BEQ @_scrollRight; If 1, jump to handle scrolling right.
[e071]DEX
[e072]BEQ @_scrollUp; If 2, jump to handle scrolling up.
;
; The screen is scrolling to the screen below.
;
[e074]LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e076]CMP #$08; Is it < 8?
[e078]BCC @_return1; If so, return.
[e07a]LDA Maybe_PlayerY_ForScroll; Load the player's Y position during scroll.
[e07c]SEC
[e07d]SBC #$04; Subtract 4
[e07f]STA Maybe_PlayerY_ForScroll; Store it.
[e081]SEC
[e082]@_return1:; [$e082]
[e082]RTS
;
; The screen is scrolling to the screen above.
;
[e083]@_scrollUp:; [$e083]
[e083]LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e085]CMP #$08; Is it < 8?
[e087]BCC @_return2; If so, return.
[e089]LDA Maybe_PlayerY_ForScroll; Load the player Y position during scroll.
[e08b]CLC
[e08c]ADC #$04; Add 4.
[e08e]STA Maybe_PlayerY_ForScroll; Store it.
[e090]SEC
[e091]@_return2:; [$e091]
[e091]RTS
;
; 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]
[e092]LDA Screen_ScrollPlayerTransitionCounter; Load the transition counter.
[e094]CMP #$04; Is it >= 4?
[e096]BCS @_finishScrollRight; If so, jump.
[e098]LDA Player_PosX_Block; Load the player X position.
[e09a]CLC
[e09b]ADC #$04; Add 4.
[e09d]STA Player_PosX_Block; Store it.
[e09f]LDA Screen_Maybe_ScrollXCounter; Load our screen scroll counter.
[e0a1]ADC #$00; Add carry.
[e0a3]STA Screen_Maybe_ScrollXCounter; Store it.
[e0a5]@_finishScrollRight:; [$e0a5]
[e0a5]LDA Player_PosX_Block
[e0a7]STA Maybe_PlayerX_ForScroll
[e0a9]RTS
[e0aa]Player_SetInitialState:; [$e0aa]
;
; Clear scroll state.
;
[e0aa]LDA #$00
[e0ac]STA Screen_Maybe_ScrollXCounter
[e0ae]STA Player_Something_ScrollPosY
[e0b0]STA PPU_ScrollScreenVert
[e0b2]STA PPU_ScrollScreenHoriz; DEADCODE: Overridden below.
;
; Set the starting speed, status, and iframes.
;
[e0b4]STA Player_MoveAcceleration; Set the player speed to 0.
[e0b6]STA Player_StatusFlag; Set the player status to 0.
[e0b8]STA Player_InvincibilityPhase; Set the invincibility phase to 0.
[e0ba]STA PPU_ScrollScreenVert; Set this again, apparently.
;
; Face the player to the right.
;
[e0bc]LDA #$40; 0x04 = Face player right bit.
[e0be]STA Player_Flags; Set that on the player's flags.
;
; Set the accessory information.
;
[e0c0]LDA #$ff
[e0c2]STA Player_DrawTileReadOffset
;
; Unused value. This is written to but never read from.
;
[e0c4]LDA #$02
[e0c6]STA Player_Unused_00ab
[e0c8]RTS
[e0c9]RETURN_E0C9:; [$e0c9]
[e0c9]RTS
[e0ca]GameLoop_UpdatePlayer:; [$e0ca]
[e0ca]JSR Player_CheckHandleAttack; Check if the player has attacked and handle it.
[e0cd]JSR Player_CheckHandleClimb
[e0d0]JSR Player_CheckHandleJump; Check if the player has jumped and handle it.
[e0d3]JSR Player_HandleIFrames; Handle any pending iframes.
;
; Check if screen scrolling is occurring.
;
[e0d6]LDA Screen_ScrollDirection; A = scrolling activity.
[e0d8]CMP #$04; Is the screen scrolling in either direction?
[e0da]BCC RETURN_E0C9; If so, return.
;
; The screen is not scrolling.
;
[e0dc]JSR Player_CheckHandleEnterDoor; Check if the player is entering a door.
[e0df]JSR Player_CheckOnBreakableBlock
[e0e2]JSR Player_CheckPushingBlock; Check if the player is pushing the block to open a path to Mascon.
[e0e5]JMP 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]
[e0e8]LDA Player_InvincibilityPhase; Check the current invincibility phase.
[e0ea]BEQ @_removeIFrames; If the phase is now 1, remove iframe status.
;
; Invincibility is active. Reduce it by 1.
;
[e0ec]DEC 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]LDA Player_InvincibilityPhase; Check the invincibility phase.
[e0f0]CMP #$39; Is it 0x39?
[e0f2]BEQ @_resetSpeed; If so, reset the speed but don't change the player status.
[e0f4]BCC @_removeIFrames; If not over, update the player status.
;
; Set the knockback speed and return, leaving the knockback
; state intact.
;
[e0f6]JMP Player_SetStandardAcceleration; Set standard acceleration.
;
; Set the knockback speed, but then clear the knockback.
;
[e0f9]@_resetSpeed:; [$e0f9]
[e0f9]JSR Player_SetStandardAcceleration; Reset the player speed.
;
; Clear knockback from the player flags.
;
[e0fc]@_removeIFrames:; [$e0fc]
[e0fc]LDA Player_StatusFlag; Load the player's flags.
[e0fe]AND #$fd; Clear the knockback bit.
[e100]STA Player_StatusFlag; Save them.
[e102]RTS
;============================================================================
; TODO: Document Player_CheckHandleAttack
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e103]Player_CheckHandleAttack:; [$e103]
[e103]LDA Player_Flags; Load the player's flags.
[e105]BMI @_updateHitPhase; If already attacking, jump.
;
; The player is not yet attacking. Check first if the
; player is allowed to attack.
;
[e107]JSR Player_IsClimbing; Is the player climbing?
[e10a]BCS @_clearAttacking; If so, clear any attacking state and return.
;
; The player isn't climbing. Check if they've attacked.
;
[e10c]LDA Joy1_ChangedButtonMask; Load the controller 1 button mask.
[e10e]AND #$40; Is the B button set?
[e110]BEQ @_clearAttacking; If not, clear any attacking state and return.
[e112]LDA Player_StatusFlag; Load the player's status flags.
[e114]AND #$01; Was the player in attack mode?
[e116]BEQ @_setAttacking; If so, set the attacking state.
[e118]RTS
;
; Clear the attacking state for the player.
;
[e119]@_clearAttacking:; [$e119]
[e119]LDA Player_StatusFlag; Load the player's flags.
[e11b]AND #$fe; Clear the Attacking bit.
[e11d]STA Player_StatusFlag; Store it.
[e11f]RTS
;
; Set the attacking state for the player.
;
; This will update the flags and begin the animation
; phase timer.
;
[e120]@_setAttacking:; [$e120]
[e120]LDA Player_Flags; Load the player's flags.
[e122]ORA #$80; Set the Attacking bit.
[e124]STA Player_Flags; Store it.
[e126]LDA Player_StatusFlag; Load the player's status flags.
[e128]ORA #$01; Set the Attacking bit.
[e12a]STA Player_StatusFlag; Store it.
;
; Clear the phase state.
;
[e12c]LDA #$00
[e12e]STA PlayerHitsPhaseTimer; Set phase timer to 0.
[e130]STA PlayerHitsPhaseCounter; Set phase counter to 0.
[e132]@_updateHitPhase:; [$e132]
[e132]INC PlayerHitsPhaseTimer; Increment the current phase timer.
[e134]LDA PlayerHitsPhaseTimer; A = updated phase timer.
[e136]LDX PlayerHitsPhaseCounter; X = phase counter.
[e138]CMP PLAYER_HIT_PHASES,X; Check against the phase lookup table.
[e13b]BCC @_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]LDA #$00
[e13f]STA PlayerHitsPhaseTimer; Reset the current phase's timer to 0.
[e141]INX; Phase counter++
[e142]CPX #$03; Is it >= 3 (end of phases)?
[e144]BCS @_finishAttacking; If so, finish the attacking state.
[e146]STX PlayerHitsPhaseCounter; Else, increment the phase counter for the next frame.
[e148]@_return:; [$e148]
[e148]RTS
[e149]@_finishAttacking:; [$e149]
[e149]LDA Player_Flags; Load the player's flags.
[e14b]AND #$7f; Clear the Attacking flag.
[e14d]STA Player_Flags; Store it.
[e14f]RETURN_E14F:; [$e14f]
[e14f]RTS
[e150]PLAYER_HIT_PHASES:; [$e150]
[e150].byte $08; [0]: 8 frames thrusting out weapon
[e151].byte $03; [1]: 3 frames of waiting
[e152].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:; [$e153]
;
; Temporarily store the player's current flags.
;
[e153]LDA Player_Flags; Load the player's flags.
[e155]AND #$40; Take only the facing bit.
[e157]PHA; Push it to the stack.
;
; Flip the facing bit and knock back in the flipped direction.
;
[e158]EOR #$40; Flip the facing bit temporarily for the purpose of knockback.
[e15a]STA Player_Flags; Store as the new flags.
[e15c]JSR Player_KnockbackHoriz; Move based on the facing bit.
;
; Restore the original flags, merging with any updated bits
; from the move.
;
[e15f]PLA; Pop the original flags.
[e160]STA Temp_00; Store it.
[e162]LDA Player_Flags; Load the flags set above.
[e164]AND #$bf; Clear the facing bit.
[e166]ORA Temp_00; OR with the original facing bit.
[e168]STA Player_Flags; Store it.
[e16a]RTS
;============================================================================
; 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]
[e16b]LDA Player_Flags; Load the player's flags.
[e16d]AND #$40; Is the player facing right?
[e16f]BNE Player_TryMoveRight; If so, move right.
[e171]JMP Player_TryMoveLeft
;============================================================================
; TODO: Document Player_CheckHandleJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e174]Player_CheckHandleJump:; [$e174]
[e174]LDA Screen_ScrollDirection
[e176]BPL RETURN_E14F
[e178]LDA Player_StatusFlag
[e17a]AND #$02
[e17c]BNE Player_HandleKnockback
[e17e]LDA Player_StatusFlag
[e180]BMI @_playerCanFly
[e182]LDA Player_Flags
[e184]AND #$05
[e186]BEQ @_playerCanFly
[e188]LDA Player_Flags
[e18a]AND #$20
[e18c]BEQ @_playerNotMoving
[e18e]LDA Player_Flags
[e190]AND #$40
[e192]BNE Player_TryMoveRight
[e194]JMP Player_TryMoveLeft
[e197]@_playerCanFly:; [$e197]
[e197]LDA Player_Flags
[e199]BMI @LAB_PRG15_MIRROR__e1be
[e19b]LDA Joy1_ButtonMask
[e19d]AND #$03
[e19f]BEQ @_playerNotMoving
[e1a1]INC Player_MovementTick
[e1a3]LSR A
[e1a4]BCS Player_TryMoveRight
[e1a6]LSR A
[e1a7]BCC @_playerNotMoving
[e1a9]JMP Player_TryMoveLeft
[e1ac]@_playerNotMoving:; [$e1ac]
[e1ac]LDA Joy1_ButtonMask
[e1ae]AND #$0c
[e1b0]BEQ @LAB_PRG15_MIRROR__e1be
[e1b2]LDA Player_PosX_Block
[e1b4]AND #$0f
[e1b6]BEQ @LAB_PRG15_MIRROR__e1be
[e1b8]LDA Player_Flags
[e1ba]AND #$08
[e1bc]BNE @LAB_PRG15_MIRROR__e1c5
[e1be]@LAB_PRG15_MIRROR__e1be:; [$e1be]
[e1be]LDA Player_Flags
[e1c0]AND #$df
[e1c2]STA Player_Flags
[e1c4]RTS
[e1c5]@LAB_PRG15_MIRROR__e1c5:; [$e1c5]
[e1c5]INC Player_MovementTick
[e1c7]LDA Player_PosX_Block
[e1c9]AND #$0f
[e1cb]CMP #$08
[e1cd]BCC Player_TryMoveLeft
[e1cf]Player_TryMoveRight:; [$e1cf]
;
; Check whether the player is moving.
;
[e1cf]LDA Player_Flags; Load the player's flags.
[e1d1]AND #$20; Check the moving bit.
[e1d3]BNE @_setMoving; If set, jump to handle movement.
;
; The player is not moving. Reset the movement speed.
;
[e1d5]JSR Player_SetStandardAcceleration; Start with the standard acceleration.
;
; Calculate movement speed and set the player to move to
; the right.
;
[e1d8]@_setMoving:; [$e1d8]
[e1d8]JSR Player_UpdateAcceleration; Calculate the new walking speed.
[e1db]LDA Player_Flags; Load the player's flags.
[e1dd]ORA #$60; Set moving and to the right.
[e1df]STA Player_Flags; Store as the new flags.
;
; Update the player's X position to factor in acceleration.
;
[e1e1]LDA Player_PosX_Frac; Load the fractional X position.
[e1e3]CLC
[e1e4]ADC Player_MoveAcceleration; Add the move acceleration.
[e1e6]STA Player_PosX_Frac; Store as the new fractional position.
[e1e8]LDA Player_PosX_Block; Load the full X position.
[e1ea]ADC Player_MoveAcceleration_U; Add the move acceleration.
[e1ec]STA Player_PosX_Block; And store it.
;
; Check if the block there is passable.
;
[e1ee]LDX #$01
[e1f0]JSR Player_CheckIfPassable; Is the block to the right passable?
[e1f3]BEQ @_checkAtRightEdge; If not, jump to check for an edge or transition boundary.
;
; The block is not passable. Cap the position.
;
[e1f5]LDA Player_PosX_Block; Load the player X position.
[e1f7]AND #$f0; Cap it.
[e1f9]STA Player_PosX_Block; And store it.
[e1fb]JMP @_return
;
; Check if the player is far enough to the right for
; any screen transition logic.
;
[e1fe]@_checkAtRightEdge:; [$e1fe]
[e1fe]LDA Player_PosX_Block; X = Player X position.
[e200]CMP #$f1; Is it too far left to perform a screen transition?
[e202]BCC @_return; If so, return.
;
; Check if the player is at the edge and can scroll.
;
[e204]LDY #$01
[e206]JSR Screen_IsEdge; Check if the screen is the right-most edge.
[e209]BCS Player_SetPosAtRightEdge; If so, jump to set the position there.
;
; There's a screen to the right. Transition there.
;
[e20b]LDA Area_ScreenToTheRight; A = Neighboring screen to the right.
[e20d]STA Area_CurrentScreen; Store as the new current screen.
[e20f]LDX #$01
[e211]JSR Area_ScrollTo; Begin scrolling to the right.
[e214]INC Screen_Maybe_ScrollXCounter; Increment the scroll counter.
[e216]LDA #$00
[e218]STA Screen_ScrollPlayerTransitionCounter; Clear the transition counter.
[e21a]@_return:; [$e21a]
[e21a]RTS
;============================================================================
; 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]
[e21b]LDA #$f0
[e21d]STA Player_PosX_Block; Set the X position to 0xF0.
[e21f]RTS
[e220]Player_TryMoveLeft:; [$e220]
;
; Check whether the player is moving.
;
[e220]LDA Player_Flags; Load the player's flags.
[e222]AND #$20; Check the moving bit.
[e224]BNE @_setMoving; If set, jump to handle movement.
;
; The player is not moving. Reset the movement speed.
;
[e226]JSR Player_SetStandardAcceleration; Start with the standard acceleration.
;
; Calculate movement speed and set the player to move to
; the left.
;
[e229]@_setMoving:; [$e229]
[e229]JSR Player_UpdateAcceleration; Calculate the new walking speed.
[e22c]LDA Player_Flags; Load the player's flags.
[e22e]AND #$bf; Set the player to face left.
[e230]ORA #$20; Set the player to moving.
[e232]STA Player_Flags; Store as the new flags.
;
; Update the player's X position to factor in acceleration.
;
[e234]LDA Player_PosX_Frac; Load the fractional X position.
[e236]SEC
[e237]SBC Player_MoveAcceleration; Subtract the move acceleration.
[e239]STA Player_PosX_Frac; Store as the new fractional position.
[e23b]LDA Player_PosX_Block; Load the full X position.
[e23d]SBC Player_MoveAcceleration_U; Subtract the move acceleration.
[e23f]PHP; Push all flags.
[e240]BCS @_saveFlags; If >= 0, jump.
[e242]LDA #$00; Else, cap at 0, so it doesn't wrap.
[e244]@_saveFlags:; [$e244]
[e244]STA Player_PosX_Block; Store the new X position.
;
; Check if the block there is passable.
;
[e246]LDX #$00
[e248]JSR Player_CheckIfPassable; Is the block to the left passable?
[e24b]BEQ @_checkAtLeftEdge; If not, jump to check for an edge or transition boundary.
;
; The block is not passable. Cap the position.
;
[e24d]PLP; Pop the flags from the acceleration calculation.
[e24e]LDA Player_PosX_Block; Load the player X position.
[e250]AND #$0f; Is this in the first 16 pixels?
[e252]BEQ @_return1; If so, we're done.
[e254]LDA Player_PosX_Block; Load the player X position.
[e256]AND #$f0; Keep all but pixels 0-16.
[e258]CLC
[e259]ADC #$10; Add 16 pixels.
[e25b]STA Player_PosX_Block; Store as the new position.
[e25d]@_return1:; [$e25d]
[e25d]RTS; And we're done.
;
; Check if the player is far enough to the left for
; any screen transition logic.
;
[e25e]@_checkAtLeftEdge:; [$e25e]
[e25e]PLP; Pop the flags from the acceleration calculation.
[e25f]BCS @_return2; If it didn't overflow off the left, return.
;
; Check if the player is at the edge and can scroll.
;
[e261]LDY #$00
[e263]JSR Screen_IsEdge; Check if the screen is the left-most edge.
[e266]BCS @_setAtLeftEdge; If at the edge, jump to cap the position.
;
; There's a screen to the left. Transition there.
;
[e268]LDA Area_ScreenToTheLeft; A = Neighboring screen to the left.
[e26a]STA Area_CurrentScreen; Store as the new current screen.
[e26c]LDX #$00
[e26e]JSR Area_ScrollTo; Begin scrolling to the left.
[e271]LDA #$00
[e273]STA Player_PosX_Block; Set the X position to 0.
[e275]STA Screen_ScrollPlayerTransitionCounter; Clear the transition counter.
[e277]@_return2:; [$e277]
[e277]RTS
;
; The player is at the left-most edge, but didn't pass it.
;
[e278]@_setAtLeftEdge:; [$e278]
[e278]LDA #$00
[e27a]STA Player_PosX_Block; Set the X position to 0.
[e27c]RTS; And return.
[e27d].byte $00; [0]:
[e27e].byte $0f; [1]:
[e27f]Player_SetStandardAcceleration:; [$e27f]
[e27f]LDA #$c0
[e281]STA Player_MoveAcceleration; Set the lower byte of movement acceleation to 0xC0.
[e283]LDA #$00
[e285]STA Player_MoveAcceleration_U; Set the upper byte to 0.
[e287]RTS
;============================================================================
; 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:; [$e288]
;
; The player's in knockback state. Bump the player
; back a bunch.
;
[e288]LDA #$00
[e28a]STA Player_MoveAcceleration; Set lower acceleration byte to 0.
[e28c]LDA #$08
[e28e]STA Player_MoveAcceleration_U; Set upper to 8.
[e290]RTS
;============================================================================
; TODO: Document Player_UpdateAcceleration
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Player_TryMoveLeft
;
Player_TryMoveRight
;============================================================================
[e291]Player_UpdateAcceleration:; [$e291]
[e291]LDA Player_StatusFlag
[e293]AND #$02
[e295]BNE Player_UpdatePosFromKnockback
[e297]LDA Joy1_ButtonMask
[e299]AND #$0c
[e29b]BEQ @LAB_PRG15_MIRROR__e2a3
[e29d]LDA Player_Flags
[e29f]AND #$08
[e2a1]BNE Player_SetStandardAcceleration
[e2a3]@LAB_PRG15_MIRROR__e2a3:; [$e2a3]
[e2a3]LDA Player_MoveAcceleration
[e2a5]CMP #$80
[e2a7]LDA Player_MoveAcceleration_U
[e2a9]SBC #$01
[e2ab]BCS @_return
[e2ad]LDA a:PlayerTitle
[e2b0]LSR A
[e2b1]LSR A
[e2b2]AND #$03
[e2b4]TAX
[e2b5]LDA Player_MoveAcceleration
[e2b7]CLC
[e2b8]ADC BYTE_ARRAY_PRG15_MIRROR__e2c4,X
[e2bb]STA Player_MoveAcceleration
[e2bd]LDA Player_MoveAcceleration_U
[e2bf]ADC #$00
[e2c1]STA Player_MoveAcceleration_U
[e2c3]@_return:; [$e2c3]
[e2c3]RTS
[e2c4]BYTE_ARRAY_PRG15_MIRROR__e2c4:; [$e2c4]
[e2c4].byte $02; [0]:
[e2c5].byte $04; [1]:
[e2c6].byte $06; [2]:
[e2c7].byte $08; [3]:
;============================================================================
; TODO: Document Player_CheckHandleClimb
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e2c8]Player_CheckHandleClimb:; [$e2c8]
[e2c8]LDA Player_StatusFlag; A = Player status flags.
[e2ca]BPL @LAB_PRG15_MIRROR__e2d6
[e2cc]LDA Joy1_ButtonMask
[e2ce]BPL @LAB_PRG15_MIRROR__e2d6
[e2d0]JSR Player_CheckIfOnLadder
[e2d3]JMP @LAB_PRG15_MIRROR__e2f4
[e2d6]@LAB_PRG15_MIRROR__e2d6:; [$e2d6]
[e2d6]JSR Player_CheckIfOnLadder
[e2d9]LDA Player_Flags
[e2db]AND #$08
[e2dd]BEQ @LAB_PRG15_MIRROR__e2fe
[e2df]LDA Joy1_ButtonMask
[e2e1]AND #$0c
[e2e3]BEQ @LAB_PRG15_MIRROR__e2fe
[e2e5]LDA Player_Flags
[e2e7]ORA #$10
[e2e9]STA Player_Flags
[e2eb]LDA Player_PosX_Block
[e2ed]AND #$0f
[e2ef]BEQ @LAB_PRG15_MIRROR__e2f4
[e2f1]JMP Player_CheckHandleClimbMaybeSide
[e2f4]@LAB_PRG15_MIRROR__e2f4:; [$e2f4]
[e2f4]LDA Joy1_ButtonMask
[e2f6]LSR A
[e2f7]LSR A
[e2f8]LSR A
[e2f9]BCS Player_CheckHandleClimbDown
[e2fb]LSR A
[e2fc]BCS Player_CheckHandleClimbUp
[e2fe]@LAB_PRG15_MIRROR__e2fe:; [$e2fe]
[e2fe]JMP Player_CheckHandleClimbMaybeSide
;============================================================================
; TODO: Document Player_CheckHandleClimbUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimb
;============================================================================
[e301]Player_CheckHandleClimbUp:; [$e301]
[e301]LDA Player_Flags
[e303]AND #$da
[e305]STA Player_Flags
[e307]LDX #$02
[e309]JSR Player_CheckIfPassable
[e30c]BNE @LAB_PRG15_MIRROR__e343
[e30e]INC Player_MovementTick
[e310]LDA Player_StatusFlag
[e312]BPL @LAB_PRG15_MIRROR__e31e
[e314]LDA Player_PosY
[e316]SEC
[e317]SBC #$01
[e319]STA Player_PosY
[e31b]JMP @LAB_PRG15_MIRROR__e32b
[e31e]@LAB_PRG15_MIRROR__e31e:; [$e31e]
[e31e]LDA BYTE_00a0
[e320]SEC
[e321]SBC #$a0
[e323]STA BYTE_00a0
[e325]LDA Player_PosY
[e327]SBC #$00
[e329]STA Player_PosY
[e32b]@LAB_PRG15_MIRROR__e32b:; [$e32b]
[e32b]BCS @LAB_PRG15_MIRROR__e343
[e32d]LDY #$02
[e32f]JSR Screen_IsEdge
[e332]BCS @LAB_PRG15_MIRROR__e344
[e334]LDA Area_ScreenAbove
[e336]STA Area_CurrentScreen
[e338]LDX #$02
[e33a]JSR Area_ScrollTo
[e33d]DEC Player_Something_ScrollPosY
[e33f]LDA #$c0
[e341]STA Player_PosY
[e343]@LAB_PRG15_MIRROR__e343:; [$e343]
[e343]RTS
[e344]@LAB_PRG15_MIRROR__e344:; [$e344]
[e344]LDA #$00
[e346]STA Player_PosY
[e348]RTS
;============================================================================
; TODO: Document Player_CheckHandleClimbDown
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimb
;============================================================================
[e349]Player_CheckHandleClimbDown:; [$e349]
[e349]LDA Player_Flags
[e34b]AND #$da
[e34d]STA Player_Flags
[e34f]LDX #$03
[e351]JSR Player_CheckIfPassable
[e354]BNE RETURN_E393
[e356]INC Player_MovementTick
[e358]LDA Player_StatusFlag
[e35a]BPL @LAB_PRG15_MIRROR__e36c
[e35c]LDA BYTE_00a0
[e35e]CLC
[e35f]ADC #$80
[e361]STA BYTE_00a0
[e363]LDA Player_PosY
[e365]ADC #$01
[e367]STA Player_PosY
[e369]JMP Player_MoveDownScreen
[e36c]@LAB_PRG15_MIRROR__e36c:; [$e36c]
[e36c]LDA BYTE_00a0
[e36e]CLC
[e36f]ADC #$c0
[e371]STA BYTE_00a0
[e373]LDA Player_PosY
[e375]ADC #$00
[e377]STA Player_PosY
;============================================================================
; TODO: Document Player_MoveDownScreen
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbDown
;============================================================================
[e379]Player_MoveDownScreen:; [$e379]
[e379]CMP #$c1
[e37b]BCC RETURN_E393
[e37d]LDY #$03
[e37f]JSR Screen_IsEdge
[e382]BCS Player_SetPosAtBottomEdge
[e384]LDA Area_ScreenBelow
[e386]STA Area_CurrentScreen
[e388]LDX #$03
[e38a]JSR Area_ScrollTo
[e38d]INC Player_Something_ScrollPosY
[e38f]LDA #$00
[e391]STA Player_PosY
[e393]RETURN_E393:; [$e393]
[e393]RTS
;============================================================================
; 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]
[e394]LDA #$c0
[e396]STA Player_PosY; Set player Y position to 0xC0 (192).
[e398]RTS
;============================================================================
; TODO: Document Player_CheckHandleClimbMaybeSide
;
; INPUTS:
; X
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimb
;============================================================================
[e399]Player_CheckHandleClimbMaybeSide:; [$e399]
[e399]LDA Something_Player_ClimbLadderCheckPos
[e39b]CMP #$20
[e39d]BCC @LAB_PRG15_MIRROR__e3a5
;
; Clear the player's Jumping state.
;
[e39f]LDA Player_Flags; Load the player's flags.
[e3a1]AND #$fe; Clear the Jumping bit.
[e3a3]STA Player_Flags; Save it back.
[e3a5]@LAB_PRG15_MIRROR__e3a5:; [$e3a5]
[e3a5]LDA Player_Flags
[e3a7]LSR A
[e3a8]BCC @LAB_PRG15_MIRROR__e3ad
[e3aa]JMP Player_StayOnLadderAndContinue
[e3ad]@LAB_PRG15_MIRROR__e3ad:; [$e3ad]
[e3ad]LDA #$03
[e3af]JSR Player_CheckIfPassable
[e3b2]BEQ @LAB_PRG15_MIRROR__e3b7
[e3b4]JMP Player_StayOnLadderAndContinue
[e3b7]@LAB_PRG15_MIRROR__e3b7:; [$e3b7]
[e3b7]JSR Area_CheckCanClimbAdjacent
[e3ba]LDA Blocks_Result
[e3bc]BEQ @LAB_PRG15_MIRROR__e3cd
[e3be]LDA Maybe_ClimbLadderOffset
[e3c0]CMP #$08
[e3c2]BCS @LAB_PRG15_MIRROR__e3c9
[e3c4]INC Maybe_ClimbLadderOffset
[e3c6]JMP Player_ContinueHandleClimbOrJump
[e3c9]@LAB_PRG15_MIRROR__e3c9:; [$e3c9]
[e3c9]LDA #$00
[e3cb]STA Maybe_ClimbLadderOffset
[e3cd]@LAB_PRG15_MIRROR__e3cd:; [$e3cd]
[e3cd]LDA Player_StatusFlag
[e3cf]BPL @LAB_PRG15_MIRROR__e3d1
[e3d1]@LAB_PRG15_MIRROR__e3d1:; [$e3d1]
[e3d1]LDA Player_Flags
[e3d3]ORA #$04
[e3d5]STA Player_Flags
[e3d7]LDA Player_Flags
[e3d9]AND #$08
[e3db]BNE Player_StayOnLadderAndContinue
[e3dd]LDA Player_StatusFlag
[e3df]BPL @LAB_PRG15_MIRROR__e3f5
[e3e1]LDA Joy1_ButtonMask
[e3e3]BPL @LAB_PRG15_MIRROR__e3f5
[e3e5]LDA BYTE_00a0
[e3e7]CLC
[e3e8]ADC #$00
[e3ea]STA BYTE_00a0
[e3ec]LDA Player_PosY
[e3ee]ADC #$01
[e3f0]STA Player_PosY
[e3f2]JMP Area_ScrollScreenDown
[e3f5]@LAB_PRG15_MIRROR__e3f5:; [$e3f5]
[e3f5]LDA Player_PosY
[e3f7]CLC
[e3f8]ADC #$08
[e3fa]STA Player_PosY
;============================================================================
; TODO: Document Area_ScrollScreenDown
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbMaybeSide
;============================================================================
[e3fc]Area_ScrollScreenDown:; [$e3fc]
[e3fc]CMP #$c1
[e3fe]BCC @LAB_PRG15_MIRROR__e41e
[e400]LDY #$03
[e402]JSR Screen_IsEdge
[e405]BCC @LAB_PRG15_MIRROR__e40e
[e407]LDA #$c0
[e409]STA Player_PosY
[e40b]JMP @LAB_PRG15_MIRROR__e42c
[e40e]@LAB_PRG15_MIRROR__e40e:; [$e40e]
[e40e]LDA Area_ScreenBelow
[e410]STA Area_CurrentScreen
[e412]LDX #$03
[e414]JSR Area_ScrollTo
[e417]INC Player_Something_ScrollPosY
[e419]LDA #$00
[e41b]STA Player_PosY
[e41d]RTS
[e41e]@LAB_PRG15_MIRROR__e41e:; [$e41e]
[e41e]LDX #$03
[e420]JSR Player_CheckIfPassable
[e423]BEQ @LAB_PRG15_MIRROR__e42b
[e425]LDA Player_PosY
[e427]AND #$f0
[e429]STA Player_PosY
[e42b]@LAB_PRG15_MIRROR__e42b:; [$e42b]
[e42b]RTS
[e42c]@LAB_PRG15_MIRROR__e42c:; [$e42c]
[e42c]LDA Player_Flags
[e42e]AND #$fb
[e430]STA Player_Flags
[e432]RTS
;============================================================================
; TODO: Document Player_ClearJumpingAndHoldingToClimb
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_ContinueHandleClimbOrJump
;============================================================================
[e433]Player_ClearJumpingAndHoldingToClimb:; [$e433]
[e433]LDA Player_Flags
[e435]AND #$fc
[e437]STA Player_Flags
[e439]RETURN_E439:; [$e439]
[e439]RTS
;============================================================================
; TODO: Document Player_StayOnLadderAndContinue
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbMaybeSide
;============================================================================
[e43a]Player_StayOnLadderAndContinue:; [$e43a]
;
; Clear the Falling Off state.
;
[e43a]LDA Player_Flags; Load the player's flags.
[e43c]AND #$fb; Clear the Falling Off flag.
[e43e]STA Player_Flags; Save it back.
[e440]LDA #$00
[e442]STA Maybe_ClimbLadderOffset
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Player_ContinueHandleClimbOrJump
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbMaybeSide
;============================================================================
[e444]Player_ContinueHandleClimbOrJump:; [$e444]
;
; Check if the player is jumping.
;
[e444]LDA Player_Flags; Load the player's flags.
[e446]LSR A; Shift the Jump flag into Carry.
[e447]BCS @_isJumping; If set (player is jumping), then jump (branch).
;
; The player is not jumping.
;
[e449]LDX Joy1_ChangedButtonMask; X = Controller 1 changed button mask.
[e44b]BPL Player_ClearJumpingAndHoldingToClimb; If the player is holding down A, then check grabbing for a ladder and return.
[e44d]JSR Player_IsClimbing
[e450]BCS RETURN_E439
[e452]LDX #$02
[e454]JSR Player_CheckIfPassable
[e457]BNE RETURN_E4B6
[e459]LDA Player_Flags
[e45b]ORA #$03
[e45d]STA Player_Flags
[e45f]LDA #$00
[e461]STA Something_Player_ClimbLadderCheckPos
[e463]@_isJumping:; [$e463]
[e463]LDX Something_Player_ClimbLadderCheckPos
[e465]CPX #$10
[e467]BCC @LAB_PRG15_MIRROR__e46b
[e469]BCS @LAB_PRG15_MIRROR__e4a2
[e46b]@LAB_PRG15_MIRROR__e46b:; [$e46b]
[e46b]LDA Player_PosY
[e46d]SEC
[e46e]SBC BYTE_ARRAY_PRG15_MIRROR__e4d6,X
[e471]STA Player_PosY
[e473]BCS @LAB_PRG15_MIRROR__e481
[e475]LDY #$02
[e477]JSR Screen_IsEdge
[e47a]BCC Area_ScrollScreenUp
[e47c]LDA #$00
[e47e]JMP @LAB_PRG15_MIRROR__e49a
[e481]@LAB_PRG15_MIRROR__e481:; [$e481]
[e481]LDX #$02
[e483]JSR Player_CheckIfPassable
[e486]BEQ Area_Something_IncDAT00a6
[e488]LDX Something_Player_ClimbLadderCheckPos
[e48a]LDA Player_PosY
[e48c]AND #$0f
[e48e]TAX
[e48f]LDA Player_PosY
[e491]AND #$f0
[e493]CPX #$00
[e495]BEQ @LAB_PRG15_MIRROR__e49a
[e497]CLC
[e498]ADC #$10
[e49a]@LAB_PRG15_MIRROR__e49a:; [$e49a]
[e49a]STA Player_PosY
[e49c]LDA #$0f
[e49e]STA Something_Player_ClimbLadderCheckPos
[e4a0]BNE Area_Something_IncDAT00a6
[e4a2]@LAB_PRG15_MIRROR__e4a2:; [$e4a2]
[e4a2]LDA Player_PosY
[e4a4]CLC
[e4a5]ADC BYTE_ARRAY_PRG15_MIRROR__e4d6,X
[e4a8]STA Player_PosY
[e4aa]LDX #$03
[e4ac]JSR Player_CheckIfPassable
[e4af]BNE Maybe_SetPlayerForScrollUp
[e4b1]Area_Something_IncDAT00a6:; [$e4b1]
[e4b1]LDX Something_Player_ClimbLadderCheckPos
[e4b3]INX
[e4b4]STX Something_Player_ClimbLadderCheckPos
[e4b6]RETURN_E4B6:; [$e4b6]
[e4b6]RTS
;============================================================================
; TODO: Document Area_ScrollScreenUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_ContinueHandleClimbOrJump
;============================================================================
[e4b7]Area_ScrollScreenUp:; [$e4b7]
[e4b7]LDA Area_ScreenAbove
[e4b9]STA Area_CurrentScreen
[e4bb]LDX #$02
[e4bd]JSR Area_ScrollTo
[e4c0]DEC Player_Something_ScrollPosY
[e4c2]LDA #$c0
[e4c4]STA Player_PosY
[e4c6]JMP Maybe_SetPlayerForScrollUp
;============================================================================
; TODO: Document Maybe_SetPlayerForScrollUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Area_ScrollScreenUp
;============================================================================
[e4c9]Maybe_SetPlayerForScrollUp:; [$e4c9]
[e4c9]LDA Player_PosY
[e4cb]AND #$f0
[e4cd]STA Player_PosY
[e4cf]LDA Player_Flags
[e4d1]AND #$fe
[e4d3]STA Player_Flags
[e4d5]RTS
[e4d6]BYTE_ARRAY_PRG15_MIRROR__e4d6:; [$e4d6]
[e4d6].byte $08; [0]:
[e4d7].byte $04; [1]:
[e4d8].byte $04; [2]:
[e4d9].byte $04; [3]:
[e4da].byte $04; [4]:
[e4db].byte $02; [5]:
[e4dc].byte $02; [6]:
[e4dd].byte $01; [7]:
[e4de].byte $01; [8]:
[e4df].byte $01; [9]:
[e4e0].byte $01; [10]:
[e4e1].byte $00; [11]:
[e4e2].byte $00; [12]:
[e4e3].byte $00; [13]:
[e4e4].byte $00; [14]:
[e4e5].byte $00; [15]:
[e4e6].byte $00; [16]:
[e4e7].byte $00; [17]:
[e4e8].byte $00; [18]:
[e4e9].byte $00; [19]:
[e4ea].byte $00; [20]:
[e4eb].byte $01; [21]:
[e4ec].byte $01; [22]:
[e4ed].byte $01; [23]:
[e4ee].byte $01; [24]:
[e4ef].byte $02; [25]:
[e4f0].byte $02; [26]:
[e4f1].byte $04; [27]:
[e4f2].byte $04; [28]:
[e4f3].byte $04; [29]:
[e4f4].byte $04; [30]:
[e4f5].byte $08; [31]:
;============================================================================
; TODO: Document Area_CheckCanClimbAdjacent
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimbMaybeSide
;============================================================================
[e4f6]Area_CheckCanClimbAdjacent:; [$e4f6]
[e4f6]LDX #$00
[e4f8]LDA Joy1_ChangedButtonMask; Load the controller 1 button mask.
[e4fa]AND #$03; If Left or Right pressed?
[e4fc]BEQ @_returnFalse; If not, jump to return a false result.
[e4fe]AND #$01; Keep the Right Button bit.
[e500]BEQ @LAB_PRG15_MIRROR__e503
[e502]INX
[e503]@LAB_PRG15_MIRROR__e503:; [$e503]
[e503]LDA Player_PosX_Block
[e505]CLC
[e506]ADC BYTE_PRG15_MIRROR__e524,X
[e509]STA Arg_PixelPosX
[e50b]LDA Player_PosY
[e50d]CLC
[e50e]ADC #$20
[e510]STA Arg_PixelPosY
[e512]CMP #$f0
[e514]BCC @_returnFalse
[e516]JSR Area_ConvertPixelsToBlockPos
[e519]JSR ScreenBuffer_IsBlockImpassable
[e51c]STA Blocks_Result
[e51e]RTS
[e51f]@_returnFalse:; [$e51f]
[e51f]LDA #$00; A = 0 (false).
[e521]STA Blocks_Result; Store it as the result.
[e523]RTS
[e524]BYTE_PRG15_MIRROR__e524:; [$e524]
[e524].byte $00; [$e524] byte
[e525]BYTE_PRG15_MIRROR__e525:; [$e525]
[e525].byte $0f; [$e525] byte
;============================================================================
; TODO: Document Player_CheckHandleEnterDoor
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e526]Player_CheckHandleEnterDoor:; [$e526]
[e526]LDA Joy1_ChangedButtonMask
[e528]AND #$08
[e52a]BEQ @_return
[e52c]JSR Area_SetStateFromDoorDestination; Set the new state based on the other end of the door.
[e52f]LDA Blocks_Result; Check the result of that.
[e531]BEQ @_return
[e533]LDA Temp_BlockPos2
[e535]CMP #$fe
[e537]BCS Player_EnterDoorToOutside
;
; The player is going inside.
;
[e539]JSR Game_RunDoorRequirementHandler
[e53c]LDA a:CurrentDoor_KeyRequirement
[e53f]BNE @_return
;
; The door is not locked.
;
[e541]LDA Temp_BlockPos2
[e543]CMP #$20
[e545]BCS Player_EnterDoorToInside
[e547]JSR Screen_FadeToBlack
[e54a]LDX #$06
[e54c]@_paletteCheckLoop:; [$e54c]
[e54c]LDA Screen_DestPaletteOrIndex
[e54e]CMP @_return+1,X
[e551]BEQ @_setupArea
[e553]DEX
[e554]BPL @_paletteCheckLoop
[e556]BMI @_enterScreen
[e558]@_setupArea:; [$e558]
[e558]LDA $e570,X
[e55b]CMP a:Areas_DefaultMusic
[e55e]BEQ @_enterScreen
[e560]STA Music_Current
[e562]STA a:Areas_DefaultMusic
[e565]@_enterScreen:; [$e565]
[e565]JMP Game_SetupEnterScreen
[e568]@_return:; [$e568]
[e568]RTS
;============================================================================
; 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.
;============================================================================
[e569].byte PALETTE_OUTSIDE; [0]:
[e56a].byte PALETTE_TOWER; [1]:
[e56b].byte PALETTE_MIST; [2]:
[e56c].byte PALETTE_SUFFER; [3]:
[e56d].byte PALETTE_DARTMOOR; [4]:
[e56e]Palette_ARRAY_PRG15_MIRROR__e569_5_:; [$e56e]
[e56e].byte PALETTE_FRATERNAL; [5]:
[e56f]Palette_ARRAY_PRG15_MIRROR__e569_6_:; [$e56f]
[e56f].byte PALETTE_KING_GRIEVES_ROOM; [6]:
[e570].byte MUSIC_APOLUNE; [0]:
[e571].byte MUSIC_MAYBE_TOWER; [1]:
[e572].byte MUSIC_FOREPAW; [2]:
[e573].byte MUSIC_MAYBE_TOWER; [3]:
[e574].byte MUSIC_DAYBREAK; [4]:
[e575]Music_ARRAY_PRG15_MIRROR__e570_5_:; [$e575]
[e575].byte MUSIC_MAYBE_TOWER; [5]:
[e576]Music_ARRAY_PRG15_MIRROR__e570_6_:; [$e576]
[e576].byte MUSIC_MAYBE_TOWER; [6]:
;============================================================================
; TODO: Document Player_EnterDoorToInside
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e577]Player_EnterDoorToInside:; [$e577]
[e577]LDX Area_LoadingScreenIndex
[e579]STX a:Area_DestScreen
[e57c]LDA Screen_DestPaletteOrIndex
[e57e]STA Area_LoadingScreenIndex
[e580]TAX
[e581]LDA BUILDING_PALETTES,X
[e584]STA Screen_DestPaletteOrIndex
[e586]LDA BUILDING_MAYBE_TILES_INDEXES,X
[e589]STA a:Building_TilesIndex
[e58c]LDA BUILDING_START_POSITIONS,X
[e58f]STA Screen_StartPosYX
[e591]LDA a:Areas_DefaultMusic
[e594]STA a:Area_Music_Outside
[e597]LDA BUILDING_MUSIC,X
[e59a]STA a:Areas_DefaultMusic
[e59d]LDA Area_CurrentArea
;
; Where does this door take the player?
;
[e59f]CMP #$04
[e5a1]BNE @LAB_PRG15_MIRROR__e5a6
[e5a3]JMP Game_BeginExitBuilding
[e5a6]@LAB_PRG15_MIRROR__e5a6:; [$e5a6]
[e5a6]LDA Player_Flags
[e5a8]AND #$bf
[e5aa]STA Player_Flags
[e5ac]JSR Screen_FadeToBlack
[e5af]JMP Game_SetupEnterBuilding
;============================================================================
; TODO: Document Player_EnterDoorToOutside
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleEnterDoor
;============================================================================
[e5b2]Player_EnterDoorToOutside:; [$e5b2]
;
; The player opened a door back to the outside.
;
[e5b2]LSR A
[e5b3]LDA a:Area_Region
[e5b6]ROL A
[e5b7]PHA
[e5b8]TAY
;
; Check if the door is locked.
;
[e5b9]LDA DOOR_LOOKUP_REQUIREMENTS,Y
[e5bc]STA a:CurrentDoor_KeyRequirement
[e5bf]JSR Game_RunDoorRequirementHandler
[e5c2]PLA
[e5c3]TAY
[e5c4]LDA a:CurrentDoor_KeyRequirement
[e5c7]BNE SUB_RETURN_E5DA
;
; This door is not locked.
;
[e5c9]LDA MAYBE_REGION_TRANSITION_MAP,Y
[e5cc]STA a:Area_Region
[e5cf]LDA MAYBE_SCREEN_TRANSITION_MAP,Y
[e5d2]STA Area_LoadingScreenIndex
[e5d4]JSR Screen_FadeToBlack
[e5d7]JMP Game_SetupAndLoadOutsideArea
[e5da]SUB_RETURN_E5DA:; [$e5da]
[e5da]RTS
[e5db]DOOR_LOOKUP_REQUIREMENTS:; [$e5db]
[e5db].byte $00; [0]: No key required
[e5dc].byte $04; [1]: "J" key required
[e5dd].byte $00; [2]: No key required
[e5de].byte $03; [3]: "Q" key required
[e5df].byte $00; [4]: No key required
[e5e0].byte $01; [5]: "A" key required
[e5e1].byte $00; [6]: No key required
[e5e2].byte $07; [7]: Ring of Dworf required
[e5e3].byte $00; [8]: No key required
[e5e4].byte $08; [9]: Demon's Ring required
[e5e5].byte $00; [10]: No key required
[e5e6].byte $00; [11]: No key required
[e5e7]MAYBE_REGION_TRANSITION_MAP:; [$e5e7]
[e5e7].byte REGION_EOLIS; [0]:
[e5e8].byte REGION_TRUNK; [1]:
[e5e9].byte REGION_EOLIS; [2]:
[e5ea].byte REGION_MIST; [3]:
[e5eb].byte REGION_TRUNK; [4]:
[e5ec].byte REGION_BRANCH; [5]:
[e5ed].byte REGION_MIST; [6]:
[e5ee].byte REGION_DARTMOOR; [7]:
[e5ef].byte REGION_BRANCH; [8]:
[e5f0].byte REGION_EVIL_FORTRESS; [9]:
[e5f1].byte REGION_DARTMOOR; [10]:
[e5f2].byte REGION_EVIL_FORTRESS; [11]:
[e5f3]MAYBE_SCREEN_TRANSITION_MAP:; [$e5f3]
[e5f3].byte $00; [0]:
[e5f4].byte $00; [1]:
[e5f5].byte $08; [2]:
[e5f6].byte $11; [3]:
[e5f7].byte $28; [4]:
[e5f8].byte $00; [5]:
[e5f9].byte $1f; [6]:
[e5fa].byte $00; [7]:
[e5fb].byte $27; [8]:
[e5fc].byte $08; [9]:
[e5fd].byte $0e; [10]:
[e5fe].byte $0e; [11]:
[e5ff]BUILDING_MUSIC:; [$e5ff]
[e5ff].byte MUSIC_KINGS_ROOM; [0]: King's Room
[e600].byte MUSIC_TEMPLE; [1]: Temple
[e601].byte MUSIC_SHOP; [2]: Hospital
[e602].byte MUSIC_SHOP; [3]: Tavern
[e603].byte MUSIC_SHOP; [4]: Tool Shop
[e604].byte MUSIC_SHOP; [5]: Key Shop
[e605].byte MUSIC_SHOP; [6]: House
[e606].byte MUSIC_SHOP; [7]: Meat Shop
[e607].byte MUSIC_SHOP; [8]: Martial Arts
[e608].byte MUSIC_SHOP; [9]: Magic Shop
;============================================================================
; Mapping of building indexes to palette indexes.
;
; XREFS:
;
Player_EnterDoorToInside
;============================================================================
[e609]BUILDING_PALETTES:; [$e609]
[e609].byte PALETTE_KINGS_ROOM; [0]: King's Room
[e60a].byte PALETTE_TEMPLE; [1]: Temple
[e60b].byte PALETTE_HOSPITAL; [2]: Hospital
[e60c].byte PALETTE_TAVERN; [3]: Tavern
[e60d].byte PALETTE_TOOL_SHOP; [4]: Tool Shop
[e60e].byte PALETTE_KEY_SHOP; [5]: Key Shop
[e60f].byte PALETTE_HOUSE; [6]: House
[e610].byte PALETTE_MEAT_SHOP; [7]: Meat Shop
[e611].byte PALETTE_MARTIAL_ARTS; [8]: Martial Arts
[e612].byte PALETTE_MAGIC_SHOP; [9]: Magic Shop
[e613]BUILDING_MAYBE_TILES_INDEXES:; [$e613]
[e613].byte $06; [0]: King's Room
[e614].byte $06; [1]: Temple
[e615].byte $06; [2]: Hospital
[e616].byte $07; [3]: Tavern
[e617].byte $07; [4]: Tool Shop
[e618].byte $07; [5]: Key Shop
[e619].byte $07; [6]: House
[e61a].byte $07; [7]: Meat Shop
[e61b].byte $08; [8]: Martial Arts
[e61c].byte $08; [9]: Magic Shop
[e61d]BUILDING_START_POSITIONS:; [$e61d]
[e61d].byte $9e; [0]: King's Room
[e61e].byte $9e; [1]: Temple
[e61f].byte $9e; [2]: Hospital
[e620].byte $8e; [3]: Tavern
[e621].byte $7e; [4]: Tool Shop
[e622].byte $7e; [5]: Key Shop
[e623].byte $7e; [6]: House
[e624].byte $7e; [7]: Meat Shop
[e625].byte $8e; [8]: Martial Arts
[e626].byte $8e; [9]: Magic Shop
[e627]Screen_IsEdge:; [$e627]
[e627]LDA Area_ScreenToTheLeft,Y; Load the screen ID at the given neighbor.
[e62a]CMP #$ff; If 0xFF, C = 1. Else, C = 0
[e62c]RTS
;============================================================================
; TODO: Document Area_CanMoveUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveRight
;============================================================================
[e62d]Area_CanMoveUp:; [$e62d]
[e62d]LDA Player_PosY
[e62f]LSR A
[e630]LSR A
[e631]LSR A
[e632]LSR A
[e633]TAX
[e634]LDY FirstColumnInRightScreen,X
[e637]JSR Area_IsBlockImpassable
[e63a]STA Blocks_Result
[e63c]INX
[e63d]LDY FirstColumnInRightScreen,X
[e640]JSR Area_IsBlockImpassable
[e643]ORA Blocks_Result
[e645]STA Blocks_Result
[e647]LDA Player_PosY
[e649]AND #$0f
[e64b]BEQ @LAB_PRG15_MIRROR__e658
[e64d]INX
[e64e]LDY FirstColumnInRightScreen,X
[e651]JSR Area_IsBlockImpassable
[e654]ORA Blocks_Result
[e656]STA Blocks_Result
[e658]@LAB_PRG15_MIRROR__e658:; [$e658]
[e658]LDA Blocks_Result
[e65a]RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e65b]Area_CanPlayerMoveRight:; [$e65b]
[e65b]LDA Player_PosX_Block
[e65d]CLC
[e65e]ADC #$10
[e660]STA Arg_PixelPosX
[e662]BCS Area_CanMoveUp
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document Area_CanPlayerMoveAtY
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveLeft
;============================================================================
[e664]Area_CanPlayerMoveAtY:; [$e664]
[e664]LDA Player_PosY
[e666]STA Arg_PixelPosY
[e668]JSR Area_ConvertPixelsToBlockPos
[e66b]JSR ScreenBuffer_IsBlockImpassable
[e66e]STA Blocks_Result
[e670]TXA
[e671]CLC
[e672]ADC #$10
[e674]TAX
[e675]JSR ScreenBuffer_IsBlockImpassable
[e678]ORA Blocks_Result
[e67a]STA Blocks_Result
[e67c]LDA Player_PosY
[e67e]AND #$0f
[e680]BEQ @_returnResult
[e682]TXA
[e683]CLC
[e684]ADC #$10
[e686]TAX
[e687]JSR ScreenBuffer_IsBlockImpassable
[e68a]ORA Blocks_Result
[e68c]STA Blocks_Result
[e68e]@_returnResult:; [$e68e]
[e68e]LDA Blocks_Result
[e690]RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e691]Area_CanPlayerMoveLeft:; [$e691]
[e691]LDA Player_PosX_Block
[e693]SEC
[e694]SBC #$01
[e696]STA Arg_PixelPosX
[e698]BCS Area_CanPlayerMoveAtY
[e69a]LDA Player_PosY
[e69c]LSR A
[e69d]LSR A
[e69e]LSR A
[e69f]LSR A
[e6a0]TAX
[e6a1]LDY LastColumnLeftScreen,X
[e6a4]JSR Area_IsBlockImpassable
[e6a7]STA Blocks_Result
[e6a9]INX
[e6aa]LDY LastColumnLeftScreen,X
[e6ad]JSR Area_IsBlockImpassable
[e6b0]ORA Blocks_Result
[e6b2]STA Blocks_Result
[e6b4]LDA Player_PosY
[e6b6]AND #$0f
[e6b8]BEQ @LAB_PRG15_MIRROR__e6c5
[e6ba]INX
[e6bb]LDY LastColumnLeftScreen,X
[e6be]JSR Area_IsBlockImpassable
[e6c1]ORA Blocks_Result
[e6c3]STA Blocks_Result
[e6c5]@LAB_PRG15_MIRROR__e6c5:; [$e6c5]
[e6c5]LDA Blocks_Result
[e6c7]RTS
[e6c8]Player_CheckIfPassable:; [$e6c8]
[e6c8]TXA
[e6c9]BEQ Area_CanPlayerMoveLeft
[e6cb]DEX
[e6cc]BEQ Area_CanPlayerMoveRight
[e6ce]DEX
[e6cf]BEQ Area_CanPlayerMoveUp
[e6d1]LDA Player_PosY
[e6d3]CLC
[e6d4]ADC #$20
[e6d6]STA Arg_PixelPosY
[e6d8]CMP #$d0
[e6da]BCC Area_CanMoveImmediatelyRight
[e6dc]LDA Player_PosX_Block
[e6de]LSR A
[e6df]LSR A
[e6e0]LSR A
[e6e1]LSR A
[e6e2]TAX
[e6e3]LDY FirstRowBelowScreen,X
[e6e6]JSR Area_IsBlockImpassable
[e6e9]STA Blocks_Result
[e6eb]LDA Player_PosX_Block
[e6ed]AND #$0f
[e6ef]BEQ @_returnResult
[e6f1]INX
[e6f2]LDY FirstRowBelowScreen,X
[e6f5]JSR Area_IsBlockImpassable
[e6f8]ORA Blocks_Result
[e6fa]STA Blocks_Result
[e6fc]@_returnResult:; [$e6fc]
[e6fc]LDA Blocks_Result
[e6fe]RTS
;============================================================================
; TODO: Document Area_CanMoveImmediatelyRight
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Area_CanPlayerMoveUp
;
Player_CheckIfPassable
;============================================================================
[e6ff]Area_CanMoveImmediatelyRight:; [$e6ff]
[e6ff]LDA Player_PosX_Block
[e701]CLC
[e702]ADC #$04
[e704]STA Arg_PixelPosX
[e706]JSR Area_ConvertPixelsToBlockPos
[e709]JSR ScreenBuffer_IsBlockImpassable
[e70c]STA Blocks_Result
[e70e]LDA Player_PosX_Block
[e710]AND #$0f
[e712]SEC
[e713]SBC #$04
[e715]CMP #$08
[e717]BCS @_returnResult
[e719]INX
[e71a]JSR ScreenBuffer_IsBlockImpassable
[e71d]ORA Blocks_Result
[e71f]STA Blocks_Result
[e721]@_returnResult:; [$e721]
[e721]LDA Blocks_Result
[e723]RTS
;============================================================================
; TODO: Document Area_CanPlayerMoveUp
;
; INPUTS:
; None.
;
; OUTPUTS:
; Z
;
; XREFS:
;
Player_CheckIfPassable
;============================================================================
[e724]Area_CanPlayerMoveUp:; [$e724]
[e724]LDA Player_PosY
[e726]SEC
[e727]SBC #$01
[e729]STA Arg_PixelPosY
[e72b]CMP #$f0
[e72d]BCC Area_CanMoveImmediatelyRight
[e72f]LDA Player_PosX_Block
[e731]LSR A
[e732]LSR A
[e733]LSR A
[e734]LSR A
[e735]TAX
[e736]LDY LastRowAboveScreen,X
[e739]JSR Area_IsBlockImpassable
[e73c]STA Blocks_Result
[e73e]LDA Player_PosX_Block
[e740]AND #$0f
[e742]BEQ @_returnResult
[e744]INX
[e745]LDY LastRowAboveScreen,X
[e748]JSR Area_IsBlockImpassable
[e74b]ORA Blocks_Result
[e74d]STA Blocks_Result
[e74f]@_returnResult:; [$e74f]
[e74f]LDA Blocks_Result
[e751]RTS
;============================================================================
; TODO: Document Player_CheckIfOnLadder
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_CheckHandleClimb
;============================================================================
[e752]Player_CheckIfOnLadder:; [$e752]
;
; 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]LDA Player_PosX_Block; A = player X position
[e754]CLC
[e755]ADC #$07; Add 7.
[e757]STA Arg_PixelPosX; Store the player's new X pixel position
[e759]LDA Player_PosY; Load the player's Y position
[e75b]STA Arg_PixelPosY; Store the player's Y pixel position
;
; Check if the block at this position is climbable.
;
[e75d]JSR Area_ConvertPixelsToBlockPos; Convert to a block position.
[e760]LDY ScreenBuffer,X; Load the block at that position.
[e763]JSR Area_GetBlockProperty; Load the block property for that block.
[e766]STA Blocks_Result; Store it.
[e768]JSR Area_IsBlockClimbable; Check if it's climbable.
[e76b]BCS @_resetCanClimb
;
; The block is not climbable. Check if the player is
; overlapping a block to the right, and check that.
;
[e76d]LDA Player_PosX_Block; A = player X position.
[e76f]AND #$0f
[e771]CMP #$08
[e773]BNE @_checkBlockBelow
[e775]INX
[e776]LDY ScreenBuffer,X
[e779]JSR Area_GetBlockProperty
[e77c]STA Blocks_Result
[e77e]JSR Area_IsBlockClimbable
[e781]BCS @_resetCanClimb
[e783]@_checkBlockBelow:; [$e783]
[e783]LDA Player_PosX_Block
[e785]CLC
[e786]ADC #$07
[e788]STA Arg_PixelPosX
[e78a]LDA Player_PosY
[e78c]CLC
[e78d]ADC #$1f
[e78f]STA Arg_PixelPosY
[e791]JSR Area_ConvertPixelsToBlockPos
[e794]LDY ScreenBuffer,X
[e797]JSR Area_GetBlockProperty
[e79a]STA Blocks_Result
[e79c]JSR Area_IsBlockClimbable
[e79f]BCS @_resetCanClimb
[e7a1]LDA Player_PosX_Block
[e7a3]AND #$0f
[e7a5]CMP #$08
[e7a7]BNE @_resetCanClimb
[e7a9]INX
[e7aa]LDY ScreenBuffer,X
[e7ad]JSR Area_GetBlockProperty
[e7b0]STA Blocks_Result
;
; Reset the can-climb bit.
;
[e7b2]@_resetCanClimb:; [$e7b2]
[e7b2]LDA Player_Flags; Load the player's flags.
[e7b4]AND #$f7; Clear the can-climb bit.
[e7b6]JSR Area_IsBlockClimbable; Is the current block climbable?
[e7b9]BCC @_clearBit5AndReturn; If not, jump.
;
; Set the can-climb bit.
;
[e7bb]ORA #$08; Else, set the can-climbable bit.
[e7bd]STA Player_Flags; Set the ladder flag bit
[e7bf]RTS
[e7c0]@_clearBit5AndReturn:; [$e7c0]
[e7c0]AND #$ef; Clear bit 5.
[e7c2]STA Player_Flags; Store it.
[e7c4]RTS
;============================================================================
; 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:; [$e7c5]
;
; Convert the player position to a normalized block position.
;
[e7c5]LDA Player_PosX_Block; Load the player's full X position.
[e7c7]CLC
[e7c8]ADC #$07; Add 7.
[e7ca]STA Arg_PixelPosX; Store as the X pixel position argument.
[e7cc]LDA Player_PosY; Load the player's full Y position.
[e7ce]STA Arg_PixelPosY; Store as the Y pixel position argument.
[e7d0]JSR Area_ConvertPixelsToBlockPos; Conver to block positions.
;
; Load the block at this position from the screen and
; check what it is.
;
[e7d3]LDY ScreenBuffer,X; Load the block from the screen buffer.
[e7d6]JSR Area_GetBlockProperty; Get the block property.
[e7d9]CMP #$03; Is it a door?
[e7db]BEQ @_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]LDA Player_PosX_Block; Load the player's full X position.
[e7df]AND #$0f; Check the lower nibble.
[e7e1]CMP #$08; Is it on a full block boundary?
[e7e3]BNE @_blockIsNotDoor; _blockIsAir
[e7e5]INX; Increment the block position.
;
; We normalized to the next overlapped block. Now check
; to see if it's a door.
;
[e7e6]LDY ScreenBuffer,X; Load the block at that position.
[e7e9]JSR Area_GetBlockProperty; Load the block property.
[e7ec]CMP #$03; Is it a door?
[e7ee]BEQ @_blockIsDoor; If so, jump.
;
; The player was not standing in front of a door.
; Consider this block passable.
;
[e7f0]@_blockIsNotDoor:; [$e7f0]
[e7f0]LDA #$00
[e7f2]STA Blocks_Result; Set the resulting block property as air.
[e7f4]RTS
;
; The player is standing in front of a door.
;
; Check to see where they are.
;
[e7f5]@_blockIsDoor:; [$e7f5]
[e7f5]LDA Area_CurrentArea; Load the current region.
[e7f7]CMP #$04; Is this the area around Victim(?) ?
[e7f9]BNE @LAB_PRG15_MIRROR__e7fe; If not, jump and continue on.
;
; We won't be handling anything in this area. We're done here.
;
[e7fb]JMP SUB_RETURN_E5DA; Else, we're done.
[e7fe]@LAB_PRG15_MIRROR__e7fe:; [$e7fe]
[e7fe]LDA #$01
[e800]STA Blocks_Result; Mark the resulting block as solid by default.
[e802]STX 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]LDA CurrentArea_DoorLocationsAddr; Load the lower byte of the doors address.
[e806]STA Temp_Addr_L; Store it temporarily.
[e808]LDA CurrentArea_DoorLocationsAddr_U; Load the upper byte.
[e80a]STA Temp_Addr_U; Store it temporarily.
;
; Switch to Bank 3 (level data).
;
[e80c]LDA a:CurrentROMBank; Load the current ROM bank.
[e80f]PHA; Push it to the stack.
[e810]LDX #$03
[e812]JSR 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]
[e815]LDY #$00; Y = 0
[e817]LDA (Temp_Addr_L),Y; Load the first byte from the referenced address.
[e819]CMP #$ff; Is it 0xFF?
[e81b]BEQ @_returnAirBlock; If so, consider this air and return.
[e81d]CMP Area_CurrentScreen; Is it the current screen index?
[e81f]BNE @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]INY; Y++
[e822]LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e824]CMP Temp_BlockPos; Is it the block position we stored?
[e826]BNE @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]INY; Y++
[e829]LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e82b]STA Temp_BlockPos2; Store that byte as a temporary block position.
[e82d]INY; Y++
[e82e]LDA (Temp_Addr_L),Y; Load the next byte from the referenced address.
[e830]STA Screen_StartPosYX; Store as the area's starting X/Y position.
[e832]LDA 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]LDY Area_CurrentArea; Y = current area
[e836]CPY #$03; Is the current area 3?
[e838]BNE @LAB_PRG15_MIRROR__e83d; If not, jump.
;
; We'll need to normalize the block position.
;
[e83a]SEC
[e83b]SBC #$20; Reduce it by 32.
[e83d]@LAB_PRG15_MIRROR__e83d:; [$e83d]
[e83d]ASL A; Multiply the block position by 4.
[e83e]ASL A
;
; Load state from the door destination at this block.
;
; We'll load the screen index, palette, and the key
; requirement.
;
[e83f]TAY; Y = A (block position)
[e840]LDA (CurrentArea_DoorDestinationsAddr),Y; Load the screen index from the door destination.
[e842]STA Area_LoadingScreenIndex; Store it as the new screen index.
[e844]INY; Y++
[e845]LDA (CurrentArea_DoorDestinationsAddr),Y; Load the palette from the door destination.
[e847]STA Screen_DestPaletteOrIndex; Store it as the new palette.
[e849]INY; Y++
[e84a]LDA (CurrentArea_DoorDestinationsAddr),Y; Load the key requirement.
[e84c]STA a:CurrentDoor_KeyRequirement; Store it.
[e84f]JMP @_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]
[e852]LDA Temp_Addr_L; Load the lower byte of the door destination address we stored.
[e854]CLC
[e855]ADC #$04; Add 4 (skip the information found on the other side of that door).
[e857]STA Temp_Addr_L; Store it back out.
[e859]LDA Temp_Addr_U; Load the upper byte of the address.
[e85b]ADC #$00; Add the carry flag, if the lower byte overflowed.
[e85d]STA Temp_Addr_U; Store it.
[e85f]JMP @_doorCheckLoop
;
; Consider this air.
;
[e862]@_returnAirBlock:; [$e862]
[e862]LDA #$00
[e864]STA Blocks_Result; Set the resulting block property to 0 (air).
;
; Restore the previous ROM bank and return.
;
[e866]@_restoreBankAndReturn:; [$e866]
[e866]PLA; Pop the previous bank from the stack.
[e867]TAX; X = A (bank)
[e868]JSR MMC1_UpdateROMBank; Update to the bank.
[e86b]RTS
[e86c]Area_ConvertPixelsToBlockPos:; [$e86c]
[e86c]LDA Arg_PixelPosY; Load the stored Y coordinate for comparison
[e86e]AND #$f0; Clear out the lower nibble
[e870]STA Temp_00
[e872]LDA Arg_PixelPosX; Load the stored X coordinate for comparison
[e874]LSR A; Convert X to block positions
[e875]LSR A
[e876]LSR A
[e877]LSR A
[e878]ORA Temp_00; Include the Y block position
[e87a]TAX; Store in X and return
[e87b]RTS
[e87c]ScreenBuffer_IsBlockImpassable:; [$e87c]
[e87c]LDY ScreenBuffer,X; Load the block at the given index, and fall through.
;
; v-- Fall through --v
;
[e87f]Area_IsBlockImpassable:; [$e87f]
[e87f]JSR 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]
[e882]TAY; Y = A
[e883]LDA BLOCK_PROPERTY_IMPASSABLE_MAP,Y; A = Impassable flag for the block property type.
[e886]RTS; 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:
;
FUN_PRG14__854c
;============================================================================
[e887]Area_IsBlockImpassableOrLadder:; [$e887]
[e887]LDY ScreenBuffer,X; Load the block property offset from the screen buffer at X.
[e88a]JSR Area_GetBlockProperty; Load the block property.
[e88d]CMP #$02; Is it a ladder?
[e88f]BNE Area_GetFromImpassableMap; If not a ladder, look up from the passable map.
;
; This is a ladder.
;
[e891]LDA #$01; Return true.
[e893]RTS
;============================================================================
; 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:; [$e894]
;
; Load the block property at this screen buffer offset.
;
[e894]LDY ScreenBuffer,X; Y = block property at screen buffer X
[e897]JSR Area_GetBlockProperty; Load the block property.
[e89a]STA Blocks_Result; Store that in a temporary variable.
;
; Check if this is a solid block.
;
[e89c]CMP #$01; Is it solid?
[e89e]BEQ @_blockIsSolid; If so, then jump.
;
; Check if this is a ladder.
;
[e8a0]CMP #$02; Is it a ladder?
[e8a2]BEQ @_blockIsSolid; If so, then jump.
;
; Check if this is a block transitioning to another screen.
;
[e8a4]CMP #$0a; Is this a transition to a screen?
[e8a6]BEQ @_blockIsSolid; If so, then jump.
;
; It's not solid. Store a result of 0 (air).
;
[e8a8]LDA #$00
[e8aa]STA Blocks_Result; Set result = 0.
[e8ac]RTS
;
; It is solid. Store a result of 1 (solid).
;
[e8ad]@_blockIsSolid:; [$e8ad]
[e8ad]LDA #$01
[e8af]STA Blocks_Result; Set result = 1.
[e8b1]RTS
;============================================================================
; 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]
[e8b2]PHA; Push A to the stack.
[e8b3]LDA Blocks_Result; Load the block property provided in the temp variable.
[e8b5]CMP #$02; Is it a ladder?
[e8b7]BEQ @_isLadder; If so, jump.
[e8b9]CMP #$0a; TODO: Is it... something else ladder-like?
[e8bb]BNE @_isNotLadder; If not, jump.
;
; This is a ladder.
;
[e8bd]@_isLadder:; [$e8bd]
[e8bd]PLA; Pop A.
[e8be]SEC; Set carry to 1 as a truthy result.
[e8bf]RTS
[e8c0]@_isNotLadder:; [$e8c0]
[e8c0]PLA; Pop A.
[e8c1]CLC; Set carry to 0 as a falsy result.
[e8c2]RTS
;============================================================================
; 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]
[e8c3]LDY ScreenBuffer,X; Load the block property index from the screen buffer.
;
; v-- Fall through --v
;
[e8c6]Area_GetBlockProperty:; [$e8c6]
[e8c6]TYA; A = Y
[e8c7]LSR A; Y = even block offset based on the index.
[e8c8]TAY; Y = A
[e8c9]BCC @_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]LDA BlockProperties,Y; Load the block property at the index.
[e8ce]LSR A; Shift to the lower nibble.
[e8cf]LSR A
[e8d0]LSR A
[e8d1]LSR A
[e8d2]RTS
;
; Load the block property, masking out the upper nibble.
;
[e8d3]@_isEven:; [$e8d3]
[e8d3]LDA BlockProperties,Y; Load the block property at the index.
[e8d6]AND #$0f; Mask out the upper nibble, giving just the lower.
[e8d8]RTS
;============================================================================
; Map of block property IDs to impassibility flags.
;
; XREFS:
;
Area_GetFromImpassableMap
;============================================================================
[e8d9]BLOCK_PROPERTY_IMPASSABLE_MAP:; [$e8d9]
[e8d9].byte $00; [0]: Air
[e8da].byte $01; [1]: Solid
[e8db].byte $00; [2]: Ladder
[e8dc].byte $00; [3]: Door
[e8dd].byte $00; [4]: Foreground
[e8de].byte $01; [5]: Breakable floor
[e8df].byte $01; [6]: Pushable
[e8e0].byte $01; [7]: ??
[e8e1].byte $01; [8]: ??
[e8e2].byte $00; [9]: Maybe: Transition down
[e8e3].byte $00; [10]: Maybe: Transition up
[e8e4].byte $01; [11]: Breakable by Mattock
[e8e5].byte $00; [12]: Area transition left-to-right
[e8e6].byte $00; [13]: Area transition right-to-left
[e8e7].byte $00; [14]: ??
[e8e8].byte $00; [15]: ??
[e8e9]Area_ScrollTo:; [$e8e9]
[e8e9]LDA a:CurrentScreen_SpecialEventID; Load the current screen's event ID.
[e8ec]AND #$7f; Keep the numeric ID.
[e8ee]CMP #$01; Is it 1 (boss room)?
[e8f0]BNE @_clearStates; If not, jump.
;
; The player is scrolling from a boss room. Reset the music
; back to the default music for the area.
;
[e8f2]LDA a:Areas_DefaultMusic; Load the default music for the area.
[e8f5]STA Music_Current; Set as the current music.
[e8f7]@_clearStates:; [$e8f7]
[e8f7]LDA #$00; A = 0
[e8f9]STA Player_MovementTick; Set player movement tick to 0.
[e8fb]STA InterruptCounter; Set interrupt counter to 0.
;
; Clear any cast magic.
;
[e8fd]LDA #$ff; A = 0xFF (unset)
[e8ff]STA a:CastMagic_Type; Set as the magic type on screen.
[e902]JMP Area_LoadScrollDataRight; Now scroll to the room.
[e905]Player_CheckOnBreakableBlock:; [$e905]
;
; Only check for falling periodically.
;
[e905]LDA InterruptCounter; Load the interrupt counter.
[e907]AND #$07; Are we ready to check for falling?
[e909]BNE @_return; If not, return.
;
; Begin checking against a block 32 pixels down.
;
[e90b]LDA Player_PosY; A = Player's Y position.
[e90d]CLC
[e90e]ADC #$20; A += 32
[e910]STA Arg_PixelPosY; Store as the Y argument for block checks.
[e912]CMP #$f0; Is it >= 0xEF? (one block below the screen)
[e914]BCS @_return; If so, stop falling.
;
; The player may be permitted to fall. Check the block
; it's on.
;
[e916]LDA Player_Flags; Load the player's flags.
[e918]AND #$40; Keep only the facing bit.
[e91a]ROL A; Shift this into bit 0.
[e91b]ROL A
[e91c]ROL A
[e91d]AND #$01; Mask out everything else.
[e91f]PHA; Push it to the stack.
;
; Check the first block the player is overlapping.
;
[e920]TAX; X = facing value.
[e921]LDA Player_PosX_Block; A = Player X position.
[e923]CLC
[e924]ADC @_CHECK_BREAKABLE_BLOCK_X_OFFSETS,X; Load a block offset to round to the nearest overlapping blocks.
[e927]STA Arg_PixelPosX; Store as the X position to check.
[e929]JSR Area_ConvertPixelsToBlockPos; Convert it to a block position.
[e92c]LDY ScreenBuffer,X; Load the block value from the screen.
[e92f]STY Blocks_Result; Store as the block result.
[e931]JSR Area_GetBlockProperty; And get its corresponding property.
[e934]CMP #$05; Is it 5 (breakable floor)?
[e936]BNE @_checkNextBlock; If not, check the next overlapping block.
[e938]PLA; Pop the facing bit from the stack.
[e939]JMP Area_HandleBreakableFloor; Handle breakable floor logic.
;
; Check the second block the player is overlapping.
;
[e93c]@_checkNextBlock:; [$e93c]
[e93c]PLA; Pop the facing bit from the stack.
[e93d]EOR #$01; XOR the facing bit to check the other direction.
[e93f]TAX; X = A
[e940]LDA Player_PosX_Block; A = Player X position.
[e942]CLC
[e943]ADC @_CHECK_BREAKABLE_BLOCK_X_OFFSETS,X; Load a block offset to round to the next overlapping blocks.
[e946]STA Arg_PixelPosX; Store as the X position to check.
[e948]JSR Area_ConvertPixelsToBlockPos; Convert it to a block position.
[e94b]LDY ScreenBuffer,X; Load the block value from the screen.
[e94e]STY Blocks_Result; Store as the block result.
[e950]JSR Area_GetBlockProperty
[e953]CMP #$05; Is it 5 (breakable floor)?
[e955]BNE @_return; If not, return.
[e957]JMP Area_HandleBreakableFloor; Handle breakable floor logic.
[e95a]@_return:; [$e95a]
[e95a]RTS
;============================================================================
; Map of facing bit values to block X offsets for checking breakable blocks.
;
; XREFS:
;
Player_CheckOnBreakableBlock
;============================================================================
[e95b]@_CHECK_BREAKABLE_BLOCK_X_OFFSETS:; [$e95b]
[e95b].byte $04; [0]: Facing left
[e95c].byte $0c; [1]: Facing right
;============================================================================
; TODO: Document Player_CheckPushingBlock
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e95d]Player_CheckPushingBlock:; [$e95d]
[e95d]LDA Joy1_ButtonMask
[e95f]AND #$03
[e961]BEQ @LAB_PRG15_MIRROR__e9b9
[e963]LDA a:SpecialItems
[e966]AND #$40
[e968]BEQ @LAB_PRG15_MIRROR__e9b9
[e96a]LDA a:Quests
[e96d]AND #$07
[e96f]CMP #$07
[e971]BNE @LAB_PRG15_MIRROR__e9b9
[e973]LDA Player_Flags
[e975]AND #$40
[e977]ROL A
[e978]ROL A
[e979]ROL A
[e97a]AND #$01
[e97c]TAX
[e97d]LDA Player_PosY
[e97f]STA Arg_PixelPosY
[e981]LDA Player_PosX_Block
[e983]CLC
[e984]ADC BYTE_ARRAY_PRG15_MIRROR__e9be,X
[e987]STA Arg_PixelPosX
;
; Fetch the block property at this position.
;
[e989]JSR Area_ConvertPixelsToBlockPos
[e98c]LDY ScreenBuffer,X
[e98f]JSR Area_GetBlockProperty
;
; Check if the block is pushable. If so, we'll handle it.
;
[e992]CMP #$06
[e994]BNE @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]INC BlockPushCounter
[e998]LDA BlockPushCounter
[e99a]CMP #$3f
[e99c]BNE @LAB_PRG15_MIRROR__e9a3
[e99e]LDA #$0f
[e9a0]JSR Sound_PlayEffect
[e9a3]@LAB_PRG15_MIRROR__e9a3:; [$e9a3]
[e9a3]LDA BlockPushCounter
[e9a5]CMP #$60
[e9a7]BCC @_return
[e9a9]LDA #$01
[e9ab]STA PathToMascon_Opening
[e9ad]STX PathToMascon_FountainCoverPos
[e9af]LDA Joy1_ButtonMask
[e9b1]LSR A
[e9b2]AND #$01
[e9b4]TAX
[e9b5]JMP Game_OpenPathToMascon
[e9b8]@_return:; [$e9b8]
[e9b8]RTS
[e9b9]@LAB_PRG15_MIRROR__e9b9:; [$e9b9]
[e9b9]LDA #$00
[e9bb]STA BlockPushCounter
[e9bd]RTS
[e9be]BYTE_ARRAY_PRG15_MIRROR__e9be:; [$e9be]
[e9be].byte $ff; [0]:
[e9bf].byte $10; [1]:
;============================================================================
; TODO: Document Player_CheckSwitchScreen
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
GameLoop_UpdatePlayer
;============================================================================
[e9c0]Player_CheckSwitchScreen:; [$e9c0]
;
; Check the current block the player is on.
;
[e9c0]LDA Player_PosY
[e9c2]STA Arg_PixelPosY
[e9c4]LDA Player_PosX_Block
[e9c6]STA Arg_PixelPosX
[e9c8]JSR Area_ConvertPixelsToBlockPos
[e9cb]JSR ScreenBuffer_LoadBlockProperty
[e9ce]CMP #$09
[e9d0]BEQ @LAB_PRG15_MIRROR__e9de
[e9d2]CMP #$0a
[e9d4]BEQ @LAB_PRG15_MIRROR__e9de
[e9d6]CMP #$0d
[e9d8]BEQ @LAB_PRG15_MIRROR__e9de
[e9da]CMP #$0c
[e9dc]BNE @_return
;
; Check the next block.
;
[e9de]@LAB_PRG15_MIRROR__e9de:; [$e9de]
[e9de]LDA Player_PosX_Block
[e9e0]CLC
[e9e1]ADC #$0f
[e9e3]STA Arg_PixelPosX
[e9e5]JSR Area_ConvertPixelsToBlockPos
[e9e8]JSR ScreenBuffer_LoadBlockProperty
[e9eb]CMP #$09
[e9ed]BEQ @LAB_PRG15_MIRROR__e9fb
[e9ef]CMP #$0a
[e9f1]BEQ @LAB_PRG15_MIRROR__e9fb
[e9f3]CMP #$0d
[e9f5]BEQ @LAB_PRG15_MIRROR__e9fb
[e9f7]CMP #$0c
[e9f9]BNE @_return
;
; Check if this is a horizontal transition block. If so,
; switch the area.
;
[e9fb]@LAB_PRG15_MIRROR__e9fb:; [$e9fb]
[e9fb]CMP #$0c
[e9fd]BEQ Player_CheckSwitchScreen_SwitchAreaHoriz
[e9ff]CMP #$0d
[ea01]BEQ Player_CheckSwitchScreen_SwitchAreaHoriz
;
; This is a vertical transition block. Switch the
; area.
;
[ea03]LDA Area_CurrentArea
[ea05]ASL A
[ea06]TAX
[ea07]LDA AREA_SCREEN_COMPARATORS,X
[ea0a]STA Temp_Addr_L
[ea0c]LDA AREA_SCREEN_COMPARATORS+1,X
[ea0f]STA Temp_Addr_U
[ea11]LDY #$00
[ea13]@LAB_PRG15_MIRROR__ea13:; [$ea13]
[ea13]LDA (Temp_Addr_L),Y
[ea15]CMP #$ff
[ea17]BEQ @_return
[ea19]CMP Area_CurrentScreen
[ea1b]BNE @LAB_PRG15_MIRROR__ea2f
[ea1d]INY
[ea1e]LDA (Temp_Addr_L),Y
[ea20]STA Area_LoadingScreenIndex
[ea22]INY
[ea23]LDA (Temp_Addr_L),Y
[ea25]STA Screen_StartPosYX
[ea27]INY
[ea28]LDA (Temp_Addr_L),Y
[ea2a]STA Screen_DestPaletteOrIndex
[ea2c]JMP Game_SetupEnterScreen
[ea2f]@LAB_PRG15_MIRROR__ea2f:; [$ea2f]
[ea2f]INY
[ea30]INY
[ea31]INY
[ea32]INY
[ea33]JMP @LAB_PRG15_MIRROR__ea13
[ea36]@_return:; [$ea36]
[ea36]RTS
[ea37]AREA_SCREEN_COMPARATORS:; [$ea37]
[ea37].word BYTE_PRG15_MIRROR__ea4f; Eolis
[ea39].word BYTE_PRG15_MIRROR__ea47; Apolune
[ea3b].word BYTE_PRG15_MIRROR__ea4f; Forepaw
[ea3d].word BYTE_PRG15_MIRROR__ea4f; Mascon
[ea3f].word BYTE_PRG15_MIRROR__ea4f; Victim
[ea41].word BYTE_PRG15_MIRROR__ea4f; Conflate
[ea43].word BYTE_PRG15_MIRROR__ea4f; Daybreak
[ea45].word BYTE_PRG15_MIRROR__ea4f; Evil Fortress
[ea47]BYTE_PRG15_MIRROR__ea47:; [$ea47]
[ea47].byte $0c; Current screen comparator
[ea48].byte $16; New screen index
[ea49].byte $b3; Y, X
[ea4a].byte $06; Area
[ea4b].byte $16; Current screen comparator
[ea4c].byte $0c; New screen index
[ea4d].byte $2d; Y, X
[ea4e].byte $06; Area
[ea4f]BYTE_PRG15_MIRROR__ea4f:; [$ea4f]
[ea4f].byte $ff; [$ea4f] byte
[ea50]Game_BeginExitBuilding:; [$ea50]
;
; Face the player to the right.
;
[ea50]LDA Player_Flags; Load the player flags.
[ea52]ORA #$40; Face the player right.
[ea54]STA Player_Flags; Store the flags.
[ea56]LDA a:Area_Music_Outside; Load the music used outside the building.
[ea59]STA a:Areas_DefaultMusic; Set as the music to play.
[ea5c]JMP 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:; [$ea5f]
;
; Check if the player is in the area of Victim.
;
[ea5f]LDA Area_CurrentArea
[ea61]CMP #$04
[ea63]BEQ Game_BeginExitBuilding
[ea65]ASL A
[ea66]TAY
[ea67]LDA AREA_TOWN_TRANSITIONS_DATA,Y
[ea6a]STA Temp_Addr_L
[ea6c]LDA AREA_TOWN_TRANSITIONS_DATA+1,Y
[ea6f]STA Temp_Addr_U
[ea71]LDY #$00
[ea73]@LAB_PRG15_MIRROR__ea73:; [$ea73]
[ea73]LDA (Temp_Addr_L),Y
[ea75]CMP #$ff
[ea77]BEQ @_return
[ea79]CMP Area_CurrentScreen
[ea7b]BNE @LAB_PRG15_MIRROR__ea94
[ea7d]INY
[ea7e]LDA (Temp_Addr_L),Y
[ea80]STA Area_CurrentArea
[ea82]INY
[ea83]LDA (Temp_Addr_L),Y
[ea85]STA Area_LoadingScreenIndex
[ea87]INY
[ea88]LDA (Temp_Addr_L),Y
[ea8a]STA Screen_StartPosYX
[ea8c]INY
[ea8d]LDA (Temp_Addr_L),Y
[ea8f]STA Screen_DestPaletteOrIndex
[ea91]JMP Game_SetupNewArea
[ea94]@LAB_PRG15_MIRROR__ea94:; [$ea94]
[ea94]TYA
[ea95]CLC
[ea96]ADC #$05
[ea98]TAY
[ea99]BCC @LAB_PRG15_MIRROR__ea73
[ea9b]@_return:; [$ea9b]
[ea9b]RTS
[ea9c]AREA_TOWN_TRANSITIONS_DATA:; [$ea9c]
[ea9c].word TOWN_TRANSITIONS_EMPTY; [0]: Eolis
[ea9e].word TOWN_TRANSITIONS_APOLUNE; [1]: Apolune
[eaa0].word TOWN_TRANSITIONS_FOREPAW; [2]: Forepaw
[eaa2].word TOWN_TRANSITIONS_MASCON; [3]: Mascon
[eaa4].word TOWN_TRANSITIONS_EMPTY; [4]: Victim
[eaa6].word TOWN_TRANSITIONS_CONFLATE; [5]: Conflate
[eaa8].word TOWN_TRANSITIONS_DAYBREAK; [6]: Daybreak
[eaaa].word TOWN_TRANSITIONS_EMPTY; [7]: Evil Fortress
[eaac]TOWN_TRANSITIONS_APOLUNE:; [$eaac]
[eaac].byte $00; [$eaac] byte
.byte AREA_MASCON
[eaae].byte $02,$92; [$eaae] byte
.byte PALETTE_TOWN
[eab1].byte $07; Entering Apolune from Trunk (screen 7)
[eab2].byte AREA_MASCON; |-> Switch to area 3
[eab3].byte $00; |-> Screen index 0
[eab4].byte $92; |-> Start position Y=9, X=2
[eab5].byte PALETTE_TOWN; '-> Palette 27
[eab6].byte $08; [$eab6] byte
.byte AREA_MASCON
[eab8].byte $01,$9e; [$eab8] byte
.byte PALETTE_TOWN
[eabb].byte $1a; Entering Forepaw from Trunk (screen 26)
[eabc].byte AREA_MASCON; |-> Switch to area 3
[eabd].byte $02; |-> Screen index 2
[eabe].byte $92; |-> Start position X=2, Y=9
[eabf].byte PALETTE_TOWN; '-> Palette 27
[eac0].byte $1d; [$eac0] byte
.byte AREA_MASCON
[eac2].byte $03,$9e; [$eac2] byte
.byte PALETTE_TOWN
[eac5].byte $ff; [$eac5] byte
[eac6]TOWN_TRANSITIONS_MASCON:; [$eac6]
[eac6].byte $00; Exiting left from Apolune screen 0
[eac7].byte AREA_APOLUNE; |-> Switch to area 1
[eac8].byte $07; |-> Screen index 6
[eac9].byte $7e; |-> Start position Y=7, X=14
[eaca].byte PALETTE_OUTSIDE; '-> Palette 6
[eacb].byte $01; Exit right from Apolune screen 1
[eacc].byte AREA_APOLUNE; |-> Switch to area 1
[eacd].byte $08; |-> Screen index 8
[eace].byte $71; |-> Start position Y=7, X=1
[eacf].byte PALETTE_OUTSIDE; '-> Palette 6
[ead0].byte $02; Exiting left from Forepaw to Trunk (screen 2)
[ead1].byte AREA_APOLUNE; |-> Switch to area 1
[ead2].byte $1a; |-> Screen index 26
[ead3].byte $7e; |-> Start position X=14, Y=7
[ead4].byte PALETTE_OUTSIDE; '-> Palette 6
[ead5].byte $03; Exiting right from Forepaw to Branch (screen 3)
[ead6].byte AREA_APOLUNE; |-> Switch to area 3
[ead7].byte $1d; |-> Screen index 29
[ead8].byte $71; |-> Start position X=1, Y=7
[ead9].byte PALETTE_OUTSIDE; |-> Palette 6
[eada].byte $04; Exiting left from Mascon to Mist (screen 4)
[eadb].byte AREA_FOREPAW; |-> Switch to area 2
[eadc].byte $09; |-> Screen index 9
[eadd].byte $9e; |-> Start position X=14, Y=9
[eade].byte PALETTE_MIST; '-> Palette 10
[eadf].byte $05; Exiting right from Mascon to Mist (screen 5)
[eae0].byte AREA_FOREPAW; |-> Switch to area 2
[eae1].byte $0c; |-> Screen index 12
[eae2].byte $91; |-> Start position X=1, Y=9
[eae3].byte PALETTE_MIST; '-> Palette 10
[eae4].byte $06; Exit left from Victim to Mist from screen 6
[eae5].byte AREA_FOREPAW; |-> Switch to area 2
[eae6].byte $22; |-> Screen index 34
[eae7].byte $9e; |-> Start position X=14, Y=9
[eae8].byte PALETTE_MIST; '-> Palette 10
[eae9].byte $07; Exit right from Victim to Mist on screen 7
[eaea].byte AREA_FOREPAW; |-> Switch to area 7
[eaeb].byte $25; |-> Screen index = 37
[eaec].byte $91; |-> Start position X=1, Y=9
[eaed].byte PALETTE_MIST; '-> Palette 10
[eaee].byte $08; Exit left from Conflate to Branch
[eaef].byte AREA_CONFLATE; |-> Switch to area 5
[eaf0].byte $0d; |-> Screen index 13
[eaf1].byte $7e; |-> Start position X=14, Y=7
[eaf2].byte PALETTE_BRANCH; '-> Palette 8
[eaf3].byte $0a; Exit left from Daybreak to Branch
[eaf4].byte AREA_CONFLATE; |-> Switch to area 5
[eaf5].byte $23; |-> Screen index 35
[eaf6].byte $7e; |-> Start position X=14, Y=7
[eaf7].byte PALETTE_BRANCH; '-> Palette 8
[eaf8].byte $0b; Exit right from Daybreak to Branch
[eaf9].byte AREA_CONFLATE; |-> Switch to area 5
[eafa].byte $24; |-> Screen index 36
[eafb].byte $71; |-> Start position X=1, Y=7
[eafc].byte PALETTE_BRANCH; '-> Palette 8
[eafd].byte $0c; Exit left from Dartmoor to Branch (screen 12)
[eafe].byte AREA_DAYBREAK; |-> Switch to area 6
[eaff].byte $03; |-> Screen index 3
[eb00].byte $7e; |-> Start position X=14, Y=7
[eb01].byte PALETTE_DARTMOOR; '-> Palette 12
[eb02].byte $ff; [$eb02] byte
[eb03]TOWN_TRANSITIONS_FOREPAW:; [$eb03]
[eb03].byte $09; Entering Mascon from Mist (screen 9)
[eb04].byte AREA_MASCON; |-> Switch to area 3
[eb05].byte $04; |-> Screen index 4
[eb06].byte $91; |-> Start position X=1, Y=9
[eb07].byte PALETTE_TOWN; '-> Palette 27
[eb08].byte $0c; Entering left to Mascon from Mist
[eb09].byte AREA_MASCON; |-> Switch to area 3
[eb0a].byte $05; |-> Screen index 5
[eb0b].byte $9e; |-> Start position X=14, Y=9
[eb0c].byte PALETTE_TOWN; '-> Palette 27
[eb0d].byte $22; Enering right to Victim from Mist (screen 34)
[eb0e].byte AREA_MASCON; |-> Switch to area 3
[eb0f].byte $06; |-> Screen index 6
[eb10].byte $91; |-> Start position X=1, Y=9
[eb11].byte PALETTE_TOWN; '-> Palette 27
[eb12].byte $25; Entering left to Victim from Mist (screen 37)
[eb13].byte AREA_MASCON; |-> Switch to area 3
[eb14].byte $07; |-> Screen index 7
[eb15].byte $9e; |-> Start position X=14, Y=9
[eb16].byte PALETTE_TOWN; '-> Palette 27
[eb17].byte $ff; [$eb17] byte
[eb18]TOWN_TRANSITIONS_CONFLATE:; [$eb18]
[eb18].byte $0d; Entering right to Conflate from Branch (screen 13)
[eb19].byte AREA_MASCON; |-> Switch to area 3
[eb1a].byte $08; |-> Screen index 8
[eb1b].byte $91; |-> Start position X=1, Y=9
[eb1c].byte PALETTE_TOWN; '-> Palette 27
[eb1d].byte $23; Entering Daybreak from Branch (screen 35)
[eb1e].byte AREA_MASCON; |-> Switch to area 3
[eb1f].byte $0a; |-> Screen index 10
[eb20].byte $92; |-> Start position X=2, Y=9
[eb21].byte PALETTE_TOWN; '-> Palette 27
[eb22].byte $24; Entering Daybreak from Branch (screen 36)
[eb23].byte AREA_MASCON; |-> Switch to area 3
[eb24].byte $0b; |-> Screen index 11
[eb25].byte $9e; |-> Start position X=14, Y=9
[eb26].byte PALETTE_TOWN; '-> Palette 27
[eb27].byte $ff; [$eb27] byte
[eb28]TOWN_TRANSITIONS_DAYBREAK:; [$eb28]
[eb28].byte $03; Entering right to Dartmoor from Branch (screen 3)
[eb29].byte AREA_MASCON; |-> Switch to area 3
[eb2a].byte $0c; |-> Screen index 12
[eb2b].byte $92; |-> Start position X=2, Y=9
[eb2c].byte PALETTE_TOWN; '-> Palette 27
[eb2d].byte $ff; [$eb2d] byte
[eb2e]TOWN_TRANSITIONS_EMPTY:; [$eb2e]
[eb2e].byte $ff; [$eb2e] 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]
[eb2f]LDA a:CurrentDoor_KeyRequirement
[eb32]BEQ @_return
[eb34]ASL A
[eb35]TAY
[eb36]LDA DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS+1,Y
[eb39]PHA
[eb3a]LDA DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS,Y
[eb3d]PHA
[eb3e]@_return:; [$eb3e]
[eb3e]RTS
;============================================================================
; A mapping of door requirement lookup function addresses.
;
; XREFS:
;
Game_RunDoorRequirementHandler
;============================================================================
[eb3f]DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS:; [$eb3f]
[eb3f].byte $3d; [0]: No key, return
[eb40]DOOR_REQUIREMENT_LOOKUP_FUNC_ADDRS_1:; [$eb40]
[eb40].byte $eb; [0]:
[eb41].byte $50; [1]: "A" Key
[eb42].byte $eb; [1]:
[eb43].byte $60; [2]: "K" Key
[eb44].byte $eb; [2]:
[eb45].byte $70; [3]: "Q" Key
[eb46].byte $eb; [3]:
[eb47].byte $80; [4]: "J" Key
[eb48].byte $eb; [4]:
[eb49].byte $90; [5]: "Jo" Key
[eb4a].byte $eb; [5]:
[eb4b].byte $a0; [6]: Ring of Elf
[eb4c].byte $eb; [6]:
[eb4d].byte $b0; [7]: Ring of Dworf
[eb4e].byte $eb; [7]:
[eb4f].byte $c0; [8]: Demon's Ring
[eb50].byte $eb; [8]:
;============================================================================
; 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]
[eb51]LDA a:SelectedItem; Load the selected item.
[eb54]CMP #$04; Is this the "A" Key?
[eb56]BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb58]LDA #$7d; 0x7D == "A" Key required IScript.
[eb5a]JSR MMC1_LoadBankAndJump; Run the IScript:
[eb5d].byte BANK_12_LOGIC; Bank = 12
[eb5e].word IScripts_Begin-1; Address = IScripts_Begin
[eb60]@_afterFarJump:; [$eb60]
[eb60]RTS
;============================================================================
; 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]
[eb61]LDA a:SelectedItem; Load the selected item.
[eb64]CMP #$05; Is this the "K" Key?
[eb66]BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb68]LDA #$7c; 0x7C == "K" Key required IScript.
[eb6a]JSR MMC1_LoadBankAndJump; Run the IScript:
[eb6d].byte BANK_12_LOGIC; Bank = 12
[eb6e].word IScripts_Begin-1; Address = IScripts_Begin
[eb70]@_afterFarJump:; [$eb70]
[eb70]RTS
;============================================================================
; 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]
[eb71]LDA a:SelectedItem; Load the selected item.
[eb74]CMP #$06; Is this the "Q" Key?
[eb76]BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb78]LDA #$7b; 0x7B == "Q" Key required IScript.
[eb7a]JSR MMC1_LoadBankAndJump; Run the IScript:
[eb7d].byte BANK_12_LOGIC; Bank = 12
[eb7e].word IScripts_Begin-1; Address = IScripts_Begin
[eb80]@_afterFarJump:; [$eb80]
[eb80]RTS
;============================================================================
; 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]
[eb81]LDA a:SelectedItem; Load the selected item.
[eb84]CMP #$07; Is this the "J" Key?
[eb86]BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb88]LDA #$02; 0x02 == "J" Key required IScript.
[eb8a]JSR MMC1_LoadBankAndJump; Run the IScript:
[eb8d].byte BANK_12_LOGIC; Bank = 12
[eb8e].word IScripts_Begin-1; Address = IScripts_Begin
[eb90]@_afterFarJump:; [$eb90]
[eb90]RTS
;============================================================================
; 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]
[eb91]LDA a:SelectedItem; Load the selected item.
[eb94]CMP #$08; Is this the "Jo" Key?
[eb96]BEQ Game_UnlockDoorWithUsableItem; If so, unlock the door and enter.
[eb98]LDA #$7e; 0x7E == "Jo" Key required IScript.
[eb9a]JSR MMC1_LoadBankAndJump; Run the IScript:
[eb9d].byte BANK_12_LOGIC; Bank = 12
[eb9e].word IScripts_Begin-1; Address = IScripts_Begin
[eba0]@_afterFarJump:; [$eba0]
[eba0]RTS
;============================================================================
; 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]
[eba1]LDA a:SpecialItems; Load the special items.
[eba4]AND #$80; Does the player have the Ring of Elf?
[eba6]BNE Game_UnlockDoor; If so, unlock the door and enter.
[eba8]LDA #$7f; 0x7F == Ring required IScript.
[ebaa]JSR MMC1_LoadBankAndJump; Run the IScript:
[ebad].byte BANK_12_LOGIC; Bank = 12
[ebae].word IScripts_Begin-1; Address = IScripts_Begin
[ebb0]@_afterFarJump:; [$ebb0]
[ebb0]RTS
;============================================================================
; 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]
[ebb1]LDA a:SpecialItems; Load the special items.
[ebb4]AND #$20; Does the player have the Ring of Dworf?
[ebb6]BNE Game_UnlockDoor; If so, unlock the door and enter.
[ebb8]LDA #$7f; 0x7F == Ring required IScript.
[ebba]JSR MMC1_LoadBankAndJump; Run the IScript:
[ebbd].byte BANK_12_LOGIC; Bank = 12
[ebbe].word IScripts_Begin-1; Address = IScripts_Begin
[ebc0]@_afterFarJump:; [$ebc0]
[ebc0]RTS
;============================================================================
; 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]
[ebc1]LDA a:SpecialItems; Load the special items.
[ebc4]AND #$10; Does the player have the Demon's Ring?
[ebc6]BNE Game_UnlockDoor; If so, unlock the door and enter.
[ebc8]LDA #$7f; 0x7F == Ring required IScript.
[ebca]JSR MMC1_LoadBankAndJump; Run the IScript:
[ebcd].byte BANK_12_LOGIC; Bank = 12
[ebce].word IScripts_Begin-1; Address = IScripts_Begin
[ebd0]@_afterFarJump:; [$ebd0]
[ebd0]RTS
[ebd1]Game_UnlockDoorWithUsableItem:; [$ebd1]
[ebd1]LDA #$84; 0x84 == Used key IScript.
[ebd3]JSR MMC1_LoadBankAndJump; Run the IScript:
[ebd6].byte BANK_12_LOGIC; Bank = 12
[ebd7].word IScripts_Begin-1; Address = IScripts_Begin
[ebd9]@_afterFarJump:; [$ebd9]
[ebd9]LDA #$ff
[ebdb]STA a:SelectedItem
[ebde]JSR UI_ClearSelectedItemPic
[ebe1]Game_UnlockDoor:; [$ebe1]
[ebe1]LDA #$00
[ebe3]STA a:CurrentDoor_KeyRequirement
[ebe6]LDA #$84
[ebe8]LDA #$06
[ebea]JSR Sound_PlayEffect
[ebed]RTS
[ebee]Player_DrawBody:; [$ebee]
[ebee]LDA a:IScript_PortraitID
[ebf1]BMI @LAB_PRG15_MIRROR__ebf4
[ebf3]RTS
;
; 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]
[ebf4]LDA Screen_ReadyState
[ebf6]CMP #$01
[ebf8]BEQ @LAB_PRG15_MIRROR__ec11
[ebfa]CMP #$05
[ebfc]BEQ @LAB_PRG15_MIRROR__ec11
;
; Ths code path should always be hit when this function is
; called.
;
[ebfe]LDA Player_PosX_Block
[ec00]STA Arg_DrawSprite_PosX
[ec02]LDA Screen_Maybe_ScrollXCounter
[ec04]STA Unused_Sprite_ScrollPosX
[ec06]LDA Player_PosY
[ec08]STA Arg_DrawSprite_PosY
[ec0a]LDA Player_Something_ScrollPosY
[ec0c]STA Unused_Sprite_ScrollPosY
[ec0e]JMP @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]
[ec11]LDA Maybe_PlayerX_ForScroll
[ec13]STA Arg_DrawSprite_PosX
[ec15]LDA PPU_ScrollScreenHoriz
[ec17]STA Unused_Sprite_ScrollPosX
[ec19]LDA Maybe_PlayerY_ForScroll
[ec1b]STA Arg_DrawSprite_PosY
[ec1d]LDA PPU_ScrollScreenVert
[ec1f]STA Unused_Sprite_ScrollPosY
[ec21]@LAB_PRG15_MIRROR__ec21:; [$ec21]
[ec21]JSR Player_CalculateVisibility
[ec24]JSR Player_SetFacingLeft
[ec27]JSR Player_GetBodySpriteFrameOffset
[ec2a]PHA
[ec2b]LDA a:SelectedArmor
[ec2e]ASL A
[ec2f]STA Temp_00
[ec31]LDA a:SelectedShield
[ec34]CMP #$03
[ec36]LDA #$00
[ec38]ROL A
[ec39]EOR #$01
[ec3b]ORA Temp_00
[ec3d]TAX
[ec3e]PLA
[ec3f]CLC
[ec40]ADC PLAYER_BODY_TILEINFO_START_OFFSETS,X
[ec43]JSR Sprite_SetPlayerAppearanceAddr
[ec46]JMP FUN_PRG15_MIRROR__ec58
[ec49]PLAYER_BODY_TILEINFO_START_OFFSETS:; [$ec49]
[ec49].byte $00; [0]: Leather Armor
[ec4a].byte $08; [1]: Leather Armor + Shield
[ec4b].byte $10; [2]: Studded Mail
[ec4c].byte $18; [3]: Studded Mail + Shield
[ec4d].byte $20; [4]: Full Plate
[ec4e].byte $28; [5]: Full Plate + Shield
[ec4f].byte $30; [6]: Battle Suit
[ec50].byte $38; [7]: Battle Suit + Shield
;============================================================================
; TODO: Document Player_SetFacingLeft
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ec51]Player_SetFacingLeft:; [$ec51]
[ec51]LDA Player_Flags
[ec53]AND #$40
[ec55]STA CurrentSprite_FlipMask
[ec57]RTS
;============================================================================
; TODO: Document FUN_PRG15_MIRROR__ec58
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ec58]FUN_PRG15_MIRROR__ec58:; [$ec58]
[ec58]LDA Player_Flags
[ec5a]BPL @_return
[ec5c]LDA PlayerHitsPhaseCounter
[ec5e]CMP #$02
[ec60]BNE @_return
[ec62]LDY #$00
[ec64]LDA Player_Flags
[ec66]AND #$40
[ec68]BEQ @LAB_PRG15_MIRROR__ec6b
[ec6a]INY
[ec6b]@LAB_PRG15_MIRROR__ec6b:; [$ec6b]
[ec6b]LDA BYTE_ARRAY_PRG15_MIRROR__eca2,Y
[ec6e]JSR Player_CalcValueAndFFForNeg
[ec71]LDA Player_PosX_Block
[ec73]CLC
[ec74]ADC Temp_04
[ec76]STA Arg_DrawSprite_PosX
[ec78]LDA Player_PosX_Block
[ec7a]ADC Temp_05
[ec7c]CMP Player_PosX_Block
[ec7e]BNE @_return
[ec80]LDA Player_PosY
[ec82]STA Arg_DrawSprite_PosY
[ec84]JSR Player_CalculateVisibility
[ec87]LDA a:SelectedArmor
[ec8a]ASL A
[ec8b]STA Temp_00
[ec8d]LDY #$00
[ec8f]LDA a:SelectedShield
[ec92]CMP #$03
[ec94]BCS @LAB_PRG15_MIRROR__ec97
[ec96]INY
[ec97]@LAB_PRG15_MIRROR__ec97:; [$ec97]
[ec97]TYA
[ec98]ORA Temp_00
[ec9a]TAY
[ec9b]LDA BYTE_ARRAY_PRG15_MIRROR__eca4,Y
[ec9e]JMP Sprite_SetPlayerAppearanceAddr
[eca1]@_return:; [$eca1]
[eca1]RTS
[eca2]BYTE_ARRAY_PRG15_MIRROR__eca2:; [$eca2]
[eca2].byte $f8; [0]:
[eca3]BYTE_ARRAY_PRG15_MIRROR__eca2_1_:; [$eca3]
[eca3].byte $10; [1]:
[eca4]BYTE_ARRAY_PRG15_MIRROR__eca4:; [$eca4]
[eca4].byte $63; [0]: Leather Armor
[eca5].byte $67; [1]: Leather Armor + Shield
[eca6].byte $64; [2]: Studded Mail
[eca7].byte $68; [3]: Studded Mail + Shield
[eca8].byte $65; [4]: Full Plate
[eca9].byte $69; [5]: Full Plate + Shield
[ecaa].byte $66; [6]: Battle Suit
[ecab].byte $6a; [7]: Battle Suit + Shield
;============================================================================
; TODO: Document Player_GetBodySpriteFrameOffset
;
; INPUTS:
; None.
;
; OUTPUTS:
; A
;
; XREFS:
;
Player_DrawBody
;============================================================================
[ecac]Player_GetBodySpriteFrameOffset:; [$ecac]
[ecac]LDA Player_Flags
[ecae]LSR A
[ecaf]BCC @LAB_PRG15_MIRROR__ecb8
[ecb1]LDA Player_Flags
[ecb3]BMI @LAB_PRG15_MIRROR__eccc
[ecb5]LDA #$03
[ecb7]RTS
[ecb8]@LAB_PRG15_MIRROR__ecb8:; [$ecb8]
[ecb8]JSR Player_IsClimbing
[ecbb]BCC @LAB_PRG15_MIRROR__ecc8
[ecbd]LDA Player_MovementTick
[ecbf]AND #$10
[ecc1]ASL A
[ecc2]ASL A
[ecc3]STA CurrentSprite_FlipMask
[ecc5]LDA #$07
[ecc7]RTS
[ecc8]@LAB_PRG15_MIRROR__ecc8:; [$ecc8]
[ecc8]LDA Player_Flags
[ecca]BPL @LAB_PRG15_MIRROR__ecd2
[eccc]@LAB_PRG15_MIRROR__eccc:; [$eccc]
[eccc]LDX PlayerHitsPhaseCounter
[ecce]LDA BYTE_ARRAY_PRG15_MIRROR__ecf3,X
[ecd1]RTS
[ecd2]@LAB_PRG15_MIRROR__ecd2:; [$ecd2]
[ecd2]LDA Player_StatusFlag
[ecd4]BPL @LAB_PRG15_MIRROR__ecdd
[ecd6]LDA Joy1_ButtonMask
[ecd8]BPL @LAB_PRG15_MIRROR__ecdd
[ecda]LDA #$03
[ecdc]RTS
[ecdd]@LAB_PRG15_MIRROR__ecdd:; [$ecdd]
[ecdd]LDA Player_Flags
[ecdf]AND #$20
[ece1]BEQ @LAB_PRG15_MIRROR__ecea
[ece3]LDA Player_MovementTick
[ece5]LSR A
[ece6]LSR A
[ece7]LSR A
[ece8]AND #$03
[ecea]@LAB_PRG15_MIRROR__ecea:; [$ecea]
[ecea]TAX
[eceb]LDA BYTE_ARRAY_PRG15_MIRROR__ecef,X
[ecee]RTS
[ecef]BYTE_ARRAY_PRG15_MIRROR__ecef:; [$ecef]
[ecef].byte $00; [0]:
[ecf0].byte $01; [1]:
[ecf1].byte $02; [2]:
[ecf2].byte $01; [3]:
[ecf3]BYTE_ARRAY_PRG15_MIRROR__ecf3:; [$ecf3]
[ecf3].byte $04; [0]:
[ecf4].byte $05; [1]:
[ecf5].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_ContinueHandleClimbOrJump
;
Player_GetBodySpriteFrameOffset
;============================================================================
[ecf6]Player_IsClimbing:; [$ecf6]
;
; Check if the player can currently climb an available ladder.
;
[ecf6]LDA Player_Flags; Load the player's flags.
[ecf8]AND #$08; Can the player currently climb?
[ecfa]BEQ @_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]LDA Player_PosX_Block; Load the player's X position.
[ecfe]AND #$0f; Is it on a block boundary?
[ed00]BEQ @_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]LDA Player_Flags; Load the player's flags.
[ed04]AND #$04; Is the player currently falling?
[ed06]BNE @_returnFalse; If so, return false.
;
; Check if the player is currently climbing.
;
[ed08]@_checkClimbing:; [$ed08]
[ed08]LDA Player_Flags; Load the player's flags.
[ed0a]AND #$10; Is the player climbing?
[ed0c]BEQ @_returnFalse; If not, return false.
[ed0e]SEC; Set C = 1 to return true.
[ed0f]RTS
[ed10]@_returnFalse:; [$ed10]
[ed10]CLC; Set C = 0 to return false.
[ed11]RTS
[ed12]Player_DrawSpriteImmediately:; [$ed12]
;
; Load and draw the player's sprite.
;
[ed12]JSR Player_LoadArmorTilesToPPU; Load the armor sprite information.
[ed15]@_drawArmorLoop:; [$ed15]
[ed15]JSR Player_LoadArmorTile; Draw a tile of armor.
[ed18]JSR PPUBuffer_Draw; Draw to the PPU.
[ed1b]INC Player_DrawTileReadOffset; Increment the read offset.
[ed1d]DEC Temp_00; Decrement the tile count.
[ed1f]BPL @_drawArmorLoop; If >= 0, loop.
;
; Load and draw the weapon sprite, if one is equipped.
;
[ed21]JSR Player_LoadWeaponTilesToPPU; Load the weapon sprite information.
[ed24]LDA Player_DrawTileReadOffset; Get the tile read offset.
[ed26]BMI @_loadShield; If < 0, skip weapon drawing.
;
; The player has a weapon equipped. Draw the tiles.
;
[ed28]@_drawWeaponLoop:; [$ed28]
[ed28]JSR Player_LoadWeaponTile; Draw a tile of the weapon.
[ed2b]JSR PPUBuffer_Draw; Draw to the PPU.
[ed2e]INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed30]DEC Temp_00; Decrement the tile count.
[ed32]BPL @_drawWeaponLoop; If >= 0, loop.
;
; Load and draw the shield sprite.
;
[ed34]@_loadShield:; [$ed34]
[ed34]JSR Player_LoadShieldTilesToPPU; Load the shield sprite information.
[ed37]LDA Player_DrawTileReadOffset; Load the tile read offset.
[ed39]BMI @_return; If < 0 (unset), return.
[ed3b]@_drawShieldLoop:; [$ed3b]
[ed3b]JSR Player_LoadShieldTile; Draw a tile of the shield.
[ed3e]INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed40]DEC Temp_00; Decrement the tile count.
[ed42]BPL @_drawShieldLoop; If >= 0, loop.
[ed44]@_return:; [$ed44]
[ed44]RTS
[ed45]Player_DrawSprite:; [$ed45]
;
; Load and draw the player's sprite.
;
[ed45]JSR Player_LoadArmorTilesToPPU; Load the armor sprite information.
[ed48]@_drawArmorLoop:; [$ed48]
[ed48]JSR Player_LoadArmorTile; Draw a tile of armor.
[ed4b]INC Player_DrawTileReadOffset; Increment the read offset.
[ed4d]DEC Temp_00; Decrement the tile count.
[ed4f]BPL @_drawArmorLoop; If >= 0, loop.
;
; Load and draw the weapon sprite, if one is equipped.
;
[ed51]JSR Player_LoadWeaponTilesToPPU; Load the weapon sprite information.
[ed54]LDA Player_DrawTileReadOffset; Get the tile read offset.
[ed56]BMI @_loadShield; If < 0, skip weapon drawing.
;
; The player has a weapon equipped. Draw the tiles.
;
[ed58]@_drawWeaponLoop:; [$ed58]
[ed58]JSR Player_LoadWeaponTile; Draw a tile of the weapon.
[ed5b]INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed5d]DEC Temp_00; Decrement the tile count.
[ed5f]BPL @_drawWeaponLoop; If >= 0, loop.
;
; Load and draw the shield sprite.
;
[ed61]@_loadShield:; [$ed61]
[ed61]JSR Player_LoadShieldTilesToPPU; Load the shield sprite information.
[ed64]LDA Player_DrawTileReadOffset; Load the tile read offset.
[ed66]BMI @_return; If < 0 (unset), return.
[ed68]@_drawShieldLoop:; [$ed68]
[ed68]JSR Player_LoadShieldTile; Draw a tile of the shield.
[ed6b]INC Player_DrawTileReadOffset; Increment the tile read offset.
[ed6d]DEC Temp_00; Decrement the tile count.
[ed6f]BPL @_drawShieldLoop; If >= 0, loop.
[ed71]@_return:; [$ed71]
[ed71]RTS
[ed72]Player_LoadArmorTilesToPPU:; [$ed72]
;
; Clear initial state for drawing the armor.
;
[ed72]LDA #$00
[ed74]STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[ed76]STA Player_SpriteSegmentPPUAddr_L; Set the sprite segment PPU addresses to 0.
[ed78]STA 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]LDA a:SelectedArmor; Load the selected armor.
[ed7d]ASL A; Convert to a word boundary.
[ed7e]LDY a:SelectedShield; Load the selected shield.
[ed81]CPY #$03; Is it the Battle Helmet?
[ed83]BCS @_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]ORA #$01; Offset the lookup index to include armor + shield tiles.
[ed87]@_loadArmor:; [$ed87]
[ed87]STA Player_ArmorLookupIndex; Store the lookup index based on the armor word boundary and possibly the shield bit.
[ed89]TAX; 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]LDA PLAYER_ARMOR_TILE_COUNTS,X; Load the tile count.
[ed8d]STA 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]TXA; A = Lookup index.
[ed90]ASL A; Multiply by 2 (so index considers armor and armor + shield entries).
[ed91]TAY; Y = result.
[ed92]JMP 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]
[ed95].byte $33; [0]: Leather Armor (51 tiles)
[ed96].byte $27; [1]: Leather Armor + Shield (39 tiles)
[ed97].byte $33; [2]: Studded Mail (51 tiles)
[ed98].byte $27; [3]: Studded Mail + Shield (39 tiles)
[ed99].byte $34; [4]: Full Plate (52 tiles)
[ed9a].byte $28; [5]: Full Plate + Shield (40 tiles)
[ed9b].byte $32; [6]: Battle Helmet (50 tiles)
[ed9c].byte $32; [7]: Battle Helmet + Shield (50 tiles)
[ed9d]Player_LoadWeaponTilesToPPU:; [$ed9d]
[ed9d]LDA #$00
[ed9f]STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[eda1]LDA a:Player_CurWeapon; Load the selected weapon.
[eda4]CMP #$ff; Is it 0xFF (unset)?
[eda6]BNE @_loadWeaponTiles; If not, jump to load tiles.
;
; No sword is equipped. Set the offset to 0xFF, to disable
; drawing.
;
[eda8]STA Player_DrawTileReadOffset; Set the read offset to 0xFF.
[edaa]RTS
;
; 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]
[edab]TAX; X = weapon.
[edac]LDA PLAYER_WEAPON_TILE_COUNTS,X; Load the tile count.
[edaf]STA Temp_00; Store it for the parent caller to use.
;
; Set the PPU address to load tiles into and load them.
;
[edb1]TXA; X = tile count.
[edb2]ASL A; Convert to a word boundary.
[edb3]TAY; Y = resulting index.
[edb4]LDA PLAYER_WEAPON_TILE_PPU_ADDRS,Y; Load the lower byte of the target PPU address for weapons.
[edb7]STA Player_SpriteSegmentPPUAddr_L; Set it for load.
[edb9]LDA PLAYER_WEAPON_TILE_PPU_ADDRS+1,Y; Load the upper byte of the target PPU address for weapons.
[edbc]STA Player_SpriteSegmentPPUAddr_U; Set it for load.
[edbe]JMP 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]
[edc1].byte $02; [0]: Dagger
[edc2].byte $05; [1]: Long Sword
[edc3].byte $06; [2]: Giant Blade
[edc4].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]
[edc5].word $0380; [0]: Dagger
[edc7].word $0380; [1]: Long Sword
[edc9].word $0380; [2]: Giant Blade
[edcb].word $0340; [3]: Dragon Slayer
[edcd]Player_LoadShieldTilesToPPU:; [$edcd]
[edcd]LDA #$00
[edcf]STA Player_DrawTileReadOffset; Set the tile read offset to 0.
[edd1]LDA a:SelectedShield; Load the selected shield.
[edd4]CMP #$03; Is it the Battle Helmet (or unset -- 0xFF)?
[edd6]BCC @_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]STA Player_DrawTileReadOffset; Set the read offset to the shield value. This will be 3 or 0xFF.
[edda]RTS; And return.
;
; An actual shield is equipped. Begin setting the lookup
; index for the shield and a tile count of 5.
;
[eddb]@_loadShieldTiles:; [$eddb]
[eddb]ASL A; Convert the shield index to a word value for lookup.
[eddc]TAY; Y = resulting index.
[eddd]LDA #$05; 5 tiles.
[eddf]STA Temp_00; Set as the tile count for the load loop.
;
; Set the PPU address to load into to 0x0300.
;
[ede1]LDA #$00
[ede3]STA Player_SpriteSegmentPPUAddr_L; Set the lower byte of the PPU address to 0.
[ede5]LDA #$03
[ede7]STA Player_SpriteSegmentPPUAddr_U; And upper to 3.
[ede9]JMP 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]
[edec]PHA; 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]LDA Area_CurrentArea; Set A = current area.
[edef]CMP #$04; Is this the building?
[edf1]BEQ @_cannotEquip; If so, branch.
;
; The weapon can be equipped. Equip it and redraw the player.
;
[edf3]PLA; Pop the weapon from the stack.
[edf4]STA a:SelectedWeapon; Set as the current weapon in the inventory.
[edf7]STA a:Player_CurWeapon; Store as the current weapon.
[edfa]JSR Player_DrawSprite; Draw the player sprite.
[edfd]CLC; Clear Carry.
[edfe]RTS
;
; The weapon cannot currently be equipped. Set the weapon
; but do not draw.
;
[edff]@_cannotEquip:; [$edff]
[edff]PLA; Pop the weapon from the stack.
[ee00]STA a:SelectedWeapon; Set as the current weapon.
[ee03]CLC; Clear Carry.
[ee04]RTS
;============================================================================
; 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]
[ee05]STA a:SelectedArmor; Set the selected armor.
[ee08]JSR Player_DrawSprite; Draw the player sprite.
[ee0b]CLC; Clear Carry.
[ee0c]RTS
;============================================================================
; 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]
[ee0d]STA a:SelectedShield; Set the selected shield.
[ee10]JSR Player_DrawSprite; Draw the player sprite.
[ee13]CLC; Clear Carry.
[ee14]RTS
;============================================================================
; 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:; [$ee15]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee15]LDA a:CurrentROMBank; Load the current ROM bank.
[ee18]PHA; Push the bank to the stack.
[ee19]LDX #$08; 8 = Sprites bank.
[ee1b]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of armor types to tile IDs.
;
[ee1e]LDA a:TILES_ARMOR_ADDRS_INDEX_REF; Load the lower byte of the address of the armor index.
[ee21]STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee23]LDA a:TILES_ARMOR_ADDRS_INDEX_REF+1; Load the upper byte.
[ee26]CLC
[ee27]ADC #$80; Add 0x80 to the upper byte.
[ee29]STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the armor type from the index.
;
[ee2b]LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee2d]PHA; Push it to the stack.
[ee2e]INY; Increment the offset.
[ee2f]LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee31]CLC
[ee32]ADC #$80; Add 0x80 to it.
[ee34]STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee36]PLA; Pop the lower byte.
[ee37]STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee39]PLA; Pop the saved ROM bank from the stack.
[ee3a]TAX; Set it to X
[ee3b]JSR MMC1_UpdateROMBank; And switch back to it.
[ee3e]RTS
;============================================================================
; 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:; [$ee3f]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee3f]LDA a:CurrentROMBank; Load the current ROM bank.
[ee42]PHA; Push the bank to the stack.
[ee43]LDX #$08; 8 = Sprites bank.
[ee45]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of weapon types to tile IDs.
;
[ee48]LDA a:TILES_WEAPON_ADDRS_INDEX_REF; Load the lower byte of the address of the weapon index.
[ee4b]STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee4d]LDA a:TILES_WEAPON_ADDRS_INDEX_REF+1; Load the upper byte.
[ee50]CLC
[ee51]ADC #$80; Add 0x80 to the upper byte.
[ee53]STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the weapon type from the
; index.
;
[ee55]LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee57]PHA; Push it to the stack.
[ee58]INY; Increment the offset.
[ee59]LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee5b]CLC
[ee5c]ADC #$80; Add 0x80 to it.
[ee5e]STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee60]PLA; Pop the lower byte.
[ee61]STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee63]PLA; Pop the saved ROM bank from the stack.
[ee64]TAX; Set it to X
[ee65]JSR MMC1_UpdateROMBank; And switch back to it.
[ee68]RTS
;============================================================================
; 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:; [$ee69]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee69]LDA a:CurrentROMBank; Load the current ROM bank.
[ee6c]PHA; Push the bank to the stack.
[ee6d]LDX #$08; 8 = Sprites bank.
[ee6f]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address of the index of shield types to tile IDs.
;
[ee72]LDA a:TILES_SHIELDS_ADDRS_INDEX_REF; Load the lower byte of the address of the shield index.
[ee75]STA Player_SpriteTileReadAddr_L; Set as the lower byte.
[ee77]LDA a:TILES_SHIELDS_ADDRS_INDEX_REF+1; Load the upper byte.
[ee7a]CLC
[ee7b]ADC #$80; Add 0x80 to the upper byte.
[ee7d]STA Player_SpriteTileReadAddr_U; And set it as the upper byte.
;
; Load the tiles start address of the shield type from the index.
;
[ee7f]LDA (Player_SpriteTileReadAddr_L),Y; Read the lower byte of the tiles start address.
[ee81]PHA; Push it to the stack.
[ee82]INY; Increment the offset.
[ee83]LDA (Player_SpriteTileReadAddr_L),Y; Read the upper byte of the tiles start address.
[ee85]CLC
[ee86]ADC #$80; Add 0x80 to it.
[ee88]STA Player_SpriteTileReadAddr_U; And store as the upper byte of the address.
[ee8a]PLA; Pop the lower byte.
[ee8b]STA Player_SpriteTileReadAddr_L; And store it as the lower byte of the address.
;
; Switch back to the saved bank.
;
[ee8d]PLA; Pop the saved ROM bank from the stack.
[ee8e]TAX; Set it to X.
[ee8f]JSR MMC1_UpdateROMBank; And switch back to it.
[ee92]RTS
[ee93]Player_LoadShieldTile:; [$ee93]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[ee93]LDA a:CurrentROMBank; Load the current ROM bank.
[ee96]PHA; Push the bank to the stack.
[ee97]LDX #$08; 8 = Sprites bank.
[ee99]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the shield tile IDs.
;
[ee9c]LDA a:USHORT_800a; Load the lower byte of the tile IDs start address.
[ee9f]STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eea1]LDA a:USHORT_800a+1; Load the upper byte of the tile IDs start address.
[eea4]STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; Draw the shield tile.
;
[eea6]JMP Player_LoadSpriteTile; Draw the tile.
[eea9]Player_LoadArmorTile:; [$eea9]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[eea9]LDA a:CurrentROMBank; Load the current ROM bank.
[eeac]PHA; Push the bank to the stack.
[eead]LDX #$08; 8 = Sprites bank.
[eeaf]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the armor tile IDs.
;
[eeb2]LDA a:USHORT_8004; Load the lower byte of the tile IDs start address.
[eeb5]STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eeb7]LDA a:USHORT_8004+1; Load the upper byte of the tile IDs start address.
[eeba]STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; Draw the armor tile.
;
[eebc]JMP Player_LoadSpriteTile; Draw the tile.
[eebf]Player_LoadWeaponTile:; [$eebf]
;
; Save the current ROM bank and switch to bank 8 (sprites).
;
[eebf]LDA a:CurrentROMBank; Load the current ROM bank.
[eec2]PHA; Push the bank to the stack.
[eec3]LDX #$08; 8 = Sprites bank.
[eec5]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the address for the start of the weapon tile IDs.
;
[eec8]LDA a:TILES_WEAPONS_START_REF; Load the lower byte of the tile IDs start address.
[eecb]STA Temp_Addr_L; Store as the lower byte of the address to read from.
[eecd]LDA a:TILES_WEAPONS_START_REF+1; Load the upper byte of the tile IDs start address.
[eed0]STA Temp_Addr_U; Store as the upper byte of the address to read from.
;
; v-- Fall through --v
;
[eed2]Player_LoadSpriteTile:; [$eed2]
[eed2]PLA; Pop the saved ROM bank from the stack.
[eed3]TAX; Set it to X.
[eed4]JSR MMC1_UpdateROMBank; And switch back to it.
;
; And then save it again and switch away again, back to
; bank 8.
;
[eed7]LDA a:CurrentROMBank; Load the current ROM bank.
[eeda]PHA; Push it to the stack.
[eedb]LDX #$08; 8 = Sprites bank.
[eedd]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Prepare the read address for the tile.
;
[eee0]LDY Player_DrawTileReadOffset; Load the tile read offset.
[eee2]LDA #$00; A = 0
[eee4]STA 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]LDA (Player_SpriteTileReadAddr_L),Y; Load the tile ID at the offset.
[eee8]ASL A; Shift left, upper bit into Carry.
[eee9]ROL Temp_05; Rotate left, carry into bit 0, bit 7 into carry.
[eeeb]ASL A; Repeat 3 more times, moving lower nibble into A, upper nibble into Temp_05.
[eeec]ROL Temp_05
[eeee]ASL A
[eeef]ROL Temp_05
[eef1]ASL A
[eef2]ROL Temp_05; Final storage of the upper byte.
[eef4]STA Temp_04; Final storage of the lower byte.
;
; Restore the saved bank.
;
[eef6]PLA; Pop the saved ROM bank from the stack.
[eef7]TAX; Set it to X.
[eef8]JSR 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]LDA Temp_04; Load the lower byte from the normalized tile ID.
[eefd]CLC
[eefe]ADC Temp_Addr_L; Add to the start of the tile IDs.
[ef00]STA Temp_04; Store as the new lower byte.
;
; Now handle the upper byte of the address.
;
[ef02]LDA Temp_05; Load the upper byte from the normalized tile ID.
[ef04]ADC Temp_Addr_U; Add the upper byte of the address.
[ef06]CLC
[ef07]ADC #$80; Add 0x80.
[ef09]STA Temp_05; Store as the new upper byte.
;
; Switch bank to bank 8 (where this probably should have
; stayed all along).
;
[ef0b]LDA a:CurrentROMBank; Load the current bank again.
[ef0e]PHA; Push it to the stack.
[ef0f]LDX #$08; 8 = Sprites bank
[ef11]JSR MMC1_UpdateROMBank; Switch to it again.
;
; Set the target PPU address to write the tiles to for the
; PPU buffer draw command.
;
[ef14]LDA Player_SpriteSegmentPPUAddr_U; Load the upper byte of the PPU address of the sprite data.
[ef16]STA a:PPU_TargetAddr_U; Store as the upper byte to write to for the PPU draw command.
[ef19]LDA Player_SpriteSegmentPPUAddr_L; Load the lower byte.
[ef1b]STA a:PPU_TargetAddr; And store it.
;
; Queue 16 bytes (1 tile) to draw to the PPU.
;
[ef1e]LDA #$10; A = 16 bytes.
[ef20]JSR PPUBuffer_QueueCommandOrLength; Queue it as the draw length.
;
; Prepare a loop for writing bytes for the tile.
;
[ef23]LDY #$00; Y = 0 (loop counter/read offset).
[ef25]@_copyLoop:; [$ef25]
[ef25]LDA (Temp_04),Y; Load a byte of the tile.
[ef27]STA PPUBuffer,X; Write it to the PPU buffer.
[ef2a]INX; X++ (buffer position).
[ef2b]INY; Y++ (loop counter).
[ef2c]CPY #$10; Is it < 16?
[ef2e]BCC @_copyLoop; If so, loop.
;
; 16 bytes are written. Update the PPU buffer write offset
; and restore the bank.
;
[ef30]STX PPUBuffer_WriteOffset; Update the PPU buffer write offset.
[ef32]PLA; Pop the saved bank from the stack.
[ef33]TAX; Set in X.
[ef34]JSR MMC1_UpdateROMBank; And update to the bank.
;
; Increment the PPU address for the next tile.
;
[ef37]LDA Player_SpriteSegmentPPUAddr_L; Load the lower byte of the PPU address.
[ef39]CLC
[ef3a]ADC #$10; Add 16 (next tile).
[ef3c]STA Player_SpriteSegmentPPUAddr_L; Store it.
[ef3e]LDA Player_SpriteSegmentPPUAddr_U; Load the upper byte.
[ef40]ADC #$00; Add Carry (if the lower byte wrapped).
[ef42]STA Player_SpriteSegmentPPUAddr_U; And store it.
[ef44]RTS
;============================================================================
; DEAD CODE: Unset the special event ID.
;
; This is not used in the game.
;============================================================================
[ef45]UNUSED_ClearScreenSpecialEventID:; [$ef45]
[ef45]LDA #$ff
[ef47]STA a:CurrentScreen_SpecialEventID
[ef4a]RTS
;============================================================================
; 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:; [$ef4b]
;
; Check if this screen has a special event ID.
;
[ef4b]LDA a:CurrentScreen_SpecialEventID; Load the screen's special event ID.
[ef4e]CMP #$ff; Is it 0xFF?
[ef50]BEQ @_return; If so, nothing to run. Return.
;
; Check if there's an entry in the table.
;
[ef52]AND #$7f; Take the lower 7 bits.
[ef54]ASL A; Convert to a word boundary for the lookup table.
[ef55]CMP #$06; Is it >= 6 (out of bounds)?
[ef57]BCS @_return; If so, return.
;
; Load the address of the handler and push as the new address
; to run.
;
[ef59]TAY; Y = A
[ef5a]LDA SPECIAL_SCREEN_EVENT_LOOKUP_TABLE+1,Y; Load the lower byte of the handler address.
[ef5d]PHA; Push it to the stack.
[ef5e]LDA SPECIAL_SCREEN_EVENT_LOOKUP_TABLE,Y; Load the upper byte.
[ef61]PHA; Push it.
[ef62]@_return:; [$ef62]
[ef62]RTS
;============================================================================
; 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]
[ef63].byte $68; [0]: Handle pushable block on path to Mascon.
[ef64]SPECIAL_SCREEN_EVENT_LOOKUP_TABLE_1:; [$ef64]
[ef64].byte $ef; [0]:
[ef65].byte $9d; [1]: Handle a standard boss battle.
[ef66].byte $ef; [1]:
[ef67].byte $d3; [2]: Handle end-game sequence after killing the final boss.
[ef68].byte $ef; [2]:
;============================================================================
; 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:; [$ef69]
;
; Check if the path to Mascon has been opened.
;
[ef69]LDA a:Quests; Load the list of completed quests.
[ef6c]AND #$20; Is the Mascon path completed?
[ef6e]BEQ @_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]LDA #$ff
[ef72]STA a:CurrentScreen_SpecialEventID; Unset the special event ID.
[ef75]LDA #$01
[ef77]STA PathToMascon_Opening; Set the path to Mascon as opened.
[ef79]LDA #$56
[ef7b]STA PathToMascon_FountainCoverPos; Set the pushable block to X=6, Y=5.
[ef7d]LDX #$00
[ef7f]JMP 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]
[ef82]LDA #$ff
[ef84]STA a:CurrentScreen_SpecialEventID; Unset the special event ID.
[ef87]RTS
;============================================================================
; 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]
[ef88]STA Temp_00
[ef8a]LDX #$07
[ef8c]@LAB_PRG15_MIRROR__ef8c:; [$ef8c]
[ef8c]LDA CurrentSprites_Entities,X
[ef8f]CMP #$ff
[ef91]BEQ @LAB_PRG15_MIRROR__ef99
[ef93]CMP Temp_00
[ef95]BEQ @LAB_PRG15_MIRROR__ef99
[ef97]SEC
[ef98]RTS
[ef99]@LAB_PRG15_MIRROR__ef99:; [$ef99]
[ef99]DEX
[ef9a]BPL @LAB_PRG15_MIRROR__ef8c
[ef9c]CLC
[ef9d]RTS
;============================================================================
; 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:; [$ef9e]
;
; 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]LDA a:CurrentScreen_SpecialEventID
[efa1]BMI @_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]ORA #$80; Set bit 7 to only run this conditional on the first game tick.
[efa5]STA a:CurrentScreen_SpecialEventID; Save it.
[efa8]JSR LimitItemDurations; Cap item durations within bounds.
[efab]LDA #$0a
[efad]STA Music_Current; Set the music to the boss battle music.
;
; Check if there's a boss on screen.
;
[efaf]@_checkForBoss:; [$efaf]
[efaf]JSR Sprites_HasBoss; Check for a boss.
[efb2]BCS @_return; If there is one, we're done. Return.
;
; The boss has been defeated. Restore the game state.
;
[efb4]LDA a:Areas_DefaultMusic; Load the default music for the area.
[efb7]STA Music_Current; Set it as the current music.
[efb9]LDA #$ff
[efbb]STA a:CurrentScreen_SpecialEventID; Clear the special event ID so this doesn't run again.
[efbe]@_return:; [$efbe]
[efbe]RTS
;============================================================================
; 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]
[efbf]LDA a:DurationOintment; Load the Ointment duration.
[efc2]BMI @_checkHourGlass; If >= 0, move on to the Hour Glass.
[efc4]LDA #$00
[efc6]STA a:DurationOintment; Set the Ointment duration to 0.
[efc9]@_checkHourGlass:; [$efc9]
[efc9]LDA a:DurationHourGlass; Load the Hour Glass duration.
[efcc]BMI @_return; If >= 0, we're done.
[efce]LDA #$00
[efd0]STA a:DurationHourGlass; Set the Hour Glass duration to 0.
[efd3]@_return:; [$efd3]
[efd3]RTS
;============================================================================
; 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]
[efd4]JSR Sprites_HasCurrentSprites; Are there sprites on screen?
[efd7]BCS @_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]LDA #$07; 0x07 == Eolis music
[efdb]STA Music_Current; Set as the current music.
;
; Play the "Player got hit" sound.
;
[efdd]LDA #$04; 0x04 == Player hit sound effect.
[efdf]JSR Sound_PlayEffect; Play it.
[efe2]JMP EndGame_Begin; Begin the end-game sequence.
[efe5]@_return:; [$efe5]
[efe5]RTS
[efe6]Maybe_IsSpriteEntityNotOnScreen:; [$efe6]
[efe6]STA Temp_00; Temp_00 = Sprite type for comparison.
[efe8]LDY #$07; Y = 7
;
; Check the entity for this index in the current sprites list.
;
[efea]@_spriteLoop:; [$efea]
[efea]LDA CurrentSprites_Entities,Y; Load the entity of the sprite at loop index.
[efed]CMP Temp_00; Is it the entity the caller is checking for?
[efef]BNE @_prepareNextLoopIter; If not, prepare for the next loop iteration.
;
; The sprite entity is on the screen. Clear carry as the result.
;
[eff1]CLC; Return false.
[eff2]RTS
[eff3]@_prepareNextLoopIter:; [$eff3]
[eff3]DEY; Y--
[eff4]BPL @_spriteLoop; If Y >= 0, loop.
;
; The sprite entity is not on the screen. Set carry as the
; result.
;
[eff6]SEC; Return true.
[eff7]RTS
;============================================================================
; 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]
[eff8]LDX #$07; X = 7 (loop counter).
;
; Check if this sprite is a boss.
;
[effa]@_loop:; [$effa]
[effa]LDY CurrentSprites_Entities,X; Load the sprite entity at that index.
[effd]LDA SPRITE_CATEGORIES_BY_ENTITY,Y; Load the category for the entity.
[f000]CMP #$07; Is it 7 (boss)?
[f002]BNE @_notBoss; If not, jump.
;
; There is a boss on screen. Return true.
;
[f004]SEC; Return true.
[f005]RTS
[f006]@_notBoss:; [$f006]
[f006]DEX; X--
[f007]BPL @_loop; If X >= 0, loop.
;
; There is no boss on screen. Return false.
;
[f009]CLC; Return false.
[f00a]RTS
;============================================================================
; 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]
[f00b]LDY #$07; Y = 7 (loop counter).
[f00d]@_loop:; [$f00d]
[f00d]LDA CurrentSprites_Entities,Y; Load the sprite entity at that index.
[f010]CMP #$ff; Is it unset?
[f012]BEQ @_prepareNextLoopIter; If yes, jump to prepare for the next loop iteration.
;
; A sprite entity was found. Return true.
;
[f014]SEC; Return true.
[f015]RTS
[f016]@_prepareNextLoopIter:; [$f016]
[f016]DEY; Y--
[f017]BPL @_loop; If Y >= 0, loop.
;
; A sprite entity was not found. Return false.
;
[f019]CLC; Return false.
[f01a]RTS
[f01b]Sprite_DrawPortraitPartAppearance:; [$f01b]
[f01b]TAY; Y = A (appearance offset).
;
; Save the current ROM bank and switch to bank 7.
;
[f01c]LDA a:CurrentROMBank; Load the current ROM bank.
[f01f]PHA; Push it to the stack.
[f020]LDX #$07; Bank 7 = Sprites
[f022]JSR MMC1_UpdateROMBank; Switch to it.
[f025]TYA; A = Y (appearance offset).
[f026]ASL A; Convert to a word boundary for an index into the tileinfo table.
[f027]TAY; Y = A (index).
[f028]PHP; Push flags to the stack.
[f029]LDA a:PORTRAIT_APPEARANCES_OFFSET; Load the lower byte of the portraits tileinfo address.
[f02c]STA Temp_Addr_L; Store as the lower byte in the read address.
[f02e]LDA a:PORTRAIT_APPEARANCES_OFFSET+1; Load the upper byte of the portraits tileinfo address.
[f031]PLP; Pop flags from the stack.
[f032]ADC #$80; Add 0x80 to the upper byte.
[f034]STA Temp_Addr_U; And store it.
[f036]JMP Sprite_Draw; Draw the portrait sprite.
[f039]Sprite_SetPlayerAppearanceAddr:; [$f039]
[f039]TAY
[f03a]LDA a:CurrentROMBank
[f03d]PHA
[f03e]LDX #$07
[f040]JSR MMC1_UpdateROMBank
[f043]TYA
;
; 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]ASL A
[f045]TAY
[f046]PHP
[f047]LDA a:PLAYER_APPEARANCES_OFFSET
[f04a]STA Temp_Addr_L
[f04c]LDA a:PLAYER_APPEARANCES_OFFSET+1
[f04f]PLP
[f050]ADC #$80
[f052]STA Temp_Addr_U
[f054]JMP Sprite_Draw
[f057]Sprite_SetAppearanceAddrFromOffset:; [$f057]
;
; Switch to ROM bank 7 (PRG).
;
[f057]TAY; Y = A (offset)
[f058]LDA a:CurrentROMBank; Load the current ROM bank.
[f05b]PHA; Push it to the stack.
[f05c]LDX #$07
[f05e]JSR 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]TYA; A = Y (restore offset)
[f062]ASL A; Multiply offset by 2, to get word boundaries in the lookup table.
[f063]TAY; Y = A (updated offset)
[f064]PHP; Push flags and stack pointer.
[f065]LDA a:SPRITE_APPEARANCES_OFFSET; Load the lower byte of the address to read from.
[f068]STA Temp_Addr_L; Store it for processing.
[f06a]LDA a:SPRITE_APPEARANCES_OFFSET+1; Load the upper byte.
[f06d]PLP; Pop the flags and stack pointer.
[f06e]ADC #$80; Add 0x80 + carry to the upper byte.
[f070]STA 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:; [$f072]
;
; Load the offset within our starting address stored in
;
Temp_Addr_L.
;
[f072]LDA (Temp_Addr_L),Y; Load the offset for the lower byte.
[f074]STA Sprite_TileInfo_ReadAddr_L; Store it.
[f076]INY; Y++ (offset)
[f077]LDA (Temp_Addr_L),Y; Load the upper byte.
[f079]ADC #$80; Add 0x80 + carry to it.
[f07b]STA Sprite_TileInfo_ReadAddr_U; Store it.
[f07d]LDY #$00; Y = 0 (read offset)
[f07f]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the value at that address.
[f081]AND #$0f; Discard the upper nibble.
[f083]STA Temp_Sprites_ColsRemaining; Store it.
[f085]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load it again.
[f087]LSR A; Move upper nibble to lower.
[f088]LSR A
[f089]LSR A
[f08a]LSR A
[f08b]STA Temp_Sprites_RowsRemaining; Store it.
[f08d]LDA #$00; A = 0
[f08f]STA Sprite_TileInfo_OffsetX; Set to 0.
[f091]STA 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]INY; Y++ (read offset)
[f094]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the X offset byte.
[f096]BPL @_calcX; If > 0, jump.
[f098]DEC Sprite_TileInfo_OffsetX; Set to 0xFF. This will set C=1 for the add below.
[f09a]@_calcX:; [$f09a]
[f09a]CLC
[f09b]ADC Arg_DrawSprite_PosX; Add Arg_DrawSprite_PosX to the X offset (which may be negative, extending left).
[f09d]STA Temp_Sprites_NormXPos; Store it.
[f09f]LDA #$00; A = 0
[f0a1]ADC Sprite_TileInfo_OffsetX; Add our stored value.
[f0a3]STA Sprite_TileInfo_OffsetX; Store it back out.
;
; Update the drawn X position to factor in any screen scrolling
; offsets.
;
[f0a5]LDA Temp_Sprites_NormXPos; Load the new calculated X position.
[f0a7]SEC
[f0a8]SBC PPU_ScrollX; Subtract any scrolling offset that may be set.
[f0aa]STA 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]INY; Y++ (read offset)
[f0ad]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the Y offset byte.
[f0af]BPL @_calcY; If > 0, jump.
[f0b1]DEC Sprite_TileInfo_OffsetY; Set to 0xFF. This will set C=1 for the add below.
[f0b3]@_calcY:; [$f0b3]
[f0b3]CLC
[f0b4]ADC Arg_DrawSprite_PosY; Add Arg_DrawSprite_PosY to the Y offset (which may be negative, extending up).
[f0b6]STA Temp_Sprites_NormYPos; Store it.
;
; Add Carry to the offset, if it's set.
;
[f0b8]LDA #$00
[f0ba]ADC Sprite_TileInfo_OffsetY; A = Y offset + carry (from above).
[f0bc]STA Sprite_TileInfo_OffsetY; Store it.
;
; And then always offset by 32 pixels (the HUD height).
;
[f0be]LDA Temp_Sprites_NormYPos; Load the normalized Y position.
[f0c0]CLC
[f0c1]ADC #$20; Add 32 (HUD height in pixels).
[f0c3]STA 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]LDA Sprite_TileInfo_OffsetY; Load the Y offset.
[f0c7]ADC #$00; Add Carry, if set.
[f0c9]STA 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]INY; Y++ (read offset).
[f0cc]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the pivot point in pixels.
[f0ce]STA 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]LDA Sprite_TileInfo_ReadAddr_L; Load the lower byte of the sprite address.
[f0d2]CLC
[f0d3]ADC #$04; Advance by 4 (skipping loaded state).
[f0d5]STA Sprite_TileInfo_ReadAddr_L; Set as the new lower byte of the address.
[f0d7]LDA Sprite_TileInfo_ReadAddr_U; Load the upper byte.
[f0d9]ADC #$00; Add the carry, if lower overflowed.
[f0db]STA Sprite_TileInfo_ReadAddr_U; Set it.
;
; We'll continue on based on whether the sprite is flipped.
;
[f0dd]LDA CurrentSprite_FlipMask; Load the flip mask.
[f0df]AND #$c0; Keep only the horiz/vert flip bits.
[f0e1]STA CurrentSprite_FlipMask; Store as a new flip mask.
[f0e3]LDA CurrentSprite_FlipMask; Load the resulting flip mask.
[f0e5]AND #$40; Check if it's flipped horizontally.
[f0e7]BEQ Sprite_Draw_Standard; If not, draw in standard orientation.
[f0e9]JMP 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:; [$f0ec]
;
; The sprite is not flipped.
;
[f0ec]LDA #$00; A = 0.
[f0ee]STA Temp_Sprites_TileYOffset; Store it as the starting tiles Y offset.
[f0f0]TAY; Y = A (0, loop counter).
[f0f1]@_drawRowLoop:; [$f0f1]
[f0f1]LDA Temp_Sprites_ColsRemaining; Load the number of tile columns remaining.
[f0f3]PHA; Push it to the stack.
[f0f4]LDA #$00; A = 0.
[f0f6]STA Temp_Sprites_TileXOffset; Store as the start tiles X offset.
;
; Check if the current sprite data value is not 0xFF (unset).
;
[f0f8]@_drawColLoop:; [$f0f8]
[f0f8]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the value at the sprite data read address.
[f0fa]CMP #$ff; Is it 0xFF?
[f0fc]BEQ @_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]LDA Sprites_SlotsFull; Can we add sprites this frame?
[f100]BNE @_endColLoop; If not, jump, try the next row.
[f102]LDX Temp_Sprites_TileXOffset; X = tile X offset.
[f104]LDA Temp_Sprites_NormXPos; A = normalized X position.
[f106]ADC SPRITE_TILE_BLOCK_POSITIONS,X; A += the offset from the lookup table at X.
[f109]STA Temp_00; Store it temporarily.
;
; Check if this tile would fit on screen in the X direction.
;
[f10b]LDA Sprite_TileInfo_OffsetX; Load the default X position.
[f10d]ADC #$00; Add carry (from overflow), if set.
[f10f]BNE @_endColLoop; If it doesn't fit on screen, jump.
[f111]LDX Temp_Sprites_TileYOffset; X = tile Y offset.
[f113]LDA Temp_Sprites_NormYPos; A = normalized Y position.
[f115]ADC SPRITE_TILE_BLOCK_POSITIONS,X; A += the offset from the lookup table at X.
[f118]STA Temp_01; Store it temporarily.
;
; Check if this tile would fit on screen in the Y direction.
;
[f11a]LDA Sprite_TileInfo_OffsetY; Load the default Y position.
[f11c]ADC #$00; Add carry (from overflow), if set.
[f11e]BNE @_endColLoop; If it doesn't fit on screen, jump.
;
; The sprite fits on screen.
;
; We'll begin writing the sprite to DMA.
;
[f120]TYA; A = Y (loop counter)
[f121]PHA; Push our loop counter to the stack.
[f122]LDA Sprites_SpriteSlot; Load the slot for this sprite.
[f124]ASL A; Multiply by 4 (for Y, tile ID, attributes, X bytes).
[f125]ASL A
[f126]EOR Sprites_StartSlotRange
;
; Write the sprite Y position to DMA.
;
[f128]TAX; X = A (read offset)
[f129]LDA Temp_01; Load the Y position.
[f12b]STA 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]INX; X++ (read offset)
[f12f]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the tile index.
[f131]CLC
[f132]ADC Sprites_PPUOffset; Add the PPU starting offset for the list of tiles.
[f134]STA 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]INX; X++ (read offset)
[f138]INY; Y++ (loop counter)
[f139]LDA (Sprite_TileInfo_ReadAddr_L),Y; Load the attribute.
[f13b]EOR CurrentSprite_FlipMask; XOR with the flip mask.
[f13d]STA 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]LDA Temp_Sprites_TileXOffset; Load the tile X offset.
[f141]AND #$01; Convert to an even/odd value for the visibility lookup table.
[f143]TAY; Y = result.
[f144]LDA MovingSpriteVisibility; Load the moving visibility of the sprite.
[f146]AND DRAW_SPRITE_VISIBILITY_MASK,Y; AND with the lookup table, determining priority.
[f149]BEQ @_storeAttribute; If non-0, keep foreground priority.
Else...
;
; The tile is obscured. Set the sprite to be a
; background sprite.
;
[f14b]LDA Temp_01; Load the attribute calculated above.
[f14d]ORA #$20; Set background priority.
[f14f]STA Temp_01; And store it.
;
; Write the tile attribute to DMA.
;
[f151]@_storeAttribute:; [$f151]
[f151]LDA Temp_01; Load the calculated attribute.
[f153]STA SPRITE_0_RANGE_1_START,X; Store the tile attributes to DMA.
;
; Write the sprite X position to DMA.
;
[f156]INX; X++ (read offset)
[f157]LDA Temp_00; Load the X position.
[f159]STA SPRITE_0_RANGE_1_START,X; Write the X coordinate to DMA.
;
; Prepare for the next column.
;
[f15c]JSR Sprites_IncNextSpriteSlot; Finish up with this sprite's state.
[f15f]PLA; Pull A (loop counter) from stack.
[f160]TAY; Y = A (loop counter)
[f161]@_endColLoop:; [$f161]
[f161]INY; Y++ (loop counter)
[f162]@_prepareNextCol:; [$f162]
[f162]INY; Y++ (loop counter)
[f163]INC Temp_Sprites_TileXOffset; Increment the tile X offset.
[f165]DEC Temp_Sprites_ColsRemaining; Decrement the number of columns remaining.
[f167]BPL @_drawColLoop; If there are columns remaining, loop.
;
; We're on the next row. Reset the column count and advance
; the row counter.
;
[f169]PLA; Pop the original total columns remaining.
[f16a]STA Temp_Sprites_ColsRemaining; Store as the new columns count for the next row.
[f16c]INC Temp_Sprites_TileYOffset; Increment the tile Y offset.
[f16e]DEC Temp_Sprites_RowsRemaining; Decrement the number of rows remaining.
[f170]BMI Sprite_Draw_Finish; If there are no more rows, finish the drawing.
[f172]JMP @_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]
[f175]PLA
[f176]TAX
[f177]JSR MMC1_UpdateROMBank
[f17a]LDA #$00
[f17c]STA Sprites_PPUOffset
[f17e]STA MovingSpriteVisibility
[f180]RTS
;============================================================================
; TODO: Document Sprite_Draw_FlippedHoriz
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Sprite_Draw
;============================================================================
[f181]Sprite_Draw_FlippedHoriz:; [$f181]
;
; The sprite is flipped.
;
[f181]LDA #$00
[f183]STA Temp_05; Set Temp_05 = 0.
[f185]LDY Temp_Sprites_ColsRemaining; Load the number of columns remaining for a row.
[f187]INY; Y++ (convert from 0-based count)
[f188]TYA; A = result
[f189]ASL A; Multiply the columns by 8.
[f18a]ASL A
[f18b]ASL A
[f18c]STA Temp_04; Store for temporary use.
[f18e]LDA Temp_Addr_L; A = 4th byte from the sprite information.
[f190]ASL A; A = A * 2
[f191]SEC
[f192]SBC Temp_04; A = A - normalized column value.
[f194]BPL @LAB_PRG15_MIRROR__f198; If negative, jump.
[f196]DEC Temp_05; Else, it's positive. Decrement Temp_05.
[f198]@LAB_PRG15_MIRROR__f198:; [$f198]
[f198]CLC
[f199]ADC Temp_Sprites_NormXPos; A = A + normalized X position
[f19b]STA Temp_Sprites_NormXPos; Store as the new position.
[f19d]LDA Sprite_TileInfo_OffsetX
[f19f]ADC Temp_05
[f1a1]STA Sprite_TileInfo_OffsetX
[f1a3]LDA #$00
[f1a5]STA Temp_Sprites_TileYOffset
[f1a7]TAY
[f1a8]@LAB_PRG15_MIRROR__f1a8:; [$f1a8]
[f1a8]LDA Temp_Sprites_ColsRemaining
[f1aa]STA Temp_Sprites_TileXOffset
[f1ac]@LAB_PRG15_MIRROR__f1ac:; [$f1ac]
[f1ac]LDA (Sprite_TileInfo_ReadAddr_L),Y
[f1ae]CMP #$ff
[f1b0]BEQ @LAB_PRG15_MIRROR__f216
[f1b2]LDA Sprites_SlotsFull
[f1b4]BNE @LAB_PRG15_MIRROR__f215
[f1b6]LDX Temp_Sprites_TileXOffset
[f1b8]LDA Temp_Sprites_NormXPos
[f1ba]ADC SPRITE_TILE_BLOCK_POSITIONS,X
[f1bd]STA Temp_00
[f1bf]LDA Sprite_TileInfo_OffsetX
[f1c1]ADC #$00
[f1c3]BNE @LAB_PRG15_MIRROR__f215
[f1c5]LDX Temp_Sprites_TileYOffset
[f1c7]LDA Temp_Sprites_NormYPos
[f1c9]ADC SPRITE_TILE_BLOCK_POSITIONS,X
[f1cc]STA Temp_01
[f1ce]LDA Sprite_TileInfo_OffsetY
[f1d0]ADC #$00
[f1d2]BNE @LAB_PRG15_MIRROR__f215
[f1d4]TYA
[f1d5]PHA
[f1d6]LDA Sprites_SpriteSlot
[f1d8]ASL A
[f1d9]ASL A
[f1da]EOR Sprites_StartSlotRange
[f1dc]TAX
[f1dd]LDA Temp_01
[f1df]STA SPRITE_0_RANGE_1_START,X
[f1e2]INX
[f1e3]LDA (Sprite_TileInfo_ReadAddr_L),Y
[f1e5]CLC
[f1e6]ADC Sprites_PPUOffset
[f1e8]STA SPRITE_0_RANGE_1_START,X
[f1eb]INX
[f1ec]INY
[f1ed]LDA (Sprite_TileInfo_ReadAddr_L),Y
[f1ef]EOR CurrentSprite_FlipMask
[f1f1]STA Temp_01
[f1f3]LDA Temp_Sprites_TileXOffset
[f1f5]AND #$01
[f1f7]TAY
[f1f8]LDA MovingSpriteVisibility
[f1fa]AND BYTE_ARRAY_PRG15_MIRROR__f226,Y
[f1fd]BEQ @LAB_PRG15_MIRROR__f205
[f1ff]LDA Temp_01
[f201]ORA #$20
[f203]STA Temp_01
[f205]@LAB_PRG15_MIRROR__f205:; [$f205]
[f205]LDA Temp_01
[f207]STA SPRITE_0_RANGE_1_START,X
[f20a]INX
[f20b]LDA Temp_00
[f20d]STA SPRITE_0_RANGE_1_START,X
[f210]JSR Sprites_IncNextSpriteSlot
[f213]PLA
[f214]TAY
[f215]@LAB_PRG15_MIRROR__f215:; [$f215]
[f215]INY
[f216]@LAB_PRG15_MIRROR__f216:; [$f216]
[f216]INY
[f217]DEC Temp_Sprites_TileXOffset
[f219]BPL @LAB_PRG15_MIRROR__f1ac
[f21b]INC Temp_Sprites_TileYOffset
[f21d]DEC Temp_Sprites_RowsRemaining
[f21f]BPL @LAB_PRG15_MIRROR__f1a8
[f221]JMP Sprite_Draw_Finish
[f224]DRAW_SPRITE_VISIBILITY_MASK:; [$f224]
[f224].byte $01; [0]: Trailing visibility
[f225].byte $02; [1]: Leading visibility
[f226]BYTE_ARRAY_PRG15_MIRROR__f226:; [$f226]
[f226].byte $02; [0]:
[f227].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:; [$f228]
;
; Alternate between sprites 0-31 and sprites 32-63.
;
; The bit being flipped is bit 5, value 32.
;
[f228]LDA Sprites_SpriteSlot; Load the sprite slot ID.
[f22a]EOR #$20; Cap the value to < 32.
[f22c]STA 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]AND #$20; Check if we're in 32-63.
[f230]BNE @_return; If so, return.
;
; It's in sprite range 0-31.
;
[f232]INC Sprites_SpriteSlot; Increase the sprite slot.
[f234]LDA Sprites_SpriteSlot; Load the new slot.
[f236]CMP #$20; Are there slots left?
[f238]BCC @_return; If so, return.
;
; The slots are full. Mark it.
;
[f23a]INC Sprites_SlotsFull; Mark slots as full.
[f23c]@_return:; [$f23c]
[f23c]RTS
[f23d]SPRITE_TILE_BLOCK_POSITIONS:; [$f23d]
[f23d].byte $00; [0]:
[f23e].byte $08; [1]:
[f23f].byte $10; [2]:
[f240].byte $18; [3]:
[f241].byte $20; [4]:
[f242].byte $28; [5]:
[f243].byte $30; [6]:
[f244].byte $38; [7]:
[f245].byte $40; [8]:
[f246].byte $48; [9]:
[f247].byte $50; [10]:
[f248].byte $58; [11]:
[f249].byte $60; [12]:
[f24a].byte $68; [13]:
[f24b].byte $70; [14]:
[f24c].byte $78; [15]:
[f24d]IScripts_LoadPortraitTiles:; [$f24d]
;
; Check if a portrait ID is set.
;
[f24d]STA a:IScript_PortraitID; Set the current portrait ID.
[f250]TAX; X = result.
[f251]BMI @_return; If unset, return.
;
; The portrait ID is set. Load the address and wait for an
; OAM reset.
;
[f253]JSR IScripts_LoadPortraitTilesAddress; Load the tiles address for this portrait ID.
[f256]JSR Game_WaitForOAMReset; Wait for OAM reset.
;
; Save the sprite palette and switch to palette 2 for the
; portrait.
;
[f259]LDA a:Palette_SpritePaletteIndex; Load the current sprite palette index
[f25c]STA a:Palette_SavedIndex; And save it for later.
[f25f]LDA #$02
[f261]JSR Screen_LoadSpritePalette; Load palette 2.
[f264]JSR PPUBuffer_WritePalette; Write the palette to the PPU.
;
; Set the target PPU address to draw the portrait to.
; This will be $0900.
;
[f267]LDA #$09; Upper byte = 0x09.
[f269]STA PPU_TargetAddr_U; Set it.
[f26b]LDA #$00; Lower byte = 0x00.
[f26d]STA PPU_TargetAddr; Set it.
;
; Begin drawing the portrait. This will loop up to
; 256 times, drawing 16 tiles each.
;
[f26f]LDY #$00; Y = 0 (loop counter).
[f271]@_drawLoop:; [$f271]
[f271]TYA; A = Y (loop counter).
[f272]PHA; Push it to the stack.
[f273]JSR IScripts_DrawPortraitTileToPPU; Draw a 16-byte tile to the PPU.
[f276]BCS @_finishDrawing
[f278]PLA; Pop the loop counter from the stack.
[f279]TAY; Set back in Y.
[f27a]INY; Y++
[f27b]BNE @_drawLoop; If not wrapped to 0, loop.
[f27d]@_return:; [$f27d]
[f27d]RTS
;
; The portrait is finished.
;
[f27e]@_finishDrawing:; [$f27e]
[f27e]PLA; Pop the loop counter from the stack.
[f27f]TAY; Set back in Y.
[f280]RTS
[f281]IScripts_ClearPortraitImage:; [$f281]
[f281]LDA #$ff
[f283]STA a:IScript_PortraitID; Clear the portrait ID.
[f286]JSR Game_WaitForOAMReset; Wait for OAM reset.
[f289]LDA a:Palette_SavedIndex; Load the saved sprite palette ID.
[f28c]JSR Screen_LoadSpritePalette; Load that palette's data.
[f28f]JSR PPUBuffer_WritePalette; Write to the PPU buffer.
[f292]JSR MMC1_LoadBankAndJump; Load sprite imagesin bank 14.
[f295].byte BANK_14_LOGIC; Bank = 14
Address = GameLoop_LoadSpriteImages
[f296].word GameLoop_LoadSpriteImages-1; GameLoop_LoadSpriteImages [$PRG15_MIRROR::f296]
[f298]@_afterFarJump:; [$f298]
[f298]RTS
[f299]IScripts_DrawPortraitAnimationFrame_Unset:; [$f299]
[f299]PLA; Clean up the stack, popping the pushed frame.
[f29a]RTS
[f29b]IScripts_DrawPortraitAnimationFrame:; [$f29b]
[f29b]PHA; Push the frame to the stack.
[f29c]LDA a:IScript_PortraitID; Load the current portrait ID.
[f29f]BMI IScripts_DrawPortraitAnimationFrame_Unset; If 0xFF, jump to finish.
;
; There's an active portrait. Begin preparing by clearing
; the flip mask state.
;
[f2a1]LDA #$00
[f2a3]STA CurrentSprite_FlipMask; Set the flip mask to 0 (not flipped).
;
; Update the general portrait image.
;
[f2a5]LDA #$90
[f2a7]STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2a9]JSR IScripts_GetPortraitOffset; Get the portrait offset into the tileinfo for the ID.
[f2ac]JSR Sprite_DrawPortraitPartAppearance; Draw as the portrait.
;
; Update the eye animation.
;
; This will switch to new tiles every frame.
;
[f2af]LDA #$90
[f2b1]STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2b3]PLA; Pull the frame from the stack.
[f2b4]PHA; Push it again. We still have it in A.
[f2b5]AND #$01; Generate an even/odd index from it.
[f2b7]TAX; X = result.
[f2b8]JSR IScripts_GetPortraitOffset; Get the offset into the table for the portrait ID.
[f2bb]CLC
[f2bc]ADC PORTRAIT_EYE_APPEARANCE_OFFSETS,X; Add the eyes animation offset for this even/odd frame.
[f2bf]JSR Sprite_DrawPortraitPartAppearance; Draw as the eyes.
;
; Update the mouth animation.
;
; This will switch to new tiles every other frame.
;
[f2c2]LDA #$90
[f2c4]STA Sprites_PPUOffset; Set the PPU offset to $0090.
[f2c6]PLA; Pull the frame from the stack.
[f2c7]LSR A; Divide the frame by 2 (reducing the animation rate to switch every other frame).
[f2c8]AND #$01; Convert that to an even/odd value.
[f2ca]TAX; X = result.
[f2cb]JSR IScripts_GetPortraitOffset; Get the offset into the table for the portrait ID.
[f2ce]CLC
[f2cf]ADC PORTRAIT_MOUTH_APPEARANCE_OFFSETS,X; Add the mouth animation offset for this even/odd frame.
[f2d2]JMP 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]
[f2d5]LDA a:IScript_PortraitID; Load the current portrait ID.
[f2d8]ASL A; Multiply by 4.
[f2d9]ASL A
[f2da]CLC
[f2db]ADC a:IScript_PortraitID; And add the ID again (effectively multiplying by a total of 5).
[f2de]RTS; Return it as A.
;============================================================================
; Offsets for the portrait eye blinking animation frames.
;
; XREFS:
;
IScripts_DrawPortraitAnimationFrame
;============================================================================
[f2df]PORTRAIT_EYE_APPEARANCE_OFFSETS:; [$f2df]
[f2df].byte $01; [0]:
[f2e0].byte $02; [1]:
;============================================================================
; Offsets for the portrait mouth movement animation frames.
;
; XREFS:
;
IScripts_DrawPortraitAnimationFrame
;============================================================================
[f2e1]PORTRAIT_MOUTH_APPEARANCE_OFFSETS:; [$f2e1]
[f2e1].byte $03; [0]:
[f2e2].byte $04; [1]:
[f2e3]IScripts_LoadPortraitTilesAddress:; [$f2e3]
;
; Switch to bank 8 for the portrait sprites.
;
[f2e3]LDA a:CurrentROMBank; Load the current ROM bank.
[f2e6]PHA; Push it to the stack.
[f2e7]LDX #$08; Bank 8 = Sprites
[f2e9]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Convert a portrait ID to a word boundary for later lookup.
;
[f2ec]LDA a:IScript_PortraitID; Load the portrait ID.
[f2ef]ASL A; Convert to a word boundary.
[f2f0]TAY; Y = result
;
; Load the address for the portrait tiles table.
;
[f2f1]LDA a:TILES_PORTRAITS_ADDRS_INDEX_REF; Load the lower byte of the start address for the portrait tiles.
[f2f4]STA Temp_Addr_L; Store as the lower byte.
[f2f6]LDA a:TILES_PORTRAITS_ADDRS_INDEX_REF+1; Load the upper byte of the portrait address.
[f2f9]CLC
[f2fa]ADC #$80; Add 0x80 to it.
[f2fc]STA 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]LDA (Temp_Addr_L),Y; Load the lower byte of the address based on the potrait ID.
[f300]PHA; Push it to the stack.
[f301]INY; Y++ (next offset)
[f302]LDA (Temp_Addr_L),Y; Load the upper byte of the tile.
[f304]CLC
[f305]ADC #$80; Add 0x80 to it.
[f307]STA Temp_Addr_U; Store as the new upper byte.
[f309]PLA; Pop the lower byte.
[f30a]STA Temp_Addr_L; Store as the lower byte.
;
; Switch back to the stored bank.
;
[f30c]JMP 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]
[f30f]PLA; Pop the saved bank from the stack.
[f310]TAX; X = A (bank)
[f311]JSR MMC1_UpdateROMBank; Switch to it.
[f314]SEC; Set C=1 for the result.
[f315]RTS; 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:; [$f316]
;
; Save the current bank and switch to bank 8.
;
[f316]LDA a:CurrentROMBank; Load the current ROM bank.
[f319]PHA; Push it to the stack.
[f31a]LDX #$08; Bank 8 = Portrait sprites
[f31c]JSR MMC1_UpdateROMBank; Switch to the bank.
;
; Load the tile and see if it's set or if it's 0xFF.
;
[f31f]LDA (Temp_Addr_L),Y; Load the tile index from the tiles address.
[f321]CMP #$ff; Is it 0xFF?
[f323]BEQ 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]LDA #$00; A = 0 (offset).
[f327]STA Temp_05
[f329]LDA (Temp_Addr_L),Y
[f32b]ASL A
[f32c]ROL Temp_05
[f32e]ASL A
[f32f]ROL Temp_05
[f331]ASL A
[f332]ROL Temp_05
[f334]ASL A
[f335]ROL Temp_05
[f337]CLC
[f338]ADC a:TILES_PORTRAITS_START_REF; Add the tile ID to the lower byte.
[f33b]STA Temp_04; Store in Temp_04.
[f33d]LDA Temp_05; Load the calculated upper byte.
[f33f]ADC a:TILES_PORTRAITS_START_REF+1; Add the upper byte for the relative start address.
[f342]CLC
[f343]ADC #$80; Add 0x80.
[f345]STA Temp_05; And store as a new upper byte.
;
; Begin writing to the PPU buffer.
;
[f347]LDA #$10; Length = 16 (tile bytes).
[f349]JSR PPUBuffer_QueueCommandOrLength; Queue that as the PPU buffer length.
;
; Begin populating the buffer with the tile's bytes.
;
[f34c]LDY #$00; Y = 0 (loop counter).
[f34e]@_drawLoop:; [$f34e]
[f34e]LDA (Temp_04),Y; Load the next byte for the tile.
[f350]STA PPUBuffer,X; Store it in the PPU buffer.
[f353]INX; X++ (destination).
[f354]INY; Y++ (loop counter).
[f355]CPY #$10; Have we hit 16 bytes?
[f357]BCC @_drawLoop; If not, loop.
;
; All tiles have been drawn. Update the write offset and
; advance the PPU address.
;
[f359]STX PPUBuffer_WriteOffset; Store the new PPU buffer write offset.
[f35b]LDA PPU_TargetAddr; Load the lower byte of the target address.
[f35d]CLC
[f35e]ADC #$10; Advance by 16 bytes.
[f360]STA PPU_TargetAddr; Store it.
[f362]LDA PPU_TargetAddr_U; Load the upper byte of the target address.
[f364]ADC #$00; Add carry, if lower wrapped.
[f366]STA PPU_TargetAddr_U; Store it.
;
; Restore the previous bank.
;
[f368]PLA; Pop the bank from the stack.
[f369]TAX; X = A (bank).
[f36a]JSR MMC1_UpdateROMBank; Switch to it.
[f36d]CLC; Set C=0 for the result.
[f36e]RTS
;============================================================================
; 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]
[f36f]CMP #$1d; Length of sound IDs array
[f371]BCS @_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]TAX; X = new sound ID
[f374]LDY a:SoundEffect_Unused_PriorityID; Y = current sound ID
[f377]LDA SOUND_PRIORITIES,Y; A = Sound type for current sound from map
[f37a]CMP SOUND_PRIORITIES,X; Is this already the current sound?
[f37d]BEQ @_queueNext; If yes, then jump
[f37f]BCC @_queueNext; If it's a lower priority (larger number), then jump.
[f381]STX a:SoundEffect_Unused_PriorityID; Set as the current sound.
[f384]RTS
;
; Set the sound effect to play at the next hardware interrupt.
;
[f385]@_queueNext:; [$f385]
[f385]STX SoundEffect_Current; Queue this up as the next effect.
[f387]@_return:; [$f387]
[f387]RTS
;============================================================================
; 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]
[f388].byte $00; [0]:
[f389].byte $08; [1]:
[f38a].byte $15; [2]: Hit enemy with weapon
[f38b].byte $14; [3]: Enemy is dead
[f38c].byte $04; [4]: Player hit
[f38d].byte $19; [5]: Magic
[f38e].byte $0b; [6]: Open door
[f38f].byte $0d; [7]:
[f390].byte $10; [8]: Item picked up
[f391].byte $11; [9]: Touched coin
[f392].byte $17; [10]: Cast magic hit an obstacle
[f393].byte $09; [11]: Cursor moved
[f394].byte $13; [12]: Text input
[f395].byte $0a; [13]:
[f396].byte $07; [14]: Password character entered
[f397].byte $0c; [15]: Block pushed
[f398].byte $12; [16]: Coin dropped
[f399].byte $0f; [17]:
[f39a].byte $0e; [18]:
[f39b].byte $06; [19]: Filling HP or MP
[f39c].byte $18; [20]: Tilte magic cast
[f39d].byte $04; [21]: Player taking step
[f39e].byte $01; [22]:
[f39f].byte $02; [23]:
[f3a0].byte $03; [24]:
[f3a1].byte $05; [25]: Gold changed
[f3a2].byte $03; [26]: Item used
[f3a3].byte $10; [27]: Touched meat
[f3a4].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:; [$f3a5]
[f3a5]LDA a:CurrentROMBank
[f3a8]PHA
[f3a9]LDX #$0d
[f3ab]JSR MMC1_UpdateROMBank
[f3ae]LDA #$00
[f3b0]STA Temp_Int24
[f3b2]LDA #$80
[f3b4]STA Temp_Int24_M
[f3b6]LDA #$12
[f3b8]STA a:PPUADDR
[f3bb]LDA #$00
[f3bd]STA a:PPUADDR
[f3c0]LDX #$60
[f3c2]@LAB_PRG15_MIRROR__f3c2:; [$f3c2]
[f3c2]JSR PPU_WriteGlyphTile
[f3c5]JSR PPU_WriteGlyphTile
[f3c8]LDA Temp_Int24
[f3ca]CLC
[f3cb]ADC #$08
[f3cd]BCC @LAB_PRG15_MIRROR__f3d1
[f3cf]INC Temp_Int24_M
[f3d1]@LAB_PRG15_MIRROR__f3d1:; [$f3d1]
[f3d1]STA Temp_Int24
[f3d3]DEX
[f3d4]BNE @LAB_PRG15_MIRROR__f3c2
[f3d6]MMC1_UpdatePRGBankToStackA:; [$f3d6]
[f3d6]PLA; Pop the bank from the stack.
[f3d7]TAX; X = A
[f3d8]JSR MMC1_UpdateROMBank; Switch to the bank.
[f3db]RTS
;============================================================================
; 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]
[f3dc]LDY #$00; Y = 0
[f3de]@_loop:; [$f3de]
[f3de]LDA (Temp_Int24),Y; Load the byte from the temp string address.
[f3e0]STA a:PPUDATA; Write to the PPU.
[f3e3]INY; Y++
[f3e4]CPY #$08; Have we written 8 bytes?
[f3e6]BNE @_loop; If not, loop.
[f3e8]RTS
;============================================================================
; 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]
[f3e9]JSR Messages_Load; Load the message.
[f3ec]@_loop:; [$f3ec]
[f3ec]JSR TextBox_LoadAndShowMessage; Show the next character from the message.
[f3ef]LDA a:MessageID; Is it 0xFF (end of message)?
[f3f2]BNE @_loop; If not, loop.
[f3f4]RTS
[f3f5]Messages_Load:; [$f3f5]
[f3f5]STA a:MessageID
;
; Save our current bank.
;
[f3f8]LDA a:CurrentROMBank; A = Our current bank
[f3fb]PHA; Push A to the stack.
;
; Switch to Bank 13 for the strings.
;
[f3fc]LDX #$0d; Update to Bank 13.
[f3fe]JSR MMC1_UpdateROMBank
[f401]LDX #$00; Set the lower byte of the address for the strings.
[f403]STX Temp_Int24
[f405]LDX #$83; Set the upper byte of the address for the strings.
[f407]STX Temp_Int24_M
;
; Set the start of our message length for the
; string scan.
;
[f409]LDY #$00; Y = 0 (start of our lookup offset)
[f40b]STY a:TextBox_Timer; Set start character pos 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]LDX a:MessageID; X = MessageID
[f411]DEX; X--
[f412]BEQ @_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]
[f414]LDA (Temp_Int24),Y; A = Character at Y from the current string.
[f416]INY; Y++
;
; If Y == 0, increment our upper byte position. This
; will happen when Y wraps around from 0xFF to 0.
;
[f417]BNE @_checkChar; If Y != 0, jump.
[f419]INC 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]
[f41b]CMP #$ff; Is A (the character) 0xFF?
[f41d]BNE @_messageLoop; If not 0xFF, loop.
[f41f]DEX; A is 0xFF. X-- to the previous message ID.
[f420]BNE @_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]
[f422]STY a:Message_ProcessedLength; Message length = Y
[f425]LDA Temp_Int24; Load the new lower byte of the string position.
[f427]STA a:Message_StartAddr; Store it.
[f42a]LDA Temp_Int24_M; Load the new upper byte of the string position.
[f42c]STA a:Message_StartAddr_U; Store it.
;
; Restore our previous bank.
;
[f42f]PLA; Pop A (our previous bank) off the stack.
[f430]TAX; X = A
[f431]JSR MMC1_UpdateROMBank; Switch bank to the bank.
;
; v-- Fall through --v
;
[f434]TextBox_ClearPasswordSize:; [$f434]
[f434]LDA #$00
[f436]STA a:TextBox_LineScrollOffset; Set line scroll offset to 0.
[f439]STA a:TextBox_CharPosForLine; Set character position in line to 0.
[f43c]STA a:TextBox_DrawnLineNum; Set the drawn line number to 0.
[f43f]STA a:TextBox_MessagePaused; Clear the message paused state.
[f442]STA a:Textbox_TitleCharOffset; Set the title character offset to 0.
[f445]LDA #$04
[f447]STA 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:; [$f44a]
;
; Set the PPU target address to $1400. These are the
; text tiles.
;
[f44a]LDA #$14; 0x14 == upper byte of the PPU address.
[f44c]STA PPU_TargetAddr_U; Store it.
[f44e]LDA #$00; 0x00 == lower byte.
[f450]STA PPU_TargetAddr; Store it.
[f452]LDY #$20; Y = 32 (loop counter).
;
; Write the value 0x00 for bytes $1400 through $1800.
;
[f454]@_writeLoop:; [$f454]
[f454]TYA; A = Y (loop counter)
[f455]PHA; Push to the stack.
[f456]LDA #$00; A = 0
[f458]LDY #$20; Y = 32
[f45a]JSR PPUBuffer_WriteValueMany; Write 0x00 32 times to the PPU buffer.
[f45d]JSR PPU_IncrementAddrBy32; Address += 32
[f460]PLA; Pull A from the stack.
[f461]TAY; Y = A (loop counter)
[f462]DEY; Y--
[f463]BNE @_writeLoop; If > 0, loop.
[f465]TextBox_ClearShopSizeAtOffset_return:; [$f465]
[f465]RTS
[f466]TextBox_ShowNextChar:; [$f466]
;
; Save the current bank and switch to bank 13 (strings).
;
[f466]LDA a:CurrentROMBank; A = current bank
[f469]PHA; Push it to the stack.
[f46a]LDX #$0d; X = 13 (bank)
[f46c]JSR MMC1_UpdateROMBank; Switch to bank 13.
;
; Show the next character.
;
[f46f]INC a:TextBox_Timer; Increment the next character position.
[f472]LDA #$01; A = 1
[f474]STA a:TextBox_PlayTextSound; Store it.
[f477]JSR Maybe_TextBox_ShowCurrentMessageID; Show this character in the message.
;
; Restore the bank and return.
;
[f47a]JMP MMC1_UpdatePRGBankToStackA; Restore the pushed bank.
;============================================================================
; TODO: Document TextBox_LoadAndShowMessage
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
IScripts_LoadAndShowMessage
;============================================================================
[f47d]TextBox_LoadAndShowMessage:; [$f47d]
;
; Save the current ROM.
;
[f47d]LDA a:CurrentROMBank
[f480]PHA
;
; Switch to Bank 13, where the strings are.
;
[f481]LDX #$0d
[f483]JSR MMC1_UpdateROMBank
[f486]LDA #$00
[f488]STA a:TextBox_PlayTextSound
[f48b]JSR TextBox_DisplayMessage
;
; Restore the previous bank.
;
[f48e]JMP MMC1_UpdatePRGBankToStackA
;============================================================================
; TODO: Document Maybe_TextBox_ShowCurrentMessageID
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowNextChar
;============================================================================
[f491]Maybe_TextBox_ShowCurrentMessageID:; [$f491]
[f491]LDA a:MessageID; Load the message ID to show.
[f494]BEQ TextBox_ClearShopSizeAtOffset_return; If unset, return.
[f496]LDA a:TextBox_MessagePaused
[f499]BNE TextBox_ClearShopSizeAtOffset_return
[f49b]LDA a:TextBox_Timer; Load the character position to display.
[f49e]AND #$03; Check the first 2 bits.
[f4a0]BNE TextBox_ClearShopSizeAtOffset_return; If neither bit is set, return.
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_DisplayMessage
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_LoadAndShowMessage
;============================================================================
[f4a2]TextBox_DisplayMessage:; [$f4a2]
[f4a2]LDA a:Textbox_TitleCharOffset
[f4a5]BEQ TextBox_ShowMessage_LoadMessage
[f4a7]CMP #$90
[f4a9]BEQ @LAB_PRG15_MIRROR__f4c3
[f4ab]AND #$0f
[f4ad]STA Temp_Int24
;
; Convert the player's title to an index in the string list.
;
[f4af]LDA a:PlayerTitle; Load the player's title.
[f4b2]ASL A; Shift left 4, converting to an index.
[f4b3]ASL A
[f4b4]ASL A
[f4b5]ASL A
[f4b6]ORA Temp_Int24
[f4b8]TAX; X = A
[f4b9]LDA PLAYER_TITLE_STRINGS,X; Look up the string.
[f4bc]INC a:Textbox_TitleCharOffset
[f4bf]CMP #$20
[f4c1]BNE TextBox_ShowMessageWithSound
[f4c3]@LAB_PRG15_MIRROR__f4c3:; [$f4c3]
[f4c3]LDA #$00
[f4c5]STA 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]
[f4c8]LDA a:Message_StartAddr
[f4cb]STA Temp_Int24
[f4cd]LDA a:Message_StartAddr_U
[f4d0]STA Temp_Int24_M
[f4d2]LDY a:Message_ProcessedLength
[f4d5]INC a:Message_ProcessedLength
[f4d8]BNE @LAB_PRG15_MIRROR__f4dd
[f4da]INC a:Message_StartAddr_U
[f4dd]@LAB_PRG15_MIRROR__f4dd:; [$f4dd]
[f4dd]LDA (Temp_Int24),Y
[f4df]CMP #$ff
[f4e1]BEQ TextBox_ShowMessage_EndOfMessage
[f4e3]CMP #$fe
[f4e5]BEQ TextBox_ShowMessage_Newline
[f4e7]CMP #$fd
[f4e9]BEQ TextBox_ShowMessage_Space
[f4eb]CMP #$fb
[f4ed]BEQ TextBox_ShowMessage_ShowPlayerTitle
[f4ef]CMP #$fc
[f4f1]BEQ 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]
[f4f3]LDX a:TextBox_PlayTextSound
[f4f6]BEQ TextBox_ShowMessage
;
; Play the message sound effect.
;
[f4f8]PHA; Push A to the stack.
[f4f9]LDA #$01; 0x01 == Message sound effect.
[f4fb]JSR Sound_PlayEffect; Play it.
[f4fe]PLA; Pop A.
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
PasswordScreen_ShowNextChar
;
TextBox_ShowMessageWithSound
;============================================================================
[f4ff]TextBox_ShowMessage:; [$f4ff]
[f4ff]JSR TextBox_WriteChar
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_Space
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_LoadMessage
;============================================================================
[f502]TextBox_ShowMessage_Space:; [$f502]
;
; If it's 0xFD, it's a space.
;
[f502]INC a:TextBox_CharPosForLine
[f505]LDA a:TextBox_CharPosForLine
[f508]CMP #$10
[f50a]BCC TextBox_ShowMessage_Return
;
; Load the next character in the message string.
;
[f50c]LDA a:Message_StartAddr
[f50f]STA Temp_Int24
[f511]LDA a:Message_StartAddr_U
[f514]STA Temp_Int24_M
[f516]LDY a:Message_ProcessedLength
[f519]LDA (Temp_Int24),Y
;
; If it's 0xFF, the message ends.
;
[f51b]CMP #$ff
[f51d]BEQ TextBox_ShowMessage_EndOfMessage
;
; If it's 0xFC, finish this part of the message.
;
[f51f]CMP #$fc
[f521]BEQ TextBox_ShowMessage_Return
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_Newline
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_LoadMessage
;============================================================================
[f523]TextBox_ShowMessage_Newline:; [$f523]
;
; If it's 0xFE, it's a newline.
;
[f523]LDA a:TextBox_CharPosForLine
[f526]BEQ TextBox_ShowMessage_LoadMessage
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document TextBox_ShowMessage_IncLineAndReset
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_ShowMessage_Prepare4Lines
;============================================================================
[f528]TextBox_ShowMessage_IncLineAndReset:; [$f528]
;
; Increment the line and reset the character position.
;
[f528]LDA #$00; A = 0
[f52a]STA a:TextBox_CharPosForLine; Set as the character position.
[f52d]LDY a:TextBox_DrawnLineNum; Y = line number
[f530]INY; Y++
;
; Check if we've reached the end of the textbox.
; If so, prepare the state for the next 4 lines.
;
[f531]CPY #$04; Is the line number 4?
[f533]BEQ TextBox_ShowMessage_Fill4Lines; If so, jump.
;
; We're not at the end of the textbox. Increment and return.
;
[f535]STY a:TextBox_DrawnLineNum; Els, set as the number of drawn lines.
[f538]RTS
[f539]TextBox_ShowMessage_Prepare4Lines:; [$f539]
;
; Check if anything's ready to be written for this line.
;
[f539]LDA a:TextBox_CharPosForLine; Load the character position (1-based; 0 for not ready).
[f53c]BNE TextBox_ShowMessage_IncLineAndReset; If > 0, the line can be drawn.
[f53e]RTS
;============================================================================
; 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]
[f53f]LDA a:Message_StartAddr; Load the lower byte of the message string address.
[f542]STA Temp_Int24; Store it for length calculation.
[f544]LDA a:Message_StartAddr_U; Load the upper byte.
[f547]STA Temp_Int24_M; Store it.
;
; Check if the next byte is 0xFF, in which case we just want
; to end the message immediately.
;
[f549]LDY a:Message_ProcessedLength; Load the message length.
[f54c]LDA (Temp_Int24),Y; Load the next byte of the message.
[f54e]CMP #$ff; Is it 0xFF (end of message)?
[f550]BEQ TextBox_ShowMessage_EndOfMessage; If true, then end message.
;
; The message isn't ending. We're paused instead.
;
[f552]LDA #$ff
[f554]STA a:TextBox_MessagePaused; Set the message as paused.
[f557]TextBox_ShowMessage_Return:; [$f557]
[f557]RTS
[f558]TextBox_ShowMessage_EndOfMessage:; [$f558]
[f558]LDA #$00; A = 0
[f55a]STA a:MessageID; Store as the message ID (terminating message).
[f55d]RTS
[f55e]TextBox_ShowMessage_ShowPlayerTitle:; [$f55e]
;
; If it's 0xFB, the title rank will be inserted.
;
[f55e]LDA #$81; A = 0x81
[f560]STA a:Textbox_TitleCharOffset; Store it.
[f563]LDA a:PlayerTitle; Load the player's title.
[f566]ASL A; Convert to an index in the Player Titles table.
[f567]ASL A
[f568]ASL A
[f569]ASL A
[f56a]TAX; X = A
[f56b]LDA PLAYER_TITLE_STRINGS,X; Load the title string at X.
[f56e]JMP 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:; [$f571]
;
; Set the start position where text will be drawn.
;
[f571]LDA a:TextBox_X; X = textbox tile X coordinate.
[f574]CLC
[f575]ADC #$02; X += 2
[f577]STA TextBox_ContentsX; Store as the text X start position.
[f579]LDA a:TextBox_Y; Y = textbox tile Y coordinate.
[f57c]CLC
[f57d]ADC #$02; Y += 2
[f57f]STA 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]JSR PPU_SetAddrForTextBoxPos; Set the PPU address for the text position.
[f584]LDX a:TextBox_LineScrollOffset; X = scroll line offset
[f587]JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
[f58a]JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
[f58d]JSR TextBox_FillPlaceholderTextForLineCapped; Write a line and advance.
;
; Clear the final line.
;
[f590]LDA #$00; A = 0 (start offset)
[f592]LDY #$10; Y = 16 (line length)
[f594]JSR 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]LDA #$00; A = 0
[f599]STA PPU_TargetAddr; Store A as the lower byte of the PPU target address.
[f59b]LDA a:TextBox_LineScrollOffset; A = scroll-aware line offset.
[f59e]CLC
[f59f]ADC #$14; A += 20
[f5a1]STA PPU_TargetAddr_U; Store as the upper byte of the PPU target addres.
;
; Clear out each of the lines that were just written.
;
[f5a3]LDX #$10; X = 16
[f5a5]@LAB_PRG15_MIRROR__f5a5:; [$f5a5]
[f5a5]TXA; A = X
[f5a6]PHA; Push A to the stack.
[f5a7]LDA #$00; A = 0
[f5a9]LDY #$10; Y = 16
[f5ab]JSR PPUBuffer_WriteValueMany; Write '0' 16 times, clearing the line.
[f5ae]JSR PPU_IncrementAddrBy16; Increment the offset by 16.
[f5b1]PLA; Pop A from the
stack (now 16).
[f5b2]TAX; X = A
[f5b3]DEX; X--
[f5b4]BNE @LAB_PRG15_MIRROR__f5a5; If X > 0, loop.
;
; Increase the Y position to draw to by 3, after these lines.
;
[f5b6]INC TextBox_ContentsY; TextBox_ContentsY += 3
[f5b8]INC TextBox_ContentsY
[f5ba]INC TextBox_ContentsY
;
; Draw a line of text to fill the address.
;
; This will be:
;
; PQRSTUVWXYZ[\]^_
;
[f5bc]JSR PPU_SetAddrForTextBoxPos; Set the text draw position back.
[f5bf]LDA a:TextBox_LineScrollOffset; A = line scroll offset.
[f5c2]JSR TextBox_FillPlaceholderTextForLine; Write a line of text.
;
; Store the new line number offset.
;
[f5c5]INX; X++
[f5c6]TXA; A = X
[f5c7]AND #$03; Cap the line number from 0..4.
[f5c9]STA a:TextBox_LineScrollOffset; Store it.
[f5cc]RTS
;============================================================================
; 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]
[f5cd]INX; lineNum++
[f5ce]TXA; A = X
;
; Cap the linenum to lines 0-4.
;
[f5cf]AND #$03
;
; v-- Fall through --v
;
[f5d1]TextBox_FillPlaceholderTextForLine:; [$f5d1]
[f5d1]TAX; X = A
;
; Calculate a character offset for the line number.
; Lines are 16 characters per line.
;
[f5d2]ASL A; Multiply by 16.
[f5d3]ASL A
[f5d4]ASL A
[f5d5]ASL A
;
; Add 64, giving us a value of "A".
;
[f5d6]ADC #$40; Add 64 (character code of "A").
[f5d8]TAY; 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]
[f5d9]TXA; A = X (offset)
[f5da]PHA; Push A to the stack.
;
; Set the write length to 16 characters (max for a line).
;
[f5db]LDA #$10; A = 16
[f5dd]JSR PPUBuffer_QueueCommandOrLength; Queue this as the write length.
[f5e0]TYA; A = Y
;
; Begin the write loop.
;
[f5e1]LDY #$10; Y = 16
[f5e3]@_loop:; [$f5e3]
[f5e3]JSR PPUBuffer_Set; Write the value to the buffer.
[f5e6]CLC
[f5e7]ADC #$01; A++ (next character value)
[f5e9]DEY; Y--
[f5ea]BNE @_loop; If Y > 0, loop.
[f5ec]STX PPUBuffer_WriteOffset; Store the new offset as the PPU upper bounds.
[f5ee]PLA; Pop A from the stack.
[f5ef]TAX; X = A
[f5f0]JMP PPU_IncrementAddrBy32; Increment the PPU address by 32.
[f5f3]TextBox_WriteChar:; [$f5f3]
;
; Normalize the ASCII value into an index in the characters
; table.
;
[f5f3]SEC; C = 1
[f5f4]SBC #$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]LDX #$00; X = 0
[f5f8]STX Temp_Int24_M; Set middle byte of string address to 0.
[f5fa]ASL A; A = A * 8 (each glyph is 8 bytes)
[f5fb]ASL A
[f5fc]ROL Temp_Int24_M; Rotate upper byte left and add carry.
[f5fe]ASL A
[f5ff]ROL Temp_Int24_M
[f601]ADC #$00
[f603]STA Temp_Int24
;
; Make the address relative to $8000.
;
[f605]LDA Temp_Int24_M; Load the upper byte of of the address.
[f607]ADC #$80; Add 0x80 to it.
[f609]STA Temp_Int24_M; Store it back.
;
; Compute the PPU target address.
;
[f60b]LDA #$00; A = 0
[f60d]STA PPU_TargetAddr_U; Store as the starting upper byte of the PPU target address.
[f60f]LDA a:TextBox_CharPosForLine; A = character position.
[f612]ASL A; Convert to a 16 byte address boundary.
[f613]ASL A
[f614]ASL A
[f615]ASL A
[f616]STA PPU_TargetAddr; Store as the lower byte of the PPU target address.
[f618]LDA a:TextBox_DrawnLineNum; A = drawn line number.
[f61b]CLC
[f61c]ADC a:TextBox_LineScrollOffset; A += line scroll offset
[f61f]AND #$03; Cap to a 0..4 line number.
[f621]ADC #$14; A += 20
[f623]STA 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]LDA a:CurrentROMBank; A = current bank
[f628]PHA; Push A to the stack.
[f629]LDX #$0d; A = bank 13
[f62b]JSR MMC1_UpdateROMBank; Switch to it.
;
; Queue up the drawn line.
;
[f62e]LDA #$10; A = 16 (line length)
[f630]JSR PPUBuffer_QueueCommandOrLength; Queue as the line length.
[f633]JSR TextBox_Write8BytesFromTemp; Write the first half of the line.
[f636]JSR TextBox_Write8BytesFromTemp; Write the second half.
;
; Finish up and restore the bank.
;
[f639]STX PPUBuffer_WriteOffset; Store the new upper bounds for the PPU buffer.
[f63b]JMP 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]
[f63e]LDY #$00; Y = 0
[f640]@_loop:; [$f640]
[f640]JSR PPUBuffer_WriteFromTemp; Write a value from temp.
[f643]TYA; A = Y
[f644]AND #$07; Consider values 0-127.
[f646]BNE @_loop; If not 0, loop.
[f648]RTS
[f649]PLAYER_TITLE_STRINGS:; [$f649]
[f649].byte "Novice "; [$f649] char
[f651].byte " "; [$f651] char
[f659].byte "Aspirant"; [$f659] char
[f661].byte " "; [$f661] char
[f669].byte "Battler "; [$f669] char
[f671].byte " "; [$f671] char
[f679].byte "Fighter "; [$f679] char
[f681].byte " "; [$f681] char
[f689].byte "Adept "; [$f689] char
[f691].byte " "; [$f691] char
[f699].byte "Chevalie"; [$f699] char
[f6a1].byte "r "; [$f6a1] char
[f6a9].byte "Veteran "; [$f6a9] char
[f6b1].byte " "; [$f6b1] char
[f6b9].byte "Warrior "; [$f6b9] char
[f6c1].byte " "; [$f6c1] char
[f6c9].byte "Swordman"; [$f6c9] char
[f6d1].byte " "; [$f6d1] char
[f6d9].byte "Hero "; [$f6d9] char
[f6e1].byte " "; [$f6e1] char
[f6e9].byte "Soldier "; [$f6e9] char
[f6f1].byte " "; [$f6f1] char
[f6f9].byte "Myrmidon"; [$f6f9] char
[f701].byte " "; [$f701] char
[f709].byte "Champion"; [$f709] char
[f711].byte " "; [$f711] char
[f719].byte "Superher"; [$f719] char
[f721].byte "o "; [$f721] char
[f729].byte "Paladin "; [$f729] char
[f731].byte " "; [$f731] char
[f739].byte "Lord "; [$f739] char
[f741].byte " "; [$f741] char
[f749]PLAYER_TITLE_EXP_NEEDED:; [$f749]
[f749].word $03e8; [0]: Aspirant (1,000 exp)
[f74b]PLAYER_TITLE_EXP_NEEDED_1_:; [$f74b]
[f74b].word $0898; [1]: Battler (2,200 exp)
[f74d].word $0dac; [2]: Fighter (3,500 exp)
[f74f].word $12c0; [3]: Adept (4,800 exp)
[f751].word $1838; [4]: Chevalier (6,200 exp)
[f753].word $1f40; [5]: Veteran (8,000 exp)
[f755].word $2710; [6]: Warrior (10,000 exp)
[f757].word $30d4; [7]: Swordman (12,500 exp)
[f759].word $3a98; [8]: Hero (15,000 exp)
[f75b].word $4650; [9]: Soldier (18,000 exp)
[f75d].word $55f0; [10]: Myrmidon (22,000 exp)
[f75f].word $6590; [11]: Champion (26,000 exp)
[f761].word $7530; [12]: Superhero (30,000 exp)
[f763].word $88b8; [13]: Paladin (35,000 exp)
[f765].word $afc8; [14]: Lord (45,000 exp)
;============================================================================
; Starting gold at each player title.
;
; XREFS:
;
Player_SetInitialExpAndGold
;============================================================================
[f767]PLAYER_TITLE_GOLD:; [$f767]
[f767].word $01f4; [0]:
[f769].word $0320; [1]:
[f76b].word $04b0; [2]:
[f76d].word $0640; [3]:
[f76f].word $0834; [4]:
[f771].word $0af0; [5]:
[f773].word $0dac; [6]:
[f775].word $10cc; [7]:
[f777].word $1450; [8]:
[f779].word $1838; [9]:
[f77b].word $1d4c; [10]:
[f77d].word $2328; [11]:
[f77f].word $2904; [12]:
[f781].word $32c8; [13]:
[f783].word $3a98; [14]:
[f785]Player_GetInventoryIndexForItem:; [$f785]
[f785]LSR A; Shift right 5, turning inventory bits into an index.
[f786]LSR A
[f787]LSR A
[f788]LSR A
[f789]LSR A
[f78a]RTS
;============================================================================
; 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]
[f78b]ASL A; Shift left 5 (here and the next 4 after falling through).
[f78c]Math_MultiplyBy16:; [$f78c]
[f78c]ASL A; Shift left 4.
[f78d]ASL A
[f78e]ASL A
[f78f]ASL A
[f790]RTS
[f791]TextBox_GetBackingAttributeData:; [$f791]
;
; Preserve X and Y on the stack.
;
[f791]TYA; A = Y
[f792]PHA; Push it to the stack.
[f793]TXA; A = X
[f794]PHA; Push it to the stack.
;
; Preserve the current ROM bank.
;
[f795]LDA a:CurrentROMBank; A = Current ROM bank.
[f798]PHA; Push it to the stack.
;
; Switch to bank 3 (area metadata).
;
[f799]LDX #$03; X = 3 (Area metadata bank)
[f79b]JSR MMC1_UpdateROMBank; Switch to the bank.
[f79e]LDA a:TextBox_ContentsY; A = Tile Y position for the text in the textbox.
[f7a1]JSR Math_MultiplyBy16; Convert to an index in the block attributes data.
[f7a4]AND #$f0; Keep the upper nibble.
[f7a6]STA Temp_Int24; Store for the final result.
[f7a8]ORA a:TextBox_ContentsX; OR the index with the tile X position for the text.
[f7ab]SEC
[f7ac]SBC #$20; Subtract 32.
;
; Load the data attribute from the screen buffer at this index.
;
[f7ae]TAX; X = A (screen buffer lookup index)
[f7af]LDY ScreenBuffer,X; Y = Screen buffer data at the index.
[f7b2]LDA (CurrentArea_BlockAttributesAddr),Y; Load the block attributes at the given offset.
[f7b4]JMP textBox_GetAreaBehindTextBox_restoreAndReturn; Jump to finish loading.
;============================================================================
; TODO: Document Textbox_Maybe_GetAreaBehindTextbox
;
; INPUTS:
; Y
;
; OUTPUTS:
; A
;
; XREFS:
;
TextBox_Close
;============================================================================
[f7b7]Textbox_Maybe_GetAreaBehindTextbox:; [$f7b7]
[f7b7]TYA
[f7b8]PHA
[f7b9]TXA
[f7ba]PHA
[f7bb]LDA a:CurrentROMBank
[f7be]PHA
[f7bf]LDX #$03
[f7c1]JSR MMC1_UpdateROMBank
[f7c4]LDA a:TextBox_ContentsY
[f7c7]ASL A
[f7c8]ASL A
[f7c9]ASL A
[f7ca]AND #$f0
[f7cc]STA Temp_Int24
[f7ce]LDA a:TextBox_ContentsX
[f7d1]LSR A
[f7d2]ORA Temp_Int24
[f7d4]SEC
[f7d5]SBC #$20
[f7d7]TAX
[f7d8]LDY ScreenBuffer,X
[f7db]LDA a:TextBox_ContentsX
[f7de]AND #$01
[f7e0]STA Temp_Int24
[f7e2]LDA a:TextBox_ContentsY
[f7e5]ASL A
[f7e6]AND #$02
[f7e8]ORA Temp_Int24
[f7ea]ASL A
[f7eb]TAX
[f7ec]LDA CurrentArea_BlockData1StartAddr,X
[f7ee]STA Temp_Int24
[f7f0]LDA CurrentArea_BlockData1StartAddr+1,X
[f7f2]STA Temp_Int24_M
[f7f4]LDA (Temp_Int24),Y
[f7f6]textBox_GetAreaBehindTextBox_restoreAndReturn:; [$f7f6]
[f7f6]STA Temp_Int24; Store the result temporarily.
;
; Restore the previous ROM bank.
;
[f7f8]PLA; Pull the save ROM bank from the stack.
[f7f9]TAX; X = A (ROM bank).
[f7fa]JSR MMC1_UpdateROMBank; Restore the bank.
;
; Restore the original X and Y registers from the stack.
;
[f7fd]PLA; Pull the original X from the stack.
[f7fe]TAX; Set back as X.
[f7ff]PLA; Pull the original Y from the stack.
[f800]TAY; Set back as Y.
;
; Load the result we calculated and return it.
;
[f801]LDA Temp_Int24; Load the calculated result.
[f803]RTS; And return it.
[f804]PPU_SetAddrForTextBoxPos:; [$f804]
[f804]LDA PPU_ScrollScreen; A = Scroll screen used for drawing.
[f806]AND #$01; Convert to 0/1 (even/odd).
[f808]ORA #$08; A += 8
[f80a]STA PPU_TargetAddr_U; Store as the PPU target address.
[f80c]LDA a:TextBox_ContentsY; Load the text Y position.
[f80f]ASL A; A = A * 16
[f810]ASL A
[f811]ASL A
[f812]ASL A
[f813]ROL PPU_TargetAddr_U; Rotate the upper byte of the target address left.
[f815]ASL A; A << 1
[f816]ROL PPU_TargetAddr_U; Rotate the upper byte of the target address left.
[f818]ORA a:TextBox_ContentsX; OR it with the X position.
[f81b]STA PPU_TargetAddr; Store as the lower byte of the target address.
[f81d]RTS
[f81e]PPU_IncrementAddrBy16:; [$f81e]
[f81e]LDA #$10; A = 16
[f820]BNE 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]
[f822]LDA #$08; A = 8
[f824]BNE PPU_IncrementAddrBy; Unconditionally jump to PPU_IncrementAddrBy.
[f826]PPU_IncrementAddrBy32:; [$f826]
[f826]LDA #$20; Set the offset to 32.
;
; v-- Fall through --v
;
[f828]PPU_IncrementAddrBy:; [$f828]
[f828]CLC
[f829]ADC PPU_TargetAddr; A += PPU_TargetAddr
[f82b]BCC @_storeAndReturn; If it overflowed, jump. No need to increment the upper byte.
[f82d]INC PPU_TargetAddr_U; Else, increment the upper byte by 1.
[f82f]@_storeAndReturn:; [$f82f]
[f82f]STA PPU_TargetAddr; Set the new target address.
[f831]RTS
;============================================================================
; 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]
[f832]LDA a:TextBox_Width; A = Textbox width
[f835]TAY; Y = A
[f836]JMP 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]
[f839]JSR PPUBuffer_Set; Set the value in the buffer at the given offset.
[f83c]DEY; Y--
[f83d]CPY #$02; Is it 2?
[f83f]BNE TextBox_FillPPUBufferForTextWidth; If not, loop.
[f841]RTS
;============================================================================
; 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]
[f842]LDA (Temp_Int24),Y; Load the value from Temp_Int24 at offset Y.
[f844]INY; Y++
;
; v-- Fall through --v
;
[f845]PPUBuffer_Set:; [$f845]
[f845]STA PPUBuffer,X; Store A in the PPU buffer at X.
[f848]INX; X++
[f849]RTS
;============================================================================
; 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]
[f84a]PHA; Push A to the stack
[f84b]TYA; A = Y (length)
[f84c]JSR PPUBuffer_QueueCommandOrLength; Write the length to the buffer.
[f84f]PLA; Pop A from the stack
[f850]@_loop:; [$f850]
[f850]JSR PPUBuffer_Set; Set the value in the PPU buffer.
[f853]DEY; Y--
[f854]BNE @_loop; If Y > 0, loop
[f856]STX PPUBuffer_WriteOffset; Set the upper bounds to write.
[f858]RTS; Return with the new offset.
[f859]MMC1_LoadBankAndJump:; [$f859]
;
; Save out the X, Y, and Z values to temporary variables.
;
[f859]STA BankedCallSetup_SavedA; Set BankedCallSetup_SavedA = A
[f85b]STX BankedCallSetup_SavedX; Set BankedCallSetup_SavedX = X
[f85d]STY 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]PLA; Pop A from the stack.
[f860]STA Temp_Int24; Lower byte of Temp_Int24 = A
[f862]PLA; Pull A from the stack.
[f863]STA Temp_Int24_M; Middle byte of Temp_Int24 = A
;
; Save A to X. We'll increment this below if we overflow
; the add.
;
[f865]TAX; X = A
;
; Compute a new return low =
Temp_Int24.L + 3.
;
[f866]LDA Temp_Int24; A = lower byte of Temp_Int24.
[f868]CLC; C = 0
[f869]ADC #$03; A = A + 3
[f86b]STA Temp_Int24_U; Upper byte of Temp_Int24 = A
;
; If the upper byte overflowed, increment X.
;
[f86d]BCC @_storeCurrentAddress; If carry is cleared, jump.
[f86f]INX; X = X + 1
;
; Preserve the target address and ROM bank on the stack.
;
[f870]@_storeCurrentAddress:; [$f870]
[f870]TXA; A = X (our adjusted offset)
[f871]PHA; Push A to stack
[f872]LDA Temp_Int24_U; A = upper byte of Temp_Int24
[f874]PHA; Push A to stack
[f875]LDA a:CurrentROMBank; A = CurrentROMBank
[f878]PHA; Push A to stack
;
; Store the loaded address and bank to jump to.
;
[f879]LDY #$03; Y = 3
[f87b]LDA (Temp_Int24),Y; A = Upper byte of the address to jump to.
[f87d]STA Maybe_Temp4; Maybe_Temp4 = A
[f87f]DEY; Y-- (2)
[f880]LDA (Temp_Int24),Y; A = Lower byte of the address to jump to.
[f882]STA Temp_Int24_U; Upper byte of Temp_Int24 = A
[f884]DEY; Y-- (1)
[f885]LDA (Temp_Int24),Y; A = Bank to load
[f887]TAX; X = A
[f888]JSR MMC1_UpdateROMBank; Update the ROM bank to X.
;
; Push the trampoline address $C5F8.
;
[f88b]LDA #$f8; A = 0xF8
[f88d]PHA; Push A to stack
[f88e]LDA #$c5; A = 0xC5
[f890]PHA; Push A to stack
;
; Push the target address for the trampoline.
;
[f891]LDA Maybe_Temp4; A = Maybe_Temp4
[f893]PHA; Push A to stack
[f894]LDA Temp_Int24_U; A = upper byte of Temp_Int24.
[f896]PHA; Push A to stack
;
; Restore the original values for A, X, and Y.
;
[f897]LDA BankedCallSetup_SavedA; A = BankedCallSetup_SavedA
[f899]LDX BankedCallSetup_SavedX; X = BankedCallSetup_SavedX
[f89b]LDY BankedCallSetup_SavedY; Y = BankedCallSetup_SavedY
[f89d]RTS
;============================================================================
; TODO: Document PPU_WriteTilesFromCHRRAM
;
; INPUTS:
; X
; Y
;
; OUTPUTS:
; TODO
;
; XREFS:
;
SplashAnimation_DrawScenery
;
StartScreen_Draw
;
UI_DrawHUDSprites
;============================================================================
[f89e]PPU_WriteTilesFromCHRRAM:; [$f89e]
[f89e]LDA a:CurrentROMBank
[f8a1]PHA
[f8a2]JSR MMC1_UpdateROMBank
[f8a5]STY Temp_Int24
[f8a7]LDA PPU_TargetAddr_U
[f8a9]STA a:PPUADDR
[f8ac]LDA PPU_TargetAddr
[f8ae]STA a:PPUADDR
[f8b1]LDY #$00
[f8b3]@LAB_PRG15_MIRROR__f8b3:; [$f8b3]
[f8b3]LDX #$10
[f8b5]@LAB_PRG15_MIRROR__f8b5:; [$f8b5]
[f8b5]LDA (IScriptOrCHRAddr),Y
[f8b7]STA a:PPUDATA
[f8ba]INY
[f8bb]BNE @LAB_PRG15_MIRROR__f8bf
[f8bd]INC IScriptOrCHRAddr_U
[f8bf]@LAB_PRG15_MIRROR__f8bf:; [$f8bf]
[f8bf]DEX
[f8c0]BNE @LAB_PRG15_MIRROR__f8b5
[f8c2]DEC Temp_Int24
[f8c4]BNE @LAB_PRG15_MIRROR__f8b3
[f8c6]PLA
[f8c7]TAX
[f8c8]JMP MMC1_UpdateROMBank
;============================================================================
; PPU address locations where statuc symbols are positioned.
;
; XREFS:
;
UI_DrawHUDSprites
;============================================================================
[f8cb]UI_STATUS_SYMBOL_PPU_ADDR_L:; [$f8cb]
[f8cb].byte $41; [0]: 0x2041 -- "M" (Magic)
[f8cc]UI_STATUS_SYMBOL_PPU_ADDR_L_1_:; [$f8cc]
[f8cc].byte $61; [1]: 0x2061 -- "P" (Power)
[f8cd].byte $6e; [2]: 0x206E -- "G" (Gold)
[f8ce].byte $4e; [3]: 0x204E -- "E" (Experience)
[f8cf].byte $56; [4]: 0x2056 -- "T" (Time)
[f8d0].byte $5b; [5]: 0x205B -- Top-left of "[" (Selected item)
[f8d1].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]
[f8d2].byte $1c; [0]: "M" (Magic)
[f8d3]UI_STATUS_TILES_1_:; [$f8d3]
[f8d3].byte $0a; [1]: ":"
[f8d4]UI_STATUS_TILES_2_:; [$f8d4]
[f8d4].byte $00; [2]:
[f8d5].byte $1f; [3]: "P" (Power)
[f8d6].byte $0a; [4]: ":"
[f8d7].byte $00; [5]:
[f8d8].byte $16; [6]: "G" (Gold)
[f8d9].byte $3a; [7]: ":"
[f8da].byte $00; [8]:
[f8db].byte $14; [9]: "E" (Experience)
[f8dc].byte $3a; [10]: ":"
[f8dd].byte $00; [11]:
[f8de].byte $23; [12]: "T" (Time)
[f8df].byte $3a; [13]: ":"
[f8e0].byte $00; [14]:
[f8e1].byte $2c; [15]: Top of "[" (current item)
[f8e2].byte $3c; [16]: <blank space>
[f8e3].byte $3d; [17]: <blank space>
[f8e4].byte $2e; [18]: Top of "]" (current item)
[f8e5].byte $00; [19]:
[f8e6].byte $2d; [20]: Bottom of "[" (current item)
[f8e7].byte $3e; [21]: <blank space>
[f8e8].byte $3f; [22]: <blank space>
[f8e9].byte $2f; [23]: Bottom of "]" (current item)
[f8ea].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]
[f8eb]LDA #$0a
[f8ed]STA a:UI_MPAndHPBarWidth
;
; Fill the entire status area with 0 (blank).
; This is 4 rows of tiles.
;
[f8f0]LDA #$20; Set the upper byte for the draw position.
[f8f2]STA a:PPUADDR; Store it.
[f8f5]LDA #$00; Set the lower byte for the draw position.
[f8f7]STA a:PPUADDR; Store it.
[f8fa]LDY #$80; Set Y = 128 (number of tiles to draw)
[f8fc]LDA #$00; Set the value to draw (blank tile).
[f8fe]JSR 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]LDX #$00; Set X = 0 (start index in UI_STATUS_SYMBOL_PPU_ADDR_L.
[f903]LDY #$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]
[f905]LDA #$20
[f907]STA a:PPUADDR; Store the upper draw position byte as 0x20.
[f90a]LDA UI_STATUS_SYMBOL_PPU_ADDR_L,X; Load the lower byte from the lookup table based on the index.
[f90d]STA 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]
[f910]LDA UI_STATUS_TILES,Y; Load the tile to draw at the current tile index.
[f913]BEQ @_advanceIndexes; If this is 0x00, we've reached the end of the run. Advance indexes.
[f915]STA a:PPUDATA; Else, write the tile.
[f918]INY; tileIndex++
[f919]BNE @_drawTiles; Loop.
;
; Advance to the next set of tiles and the next lower address
; (draw position) in the lookup tables.
;
[f91b]@_advanceIndexes:; [$f91b]
[f91b]INY; tileIndex++
[f91c]INX; position++
;
; Check if we've completed the lookup table.
;
; There's only 7 addresses/positions to work with.
;
[f91d]CPX #$07; Have we reached the end (draw position 7)?
[f91f]BNE @_goToNextPosition; If not, loop.
;
; We've finished drawing the status UI.
;
[f921]JSR UI_DrawSelectedItem
[f924]LDA #$40
[f926]STA IScriptOrCHRAddr
[f928]LDA #$81
[f92a]STA IScriptOrCHRAddr_U
[f92c]LDA #$00
[f92e]STA PPU_TargetAddr
[f930]LDA #$10
[f932]STA PPU_TargetAddr_U
[f934]LDX #$0a
[f936]LDY #$3c
[f938]JSR PPU_WriteTilesFromCHRRAM
[f93b]RTS
;============================================================================
; 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]
[f93c]LDA a:NextPlayerTitle
[f93f]CMP #$0f; Have we hit the max title? (There are 15)
[f941]BEQ @_return; If yes, exit.
[f943]ASL A; Convert to an index in the lookup table.
;
; Load the 16-bit experience level for the title.
;
[f944]TAX
[f945]LDA a:Experience; Load the lower byte from the lookup table.
[f948]CMP PLAYER_TITLE_EXP_NEEDED,X; Compare it.
[f94b]LDA a:Experience_U; Load the upper byte from the lookup table.
[f94e]SBC PLAYER_TITLE_EXP_NEEDED+1,X; Compare it.
[f951]BCC @_return; If we're under, exit.
;
; The player has met the next title's requirements.
;
[f953]INC a:NextPlayerTitle; We have enough. Increase the player's next title.
[f956]@_return:; [$f956]
[f956]RTS
;============================================================================
; Update experience for the player.
;
; This will update from upper and lower values in RAM.
;
; INPUTS:
;
Temp_Int24:
; The lower byte of experience to add.
;
;
Temp_Int24+1:
; The upper byte of experience to add.
;
; OUTPUTS:
; None
;
; XREFS:
;
Player_AddExperienceFromSprite
;
Player_Add100XP
;============================================================================
[f957]Player_UpdateExperience:; [$f957]
;
; Update the lower byte of experience.
;
[f957]LDA a:Experience; Load the current lower byte of experience.
[f95a]CLC
[f95b]ADC Temp_Int24
[f95d]STA a:Experience; Set the new lower byte of experience.
;
; Update the upper byte of experience.
;
[f960]LDA a:Experience_U; Load the current upper byte of experience.
[f963]ADC Temp_Int24_M
[f965]STA a:Experience_U; Set the new upper byte of experience.
[f968]BCC @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]LDA #$ff
[f96c]STA a:Experience; Set lower byte of experience to max.
[f96f]STA a:Experience_U; Set upper byte of experience to max.
[f972]@LAB_PRG15_MIRROR__f972:; [$f972]
[f972]JSR 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:; [$f975]
;
; Set the address of the first digit of the experience to write
; (0x2050).
;
[f975]LDA #$20
[f977]STA PPU_TargetAddr_U; Store 0x20 as the upper byte.
[f979]LDA #$50
[f97b]STA PPU_TargetAddr; Store 0x50 as the upper byte.
[f97d]LDA a:Experience; Load the lower byte of the experience.
[f980]STA Temp_Int24; Save to Temp_Int24.
[f982]LDA a:Experience_U; Load the upper byte of the experience.
[f985]STA Temp_Int24_M; Save to Temp_Int24+1.
[f987]LDA #$00
[f989]STA Temp_Int24_U
[f98b]LDY #$05
[f98d]JMP UI_DrawDigitsZeroPadded; Trigger the render.
[f990]UI_DrawTimeValue:; [$f990]
;
; Set the number of seconds remaining as a 24-bit integer.
;
; Only the lower byte is used. The rest are 0'd out.
;
[f990]STA Temp_Int24; Set number of seconds as the lower byte.
[f992]LDA #$00
[f994]STA Temp_Int24_M; Set middle byte = 0.
[f996]STA Temp_Int24_U; Set upper byte = 0.
;
; Set the draw position (first digit of time remaining).
; This is 0x2058.
;
[f998]LDA #$20
[f99a]STA PPU_TargetAddr_U; Set high byte of position as 0x20.
[f99c]LDA #$58
[f99e]STA PPU_TargetAddr; Set high byte of position as 0x58.
;
; Draw the digits with zero-padding.
;
[f9a0]LDY #$02; Set the number of digits to draw.
[f9a2]JMP 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:; [$f9a5]
;
; Subtract from the lower byte.
;
[f9a5]LDA a:Gold; Load the lower byte of the current amount.
[f9a8]SEC
[f9a9]SBC Temp_Int24; Subtract the provided value.
[f9ab]STA a:Gold; Store as the new lower byte.
;
; Subtract from the middle byte.
;
[f9ae]LDA a:Gold_M; Load the middle byte of the current amount.
[f9b1]SBC Temp_Int24_M; Subtract the provided value and the carry.
[f9b3]STA a:Gold_M; Store as the new middle byte.
;
; Subtract from the upper byte.
;
[f9b6]LDA a:Gold_U; Load the upper byte of the current amount.
[f9b9]SBC #$00; Subtract the carry.
[f9bb]STA a:Gold_U; Store as the new upper byte.
;
; Draw the new amount.
;
[f9be]JMP 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:; [$f9c1]
;
; Add to the lower byte.
;
[f9c1]LDA a:Gold; Load the lower byte of the current amount.
[f9c4]CLC
[f9c5]ADC Temp_Int24; Add the new amount.
[f9c7]STA a:Gold; Store as the new lower byte.
;
; Add to the middle byte.
;
[f9ca]LDA a:Gold_M; Load the middle byte of the current amount.
[f9cd]ADC Temp_Int24_M; Add the new amount + carry.
[f9cf]STA a:Gold_M; Store as the new middle byte.
;
; Add to the upper byte.
;
[f9d2]LDA a:Gold_U; Load the upper byte of the current amount.
[f9d5]ADC #$00; Add the carry.
[f9d7]STA 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]BCC UI_DrawGoldValue; If no carry, draw the current amount.
;
; Carry was set. We're maxed. Make it official.
;
[f9dc]LDA #$ff
[f9de]STA a:Gold; Set lower to max.
[f9e1]STA a:Gold_M; Set middle to max.
[f9e4]STA a:Gold_U; Set upper to max.
;
; v-- Fall through --v
;
[f9e7]UI_DrawGoldValue:; [$f9e7]
;
; Set the draw position for the first digit of gold.
; This is 0x2070.
;
[f9e7]LDA #$20
[f9e9]STA PPU_TargetAddr_U; Set the upper byte to 0x20.
[f9eb]LDA #$70
[f9ed]STA PPU_TargetAddr; Set the lower byte to 0x70.
;
; Load the gold into the 24-bit temporary integer used for
; drawing digits.
;
[f9ef]LDA a:Gold; Load the lower byte of the gold value.
[f9f2]STA Temp_Int24; Set a the lower byte for the digits to draw.
[f9f4]LDA a:Gold_M; Load the middle byte of the gold value.
[f9f7]STA Temp_Int24_M; Set a the middle byte for the digits to draw.
[f9f9]LDA a:Gold_U; Load the upper byte of the gold value.
[f9fc]STA Temp_Int24_U; Set a the upper byte for the digits to draw.
;
; Draw the new amount as 7 digits, zero-padded.
;
[f9fe]LDY #$07
[fa00]JMP 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]
[fa03]JSR 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]
[fa06]TYA; A = Y (number of digits).
[fa07]PHA; Push it to the stack.
[fa08]JSR UI_PopulateDigits; Populate the ASCII digits to render.
[fa0b]UI_DrawDigitsZeroPadded_Populated:; [$fa0b]
[fa0b]PLA; Pull the number of digits from the stack.
[fa0c]TAY; Y = A
[fa0d]JSR PPUBuffer_QueueCommandOrLength; Set as the number of tiles to draw in the PPU buffer.
[fa10]STY Temp_Int24; Store the number temporarily.
[fa12]LDA #$07; A = 7 (maximum number of digits).
[fa14]SEC
[fa15]SBC Temp_Int24; Subtract the number of tiles to draw.
[fa17]TAY; Y = result (loop counter).
[fa18]@_drawDigitsLoop:; [$fa18]
[fa18]LDA UI_DigitsToRender,Y; Load the digit to draw at this index.
[fa1b]JSR PPUBuffer_Set; Set it in the PPU buffer.
[fa1e]INY; Y++
[fa1f]CPY #$07; Is it 7 (max)?
[fa21]BNE @_drawDigitsLoop; If not, loop.
[fa23]STX PPUBuffer_WriteOffset; Store the resulting PPU buffer write offset.
[fa25]RTS
;============================================================================
; 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]
[fa26]JSR PPU_SetAddrForTextBoxPos; Set the PPU position address for the text.
[fa29]TYA; A = Y (number of digits)
[fa2a]PHA; Push it to the stack.
[fa2b]JSR UI_PopulateDigitsNoLeadingZeroes; Populate the digits with no leading zeros.
[fa2e]JMP 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]
[fa31]JSR UI_PopulateDigits; Populate the ASCII digits.
[fa34]INX; Start at the provided index + 1.
;
; Check if the digit displays "0". If not, we're done.
;
[fa35]@_loop:; [$fa35]
[fa35]LDA UI_DigitsToRender,X; Load the current ASCII digit.
[fa38]CMP #$30; Is it an ASCII "0"?
[fa3a]BNE @_return; If not, we're done.
;
; Clear out the digit entirely.
;
[fa3c]LDA #$00; NUL out this digit.
[fa3e]STA UI_DigitsToRender,X; Store that as the new digit value.
[fa41]INX; i++
[fa42]CPX #$06; Are we at the end of the loop?
[fa44]BNE @_loop; If not, loop again.
[fa46]@_return:; [$fa46]
[fa46]RTS
;============================================================================
; 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:; [$fa47]
;
; Prepare to loop 7 times (the max number of digits).
;
[fa47]LDX #$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]
[fa49]JSR UI_GetValueForDigit; Get the value for this digit.
[fa4c]ORA #$30; Normalize the value to an ASCII digit.
[fa4e]STA UI_DigitsToRender,X; Store in the target position in RAM.
[fa51]DEX; i--
[fa52]BPL @_loop; Loop if we're not done.
[fa54]RTS
;============================================================================
; 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]
[fa55]LDY #$18; Prepare to loop over the 24 bits.
[fa57]LDA #$00; Set A (our value) = 0.
[fa59]@_loop:; [$fa59]
[fa59]ASL Temp_Int24; Shift the low byte << 1;
Set C as the out going bit.
[fa5b]ROL Temp_Int24_M; Set mid byte = (mid << 1) | C
[fa5d]ROL Temp_Int24_U; Set high byte = (high << 1) | C
[fa5f]ROL 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]CMP #$0a; Is A >= 10?
[fa62]BCC @_finishLoopIter; Branch if not.
[fa64]SBC #$0a; A >= 10, so subtract 10.
[fa66]INC Temp_Int24; Set quotient bit to 1.
[fa68]@_finishLoopIter:; [$fa68]
[fa68]DEY; i-- (process next bit)
[fa69]BNE @_loop; Loop until we hit 0.
[fa6b]RTS
[fa6c]STATUS_BAR_PPU_ADDR_L:; [$fa6c]
[fa6c].byte $63; [0]: Power bar
[fa6d]STATUS_BAR_PPU_ADDR_L_1_:; [$fa6d]
[fa6d].byte $43; [1]: Mana bar
[fa6e].byte $08; [0]: Power bar
[fa6f]BYTE_ARRAY_PRG15_MIRROR__fa6e_1_:; [$fa6f]
[fa6f].byte $09; [1]: Manab ar
[fa70].byte $0c; [0]: Power bar
[fa71]BYTE_ARRAY_PRG15_MIRROR__fa70_1_:; [$fa71]
[fa71].byte $0d; [1]: Mana bar
[fa72]BYTE_ARRAY_PRG15_MIRROR__fa72:; [$fa72]
[fa72].byte $c0; [0]:
[fa73]BYTE_ARRAY_PRG15_MIRROR__fa72_1_:; [$fa73]
[fa73].byte $d0; [1]:
[fa74].byte $60; [2]:
;============================================================================
; TODO: Document UI_DrawPlayerHPValue
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
IScriptAction_AddHP
;
Player_AddHP
;
UI_DrawPlayerHP
;============================================================================
[fa75]UI_DrawPlayerHPValue:; [$fa75]
;
; Cap the amount of HP to 80, if over.
;
[fa75]CMP #$51
[fa77]BCC @LAB_PRG15_MIRROR__fa7b
[fa79]LDA #$50
[fa7b]@LAB_PRG15_MIRROR__fa7b:; [$fa7b]
[fa7b]STA a:Temp_AddedHPValue
[fa7e]STA a:Player_HP_U
[fa81]LDY #$00
[fa83]BEQ UI_DrawManaOrHPBar
;
; v-- Fall through --v
;
;============================================================================
; Set the number of mana points for the player.
;
; Once set, the mana bar will be drawn to reflect the new amount.
;
; INPUT:
; A:
; The mana points to set.
;
; OUTPUT:
; None
;
; XREFS:
;
IScriptAction_AddMP
;
Player_AddMP
;
Player_ReduceMP
;
UI_DrawHUD
;============================================================================
[fa85]Player_SetMP:; [$fa85]
;
; Cap the number of mana points to 80, if over.
;
[fa85]CMP #$51; Check if over 80 mana points.
[fa87]BCC @_storePoints; If not over, we'll store what was passed in.
[fa89]LDA #$50; Cap to 80 mana points.
[fa8b]@_storePoints:; [$fa8b]
[fa8b]STA a:Player_MP; Set the player's current mana points.
[fa8e]LDY #$01
;
; v-- Fall through --v
;
;============================================================================
; TODO: Document UI_DrawManaOrHPBar
;
; INPUTS:
; A
; Y
;
; OUTPUTS:
; TODO
;
; XREFS:
;
UI_DrawPlayerHPValue
;============================================================================
[fa90]UI_DrawManaOrHPBar:; [$fa90]
;
; Set the upper byte of the 24-bit integer of the value to draw.
;
[fa90]STA Temp_Int24_U
[fa92]LDA a:UI_MPAndHPBarWidth
[fa95]ASL A
[fa96]ASL A
[fa97]ASL A
[fa98]CMP Temp_Int24_U
[fa9a]BCS @LAB_PRG15_MIRROR__fa9e
[fa9c]STA Temp_Int24_U
[fa9e]@LAB_PRG15_MIRROR__fa9e:; [$fa9e]
[fa9e]STY Maybe_Temp4
;
; 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.
;
[faa0]LDA #$20
[faa2]STA PPU_TargetAddr_U
[faa4]LDA STATUS_BAR_PPU_ADDR_L,Y
[faa7]STA PPU_TargetAddr
[faa9]LDX a:UI_MPAndHPBarWidth
[faac]INX
[faad]TXA
[faae]JSR PPUBuffer_QueueCommandOrLength
[fab1]LDA Temp_Int24_U
[fab3]LSR A
[fab4]LSR A
[fab5]LSR A
[fab6]BEQ @LAB_PRG15_MIRROR__fac6
[fab8]PHA
[fab9]STA Temp_Int24
[fabb]LDA $fa6e,Y
[fabe]@LAB_PRG15_MIRROR__fabe:; [$fabe]
[fabe]JSR PPUBuffer_Set
[fac1]DEC Temp_Int24
[fac3]BNE @LAB_PRG15_MIRROR__fabe
[fac5]PLA
[fac6]@LAB_PRG15_MIRROR__fac6:; [$fac6]
[fac6]CMP a:UI_MPAndHPBarWidth
[fac9]BEQ @LAB_PRG15_MIRROR__fae2
[facb]PHA
[facc]LDA $fa70,Y
[facf]JSR PPUBuffer_Set
[fad2]PLA
[fad3]TAY
[fad4]LDA #$07
[fad6]@LAB_PRG15_MIRROR__fad6:; [$fad6]
[fad6]INY
[fad7]CPY a:UI_MPAndHPBarWidth
[fada]BEQ @LAB_PRG15_MIRROR__fae2
[fadc]JSR PPUBuffer_Set
[fadf]JMP @LAB_PRG15_MIRROR__fad6
[fae2]@LAB_PRG15_MIRROR__fae2:; [$fae2]
[fae2]LDA #$0b
[fae4]JSR PPUBuffer_Set
[fae7]STX PPUBuffer_WriteOffset
[fae9]LDA Temp_Int24_U
[faeb]AND #$07
[faed]JSR Math_MultiplyBy16
[faf0]TAY
[faf1]LDA #$10
[faf3]STA PPU_TargetAddr_U
[faf5]LDX Maybe_Temp4
[faf7]LDA BYTE_ARRAY_PRG15_MIRROR__fa72,X
[fafa]STA PPU_TargetAddr
[fafc]LDA #$10
[fafe]JSR PPUBuffer_QueueCommandOrLength
[fb01]LDA Maybe_Temp4
[fb03]BNE @LAB_PRG15_MIRROR__fb14
;
; DEADCODE: All this is wired off behind
Maybe_Temp4, which
; is never actually set. This appears to be code they
; were working on but never completed/got right. Which
; would not be a shock since part of it reaches into
; program code instead of an array of values.
;
[fb05]@LAB_PRG15_MIRROR__fb05:; [$fb05]
[fb05]LDA BYTE_PRG15_MIRROR__fb2f,Y
[fb08]INY
[fb09]JSR PPUBuffer_Set
[fb0c]TYA
[fb0d]AND #$0f
[fb0f]BNE @LAB_PRG15_MIRROR__fb05
[fb11]STX PPUBuffer_WriteOffset
[fb13]RTS
[fb14]@LAB_PRG15_MIRROR__fb14:; [$fb14]
[fb14]LDA BYTE_PRG15_MIRROR__fb37,Y
[fb17]INY
[fb18]JSR PPUBuffer_Set
[fb1b]TYA
[fb1c]AND #$07
[fb1e]BNE @LAB_PRG15_MIRROR__fb14
;
; XXX Is this correct? It's reading from program code.
;
[fb20]@LAB_PRG15_MIRROR__fb20:; [$fb20]
[fb20]LDA @LAB_PRG15_MIRROR__fb27,Y
[fb23]INY
[fb24]JSR PPUBuffer_Set
[fb27]@LAB_PRG15_MIRROR__fb27:; [$fb27]
[fb27]TYA
[fb28]AND #$07
[fb2a]BNE @LAB_PRG15_MIRROR__fb20
[fb2c]STX PPUBuffer_WriteOffset
[fb2e]RTS
[fb2f]BYTE_PRG15_MIRROR__fb2f:; [$fb2f]
[fb2f].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb2f] byte
[fb37]BYTE_PRG15_MIRROR__fb37:; [$fb37]
[fb37].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb37] byte
[fb3f].byte $00,$ff,$80,$80,$80,$80,$ff,$00; [$fb3f] byte
[fb47].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb47] byte
[fb4f].byte $00,$ff,$c0,$c0,$c0,$c0,$ff,$00; [$fb4f] byte
[fb57].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb57] byte
[fb5f].byte $00,$ff,$e0,$e0,$e0,$e0,$ff,$00; [$fb5f] byte
[fb67].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb67] byte
[fb6f].byte $00,$ff,$f0,$f0,$f0,$f0,$ff,$00; [$fb6f] byte
[fb77].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb77] byte
[fb7f].byte $00,$ff,$f8,$f8,$f8,$f8,$ff,$00; [$fb7f] byte
[fb87].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb87] byte
[fb8f].byte $00,$ff,$fc,$fc,$fc,$fc,$ff,$00; [$fb8f] byte
[fb97].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fb97] byte
[fb9f].byte $00,$ff,$fe,$fe,$fe,$fe,$ff,$00; [$fb9f] byte
[fba7].byte $00,$ff,$00,$00,$00,$00,$ff,$00; [$fba7] byte
;============================================================================
; TODO: Document UI_DrawSelectedItem
;
; INPUTS:
; None.
;
; OUTPUTS:
; TODO
;
; XREFS:
;
UI_DrawHUDSprites
;============================================================================
[fbaf]UI_DrawSelectedItem:; [$fbaf]
[fbaf]LDA #$13
[fbb1]STA a:PPUADDR
[fbb4]LDA #$c0
[fbb6]STA a:PPUADDR
[fbb9]LDA a:SelectedItem
[fbbc]BPL @LAB_PRG15_MIRROR__fbc5
[fbbe]LDY #$40
[fbc0]LDA #$00
[fbc2]JMP PPU_FillData
[fbc5]@LAB_PRG15_MIRROR__fbc5:; [$fbc5]
[fbc5]ASL A
[fbc6]ASL A
[fbc7]TAY
[fbc8]LDA a:CurrentROMBank
[fbcb]PHA
[fbcc]LDX #$0a
[fbce]JSR MMC1_UpdateROMBank
[fbd1]@LAB_PRG15_MIRROR__fbd1:; [$fbd1]
[fbd1]LDA ITEM_TILEMAP_INDEXES,Y
[fbd4]JSR UI_Maybe_GetItemSpritePPUTileAddr
[fbd7]TYA
[fbd8]PHA
[fbd9]LDY #$00
[fbdb]@LAB_PRG15_MIRROR__fbdb:; [$fbdb]
[fbdb]LDA (Temp_Int24),Y
[fbdd]STA a:PPUDATA
[fbe0]INY
[fbe1]CPY #$10
[fbe3]BNE @LAB_PRG15_MIRROR__fbdb
[fbe5]PLA
[fbe6]TAY
[fbe7]INY
[fbe8]TYA
[fbe9]AND #$03
[fbeb]BNE @LAB_PRG15_MIRROR__fbd1
[fbed]JMP MMC1_UpdatePRGBankToStackA
;============================================================================
; TODO: Document UI_Maybe_GetItemSpritePPUTileAddr
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_LoadItemSourceTiles
;
UI_DrawSelectedItem
;============================================================================
[fbf0]UI_Maybe_GetItemSpritePPUTileAddr:; [$fbf0]
[fbf0]STA Temp_Int24_M
[fbf2]LDA #$00
[fbf4]LSR Temp_Int24_M
[fbf6]ROR A
[fbf7]LSR Temp_Int24_M
[fbf9]ROR A
[fbfa]LSR Temp_Int24_M
[fbfc]ROR A
[fbfd]LSR Temp_Int24_M
[fbff]ROR A
[fc00]ADC #$00
[fc02]STA Temp_Int24
[fc04]LDA Temp_Int24_M
[fc06]ADC #$85
[fc08]STA Temp_Int24_M
[fc0a]RTS
;============================================================================
; TODO: Document Player_SetItem
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
Player_Equip
;============================================================================
[fc0b]Player_SetItem:; [$fc0b]
[fc0b]STA a:SelectedItem
[fc0e]LDX #$13
[fc10]STX PPU_TargetAddr_U
[fc12]LDX #$c0
[fc14]STX PPU_TargetAddr
[fc16]ORA #$80
;============================================================================
; TODO: Document TextBox_LoadItemSourceTiles
;
; INPUTS:
; A
;
; OUTPUTS:
; TODO
;
; XREFS:
;
TextBox_DrawItemImage
;============================================================================
[fc18]TextBox_LoadItemSourceTiles:; [$fc18]
[fc18]PHA
[fc19]JSR Player_GetInventoryIndexForItem
[fc1c]TAX
[fc1d]LDA FUN_PRG15_MIRROR__fc0b__LOWER_ADDR_TABLE,X
[fc20]STA Temp_Int24_U
[fc22]LDA FUN_PRG15_MIRROR__fc0b__UPPER_ADDR_TABLE,X
[fc25]STA Maybe_Temp4
[fc27]PLA
[fc28]AND #$1f
[fc2a]ASL A
[fc2b]ASL A
[fc2c]TAY
[fc2d]LDA a:CurrentROMBank
[fc30]PHA
[fc31]LDX #$0a
[fc33]JSR MMC1_UpdateROMBank
[fc36]@LAB_PRG15_MIRROR__fc36:; [$fc36]
[fc36]LDA (Temp_Int24_U),Y
[fc38]JSR UI_Maybe_GetItemSpritePPUTileAddr
[fc3b]TYA
[fc3c]PHA
[fc3d]LDA #$10
[fc3f]JSR PPUBuffer_QueueCommandOrLength
[fc42]LDY #$00
[fc44]@LAB_PRG15_MIRROR__fc44:; [$fc44]
[fc44]JSR PPUBuffer_WriteFromTemp
[fc47]CPY #$10
[fc49]BNE @LAB_PRG15_MIRROR__fc44
[fc4b]STX PPUBuffer_WriteOffset
[fc4d]JSR PPU_IncrementAddrBy16
[fc50]PLA
[fc51]TAY
[fc52]INY
[fc53]TYA
[fc54]AND #$03
[fc56]BNE @LAB_PRG15_MIRROR__fc36
[fc58]JMP MMC1_UpdatePRGBankToStackA
[fc5b]FUN_PRG15_MIRROR__fc0b__LOWER_ADDR_TABLE:; [$fc5b]
[fc5b].byte $a0; [0]:
[fc5c].byte $b0; [1]:
[fc5d].byte $c0; [2]:
[fc5e].byte $d0; [3]:
[fc5f].byte $e4; [4]:
[fc60]FUN_PRG15_MIRROR__fc0b__UPPER_ADDR_TABLE:; [$fc60]
[fc60].byte $b4; [0]:
[fc61].byte $b4; [1]:
[fc62].byte $b4; [2]:
[fc63].byte $b4; [3]:
[fc64].byte $b4; [4]:
[fc65]Game_ShowStartScreen:; [$fc65]
[fc65]LDX #$ff; X = 0xFF
[fc67]TXS; Store X in memory.
[fc68]JSR MMC1_LoadBankAndJump; Jump to:
[fc6b].byte BANK_12_LOGIC; Bank = 12
[fc6c].word StartScreen_Draw-1; Address = StartScreen_Draw
;
; Wait for a choice at the game's start screen.
;
[fc6e]@_waitForInput:; [$fc6e]
[fc6e]JSR WaitForNextFrame; Wait for the next frame.
[fc71]JSR Screen_ResetSpritesForNonGame
[fc74]JSR MMC1_LoadBankAndJump; Jump to:
[fc77].byte BANK_12_LOGIC; Bank = 12
[fc78].word StartScreen_CheckHandleInput-1; Address = StartScreen_CheckHandleInput
[fc7a]@_afterCheckHandleInputFarJump:; [$fc7a]
[fc7a]LDA Joy1_ChangedButtonMask; Check the changed controller 1 button mask.
[fc7c]AND #$10; Was the Start button pressed?
[fc7e]BEQ @_waitForInput; If not, loop.
;
; The player pressed Start. Check what they chose.
;
; But first, play the titlescreen music.
;
[fc80]LDA #$08
[fc82]STA Music_Current
[fc84]LDA a:DAT_0687; Check the chosen option.
[fc87]BEQ @_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]JSR MMC1_LoadBankAndJump; Jump to:
[fc8c].byte BANK_12_LOGIC; Bank = 12
[fc8d].word PasswordScreen_Show-1; Address = PasswordScreen_Show
[fc8f]@_afterPasswordScreenShow:; [$fc8f]
[fc8f]JSR MMC1_LoadBankAndJump; Jump to:
[fc92].byte BANK_12_LOGIC; Bank = 12
[fc93].word Player_SetInitialExpAndGold-1; Address = Player_SetInitialExpAndGold
[fc95]@_afterSetExpGoldFarJump:; [$fc95]
[fc95]JMP Player_Spawn
[fc98]@_startGame:; [$fc98]
[fc98]JSR MMC1_LoadBankAndJump; Jump to:
[fc9b].byte BANK_12_LOGIC; Bank = 12
[fc9c].word SplashAnimation_RunIntro-1; Address = SplashAnimation_RunIntro
[fc9e]@_afterRunIntroFarJump:; [$fc9e]
[fc9e]JSR MMC1_LoadBankAndJump; Jump to:
[fca1].byte BANK_12_LOGIC; Bank = 12
[fca2].word Player_SetStartGameState-1; Address = Player_SetStartGameState
;
; Begin the game.
;
[fca4]@_afterSetStartGameStateFarJump:; [$fca4]
[fca4]JMP Game_Start
[fca7]UI_DrawPromptInputSymbol:; [$fca7]
[fca7]STX Arg_DrawSprite_PosX; Set the X argument.
[fca9]STY Arg_DrawSprite_PosY; Set the Y argument.
[fcab]LDX #$00
[fcad]STX CurrentSprite_FlipMask; Clear the flip mask.
[fcaf]JMP 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]
[fcb2]STA a:PPUDATA; Store the value in PPUDATA.
[fcb5]DEY; Y--
[fcb6]BNE PPU_FillData; If not 0, loop.
[fcb8]RTS
;============================================================================
; 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:; [$fcb9]
;
; Set the start position to the top-left ($2000).
;
[fcb9]LDA #$20
[fcbb]STA a:PPUADDR
[fcbe]LDA #$00
[fcc0]STA a:PPUADDR
;
; Prepare the value and loop counter.
;
[fcc3]TAY; Y = 0 (value to write)
[fcc4]LDX #$04; X = 4 (loop counter)
;
; Begin the loop.
;
[fcc6]@_loop:; [$fcc6]
[fcc6]JSR PPU_FillData; Fill 256 bytes of data.
[fcc9]DEX; X--
[fcca]BNE @_loop; If not 0, loop.
[fccc]RTS
[fccd].byte $bd,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fccd] undefined
[fcd5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fcd5] undefined
[fcdd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fcdd] undefined
[fce5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fce5] undefined
[fced].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fced] undefined
[fcf5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fcf5] undefined
[fcfd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fcfd] undefined
[fd05].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd05] undefined
[fd0d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd0d] undefined
[fd15].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd15] undefined
[fd1d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd1d] undefined
[fd25].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd25] undefined
[fd2d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd2d] undefined
[fd35].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd35] undefined
[fd3d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd3d] undefined
[fd45].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd45] undefined
[fd4d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd4d] undefined
[fd55].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd55] undefined
[fd5d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd5d] undefined
[fd65].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd65] undefined
[fd6d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd6d] undefined
[fd75].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd75] undefined
[fd7d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd7d] undefined
[fd85].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd85] undefined
[fd8d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd8d] undefined
[fd95].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd95] undefined
[fd9d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fd9d] undefined
[fda5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fda5] undefined
[fdad].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdad] undefined
[fdb5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdb5] undefined
[fdbd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdbd] undefined
[fdc5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdc5] undefined
[fdcd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdcd] undefined
[fdd5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdd5] undefined
[fddd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fddd] undefined
[fde5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fde5] undefined
[fded].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fded] undefined
[fdf5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdf5] undefined
[fdfd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fdfd] undefined
[fe05].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe05] undefined
[fe0d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe0d] undefined
[fe15].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe15] undefined
[fe1d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe1d] undefined
[fe25].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe25] undefined
[fe2d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe2d] undefined
[fe35].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe35] undefined
[fe3d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe3d] undefined
[fe45].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe45] undefined
[fe4d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe4d] undefined
[fe55].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe55] undefined
[fe5d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe5d] undefined
[fe65].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe65] undefined
[fe6d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe6d] undefined
[fe75].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe75] undefined
[fe7d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe7d] undefined
[fe85].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe85] undefined
[fe8d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe8d] undefined
[fe95].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe95] undefined
[fe9d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fe9d] undefined
[fea5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fea5] undefined
[fead].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fead] undefined
[feb5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$feb5] undefined
[febd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$febd] undefined
[fec5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fec5] undefined
[fecd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fecd] undefined
[fed5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fed5] undefined
[fedd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fedd] undefined
[fee5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fee5] undefined
[feed].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$feed] undefined
[fef5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fef5] undefined
[fefd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$fefd] undefined
[ff05].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff05] undefined
[ff0d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff0d] undefined
[ff15].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff15] undefined
[ff1d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff1d] undefined
[ff25].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff25] undefined
[ff2d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff2d] undefined
[ff35].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff35] undefined
[ff3d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff3d] undefined
[ff45].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff45] undefined
[ff4d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff4d] undefined
[ff55].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff55] undefined
[ff5d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff5d] undefined
[ff65].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff65] undefined
[ff6d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff6d] undefined
[ff75].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff75] undefined
[ff7d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff7d] undefined
[ff85].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff85] undefined
[ff8d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff8d] undefined
[ff95].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff95] undefined
[ff9d].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ff9d] undefined
[ffa5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffa5] undefined
[ffad].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffad] undefined
[ffb5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffb5] undefined
[ffbd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffbd] undefined
[ffc5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffc5] undefined
[ffcd].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffcd] undefined
[ffd5].byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff; [$ffd5] undefined
[ffdd].byte $ff,$ff,$ff; [$ffdd] undefined
.byte $20; Game title
[ffe1].byte $20,$20,$20,$20,$20,$20,$20,$46; [$ffe1] string
[ffe9].byte $41,$58,$41,$4e,$41,$44,$55; [$ffe9] string
.word $4227; PRG Checksum
[fff2].word $0000; CHR CHecksum
[fff4].byte $48; CHR size: 0 = 8KiB
CHR type: 0 = CHR ROM
PRG size: 5 = 512KiB
[fff5].byte $04; Mapper: 4 = MMC
Nametable: 0 = Horizontal arrangement
[fff6].byte $01; Title encoding: 1 = ASCII
[fff7].byte $07; Title length: 8 bytes
[fff8].byte $18; Licensee Code: Hudson Soft
[fff9].byte $94; Header validation byte
[fffa].word OnInterrupt; OnInterrupt [$PRG15_MIRROR::fffa]
[fffc].word Game_Init; Game_Init [$PRG15_MIRROR::fffc]
[fffe].byte $d5; [$fffe] undefined
[ffff]MMC1_SERIAL:; [$ffff]
[ffff].byte $c9; [$ffff] undefined