This page contains copyright code which was written by Matthew Smith for the long-since dissolved companies Bug-Byte and Software Projects. In a private email of 31st August 1999, he gave me his permission to post disassemblies of MM and JSW, provided that they include the following line at the top and bottom of the file:
(C) 1983,1984,1999 Matthew Smith - all rights reserved
There are several annotated disassemblies of Manic Miner and Jet Set Willy in existence:
This page presents Carl Woffenden's annotated disassemblies of various code-fragments in MM/JSW - thanks to him for sending me the last revisions and for permission to host them:
B8068: DEFB 0 ; Willy's y * 2
B8069: DEFB 0 ; Willy's anim frame 0 - 3
B806A: DEFB 0 ; Willy's walking/facing flag:
; bit-0 = off - right, on - left
; bit-1 = off - still, on - walk
B806B: DEFB 0 ; Willy's action:
; -1: dead
; 0: walking/standing
; 1: jumping
; 2: falling
; >2: fall height
W806C: DEFW 0 ; Willy's position in attribute buffer
B806E: DEFB 0 ; Willy's jump phase counter (0x00 - 0x12)
; movement lookup table:
T8408: DEFB 0,1,0,1,1,3,1,3,2,0,2,0,0,1,2,3
; *************************** start of movement code *************************
C8ABB:
LD A,(B806B) ; action states
CP 1
JR NZ,R8B10 ; branch (+50) if not currently JUMPING
LD A,(B806E) ; JUMP phase counter
RES 0,A
SUB 8
LD HL,B8068 ; address of Willy's y * 2
ADD A,(HL)
LD (HL),A ; jump height algorithm is (y * 2 & 0xFE) - 8
CALL C8B82 ; update Willy's attribute buffer position
LD A,(B803B) ; wall attr
CP (HL) ; HL now holding attribute buffer position
JP Z,L8BA2 ; branch if Willy's head hits wall
INC HL ; move one col right
CP (HL)
JP Z,L8BA2 ; branch if Willy's head hits wall
LD A,(B806E) ; JUMP phase counter
INC A
LD (B806E),A ; store incremented JUMP phase
SUB 8 ; **** beeper starts here ****
JP P,L8AEB ; branch if SUB 8 goes below 0
NEG ; else phase = 0 - phase
L8AEB:
INC A
RLCA
RLCA
RLCA ; multiply height by 16
LD D,A
LD C,20h
LD A,(BORDER) ; maintain border colour
R8AF5:
OUT (0FEh),A
XOR 18h ; toggle ear/mic bits
LD B,D
R8AFA:
DJNZ R8AFA ; wait/pitch based on height
DEC C
JR NZ,R8AF5 ; **** beeper ends here (after 32 cycles) ****
LD A,(B806E) ; JUMP phase counter
CP 12h ; 18 steps in a JUMP
JP Z,L8B96 ; branch if at end of JUMP phase
CP 10h ; [dunno yet why 16?]
JR Z,R8B10 ; [+07]
CP 0Dh ; [dunno yet why 12?]
JP NZ,L8C83 ; branch if not 12 (do horizontal movement)
; entry for manual control
R8B10:
LD A,(B8068) ; Willy's y * 2
AND 0Fh ; **** JSW is 0x0E ****
JR NZ,R8B51 ; branch (+3C) if y isn't row aligned
LD HL,(W806C) ; Willy's position in attribute buffer
LD DE,64
ADD HL,DE ; move two rows down
LD A,(B8032) ; crumble attr
CP (HL)
CALL Z,C8BBA ; matched crumble
LD A,(B804D) ; nasty (1) attr
CP (HL)
JR Z,R8B51 ; branch (+28) if matched nasty
LD A,(B8056) ; nasty (2) attr
CP (HL)
JR Z,R8B51 ; branch (+22) if matched nasty
INC HL ; move one col right (and redo the matches)
LD A,(B8032) ; crumble attr
CP (HL)
CALL Z,C8BBA ; branch if matched crumble
LD A,(B804D) ; nasty (1) attr
CP (HL)
JR Z,R8B51 ; branch (+14) if matched nasty
LD A,(B8056) ; nasty (2) attr
CP (HL)
JR Z,R8B51 ; branch (+0E) if matched nasty
LD A,(B8020) ; bkgnd attr
CP (HL)
DEC HL ; move one col left
JP NZ,L8BDD ; not matched bkgnd (from earlier CP before DEC)
CP (HL)
JP NZ,L8BDD ; branch if not matched bkgnd
R8B51:
LD A,(B806B) ; Willy's action
CP 1
JP Z,L8C83 ; branch if JUMPING (do horizontal movement)
LD HL,B806A ; walking/facing flags
RES 1,(HL) ; reset walking bit
OR A
JP Z,L8B9C ; branch if STANDING (this sets FALLING flag)
INC A ; else falling so increment fall height
LD (B806B),A ; then store it
RLCA ; **** beeper starts here ****
RLCA
RLCA
RLCA ; multiply height by 16
LD D,A
LD C,20h
LD A,(BORDER) ; maintain border colour
R8B70:
OUT (0FEh),A ; make a noise
XOR 18h ; toggle ear/mic bits
LD B,D
R8B75:
DJNZ R8B75 ; wait/pitch based on height
DEC C
JR NZ,R8B70 ;$-08 ; **** beeper ends here (after 32 cycles) ****
LD A,(B8068) ; Willy's y * 2
ADD A,8 ; fall 4 pixels down
LD (B8068),A ; then store value
C8B82: ; arrive here with A holding Willy's y * 2
AND 0F0h ; align to nearest row (0, 16, 32, etc.)
LD L,A
XOR A ; clear A
RL L ; double (y * 2) using carry for next ADC
ADC A,5Ch ; 0x5C00 being start of attribute buffer
LD H,A
LD A,(W806C) ; Willy's position in attribute buffer
AND 1Fh ; col mask
OR L ; current row (minus its MSB)
LD L,A
LD (W806C),HL ; Willy's position updated
RET
L8B96: ; change from JUMPING to FALLING
LD A,6
LD (B806B),A ; sets action/fall distance to 6?
RET
L8B9C:
LD A,2
LD (B806B),A ; sets action flag to FALLING
RET
; when Willy's head hits a wall while JUMPING
L8BA2:
LD A,(B8068) ; Willy's y * 2
ADD A,10h ; 8 pixels (one block) down
AND 0F0h ; rounded to nearest block
LD (B8068),A ; then store it
CALL C8B82 ; set Willy's attribute buffer position
LD A,2
LD (B806B),A ; sets action flag to FALLING
LD HL,B806A ; walking/facing flag
RES 1,(HL) ; reset WALKING bit
RET
; Sent here because a crumble block matched. HL contains the block's address
; in the attribute buffer. Basically it handles the crumbling blocks until
; they finally become bkgnd blocks.
C8BBA:
LD C,L
LD A,H
ADD A,1Bh
OR 7
LD B,A
R8BC1:
DEC B
LD A,(BC)
INC B
LD (BC),A
DEC B
LD A,B
AND 7
JR NZ,R8BC1 ;$-08
XOR A
LD (BC),A
LD A,B
ADD A,7
LD B,A
LD A,(BC)
OR A
RET NZ
LD A,(B8020) ; bkgnd attr
INC H
INC H
LD (HL),A ; set the block to bkgnd
DEC H
DEC H
RET
L8BDD: ; HL is currently attributes under Willy's feet
LD A,(B806B) ; Willy's action/fall distance
CP 0Ch
JP NC,L8D06 ; branch if fallen beyond 12 (dead)
LD E,0FFh ; equivalent to nothing pressed (active low)
XOR A
LD (B806B),A ; Willy's action set to STANDING
LD A,(B8044) ; conveyor attr
CP (HL)
JR Z,R8BF5 ; branch (+06) if matched conveyor
INC HL ; move one col right
CP (HL)
JR NZ,R8BFB ; branch (+08) if not matched conveyor
R8BF5:
LD A,(B806F) ; conveyor direction
SUB 3 ; left = 253 (LSBs 01), right 254 (LSBs 10)
LD E,A
R8BFB: ; the input routines
LD BC,0DFFEh
IN A,(C) ; read Y, U, I, O, P
AND 1Fh ; mask key bits (0-4)
OR 20h ; set bit-5 (used for LEFT presses later)
AND E
LD E,A
LD BC,0FBFEh
IN A,(C) ; read Q, W, E, R, T
AND 1Fh ; mask key bits (0-4)
RLC A ; changes key sequence to L-R not R-L
OR 1 ; 'unpresses' a RIGHT key
AND E
LD E,A
LD B,0F7h
IN A,(C) ; read 1, 2, 3, 4, 5
RRCA ; only interested in key 5 (now at bit-3)
OR 0F7h ; so mask (active low)
AND E
LD E,A
LD B,0EFh
IN A,(C) ; read 6, 7, 8, 9, 0
OR 0FBh ; mask key 8 (bit-2, active low)
AND E
LD E,A
LD A,(KEMP)
OR A
JR Z,R8C34 ; branch (+0C) if no joystick connected
LD BC,1Fh
IN A,(C) ; else read joystick port
AND 3 ; left and right mask (bits are xxxFUDLR)
CPL ; because Kempston is active high
AND E
LD E,A
R8C34: ; reach here with E holding key/joystick data
LD C,0
LD A,E
AND 2Ah ; 0x2A = 101010 - mask all LEFT keys
CP 2Ah
JR Z,R8C3F ; branch if no LEFT keys pressed inc. conveyor
LD C,4 ; else set LEFT flag (bit-2)
R8C3F:
LD A,E
AND 15h ; 0x15 = 010101 - mask all RIGHT keys
CP 15h
JR Z,R8C48 ; branch if no RIGHT keys pressed inc. conveyor
SET 3,C ; else set RIGHT flag (bit-3)
R8C48:
LD A,(B806A) ; flag bits (bit-0 direction, bit-1 walking)
ADD A,C ; combined with current key presses
LD C,A
LD B,0
LD HL,T8408 ; movement lookup table
ADD HL,BC ; offset into table
LD A,(HL)
LD (B806A),A ; flag bits are now set from the table
LD BC,07EFEh
IN A,(C); ; read bottom row CAPS to SPACE
AND 1Fh ; mask key bits (0-4)
CP 1Fh
JR NZ,R8C7B ; branch (+1B) if any key at all is pressed
LD B,0EFh
IN A,(C) ; read 6, 7, 8, 9, 0
AND 9 ; mask keys 7 and 0
CP 9
JR NZ,R8C7B ; branch (+11) if any key at all is pressed
LD A,(KEMP) ; no jump key pressed so we check the joystick
OR A
JR Z,L8C83 ; none connected so do horizontal movement (+13)
LD BC,1Fh
IN A,(C) ; read Kempston joystick
BIT 4,A ; fire bit
JR Z,L8C83 ; fire not pressed so do horizontal movement
R8C7B:
XOR A
LD (B806E),A ; set the jump counter to 0
INC A
LD (B806B),A ; set the action flag to 1 (for jumping)
; horizontal movement
L8C83:
LD A,(B806A) ; walking/facing flags
AND 2 ; walking bit
RET Z ; not currently moving so return
LD A,(B806A)
AND 1 ; facing flag
JP Z,L8CCA ; flag = 0 so go right
; else go left
LD A,(B8069) ; Willy's anim frame
OR A
JR Z,R8C9C ; branch (+07) if frame 0 (far left of 16x16)
DEC A ; else decrement it
LD (B8069),A
RET ; nothing else to check
R8C9C:
LD HL,(W806C) ; Willy's position on attribute buffer
DEC HL ; move one col left
LD DE,32
ADD HL,DE ; move one row down
LD A,(B803B) ; wall attr
CP (HL)
RET Z ; return if we hit a wall (Willy's feet block)
LD A,(B8068) ; Willy's y * 2
AND 0Fh
JR Z,R8CB9 ; branch (+0B) if y is block aligned
LD A,(B803B) ; wall attr
ADD HL,DE ; move one row down again
CP (HL)
RET Z ; return if we hit a wall (below Willy's feet)
OR A ; [this must be to reset the carry flag?]
SBC HL,DE ; move one row up
R8CB9:
LD A,(B803B) ; wall attr
OR A ; [this must be to reset the carry flag?]
SBC HL,DE ; move one row up again
CP (HL)
RET Z ; return if we hit a wall (Willy's head block)
LD (W806C),HL ; update Willy's position
LD A,3 ; frame 3 (far right if 16x16)
LD (B8069),A ; update Willy's anim frame
RET
; moving right
L8CCA:
LD A,(B8069) ; Willy's anim frame
CP 3
JR Z,R8CD6 ; branch (+07) if frame 3 (far right of 16x16)
INC A ; else increment it
LD (B8069),A
RET ; nothing else to check
R8CD6:
LD HL,(W806C) ; Willy's position on attribute buffer
INC HL
INC HL ; move 2 cols right (block to right of Willy)
LD DE,32
LD A,(B803B) ; wall attr
ADD HL,DE ; move one row down
CP (HL)
RET Z ; return if we hit a wall (Willy's feet block)
LD A,(B8068) ; Willy's y * 2
AND 0Fh
JR Z,R8CF4 ; branch (+0B) if y is block aligned
LD A,(B803B) ; wall attr
ADD HL,DE ; move one row down again
CP (HL)
RET Z ; return if we hit a wall (below Willy's feet)
OR A ; [this must be to reset the carry flag?]
SBC HL,DE ; move one row back up
R8CF4:
LD A,(B803B) ; wall attr
OR A ; [this must be to reset the carry flag?]
SBC HL,DE ; move one row up again
CP (HL)
RET Z ; return if we hit a wall (Willy's head block)
DEC HL ; all was ok so move back left by one col
LD (W806C),HL ; and update Willy's position
XOR A ; sets frame to zero (far left of 16x16)
LD (B8069),A ; update Willy's anim frame
RET
L8D05:
POP HL
L8D06:
POP HL
L8D07:
LD A,0FFh ; Willy has been killed (#806B = 0xFF)
LD (B806B),A
JP L87A2
; *************************** end of movement code ***************************
; *********************** Some extra collision checking **********************
C923A:
LD HL,(W806C) ; Willy's position in attribute buffer
LD DE,31
LD C,0Fh
CALL C925F
INC HL ; move one col right
CALL C925F
ADD HL,DE ; move one row down and one col left
CALL C925F
INC HL ; move one col right
CALL C925F
LD A,(B8068) ; Willy's y * 2
LD C,A
ADD HL,DE ; move one row down and one col left
CALL C925F
INC HL ; move one col right
CALL C925F
JR R927F
C925F:
LD A,(B8020) ; bkgnd attr
CP (HL)
JR NZ,R9270 ; branch (+0D) if not bkgnd
LD A,C
AND 0Fh
JR Z,R9270 ; branch (+08) if y is block aligned
LD A,(B8020) ; bkgnd attr
OR 7
LD (HL),A
R9270:
LD A,(B804D) ; nasty (1) attr
CP (HL)
JP Z,L8D05 ; killed
LD A,(B8056) ; nasty (2) attr
CP (HL)
JP Z,L8D05 ; killed
RET
; draw Willy (run out of steam here but the result is obvious enough)
R927F:
LD A,(B8068) ; Willy's y * 2
LD IXH,83h
LD IXL,A
LD A,(B806A) ; Willy's walking/facing flags
AND 1 ; facing mask
RRCA
LD E,A
LD A,(B8069) ; Willy's anim frame
AND 3 ; frames mask
RRCA
RRCA
RRCA ; frame multiplied by 8
OR E
LD E,A
LD D,82h ; as in 0x8200, start address for Willy's frames
LD B,10h ; 16 lines
LD A,(W806C) ; Willy's position in attribute buffer
AND 1Fh ; just the col
LD C,A
R92A2:
LD A,(IX+0)
LD H,(IX+1)
OR C
LD L,A
LD A,(DE)
OR (HL)
LD (HL),A
INC HL
INC DE
LD A,(DE)
OR (HL)
LD (HL),A
INC IX
INC IX
INC DE
DJNZ R92A2 ; loop (-15) until all lines are drawn
RET
; ************************** End of collision checking ***********************
Jet Set Willy Manic Miner (Bug Byte)
900A: 8C9C:
LD HL, (#85D3) LD HL,(#806C) ; Willy's attr buffer position
LD A, L
AND #1F
JP Z, #948A ; branch if exit right of room
ADD HL, BC
DEC HL DEC HL ; one col left
LD DE, 32 LD DE, 32
ADD HL, DE ADD HL, DE ; one row down
LD A, (#80B2) LD A, (#803B) ; wall attr from working room
CP (HL) CP (HL)
RET Z RET Z ; return if we hit a wall
LD A, (#85CF) LD A, (#8068) ; Willy's y (really y * 2)
SRA C ; BC contains ramp offset
ADD C
LD B, A
AND #0F AND #0F
JR Z, #9032 JR Z, #8CB9 ; branch if y is block aligned
LD A, (#80B2) LD A, (#803B) ; wall attr from working room
ADD HL, DE ADD HL, DE ; one row down
CP (HL) CP (HL)
RET Z RET Z ; return if we hit a wall
OR A OR A
SBC HL, DE SBC HL, DE ; one row back up
9032: 8CB9:
LD A, (#803B) ; wall attr from working room
OR A OR A
SBC HL, DE SBC HL,DE ; one row up again
CP (HL)
RET Z ; return if we hit a wall
LD (#85D3), HL LD (#806C), HL ; update Willy's position
LD A, B
LD (#85CF), A ; store updated y
LD A, 3 LD A, 3 ; frame 3 (far right of 16x16)
LD (#85D2), A LD (#8069),A ; update Willy's anim frame
RET RET
; Original disassembly taken from Geoff Eddy's website.
8000 DEFS #0100 ; data for current room
8100 DEFS #0048 ; guardians in current room
8148 DEFS #00B8 ; blank
8200 DEFS #0100 ; screen coord lookup table
; (#8200 + 2y) contains address of first
; byte in line y (of 128).
8300 DEFS #0080 ; x-offsets between successive dots in a rope.
; All bytes are 0-6, thus:
; 00000000000000000000000000000000
; 11111111111122222222222222222222
; 22122112112232323333330000000000
; 00000000000000000000000000000000
8380 DEFS #0080 ; y-offsets, similarly
; 66666666666666666666666666666666
; 66666666666666664664646464446444
; 44444444444444444444440000000000
; 00000000000000000000000000000000
8400 DEFS #0020 ; attributes of the "Press Enter" message
; on the title screen. @@@
8420 21 DEFB #21 ; current room number
8421 00010001 DEFS 4 ; Willy state transition table
8425 01030103 DEFS 4
8429 02000200 DEFS 4
842D 00010203 DEFS 4
8431 DEFS #20 ; UDGs for title screen
8451 FF DEFB #FF ; total number of items @@@ (was at #A3FF)
8452 6F6F DEFM "oo" ; spare bytes
8454 DEFS #0100 ; scrolling message on title screen
8554 DEFS #20 ; "Items collected" "time left" string
8574 DEFM "Game Over00099999999999999"
858D DEFS #3E ; zeros (used to be security messages @@@)
85CB DEFS #1A ; timer; just counts from 0 to ff
85CC 10 DEFB #10 ; number of lives @@@ (was 7)
85CD 00 DEFB #00 ; counter
85CE 00 DEFB #00 ; Kempston flag
; These variables are set up when a room is entered and copied to the
; set at 85D7-DD. They are copied back when a life is lost.
85CF D0 DEFB #D0 ; Willy's y * 2 (used for buffer table offset)
85D0 00 DEFB #00 ; Willy's walking/facing flag:
; bit-0 = off - right, on - left
; bit-1 = off - still, on - walk
85D1 00 DEFB #00 ; Willy's action:
; -1: dead
; 0: walking/standing
; 1: jumping
; 2: falling
; >2: fall height
85D2 00 DEFB #00 ; Willy's anim frame 0-3
85D3 B45D DEFW #5DB4 ; Willy's position 0101|110Y|YYYX|XXXX
; actually an address in the attribute buffer
85D5 00 DEFB #00 ; Willy's jump phase counter (0 - 18)
85D6 00 DEFB #00 ; Willy's rope dot position or rope grab delay
85D7 DEFS 7 ; copies of #85CF-#85D5 used after dying
85DE 00 DEFB #00 ; number of items collected
85DF 00 DEFB #00 ; game/item status:
; 0: normal
1: all collected
2: running from bedroom
3: in toilet
85E0 00 DEFB #00 ; pause counter
; *************************** start of movement code *************************
8DD3 3AD685 LD A, (#85D6) ; Willy's dot when on a rope
8DD6 3D DEC A ; not on a rope = -1
8DD7 CB7F BIT 7, A ; not on rope or rope delay signified by bit-7
8DD9 CAD48E JP Z, #8ED4 ; branch if on rope
8DDC 3AD185 LD A, (#85D1) ; action states
8DDF FE01 CP #01
8DE1 2053 JR NZ, #8E36 ; branch if not currently JUMPING
8DE3 3AD585 LD A, (#85D5) ; JUMP phase counter
8DE6 E6FE AND #FE
8DE8 D608 SUB #08
8DEA 21CF85 LD HL, #85CF ; address of Willy's y * 2
8DED 86 ADD (HL)
8DEE 77 LD (HL), A ; jump height is (y * 2 & 0xFE) - 8
8DEF FEF0 CP #F0
8DF1 D2B094 JP NC, #94B0 ; jumped into room above
8DF4 CD9C8E CALL #8E9C ; update Willy's attribute buffer position
8DF7 3AB280 LD A, (#80B2) ; wall attr
8DFA BE CP (HL)
8DFB CABC8E JP Z, #8EBC ; branch if Willy's head hits wall
8DFE 23 INC HL ; move one col right
8DFF BE CP (HL)
8E00 CABC8E JP Z, #8EBC ; branch if Willy's head hits wall
8E03 3AD585 LD A, (#85D5) ; JUMP phase counter
8E06 3C INC A
8E07 32D585 LD (#85D5), A ; store incremented JUMP phase
8E0A D608 SUB #08 ; **** beeper starts here ****
8E0C F2118E JP P, #8E11 ; branch if SUB 8 goes below 0
8E0F ED44 NEG ; else phase = 0 - phase
8E11 3C INC A
8E12 07 RLCA
8E13 07 RLCA
8E14 07 RLCA ; multiply height by 16
8E15 57 LD D, A
8E16 0E20 LD C, #20
8E18 3ADE80 LD A, (#80DE) ; maintain border colour
8E1B D3FE OUT (#FE), A
8E1D EE18 XOR #18 ; toggle ear/mic bits
8E1F 42 LD B, D
8E20 10FE DJNZ #8E20 ; wait/pitch based on height
8E22 0D DEC C
8E23 20F6 JR NZ, #8E1B ; **** beeper ends here (after 32 cycles) ****
8E25 3AD585 LD A, (#85D5) ; JUMP phase counter
8E28 FE12 CP #12 ; 0x12 being 18 steps in a JUMP
8E2A CAB08E JP Z, #8EB0 ; branch if at end of JUMP phase
8E2D FE10 CP #10
8E2F 2805 JR Z, #8E36
8E31 FE0D CP #0D
8E33 C2BC8F JP NZ, #8FBC ; branch if not 12 (do horizontal movement)
8E36 3ACF85 LD A, (#85CF) ; Willy's y * 2
8E39 E60E AND #0E ; *** note: this is 0x0E in MM ***
8E3B 2025 JR NZ, #8E62 ; branch if y isn't row aligned
8E3D 2AD385 LD HL, (#85D3) ; Willy's position in attribute buffer
8E40 114000 LD DE, 64
8E43 19 ADD HL, DE
8E44 CB4C BIT 1, H
8E46 C2D294 JP NZ, #94D2 ; branch if we're in the room below
8E49 3ABB80 LD A, (#80BB) ; nasty attr
8E4C BE CP (HL)
8E4D 2813 JR Z, #8E62 ; branch if matched nasty
8E4F 23 INC HL ; move one col right
8E50 3ABB80 LD A, (#80BB) ; nasty attr
8E53 BE CP (HL)
8E54 280C JR Z, #8E62 ; branch if matched nasty
8E56 3AA080 LD A, (#80A0) ; bkgnd attr
8E59 BE CP (HL)
8E5A 2B DEC HL ; move one col left (resets HL to left foot)
8E5B C2D48E JP NZ, #8ED4 ; branch if not matched bkgnd
8E5E BE CP (HL)
8E5F C2D48E JP NZ, #8ED4 ; branch if not matched bkgnd
; There is no ground below Willy; why?
8E62 3AD185 LD A, (#85D1) ; Willy's action (standing/jumping/falling)
8E65 FE01 CP #01
8E67 CABC8F JP Z, #8FBC ; branch if JUMPING (do horizontal movement)
8E6A 21D085 LD HL, #85D0 ; walking/facing flags
8E6D CB8E RES 1, (HL) ; reset walking bit
8E6F 3AD185 LD A, (#85D1) ; Willy's action (dunno why it's LDed again?)
8E72 B7 OR A
8E73 CAB68E JP Z, #8EB6 ; branch if STANDING (this sets FALLING flag)
8E76 3C INC A ; else falling so increment fall height
8E77 FE10 CP #10 ; *** MM differences ***
8E79 2002 JR NZ, #8E7D ; *** need to investigate!!! ***
8E7B 3E0C LD A, #0C
8E7D 32D185 LD (#85D1), A ; then store it
8E80 07 RLCA ; **** beeper starts here ****
8E81 07 RLCA
8E82 07 RLCA
8E83 07 RLCA ; multiply height by 16
8E84 57 LD D, A
8E85 0E20 LD C, #20
8E87 3ADE80 LD A, (#80DE) ; maintain border colour
8E8A D3FE OUT (#FE), A ; make a noise
8E8C EE18 XOR #18 ; toggle ear/mic bits
8E8E 42 LD B, D
8E8F 10FE DJNZ #8E8F
8E91 0D DEC C
8E92 20F6 JR NZ, #8E8A ; **** beeper ends here (after 32 cycles) ****
8E94 3ACF85 LD A, (#85CF) ; Willy's y * 2
8E97 C608 ADD #08 ; 4 pixels down
8E99 32CF85 LD (#85CF), A ; then store it
; arrive here with A holding Willy's y * 2
8E9C E6F0 AND #F0 ; align to nearest row (0, 16, 32, etc.)
8E9E 6F LD L, A
8E9F AF XOR A
8EA0 CB15 RL L
8EA2 CE5C ADC #5C ; 0x5C00 being start of attribute buffer
8EA4 67 LD H, A
8EA5 3AD385 LD A, (#85D3) ; Willy's attribute buffer position
8EA8 E61F AND #1F ; col mask
8EAA B5 OR L
8EAB 6F LD L, A
8EAC 22D385 LD (#85D3), HL ; Willy's position updated
8EAF C9 RET
;
; change from JUMPING to FALLING
8EB0 3E06 LD A, #06
8EB2 32D185 LD (#85D1), A ; sets action/fall distance to 6?
8EB5 C9 RET
;
8EB6 3E02 LD A, #02
8EB8 32D185 LD (#85D1), A ; sets action flag to FALLING
8EBB C9 RET
; Willy has hit something while jumping.
8EBC 3ACF85 LD A, (#85CF) ; Willy's y * 2
8EBF C610 ADD #10 ; 8 pixels (one block) down
8EC1 E6F0 AND #F0 ; rounded to nearest block
8EC3 32CF85 LD (#85CF), A ; then store it
8EC6 CD9C8E CALL #8E9C ; set Willy's attribute buffer position
8EC9 3E02 LD A, #02
8ECB 32D185 LD (#85D1), A ; sets action flag to FALLING
8ECE 21D085 LD HL, #85D0 ; walking/facing flag
8ED1 CB8E RES 1, (HL) ; reset WALKING bit
8ED3 C9 RET
; HL is currently attributes under Willy's feet
8ED4 1EFF LD E, #FF ; equivalent to nothing pressed (active low)
8ED6 3AD685 LD A, (#85D6) ; Willy's y when on a rope
8ED9 3D DEC A
8EDA CB7F BIT 7, A
8EDC 281C JR Z, #8EFA ; branch if on a rope
8EDE 3AD185 LD A, (#85D1) ; Willy's action/fall distance
8EE1 FE0C CP #0C
8EE3 D2B790 JP NC, #90B7 ; branch if fallen beyond 12 (dead)
8EE6 AF XOR A
8EE7 32D185 LD (#85D1), A ; Willy's action set to STANDING (0)
8EEA 3ACD80 LD A, (#80CD) ; conveyor attr
8EED BE CP (HL)
8EEE 2804 JR Z, #8EF4 ; branch if matched conveyor
8EF0 23 INC HL ; move one col right
8EF1 BE CP (HL)
8EF2 2006 JR NZ, #8EFA ; branch if not matched conveyor
8EF4 3AD680 LD A, (#80D6) ; conveyor direction
8EF7 D603 SUB #03 ; left = 253 (LSBs 01), right 254 (LSBs 10)
8EF9 5F LD E, A
; the input routines
8EFA 01FEDF LD BC, #DFFE
8EFD ED78 IN A, (C) ; read Y, U, I, O, P
8EFF E61F AND #1F ; mask only key bits (0-4)
8F01 F620 OR #20 ; set bit-5 (used for LEFT presses later)
8F03 A3 AND E
8F04 5F LD E, A
8F05 3ADF85 LD A, (#85DF) ; All items collected? *** Not MM ***
8F08 E602 AND #02
8F0A 0F RRCA
8F0B AB XOR E
8F0C 5F LD E, A ; *** End of not MM section ***
8F0D 01FEFB LD BC, #FBFE
8F10 ED78 IN A, (C) ; read Q, W, E, R, T
8F12 E61F AND #1F ; mask key bits (0-4)
8F14 CB07 RLC A ; changes key sequence to L-R not R-L
8F16 F601 OR #01 ; 'unpresses' a RIGHT key
8F18 A3 AND E
8F19 5F LD E, A
8F1A 06EF LD B, #EF
8F1C ED78 IN A, (C) ; read 6, 7, 8, 9, 0
8F1E 0F RRCA ; only interested in key 6 (now at bit-3)
8F1F F6F7 OR #F7 ; so mask (active low)
8F21 A3 AND E
8F22 5F LD E, A
8F23 06EF LD B, #EF
8F25 ED78 IN A, (C) ; read 6, 7, 8, 9, 0
8F27 F6FB OR #FB ; mask key 8 (bit-2, active low)
8F29 A3 AND E
8F2A 5F LD E, A
8F2B ED78 IN A, (C) ; read 6, 7, 8, 9, 0
8F2D 0F RRCA ; only interested in key 7 (now at bit-2)
8F2E F6FB OR #FB ; so mask (active low)
8F30 A3 AND E
8F31 5F LD E, A
8F32 3ACE85 LD A, (#85CE)
8F35 B7 OR A
8F36 280A JR Z, #8F42 ; branch if no joystick connected
8F38 011F00 LD BC, #001F
8F3B ED78 IN A, (C) ; else read joystick port
8F3D E603 AND #03 ; left and right mask (bits are xxxFUDLR)
8F3F 2F CPL ; because Kempston is active high
8F40 A3 AND E ; reach here with E holding key/joystick data
8F41 5F LD E, A
8F42 0E00 LD C, #00
8F44 7B LD A, E
8F45 E62A AND #2A
8F47 FE2A CP #2A
8F49 2806 JR Z, #8F51
8F4B 0E04 LD C, #04
8F4D AF XOR A
8F4E 32E085 LD (#85E0), A
8F51 7B LD A, E
8F52 E615 AND #15
8F54 FE15 CP #15
8F56 2806 JR Z, #8F5E
8F58 CBD9 SET 3, C
8F5A 8F ADC A
8F5B 32E085 LD (#85E0), A
8F5E 3AD085 LD A, (#85D0)
8F61 81 ADD C
8F62 4F LD C, A
8F63 0600 LD B, #00
8F65 212184 LD HL, #8421
8F68 09 ADD HL, BC
8F69 7E LD A, (HL)
8F6A 32D085 LD (#85D0), A
8F6D 01FE7E LD BC, #7EFE
8F70 ED78 IN A, (C) ; read bottom row CAPS to SPACE
8F72 E61F AND #1F ; mask key bits (0-4)
8F74 FE1F CP #1F
8F76 2017 JR NZ, #8F8F ; branch if any key at all is pressed
8F78 06EF LD B, #EF
8F7A ED78 IN A, (C) ; read 6, 7, 8, 9, 0
8F7C CB47 BIT 0, A
8F7E 280F JR Z, #8F8F ; branch if 0 (bit-0) is pressed
8F80 3ACE85 LD A, (#85CE) ; no jump key pressed so we check the joystick
8F83 B7 OR A
8F84 2836 JR Z, #8FBC ; none connected so do horizontal movement
8F86 011F00 LD BC, #001F
8F89 ED78 IN A, (C) ; read Kempston joystick
8F8B CB67 BIT 4, A ; fire bit
8F8D 282D JR Z, #8FBC ; fire not pressed so do horizontal movement
8F8F 3ADF85 LD A, (#85DF) ; *** Item to collect? ***
8F92 CB4F BIT 1, A
8F94 2026 JR NZ, #8FBC
8F96 AF XOR A
8F97 32D585 LD (#85D5), A ; set JUMP phase counter to 0
8F9A 32E085 LD (#85E0), A ; set PAUSE counter to 0
8F9D 3C INC A
8F9E 32D185 LD (#85D1), A ; set the action flag to 1 (for jumping)
8FA1 3AD685 LD A, (#85D6) ; Willy's y when on a rope
8FA4 3D DEC A
8FA5 CB7F BIT 7, A
8FA7 2013 JR NZ, #8FBC ; branch if not on a rope
8FA9 3EF0 LD A, #F0
8FAB 32D685 LD (#85D6), A
8FAE 3ACF85 LD A, (#85CF)
8FB1 E6F0 AND #F0
8FB3 32CF85 LD (#85CF), A
8FB6 21D085 LD HL, #85D0
8FB9 CBCE SET 1, (HL)
8FBB C9 RET
; could be move decision code?
8FBC 3AD085 LD A, (#85D0) ; move state
8FBF E602 AND #02
8FC1 C8 RET Z
8FC2 3AD685 LD A, (#85D6) ; Willy's y when on a rope
8FC5 3D DEC A
8FC6 CB7F BIT 7, A
8FC8 C8 RET Z ; return if on rope
8FC9 3AD085 LD A, (#85D0)
8FCC E601 AND #01
8FCE CA4290 JP Z, #9042 ; move left
8FD1 3AD285 LD A, (#85D2) ; Willy's phase
8FD4 B7 OR A
8FD5 2805 JR Z, #8FDC
8FD7 3D DEC A
8FD8 32D285 LD (#85D2), A
8FDB C9 RET
; move Willy left
8FDC 3AD185 LD A, (#85D1) ; Willy's action states
8FDF 010000 LD BC, #0000
8FE2 FE00 CP #00
8FE4 2024 JR NZ, #900A
8FE6 2AD385 LD HL, (#85D3)
8FE9 010000 LD BC, #0000
8FEC 3ADA80 LD A, (#80DA) ; ramp direction (0 = L, 1 = R)
8FEF 3D DEC A
8FF0 F6A1 OR #A1
8FF2 EEE0 XOR #E0 ; ramp direction is now #1F = L, #41 = R
8FF4 5F LD E, A
8FF5 1600 LD D, #00
8FF7 19 ADD HL, DE
8FF8 3AC480 LD A, (#80C4) ; ramp attr
8FFB BE CP (HL)
8FFC 200C JR NZ, #900A
8FFE 012000 LD BC, #0020
9001 3ADA80 LD A, (#80DA)
9004 B7 OR A
9005 2003 JR NZ, #900A
9007 01E0FF LD BC, #FFE0
900A 2AD385 LD HL, (#85D3) ; lots of stuff jumps to here...
900D 7D LD A, L
900E E61F AND #1F
9010 CA8A94 JP Z, #948A
9013 09 ADD HL, BC
9014 2B DEC HL
9015 112000 LD DE, 32
9018 19 ADD HL, DE ; move one row down
9019 3AB280 LD A, (#80B2) ; wall attr
901C BE CP (HL)
901D C8 RET Z ; return if we hit a wall (Willy's feet block)
901E 3ACF85 LD A, (#85CF)
9021 CB29 SRA C
9023 81 ADD C
9024 47 LD B, A
9025 E60F AND #0F
9027 2809 JR Z, #9032 ; branch if y is block aligned
9029 3AB280 LD A, (#80B2) ; wall attr
902C 19 ADD HL, DE
902D BE CP (HL)
902E C8 RET Z
902F B7 OR A
9030 ED52 SBC HL, DE ; move one row up
9032 B7 OR A
9033 ED52 SBC HL, DE ; move one row up again **MM tests this block**
9035 22D385 LD (#85D3), HL ; update Willy's position
9038 78 LD A, B
9039 32CF85 LD (#85CF), A
903C 3E03 LD A, #03 ; frame 3 (far right if 16x16)
903E 32D285 LD (#85D2), A ; update Willy's anim frame
9041 C9 RET
; moving right
9042 3AD285 LD A, (#85D2) ; Willy's anim frame
9045 FE03 CP #03
9047 2805 JR Z, #904E ; branch if frame 3 (far right of 16x16)
9049 3C INC A ; else increment it
904A 32D285 LD (#85D2), A
904D C9 RET ; nothing else to check
904E 3AD185 LD A, (#85D1) ; Willy's action states
9051 010000 LD BC, #0000
9054 B7 OR A
9055 2021 JR NZ, #9078 ; if fall/move isn't 0 (ie not standing)
9057 2AD385 LD HL, (#85D3) ; Willy's position on attribute buffer
905A 3ADA80 LD A, (#80DA) ; ramp direction (0 = L, 1 = R)
905D 3D DEC A
905E F69D OR #9D
9060 EEBF XOR #BF ; ramp direction is now #40 = L, #22 = R
9062 5F LD E, A
9063 1600 LD D, #00 ; attribute we're looking at is now either
9065 19 ADD HL, DE ; two rows or one row and two cols below
9066 3AC480 LD A, (#80C4) ; ramp attr
9069 BE CP (HL)
906A 200C JR NZ, #9078 ; branch if we're not on a ramp
906C 012000 LD BC, #0020
906F 3ADA80 LD A, (#80DA) ; ramp direction
9072 B7 OR A
9073 2803 JR Z, #9078 ; ramp is left
9075 01E0FF LD BC, #FFE0 ; either #0020 (left) or #FFE0 (right) or 0
9078 2AD385 LD HL, (#85D3) ; Willy's position on attribute buffer
907B 09 ADD HL, BC ; now we're either one row below or one up...
907C 23 INC HL ; ...if on a ramp, or none otherwise
907D 23 INC HL ; and two cols right (block to right of Willy)
907E 7D LD A, L
907F E61F AND #1F
9081 CA9E94 JP Z, #949E ; off the left of the screen
9084 112000 LD DE, 32
9087 3AB280 LD A, (#80B2) ; wall attr
908A 19 ADD HL, DE ; move one row down
908B BE CP (HL)
908C C8 RET Z ; return if we hit a wall (Willy's feet block)
908D 3ACF85 LD A, (#85CF) ; Willy's y
9090 CB29 SRA C
9092 81 ADD C
9093 47 LD B, A ; store y * 2 for later
9094 E60F AND #0F
9096 2809 JR Z, #90A1 ; branch if y is block aligned
9098 3AB280 LD A, (#80B2) ; wall attr
909B 19 ADD HL, DE ; move one row down again
909C BE CP (HL)
909D C8 RET Z ; return if we hit a wall (below Willy's feet)
909E B7 OR A ; [this must be to reset the carry flag?]
909F ED52 SBC HL, DE ; move one row back up
90A1 3AB280 LD A, (#80B2) ; wall attr
90A4 B7 OR A ; [this must be to reset the carry flag?]
90A5 ED52 SBC HL, DE ; move one row up again
90A7 BE CP (HL)
90A8 C8 RET Z ; return if we hit a wall (Willy's head block)
90A9 2B DEC HL ; all was ok so move back left by one col
90AA 22D385 LD (#85D3), HL ; and update Willy's position
90AD AF XOR A ; sets frame to zero (far left of 16x16)
90AE 32D285 LD (#85D2), A ; update Willy's anim frame
90B1 78 LD A, B
90B2 32CF85 LD (#85CF), A ; updates Willy's y * 2
90B5 C9 RET
90B6 E1 POP HL
90B7 E1 POP HL
90B8 3EFF LD A, #FF ; Willy has been killed (#85D1 = 0xFF)
90BA 32D185 LD (#85D1), A
90BD C3F589 JP #89F5
; *************************** end of movement code ***************************
; *********************** Some extra collision checking **********************
95C8 2AD385 LD HL, (#85D3) ; Willy's position on attribute buffer
95CB 0600 LD B, #00
95CD 3ADA80 LD A, (#80DA) ; ramp direction (0 = L, 1 = R)
95D0 E601 AND #01
95D2 C640 ADD #40 ; down 2 rows
95D4 5F LD E, A
95D5 1600 LD D, #00
95D7 19 ADD HL, DE ; L ramps move 2 rows; R moves 1 col & 2 rows
95D8 3AC480 LD A, (#80C4) ; ramp attr
95DB BE CP (HL)
95DC 201A JR NZ, #95F8 ; branch if we're not on a ramp
95DE 3AD185 LD A, (#85D1) ; Willy's action/fall distance
95E1 B7 OR A
95E2 2014 JR NZ, #95F8 ; branch if we're not walking or standing
95E4 3AD285 LD A, (#85D2) ; Willy's anim frame
95E7 E603 AND #03 ; frames mask
95E9 07 RLCA
95EA 07 RLCA ; frame multiplied by 4
95EB 47 LD B, A
95EC 3ADA80 LD A, (#80DA) ; ramp direction (0 = L, 1 = R)
95EF E601 AND #01
95F1 3D DEC A ; gives L = 0xFF or R = 0x00
95F2 EE0C XOR #0C ; gives L = 0xF3 or R = 0x0C
95F4 A8 XOR B ; basically L ramps give an offset of FRAME * 2
95F5 E60C AND #0C ; whereas R ramps offset by (3 - FRAME) * 2
95F7 47 LD B, A ; (given that Willy's y is multiplied by 2)
; Normal MM-style code starts here.
95F8 2AD385 LD HL, (#85D3) ; Willy's position in attribute buffer
95FB 111F00 LD DE, #001F
95FE 0E0F LD C, #0F
9600 CD1E96 CALL #961E ; test for nasty (head left)
9603 23 INC HL ; move one col right
9604 CD1E96 CALL #961E ; test for nasty (head right)
9607 19 ADD HL, DE ; move one row down and one col left
9608 CD1E96 CALL #961E ; test for nasty (legs left)
960B 23 INC HL ; move one col right
960C CD1E96 CALL #961E ; test for nasty (legs right)
960F 3ACF85 LD A, (#85CF) ; Willy's y * 2
9612 80 ADD B ; compensate for ramp (see #95C8)
9613 4F LD C, A
9614 19 ADD HL, DE ; move one row down and one col left
9615 CD1E96 CALL #961E ; test for nasty (under left foot)
9618 23 INC HL ; move one col right
9619 CD1E96 CALL #961E ; test for nasty (under right foot)
961C 1819 JR #9637 ; no nasties so draw Willy
; code to test for nasty blocks (also sets Willy's colour attributes)
961E 3AA080 LD A, (#80A0) ; bkgnd attr
9621 BE CP (HL)
9622 200B JR NZ, #962F ; branch if not bkgnd
9624 79 LD A, C ; either contains 0x0F or y * 2 + ramp offset
9625 E60F AND #0F ; block mask (given that y is multiplied by 2)
9627 2806 JR Z, #962F ; branch if block aligned (no need to colour)
9629 3AA080 LD A, (#80A0) ; bkgnd attr
962C F607 OR #07 ; set the ink to white
962E 77 LD (HL), A ; then put this colour in the attribute buffer
962F 3ABB80 LD A, (#80BB) ; nasty attr
9632 BE CP (HL)
9633 CAB690 JP Z, #90B6 ; branch if killed
9636 C9 RET
; code to draw Willy
9637 3ACF85 LD A, (#85CF) ; Willy's y * 2
963A 80 ADD B ; compensate for ramp (see #95C8)
; the remaining draw code doesn't need commenting
; ****************************************************************************
IX+0 -- 3 MSBs are continuous frame count (HGs use MSB as direction)
; Horizontal guardian
; -------------------
L9133: BIT 7,(IX+&00) ; direction
JR NZ,L915C ; branch if moving right (bit-7 == 1)
; Move horizontal guardian left
; -----------------------------
LD A,(IX+&00)
SUB &20 ; decrement 'frame' counter by 1
AND &7F ; set MSB to 0 (moving left flag)
LD (IX+&00),A ; update it
CP &60
JR C,L91B6 ; branch if frame != 3
LD A,(IX+&02) ; frame and col whatnot
AND &1F ; mask just the col
CP (IX+&06) ; compare with min (left) boundary
JR Z,L9156 ; branch if reached boundary (flip direction)
DEC (IX+&02) ; else decrement the current col
JR L91B6 ; do next guardian
L9156: LD (IX+&00),&81 ; set to moving right and zero frame count
JR L91B6 ; do next guardian
; Move horizontal guardian right
; ------------------------------
L915C: LD A,(IX+&00)
ADD A,&20 ; increment 'frame' counter by 1
OR &80 ; set MSB to 1 (moving right flag)
LD (IX+&00),A ; update it
CP &A0
JR NC,L91B6
LD A,(IX+&02) ; frame and col whatnot
AND &1F ; mask just the col
CP (IX+&07) ; compare with max (right) boundary
JR Z,L9179 ; branch if reached boundary (flip direction)
INC (IX+&02) ; else increment the current col
JR L91B6 ; do next guardian
L9179: LD (IX+&00),&61 ; set to moving left and on last frame
JR L91B6 ; do next guardian
; Vertical guardian
; -----------------
L917F: LD A,(IX+&00)
XOR &08 ; flip bit-3
LD (IX+&00),A
AND &18 ; bit-4 and bit-3 (anim speed and cycling thing)
JR Z,L9193 ; branch to skip every other frame (anim slow)
LD A,(IX+&00)
ADD A,&20 ; else increment the frame
LD (IX+&00),A
L9193: LD A,(IX+&03) ; y (x2)
ADD A,(IX+&04) ; add the step (x2)
LD (IX+&03),A ; update y (x2)
CP (IX+&07) ; compare with max (bottom) boundary
JR NC,L91AE ; branch if >= max
CP (IX+&06) ; compare with min (top) boundary
JR Z,L91A8 ; branch if == min
JR NC,L91B6 ; branch if >= min (do next guard)
L91A8: LD A,(IX+&06)
LD (IX+&03),A ; set the y (x2) to min (top) boundary
L91AE: LD A,(IX+&04)
NEG ; negate velocity
LD (IX+&04),A
L91B6: LD DE,&0008 ; 8 bytes per record
ADD IX,DE
JP L90C4 ; do next guardian
IX+0 bit-7 direction
IX+2
IX+4 start col, later used for actual working col
L9237: BIT 7,(IX+&00) ; arrow direction (0 = L, 1 = R)
JR NZ,L9244 ; branch if moving right
DEC (IX+&04) ; move left one col
LD C,&2C ; 44
JR L9249
L9244: INC (IX+&04) ; move right one col
LD C,&F4 ; 244
L9249: LD A,(IX+&04) ; arrow col/timer
CP C
JR NZ,L9262 ; branch if not equal to 44 (L) or 244 (R)
LD BC,&0280
LD A,(BORDER)
L9255: OUT (&FE),A ; else do beep
XOR &18
L9259: DJNZ L9259
LD B,C
DEC C
JR NZ,L9255
JP L93B3 ; after beeping move on to next guardian
L9262: AND &E0
JP NZ,L93B3 ; branch if the current col is off-screen
LD E,(IX+&02) ; y * 2 [need to look at - block aligned?]
LD D,&82 ; 0x8200 buffer row lookup table
LD A,(DE)
ADD A,(IX+&04) ; current col
LD L,A ; L holds low-byte of attribute buffer
LD A,E
AND &80
RLCA
OR &5C
LD H,A ; HL now points to arrow's attr buffer position
LD (IX+&05),&00 ; (for later testing *no* pixels for collision)
LD A,(HL) ; get the attr where the arrow would be
AND &07 ; ink mask
CP &07
JR NZ,L9286 ; branch if not white (white ink is Willy)
DEC (IX+&05) ; else IX+5 = 255 (test for collision later)
L9286: LD A,(HL) ; get the attr where the arrow would be
OR &07 ; set the ink to white
LD (HL),A ; then put it back in the buffer
INC DE ; down one row? [arrows appear not to start @ 0]
LD A,(DE)
LD H,A
DEC H ; [too tired right now! Anyway HL is pixel addr]
LD A,(IX+&06) ; top/bottom pixel pattern
LD (HL),A ; draw the top of the arrow
INC H ; down one row
LD A,(HL) ; get the pixels where the arrow middle wil be
AND (IX+&05) ; holds 255 if the ink is white or 0 otherwise
JP NZ,DIED1 ; branch if anything is there (and on white)
LD (HL),&FF ; draw the middle of the arrow (an 8px line)
INC H ; down one row
LD A,(IX+&06) ; top/bottom pixel pattern
LD (HL),A ; draw the bottom of the arrow
JP L93B3
0 guardian type (with bit-7 for swing direction)
1 [moving] swing table index offset
2 [init] start col
3 [drawing] dot col (currently being drawn into)
4 rope length
5 [drawing] dot bitmap pattern
6 *not used* (I'm guessing it was pointing to the swing table)
7 maximum rope swing
8
9 [drawing] dot y * 2 (offset into lookup table)
A
B [drawing] Willy is on THIS rope
C
#85CF: Willy's current rope dot
T8100: DEFS #0048 ; guardian records
T8200: DEFS #0100 ; screen buffer y lookup table
; Rope swing table
T8300: DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; x offsets
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DEFB 1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2
DEFB 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
DEFB 2,2,1,2,2,1,1,2,1,1,2,2,3,2,3,2
DEFB 3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 ; y offsets
DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
DEFB 4,6,6,4,6,4,6,4,6,4,4,4,6,4,4,4
DEFB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
DEFB 4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
; ******************************** Move rope *********************************
BIT 7,(IX+&00) ; rope direction (0 = left, 1 = right)
JR Z,L90FF ; branch if moving left else moving right
LD A,(IX+&01) ; rope swing table index offset
BIT 7,A
JR Z,L90F5 ; branch if not negative
SUB &02 ; subract 2 (drawing LHS)
CP &94
JR NC,L911D
SUB &02 ; subract another 2 if less than -108
CP &80
JR NZ,L911D
XOR A ; set the swing to 0 if equal to -128
JR L911D
L90F5: ADD A,&02 ; add 2 (drawing RHS)
CP &12
JR NC,L911D
ADD A,&02 ; add another 2 if less than 18
JR L911D
; right rope swing code
L90FF: LD A,(IX+&01)
BIT 7,A
JR NZ,L9115 ; branch if negative
SUB &02 ; subract 2 (drawing RHS)
CP &14
JR NC,L911D
SUB &02 ; subract another 2 if less than 20
OR A
JR NZ,L911D
LD A,&80 ; set the swing to -128 if equal to 0
JR L911D
L9115: ADD A,&02 ; add 2 (drawing LHS)
CP &92
JR NC,L911D
ADD A,&02 ; add another 2 if less than -112
L911D: LD (IX+&01),A ; update the current rope swing
AND &7F
CP (IX+&07) ; maximum rope swing
JP NZ,L91B6 ; branch if rope can continue swinging
LD A,(IX+&00)
XOR &80 ; else reverse the rope's direction
LD (IX+&00),A
JP L91B6
L91B6: ; continue for the other guardians
; ***************************** Draw guardians *******************************
L91BE: LD IX,&8100 ; current room's guardian records
L91C2: LD A,(IX+&00) ; guardian type
CP &FF
RET Z ; return to main loop if at end of records
AND &07 ; guardian type mask
JP Z,L93B3 ; branch if an empty record
CP &03
JP Z,L92A4 ; branch if type 3 (rope)
CP &04
JR Z,L9237 ; branch if type 4 (arrow)
; [code continues for H and V guardians]
; rope entry point
; [initialise this rope]
L92A4: LD IY,&8200 ; screen buffer y lookup table
LD (IX+&09),&00 ; current dot y * 2 (offset into lookup table)
LD A,(IX+&02) ; rope start col
LD (IX+&03),A ; current dot col (reset to start col)
LD (IX+&05),&80 ; dot bitmap pattern (LHS pixel to start with)
; [start of the loop to draw the dots]
L92B6: LD A,(IY+&00) ; low-byte of buffer y table
ADD A,(IX+&03) ; add current dot col to get screen low-byte
LD L,A
LD H,(IY+&01) ; HL at screen address of current dot col
LD A,(#85D6) ; rope dot Willy is on (0 when not on a rope)
OR A
JR NZ,L92D6 ; branch if Willy is already on a rope
; [find if Willy is touching a rope]
LD A,(IX+&05) ; bitmap pattern of previous dot
AND (HL) ; compare the dot pattern with screen buffer
JR Z,L930E ; branch if nothing is where the rope should be
LD A,(IX+&09) ; else Willy is touching so take current dot y
LD (#85D6),A ; set Willy's rope dot to current dot
SET 0,(IX+&0B) ; set the flag to show Willy is on this rope
; [set Willy's frame and position]
L92D6: CP (IX+&09) ; compare Willy's dot with this rope dot
JR NZ,L930E ; branch if Willy is not at this dot
BIT 0,(IX+&0B)
JR Z,L930E ; branch if Willy is not on this rope
LD B,(IX+&03) ; used later for setting Willy's col to dot col
LD A,(IX+&05) ; dot bitmap pattern
LD C,&01 ; used later for setting Willy's frame to 1
CP &04
JR C,L92FC ; branch if dot pattern fits ......XX
LD C,&00 ; used later for setting Willy's frame to 0
CP &10
JR C,L92FC ; branch if dot pattern fits ....XX..
DEC B ; used later for moving Willy one col left
LD C,&03 ; used later for setting Willy's frame to 3
CP &40
JR C,L92FC ; branch if dot pattern fits ..XX....
LD C,&02 ; else Willy's frame is 2 (pattern XX......)
L92FC: LD (#85D2),BC ; Willy's anim frame (and col)
LD A,IYL ; row taken from lookup index low-byte
SUB &10 ; up 8 pixels
LD (#85CF),A ; update Willy's y * 2
PUSH HL
CALL L8E9C ; update Willy's attribute buffer position
POP HL
JR L930E
; [draw the current dot/create the next dot]
L930E: LD A,(IX+&05) ; bitmap pattern for drawing the dot
OR (HL) ; combine with the existing screen pixels
LD (HL),A ; draw it back to screen
LD A,(IX+&09) ; current dot's y * 2
ADD A,(IX+&01) ; add the rope swing table index offset
LD L,A ; put it into low-byte of lookup address
SET 7,L ; using y offsets in rope table
LD H,&83 ; as in 0x8300, the rope table
LD E,(HL) ; y offset
LD D,&00
ADD IY,DE ; move down the screen by the offset rows
RES 7,L ; using x offsets in rope table
LD A,(HL) ; x offset
OR A
JR Z,L9350 ; branch if 0 (bit pattern remains the same)
; [create the dot pattern]
LD B,A
BIT 7,(IX+&01) ; rope swing table index offset sign
JR Z,L9341 ; branch if a positive number
L9330: RLC (IX+&05) ; else move the dot pattern to the left
BIT 0,(IX+&05)
JR Z,L933D ; branch if the dot is not in a new col
DEC (IX+&03) ; else move current dot col left
L933D: DJNZ L9330 ; looping until the dot pattern is finished
JR L9350 ; [skipping over code for moving right]
L9341: RRC (IX+&05) ; move the dot pattern to the right
BIT 7,(IX+&05)
JR Z,L934E ; branch if the dot is not in a new col
INC (IX+&03) ; else move current dot col right
L934E: DJNZ L9341 ; looping until the dot pattern is finished
; [repeat to draw the remaining rope sections]
L9350: LD A,(IX+&09) ; current dot row
CP (IX+&04) ; rope length
JR Z,L935E ; branch if reached end of rope
INC (IX+&09) ; else move down to the next dot row
JP L92B6 ; and repeat for the next dot
; [test whether Willy is still on this rope]
L935E: LD A,(85D6) ; Willy's rope dot position
BIT 7,A
JR Z,L936F ; branch if is still on the rope (bit-7 = 0)
INC A ; else he's off but there's a delay before...
LD (85D6),A ; ...he can get back on (16 ticks)
RES 0,(IX+&0B) ; flag him as not being on this rope
JR L93B3 ; no need to perform next section so done
; [allow Willy to climb up or down this rope]
L936F: BIT 0,(IX+&0B)
JR Z,L93B3 ; branch if Willy is not on this rope
LD A,(#85D0) ; Willy's walking/facing flags
BIT 1,A
JR Z,L93B3 ; branch if Willy is standing still
RRCA ; facing flag now at bit-7 (walking at bit-0)
XOR (IX+&00) ; XOR with rope swing direction - left rope...
RLCA ; ...is normal Willy L/R, right rope reverses
RLCA ; facing result in bit-1
AND &02 ; mask just bit-1
DEC A ; result is now 1 or -1
LD HL,#85D6 ; Willy's rope dot address
ADD A,(HL) ; move either up one dot or down one dot
LD (HL),A
LD A,(#80EB) ; room above this room
LD C,A
LD A,(#8420) ; current room
CP C
JR NZ,L939B ; branch if going up leads to a different room
LD A,(HL) ; Willy's rope dot
CP &0C
JR NC,L939B ; branch if Willy's rope dot > 12
LD (HL),&0C ; else set Willy's rope dot to 12 (can't go up)
L939B: LD A,(HL) ; Willy's rope dot
CP (IX+&04) ; rope length
JR C,L93B3 ; branch if Willy's rope dot < the rope length
JR Z,L93B3 ; branch if Willy's rope dot = the rope length
LD (HL),&F0 ; else set bit-7 (not attached) and prep delay
LD A,(#85CF) ; Willy's y * 2
AND &F8 ; align it to half a block
LD (#85CF),A ; then update it
XOR A
LD (#85D1),A ; change Willy's action to standing
JR L93B3
L93B3: LD DE,&0008
ADD IX,DE ; move to next guardian record
JP L91C2
(C) 1983,1984,1999 Matthew Smith - all rights reserved