Regions of memory allocated: - 0x0000 - 0x003f for ISRs - 0x0040 - 0x1cff for code - 0x1d00 - 0x1fff for variables expressing game state - 0x1d00 player direction (of last movement), as sprite index - 0x1d01 crate type of current crate (happy or sad), as sprite index - 0x1d02 current level number - 0x1d03 current level has been won - 0x1d20 total size of current level tile data i.e. (cols+1)*rows - 0x1d22 beginning of level data - 0x2000 - 0x23ff for sprites 0x2000 - 0x20cf for the capital letters A - Z 0x20d0 - 0x212f for the decimal digits 0 - 9 0x2130 - 0x____ for game sprites wall #36 or 0x24 crate #37 or 0x25 crate on target #38 or 0x26 target #39 or 0x27 player right #40 or 0x28 player left #41 or 0x29 player down #42 or 0x2a player up #43 or 0x2b nothing #44 or 0x2c - 0x2400 - 0x2fff for the stack - 0x3000 - 0x4fff for video memory - 0x5000 - 0x???? for game data - 0x5000 - 0x5007 is "SOKOBAN" - 0x5008 - 0x500d is "LEVEL" - 0x500e - 0x____ is "PRESS R TO CONTINUE" - 0x5080 - 0x509f is a sequence of 16 pointers to at most 16 game levels - 0x50a0 - 0x???? is all of the sokoban levels Inputs and outputs: - Arrow keys on port 0: - Bit 0: W pressed and was not previously pressed - Bit 1: S ... - Bit 2: A ... - Bit 3: D ... - Reset key on port 1: - Bit 0: R pressed -> restart level - R key (to restart level) - Input port 2: Clock that counts with overflow in a single byte. - Output port 1: - Bit 0: walk/scoot sound - Bit 1: "nope" (invalid move) sound - Bit 2: crate pushing scoot sound - Bit 3: "tada" level passed sound - Bit 7: sounds kill switch, stops playing all sounds List of areas with constant fixed memory pointers to them: - sprites - letters of the alphabet - wall tile - player sprite - crate sprite - target sprite - one for each level - mem address of current level - x- and y-coordinate of player Format of a level: - 1 byte telling the width (num cols) - 1 byte telling the height (num rows) - 1 byte saying which column to start printing it in - 1 byte saying which row to start printing it in - All of the data - Before each row, 1 byte saying how many cells in that row (redundant) - 1 byte per cell - Each byte is the index of the sprite for that cell - 2 bytes saying the position of the player (col, row) - 1 byte saying the number of crates - 2 bytes per crate, saying the position (col, row) of each - At least one null byte MORE ABOUT LEVELS: - maximum size of a level is 20x20 - maximum number of crates in a level is 16 Subroutines needed: - Get value of cell at given x,y position of current puzzle - Print message on screen starting at given location - Draw sprite number X at given cell on screen - Write a whole sequence of sprites by their indices? - Draw current state of the current level QUESTION: How to compute the centering of the Sokoban puzzle on the screen? Maybe the way it is displayed should be hardcoded as part of the level itself? QUESTION: Should I bother animating? Maybe it won't be so bad because at most a player + box will move? --- --- --- ROUTINE DRAWTILE 0x0060 Calling convention for drawTile: - registers HL must contain address of sprite - registers B,C must contain coords on 32x32 screen to draw the 8x8 tile - does it need to preserve any registers and/or flags? Algorithm: 1. somehow set DE to be 0x3000 + B + 256*C, as a u16. A <- C 0x79 A <- A + 0x30 0xc6 0x30 D <- A 0x57 E <- B 0x58 2. set A = 8. A <- 0x8 0x3e 0x08 3. load value of (HL) into B. B <- MEM 0x46 4. set (DE) to value of B. XCHG 0xeb MEM <- B 0x70 XCHG 0xeb 5. set HL = HL+1. INX HL 0x23 6. set DE = DE+32. B <- 0x00 0x06 0x00 C <- 0x20 0x0e 0x20 XCHG 0xeb DAD BC 0x09 XCHG 0xeb 7. decrement A. DCR A 0x3d 8. if A != 0, jump to 3. JNZ __ 0xc2 0x__ 0x__ -> my calculation is that this should be 0x29 + 39 9. return RET 0xc9 Test script: B <- 0x00 0x06 0x00 C <- 0x00 0x0e 0x00 H <- 0x20 0x26 0x20 L <- 0x00 0x2e 0x00 CALL 0x0060 0xcd 0x60 0x00 JMP 0x0000 0xc3 0x00 0x00 --- --- --- --- --- --- ROUTINE DRAWSPRITE 0x03c0 Calling convention: - A must contain the index of the sprite - (B,C) must be the coordinates on the 32x32 screen to start drawing Algorithm: 1. Push HL. PUSH HL 0xe5 1. somehow set HL = 0x2000 + 8*MEM. L <- A 0x6f H <- 0x04 0x26 0x04 HL <- HL + HL 0x29 HL <- HL + HL 0x29 HL <- HL + HL 0x29 5. push BC. PUSH BC 0xc5 6. push DE. PUSH DE 0xd5 7. call drawSprite. CALL drawSprite 0xcd 0x60 0x00 8. pop DE. POP DE 0xd1 9. pop BC. POP BC 0xc1 10. pop HL. POP HL 0xe1 14. return. RET 0xc9 --- --- --- --- --- --- ROUTINE DRAWTILERUN 0x0080 Calling convention for drawTileRun: - HL must contain the address of the sprite ID sequence - Format: 1b length of sequence, followed by L bytes - (B,C) must be the coordinates on the 32x32 screen to start drawing - After calling, HL points to the last element of the sequence - After calling, (B,C) are the coords of the cell AFTER the last cell - E is not affected Algorithm: 1. set D = (HL). D <- (HL) 0x56 2. set HL = HL + 1. INX HL 0x23 3. push HL. PUSH HL 0xe5 4. somehow set HL = 0x2000 + 8*MEM. L <- (HL) 0x6e H <- 0x04 0x26 0x04 HL <- HL + HL 0x29 HL <- HL + HL 0x29 HL <- HL + HL 0x29 5. push BC. PUSH BC 0xc5 6. push DE. PUSH DE 0xd5 7. call drawSprite. CALL drawSprite 0xcd 0x60 0x00 8. pop DE. POP DE 0xd1 9. pop BC. POP BC 0xc1 10. pop HL. POP HL 0xe1 11. set B = B+1 to move to the next column. INR B 0x04 12. set D = D-1 to decrement number of chars left. DCR D 0x15 13. if D != 0, go back to step 2. JNZ ____ 0xc2 0x__ 0x__ 14. return. RET 0xc9 Test code: H <- 0x50 0x26 0x50 L <- 0x00 0x2e 0x00 CALL 0x0080 0xcd 0x80 0x00 JMP 0x0000 0xc3 0x00 0x00 --- --- --- --- --- --- ROUTINE DRAWTILEGRID 0x00a0 Calling convention for drawTileGrid: - HL must contain the pointer to the tile grid / game level - Upon returning, HL points to the last piece of tile data in the level Algorithm: 1. Load the height of the level into D INX HL 0x23 D <- MEM 0x56 2. Load the screen grid position (col, row) into (B, C) INX HL 0x23 B <- MEM 0x46 INX HL 0x23 C <- MEM 0x4e 3. Move HL forward one into the next (first?) row of level INX HL 0x23 4. Push BC and DE PUSH BC 0xc5 PUSH DE 0xd5 5. Call DrawTileRun CALL DrawTileRun 0xcd 0x80 00 6. Pop DE and BC POP DE 0xd1 POP BC 0xc1 7. Increment C INR C 0x0c 8. Decrement D DCR D 0x15 9. If D != 0, jump back to (3). JNZ #3 0xc2 0xa6 0x00 10. Return RET 0xc9 Test code: H <- 0x1d 0x26 0x1d L <- 0x00 0x2e 0x00 CALL 0x00a0 0xcd 0xa0 0x00 JMP self 0xc3 0x07 0x00 --- --- --- --- --- --- ROUTINE CLEARSCREEN --- --- --- --- --- --- ROUTINE DRAWPLAYER 0x0100 Calling convention: - Does not assume anything about register states, uses currently loaded level Algorithm: 1. Load the total size of the level data into HL from 0x1d20. LHLD 0x1d20 0x2a 0x20 0x1d 2. Load the (col, row) start position of the grid into (B, C) from 0x1d24, 0x1d25. LDA 0x1d24 0x3a 0x24 0x1d B <- A 0x47 LDA 0x1d25 0x3a 0x25 0x1d C <- A 0x4f 3. Set HL to HL + 0x1d22 + 0x0004. MVI D 0x1d 0x16 0x1d MVI E 0x22 0x1e 0x26 DAD D 0x19 4. Load the player's X and Y coordinates (col, row) into (D, E) MOV D MEM 0x56 INX HL 0x23 MOV E MEM 0x5e 5. Add D to B, and add E to C. A <- B 0x78 ADD D 0x82 B <- A 0x47 A <- C 0x79 ADD E 0x83 C <- A 0x4f 6. Load the sprite number of the player's sprite from 0x1d00. LDA 0x1d00 0x3a 0x00 0x1d 7. Set HL to the address of the sprite with that index. L <- A 0x6f H <- 0x04 0x26 0x04 HL <- HL + HL 0x29 HL <- HL + HL 0x29 HL <- HL + HL 0x29 8. Call DrawTile. CALL DrawTile 0xcd 0x60 0x00 9. Return. RET 0xc9 --- --- --- --- --- --- ROUTINE GETTILEADDR 0x0130 Calling convention for GetTileAddr: - Assumes a level is loaded - B, C contain the indices (col, row) of the tile to get - HL is set to the address of the tile at the given indices upon return Algorithm: 1. Push BC and DE. PUSH PSW 0xf5 PUSH BC 0xc5 PUSH DE 0xd5 2. Set D to 0x00 and E to cols+1. MVI D 0x00 0x16 0x00 LDA 0x1d22 0x3a 0x22 0x1d INR A 0x3c E <- A 0x5f 3. Set HL to 0x1d26. H <- 0x1d 0x26 0x1d L <- 0x26 0x2e 0x26 4. Add DE to HL, repeating this addition C many times. (*) A <- C 0x79 ANA A 0xa7 JZ (!) 0xca 0x48 0x01 DAD DE (*) 0x19 DCR C 0x0d JMP (*) 0xc3 0x3e 0x01 5. Add B+1 to HL. (!) C <- B 0x48 B <- 0x00 0x06 0x00 DAD BC 0x09 INX HL 0x23 6. Load (HL) into A. A <- MEM 0x7e 7. Pop DE and BC. POP DE 0xd1 POP BC 0xc1 POP PSW 0xf1 8. Return. RET 0xc9 --- --- --- --- --- --- ROUTINE RELATIVIZECOORDS 0x01a0 Calling convention for relativizeCoords: - A level should be loaded - (B, C) should be the relative coordinates to be absolutized - Only messes with register A otherwise Algorithm: LDA 0x1d24 0x3a 0x24 0x1d A <- A + B 0x80 B <- A 0x47 LDA 0x1d25 0x3a 0x25 0x1d A <- A + C 0x81 C <- A 0x4f RET 0xc9 --- --- --- --- --- --- ROUTINE DRAWCRATES 0x0160 Calling convention for drawCrates: - Does not assume anything about register states, uses currently loaded level Algorithm: 1. Load the total size of the level data into HL from 0x1d20. LHLD 0x1d20 0x2a 0x20 0x1d 2. Load the (col, row) start position of the grid into (B, C) from 0x1d24, 0x1d25. LDA 0x1d24 0x3a 0x24 0x1d B <- A 0x47 LDA 0x1d25 0x3a 0x25 0x1d C <- A 0x4f 3. Set HL to HL + 0x1d22 + 0x0007. MVI D 0x1d 0x16 0x1d MVI E 0x22 0x1e 0x29 DAD D 0x19 4. Load up the coords of the next crate and draw it, until finding a null byte. (*) B <- MEM 0x46 INX HL 0x23 C <- MEM 0x4e XCHG 0xeb CALL GetTileAddr 0xcd 0x40 0x01 A <- MEM 0x7e ANI 0xf7 0xe6 0xf7 XRI 0x01 0xee 0x01 L <- A 0x6f H <- 0x04 0x26 0x04 HL <- HL + HL 0x29 HL <- HL + HL 0x29 HL <- HL + HL 0x29 PUSH DE 0xd5 CALL Relativize 0xcd 0xa0 0x01 CALL DrawTile 0xcd 0x60 0x00 POP DE 0xd1 XCHG 0xeb INX HL 0x23 CMP MEM 0xbe JNZ (*) 0xc2 0x70 0x01 6. Return. RET 0xc9 --- --- --- --- --- --- ROUTINE LOADLEVEL 0x00c0 Calling convention for loadLevel: - B should contain the index of the level, between 0 and 15 inclusive Algorithm: 1. Point to the "levels list" data segment H <- 0x50 0x26 0x50 L <- 0x80 0x2e 0x80 2. Jump pointer ahead by 2 * (level index). A <- B 0x78 B <- 0x00 0x06 0x00 RLC 0x07 C <- A 0x4f DAD BC 0x09 3. Go to the address listed in those two bytes. A <- MEM 0x7e INX HL 0x23 L <- MEM 0x6e H <- A 0x67 4. Compute the total size of the level data segment, store it in 2 bytes at 0x1d20. A <- MEM 0x7e INR A 0x3c INX HL 0x23 C <- MEM 0x4e XCHG 0xeb H <- 0x00 0x26 0x00 L <- 0x00 0x2e 0x00 DAD BC (*) 0x09 DCR A 0x3d JNZ (*) 0xc2 0xd7 0x00 (0x17 + 0x00c0 = 0x00d7) SHLD 0x1d20 0x22 0x20 0x1d XCHG 0xeb 5. Copy the whole level one byte at a time to start at 0x1d22, stopping at the null byte. D <- 0x1d 0x16 0x1d E <- 0x21 0x1e 0x21 DCX HL 0x2b DCX HL 0x2b INX DE (*) 0x13 INX HL 0x23 A <- MEM 0x7e STAX DE 0x12 ANA A 0xa7 JNZ (*) 0xc2 0xe6 0x00 (0x26 + 0x00c0 = 0x00e6) 6. Store one null byte after the level, and return. INX DE 0x13 A <- 0x00 0x3e 0x00 STAX DE 0x12 RET 0xc9 Test code: B <- 0x00 0x06 0x00 CALL 0x00c0 0xcd 0xc0 0x00 H <- 0x1d 0x26 0x1d L <- 0x22 0x2e 0x22 CALL 0x00a0 0xcd 0xa0 0x00 JMP SELF 0xc3 0x0c 0x00 --- --- --- --- --- --- ROUTINE GOTOENTITIES 0x01c0 Calling convention: - A level must be loaded - Upon return, HL is set to the beginning of the entities section (player coords) - Does not necessarily preserve any registers Algorithm: 1. Load the total size of the level data into HL from 0x1d20. LHLD 0x1d20 0x2a 0x20 0x1d 2. Set HL to HL + 0x1d22 + 0x0004. MVI D 0x1d 0x16 0x1d MVI E 0x22 0x1e 0x26 DAD D 0x19 3. Return. RET 0xc9 --- --- --- --- --- --- ROUTINE HASCRATE 0x01e0 Calling convention: - (B, C) contain the (col, row) to check for a crate - Upon return, the carry bit will be set iff there is a crate there - Bonus: HL will be pointing to the space right after the offending crate, if one is found Algorithm: 1. Go to the crates section. CALL GoToEntities 0xcd 0xc0 0x01 INX HL 0x23 INX HL 0x23 INX HL 0x23 2. Until finding a null byte, load up the crate coords and compare. (*) D <- MEM 0x56 INX HL 0x23 E <- MEM 0x5e INX HL 0x23 A <- B 0x78 CMP D 0xba JNZ (!) 0xc2 0xf6 0x01 A <- C 0x79 CMP E 0xbb JNZ (!) 0xc2 0xf6 0x01 STC 0x37 RET 0xc9 (!) A <- MEM 0x7e CPI 0x00 0xfe 0x00 JNZ (*) 0xc2 0xe6 0x01 STC 0x37 CMC 0x3f RET 0xc9 --- --- --- --- --- --- ROUTINE DOPLAYERMOVEMENT 0x0200 Calling convention: - (B, C) should contain (dx, dy) of the player movement (+/-1, 0) OR (0, +/-1) ONLY! Algorithm: 1. Load (x,y) of player. CALL GoToEntities 0xcd 0xc0 0x01 D <- MEM 0x56 INX HL 0x23 E <- MEM 0x5e PUSH BC 0xc5 2. Get content of tile at (x+dx,y+dy). A <- D 0x7a ADD B 0x80 B <- A 0x47 A <- E 0x7e ADD C 0x81 C <- A 0x4f CALL GetTileAddr 0xcd 0x40 0x01 A <- MEM 0x7e 3. If it is a wall, cancel everything by jumping to the end. Play a "nope" sound. CMP 0x24 0xfe 0x24 JZ (*) 0xca 0x__ 0x__ 4. Determine if there is a crate there by iterating through crate list, stash pointer. CALL HasCrate 0xcd 0xe0 0x01 JNC (!) 0xd2 0x__ 0x__ 5. If there is a crate, get the content of the tile at (x+2dx,y+2dy). D <- B 0x50 E <- C 0x59 POP BC 0xc1 PUSH BC 0xc5 A <- D 0x7a ADD B 0x80 B <- A 0x47 A <- E 0x7b ADD C 0x81 C <- A 0x4f PUSH HL 0xe5 CALL GetTileAddr 0xcd 0x40 0x01 A <- MEM 0x7e POP HL 0xe1 6. If it's a wall, jump to the end and play the nope sound. CMP 0x24 0xfe 0x24 JZ (*) 0xca 0x__ 0x__ 7. If there is a crate on that tile, jump to the end and play the nope sound. PUSH HL 0xe5 CALL HasCrate 0xcd 0xe0 0x01 POP HL 0xe1 JC (*) 0xda 0x__ 0x__ 8. Set this crate's position to (x+2dx,y+2dy). DCX HL 0x2b MEM <- C 0x71 DCX HL 0x2b MEM <- B 0x70 A <- 0x04 0x3e 0x04 OUT 1 0xd3 0x01 9. If we've made it this far, set the player's position to (x+dx,y+dy). (!) CALL GoToEntities 0xcd 0xc0 0x01 AT 0x0241 POP BC 0xc1 PUSH BC 0xc5 A <- MEM 0x7e ADD B 0x80 MEM <- A 0x77 INX HL 0x23 A <- MEM 0x7e ADD C 0x81 MEM <- A 0x77 10. End of function, return. POP BC 0xc1 RET 0xc9 11. End of function failed, play nope sound and return. (*) A <- 0x02 0x3e 0x02 AT 0x0250 OUT 1 0xd3 0x01 POP BC 0xc1 RET 0xc9 --- --- --- --- --- --- ROUTINE HANDLEMOVEMENT 0x0260 Calling convention: - No assumptions about registers, but level should be loaded Algorithm: 1. Get input key map LDA 0x00 0x1d 0x3a 0x00 0x1d D <- A 0x57 IN 0 0xdb 0x00 2. If the W bit is set, move the player up. B <- 0x00 0x06 0x00 C <- 0xff 0x0e 0xff D <- 0x2b 0x16 0x2b RAR 0x1f JC (*) 0xda 0x__ 0x__ 3. If the S bit is set, move the player down. C <- 0x01 0x0e 0x01 RAR 0x1f D <- 0x2a 0x16 0x2a JC (*) 0xda 0x__ 0x__ 4. If the A bit is set, move the player left. B <- 0xff 0x06 0xff C <- 0x00 0x0e 0x00 D <- 0x29 0x16 0x29 RAR 0x1f JC (*) 0xda 0x__ 0x__ 5. If the D bit is set, move the player right. B <- 0x01 0x06 0x01 D <- 0x28 0x16 0x28 RAR 0x1f JC (*) 0xda 0x__ 0x__ 6. Return. RET 0xc9 (*) A <- D 0x7a STA 0x00 0x1d 0x32 0x00 0x1d A <- 0x1 0x3e 0x01 OUT 1 0xd3 0x01 CALL DoPlayerMovement 0xcd 0x00 0x02 AT 0x028c RET 0xc9 Sample test code: H <- 0x1d 0x26 0x1d L <- 0x22 0x2e 0x22 CALL DrawTileGrid 0xcd 0xa0 0x00 CALL DrawPlayer 0xcd 0x00 0x01 CALL DrawCrates 0xcd 0x60 0x01 CALL HandleMovement 0xcd 0x60 0x02 JMP beginning 0xc3 0x00 0x00 --- --- --- --- --- --- ROUTINE CHECKWINCONDITION 0x02a0 Calling convention: - A level must be loaded. - Sets the "level passed" flag at 0x1d03 iff the player has won. - Zero flag is set upon return iff the level was just switched from non-won to won. Algorithm: 1. Skip to crates section. CALL GoToEntities 0xcd 0xc0 0x01 INX HL 0x23 INX HL 0x23 INX HL 0x23 2. For each crate, check that the corresponding tile is a target (0x27). (*) B <- MEM 0x46 AT 0x02b0 INX HL 0x23 C <- MEM 0x4e INX HL 0x23 XCHG 0xeb CALL GetTileAddr 0xcd 0x30 0x01 A <- MEM 0x7e CMP A 0x27 0xfe 0x27 RNZ 0xc0 XCHG 0xeb A <- MEM 0x7e ANA A 0xa7 JNZ (*) 0xc2 0xb0 0x02 LDA 0x1d03 0x3a 0x03 0x1d ANA A 0xa7 RNZ 0xc0 A <- 0xff 0x3e 0xff STA 0x1d03 0x32 0x03 0x1d A <- 0x8 0x3e 0x08 OUT 1 0xd3 0x01 RET 0xc9 --- --- --- 0603CDC0 00C30500 CD600226 1D2E22CD A000CD00 01CD6001 CDA002C9 --- --- --- ROUTINE PLAYLEVELLOOP 0x02e0 ALLOTTED UNTIL 0x0360 Calling convention: - Level must be loaded Algorithm: 1. Check the win flag. If zero, proceed to check for reset. Else increment level number. LDA 0x1d03 0x3a 0x03 0x1d CPI 0xff 0xfe 0xff 2. If R is pressed, cancel all sounds and proceed to reload the current level, else return. IN 1 0xdb 0x01 RRC 0x0f JNC (4) 0xd2 0x10 0x03 A <- 0x80 0x3e 0x80 OUT 1 0xd3 0x01 A <- 0x00 0x3e 0x00 STA 0x1d03 0x32 0x03 0x1d 3. Load the level corresponding to the level number in 0x1d02, clear screen. LDA 0x1d02 0x3a 0x02 0x1d AT 0x0300 B <- A 0x47 CALL LoadLevel 0xcd 0xc0 0x00 CALL ClearScreen 0xcd 0x60 0x03 RET 0xc9 4. If the level has not currently been won, process keyboard input. AT 0x0310 LDA 0x1d03 0x3a 0x03 0x1d CPI 0x00 0xfe 0x00 CZ HandleMovement 0xcc 0x60 0x02 5. Draw the grid of tiles from 0x1d22. H <- 0x1d 0x26 0x1d L <- 0x22 0x2e 0x22 CALL DrawTileGrid 0xcd 0xa0 0x00 CALL PrintLevelText 0xcd 0x80 0x03 6. Draw the player. CALL DrawPlayer 0xcd 0x00 0x01 7. Draw the crates. CALL DrawCrates 0xcd 0x60 0x01 8. Check the win condition and update level number accordingly. CALL CheckWinCondition 0xcd 0xa0 0x02 RNZ 0xc0 LDA 0x1d02 0x3a 0x02 0x1d INR A 0x3c ANI 0x0f 0xe6 0x0f STA 0x1d02 0x32 0x02 0x1d 9. Return. RET 0xc9 --- --- --- --- --- --- ROUTINE CLEARSCREEN 0x0360 Calling convention: - None, may be called anywhere, but may affect registers Algorithm: H <- 0x30 0x26 0x30 L <- 0x00 0x2e 0x00 MEM <- 0x00 0x36 0x00 INX HL 0x23 A <- H 0x7c CPI 0x50 0xfe 0x50 RZ 0xc8 JMP (back to MEM step) 0xc3 64 03 --- --- --- --- --- --- ROUTINE PRINTLEVELTEXT 0x0380 Calling convention: - None Algorithm: H <- 0x50 0x26 0x50 L <- 0x00 0x2e 0x00 B <- 0x01 0x06 0x01 C <- 0x01 0x0e 0x01 CALL DrawTileRun 0xcd 0x80 0x00 B <- 0x01 0x06 0x01 C <- 0x02 0x0e 0x02 H <- 0x50 0x26 0x50 L <- 0x08 0x2e 0x08 CALL DrawTileRun 0xcd 0x80 0x00 LDA 0x1d02 0x3a 0x02 0x1d DAA 0x27 E <- A 0x5f RRC 0x0f RRC 0x0f RRC 0x0f RRC 0x0f INR B 0x04 ANI 0x0f 0xe6 0x0f ADI 0x1a 0xc6 0x1a CALL DrawSprite 0xcd 0xc0 0x03 A <- E 0x7b ANI 0x0f 0xe6 0x0f ADI 0x1a 0xc6 0x1a INR B 0x04 CALL DrawSprite 0xcd 0xc0 0x03 LDA 0x1d03 0x3a 0x03 0x1d ANA A 0xa7 RZ 0xc8 H <- 0x50 0x26 50 L <- 0x0e 0x2e 0e B <- 0x01 0x06 0x01 CALL DrawTileRun 0xcd 80 00 RET 0xc9 --- --- ---