Difference between revisions of "Disassembly Notes (NES)"
Line 2: | Line 2: | ||
If you sit idle on the title screen for a while, a demo plays. It's triggered by the frame counter rolling over to <code>00</code> 8 times in a row. The frame counter cycles through from <code>00</code>-<code>FF</code>. So this means it takes anywhere from ~29-34 seconds to start ((256 frames * 7 + [1,256] frames) / 60 frames per second). It can be disabled a number of ways, but the simplest way is editing the jump offset (to <code>0</code>) when it decides to load the demo code (at <code>$9905</code>), accomplishable through the Game Genie code <code>AAAPSP</code>. Next, while the demo board is a generatable one (seed <code>03BE</code>), it's stored directly in the ROM (<code>$CF00</code>-<code>$CF7F</code>) and loaded into memory. The capsules generated do not match this seed, but they're stored right after the board (<code>$CF80</code>-<code>$CFFF</code>). The controls are stored in pairs of bytes, the first byte being the buttons pressed (bitmask - A, B, Select, Start, Up, Down, Left, Right) and the second being the number of frames to hold down those buttons. These are stored right after the board/capsules at <code>$D000</code>-<code>$D1FF</code>. | If you sit idle on the title screen for a while, a demo plays. It's triggered by the frame counter rolling over to <code>00</code> 8 times in a row. The frame counter cycles through from <code>00</code>-<code>FF</code>. So this means it takes anywhere from ~29-34 seconds to start ((256 frames * 7 + [1,256] frames) / 60 frames per second). It can be disabled a number of ways, but the simplest way is editing the jump offset (to <code>0</code>) when it decides to load the demo code (at <code>$9905</code>), accomplishable through the Game Genie code <code>AAAPSP</code>. Next, while the demo board is a generatable one (seed <code>03BE</code>), it's stored directly in the ROM (<code>$CF00</code>-<code>$CF7F</code>) and loaded into memory. The capsules generated do not match this seed, but they're stored right after the board (<code>$CF80</code>-<code>$CFFF</code>). The controls are stored in pairs of bytes, the first byte being the buttons pressed (bitmask - A, B, Select, Start, Up, Down, Left, Right) and the second being the number of frames to hold down those buttons. These are stored right after the board/capsules at <code>$D000</code>-<code>$D1FF</code>. | ||
+ | |||
+ | == Pill movement == | ||
+ | |||
+ | This is all the code responsible for moving the pill each frame. The disassembly and function descriptions are from [https://github.com/brianhuffman/drmario Brian Huffman's disassembly]; this version has added detailed English descriptions of the code. I also did a cursory look through the code for what $06f1 does, but I couldn't find anything obvious that read from that address. So either it's dead code, or it's read via indirect addressing. | ||
+ | |||
+ | jsr R8D70_DROP_PILL ; may go to state 1 ; 8D66 20 70 8D | ||
+ | jsr R8DBF_PILL_MOVE ; 8D69 20 BF 8D | ||
+ | jsr R8E2B_PILL_ROTATE ; 8D6C 20 2B 8E | ||
+ | |||
+ | ; -------------------------------------- | ||
+ | ; Called once, in L8D56. | ||
+ | R8D70_DROP_PILL: | ||
+ | begin | ||
+ | lda L43_CLOCK ; 8D70 A5 43 | ||
+ | and #$01 ; 8D72 29 01 | ||
+ | beq L8D7E ; 8D74 F0 08 Every other frame, | ||
+ | lda L5C_INPUT_OLD ; 8D76 A5 5C | ||
+ | and #$0F ; 8D78 29 0F | ||
+ | cmp #$04 ; "down" ; 8D7A C9 04 if the user is pressing down but not left, right, or up, | ||
+ | beq L8D94 ; 8D7C F0 16 skip ahead to L8D94. | ||
+ | L8D7E: | ||
+ | inc L92_DROP_TIMER ; 8D7E E6 92 Increase the drop counter. | ||
+ | lda L8A_EXTRA_SPEED ; 8D80 A5 8A | ||
+ | sta LA0_TEMP ; redundant copy ; 8D82 85 A0 | ||
+ | ldx L8B_SPEED ; 8D84 A6 8B | ||
+ | lda LA38D,x ; $0F,$19,$1F ; 8D86 BD 8D A3 Load the base speed offset for the player-selected speed (low, med, hi), | ||
+ | add LA0_TEMP ; 8D89 18 65 A0 then add in the offset for having played a long time. | ||
+ | tax ; 8D8C AA | ||
+ | lda LA795,x ; drop timer threshold ; 8D8D BD 95 A7 Load the gravity value. | ||
+ | cmp L92_DROP_TIMER ; 8D90 C5 92 If the drop counter is <= the gravity value, | ||
+ | bcs break ; 8D92 B0 2A return from this function. | ||
+ | L8D94: | ||
+ | dec L86_PILL_ROW ; 8D94 C6 86 Move down. | ||
+ | lda #$00 ; 8D96 A9 00 | ||
+ | sta L92_DROP_TIMER ; 8D98 85 92 Reset the drop counter to 0. | ||
+ | jsr R90D3_COLLISION ; 8D9A 20 D3 90 | ||
+ | if_eq ; 8D9D D0 06 If we are over in-bounds empty space, | ||
+ | lda L86_PILL_ROW ; 8D9F A5 86 | ||
+ | cmp #$FF ; 8DA1 C9 FF and not below the board, | ||
+ | bne break ; 8DA3 D0 19 return from this function. | ||
+ | end_if | ||
+ | inc L86_PILL_ROW ; 8DA5 E6 86 Move up. | ||
+ | lda #$07 ; 8DA7 A9 07 | ||
+ | sta L06F1_ ; 8DA9 8D F1 06 Set $06F1 to 7. | ||
+ | jsr R8F52_PLACE_PILL ; 8DAC 20 52 8F Lock the pill in place. | ||
+ | ; make the program crash if L0740_CHECKSUM_FAIL is nonzero | ||
+ | lda L0740_CHECKSUM_FAIL ; 8DAF AD 40 07 | ||
+ | if_ne ; 8DB2 F0 05 | ||
+ | lda L53_SPRITE_NUM ; 8DB4 A5 53 | ||
+ | pha ; 8DB6 48 | ||
+ | pha ; 8DB7 48 | ||
+ | pha ; 8DB8 48 | ||
+ | end_if | ||
+ | inc L97_GAME_STATE ; 8DB9 E6 97 | ||
+ | jmp break ; 8DBB 4C BE 8D | ||
+ | end | ||
+ | rts ; 8DBE 60 | ||
+ | |||
+ | ; -------------------------------------- | ||
+ | ; Increment or decrement pill column based on controller input. | ||
+ | ; Repeat every 6 frames after holding for 16. | ||
+ | R8DBF_PILL_MOVE: | ||
+ | begin | ||
+ | lda L5B_INPUT_NEW ; 8DBF A5 5B | ||
+ | and #$03 ; left or right ; 8DC1 29 03 | ||
+ | if_eq ; 8DC3 D0 15 If the user did not start pressing left or right, | ||
+ | lda L5C_INPUT_OLD ; 8DC5 A5 5C | ||
+ | and #$03 ; left or right ; 8DC7 29 03 | ||
+ | beq break ; 8DC9 F0 5F and neither left nor right is currently pressed, return from this function. Otherwise, | ||
+ | inc L93_REPEAT_TIMER ; 8DCB E6 93 increase the DAS counter. | ||
+ | lda L93_REPEAT_TIMER ; 8DCD A5 93 | ||
+ | cmp #$10 ; 8DCF C9 10 | ||
+ | bmi break ; 8DD1 30 57 If the DAS counter is under 16, return from this function. Otherwise, | ||
+ | lda #$0A ; 8DD3 A9 0A | ||
+ | sta L93_REPEAT_TIMER ; 8DD5 85 93 set the DAS counter to 10. | ||
+ | else ; 8DD7 4C E3 8D If the user started pressing left or right, | ||
+ | lda #$00 ; 8DDA A9 00 | ||
+ | sta L93_REPEAT_TIMER ; 8DDC 85 93 set the DAS counter to 0, | ||
+ | lda #$03 ; 8DDE A9 03 | ||
+ | sta L06F1_ ; 8DE0 8D F1 06 and set $06F1 to 3. | ||
+ | end_if | ||
+ | lda L5C_INPUT_OLD ; 8DE3 A5 5C | ||
+ | and #$01 ; "right" ; 8DE5 29 01 | ||
+ | if_ne ; 8DE7 F0 20 If the user is pressing right, | ||
+ | lda LA5_PILL_DIR ; 8DE9 A5 A5 | ||
+ | and #$01 ; 1 = U/D, 0 = L/R ; 8DEB 29 01 | ||
+ | add #$06 ; 8DED 18 69 06 | ||
+ | cmp L85_PILL_COLUMN ; 8DF0 C5 85 | ||
+ | if_ne ; 8DF2 F0 15 and the pill is not in the rightmost column for its orientation, | ||
+ | inc L85_PILL_COLUMN ; 8DF4 E6 85 move right. | ||
+ | jsr R90D3_COLLISION ; 8DF6 20 D3 90 | ||
+ | if_eq ; 8DF9 D0 08 If that would put us over in-bounds empty space, | ||
+ | lda #$03 ; 8DFB A9 03 | ||
+ | sta L06F1_ ; 8DFD 8D F1 06 set $06F1 to 3. | ||
+ | else ; 8E00 4C 09 8E If that would put us out of bounds or over a non-empty space, | ||
+ | dec L85_PILL_COLUMN ; 8E03 C6 85 move left | ||
+ | lda #$0F ; 8E05 A9 0F | ||
+ | sta L93_REPEAT_TIMER ; 8E07 85 93 and set the DAS counter to 16. | ||
+ | end_if | ||
+ | end_if | ||
+ | end_if | ||
+ | lda L5C_INPUT_OLD ; 8E09 A5 5C | ||
+ | and #$02 ; "left" ; 8E0B 29 02 | ||
+ | if_ne ; 8E0D F0 1B If the user is pressing left, | ||
+ | lda L85_PILL_COLUMN ; 8E0F A5 85 | ||
+ | cmp #$00 ; 8E11 C9 00 | ||
+ | if_ne ; 8E13 F0 15 and we're not in the left-most column, | ||
+ | dec L85_PILL_COLUMN ; 8E15 C6 85 move left. | ||
+ | jsr R90D3_COLLISION ; 8E17 20 D3 90 | ||
+ | if_eq ; 8E1A D0 08 If that would put us over in-bounds empty space, | ||
+ | lda #$03 ; 8E1C A9 03 | ||
+ | sta L06F1_ ; 8E1E 8D F1 06 set $06F1 to 3. | ||
+ | else ; 8E21 4C 2A 8E If that would put us out of bounds or over a non-empty space, | ||
+ | inc L85_PILL_COLUMN ; 8E24 E6 85 move right, | ||
+ | lda #$0F ; 8E26 A9 0F | ||
+ | sta L93_REPEAT_TIMER ; 8E28 85 93 and set the DAS counter to 16. | ||
+ | end_if | ||
+ | end_if | ||
+ | end_if | ||
+ | end | ||
+ | rts ; 8E2A 60 | ||
+ | |||
+ | ; ---------------------------------------------------------------------------- | ||
+ | ; Pill collision detection. | ||
+ | ; Returns A = 0, Z set for no collision. | ||
+ | ; Returns A = FF, Z clear for collision. | ||
+ | R90D3_COLLISION: | ||
+ | .scope | ||
+ | ldx LA6_PILL_SIZE ; 90D3 A6 A6 | ||
+ | dex ; 90D5 CA | ||
+ | dex ; 90D6 CA | ||
+ | lda LA5_PILL_DIR ; 90D7 A5 A5 | ||
+ | and #$01 ; 1 = U/D, 0 = L/R ; 90D9 29 01 | ||
+ | if_eq ; 90DB D0 13 If the current pill is horizontal, | ||
+ | lda LA455,x ; $00,$FF,$FF ; 90DD BD 55 A4 | ||
+ | add L85_PILL_COLUMN ; 90E0 18 65 85 and the pill's column is negative, | ||
+ | bmi collision ; 90E3 30 4C it's a collision. | ||
+ | lda LA458,x ; $F9,$F9,$FA ; 90E5 BD 58 A4 | ||
+ | add L85_PILL_COLUMN ; 90E8 18 65 85 and the pill's column - 7 is nonnegative, | ||
+ | bpl collision ; 90EB 10 44 it's a collision. | ||
+ | else ; 90ED 4C F8 90 If the current pill is vertical, | ||
+ | lda LA45B,x ; $00,$FF,$FF,... ; 90F0 BD 5B A4 | ||
+ | add L86_PILL_ROW ; 90F3 18 65 86 and the pill's row is negative, | ||
+ | bmi collision ; 90F6 30 39 it's a collision. | ||
+ | end_if | ||
+ | lda #$00 ; 90F8 A9 00 | ||
+ | sta L57_ ; 90FA 85 57 $57 = 0 | ||
+ | ldx L86_PILL_ROW ; 90FC A6 86 | ||
+ | lda LA474,x ; 120 - x*8 ; 90FE BD 74 A4 | ||
+ | add L85_PILL_COLUMN ; 9101 18 65 85 | ||
+ | sta L47_TEMP ; 9104 85 47 $47 = pill's current position | ||
+ | lda LA6_PILL_SIZE ; 9106 A5 A6 | ||
+ | sub #$02 ; 9108 38 E9 02 | ||
+ | asl a ; 910B 0A | ||
+ | sta L49_TEMP ; 910C 85 49 $49 = 0 | ||
+ | ; L49_TEMP = LA6_PILL_SIZE - 2 | ||
+ | lda LA5_PILL_DIR ; 910E A5 A5 | ||
+ | and #$01 ; 1 = U/D, 0 = L/R ; 9110 29 01 | ||
+ | add L49_TEMP ; 9112 18 65 49 | ||
+ | asl a ; 9115 0A | ||
+ | asl a ; 9116 0A | ||
+ | tax ; 9117 AA x = 0 for horizontal pill, 4 for vertical pill | ||
+ | ; x = ((LA5_PILL_DIR & #1) + L49_TEMP) << 2 | ||
+ | lda #$04 ; 9118 A9 04 | ||
+ | sta L48_TEMP ; 911A 85 48 for(i=0; i<4; i++) | ||
+ | loop | ||
+ | lda LA4C4,x ; 0,1,0,0, f8,0,0,0 ; 911C BD C4 A4 | ||
+ | add L47_TEMP ; 911F 18 65 47 | ||
+ | tay ; 9122 A8 y = pill's position + { horizontal pill -> 0 or 1; vertical pill -> 0 or -8 } | ||
+ | lda ($57),y ; 9123 B1 57 | ||
+ | cmp #$FF ; 9125 C9 FF If the board at index y is not empty, | ||
+ | bne collision ; 9127 D0 08 it's a collision. | ||
+ | inx ; 9129 E8 | ||
+ | dec L48_TEMP ; 912A C6 48 | ||
+ | while_ne ; 912C D0 EE | ||
+ | lda #$00 ; 912E A9 00 It's not a collision. | ||
+ | rts ; 9130 60 | ||
+ | |||
+ | collision: | ||
+ | lda #$FF ; 9131 A9 FF | ||
+ | rts ; 9133 60 | ||
+ | .endscope | ||
+ | |||
+ | |||
+ | ; -------------------------------------- | ||
+ | ; Increment or decrement LA5_PILL_DIR (mod 4) depending on controller input. | ||
+ | R8E2B_PILL_ROTATE: | ||
+ | lda LA5_PILL_DIR ; 8E2B A5 A5 | ||
+ | sta L4A_TEMP ; 8E2D 85 4A | ||
+ | lda L85_PILL_COLUMN ; 8E2F A5 85 | ||
+ | sta L4B_TEMP ; 8E31 85 4B | ||
+ | lda L5B_INPUT_NEW ; 8E33 A5 5B | ||
+ | and #$80 ; "A" ; 8E35 29 80 | ||
+ | if_ne ; 8E37 F0 10 If the user started pressing A, | ||
+ | lda #$05 ; 8E39 A9 05 | ||
+ | sta L06F1_ ; 8E3B 8D F1 06 set $06f1 to 5, | ||
+ | dec LA5_PILL_DIR ; 8E3E C6 A5 | ||
+ | lda LA5_PILL_DIR ; 8E40 A5 A5 | ||
+ | and #$03 ; 8E42 29 03 | ||
+ | sta LA5_PILL_DIR ; 8E44 85 A5 subtract one (mod 4) from the current rotation count, | ||
+ | jsr R8E60_CHECK_ROTATE ; 8E46 20 60 8E and then roll back the change if there's a problem. | ||
+ | end_if | ||
+ | lda L5B_INPUT_NEW ; 8E49 A5 5B | ||
+ | and #$40 ; "B" ; 8E4B 29 40 If the user started pressing B, | ||
+ | if_ne ; 8E4D F0 10 | ||
+ | lda #$05 ; 8E4F A9 05 | ||
+ | sta L06F1_ ; 8E51 8D F1 06 set $06f1 to 5, | ||
+ | inc LA5_PILL_DIR ; 8E54 E6 A5 | ||
+ | lda LA5_PILL_DIR ; 8E56 A5 A5 | ||
+ | and #$03 ; 8E58 29 03 | ||
+ | sta LA5_PILL_DIR ; 8E5A 85 A5 add one (mod 4) to the current rotation count, | ||
+ | jsr R8E60_CHECK_ROTATE ; 8E5C 20 60 8E and then roll back the change if there's a problem. | ||
+ | end_if | ||
+ | rts ; 8E5F 60 | ||
+ | |||
+ | |||
+ | ; -------------------------------------- | ||
+ | ; Adjust or undo pill rotation if there are collisions. | ||
+ | ; Previous DIR and COLUMN values are passed in L4A_TEMP/L4B_TEMP. | ||
+ | R8E60_CHECK_ROTATE: | ||
+ | begin | ||
+ | lda LA5_PILL_DIR ; 8E60 A5 A5 | ||
+ | and #$01 ; 1 = U/D, 0 = L/R ; 8E62 29 01 | ||
+ | if_eq ; 8E64 D0 19 If the current pill is horizontal, | ||
+ | jsr R90D3_COLLISION ; 8E66 20 D3 90 | ||
+ | if_eq ; 8E69 D0 12 and over in-bounds empty space, | ||
+ | lda L5C_INPUT_OLD ; 8E6B A5 5C | ||
+ | and #$02 ; "left" ; 8E6D 29 02 | ||
+ | beq break ; 8E6F F0 1B and the user is not pressing left, return from this function. | ||
+ | dec L85_PILL_COLUMN ; 8E71 C6 85 Move left. | ||
+ | jsr R90D3_COLLISION ; 8E73 20 D3 90 | ||
+ | beq break ; 8E76 F0 14 If the pill is over in-bounds empty space, return from this function. | ||
+ | inc L85_PILL_COLUMN ; 8E78 E6 85 Move right. | ||
+ | jmp break ; 8E7A 4C 8C 8E Return from this function. | ||
+ | end_if | ||
+ | dec L85_PILL_COLUMN ; 8E7D C6 85 Move left. | ||
+ | end_if | ||
+ | jsr R90D3_COLLISION ; 8E7F 20 D3 90 If the current pill is out of bounds or over non-empty space, | ||
+ | beq break ; 8E82 F0 08 | ||
+ | |||
+ | lda L4A_TEMP ; 8E84 A5 4A | ||
+ | sta LA5_PILL_DIR ; 8E86 85 A5 | ||
+ | lda L4B_TEMP ; 8E88 A5 4B | ||
+ | sta L85_PILL_COLUMN ; 8E8A 85 85 roll back to the position and rotation we were at before we started the rotation. | ||
+ | end | ||
+ | rts ; 8E8C 60 | ||
+ | |||
+ | == External resources == | ||
[https://github.com/brianhuffman/drmario Brian Huffman's disassembly notes] | [https://github.com/brianhuffman/drmario Brian Huffman's disassembly notes] |
Revision as of 01:41, 13 February 2023
Demo
If you sit idle on the title screen for a while, a demo plays. It's triggered by the frame counter rolling over to 00
8 times in a row. The frame counter cycles through from 00
-FF
. So this means it takes anywhere from ~29-34 seconds to start ((256 frames * 7 + [1,256] frames) / 60 frames per second). It can be disabled a number of ways, but the simplest way is editing the jump offset (to 0
) when it decides to load the demo code (at $9905
), accomplishable through the Game Genie code AAAPSP
. Next, while the demo board is a generatable one (seed 03BE
), it's stored directly in the ROM ($CF00
-$CF7F
) and loaded into memory. The capsules generated do not match this seed, but they're stored right after the board ($CF80
-$CFFF
). The controls are stored in pairs of bytes, the first byte being the buttons pressed (bitmask - A, B, Select, Start, Up, Down, Left, Right) and the second being the number of frames to hold down those buttons. These are stored right after the board/capsules at $D000
-$D1FF
.
Pill movement
This is all the code responsible for moving the pill each frame. The disassembly and function descriptions are from Brian Huffman's disassembly; this version has added detailed English descriptions of the code. I also did a cursory look through the code for what $06f1 does, but I couldn't find anything obvious that read from that address. So either it's dead code, or it's read via indirect addressing.
jsr R8D70_DROP_PILL ; may go to state 1 ; 8D66 20 70 8D jsr R8DBF_PILL_MOVE ; 8D69 20 BF 8D jsr R8E2B_PILL_ROTATE ; 8D6C 20 2B 8E ; -------------------------------------- ; Called once, in L8D56. R8D70_DROP_PILL: begin lda L43_CLOCK ; 8D70 A5 43 and #$01 ; 8D72 29 01 beq L8D7E ; 8D74 F0 08 Every other frame, lda L5C_INPUT_OLD ; 8D76 A5 5C and #$0F ; 8D78 29 0F cmp #$04 ; "down" ; 8D7A C9 04 if the user is pressing down but not left, right, or up, beq L8D94 ; 8D7C F0 16 skip ahead to L8D94. L8D7E: inc L92_DROP_TIMER ; 8D7E E6 92 Increase the drop counter. lda L8A_EXTRA_SPEED ; 8D80 A5 8A sta LA0_TEMP ; redundant copy ; 8D82 85 A0 ldx L8B_SPEED ; 8D84 A6 8B lda LA38D,x ; $0F,$19,$1F ; 8D86 BD 8D A3 Load the base speed offset for the player-selected speed (low, med, hi), add LA0_TEMP ; 8D89 18 65 A0 then add in the offset for having played a long time. tax ; 8D8C AA lda LA795,x ; drop timer threshold ; 8D8D BD 95 A7 Load the gravity value. cmp L92_DROP_TIMER ; 8D90 C5 92 If the drop counter is <= the gravity value, bcs break ; 8D92 B0 2A return from this function. L8D94: dec L86_PILL_ROW ; 8D94 C6 86 Move down. lda #$00 ; 8D96 A9 00 sta L92_DROP_TIMER ; 8D98 85 92 Reset the drop counter to 0. jsr R90D3_COLLISION ; 8D9A 20 D3 90 if_eq ; 8D9D D0 06 If we are over in-bounds empty space, lda L86_PILL_ROW ; 8D9F A5 86 cmp #$FF ; 8DA1 C9 FF and not below the board, bne break ; 8DA3 D0 19 return from this function. end_if inc L86_PILL_ROW ; 8DA5 E6 86 Move up. lda #$07 ; 8DA7 A9 07 sta L06F1_ ; 8DA9 8D F1 06 Set $06F1 to 7. jsr R8F52_PLACE_PILL ; 8DAC 20 52 8F Lock the pill in place. ; make the program crash if L0740_CHECKSUM_FAIL is nonzero lda L0740_CHECKSUM_FAIL ; 8DAF AD 40 07 if_ne ; 8DB2 F0 05 lda L53_SPRITE_NUM ; 8DB4 A5 53 pha ; 8DB6 48 pha ; 8DB7 48 pha ; 8DB8 48 end_if inc L97_GAME_STATE ; 8DB9 E6 97 jmp break ; 8DBB 4C BE 8D end rts ; 8DBE 60 ; -------------------------------------- ; Increment or decrement pill column based on controller input. ; Repeat every 6 frames after holding for 16. R8DBF_PILL_MOVE: begin lda L5B_INPUT_NEW ; 8DBF A5 5B and #$03 ; left or right ; 8DC1 29 03 if_eq ; 8DC3 D0 15 If the user did not start pressing left or right, lda L5C_INPUT_OLD ; 8DC5 A5 5C and #$03 ; left or right ; 8DC7 29 03 beq break ; 8DC9 F0 5F and neither left nor right is currently pressed, return from this function. Otherwise, inc L93_REPEAT_TIMER ; 8DCB E6 93 increase the DAS counter. lda L93_REPEAT_TIMER ; 8DCD A5 93 cmp #$10 ; 8DCF C9 10 bmi break ; 8DD1 30 57 If the DAS counter is under 16, return from this function. Otherwise, lda #$0A ; 8DD3 A9 0A sta L93_REPEAT_TIMER ; 8DD5 85 93 set the DAS counter to 10. else ; 8DD7 4C E3 8D If the user started pressing left or right, lda #$00 ; 8DDA A9 00 sta L93_REPEAT_TIMER ; 8DDC 85 93 set the DAS counter to 0, lda #$03 ; 8DDE A9 03 sta L06F1_ ; 8DE0 8D F1 06 and set $06F1 to 3. end_if lda L5C_INPUT_OLD ; 8DE3 A5 5C and #$01 ; "right" ; 8DE5 29 01 if_ne ; 8DE7 F0 20 If the user is pressing right, lda LA5_PILL_DIR ; 8DE9 A5 A5 and #$01 ; 1 = U/D, 0 = L/R ; 8DEB 29 01 add #$06 ; 8DED 18 69 06 cmp L85_PILL_COLUMN ; 8DF0 C5 85 if_ne ; 8DF2 F0 15 and the pill is not in the rightmost column for its orientation, inc L85_PILL_COLUMN ; 8DF4 E6 85 move right. jsr R90D3_COLLISION ; 8DF6 20 D3 90 if_eq ; 8DF9 D0 08 If that would put us over in-bounds empty space, lda #$03 ; 8DFB A9 03 sta L06F1_ ; 8DFD 8D F1 06 set $06F1 to 3. else ; 8E00 4C 09 8E If that would put us out of bounds or over a non-empty space, dec L85_PILL_COLUMN ; 8E03 C6 85 move left lda #$0F ; 8E05 A9 0F sta L93_REPEAT_TIMER ; 8E07 85 93 and set the DAS counter to 16. end_if end_if end_if lda L5C_INPUT_OLD ; 8E09 A5 5C and #$02 ; "left" ; 8E0B 29 02 if_ne ; 8E0D F0 1B If the user is pressing left, lda L85_PILL_COLUMN ; 8E0F A5 85 cmp #$00 ; 8E11 C9 00 if_ne ; 8E13 F0 15 and we're not in the left-most column, dec L85_PILL_COLUMN ; 8E15 C6 85 move left. jsr R90D3_COLLISION ; 8E17 20 D3 90 if_eq ; 8E1A D0 08 If that would put us over in-bounds empty space, lda #$03 ; 8E1C A9 03 sta L06F1_ ; 8E1E 8D F1 06 set $06F1 to 3. else ; 8E21 4C 2A 8E If that would put us out of bounds or over a non-empty space, inc L85_PILL_COLUMN ; 8E24 E6 85 move right, lda #$0F ; 8E26 A9 0F sta L93_REPEAT_TIMER ; 8E28 85 93 and set the DAS counter to 16. end_if end_if end_if end rts ; 8E2A 60 ; ---------------------------------------------------------------------------- ; Pill collision detection. ; Returns A = 0, Z set for no collision. ; Returns A = FF, Z clear for collision. R90D3_COLLISION: .scope ldx LA6_PILL_SIZE ; 90D3 A6 A6 dex ; 90D5 CA dex ; 90D6 CA lda LA5_PILL_DIR ; 90D7 A5 A5 and #$01 ; 1 = U/D, 0 = L/R ; 90D9 29 01 if_eq ; 90DB D0 13 If the current pill is horizontal, lda LA455,x ; $00,$FF,$FF ; 90DD BD 55 A4 add L85_PILL_COLUMN ; 90E0 18 65 85 and the pill's column is negative, bmi collision ; 90E3 30 4C it's a collision. lda LA458,x ; $F9,$F9,$FA ; 90E5 BD 58 A4 add L85_PILL_COLUMN ; 90E8 18 65 85 and the pill's column - 7 is nonnegative, bpl collision ; 90EB 10 44 it's a collision. else ; 90ED 4C F8 90 If the current pill is vertical, lda LA45B,x ; $00,$FF,$FF,... ; 90F0 BD 5B A4 add L86_PILL_ROW ; 90F3 18 65 86 and the pill's row is negative, bmi collision ; 90F6 30 39 it's a collision. end_if lda #$00 ; 90F8 A9 00 sta L57_ ; 90FA 85 57 $57 = 0 ldx L86_PILL_ROW ; 90FC A6 86 lda LA474,x ; 120 - x*8 ; 90FE BD 74 A4 add L85_PILL_COLUMN ; 9101 18 65 85 sta L47_TEMP ; 9104 85 47 $47 = pill's current position lda LA6_PILL_SIZE ; 9106 A5 A6 sub #$02 ; 9108 38 E9 02 asl a ; 910B 0A sta L49_TEMP ; 910C 85 49 $49 = 0 ; L49_TEMP = LA6_PILL_SIZE - 2 lda LA5_PILL_DIR ; 910E A5 A5 and #$01 ; 1 = U/D, 0 = L/R ; 9110 29 01 add L49_TEMP ; 9112 18 65 49 asl a ; 9115 0A asl a ; 9116 0A tax ; 9117 AA x = 0 for horizontal pill, 4 for vertical pill ; x = ((LA5_PILL_DIR & #1) + L49_TEMP) << 2 lda #$04 ; 9118 A9 04 sta L48_TEMP ; 911A 85 48 for(i=0; i<4; i++) loop lda LA4C4,x ; 0,1,0,0, f8,0,0,0 ; 911C BD C4 A4 add L47_TEMP ; 911F 18 65 47 tay ; 9122 A8 y = pill's position + { horizontal pill -> 0 or 1; vertical pill -> 0 or -8 } lda ($57),y ; 9123 B1 57 cmp #$FF ; 9125 C9 FF If the board at index y is not empty, bne collision ; 9127 D0 08 it's a collision. inx ; 9129 E8 dec L48_TEMP ; 912A C6 48 while_ne ; 912C D0 EE lda #$00 ; 912E A9 00 It's not a collision. rts ; 9130 60 collision: lda #$FF ; 9131 A9 FF rts ; 9133 60 .endscope ; -------------------------------------- ; Increment or decrement LA5_PILL_DIR (mod 4) depending on controller input. R8E2B_PILL_ROTATE: lda LA5_PILL_DIR ; 8E2B A5 A5 sta L4A_TEMP ; 8E2D 85 4A lda L85_PILL_COLUMN ; 8E2F A5 85 sta L4B_TEMP ; 8E31 85 4B lda L5B_INPUT_NEW ; 8E33 A5 5B and #$80 ; "A" ; 8E35 29 80 if_ne ; 8E37 F0 10 If the user started pressing A, lda #$05 ; 8E39 A9 05 sta L06F1_ ; 8E3B 8D F1 06 set $06f1 to 5, dec LA5_PILL_DIR ; 8E3E C6 A5 lda LA5_PILL_DIR ; 8E40 A5 A5 and #$03 ; 8E42 29 03 sta LA5_PILL_DIR ; 8E44 85 A5 subtract one (mod 4) from the current rotation count, jsr R8E60_CHECK_ROTATE ; 8E46 20 60 8E and then roll back the change if there's a problem. end_if lda L5B_INPUT_NEW ; 8E49 A5 5B and #$40 ; "B" ; 8E4B 29 40 If the user started pressing B, if_ne ; 8E4D F0 10 lda #$05 ; 8E4F A9 05 sta L06F1_ ; 8E51 8D F1 06 set $06f1 to 5, inc LA5_PILL_DIR ; 8E54 E6 A5 lda LA5_PILL_DIR ; 8E56 A5 A5 and #$03 ; 8E58 29 03 sta LA5_PILL_DIR ; 8E5A 85 A5 add one (mod 4) to the current rotation count, jsr R8E60_CHECK_ROTATE ; 8E5C 20 60 8E and then roll back the change if there's a problem. end_if rts ; 8E5F 60 ; -------------------------------------- ; Adjust or undo pill rotation if there are collisions. ; Previous DIR and COLUMN values are passed in L4A_TEMP/L4B_TEMP. R8E60_CHECK_ROTATE: begin lda LA5_PILL_DIR ; 8E60 A5 A5 and #$01 ; 1 = U/D, 0 = L/R ; 8E62 29 01 if_eq ; 8E64 D0 19 If the current pill is horizontal, jsr R90D3_COLLISION ; 8E66 20 D3 90 if_eq ; 8E69 D0 12 and over in-bounds empty space, lda L5C_INPUT_OLD ; 8E6B A5 5C and #$02 ; "left" ; 8E6D 29 02 beq break ; 8E6F F0 1B and the user is not pressing left, return from this function. dec L85_PILL_COLUMN ; 8E71 C6 85 Move left. jsr R90D3_COLLISION ; 8E73 20 D3 90 beq break ; 8E76 F0 14 If the pill is over in-bounds empty space, return from this function. inc L85_PILL_COLUMN ; 8E78 E6 85 Move right. jmp break ; 8E7A 4C 8C 8E Return from this function. end_if dec L85_PILL_COLUMN ; 8E7D C6 85 Move left. end_if jsr R90D3_COLLISION ; 8E7F 20 D3 90 If the current pill is out of bounds or over non-empty space, beq break ; 8E82 F0 08 lda L4A_TEMP ; 8E84 A5 4A sta LA5_PILL_DIR ; 8E86 85 A5 lda L4B_TEMP ; 8E88 A5 4B sta L85_PILL_COLUMN ; 8E8A 85 85 roll back to the position and rotation we were at before we started the rotation. end rts ; 8E8C 60