Lode Runner: Apple II reverse engineering

13. The whole thing#

We then put together the entire assembly file, including all the data that happened to be in the uninitialized sections.

⟨zero page initial [582]⟩=
    ORG     $0000
ZERO_PAGE_INITIAL:
    HEX     4C 00 28 00 C0 68 0D 00 60 10 00 40 D0 3F 00 10 
    HEX     D4 B4 9C FA B0 FF 37 FB FE FE FC EC 76 D7 E6 20 
    HEX     00 28 00 4C 00 00 00 D5 00 04 18 60 20 0F 0C D6 
    HEX     FC FA FF 73 FD 8D 50 8E B5 B7 04 FF FB B7 00 1F 
    HEX     00 18 01 60 00 00 14 D8 E8 B7 91 9A 04 02 00 08 
    HEX     00 FD 1C 3C EC AC B4 38 C4 E9 B6 FF BA BB 2A FE 
    HEX     86 3E 85 3A A6 3E 86 40 A0 00 A5 3A 84 3C 85 3D 
    HEX     A6 08 20 AE 00 C5 00 D0 F9 20 AE 00 C5 01 D0 F5 
    HEX     20 AE 00 C5 02 D0 01 20 8C C0 10 FB 06 85 3F BD 
    HEX     8C C0 10 FB 25 CA 00 3C C8 FF EC 0E 00 C0 BD 8C 
    HEX     C0 10 FB C5 03 D0 BD 00 3D C6 40 D0 DA 60 BD 8C 
    HEX     C0 10 FB 60 A2 D4 86 00 E8 86 01 E8 86 02 E8 86 
    HEX     03 A9 04 AA 60 FF 2A 29 B1 FF 48 2A 09 01 8A 49 
    HEX     FF C9 10 AA E8 09 AA EA FF A2 00 48 CA D0 FC A2 
    HEX     0F BD F0 04 9D 00 01 CA D0 F7 9A 60 18 69 05 29 
    HEX     07 69 01 00 EA E8 CA E8 CA E8 CA C8 88 C8 88 C8    
⟨stack initial [584]⟩=
    ORG     $0100
STACK_INITIAL:
    HEX     BE B9 B7 B4 7B 63 90 61 FF 8B FE 07 FF 03 FF 5F 
    HEX     5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 7A 
    HEX     5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 7A 
    HEX     5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 7A 
    HEX     5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 7A 
    HEX     5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 5A 7A 
    HEX     1D 7C FE 31 29 12 2D 60 FB 5D F7 72 D9 ED D1 A8 
    HEX     FC FC FE 7B F7 FF 5E FF 35 FE EC CD BF EF 3D DD 
    HEX     DF EF FF DF F7 7B 7F BF 7F BF ED E5 BF DF ED D4 
    HEX     01 2E 00 10 4A 84 48 83 02 22 50 82 9A 0C 46 04 
    HEX     D7 19 AD 7F 4C C7 DF BE FF 8F 7B CF DF FF B3 DA 
    HEX     00 08 02 F2 00 05 86 00 09 F3 00 63 08 00 43 02 
    HEX     7F DF FD CF EB BF AF BF D5 FF C7 E7 BD FF BF 7B 
    HEX     C9 E4 61 06 19 07 04 56 01 63 42 0F 93 43 C2 46 
    HEX     89 4A 00 52 05 2F 22 A3 A0 37 00 84 0A 01 80 40 
    HEX     00 09 00 06 17 26 FC 00 26 FC 6F BE 66 BE C6 42
⟨random initial [586]⟩=
    ORG     $0200
RANDOM_INIT_DATA:
    INCLUDE "random_init_data.asm"

    ORG     $0C00
MORE_RANDOM_INIT_DATA:
    INCLUDE "more_random_init_data.asm"

    ORG     $1EB2
PADDING:
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00

Upon load, the Lode Runner file on disk gets dumped into memory at 0800-88FF and then is jumped to at 0800. The routine there, RELOCATE0, relocates the entire file to 3F00-BFFF and then jumps to RELOCATE1. That routine can relocate various segments, but in this case it only moves 3F00-5EFF to 0000-1FFF and leaves the rest alone.

There is some UNUSED code after RELOCATE1 which appears to zero out most (but not all) of graphics page 1. It seems to leave the first 0xA8 bytes untouched.

⟨defines [588]⟩+=
SRC_PTR     EQU     $3C     ; 2 bytes
DEST_PTR    EQU     $3E     ; 2 bytes
⟨relocation routine [590]⟩=
    ORG     $5F00
RELOCATE0_ALIAS:
    SUBROUTINE

    LDA     #$64
    STA     $0800
    LDA     #$76
    STA     $0801
    LDA     #$0D
    STA     $0802

    ; Copy 0x8100 bytes from $0800-$88FF to $3F00-$BFFF
    LDY     #$88
    BEQ     RELOCATE1       ; Never happens
    STY     SRC_PTR+1
    LDA     #$BF
    STA     DEST_PTR+1
    LDX     #$81
    LDY     #$00
    STY     SRC_PTR
    STY     DEST_PTR

.loop:
    LDA     (SRC_PTR),Y
    STA     (DEST_PTR),Y
    INY
    BNE     .loop
    DEC     SRC_PTR+1
    DEC     DEST_PTR+1
    DEX
    BNE     .loop
    JMP     RELOCATE1

RELOCATE1:
    SUBROUTINE

    LDX     #<RELOCATE_TABLE

.loop:
    LDA     RELOCATE_TABLE-<RELOCATE_TABLE,X
    STA     .rd_insn+2
    INX
    LDA     RELOCATE_TABLE-<RELOCATE_TABLE,X
    STA     .cmp_insn+1
    INX
    LDA     RELOCATE_TABLE-<RELOCATE_TABLE,X
    STA     .wr_insn+2
    INX
    LDY     #$00

.loop2:
.rd_insn:
    LDA     $0800,Y     ; address replaced above
.wr_insn:
    STA     $0800,Y     ; address replaced above
    INY
    BNE     .loop2

    INC     .rd_insn+2
    INC     .wr_insn+2
    LDA     .rd_insn+2
.cmp_insn:
    CMP     #$FF        ; comparison value replaced above
    BNE     .loop2

    DEC     RELOCATE_SEGMENT_COUNT
    BNE     .loop

    JMP     MAIN

UNUSED:
    SUBROUTINE

    LDX     #$A8

.loop:
    LDA     #$00

.loop2:
.wr_insn:
    STA     $2000,X
    INX
    BNE     .loop2
    INC     .wr_insn+2
    LDA     .wr_insn+2
    CMP     #$40
    BNE     .loop

    ; Fall through to MAIN

    ORG     $5FA6
RELOCATE_SEGMENT_COUNT:
    HEX     04
RELOCATE_TABLE:
    HEX     3F 47 00
    HEX     47 5F 08
    HEX     60 8E 60
    HEX     8E C0 8E
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Used in ⟨*⟩
Defines RELOCATE0_ALIAS, RELOCATE1
Uses MAIN

Dead code is code that disassembles, but doesn't seem to be called.

⟨dead code [592]⟩+=
    ORG     $6000

    JSR     DETECT_LACK_OF_JOYSTICK
    LDA     #$01
    JSR     ACCESS_HI_SCORE_DATA_FROM_DISK

    ; Fallthrough to RESET_GAME
⟨zeroed areas [594]⟩=
    ORG     $8D53
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    HEX     00 00 00 00 00 00 00 00 00 00 00 00 00

    ORG     $8E46
    HEX     00 00 00 00 00 00 00 00 00 00

    ORG     $8E53
    ; through to $8FFF.
    DS      $1AD
Used in ⟨*⟩
⟨garbage area [596]⟩=
    ORG     $9000
    INCLUDE "garbage.asm"

    ORG     $B5F0
    HEX     C5 A2 D4 A2 00 D0 8A 94 80 80 80 80 80 80 80 80
Used in ⟨*⟩
⟨dos [598]⟩=
    ORG     $B600
    INCLUDE "dos.asm"
Used in ⟨*⟩
⟨routines [600]⟩+=
    ; Initialized "uninitialized" data

    ⟨zero page initial⟩
    ⟨stack initial⟩
    ⟨random initial⟩

    ; Sprite routines

    ⟨erase sprite at screen coordinate⟩
    ⟨draw sprite at screen coordinate⟩
    ⟨draw player⟩
    ⟨char to sprite num⟩
    ⟨put char⟩
    ⟨put string⟩
    ⟨put digit⟩
    ⟨to decimal3⟩
    ⟨bcd to decimal2⟩

    ; Screen and level routines

    ⟨add and update score⟩
    ⟨put status⟩
    ⟨level draw routine⟩
    <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]] routine>>
    ⟨splash screen⟩
    ⟨construct and display high score screen⟩
    ⟨iris wipe⟩
    ⟨iris wipe step⟩
    ⟨draw wipe step⟩
    ⟨draw wipe block⟩
    ⟨load compressed level data⟩
    ⟨load level⟩

    ; Sound routines

    ⟨beep⟩
    ⟨load sound data⟩
    ⟨append note⟩
    ⟨play note⟩
    ⟨sound delay⟩
    ⟨play sound⟩
    ⟨append level cleared note⟩

    ; Joystick routines

    ⟨read paddles⟩
    ⟨check joystick or delay⟩
    ⟨detect lack of joystick⟩

    ; Keyboard routines

    ⟨wait key⟩
    ⟨wait key queued⟩
    ⟨wait for key⟩
    ⟨wait for key page1⟩
    ⟨editor wait for key⟩
    ⟨hit key to continue⟩
    ⟨get level from keyboard⟩

    ; Player movement routines

    ⟨get player sprite and coord data⟩
    ⟨increment player animation state⟩
    ⟨check for gold picked up by player⟩
    ⟨check for input⟩
    ⟨ctrl handlers⟩
    ⟨return handler⟩
    ⟨check buttons⟩
    ⟨try moving up⟩
    ⟨try moving down⟩
    ⟨try moving left⟩
    ⟨try moving right⟩
    ⟨try digging left⟩
    ⟨try digging right⟩
    ⟨drop player in hole⟩
    ⟨move player⟩
    ⟨check for mode 1 input⟩

    ; Guard AI routines

    ⟨guard resurrections⟩
    ⟨guard store and load data⟩
    ⟨get guard sprite and coords⟩
    ⟨move guards⟩
    ⟨move guard⟩
    ⟨determine guard move⟩
    ⟨determine guard left right limits⟩
    ⟨should guard move left⟩
    ⟨should guard move right⟩
    ⟨should guard move up or down⟩
    ⟨nudge guards⟩
    ⟨check for gold picked up by guard⟩
    ⟨increment guard animation state⟩
    ⟨try guard move left⟩
    ⟨try guard move right⟩
    ⟨try guard move up⟩
    ⟨try guard move down⟩
    ⟨pseudo distance⟩
    ⟨guard drop gold⟩
    ⟨guard find candidate row below⟩
    ⟨guard find candidate row above⟩

    ; Disk routines

    ⟨rwts targets⟩
    ⟨jump to RWTS indirectly⟩
    ⟨indirect call⟩
    ⟨bad data disk⟩
    ⟨dont manipulate master disk⟩
    ⟨access hi score data⟩
    ⟨record hi score data⟩
    ⟨check for valid data disk⟩
    ⟨editor initialize disk⟩
    ⟨editor clear high scores⟩
    ⟨format patch⟩

    ; Startup code

    ⟨startup code⟩
    ⟨check for button down⟩
    ⟨no button pressed⟩
    ⟨button pressed at startup⟩
    ⟨key pressed at startup⟩
    ⟨ctrl-e pressed⟩
    ⟨return pressed⟩
    ⟨timed out waiting for button or keypress⟩
    ⟨check game mode⟩
    ⟨reset game if not mode 1⟩
    ⟨display high score screen⟩
    ⟨long delay attract mode⟩

    ; Game loop

    ⟨Initialize game data⟩
    ⟨game loop⟩
    ⟨handle timers⟩
    ⟨do ladders⟩
    ⟨anims⟩
    ⟨show anim line⟩

    ; Editor routines

    ⟨level editor⟩
    ⟨editor clear level⟩
    ⟨editor move level⟩
    ⟨editor play level⟩
    ⟨editor edit level⟩
    ⟨get key for edit level⟩
    ⟨level editor key functions⟩
⟨* [602]⟩=
(root chunk — tangled to file)

\chapter{Defined Chunks} \nowebchunks \chapter{Index} \nowebindex