├── gamegenie.cdl.gz ├── screenshot-ntsc.png ├── screenshot-sprites.png ├── hashes.md5 ├── screenshot-pal,no_scroll,no_sprites,grid.png ├── assemble.sh ├── genie.asm ├── cdl-summary-raw.csv ├── README.md └── prg.asm /gamegenie.cdl.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qalle2/nes-gg-disassembly/HEAD/gamegenie.cdl.gz -------------------------------------------------------------------------------- /screenshot-ntsc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qalle2/nes-gg-disassembly/HEAD/screenshot-ntsc.png -------------------------------------------------------------------------------- /screenshot-sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qalle2/nes-gg-disassembly/HEAD/screenshot-sprites.png -------------------------------------------------------------------------------- /hashes.md5: -------------------------------------------------------------------------------- 1 | 0a0b0b2ed4f45699a0d27cd6ddb4d906 *chr.bin 2 | e354fb5b20e1b9fe4e5ca330f9b3391a *genie.nes 3 | 8d699c97d164d406c2912aece164cd32 *prg.bin 4 | -------------------------------------------------------------------------------- /screenshot-pal,no_scroll,no_sprites,grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qalle2/nes-gg-disassembly/HEAD/screenshot-pal,no_scroll,no_sprites,grid.png -------------------------------------------------------------------------------- /assemble.sh: -------------------------------------------------------------------------------- 1 | # Warning: this script deletes files. Run at your own risk. 2 | rm -f prg.bin genie.nes 3 | asm6 prg.asm prg.bin 4 | asm6 genie.asm genie.nes 5 | md5sum -c --quiet hashes.md5 6 | -------------------------------------------------------------------------------- /genie.asm: -------------------------------------------------------------------------------- 1 | ; NES Game Genie disassembly (ASM6) 2 | 3 | ; iNES header; see https://www.nesdev.org/wiki/INES 4 | base $0000 5 | db "NES", $1a ; file id 6 | db 1, 1 ; 16 KiB PRG ROM, 8 KiB CHR ROM 7 | db %00000000, %00000000 ; mapper 0 (NROM), horiz. mirroring 8 | pad $0010, $00 ; unused 9 | 10 | ; PRG ROM (4 * 4 KiB) 11 | rept 4 12 | incbin "prg.bin" 13 | endr 14 | 15 | ; CHR ROM (32 * 256 bytes) 16 | rept 32 17 | incbin "chr.bin" 18 | endr 19 | -------------------------------------------------------------------------------- /cdl-summary-raw.csv: -------------------------------------------------------------------------------- 1 | "ROM address","bank","offset in bank","NES address","CDL byte repeat count","CDL byte","CDL byte description" 2 | 0,0,0,49152,13,2,"data" 3 | 13,0,13,49165,12275,0,"unaccessed" 4 | 12288,0,12288,61440,41,9,"code" 5 | 12329,0,12329,61481,1,11,"code, data" 6 | 12330,0,12330,61482,228,9,"code" 7 | 12558,0,12558,61710,1,11,"code, data" 8 | 12559,0,12559,61711,107,9,"code" 9 | 12666,0,12666,61818,1,11,"code, data" 10 | 12667,0,12667,61819,62,9,"code" 11 | 12729,0,12729,61881,1,11,"code, data" 12 | 12730,0,12730,61882,140,9,"code" 13 | 12870,0,12870,62022,2,0,"unaccessed" 14 | 12872,0,12872,62024,19,9,"code" 15 | 12891,0,12891,62043,1,11,"code, data" 16 | 12892,0,12892,62044,76,9,"code" 17 | 12968,0,12968,62120,1,11,"code, data" 18 | 12969,0,12969,62121,16,9,"code" 19 | 12985,0,12985,62137,1,11,"code, data" 20 | 12986,0,12986,62138,119,9,"code" 21 | 13105,0,13105,62257,1,11,"code, data" 22 | 13106,0,13106,62258,67,9,"code" 23 | 13173,0,13173,62325,1,0,"unaccessed" 24 | 13174,0,13174,62326,63,9,"code" 25 | 13237,0,13237,62389,1,11,"code, data" 26 | 13238,0,13238,62390,69,9,"code" 27 | 13307,0,13307,62459,36,10,"data" 28 | 13343,0,13343,62495,1,11,"code, data" 29 | 13344,0,13344,62496,160,9,"code" 30 | 13504,0,13504,62656,1,11,"code, data" 31 | 13505,0,13505,62657,108,9,"code" 32 | 13613,0,13613,62765,1,11,"code, data" 33 | 13614,0,13614,62766,47,9,"code" 34 | 13661,0,13661,62813,1,11,"code, data" 35 | 13662,0,13662,62814,7,9,"code" 36 | 13669,0,13669,62821,1,11,"code, data" 37 | 13670,0,13670,62822,73,9,"code" 38 | 13743,0,13743,62895,1,11,"code, data" 39 | 13744,0,13744,62896,240,9,"code" 40 | 13984,0,13984,63136,1,11,"code, data" 41 | 13985,0,13985,63137,53,9,"code" 42 | 14038,0,14038,63190,1,11,"code, data" 43 | 14039,0,14039,63191,36,9,"code" 44 | 14075,0,14075,63227,1,11,"code, data" 45 | 14076,0,14076,63228,3,9,"code" 46 | 14079,0,14079,63231,128,42,"data (indirectly accessed)" 47 | 14207,0,14207,63359,101,9,"code" 48 | 14308,0,14308,63460,1,11,"code, data" 49 | 14309,0,14309,63461,7,9,"code" 50 | 14316,0,14316,63468,1,11,"code, data" 51 | 14317,0,14317,63469,86,9,"code" 52 | 14403,0,14403,63555,20,10,"data" 53 | 14423,0,14423,63575,1,11,"code, data" 54 | 14424,0,14424,63576,13,9,"code" 55 | 14437,0,14437,63589,1,11,"code, data" 56 | 14438,0,14438,63590,21,9,"code" 57 | 14459,0,14459,63611,1,11,"code, data" 58 | 14460,0,14460,63612,41,9,"code" 59 | 14501,0,14501,63653,1,11,"code, data" 60 | 14502,0,14502,63654,49,9,"code" 61 | 14551,0,14551,63703,1,11,"code, data" 62 | 14552,0,14552,63704,49,9,"code" 63 | 14601,0,14601,63753,1,11,"code, data" 64 | 14602,0,14602,63754,81,9,"code" 65 | 14683,0,14683,63835,1,11,"code, data" 66 | 14684,0,14684,63836,22,9,"code" 67 | 14706,0,14706,63858,1,11,"code, data" 68 | 14707,0,14707,63859,5,9,"code" 69 | 14712,0,14712,63864,1,11,"code, data" 70 | 14713,0,14713,63865,148,9,"code" 71 | 14861,0,14861,64013,16,10,"data" 72 | 14877,0,14877,64029,1,0,"unaccessed" 73 | 14878,0,14878,64030,16,10,"data" 74 | 14894,0,14894,64046,1,0,"unaccessed" 75 | 14895,0,14895,64047,54,9,"code" 76 | 14949,0,14949,64101,1,11,"code, data" 77 | 14950,0,14950,64102,34,9,"code" 78 | 14984,0,14984,64136,32,10,"data" 79 | 15016,0,15016,64168,98,9,"code" 80 | 15114,0,15114,64266,1,11,"code, data" 81 | 15115,0,15115,64267,75,9,"code" 82 | 15190,0,15190,64342,1,11,"code, data" 83 | 15191,0,15191,64343,60,9,"code" 84 | 15251,0,15251,64403,1,11,"code, data" 85 | 15252,0,15252,64404,16,9,"code" 86 | 15268,0,15268,64420,1,11,"code, data" 87 | 15269,0,15269,64421,52,9,"code" 88 | 15321,0,15321,64473,1,11,"code, data" 89 | 15322,0,15322,64474,297,9,"code" 90 | 15619,0,15619,64771,8,10,"data" 91 | 15627,0,15627,64779,1,11,"code, data" 92 | 15628,0,15628,64780,33,9,"code" 93 | 15661,0,15661,64813,16,10,"data" 94 | 15677,0,15677,64829,140,9,"code" 95 | 15817,0,15817,64969,1,11,"code, data" 96 | 15818,0,15818,64970,54,9,"code" 97 | 15872,0,15872,65024,1,11,"code, data" 98 | 15873,0,15873,65025,70,9,"code" 99 | 15943,0,15943,65095,4,0,"unaccessed" 100 | 15947,0,15947,65099,37,9,"code" 101 | 15984,0,15984,65136,1,11,"code, data" 102 | 15985,0,15985,65137,13,9,"code" 103 | 15998,0,15998,65150,2,0,"unaccessed" 104 | 16000,0,16000,65152,9,9,"code" 105 | 16009,0,16009,65161,2,0,"unaccessed" 106 | 16011,0,16011,65163,20,9,"code" 107 | 16031,0,16031,65183,31,10,"data" 108 | 16062,0,16062,65214,2,0,"unaccessed" 109 | 16064,0,16064,65216,40,42,"data (indirectly accessed)" 110 | 16104,0,16104,65256,6,0,"unaccessed" 111 | 16110,0,16110,65262,248,42,"data (indirectly accessed)" 112 | 16358,0,16358,65510,10,0,"unaccessed" 113 | 16368,0,16368,65520,2,10,"data" 114 | 16370,0,16370,65522,8,0,"unaccessed" 115 | 16378,0,16378,65530,4,10,"data" 116 | 16382,0,16382,65534,2,0,"unaccessed" 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nes-gg-disassembly 2 | *Note: This project has been moved to [Codeberg](https://codeberg.org/qalle/nes-gamegenie-disassembly). The version here on GitHub will no longer be updated.* 3 | 4 | A disassembly of the ROM of the [NES](https://en.wikipedia.org/wiki/Nintendo_Entertainment_System) cheat cartridge [*Game Genie*](https://en.wikipedia.org/wiki/Game_Genie). Assembles with [ASM6](https://www.romhacking.net/utilities/674/). 5 | 6 | Table of contents: 7 | * [List of files](#list-of-files) 8 | * [How to assemble](#how-to-assemble) 9 | * [Screenshots](#screenshots) 10 | * [Parts of the Game Genie screen](#parts-of-the-game-genie-screen) 11 | * [Bugs](#bugs) 12 | * [FCEUX Code/Data log](#fceux-codedata-log) 13 | * [Speculation](#speculation) 14 | * [References](#references) 15 | * [Programs used](#programs-used) 16 | 17 | ## List of files 18 | * `assemble.sh`: a Linux script that assembles the program (warning: deletes files; see also "How to assemble" below) 19 | * `cdl-summary-raw.csv`: see "FCEUX Code/Data log" below 20 | * `gamegenie.cdl.gz`: see "FCEUX Code/Data log" below 21 | * `genie.asm`: source code (ASM6) – high-level structure 22 | * `prg.asm`: source code (ASM6) – PRG ROM 23 | * `screenshot-*.png`: screenshots 24 | 25 | ## How to assemble 26 | * Download the original Game Genie ROM from e.g. [NESDev](https://archive.nesdev.org). 27 | * Copy the CHR ROM data (the last 256 bytes) to a new file, `chr.bin`. (Use e.g. a hex editor.) 28 | * Assemble: 29 | * `asm6 prg.asm prg.bin` 30 | * `asm6 genie.asm genie.nes` 31 | * `prg.bin` is no longer needed. 32 | * Make sure `genie.nes` is identical to the original Game Genie ROM file. 33 | 34 | ## Screenshots 35 | 36 | ### Normal screenshot 37 | * NTSC mode (256×224 pixels) 38 | 39 | ![normal](screenshot-ntsc.png) 40 | 41 | ### Attribute Table byte boundaries 42 | * PAL mode (256×240 pixels) 43 | * background scrolling disabled (by hacking the ROM) 44 | * sprites hidden (using emulator settings) 45 | * a red 32×32-pixel grid and text added (photoshopped) 46 | 47 | ![attribute byte boundaries](screenshot-pal,no_scroll,no_sprites,grid.png) 48 | 49 | ### Sprites 50 | * NTSC mode (256×224 pixels) 51 | * background hidden (using emulator settings) 52 | * the hand cursor, the revolving cursor, a flying letter (`L`) and flying particles can be seen 53 | 54 | ![sprites](screenshot-sprites.png) 55 | 56 | ## Parts of the Game Genie screen 57 | * *flying letter*: a letter that flies from the virtual keyboard to the input area when a letter is entered 58 | * *hand cursor*: looks like a hand, moved with arrow keys, on the input area or on the virtual keyboard 59 | * *input area*: where the entered codes appear; initially filled with dashes (`-`) 60 | * *particles*: small squares that fly when a letter is deleted 61 | * *revolving cursor*: revolves around a letter or dash (`-`) on the input area; looks like a plus sign (`+`) 62 | * *virtual keyboard*: the letters `AEPOZXLU GKISTVYN` 63 | 64 | ## Bugs 65 | * Sometimes, the letter or dash (`-`) under the revolving cursor on the input area does not flash. 66 | * How to reproduce: 67 | * boot up the ROM 68 | * observe the first dash (it does not flash) 69 | * press B 70 | * observe the first dash (it flashes) 71 | * You can sometimes delete non-final letters of the code. 72 | * How to reproduce: 73 | * enter `AAAAAAAA` on the last line (you need to move the cursor manually from the 6th to the 7th letter) 74 | * press B three times 75 | * observe the code (`AAAAA-AA`) 76 | * The bottom half of the flying letter sometimes looks corrupt and flashing. 77 | * How to reproduce (use pause&frame advance on an emulator for best results): 78 | * enter any two letters (e.g. `UU`) 79 | * immediately delete the last letter (press B) 80 | * immediately delete another letter (press B) 81 | * observe the bottom half of the flying letter (it is corrupt and flashes) 82 | * A variation of the previous bug: sometimes, the bottom half of the flying letter flashes and some flying particles disappear. 83 | * How to reproduce (use pause&frame advance on an emulator for best results): 84 | * enter two letters or more (e.g. `UUU`) 85 | * delete the last letter (press B) 86 | * immediately delete another letter (press B) 87 | * immediately enter another letter (e.g. `U`) 88 | * observe the bottom half of the flying letter (it flashes) and note the missing particles 89 | 90 | The sprite-related bugs seem to be caused by some sprites being assigned to both the flying letter and the flying particles. 91 | 92 | ## FCEUX Code/Data log 93 | I created a code/data log file (`.cdl`) of the ROM using the Code/Data Logger in FCEUX. 94 | The file is in `gamegenie.cdl.gz` (gz compressed). 95 | 96 | I also converted the CDL file into a human-readable format using my [cdl-summary](https://github.com/qalle2/cdl-summary) with the arguments `--prg-size 16` and `--bank-size 16`. 97 | The unedited result is in `cdl-summary-raw.csv`. 98 | 99 | Below is the manually-edited data. 100 | The columns are: CPU address range (hexadecimal), length (decimal), description. 101 | 102 | ``` 103 | f000-f028 ( 41): code 104 | f029-f029 ( 1): code, data 105 | f02a-f10d (228): code 106 | f10e-f10e ( 1): code, data 107 | f10f-f179 (107): code 108 | f17a-f17a ( 1): code, data 109 | f17b-f1b8 ( 62): code 110 | f1b9-f1b9 ( 1): code, data 111 | f1ba-f245 (140): code 112 | f246-f247 ( 2): unaccessed 113 | f248-f25a ( 19): code 114 | f25b-f25b ( 1): code, data 115 | f25c-f2a7 ( 76): code 116 | f2a8-f2a8 ( 1): code, data 117 | f2a9-f2b8 ( 16): code 118 | f2b9-f2b9 ( 1): code, data 119 | f2ba-f330 (119): code 120 | f331-f331 ( 1): code, data 121 | f332-f374 ( 67): code 122 | f375-f375 ( 1): unaccessed 123 | f376-f3b4 ( 63): code 124 | f3b5-f3b5 ( 1): code, data 125 | f3b6-f3fa ( 69): code 126 | f3fb-f41e ( 36): data 127 | f41f-f41f ( 1): code, data 128 | f420-f4bf (160): code 129 | f4c0-f4c0 ( 1): code, data 130 | f4c1-f52c (108): code 131 | f52d-f52d ( 1): code, data 132 | f52e-f55c ( 47): code 133 | f55d-f55d ( 1): code, data 134 | f55e-f564 ( 7): code 135 | f565-f565 ( 1): code, data 136 | f566-f5ae ( 73): code 137 | f5af-f5af ( 1): code, data 138 | f5b0-f69f (240): code 139 | f6a0-f6a0 ( 1): code, data 140 | f6a1-f6d5 ( 53): code 141 | f6d6-f6d6 ( 1): code, data 142 | f6d7-f6fa ( 36): code 143 | f6fb-f6fb ( 1): code, data 144 | f6fc-f6fe ( 3): code 145 | f6ff-f77e (128): data (indirectly accessed) 146 | f77f-f7e3 (101): code 147 | f7e4-f7e4 ( 1): code, data 148 | f7e5-f7eb ( 7): code 149 | f7ec-f7ec ( 1): code, data 150 | f7ed-f842 ( 86): code 151 | f843-f856 ( 20): data 152 | f857-f857 ( 1): code, data 153 | f858-f864 ( 13): code 154 | f865-f865 ( 1): code, data 155 | f866-f87a ( 21): code 156 | f87b-f87b ( 1): code, data 157 | f87c-f8a4 ( 41): code 158 | f8a5-f8a5 ( 1): code, data 159 | f8a6-f8d6 ( 49): code 160 | f8d7-f8d7 ( 1): code, data 161 | f8d8-f908 ( 49): code 162 | f909-f909 ( 1): code, data 163 | f90a-f95a ( 81): code 164 | f95b-f95b ( 1): code, data 165 | f95c-f971 ( 22): code 166 | f972-f972 ( 1): code, data 167 | f973-f977 ( 5): code 168 | f978-f978 ( 1): code, data 169 | f979-fa0c (148): code 170 | fa0d-fa1c ( 16): data 171 | fa1d-fa1d ( 1): unaccessed 172 | fa1e-fa2d ( 16): data 173 | fa2e-fa2e ( 1): unaccessed 174 | fa2f-fa64 ( 54): code 175 | fa65-fa65 ( 1): code, data 176 | fa66-fa87 ( 34): code 177 | fa88-faa7 ( 32): data 178 | faa8-fb09 ( 98): code 179 | fb0a-fb0a ( 1): code, data 180 | fb0b-fb55 ( 75): code 181 | fb56-fb56 ( 1): code, data 182 | fb57-fb92 ( 60): code 183 | fb93-fb93 ( 1): code, data 184 | fb94-fba3 ( 16): code 185 | fba4-fba4 ( 1): code, data 186 | fba5-fbd8 ( 52): code 187 | fbd9-fbd9 ( 1): code, data 188 | fbda-fd02 (297): code 189 | fd03-fd0a ( 8): data 190 | fd0b-fd0b ( 1): code, data 191 | fd0c-fd2c ( 33): code 192 | fd2d-fd3c ( 16): data 193 | fd3d-fdc8 (140): code 194 | fdc9-fdc9 ( 1): code, data 195 | fdca-fdff ( 54): code 196 | fe00-fe00 ( 1): code, data 197 | fe01-fe46 ( 70): code 198 | fe47-fe4a ( 4): unaccessed 199 | fe4b-fe6f ( 37): code 200 | fe70-fe70 ( 1): code, data 201 | fe71-fe7d ( 13): code 202 | fe7e-fe7f ( 2): unaccessed 203 | fe80-fe88 ( 9): code 204 | fe89-fe8a ( 2): unaccessed 205 | fe8b-fe9e ( 20): code 206 | fe9f-febd ( 31): data 207 | febe-febf ( 2): unaccessed 208 | fec0-fee7 ( 40): data (indirectly accessed) 209 | fee8-feed ( 6): unaccessed 210 | feee-ffe5 (248): data (indirectly accessed) 211 | ffe6-ffef ( 10): unaccessed 212 | fff0-fff1 ( 2): data 213 | fff2-fff9 ( 8): unaccessed 214 | fffa-fffd ( 4): data 215 | fffe-ffff ( 2): unaccessed 216 | ``` 217 | 218 | ## Speculation 219 | The Game Genie may have been originally designed to support four codes instead of three: 220 | * At `$fdb0`, `decoded_codes` (`$0090`) is initialized to length 16 instead of 12. 221 | * At `$fe43`, the address of the code that was just decoded is compared to the first three codes, not just two. 222 | * There is space for 32 letters in `entered_letters` (`$066b`). 223 | * Even on an NTSC TV, there would be just enough space vertically for four codes (4 tiles for logo, 8 tiles for virtual keyboard, 16 tiles for the codes). 224 | 225 | However: 226 | * There are not enough bits for four codes in `genie_master_control` (`$8000`). 227 | * There are no unused registers immediately after `genie_master_control`. 228 | * A genie usually grants three wishes, not four. 229 | 230 | ## References 231 | * [NESDev Wiki](https://wiki.nesdev.org) (e.g. [the Game Genie article](https://wiki.nesdev.org/w/index.php/Game_Genie)) 232 | * [NES Game Genie Code Format DOC](https://archive.nesdev.org/nesgg.txt) 233 | 234 | However, I have *not* used earlier Game Genie disassemblies such as [game-genie-disassembly by Kevin Selwyn](https://github.com/kevinselwyn/game-genie-disassembly) (I discovered it just before publishing my disassembly). 235 | 236 | ## Programs used 237 | * [FCEUX](https://fceux.com) (Debugger, Code/Data Logger, etc.) 238 | * [`nes-sprites.lua`](https://forums.nesdev.org/viewtopic.php?f=2&t=13255) for FCEUX by tokumaru 239 | * a quick&dirty disassembler I wrote myself 240 | -------------------------------------------------------------------------------- /prg.asm: -------------------------------------------------------------------------------- 1 | ; NES Game Genie - PRG ROM (ASM6) 2 | ; TODO: some subs starting from check_button_b need tidying up. 3 | 4 | ; --- Constants --------------------------------------------------------------- 5 | 6 | ; Notes: 7 | ; - Each variable takes one byte unless otherwise mentioned. 8 | ; - Signed integers are two's complement unless otherwise noted. 9 | 10 | ; CPU addresses - RAM 11 | 12 | ram_clear_ptr equ $00 ; RAM clear pointer (2 bytes, overlaps) 13 | ppu_ctrl_mirror equ $00 14 | ppu_mask_mirror equ $01 15 | temp1 equ $04 ; a temporary variable, many uses 16 | vram_addr_hi equ $05 ; VRAM address high 17 | joypad1_status equ $07 18 | joypad2_status equ $08 19 | skip_nmi equ $0c 20 | graphic_width equ $0d ; width of graphic in nybbles/tiles 21 | graphic_height equ $0e ; height of graphic in nybbles/tiles 22 | ram_prog_target equ $10 ; RAM program target 23 | graphic_x_offs equ $15 ; graphic X offset 24 | graphic_y_offs equ $16 ; graphic Y offset 25 | graphic_x equ $17 26 | graphic_y equ $18 27 | graphic_nybble equ $19 28 | attr_fill_byte equ $1a ; attribute fill byte 29 | vram_block_x equ $1e 30 | vram_block_y equ $1f 31 | nybble_vram_lo equ $20 ; low 32 | nybble_vram_hi equ $21 ; high 33 | multiply_temp equ $27 ; temporary for multiplication 34 | vram_block_cost equ $28 35 | vram_buf_rd_pos equ $29 ; next read position in VRAM buffer 36 | vram_buf_wr_pos equ $2a ; next write position in VRAM buffer 37 | vram_buf_free equ $2b ; number of free bytes in VRAM buffer 38 | vram_budget equ $2c 39 | metasprite_x equ $2d 40 | always_zero1 equ $2e ; always 0 41 | metasprite_y equ $2f 42 | always_zero2 equ $30 ; always 0 43 | graphic_id equ $31 44 | unused1 equ $33 45 | metaspr_width equ $34 ; metasprite width in nybbles/tiles 46 | metaspr_height equ $35 ; metasprite height in nybbles/tiles 47 | always_00011100 equ $36 ; always %00011100 48 | grafic_dataleft equ $37 ; graphic data left 49 | metaspr_index equ $38 ; metasprite index 50 | graphics_ptr equ $39 ; graphics pointer (2 bytes) 51 | nybbles_left_x equ $3b 52 | sprite_y equ $3c ; 2 bytes 53 | sprite_x equ $3e ; 2 bytes 54 | nybble_offs equ $40 ; nybble offset 55 | always_one equ $41 ; 1 56 | always_zero3 equ $42 ; 0 57 | odd_frame_flag1 equ $43 58 | scroll_x_mirror equ $45 59 | scroll_y_mirror equ $46 60 | nmi_done equ $49 61 | unused2 equ $4a 62 | first_free_byte equ $4b 63 | keybd_graphic equ $4c ; keyboard graphic 64 | keybd_graphic_x equ $4d ; keyboard graphic X position 65 | keybd_graphic_y equ $4e ; keyboard graphic Y position 66 | hand_metasprite equ $4f ; hand cursor; metasprite number (0) 67 | hand_x_pixel equ $50 ; 14-238 68 | hand_y_pixel equ $51 ; 60-204 69 | hand_x_letr_trg equ $52 ; hand cursor - X target in letters (0-7) 70 | hand_y_letr_trg equ $53 ; hand cursor - Y target in letters (0-4) 71 | revolv_metaspr equ $54 ; revolving cursor - metasprite number (1) 72 | revolv_x equ $55 ; revolving cursor - X pos (10-234) 73 | revolv_y equ $56 ; revolving cursor - Y pos (152-216) 74 | hand_x_spd_ptr equ $57 ; hand cursor - X speed pointer (2 bytes) 75 | hand_y_spd_ptr equ $59 ; hand cursor - Y speed pointer (2 bytes) 76 | hand_y_spd_offs equ $5b ; hand cursor - Y speed offset 77 | hand_x_spd_offs equ $5c ; hand cursor - X speed offset 78 | last_x_inp_acc equ $5d ; last X input accepted (pad_left/pad_right) 79 | last_y_inp_acc equ $5e ; last Y input accepted (pad_up/pad_down) 80 | odd_frame_flag2 equ $5f 81 | hand_x_keyboard equ $60 ; hand cursor - last X pos on virtual keyboard (0-7) 82 | hand_y_keyboard equ $61 ; hand cursor - last Y pos on virtual keyboard (0-1) 83 | hand_x_letr equ $62 ; hand cursor - X position in letters (0-7) 84 | hand_y_letr equ $63 ; hand cursor - Y position in letters (0-4) 85 | revolv_x_letr1 equ $64 ; revolving cursor - X position in letters (0-7) 86 | revolv_y_letr1 equ $65 ; revolving cursor - Y position in letters (0-2) 87 | prev_btn_a_stat equ $66 ; previous status of button A 88 | prev_btn_b_stat equ $67 ; previous status of button B 89 | revolv_x_target equ $68 ; revolving cursor - X target (10-234) 90 | revolv_y_target equ $69 ; revolving cursor - Y target (152-216, step 32) 91 | revolv_trg_spd equ $6a ; revolving cursor - target speed (signed, excess-128) 92 | revolv_spd_x equ $6b ; revolving cursor - X speed (signed) 93 | revolv_spd_y equ $6c ; revolving cursor - Y speed (signed) 94 | revolv_phase equ $6d ; revolving cursor - phase of animation (0-15) 95 | revolv_pos equ $6e ; revolving cursor - X/Y pos (arg for sub) 96 | revolv_target equ $6f ; revolving cursor - X/Y target (arg for sub) 97 | parti_start_x equ $70 ; particles - start X position 98 | parti_start_y equ $71 ; particles - start Y position 99 | parti_set_flag equ $72 ; particles - which set to spawn 100 | parti_set1timer equ $73 ; particles - timer of set 1 (counts up!) 101 | parti_set2timer equ $74 ; particles - timer of set 2 (counts up!) 102 | particle_timer equ $75 ; particles - timer (counts down!) 103 | flying_x equ $76 ; flying letter - X position 104 | flying_y equ $77 ; flying letter - Y position 105 | flying_x_spd equ $78 ; flying letter - X speed (signed, -14...+14) 106 | flying_y_spd equ $79 ; flying letter - Y speed (3-9) 107 | flying_timelft1 equ $7a ; flying letter - time left 1 108 | flying_metaspr equ $7b ; flying letter - metasprite number (2) 109 | flying_timelft2 equ $7c ; flying letter - time left 2 110 | entered_letr equ $7d ; entered letter 111 | rows_left equ $7e ; only in fill_atrblkrows 112 | anim_colr_phase equ $7f ; phase of animated color (0-7) 113 | code_ptr equ $80 ; code pointer (2 bytes, overlaps) 114 | anim_colr_delay equ $80 ; delay of animated color (0-4) 115 | revolv_x_letr2 equ $81 ; revolving cursor - X position in letters (0-7?) 116 | decoded_cod_ptr equ $82 ; pointer to decoded codes (2 bytes, overlaps) 117 | revolv_y_letr2 equ $82 ; revolving cursor - Y position in letters (2-4) 118 | revolv_y_ltr2pr equ $83 ; revolving cursor - previous Y pos in letters (2-4) 119 | temp2 equ $84 ; temporary, many uses 120 | code_length equ $85 121 | code_enab_mask equ $86 ; code enable bitmask 122 | comp_enab_mask equ $87 ; compare value enable bitmask 123 | codes_left equ $88 ; number of codes left to decode 124 | genie_ctrl_val equ $89 ; Game Genie hardware control value 125 | decoded_code equ $8a ; 4 bytes; see "arrays" below 126 | decoded_codes equ $90 ; 16 bytes; see "arrays" below 127 | 128 | sprite_data equ $0200 ; interleaved sprite data; 256 bytes; copied to OAM 129 | vram_buffer equ $0300 ; 256 bytes; several blocks to be copied to VRAM 130 | attr_data_copy equ $0400 ; 64 bytes; copy of attribute table data 131 | vram_block equ $0440 ; 35 bytes; see "arrays" below 132 | sprite_data_atr equ $0463 ; 64 bytes; see "arrays" below 133 | sprite_data_x equ $04a3 ; 64 bytes; see "arrays" below 134 | sprite_data_y equ $04e3 ; 64 bytes; see "arrays" below 135 | sprite_data_til equ $0523 ; 64 bytes; see "arrays" below 136 | metaspr_indexes equ $0563 ; 50 bytes; indexes to metasprites 137 | metasprites equ $0595 ; 46 bytes; see "arrays" below 138 | parti_spds_x equ $060b ; 64 bytes; see "arrays" below 139 | parti_spds_y equ $062b ; 64 bytes; see "arrays" below 140 | entered_letrs equ $066b ; 24 bytes; see "arrays" below 141 | 142 | ; Arrays: 143 | ; 144 | ; decoded_code: 145 | ; current decoded Game Genie code 146 | ; bytes: address high, address low, replace value, compare value 147 | ; decoded_codes: 148 | ; all decoded codes; 12 bytes actually used; bytes for each code: 149 | ; address high, address low, compare value, replace value 150 | ; (note: order differs from decoded_code) 151 | ; vram_block: 152 | ; a block of bytes to be copied to VRAM 153 | ; 3 bytes: payload size (1-32), address high, address low 154 | ; 1-32 bytes: payload 155 | ; sprite_data_atr, sprite_data_x, sprite_data_y, sprite_data_til: 156 | ; attributes, X positions, Y positions and tiles of sprites 157 | ; 0-19: hand cursor (5*4 sprites) 158 | ; 20-23: revolving cursor (2*2 sprites) 159 | ; 24-39: flying letter (4*4 sprites) 160 | ; 32-47: 1st particle set (16 sprites; overlaps with flying letter) 161 | ; 48-63: 2nd particle set (16 sprites) 162 | ; metasprites: 163 | ; objects consisting of several hardware sprites 164 | ; 2 + 5*4 bytes for hand cursor 165 | ; 2 + 2*2 bytes for revolving cursor 166 | ; 2 + 4*4 bytes for flying letter 167 | ; for each one: 168 | ; 1 byte: width in hardware sprites 169 | ; 1 byte: height in hardware sprites 170 | ; width * height bytes: indexes to planar sprite data 171 | ; parti_spds_x, parti_spds_y: 172 | ; horizontal/vertical speeds of flying particles; signed; only indexes 173 | ; 32-63 used in each 174 | ; entered_letrs 175 | ; letters entered by user (0 = none, 3-18 = AEPOZXLUGKISTVYN) 176 | 177 | ; CPU addresses - NES memory-mapped registers; see: 178 | ; - https://www.nesdev.org/wiki/PPU_registers 179 | ; - https://www.nesdev.org/wiki/APU_registers 180 | ; - https://www.nesdev.org/wiki/Controller_reading_code 181 | ppu_ctrl equ $2000 182 | ppu_mask equ $2001 183 | ppu_status equ $2002 184 | oam_addr equ $2003 185 | ppu_scroll equ $2005 186 | ppu_addr equ $2006 187 | ppu_data equ $2007 188 | oam_dma equ $4014 189 | pulse1_regs equ $4000 ; $4000-$4003 190 | noise_regs equ $400c ; $400c, $400e-$400f 191 | sound_ctrl equ $4015 192 | joypad1 equ $4016 193 | joypad2 equ $4017 194 | 195 | ; CPU addresses - Game Genie memory-mapped registers; 196 | ; see: https://www.nesdev.org/wiki/Game_Genie 197 | ; - genie_codes: address high/low, compare, replace for 3 codes 198 | genie_ctrl equ $8000 ; master control 199 | genie_codes equ $8001 ; $8001-$800c (12 bytes, see above) 200 | genie_unknown1 equ $fff0 201 | genie_unknown2 equ $fff1 202 | 203 | ; PPU addresses 204 | vram_nt0 equ $2000 ; name table 0 205 | vram_at0 equ $23c0 ; attribute table 0 206 | vram_palette equ $3f00 ; palette 207 | 208 | ; joypad bitmasks 209 | pad_a equ %10000000 ; A button 210 | pad_b equ %01000000 ; B button 211 | pad_st_sel equ %00110000 ; start or select button 212 | pad_up equ %00001000 ; d-pad up 213 | pad_down equ %00000100 ; d-pad down 214 | pad_left equ %00000010 ; d-pad left 215 | pad_right equ %00000001 ; d-pad right 216 | 217 | ; --- Macros ------------------------------------------------------------------ 218 | 219 | ; These macros prevent ASM6 from automatically optimizing 16-bit addressing to 220 | ; 8-bit when the address is $00-$ff. 221 | macro deca _word ; DEC absolute 222 | hex ce 223 | dw _word 224 | endm 225 | macro ldaa _word ; LDA absolute 226 | hex ad 227 | dw _word 228 | endm 229 | macro ldxa _word ; LDX absolute 230 | hex ae 231 | dw _word 232 | endm 233 | macro staa _word ; STA absolute 234 | hex 8d 235 | dw _word 236 | endm 237 | 238 | macro add _operand ; add without carry 239 | clc 240 | adc _operand 241 | endm 242 | macro sub _operand ; subtract with carry 243 | sec 244 | sbc _operand 245 | endm 246 | 247 | ; ----------------------------------------------------------------------------- 248 | 249 | base $f000 ; last 4 KiB of CPU address space 250 | 251 | init1 ; Part 1/3 of initialization. Called by reset vector. 252 | 253 | sei ; ignore IRQs 254 | cld ; disable decimal mode 255 | lda #%00000000 256 | sta ppu_ctrl ; disable NMI 257 | ldx #$ff 258 | txs ; initialize stack pointer 259 | 260 | ; do something to unknown Game Genie registers 261 | lda #$00 262 | sta genie_unknown1 263 | jsr delay 264 | sta genie_unknown2 265 | jsr delay 266 | sta genie_unknown1 267 | 268 | jmp init2 ; continue initialization 269 | 270 | delay ldx #96 ; wait; called by init1 271 | ldy #8 272 | - dex 273 | bne - 274 | dey 275 | bne - 276 | rts 277 | 278 | init2 ; Part 2/3 of initialization. Called by init1. 279 | 280 | ldx #10 ; wait for VBlank 10 times 281 | - lda ppu_status 282 | bpl - 283 | dex 284 | bne - 285 | 286 | ldx #$ff ; reinitialize stack pointer (why?) 287 | txs 288 | 289 | lda #$07 ; clear RAM (fill $0000-$07ff with $00) 290 | sta ram_clear_ptr+1 291 | lda #$00 292 | sta ram_clear_ptr+0 293 | tay 294 | - sta (ram_clear_ptr),y 295 | iny 296 | bne - 297 | dec ram_clear_ptr+1 298 | bpl - 299 | 300 | lda #%00000110 ; hide sprites and background 301 | sta ppu_mask_mirror 302 | sta ppu_mask 303 | 304 | ; enable NMI; use 8*8-pixel sprites, pattern table 0, name 305 | ; table 0 and 1-byte VRAM address autoincrement 306 | lda #%10000000 307 | sta ppu_ctrl_mirror 308 | sta ppu_ctrl 309 | 310 | deca vram_buf_free ; set to 255 311 | 312 | jmp init3 ; continue initialization 313 | 314 | ; ----------------------------------------------------------------------------- 315 | 316 | read_joypads ; Read joypads. Out: joypad1_status, joypad2_status. 317 | ; Bits: A, B, select, start, up, down, left, right. 318 | ; Called by do_every_frame. 319 | 320 | lda #%00000001 ; initialize joypads 321 | sta joypad1 322 | lda #%00000000 323 | sta joypad1 324 | 325 | ; read joypad 1 (for each button, copy least significant bit 326 | ; of joypad1 to joypad1_status via carry) 327 | ldy #8 328 | - lda joypad1 329 | ror a 330 | rol joypad1_status 331 | dey 332 | bne - 333 | 334 | ldy #8 ; read joypad 2 in similar fashion 335 | - lda joypad2 336 | ror a 337 | rol joypad2_status 338 | dey 339 | bne - 340 | 341 | rts 342 | 343 | ; ----------------------------------------------------------------------------- 344 | 345 | ; The non-maskable interrupt routine. 346 | ; In: skip_nmi (if set, skip PPU stuff). 347 | ; Out: nmi_done (set when exiting). 348 | ; Called by NMI vector. 349 | 350 | nmi pha ; push A, X, Y 351 | txa 352 | pha 353 | tya 354 | pha 355 | lda skip_nmi ; if flag set, skip PPU stuff 356 | bne + 357 | jsr sprite_dma ; do sprite DMA 358 | jsr flush_vram_buf ; flush VRAM buffer 359 | ldaa scroll_x_mirror ; update PPU registers from mirrors 360 | sta ppu_scroll 361 | ldaa scroll_y_mirror 362 | sta ppu_scroll 363 | lda ppu_ctrl_mirror 364 | sta ppu_ctrl 365 | + lda #1 ; set flag; pull Y, X, A 366 | staa nmi_done 367 | pla 368 | tay 369 | pla 370 | tax 371 | pla 372 | rti 373 | 374 | ; ----------------------------------------------------------------------------- 375 | 376 | draw_bg_graphic ; Copy a graphic (e.g. the Game Genie logo) to name table. 377 | ; In: A = id (see graphix_offsets), X/Y = X/Y position in 378 | ; tiles. 379 | ; Called by init_background, letter_input, check_button_b. 380 | 381 | stx graphic_x 382 | sty graphic_y 383 | jsr set_graphix_ptr ; A = id 384 | 385 | ldy #0 ; get width and height in nybbles/tiles 386 | lda (graphics_ptr),y 387 | sta graphic_width 388 | iny 389 | lda (graphics_ptr),y 390 | sta graphic_height 391 | 392 | ldaa graphics_ptr+0 ; advance pointer to start of data 393 | add #2 394 | staa graphics_ptr+0 395 | ldaa graphics_ptr+1 396 | adc #0 397 | staa graphics_ptr+1 398 | 399 | lda #0 ; copy nybbles to VRAM buffer 400 | sta graphic_y_offs 401 | -- lda #0 ; start Y loop 402 | sta graphic_x_offs 403 | - jsr tile_to_vrambuf ; start X loop 404 | inc graphic_x_offs 405 | lda graphic_x_offs 406 | cmp graphic_width 407 | bne - 408 | inc graphic_y_offs 409 | lda graphic_y_offs 410 | cmp graphic_height 411 | bne -- 412 | 413 | rts 414 | 415 | tile_to_vrambuf ; Copy one nybble/tile of a graphic to VRAM buffer. 416 | ; In: 417 | ; graphic_x/graphic_y: 418 | ; top left position in tiles 419 | ; graphic_x_offs/graphic_y_offs: 420 | ; position inside graphic in tiles 421 | ; vram_addr_hi: 422 | ; high byte of VRAM address 423 | ; Called by draw_bg_graphic. 424 | 425 | lda graphic_y_offs ; offset to nybble -> nybble_offs 426 | ldx graphic_width 427 | jsr multiply ; X * A -> A 428 | add graphic_x_offs 429 | staa nybble_offs 430 | 431 | lsr a ; graphic data byte -> temp1 432 | tay 433 | lda (graphics_ptr),y 434 | sta temp1 435 | 436 | ; nybble from byte -> graphic_nybble; 437 | ; if even offset, upper nybble; if odd, lower 438 | ldaa nybble_offs 439 | and #%00000001 440 | beq + 441 | lda temp1 442 | and #%00001111 443 | jmp ++ 444 | + lda temp1 445 | lsr a 446 | lsr a 447 | lsr a 448 | lsr a 449 | ++ sta graphic_nybble 450 | 451 | ; vram_addr_hi * 256 452 | ; + (graphic_y + graphic_y_offs) * 32 453 | ; + graphic_x + graphic_x_offs - 4 454 | ; -> word(nybble_vram_hi, nybble_vram_lo) 455 | 456 | ; graphic_x + graphic_x_offs - 4 -> vram_block_x 457 | lda graphic_x 458 | add graphic_x_offs 459 | sub #4 460 | sta vram_block_x 461 | 462 | ; (graphic_y + graphic_y_offs) * 32 + vram_addr_hi * 256 463 | ; -> word(nybble_vram_hi, nybble_vram_lo) 464 | lda graphic_y 465 | add graphic_y_offs 466 | sta vram_block_y 467 | lda vram_block_y 468 | asl a 469 | asl a 470 | asl a 471 | sta nybble_vram_lo 472 | lda #0 ; start computing nybble_vram_hi 473 | asl nybble_vram_lo ; 4th shift 474 | rol a ; save overflowed bit 475 | asl nybble_vram_lo ; 5th shift 476 | rol a ; save overflowed bit 477 | add vram_addr_hi 478 | sta nybble_vram_hi 479 | 480 | ; word(nybble_vram_hi, nybble_vram_lo) += vram_block_x 481 | lda nybble_vram_lo 482 | add vram_block_x 483 | sta nybble_vram_lo 484 | lda nybble_vram_hi 485 | adc #0 486 | sta nybble_vram_hi 487 | 488 | ; set up a VRAM block with data size 1 and copy it to VRAM 489 | ; buffer 490 | lda nybble_vram_hi 491 | sta vram_block+1 492 | lda nybble_vram_lo 493 | sta vram_block+2 494 | lda graphic_nybble 495 | sta vram_block+3 496 | lda #1 497 | sta vram_block+0 498 | jsr vram_blk_to_buf 499 | 500 | rts 501 | 502 | ; ----------------------------------------------------------------------------- 503 | 504 | multiply ; Multiply (X * A -> A). 505 | ; Called by tile_to_vrambuf, draw_metaspr_gr. 506 | sta multiply_temp 507 | lda #0 508 | cpx #0 509 | beq + 510 | clc 511 | - adc multiply_temp 512 | dex 513 | bne - 514 | + rts 515 | 516 | ; ----------------------------------------------------------------------------- 517 | 518 | sprite_dma ; Copy interleaved sprite data to OAM. 519 | ; Called by nmi, init3. 520 | lda #$00 521 | sta oam_addr 522 | lda #>sprite_data 523 | sta oam_dma 524 | rts 525 | 526 | ; ----------------------------------------------------------------------------- 527 | 528 | flush_vram_buf ; Move as many blocks as possible from vram_buffer to VRAM. 529 | ; Each block in vram_buffer: data size, address high, address 530 | ; low, data. 531 | ; Called by nmi, vram_blk_to_buf. 532 | 533 | ; maximum sum of (totalDataSize + blocks * 5) to copy 534 | lda #100 535 | sta vram_budget 536 | 537 | ldy vram_buf_rd_pos 538 | -- cpy vram_buf_wr_pos ; exit if all blocks copied 539 | beq + 540 | lda vram_buffer,y ; get data size; compute cost of copying 541 | tax ; it (dataSize + 5) 542 | add #5 543 | sta vram_block_cost 544 | lda vram_budget ; remove cost from budget 545 | sub vram_block_cost 546 | bcc + ; exit if out of budget 547 | sta vram_budget 548 | lda vram_buf_free ; add total data size to number of free 549 | clc ; bytes 550 | adc vram_buffer,y 551 | adc #3 552 | sta vram_buf_free 553 | iny ; get and set VRAM address 554 | lda vram_buffer,y 555 | sta ppu_addr 556 | iny 557 | lda vram_buffer,y 558 | sta ppu_addr 559 | iny ; copy block data to VRAM 560 | - lda vram_buffer,y 561 | sta ppu_data 562 | iny 563 | dex 564 | bne - 565 | jmp -- ; next block 566 | 567 | + sty vram_buf_rd_pos ; all blocks copied or out of budget 568 | rts 569 | 570 | ; ----------------------------------------------------------------------------- 571 | 572 | vram_blk_to_buf ; Copy current VRAM block to VRAM buffer. 573 | ; Called by tile_to_vrambuf, update_attr_blk, init3, 574 | ; animate_color, hilite_inp_row. 575 | 576 | ; total block size -> temp1; does block fit in VRAM buffer? 577 | lda vram_block+0 578 | add #3 579 | sta temp1 580 | lda vram_buf_free 581 | cmp temp1 582 | bcs + 583 | 584 | ; does not fit; if NMI is being skipped, flush VRAM buffer and 585 | ; restart sub, otherwise just restart sub 586 | lda skip_nmi 587 | beq vram_blk_to_buf 588 | jsr flush_vram_buf 589 | jmp vram_blk_to_buf 590 | 591 | + ; fits; subtract total block size from free space, copy VRAM 592 | ; block to VRAM buffer (X/Y = source/destination index) 593 | sub temp1 594 | sta vram_buf_free 595 | ldx #0 596 | ldy vram_buf_wr_pos 597 | - lda vram_block,x 598 | sta vram_buffer,y 599 | inx 600 | iny 601 | cpx temp1 602 | bne - 603 | sty vram_buf_wr_pos 604 | rts 605 | 606 | ; ----------------------------------------------------------------------------- 607 | 608 | draw_metaspr_gr ; Draw a graphic (e.g. the hand cursor) as a metasprite. 609 | ; In: A = graphic id (see graphix_offsets) 610 | ; Out: A = index to metaspr_indexes 611 | ; Called by init3. 612 | 613 | sta graphic_id ; set pointer to address of graphic 614 | jsr set_graphix_ptr 615 | 616 | ldy #0 ; get width and height 617 | lda (graphics_ptr),y 618 | sta metaspr_width 619 | tax 620 | iny 621 | lda (graphics_ptr),y 622 | sta metaspr_height 623 | 624 | ; width * height (A * X) -> A, grafic_dataleft 625 | jsr multiply 626 | sta grafic_dataleft 627 | 628 | add #2 ; total size of graphic -> A 629 | 630 | ; find first free byte in metaspr_indexes; 631 | ; save to metaspr_index and X; 632 | ; add total size of graphic to all following bytes in 633 | ; metaspr_indexes 634 | jsr findfreemetaspr ; A = value to add 635 | sta metaspr_index 636 | tax 637 | 638 | lda metaspr_indexes,x ; index to metasprites -> X 639 | tax 640 | 641 | ; save width and height of graphic to metasprite data 642 | lda metaspr_width 643 | sta metasprites,x 644 | lda metaspr_height 645 | sta metasprites+1,x 646 | 647 | inx ; indexes to individual sprites 648 | inx 649 | 650 | ; assign grafic_dataleft free sprites to metasprite, starting 651 | ; from first free sprite 652 | ldy #255 653 | - iny 654 | lda sprite_data_atr,y 655 | bne - 656 | tya 657 | sta metasprites,x 658 | inx 659 | dec grafic_dataleft 660 | bne - 661 | 662 | ; set up tiles and attributes for individual sprites 663 | ldx metaspr_index 664 | lda graphic_id 665 | jsr init_metasprite ; A = id, X = index to metaspr_indexes 666 | 667 | ; set up positions for individual sprites 668 | ldx metaspr_index 669 | jsr update_metaspr ; X = metasprite index 670 | 671 | lda metaspr_index ; return metasprite index 672 | rts 673 | 674 | ; ----------------------------------------------------------------------------- 675 | 676 | set_graphix_ptr ; Set graphics pointer. 677 | ; In: A = graphic id (see graphix_offsets). 678 | ; Out: graphics_ptr = address of graphic. 679 | ; Called by draw_bg_graphic, draw_metaspr_gr, init_metasprite. 680 | 681 | sta temp1 ; id 682 | 683 | lda #graphix_offsets 686 | sta graphics_ptr+1 687 | 688 | lda temp1 ; word(graphics_ptr) += graphic_id >> 7 689 | bpl + 690 | inc graphics_ptr+1 ; never accessed 691 | + asl a ; offset to offset 692 | tay 693 | lda (graphics_ptr),y ; offset to graphic 694 | pha 695 | iny 696 | lda (graphics_ptr),y 697 | add #graphix_offsets 701 | sta graphics_ptr+1 702 | 703 | rts 704 | 705 | ; ----------------------------------------------------------------------------- 706 | 707 | update_metaspr ; Update metasprite's position to its individual sprites' 708 | ; positions. 709 | ; In: 710 | ; X: metasprite index (0 = hand cursor, 1 = revolving cursor, 711 | ; 2 = flying letter) 712 | ; metasprite_x, metasprite_y: position of metasprite 713 | ; Called by draw_metaspr_gr, do_every_frame, update_revolv, 714 | ; input_effects, move_flying_ltr. 715 | 716 | lda metaspr_indexes,x ; index to metasprite info 717 | tax 718 | 719 | lda metasprites,x ; get size of metasprite 720 | sta metaspr_width 721 | lda metasprites+1,x 722 | sta metaspr_height 723 | 724 | inx ; start of target sprite indexes 725 | inx 726 | 727 | lda metasprite_y ; init Y position of target sprites 728 | sta sprite_y+0 729 | lda always_zero2 730 | sta sprite_y+1 731 | 732 | -- ; outer loop; hide sprite row if beyond bottom edge of screen; 733 | ; init inner loop counter and X position of target sprites 734 | lda sprite_y+1 735 | bne hide_sprite_row 736 | lda metaspr_width 737 | sta nybbles_left_x 738 | lda metasprite_x 739 | sta sprite_x+0 740 | lda always_zero1 741 | sta sprite_x+1 742 | ; 743 | - ; inner loop; hide sprite if beyond right edge of screen 744 | lda sprite_x+1 745 | bne + 746 | lda metasprites,x ; index to target sprite 747 | tay 748 | lda sprite_data_til,y ; hide if target tile = 0 749 | beq + 750 | lda sprite_x+0 ; set target sprite position 751 | sta sprite_data_x,y 752 | lda sprite_y+0 753 | sta sprite_data_y,y 754 | inx 755 | jmp ++ 756 | ; 757 | + lda metasprites,x ; hide sprite 758 | tay 759 | lda #255 760 | sta sprite_data_y,y 761 | inx 762 | ; 763 | ++ lda sprite_x+0 ; next sprite 764 | add #8 765 | sta sprite_x+0 766 | lda sprite_x+1 767 | adc #0 768 | sta sprite_x+1 769 | dec nybbles_left_x 770 | bne - 771 | ; 772 | next_row lda sprite_y+0 ; next row of sprites 773 | add #8 774 | sta sprite_y+0 775 | lda sprite_y+1 776 | adc #0 777 | sta sprite_y+1 778 | dec metaspr_height 779 | bne -- 780 | 781 | rts 782 | 783 | hide_sprite_row lda metaspr_width ; hide row of sprites 784 | sta nybbles_left_x 785 | - lda metasprites,x 786 | tay 787 | lda #255 788 | sta sprite_data_y,y 789 | inx 790 | dec nybbles_left_x 791 | bne - 792 | jmp next_row 793 | 794 | ; ----------------------------------------------------------------------------- 795 | 796 | init_metasprite ; Set up tiles and attributes for individual sprites of a 797 | ; metasprite. 798 | ; In: A = graphic id (see graphix_offsets), 799 | ; X = index to metaspr_indexes. 800 | ; Called by draw_metaspr_gr, input_effects. 801 | 802 | sta graphic_id 803 | 804 | lda metaspr_indexes,x ; index to metasprites 805 | add #2 806 | pha 807 | 808 | lda graphic_id ; start reading graphic (A = id) 809 | jsr set_graphix_ptr 810 | 811 | ldy #0 ; get dimensions of graphic 812 | lda (graphics_ptr),y 813 | sta metaspr_width 814 | iny 815 | lda (graphics_ptr),y 816 | sta metaspr_height 817 | 818 | lda #0 ; 2 -> nybble_offs 819 | ora #%00011100 820 | ldx unused1 821 | sta always_00011100 822 | lda #2 823 | ldy #1 824 | sty always_one 825 | sta nybble_offs 826 | lda #0 827 | sta always_zero3 828 | 829 | pla ; index to metasprites -> X 830 | tax 831 | 832 | init_loop_y ; copy all rows of graphics data to target sprites 833 | lda metaspr_width ; inner loop counter 834 | sta nybbles_left_x 835 | 836 | init_loop_x ; copy one row of graphics data to target sprites 837 | lda nybble_offs ; graphics data byte -> temp1 838 | lsr a 839 | add #1 840 | tay 841 | lda (graphics_ptr),y 842 | sta temp1 843 | 844 | lda nybble_offs ; if even offset, push high nybble, 845 | and #%00000001 ; else low 846 | beq + 847 | lda temp1 848 | and #%00001111 849 | jmp ++ 850 | + lda temp1 851 | lsr a 852 | lsr a 853 | lsr a 854 | lsr a 855 | ++ pha 856 | 857 | lda metasprites,x ; nybble -> target sprite tile 858 | tay 859 | pla 860 | sta sprite_data_til,y 861 | lda always_00011100 ; %00011100 -> target sprite attribute 862 | sta sprite_data_atr,y 863 | 864 | inx ; next target sprite 865 | 866 | lda nybble_offs ; increment 867 | add always_one 868 | sta nybble_offs 869 | 870 | dec nybbles_left_x 871 | bne init_loop_x 872 | 873 | lda nybble_offs ; do nothing 874 | add always_zero3 875 | sta nybble_offs 876 | 877 | dec metaspr_height 878 | bne init_loop_y 879 | 880 | rts 881 | 882 | ; ----------------------------------------------------------------------------- 883 | 884 | convert_sprites ; Convert planar sprite data to interleaved. 885 | ; Sprites with attribute byte $00 will be hidden. 886 | ; Called by do_every_frame. 887 | 888 | ; ascending order every 2nd frame, descending every 2nd frame 889 | lda odd_frame_flag1 890 | eor #%00000001 891 | sta odd_frame_flag1 892 | bne ++ 893 | 894 | ; descending order: planar sprites 61-0 -> interleaved sprites 895 | ; 2-63 896 | ldy #(2*4) ; destination offset 897 | ldx #61 ; source offset 898 | - lda sprite_data_atr,x 899 | beq + ; hide if attributes are $00 900 | ; 901 | ; A = attribute byte, X = src index, Y = dst index 902 | jsr convert_sprite 903 | dex 904 | bpl - 905 | rts 906 | + jsr hide_sprite_sub ; Y = index 907 | dex 908 | bpl - 909 | rts ; never accessed 910 | 911 | ++ ; ascending order: planar sprites 0-61 -> interleaved sprites 912 | ; 2-63 913 | ldy #(2*4) ; destination offset 914 | ldx #0 ; source offset 915 | - lda sprite_data_atr,x 916 | beq + ; hide if attributes are $00 917 | ; 918 | ; A = attribute byte, X = src index, Y = dst index 919 | jsr convert_sprite 920 | inx 921 | cpx #62 922 | bne - 923 | rts 924 | + jsr hide_sprite_sub ; Y = index 925 | inx 926 | cpx #62 927 | bne - 928 | rts 929 | 930 | convert_sprite ; Convert one non-hidden sprite from planar to interleaved. 931 | ; In: A = attribute byte, X/Y = source/target index. 932 | ; Out: Y += 4. 933 | ; Called by convert_sprites. 934 | sta sprite_data+2,y 935 | lda sprite_data_y,x 936 | sta sprite_data+0,y 937 | lda sprite_data_til,x 938 | sta sprite_data+1,y 939 | lda sprite_data_x,x 940 | sta sprite_data+3,y 941 | iny 942 | iny 943 | iny 944 | iny 945 | rts 946 | 947 | hide_sprite_sub ; Hide a sprite in sprite_data. 948 | ; In: Y = index, out: Y += 4. 949 | ; Called by convert_sprites. 950 | lda #255 951 | sta sprite_data,y 952 | iny 953 | iny 954 | iny 955 | iny 956 | rts 957 | 958 | ; ----------------------------------------------------------------------------- 959 | 960 | update_attr_blk ; Change the value of an attribute block (2 bits) within one 961 | ; attribute byte, preserving the other bits. 962 | ; In: 963 | ; vram_block_x: X position of attribute block (0-15) 964 | ; vram_block_y: Y position of attribute block (0-14) 965 | ; attr_fill_byte: value of new block (which 2 bits are read 966 | ; depends on vram_block_x and vram_block_y) 967 | ; Called by update_atr_byte, fill_atrblkrows. 968 | 969 | ; position of block within byte (0-3) -> Y 970 | lda vram_block_y 971 | and #%00000001 972 | asl a 973 | sta temp1 974 | lda vram_block_x 975 | and #%00000001 976 | ora temp1 977 | tay 978 | 979 | ; position of byte within attribute table (0-63) -> A, X 980 | lda vram_block_y 981 | asl a 982 | asl a 983 | and #%11111000 984 | sta temp1 985 | lda vram_block_x 986 | lsr a 987 | add temp1 988 | tax 989 | 990 | ; vram_at0 + A -> VRAM block target address 991 | ora #vram_at0 994 | sta vram_block+1 995 | 996 | lda #1 ; size of data to copy: one byte 997 | sta vram_block+0 998 | 999 | ; combine old and new bits of attribute byte: 1000 | ; (newAttributeByte & bitmaskDeterminedByY) 1001 | ; | (attr_data_copy,x & ~bitmaskDeterminedByY) 1002 | ; -> attr_data_copy,x and vram_block+3 (start of data) 1003 | lda attr_blk_masks,y 1004 | and attr_fill_byte 1005 | sta temp1 1006 | lda attr_blk_masks,y 1007 | eor #%11111111 1008 | and attr_data_copy,x 1009 | ora temp1 1010 | sta attr_data_copy,x 1011 | sta vram_block+3 1012 | 1013 | jmp vram_blk_to_buf ; ends with RTS 1014 | 1015 | attr_blk_masks ; Attribute block bitmasks. Read by update_attr_blk. 1016 | db %00000011, %00001100, %00110000, %11000000 1017 | 1018 | ; ----------------------------------------------------------------------------- 1019 | 1020 | initial_palette ; Initial palette. 32 bytes. Read by init3. 1021 | ; 1022 | ; background 0 1023 | hex 0d ; background (black; causes problems on some TVs) 1024 | hex 00 00 ; unused (gray) 1025 | hex 2c ; virtual keyboard (cyan) 1026 | ; background 1 1027 | hex 00 00 00 ; unused (gray) 1028 | hex 26 ; initial animated color (red, never seen) 1029 | ; background 2 1030 | hex 00 00 00 ; unused (gray) 1031 | hex 20 ; highlight (white) 1032 | ; background 3 1033 | hex 00 00 00 ; unused (gray) 1034 | hex 00 ; input area (gray) 1035 | ; 1036 | ; sprites 0 1037 | hex 0d ; background (black; causes problems on some TVs) 1038 | hex 00 00 ; unused (gray) 1039 | hex 13 ; letters, revolving crsr 1, particles 1 (purple) 1040 | ; sprites 1 1041 | hex 00 00 00 ; unused (gray) 1042 | hex 26 ; hand cursor 1 (red) 1043 | ; sprites 2 1044 | hex 00 00 00 ; unused (gray) 1045 | hex 20 ; hand crsr 2, revolv crsr 2, particles 2 (white) 1046 | ; sprites 3 1047 | hex 00 00 00 ; unused (gray) 1048 | hex 28 ; unused (yellow) 1049 | 1050 | init3 ; Part 3/3 of initialization. Called by init2. 1051 | 1052 | lda #1 ; set flag 1053 | sta skip_nmi 1054 | 1055 | lda #>vram_nt0 ; clear first name & attribute table 1056 | sta vram_addr_hi ; (fill VRAM $2000-$23ff with $00) 1057 | sta ppu_addr 1058 | lda #vram_palette 1120 | sta vram_block+1 1121 | lda # pointer 1192 | staa hand_y_spd_ptr+0 1193 | lda #>hand_spds_neg 1194 | staa hand_y_spd_ptr+1 1195 | 1196 | ; if moved from 3rd line to 2nd, 32 -> hand_y_spd_offs, 1197 | ; else 0 -> hand_y_spd_offs 1198 | lda #pad_up 1199 | jsr hand_area_chang 1200 | beq + 1201 | lda #32 1202 | jmp ++ 1203 | + lda #0 1204 | ++ staa hand_y_spd_offs 1205 | lda #pad_up 1206 | staa last_y_inp_acc 1207 | 1208 | jmp check_horiz 1209 | 1210 | check_down lda joypad1_status ; down pressed? 1211 | and #pad_down 1212 | beq check_horiz 1213 | lda #pad_down ; move hand if possible 1214 | jsr set_hand_target ; A = direction, out: A = success 1215 | beq check_horiz 1216 | 1217 | lda # pointer 1218 | staa hand_y_spd_ptr+0 1219 | lda #>hand_spds_pos 1220 | staa hand_y_spd_ptr+1 1221 | 1222 | ; if moved from 2nd line to 3rd, 32 -> hand_y_spd_offs, 1223 | ; else 0 -> hand_y_spd_offs 1224 | lda #pad_down 1225 | jsr hand_area_chang 1226 | beq + 1227 | lda #32 1228 | jmp ++ 1229 | + lda #0 1230 | ++ staa hand_y_spd_offs 1231 | lda #pad_down 1232 | staa last_y_inp_acc 1233 | 1234 | check_horiz ; if X speed pointer set, skip checking horizontal arrows 1235 | ldaa hand_x_spd_ptr+1 1236 | bne arrows_checked 1237 | 1238 | lda joypad1_status ; left pressed? 1239 | and #pad_left 1240 | beq check_right 1241 | lda #pad_left ; move hand if possible 1242 | jsr set_hand_target ; A = direction, out: A = success 1243 | beq check_right 1244 | 1245 | lda # pointer 1246 | staa hand_x_spd_ptr+0 1247 | lda #>hand_spds_neg 1248 | staa hand_x_spd_ptr+1 1249 | lda #0 1250 | staa hand_x_spd_offs 1251 | lda #pad_left 1252 | staa last_x_inp_acc 1253 | 1254 | jmp arrows_checked 1255 | 1256 | check_right lda joypad1_status ; right pressed? 1257 | and #pad_right 1258 | beq arrows_checked 1259 | lda #pad_right ; move hand if possible 1260 | jsr set_hand_target ; A = direction, out: A = success 1261 | beq arrows_checked 1262 | 1263 | lda # pointer 1264 | staa hand_x_spd_ptr+0 1265 | lda #>hand_spds_pos 1266 | staa hand_x_spd_ptr+1 1267 | lda #0 1268 | staa hand_x_spd_offs 1269 | lda #pad_right 1270 | staa last_x_inp_acc 1271 | 1272 | arrows_checked rts 1273 | 1274 | ; ----------------------------------------------------------------------------- 1275 | 1276 | findfreemetaspr ; Find the first free byte in metasprite indexes and add the 1277 | ; specified value to all following bytes. 1278 | ; In: A = value to add. 1279 | ; Out: A = index to first free byte. 1280 | ; Called by draw_metaspr_gr. 1281 | 1282 | sta temp1 ; value to add 1283 | 1284 | ; find first free byte (same as following byte) 1285 | ldx #255 1286 | - inx 1287 | lda metaspr_indexes+1,x 1288 | cmp metaspr_indexes,x 1289 | bne - 1290 | 1291 | stx first_free_byte ; index to first free byte 1292 | sta unused2 ; never used anywhere 1293 | 1294 | inx ; add value to all following bytes 1295 | - lda temp1 1296 | clc 1297 | adc metaspr_indexes,x 1298 | sta metaspr_indexes,x 1299 | inx 1300 | cpx #50 1301 | bne - 1302 | 1303 | lda first_free_byte 1304 | rts 1305 | 1306 | ; ----------------------------------------------------------------------------- 1307 | 1308 | init_background ; Initialize background graphics. Called by init3. 1309 | 1310 | lda #0 ; center background graphics vertically 1311 | sta scroll_x_mirror 1312 | lda #4 1313 | sta scroll_y_mirror 1314 | 1315 | lda #1 ; draw the Game Genie logo 1316 | ldx #5 1317 | ldy #3 1318 | jsr draw_bg_graphic ; A=id, X=X, Y=Y 1319 | 1320 | ; draw virtual keyboard (2*8 letters) 1321 | lda #3 ; "A" 1322 | sta keybd_graphic 1323 | lda #4 1324 | sta keybd_graphic_x 1325 | lda #8 1326 | sta keybd_graphic_y 1327 | - lda keybd_graphic ; draw letter 1328 | ldx keybd_graphic_x 1329 | ldy keybd_graphic_y 1330 | jsr draw_bg_graphic ; A=id, X=X, Y=Y 1331 | lda keybd_graphic_x ; increment X position 1332 | add #4 1333 | sta keybd_graphic_x 1334 | ; 1335 | cmp #(9*4) ; next line if necessary 1336 | bne + 1337 | lda #4 1338 | sta keybd_graphic_x 1339 | lda keybd_graphic_y 1340 | add #4 1341 | sta keybd_graphic_y 1342 | ; 1343 | + inc keybd_graphic ; loop until "N" (id 18) drawn 1344 | lda keybd_graphic 1345 | cmp #(18+1) 1346 | bne - 1347 | 1348 | ; draw input area (3*8 dashes) 1349 | lda #3 ; "A" (only used as loop counter) 1350 | sta keybd_graphic 1351 | lda #4 1352 | sta keybd_graphic_x 1353 | lda #18 1354 | sta keybd_graphic_y 1355 | - lda #19 ; draw dash 1356 | ldx keybd_graphic_x 1357 | ldy keybd_graphic_y 1358 | jsr draw_bg_graphic ; A=id, X=X, Y=Y 1359 | lda keybd_graphic_x ; increment X position 1360 | add #4 1361 | sta keybd_graphic_x 1362 | ; 1363 | cmp #(9*4) ; new line if necessary 1364 | bne + 1365 | lda #4 1366 | sta keybd_graphic_x 1367 | lda keybd_graphic_y 1368 | add #4 1369 | sta keybd_graphic_y 1370 | ; 1371 | + inc keybd_graphic ; loop until all dashes drawn 1372 | lda keybd_graphic 1373 | cmp #(3+24) 1374 | bne - 1375 | 1376 | ; set attribute table data 1377 | lda #%01010101 ; "Game Genie" logo 1378 | ldx #4 1379 | ldy #0 1380 | jsr fill_atrblkrows ; A=byte, X=rows, Y=first row 1381 | lda #%00000000 ; virtual keyboard 1382 | ldx #4 1383 | ldy #4 1384 | jsr fill_atrblkrows 1385 | lda #%10101010 ; 1st code 1386 | ldx #2 1387 | ldy #9 1388 | jsr fill_atrblkrows 1389 | lda #%11111111 ; 2nd and 3rd code 1390 | ldx #4 1391 | ldy #11 1392 | jsr fill_atrblkrows 1393 | 1394 | ldx #0 ; highlight top left letter on virtual 1395 | ldy #0 ; keyboard 1396 | jsr hilite_atr_byte 1397 | 1398 | lda #2 ; ? (no visible effect) 1399 | staa revolv_y_letr2 1400 | 1401 | rts 1402 | 1403 | ; ----------------------------------------------------------------------------- 1404 | 1405 | move_hand ; Called by do_every_frame. 1406 | 1407 | lda odd_frame_flag2 ; check arrows every 2nd frame 1408 | eor #%00000001 1409 | sta odd_frame_flag2 1410 | bne + 1411 | jsr check_arrows 1412 | 1413 | + lda hand_x_spd_ptr+1 ; horizontal movement 1414 | beq move_hand_vert ; not moving horizontally 1415 | 1416 | ; get speed (if offset % 16 = 15, terminator) 1417 | ldy hand_x_spd_offs 1418 | lda (hand_x_spd_ptr),y 1419 | cmp #$80 1420 | bne + 1421 | 1422 | lda #0 ; stop hand by resetting pointer 1423 | sta hand_x_spd_ptr+1 1424 | jmp ++ 1425 | 1426 | + ldx #hand_x_pixel ; no terminator 1427 | ; A = speed, X = address of position; out: Z 1428 | jsr move_hand2 1429 | ; every 2nd frame, skip rest of horizontal movement 1430 | beq move_hand_vert 1431 | 1432 | ++ tya ; hand_x_spd_offs 1433 | and #%00001111 ; get position on current line 1434 | cmp #7 1435 | bne + 1436 | 1437 | jsr upd_hand_ltrpos ; hand_x_spd_offs % 16 = 7 1438 | lda joypad1_status 1439 | and last_x_inp_acc 1440 | beq + 1441 | 1442 | lda last_x_inp_acc 1443 | jsr set_hand_target ; A = direction, out: A = success 1444 | beq + 1445 | 1446 | ldy #(16-1) 1447 | 1448 | + iny 1449 | sty hand_x_spd_offs 1450 | 1451 | move_hand_vert lda hand_y_spd_ptr+1 ; move vertically 1452 | beq move_hand_exit ; not moving vertically 1453 | 1454 | ldy hand_y_spd_offs ; get speed 1455 | lda (hand_y_spd_ptr),y 1456 | cmp #$80 ; terminator? 1457 | bne + 1458 | 1459 | lda #0 ; stop hand by resetting pointer 1460 | sta hand_y_spd_ptr+1 1461 | jmp ++ 1462 | 1463 | + ldx #hand_y_pixel ; no terminator 1464 | jsr move_hand2 ; in: A=speed, X=addr of pos; out: Z 1465 | beq move_hand_exit ; every 2nd frame, skip the rest 1466 | 1467 | ++ tya ; hand_y_spd_offs 1468 | and #%00001111 ; get position on current line 1469 | cmp #7 1470 | bne ++ 1471 | 1472 | jsr upd_hand_ltrpos ; hand_y_spd_offs % 16 = 7 1473 | lda joypad1_status 1474 | and last_y_inp_acc 1475 | beq ++ 1476 | 1477 | lda last_y_inp_acc 1478 | jsr set_hand_target ; A = direction, out: A = success 1479 | beq ++ 1480 | 1481 | lda last_y_inp_acc ; success 1482 | jsr hand_area_chang ; A = direction 1483 | beq + 1484 | ldy #(3*16-1) 1485 | jmp ++ 1486 | + ldy #(16-1) 1487 | 1488 | ++ iny 1489 | sty hand_y_spd_offs 1490 | 1491 | move_hand_exit rts 1492 | 1493 | ; Hand cursor speeds. 8*16 bytes. $80 = terminator. 1494 | ; Read indirectly by move_hand using hand_x_spd_ptr and 1495 | ; hand_y_spd_ptr. 1496 | hand_spds_pos ; positive speeds 1497 | hex 01 01 02 02 03 03 04 04 03 03 02 02 02 01 ff 80 ; sum=32 1498 | hex 05 04 04 03 03 04 05 04 03 03 02 02 02 01 ff 80 ; sum=44 1499 | hex 02 03 04 05 06 06 06 04 03 03 02 02 02 01 ff 80 ; sum=48 1500 | hex 05 06 07 08 07 06 05 04 03 03 02 02 02 01 ff 80 ; sum=60 1501 | hand_spds_neg ; negative speeds (same as above but negated in 2's complement) 1502 | hex ff ff fe fe fd fd fc fc fd fd fe fe fe ff 01 80 1503 | hex fb fc fc fd fd fc fb fc fd fd fe fe fe ff 01 80 1504 | hex fe fd fc fb fa fa fa fc fd fd fe fe fe ff 01 80 1505 | hex fb fa f9 f8 f9 fa fb fc fd fd fe fe fe ff 01 80 1506 | 1507 | move_hand2 ; Add hand cursor speed to position. 1508 | ; In: A = speed (horizontal/vertical), 1509 | ; X = address of position (hand_x_pixel/hand_y_pixel). 1510 | ; Out: zero flag reflects odd_frame_flag2. 1511 | ; Called by move_hand. 1512 | 1513 | pha ; divide speed by 2, round toward -inf 1514 | asl a 1515 | pla 1516 | ror a 1517 | 1518 | dec odd_frame_flag2 ; carry: 0 or LSB of original speed 1519 | beq + 1520 | clc 1521 | + adc $00,x ; add newSpeed+C to hand cursor pos 1522 | sta $00,x 1523 | 1524 | inc odd_frame_flag2 1525 | rts 1526 | 1527 | ; ----------------------------------------------------------------------------- 1528 | 1529 | hand_area_chang ; Has the hand cursor moved between the virtual keyboard and 1530 | ; the input area? 1531 | ; In: A = direction (pad_up/pad_down), hand_y_letr_trg. 1532 | ; Out: A (0=no, 1=yes). 1533 | ; Called by check_arrows, move_hand. 1534 | 1535 | cmp #pad_down ; moved down to input area? 1536 | bne + 1537 | lda hand_y_letr_trg 1538 | cmp #2 1539 | bne ++ 1540 | lda #1 1541 | rts 1542 | 1543 | + cmp #pad_up ; moved up to virtual keyboard? 1544 | bne ++ 1545 | lda hand_y_letr_trg 1546 | cmp #1 1547 | bne ++ 1548 | lda #1 1549 | rts 1550 | 1551 | ++ lda #0 ; no 1552 | rts 1553 | 1554 | ; ----------------------------------------------------------------------------- 1555 | 1556 | set_hand_target ; Move hand cursor to specified direction if possible. 1557 | ; In: A = direction (pad_right/pad_left/pad_down/pad_up) 1558 | ; Out: hand_x_letr_trg/hand_y_letr_trg, 1559 | ; A = success (1=yes, 0=no). 1560 | ; Called by check_arrows, move_hand. 1561 | 1562 | cmp #pad_right ; check right 1563 | bne + 1564 | lda hand_x_letr_trg ; try to move 1565 | cmp #7 1566 | beq hand_move_fail 1567 | inc hand_x_letr_trg 1568 | jmp hand_move_succ 1569 | 1570 | + cmp #pad_left ; check left 1571 | bne + 1572 | lda hand_x_letr_trg ; try to move 1573 | beq hand_move_fail 1574 | dec hand_x_letr_trg 1575 | jmp hand_move_succ 1576 | 1577 | + cmp #pad_down ; check down 1578 | bne + 1579 | lda hand_y_letr_trg ; try to move 1580 | cmp #4 1581 | beq hand_move_fail 1582 | inc hand_y_letr_trg 1583 | jmp hand_move_succ 1584 | 1585 | + cmp #pad_up ; check up 1586 | bne hand_move_succ 1587 | lda hand_y_letr_trg ; try to move 1588 | beq hand_move_fail 1589 | dec hand_y_letr_trg 1590 | jmp hand_move_succ 1591 | 1592 | hand_move_succ lda #1 ; success 1593 | rts 1594 | hand_move_fail lda #0 1595 | rts 1596 | 1597 | ; ----------------------------------------------------------------------------- 1598 | 1599 | hilite_atr_byte ; Highlight an attribute byte. An alternate entry point for 1600 | ; update_atr_byte. 1601 | ; Called by: (see update_atr_byte). 1602 | lda #%10101010 1603 | 1604 | update_atr_byte ; Update an attribute table byte (2*2 attribute blocks). 1605 | ; In: A = new attribute table byte, 1606 | ; X/Y = position (X = 0-7; Y = 0-1 if virtual keyboard, 1607 | ; 2-4 if input area). 1608 | ; Alternate entry points: hilite_atr_byte, clear_atr_byte. 1609 | ; Called by (incl. alternate entry points) init_background, 1610 | ; upd_hand_ltrpos, hilite_inp_area. 1611 | 1612 | sta attr_fill_byte 1613 | txa ; get attribute block X 1614 | asl a 1615 | sta vram_block_x 1616 | tya ; get attribute block Y 1617 | asl a 1618 | add #4 1619 | cmp #8 1620 | bcc + ; on virtual keyboard 1621 | add #1 1622 | + sta vram_block_y ; update 2*2 attribute blocks 1623 | jsr update_attr_blk 1624 | inc vram_block_x 1625 | jsr update_attr_blk 1626 | inc vram_block_y 1627 | jsr update_attr_blk 1628 | dec vram_block_x 1629 | jmp update_attr_blk ; ends with RTS 1630 | 1631 | clear_atr_byte ; Clear an attribute type. An alternate entry point for 1632 | ; update_atr_byte. 1633 | ; Called by: (see update_atr_byte). 1634 | lda #%00000000 1635 | jmp update_atr_byte 1636 | 1637 | ; ----------------------------------------------------------------------------- 1638 | 1639 | upd_hand_ltrpos ; Update letter position of hand cursor. Called by move_hand. 1640 | 1641 | tya ; push Y 1642 | pha 1643 | 1644 | ldx hand_x_keyboard ; un-highlight the letter the cursor is 1645 | ldy hand_y_keyboard ; leaving 1646 | jsr clear_atr_byte 1647 | 1648 | lda hand_y_letr_trg ; where's the target letter? 1649 | cmp #2 1650 | bcs + 1651 | 1652 | ; on virtual keyboard; update actual hand position and last 1653 | ; position on keyboard 1654 | sta hand_y_keyboard 1655 | sta hand_y_letr 1656 | lda hand_x_letr_trg 1657 | sta hand_x_keyboard 1658 | sta hand_x_letr 1659 | ldx hand_x_keyboard ; highlight letter the cursor is on 1660 | ldy hand_y_keyboard 1661 | jsr hilite_atr_byte 1662 | pla ; pull Y 1663 | tay 1664 | rts 1665 | 1666 | + ; on input area; update actual hand position 1667 | sta hand_y_letr 1668 | lda hand_x_letr_trg 1669 | sta hand_x_letr 1670 | pla ; pull Y 1671 | tay 1672 | rts 1673 | 1674 | ; ----------------------------------------------------------------------------- 1675 | 1676 | init_sprite_atr ; Initial sprite attribute data. Bytes are in reverse order. 1677 | ; Bits: VHBUUUPP (Vflip, Hflip, Behind bg, Unimplemented, 1678 | ; Palette). 1679 | ; Read by init3. 1680 | db %00011001, %00011001, %00011001, %00011001, %00011010 ;19-15 1681 | db %00011001, %00011001, %00011001, %00011001, %00011010 ;14-10 1682 | db %00011001, %00011001, %00011001, %00011001, %00011010 ;9-5 1683 | db %00011001, %00011001, %00011001, %00011001, %00011010 ;4-0 1684 | 1685 | ; ----------------------------------------------------------------------------- 1686 | 1687 | upd_revolv_attr ; Change attributes of revolving cursor's sprites (20-23). 1688 | ; Called by update_revolv. 1689 | 1690 | ; change subpalette between 0 and 2 (why not just EOR?) 1691 | lda sprite_data_atr+20 1692 | cmp #%00011010 1693 | bne + 1694 | lda #%00011000 1695 | jmp ++ 1696 | + lda #%00011010 1697 | 1698 | ++ ; put sprite behind background if phase is 0-6 1699 | ldxa revolv_phase 1700 | cpx #7 1701 | bcs + 1702 | ora #%00100000 1703 | 1704 | + sta sprite_data_atr+20 1705 | sta sprite_data_atr+21 1706 | sta sprite_data_atr+22 1707 | sta sprite_data_atr+23 1708 | rts 1709 | 1710 | ; ----------------------------------------------------------------------------- 1711 | 1712 | letter_input ; Button A went from off to on and hand cursor is on virtual 1713 | ; keyboard. Called by check_button_a. 1714 | 1715 | ; push id of graphic to draw 1716 | ; (hand_y_letr * 8 + hand_x_letr + 3; see graphix_offsets) 1717 | lda hand_y_letr 1718 | asl a 1719 | asl a 1720 | asl a 1721 | add hand_x_letr 1722 | adc #3 1723 | pha 1724 | 1725 | ; Y position of revolving cursor in tiles -> Y 1726 | ; (revolv_y_letr1 * 4 + 18) 1727 | lda revolv_y_letr1 1728 | asl a 1729 | asl a 1730 | add #18 1731 | tay 1732 | 1733 | ; X position of revolving cursor in tiles -> X 1734 | ; (revolv_x_letr1 * 4 + 4) 1735 | lda revolv_x_letr1 1736 | asl a 1737 | asl a 1738 | add #4 1739 | tax 1740 | 1741 | pla 1742 | pha 1743 | jsr draw_bg_graphic ; A=id, X=X, Y=Y 1744 | jsr input_effects 1745 | pla ; id 1746 | jsr save_letter 1747 | 1748 | jmp + ; why? 1749 | + inc revolv_x_letr1 ; may be undone later 1750 | 1751 | lda revolv_x_letr1 ; 6 letters on current line? 1752 | cmp #6 1753 | bne check_8letters 1754 | 1755 | ; 6 letters on current line; 1756 | ; if 3rd one is A/P/Z/L/G/I/T/Y, the code is complete 1757 | lda revolv_y_letr1 1758 | asl a 1759 | asl a 1760 | asl a 1761 | add #(3-1) 1762 | tax 1763 | lda entered_letrs,x 1764 | sub #3 1765 | and #%00000001 1766 | beq code_complete 1767 | 1768 | ; always 6; causes next branch to be always taken 1769 | lda revolv_x_letr1 1770 | 1771 | check_8letters cmp #8 ; 8 letters on current line? 1772 | bne letr_input_end 1773 | 1774 | code_complete ; if not on last line, move to start of next line, 1775 | ; else undo cursor X increment 1776 | lda revolv_y_letr1 1777 | cmp #2 1778 | beq + 1779 | lda #0 1780 | sta revolv_x_letr1 1781 | inc revolv_y_letr1 1782 | jmp letr_input_end 1783 | + dec revolv_x_letr1 1784 | 1785 | letr_input_end jmp hilite_inp_area ; move highlight to new letter and RTS 1786 | 1787 | ; ----------------------------------------------------------------------------- 1788 | 1789 | check_button_a ; If status of button A has changed from off to on, input 1790 | ; letter (if hand cursor on keyboard) or move revolving cursor 1791 | ; (if hand cursor on input area). 1792 | ; Called by do_every_frame. 1793 | 1794 | lda joypad1_status 1795 | and #pad_a 1796 | cmp prev_btn_a_stat 1797 | bne + 1798 | rts 1799 | + sta prev_btn_a_stat 1800 | cmp #pad_a 1801 | beq + 1802 | rts 1803 | + lda hand_y_letr 1804 | cmp #2 1805 | bcs + 1806 | jmp letter_input ; hand on keyboard (ends with RTS) 1807 | + jmp move_revolv_man ; hand on input area (ends with RTS) 1808 | 1809 | ; ----------------------------------------------------------------------------- 1810 | 1811 | check_button_b ; Called by do_every_frame. 1812 | 1813 | ; if button B went from off to on, continue, else exit 1814 | lda joypad1_status 1815 | and #pad_b 1816 | cmp prev_btn_b_stat 1817 | bne + 1818 | rts 1819 | + sta prev_btn_b_stat 1820 | cmp #pad_b 1821 | beq + 1822 | rts 1823 | 1824 | + jmp + ; why? 1825 | + ; continue depending on where revolving cursor is on 1st column 1826 | lda revolv_x_letr1 1827 | beq movback_or_exit 1828 | lda revolv_y_letr1 1829 | cmp #2 1830 | bne exit_if_no_letr ; on 1st/2nd code, 2nd-8th column 1831 | 1832 | ; non-first column, last code; 1833 | ; maximum length of last code minus one -> A 1834 | ; (7 if 3rd letter is E/O/X/U/K/S/V/N, else 5) 1835 | lda entered_letrs+2*8+2 1836 | sub #3 1837 | and #%00000001 1838 | bne + 1839 | lda #(6-1) 1840 | bne ++ 1841 | + lda #(8-1) 1842 | 1843 | ++ cmp revolv_x_letr1 1844 | ; branch if cursor not on last possible letter of last code 1845 | bne exit_if_no_letr 1846 | 1847 | ; cursor is on last possible letter of last code 1848 | ; (revolv_x_letr1 = 5/7) 1849 | tax ; is code of maximum length? 1850 | lda entered_letrs+2*8,x 1851 | ; branch if last code is maximum length (cursor is on last 1852 | ; letter) 1853 | bne erase_letter 1854 | ; 3rd code is maximum length minus one (cursor is on following 1855 | ; position) 1856 | 1857 | exit_if_no_letr ; cursor is on 2nd-8th column, except on last possible letter 1858 | ; of 3rd code 1859 | jsr get_letter ; is letter zero? -> zero flag 1860 | bne erase_letr_end 1861 | 1862 | movback_or_exit ; move backwards or exit 1863 | ; cursor is on a dash and/or on 1st column; 1864 | ; if on first letter of first code, exit, otherwise move cursor 1865 | ; and erase letter 1866 | lda revolv_x_letr1 1867 | ora revolv_y_letr1 1868 | beq erase_letr_end 1869 | jsr mov_revolv_back 1870 | 1871 | erase_letter ; get position of letter to erase in tiles, store to X and Y 1872 | lda revolv_y_letr1 1873 | asl a 1874 | asl a 1875 | add #18 1876 | tay 1877 | lda revolv_x_letr1 1878 | asl a 1879 | asl a 1880 | add #4 1881 | tax 1882 | 1883 | ; spawn particles, draw dash on letter, mark letter as empty 1884 | jsr spawn_particls1 1885 | lda #19 1886 | jsr draw_bg_graphic ; A=id, X=X, Y=Y 1887 | lda #0 1888 | jmp save_letter ; ends with RTS 1889 | 1890 | erase_letr_end jmp mov_revolv_back ; why? 1891 | 1892 | mov_revolv_back ; Move revolving cursor backwards. Called by check_button_b. 1893 | 1894 | ; if column > 0: move backwards 1895 | ; if column = 0, row > 0: move to end of previous line 1896 | ; if column = 0, row = 0: do nothing 1897 | dec revolv_x_letr1 1898 | lda revolv_x_letr1 1899 | cmp #$ff 1900 | bne ++ 1901 | lda revolv_y_letr1 1902 | beq + 1903 | lda #7 1904 | sta revolv_x_letr1 1905 | dec revolv_y_letr1 1906 | jmp ++ 1907 | + inc revolv_x_letr1 1908 | ++ jsr fix_revolv_x ; move left to after last letter 1909 | 1910 | jmp hilite_inp_area ; ends with RTS 1911 | 1912 | ; ----------------------------------------------------------------------------- 1913 | 1914 | move_revolv_man ; Move revolving cursor manually. TODO: args? 1915 | ; Called by check_button_a. 1916 | 1917 | lda hand_y_letr ; move revolving cursor to hand cursor 1918 | sub #2 1919 | sta revolv_y_letr1 1920 | lda hand_x_letr 1921 | sta revolv_x_letr1 1922 | jsr fix_revolv_x 1923 | 1924 | txa ; push X, Y (why Y?) 1925 | pha 1926 | tya 1927 | pha 1928 | 1929 | jsr hilite_inp_area 1930 | 1931 | pla ; pull Y, X 1932 | tay 1933 | pla 1934 | tax 1935 | 1936 | rts 1937 | 1938 | ; ----------------------------------------------------------------------------- 1939 | 1940 | update_revolv ; Update position, phase and attributes of revolving cursor. 1941 | ; Called by do_every_frame. 1942 | 1943 | ; revolv_y_letr1 * 32 + 152 -> revolv_y_target 1944 | lda revolv_y_letr1 1945 | asl a 1946 | asl a 1947 | asl a 1948 | asl a 1949 | asl a 1950 | add #152 1951 | sta revolv_y_target 1952 | 1953 | ; revolv_x1 * 32 + 10 -> revolv_x_target 1954 | lda revolv_x_letr1 1955 | asl a 1956 | asl a 1957 | asl a 1958 | asl a 1959 | asl a 1960 | add #10 1961 | sta revolv_x_target 1962 | 1963 | lda revolv_x ; get horizontal speed 1964 | staa revolv_pos 1965 | lda revolv_x_target 1966 | staa revolv_target 1967 | jsr get_revolv_spd 1968 | add #128 1969 | sta revolv_trg_spd 1970 | lda revolv_spd_x 1971 | jsr accel_revolv ; A = speed 1972 | sta revolv_spd_x 1973 | 1974 | lda revolv_y ; get vertical speed 1975 | staa revolv_pos 1976 | lda revolv_y_target 1977 | staa revolv_target 1978 | jsr get_revolv_spd 1979 | add #128 1980 | sta revolv_trg_spd 1981 | lda revolv_spd_y 1982 | jsr accel_revolv ; A = speed 1983 | sta revolv_spd_y 1984 | 1985 | lda revolv_x ; add speed to position 1986 | add revolv_spd_x 1987 | sta revolv_x 1988 | lda revolv_y 1989 | add revolv_spd_y 1990 | sta revolv_y 1991 | 1992 | ldx revolv_phase ; adjust position by phase 1993 | lda revolv_x 1994 | clc 1995 | adc revolv_xoffsets,x 1996 | sta metasprite_x 1997 | lda revolv_y 1998 | clc 1999 | adc revolv_yoffsets,x 2000 | sta metasprite_y 2001 | 2002 | inx ; increment phase 2003 | cpx #16 2004 | bne + 2005 | ldx #0 2006 | + stx revolv_phase 2007 | 2008 | ; update metasprite position (X = metasprite index) 2009 | ldx revolv_metaspr 2010 | jsr update_metaspr 2011 | ; update attributes of sprites (ends with RTS) 2012 | jmp upd_revolv_attr 2013 | 2014 | ; X/Y offsets of revolving cursor by phase. 2015 | ; Read by update_revolv. 2016 | revolv_xoffsets ; Sine wave in two's complement. 17 values, -10...+10, last 2017 | ; one never accessed. 2018 | db 0, 4, 7, 9, 10, 9, 7, 4 2019 | db 0, 256-4, 256-7, 256-9, 256-10, 256-9, 256-7, 256-4 2020 | db 0 2021 | revolv_yoffsets ; Inverted cosine wave in two's complement. 17 values, 2022 | ; -10...+10, last one never accessed. 2023 | db 256-10, 256-9, 256-7, 256-4, 0, 4, 7, 9 2024 | db 10, 9, 7, 4, 0, 256-4, 256-7, 256-9 2025 | db 256-10 2026 | 2027 | get_revolv_spd ; Compute speed (X or Y) for revolving cursor. 2028 | ; In: revolv_pos, revolv_target. 2029 | ; Out: A = speed in pixels per frame as a signed integer. 2030 | ; Called by update_revolv. 2031 | 2032 | lda revolv_pos 2033 | cmp revolv_target 2034 | bcc ++ 2035 | bne + 2036 | lda #0 ; pos = target; 0 -> A 2037 | rts 2038 | 2039 | + ; pos > target; floor((target - pos) / 8) -> A 2040 | lda revolv_target 2041 | sub revolv_pos 2042 | ldx #3 2043 | - sec ; sign extension 2044 | ror a 2045 | dex 2046 | bne - 2047 | rts 2048 | 2049 | ++ ; pos < target; floor((target - pos) / 8) + 1 -> A; 2050 | ; note: ceil((target - pos) / 8), i.e., 2051 | ; floor((target - pos + 7) / 8), 2052 | ; would be symmetrical with pos > target 2053 | lda revolv_target 2054 | sub revolv_pos 2055 | ldx #3 2056 | - lsr a 2057 | dex 2058 | bne - 2059 | add #1 2060 | rts 2061 | 2062 | accel_revolv ; Accelerate revolving cursor by -1/0/+1 toward target speed. 2063 | ; In: 2064 | ; A = current speed (X or Y, pixels/frame, 2's complement) 2065 | ; revolv_trg_spd = target speed (X or Y, pixels/frame, 2066 | ; excess-128) 2067 | ; Out: A = new speed. Called by update_revolv. 2068 | 2069 | ; convert into excess-128 (%00000000 = -128, %11111111 = +127) 2070 | eor #%10000000 2071 | cmp revolv_trg_spd 2072 | bcc + ; current < target (increment) 2073 | beq ++ ; current = target (no change) 2074 | sbc #1 ; current > target (decrement) 2075 | jmp ++ 2076 | + adc #1 2077 | ++ eor #%10000000 ; convert back into two's complement 2078 | rts 2079 | 2080 | ; ----------------------------------------------------------------------------- 2081 | 2082 | spawn_particls2 ; Spawn one of two sets of particles. 2083 | ; In: parti_set_flag = which set. 2084 | ; Out: parti_set_flag flipped. 2085 | ; Called by spawn_particls1. 2086 | 2087 | lda parti_set_flag 2088 | beq + 2089 | 2090 | lda #0 ; flip flag, spawn 2nd set 2091 | sta parti_set_flag 2092 | ldx #48 2093 | jsr spawn_particls3 ; X = index to sprite data 2094 | lda #1 ; init timer (this one counts up) 2095 | sta parti_set2timer 2096 | rts 2097 | 2098 | + lda #1 ; flip flag, spawn 1st set 2099 | sta parti_set_flag 2100 | ldx #32 2101 | jsr spawn_particls3 ; X = index to sprite data 2102 | lda #1 ; init timer (this one counts up) 2103 | sta parti_set1timer 2104 | rts 2105 | 2106 | ; Initial horizontal/vertical speeds of particles. 2107 | ; Four waves in two's complement. 8 values per wave. 2108 | ; Read by spawn_particls3. 2109 | ; Values: outer rings -8...+8, inner rings -4...+4. 2110 | init_prt_spds_x ; sine wave 2111 | db 0, 6, 8, 6, 0, 256-6, 256-8, 256-6 ; outer ring 2112 | db 0, 3, 4, 3, 0, 256-3, 256-4, 256-3 ; inner ring 2113 | init_prt_spds_y ; inverted cosine wave 2114 | db 256-8, 256-6, 0, 6, 8, 6, 0, 256-6 ; outer ring 2115 | db 256-4, 256-3, 0, 3, 4, 3, 0, 256-3 ; inner ring 2116 | 2117 | spawn_particls3 ; Write initial particle data (16 sprites). 2118 | ; In: X = first index to sprite data. 2119 | ; Called by spawn_particls2. 2120 | 2121 | ldy #0 ; Y/X = source/destination index 2122 | - lda init_prt_spds_x,y ; horizontal position 2123 | add parti_start_x 2124 | sta sprite_data_x,x 2125 | lda init_prt_spds_y,y ; vertical position 2126 | add parti_start_y 2127 | sta sprite_data_y,x 2128 | lda init_prt_spds_x,y ; horizontal speed 2129 | sta parti_spds_x,x 2130 | lda init_prt_spds_y,y ; vertical speed 2131 | sta parti_spds_y,x 2132 | lda #$01 ; tile 2133 | sta sprite_data_til,x 2134 | lda #%00011010 ; attribute (palette 2) 2135 | sta sprite_data_atr,x 2136 | inx ; end of loop 2137 | iny 2138 | cpy #16 2139 | bne - 2140 | 2141 | lda #%00001110 ; make noise (TODO: what kind of?) 2142 | sta noise_regs+2 2143 | lda #%00000100 2144 | sta noise_regs+3 2145 | lda #%00100101 2146 | sta noise_regs+0 2147 | lda #24 ; set timer (this one counts down) 2148 | sta particle_timer 2149 | 2150 | rts 2151 | 2152 | ; ----------------------------------------------------------------------------- 2153 | 2154 | move_particles ; Move particles. Called by do_every_frame. 2155 | ; (There are 2 sets of particles: one from each 2nd explosion.) 2156 | 2157 | lda particle_timer ; stop noise if timer reaches zero 2158 | beq + 2159 | dec particle_timer 2160 | bne + 2161 | lda #%00110000 2162 | sta noise_regs+0 2163 | 2164 | + lda parti_set1timer ; process set 1 2165 | beq move_parti_set2 2166 | ldx #32 2167 | inc parti_set1timer 2168 | lda parti_set1timer 2169 | cmp #24 2170 | beq ++ 2171 | jsr move_particles2 ; A = timer, X = index to sprite data 2172 | 2173 | move_parti_set2 lda parti_set2timer ; process set 2 2174 | beq + 2175 | ldx #48 2176 | inc parti_set2timer 2177 | lda parti_set2timer 2178 | cmp #24 2179 | beq +++ 2180 | jsr move_particles2 ; A = timer, X = index to sprite data 2181 | + rts 2182 | 2183 | ++ lda #0 ; hide set 1 2184 | sta parti_set1timer 2185 | jsr hide_partic_set 2186 | jmp move_parti_set2 2187 | 2188 | +++ lda #0 ; hide set 2 2189 | sta parti_set2timer 2190 | jmp hide_partic_set ; ends with RTS 2191 | 2192 | move_particles2 ; Move particles, part 2. 2193 | ; In: A = timer (parti_set1timer/parti_set2timer), 2194 | ; X = sprite data index (32/48). 2195 | ; Called by move_particles. 2196 | 2197 | and #%00000111 ; timer % 8 -> temp1 2198 | sta temp1 2199 | 2200 | ldy #16 2201 | particle_loop lda sprite_data_y,x ; if sprite is hidden, skip it 2202 | cmp #255 2203 | beq particle_moved 2204 | 2205 | lda sprite_data_atr,x ; change palette 2206 | eor #%00000010 2207 | sta sprite_data_atr,x 2208 | 2209 | lda parti_spds_x,x ; detect X position underflow/overflow 2210 | bpl + 2211 | clc ; moving left 2212 | adc sprite_data_x,x 2213 | bcs ++ 2214 | jmp hide_particle 2215 | + clc ; moving right 2216 | adc sprite_data_x,x 2217 | bcc ++ 2218 | 2219 | hide_particle ; X/Y position underflow/overflow; hide sprite and move on 2220 | lda #$ff 2221 | sta sprite_data_y,x 2222 | lda #$00 2223 | sta sprite_data_atr,x 2224 | jmp particle_moved 2225 | 2226 | ++ sta sprite_data_x,x 2227 | 2228 | lda parti_spds_y,x ; detect Y position underflow/overflow 2229 | bpl + 2230 | clc ; moving up 2231 | adc sprite_data_y,x 2232 | bcs ++ 2233 | jmp hide_particle 2234 | + clc ; moving down 2235 | adc sprite_data_y,x 2236 | bcs hide_particle 2237 | 2238 | ++ sta sprite_data_y,x 2239 | 2240 | ; slow particle down every 8th frame (temp1 = timer % 8) 2241 | lda temp1 2242 | bne particle_moved 2243 | lda parti_spds_x,x 2244 | jsr dec_abs_value ; A: value to decrement 2245 | sta parti_spds_x,x 2246 | lda parti_spds_y,x 2247 | jsr dec_abs_value ; A: value to decrement 2248 | sta parti_spds_y,x 2249 | 2250 | particle_moved inx 2251 | dey 2252 | bne particle_loop 2253 | rts 2254 | 2255 | dec_abs_value ; If number is nonzero, decrement absolute value in 2's 2256 | ; complement. 2257 | ; In: A = number to decrement; N & Z flags must reflect it. 2258 | ; Called by move_particles2. 2259 | beq ++ 2260 | bpl + 2261 | add #1 2262 | rts 2263 | + sub #1 2264 | ++ rts 2265 | 2266 | hide_partic_set ; Hide one set of particles (16 sprites starting from X). 2267 | ; Called by move_particles. 2268 | ldy #16 2269 | - lda #%00000000 2270 | sta sprite_data_atr,x 2271 | lda #255 2272 | sta sprite_data_y,x 2273 | inx 2274 | dey 2275 | bne - 2276 | rts 2277 | 2278 | ; ----------------------------------------------------------------------------- 2279 | 2280 | spawn_particls1 ; If revolving cursor is on a letter, continue to prepare 2281 | ; particles. 2282 | ; In: X/Y = position of letter in tiles. 2283 | ; Called by check_button_b. 2284 | 2285 | tya ; push Y 2286 | pha 2287 | asl a ; Y * 8 + 10 -> parti_start_y 2288 | asl a 2289 | asl a 2290 | add #10 2291 | sta parti_start_y 2292 | 2293 | txa ; push X 2294 | pha 2295 | sub #4 ; (X - 4) * 8 + 13 -> parti_start_x 2296 | asl a 2297 | asl a 2298 | asl a 2299 | add #13 2300 | sta parti_start_x 2301 | 2302 | ; spawn only if on a letter (why not check earlier?) 2303 | jsr get_letter 2304 | beq + 2305 | jsr spawn_particls2 2306 | + pla ; pull X, Y 2307 | tax 2308 | pla 2309 | tay 2310 | 2311 | rts 2312 | 2313 | ; ----------------------------------------------------------------------------- 2314 | 2315 | save_letter ; Save entered letter. 2316 | ; In: A = letter. 2317 | ; Called by letter_input, check_button_b. 2318 | pha 2319 | jsr get_revolv_pos ; 0-23 -> X 2320 | pla 2321 | sta entered_letrs,x 2322 | rts 2323 | 2324 | ; ----------------------------------------------------------------------------- 2325 | 2326 | fix_revolv_x ; Move revolving cursor left until it lies immediately after a 2327 | ; letter, on a letter or on the first column. 2328 | ; Out: X = letter position to highlight (index to 2329 | ; entered_letrs), revolv_x_letr1. 2330 | ; Called by check_button_b, move_revolv_man. 2331 | 2332 | jsr get_revolv_pos ; 0-23 -> X 2333 | lda revolv_x_letr1 ; exit if cursor at column 0 or letter 2334 | beq + 2335 | lda entered_letrs,x 2336 | bne + 2337 | ; 2338 | - dex ; move cursor left until at column 0 or 2339 | lda entered_letrs,x ; a letter 2340 | bne + 2341 | dec revolv_x_letr1 2342 | bne - 2343 | + rts 2344 | 2345 | ; ----------------------------------------------------------------------------- 2346 | 2347 | get_letter ; Get letter at revolving cursor. 2348 | ; Out: A = letter, X = cursor position (0-23), zero flag 2349 | ; reflects A. 2350 | ; Called by check_button_b, spawn_particls1. 2351 | jsr get_revolv_pos 2352 | lda entered_letrs,x 2353 | rts 2354 | 2355 | ; ----------------------------------------------------------------------------- 2356 | 2357 | get_revolv_pos ; Get position of revolving cursor. 2358 | ; Out: X = cursor position (0-23). 2359 | ; Called by save_letter, fix_revolv_x, get_letter. 2360 | lda revolv_y_letr1 ; revolv_y_letr1 * 8 + revolv_x_letr1 2361 | asl a 2362 | asl a 2363 | asl a 2364 | add revolv_x_letr1 2365 | tax 2366 | rts 2367 | 2368 | ; ----------------------------------------------------------------------------- 2369 | 2370 | input_effects ; Letter input extra effects: spawn flying letter and play 2371 | ; sound. 2372 | ; Called by letter_input. 2373 | 2374 | ; hand_x_letr * 32 -> flying_x, metasprite_x 2375 | lda hand_x_letr 2376 | asl a 2377 | asl a 2378 | asl a 2379 | asl a 2380 | asl a 2381 | sta flying_x 2382 | sta metasprite_x 2383 | 2384 | ; hand_y_letr * 8 -> stack 2385 | ; hand_y_letr * 32 + 64 -> flying_y, metasprite_y 2386 | lda hand_y_letr 2387 | asl a 2388 | asl a 2389 | asl a 2390 | pha 2391 | asl a 2392 | asl a 2393 | add #64 2394 | sta flying_y 2395 | sta metasprite_y 2396 | 2397 | ; hand_y_letr * 8 + hand_x_letr -> entered_letr 2398 | ; (for playing sound) 2399 | pla 2400 | add hand_x_letr 2401 | staa entered_letr 2402 | 2403 | ; spawn flying letter graphic; 2404 | ; A = id = hand_y_letr * 8 + hand_x_letr + 3; 2405 | ; X = index to metaspr_indexes 2406 | adc #3 2407 | ldx flying_metaspr 2408 | jsr init_metasprite 2409 | 2410 | ldx flying_metaspr 2411 | jsr update_metaspr ; X = metasprite index 2412 | 2413 | ; compute Y speed of flying letter (always positive): 2414 | ; floor((revolv_y_letr1 * 32 + 144 - flying_y) / 16) 2415 | ; -> flying_y_spd 2416 | lda revolv_y_letr1 2417 | asl a 2418 | asl a 2419 | asl a 2420 | asl a 2421 | asl a 2422 | add #144 2423 | sub flying_y 2424 | lsr a 2425 | lsr a 2426 | lsr a 2427 | lsr a 2428 | sta flying_y_spd 2429 | 2430 | ; compute X speed of flying letter (signed): 2431 | ; (revolv_x_letr1 - hand_x_letr) * 2 -> flying_x_spd 2432 | lda revolv_x_letr1 2433 | sub hand_x_letr 2434 | asl a 2435 | sta flying_x_spd 2436 | 2437 | lda #16 2438 | sta flying_timelft1 2439 | 2440 | ; play sound depending on letter (TODO: what kind of?) 2441 | ; bits of entered_letr: 0000ABCD 2442 | ldaa entered_letr 2443 | asl a 2444 | asl a 2445 | asl a 2446 | asl a 2447 | sta temp1 2448 | lda #0 2449 | asl temp1 2450 | rol a 2451 | asl temp1 2452 | rol a ; bits: 000000AB 2453 | add #2 ; bits: 00000A1B 2454 | sta pulse1_regs+3 2455 | lda temp1 ; bits: CD000000 2456 | sta pulse1_regs+2 2457 | lda #%00100100 2458 | sta pulse1_regs+0 2459 | lda #%11111001 2460 | sta pulse1_regs+1 2461 | 2462 | lda #20 2463 | staa flying_timelft2 2464 | 2465 | rts 2466 | 2467 | ; ----------------------------------------------------------------------------- 2468 | 2469 | move_flying_ltr ; Move flying letter. Called by do_every_frame. 2470 | 2471 | lda flying_timelft2 2472 | beq + 2473 | dec flying_timelft2 2474 | bne + 2475 | 2476 | lda #%00110000 ; flying_timelft2 went from 1 to 0 2477 | sta pulse1_regs+0 2478 | + lda flying_timelft1 2479 | bne + 2480 | rts 2481 | 2482 | + dec flying_timelft1 2483 | bne + 2484 | ldx flying_metaspr ; flying_timelft went from 1 to 0 2485 | lda #255 2486 | sta metasprite_y 2487 | jmp update_metaspr ; X = metasprite index; ends with RTS 2488 | 2489 | + lda flying_x ; update flying letter X position 2490 | add flying_x_spd 2491 | sta flying_x 2492 | sta metasprite_x 2493 | lda flying_y ; update flying letter Y position 2494 | add flying_y_spd 2495 | sta flying_y 2496 | sta metasprite_y 2497 | 2498 | ldx flying_metaspr 2499 | jmp update_metaspr ; X = metasprite index; ends with RTS 2500 | 2501 | ; ----------------------------------------------------------------------------- 2502 | 2503 | fill_atrblkrows ; Fill rows of blocks in attribute table. 2504 | ; In: A = fill byte, X = number of rows, Y = first row. 2505 | ; Called by init_background. 2506 | 2507 | sta attr_fill_byte 2508 | sty vram_block_y 2509 | stx rows_left 2510 | 2511 | -- lda #$00 2512 | - sta vram_block_x 2513 | jsr update_attr_blk 2514 | lda vram_block_x 2515 | add #1 2516 | cmp #16 2517 | bne - 2518 | inc vram_block_y 2519 | dec rows_left 2520 | bne -- 2521 | rts 2522 | 2523 | ; ----------------------------------------------------------------------------- 2524 | 2525 | animate_color ; Animate color 3 of background subpalette 1. 2526 | ; Called by do_every_frame. 2527 | 2528 | ; increment anim_colr_delay every frame 2529 | ; increment anim_colr_phase every 5th frame 2530 | ldx anim_colr_phase 2531 | ldy anim_colr_delay 2532 | iny 2533 | cpy #5 2534 | bne + 2535 | ldy #0 2536 | inx 2537 | + sty anim_colr_delay 2538 | cpx #8 2539 | bne + 2540 | ldx #0 2541 | + stx anim_colr_phase 2542 | 2543 | ; set up VRAM block (data size 1) and copy it to VRAM buffer 2544 | lda animated_colors,x 2545 | sta vram_block+3 2546 | lda #1 2547 | sta vram_block+0 2548 | lda #>(vram_palette+4+3) 2549 | sta vram_block+1 2550 | lda #<(vram_palette+4+3) 2551 | sta vram_block+2 2552 | jmp vram_blk_to_buf ; ends with RTS 2553 | 2554 | animated_colors ; read by animate_color 2555 | hex 21 2c 2b 28 ; sky blue, cyan, green, yellow 2556 | hex 27 25 24 2c ; orange, pink, purple, cyan 2557 | 2558 | ; ----------------------------------------------------------------------------- 2559 | 2560 | hilite_inp_area ; Move highlight to another letter on input area. 2561 | ; In: revolv_x_letr1, revolv_x_letr2, revolv_y_letr1, 2562 | ; revolv_y_letr2. 2563 | ; Called by letter_input, check_button_b, move_revolv_man. 2564 | 2565 | ; set attribute %10 to the letter the revolving cursor exits; 2566 | ; store old Y position 2567 | ldx revolv_x_letr2 2568 | ldy revolv_y_letr2 2569 | sty revolv_y_ltr2pr 2570 | lda #%10101010 2571 | jsr update_atr_byte ; A = byte, X = X, Y = Y 2572 | 2573 | lda revolv_y_letr1 ; update position 2574 | add #2 2575 | sta revolv_y_letr2 2576 | lda revolv_x_letr1 2577 | sta revolv_x_letr2 2578 | 2579 | jsr hilite_inp_row 2580 | 2581 | ; set attribute %01 to the letter the revolving cursor enters 2582 | ldx revolv_x_letr2 2583 | ldy revolv_y_letr2 2584 | lda #%01010101 2585 | jmp update_atr_byte ; A = byte, X/Y = pos; ends with RTS 2586 | 2587 | inparearow_atrs ; Attribute table data to write when entering a row on input 2588 | ; area (16 bytes). 2589 | ; Read by hilite_inp_row. 2590 | hex af af af af af af af af ; 8 * %10101111 2591 | hex fa fa fa fa fa fa fa fa ; 8 * %11111010 2592 | 2593 | hilite_inp_row ; Highlight active row on input area using attribute table. 2594 | ; In: revolv_y_letr2, revolv_y_ltr2pr. 2595 | ; Called by hilite_inp_area. 2596 | 2597 | lda revolv_y_ltr2pr ; exit if Y position of revolving 2598 | cmp revolv_y_letr2 ; cursor unchanged 2599 | beq hilite_exit 2600 | 2601 | ; set up a VRAM block to change attribute data of all rows to 2602 | ; gray (data: 32 * %11111111) and copy block to VRAM buffer 2603 | lda #>(vram_at0+4*8) 2604 | sta vram_block+1 2605 | lda #<(vram_at0+4*8) 2606 | sta vram_block+2 2607 | ldy #(4*8-1) 2608 | - lda #%11111111 2609 | sta vram_block+3,y 2610 | sta attr_data_copy+4*8,y 2611 | dey 2612 | bpl - 2613 | lda #32 2614 | sta vram_block+0 2615 | jsr vram_blk_to_buf 2616 | 2617 | ; set up a VRAM block to change attribute data of active row to 2618 | ; white (%10) and copy block to VRAM buffer; 2619 | ; address: vram_at0 + (2 + revolv_y_letr2) * 8; 2620 | ; data: 16 bytes from inparearow_atrs 2621 | lda revolv_y_letr2 2622 | sub #2 2623 | asl a 2624 | asl a 2625 | asl a 2626 | add #<(vram_at0+4*8) 2627 | sta vram_block+2 2628 | lda #>(vram_at0+4*8) 2629 | sta vram_block+1 2630 | ldy #(2*8-1) 2631 | - lda inparearow_atrs,y 2632 | sta vram_block+3,y 2633 | dey 2634 | bpl - 2635 | lda #16 2636 | sta vram_block+0 2637 | jsr vram_blk_to_buf 2638 | 2639 | hilite_exit rts 2640 | 2641 | ; ----------------------------------------------------------------------------- 2642 | 2643 | check_sel_start ; If select or start pressed and allowed, decode entered codes 2644 | ; and start the game. 2645 | ; Called by do_every_frame. 2646 | 2647 | ; If neither button pressed, allow them next time and exit. 2648 | ; If either pressed but not allowed, just exit. 2649 | ; If either pressed and allowed, continue. 2650 | lda joypad1_status 2651 | and #pad_st_sel 2652 | bne + 2653 | lda #1 ; allow them 2654 | sta temp2 2655 | - rts 2656 | + lda temp2 ; allow them? 2657 | beq - 2658 | 2659 | lda #%00000000 ; disable NMI and rendering 2660 | sta ppu_ctrl 2661 | sta ppu_mask 2662 | lda #entered_letrs 2665 | sta code_ptr+1 2666 | lda #decoded_codes 2669 | sta decoded_cod_ptr+1 2670 | ; 2671 | ldx #(16-1) ; fill decoded_codes with $ff (why 16 2672 | lda #$ff ; bytes?) 2673 | - sta decoded_codes,x 2674 | dex 2675 | bpl - 2676 | lda #3 ; how many codes to decode 2677 | sta codes_left 2678 | 2679 | lda #%11101111 ; AND bitmask to enable 1st code in 2680 | sta code_enab_mask ; genie_ctrl_val 2681 | 2682 | lda #%00000010 ; OR bitmask to enable compare value of 2683 | sta comp_enab_mask ; 1st code in genie_ctrl_val 2684 | 2685 | ; value to write to genie_ctrl; 2686 | ; bits: 0CBAcbaG (A/B/C = disable code, a/b/c = enable compare 2687 | ; value, G = switch to game mode); 2688 | ; start with all codes disabled 2689 | lda #%01110001 2690 | sta genie_ctrl_val 2691 | 2692 | decode_allcodes ; the start the long outer loop 2693 | ; bit 3 of temp2 = least significant bit of previous letter 2694 | ldy #0 2695 | sty temp2 2696 | 2697 | ; Phase 1/2 of decoding: modify the letters of one code: 2698 | ; - if 0 (no letter), exit loop 2699 | ; - subtract 3 to get a 4-bit value 2700 | ; - shift right once 2701 | ; - copy least significant bit of previous letter to bit 3 of 2702 | ; this letter 2703 | 2704 | - lda (code_ptr),y 2705 | beq + 2706 | sub #3 2707 | and #%00001111 2708 | lsr a 2709 | ; 2710 | ora temp2 ; bit 3 = least significant bit of 2711 | sta (code_ptr),y ; previous letter 2712 | ; 2713 | lda #$00 ; store the bit that was just shifted 2714 | rol a ; out to bit 3 of temp2 2715 | asl a 2716 | asl a 2717 | asl a 2718 | sta temp2 2719 | iny 2720 | cpy #8 2721 | bne - 2722 | 2723 | + sty code_length ; if code isn't 6/8 letters, ignore it 2724 | cpy #8 2725 | beq + 2726 | cpy #6 2727 | beq ++ 2728 | jmp next_code 2729 | 2730 | + ldy #3 ; regardless of code length, read 4th 2731 | lda (code_ptr),y ; value and discard it 2732 | jmp +++ 2733 | ++ ldy #3 2734 | lda (code_ptr),y 2735 | 2736 | +++ ; copy the bit that was shifted out from the last value, 2737 | ; to bit 3 of 1st value; thus, the values will have been 2738 | ; rotated instead of shifted (temp2 = LSB of previous letter) 2739 | ldy #0 2740 | lda (code_ptr),y 2741 | and #%00000111 2742 | ora temp2 2743 | sta (code_ptr),y 2744 | 2745 | ; Phase 2/2 of decoding: copy 8 nybbles from one semi-decoded 2746 | ; code to 4 bytes in decoded_code according to code_key. 2747 | ldx #0 ; X = source nybble offset 2748 | stx temp2 ; temp2 = target byte offset 2749 | - ldy code_key,x ; get high nybble 2750 | lda (code_ptr),y 2751 | asl a 2752 | asl a 2753 | asl a 2754 | asl a 2755 | inx ; get low nybble 2756 | ldy code_key,x 2757 | ora (code_ptr),y 2758 | ldy temp2 ; store byte 2759 | sta decoded_code,y 2760 | inc temp2 2761 | inx 2762 | cpx #8 2763 | bne - 2764 | 2765 | lda decoded_code+0 ; clear MSB of address 2766 | and #%01111111 2767 | sta decoded_code+0 2768 | 2769 | ; ignore code if address matches any previously-decoded code 2770 | lda decoded_code+0 ; A/X = address high/low 2771 | ldx decoded_code+1 2772 | cmp decoded_codes+0 ; ignore if matches 1st code 2773 | bne + 2774 | cpx decoded_codes+1 2775 | beq next_code 2776 | + cmp decoded_codes+4 ; ignore if matches 2nd code 2777 | bne + 2778 | cpx decoded_codes+4+1 2779 | beq next_code 2780 | + cmp decoded_codes+2*4 ; ignore if matches 3rd code 2781 | bne + ; (useless; it's always $ffff) 2782 | cpx decoded_codes+2*4+1 2783 | beq next_code 2784 | 2785 | + ; store code to decoded_codes (note: replace and compare value 2786 | ; trade places) 2787 | ldy #1 2788 | - lda decoded_code,y 2789 | sta (decoded_cod_ptr),y ; address 2790 | dey 2791 | bpl - 2792 | ldy #3 ; replace value 2793 | lda decoded_code-1,y 2794 | sta (decoded_cod_ptr),y 2795 | dey ; compare value 2796 | lda decoded_code+1,y 2797 | sta (decoded_cod_ptr),y 2798 | 2799 | ; enable code by ANDing genie_ctrl_val with current 2800 | ; code_enab_mask; if code is 8 letters, also enable compare 2801 | ; value by ORing genie_ctrl_val with current comp_enab_mask 2802 | lda genie_ctrl_val 2803 | and code_enab_mask 2804 | ldx code_length 2805 | cpx #8 2806 | bne + 2807 | ora comp_enab_mask 2808 | + sta genie_ctrl_val 2809 | 2810 | next_code sec ; prepare for next code by shifting 2811 | rol code_enab_mask ; bitmasks left 2812 | asl comp_enab_mask 2813 | 2814 | lda code_ptr+0 ; advance source pointer 2815 | add #8 ; (high byte never increments) 2816 | sta code_ptr+0 2817 | bcc + 2818 | inc code_ptr+1 2819 | 2820 | + lda decoded_cod_ptr+0 ; advance target pointer (high byte 2821 | add #4 ; never increments) 2822 | sta decoded_cod_ptr+0 2823 | bcc + 2824 | inc decoded_cod_ptr+1 2825 | 2826 | + dec codes_left ; the end of the long outer loop 2827 | beq + 2828 | jmp decode_allcodes 2829 | 2830 | + ; copy a short program to RAM (and two extra bytes for some 2831 | ; reason) 2832 | ldx #(code_key-ram_program+2-1) 2833 | - lda ram_program,x 2834 | sta ram_prog_target,x 2835 | dex 2836 | bpl - 2837 | 2838 | jmp ram_prog_target ; execute program in RAM 2839 | 2840 | ram_program ; a short program that is copied to RAM and executed; 2841 | ; copies decoded codes to Game Genie registers $8001-$800c 2842 | ldx #(3*4-1) 2843 | - lda decoded_codes,x 2844 | sta genie_codes,x 2845 | dex 2846 | bpl - 2847 | ; switch to game mode with correct codes enabled 2848 | lda genie_ctrl_val 2849 | sta genie_ctrl 2850 | lda #%00000000 ; for unknown reason 2851 | sta genie_ctrl 2852 | jmp (reset_vector) ; reset system 2853 | 2854 | code_key ; how to descramble codes; read by check_sel_start 2855 | db 3, 5, 2, 4, 1, 0, 7, 6 2856 | 2857 | ; ----------------------------------------------------------------------------- 2858 | 2859 | macro offs_to_graphic _addr 2860 | ; emit 16-bit offset relative to graphix_offsets, high byte 2861 | ; first 2862 | db >(_addr-graphix_offsets) 2863 | db <(_addr-graphix_offsets) 2864 | endm 2865 | 2866 | graphix_offsets ; Offsets to actual graphics data (see below). 2 bytes each, 2867 | ; high byte first. 2868 | ; Read indirectly by set_graphix_ptr using graphics_ptr. 2869 | 2870 | offs_to_graphic graphic_unused ; 0 (never accessed) 2871 | offs_to_graphic graphic_logo ; 1 2872 | offs_to_graphic graphic_revolv ; 2 2873 | offs_to_graphic graphic_letr_a ; 3 2874 | offs_to_graphic graphic_letr_e ; 4 2875 | offs_to_graphic graphic_letr_p ; 5 2876 | offs_to_graphic graphic_letr_o ; 6 2877 | offs_to_graphic graphic_letr_z ; 7 2878 | offs_to_graphic graphic_letr_x ; 8 2879 | offs_to_graphic graphic_letr_l ; 9 2880 | offs_to_graphic graphic_letr_u ; 10 2881 | offs_to_graphic graphic_letr_g ; 11 2882 | offs_to_graphic graphic_letr_k ; 12 2883 | offs_to_graphic graphic_letr_i ; 13 2884 | offs_to_graphic graphic_letr_s ; 14 2885 | offs_to_graphic graphic_letr_t ; 15 2886 | offs_to_graphic graphic_letr_v ; 16 2887 | offs_to_graphic graphic_letr_y ; 17 2888 | offs_to_graphic graphic_letr_n ; 18 2889 | offs_to_graphic graphic_dash ; 19 2890 | offs_to_graphic graphic_hand ; 20 2891 | 2892 | ; The actual graphics data. 2893 | ; Read indirectly using graphics_ptr. 2894 | ; Read by draw_bg_graphic, tile_to_vrambuf, draw_metaspr_gr, 2895 | ; init_metasprite. 2896 | ; 2897 | ; Format of each graphic: 2898 | ; 1 byte: width in tiles 2899 | ; 1 byte: height in tiles 2900 | ; width * height / 2 bytes: data: 2901 | ; 1 nybble = 1 tile 2902 | ; bits in each nybble (3 = most significant) represent 2903 | ; 2*2 virtual pixels: 2904 | ; 23 2905 | ; 01 2906 | 2907 | graphic_unused db 8, 2 ; not a valid graphic (never accessed) 2908 | db %00000001, %00100011, %01000101, %01100111 2909 | graphic_logo db 30, 4 ; "Game Genie" logo 2910 | ; line 0 start 2911 | db %00101100, %11000000, %00000000, %00000000, %00000000 2912 | db %00000000, %00000000, %00000010, %11001100, %00000000 2913 | db %00000000, %00000000, %00001000, %00000000, %00000000 2914 | ; line 1 start 2915 | db %01010010, %00110010, %11001100, %01011101, %10010110 2916 | db %00101100, %11100100, %00000101, %00100011, %00101100 2917 | db %11100100, %11011100, %01101010, %00101100, %11100100 2918 | ; line 2 start 2919 | db %01100000, %00101010, %00000000, %01010101, %01011010 2920 | db %10100000, %01000000, %00000110, %00000010, %10100000 2921 | db %01000000, %01010000, %10101010, %10100000, %01000000 2922 | ; line 3 start 2923 | db %00001100, %01000000, %11000000, %01000100, %01001000 2924 | db %00001100, %11000100, %00000000, %11000100, %00001100 2925 | db %11000100, %01000000, %10001000, %00001100, %11000100 2926 | graphic_revolv db 2, 2 ; revolving cursor 2927 | db %10110001 ; line 0 2928 | db %10000000 ; line 1 2929 | graphic_letr_a db 4, 4 ; "A" 2930 | db %00001010, %00000000 ; line 0 2931 | db %00000101, %01010000 ; line 1 2932 | db %10101100, %11100000 ; line 2 2933 | db %11000100, %11000100 ; line 3 2934 | graphic_letr_e db 4, 4 ; "E" 2935 | db %10001101, %11100000 ; line 0 2936 | db %00000111, %00010000 ; line 1 2937 | db %00000101, %00100000 ; line 2 2938 | db %10001100, %11000000 ; line 3 2939 | graphic_letr_p db 4, 4 ; "P" 2940 | db %10001101, %01100000 ; line 0 2941 | db %00000111, %10010000 ; line 1 2942 | db %00000101, %00000000 ; line 2 2943 | db %10001100, %00000000 ; line 3 2944 | graphic_letr_o db 4, 4 ; "O" 2945 | db %00001001, %01100000 ; line 0 2946 | db %10100000, %00000101 ; line 1 2947 | db %10000001, %00100100 ; line 2 2948 | db %00001000, %01000000 ; line 3 2949 | graphic_letr_z db 4, 4 ; "Z" 2950 | db %10101100, %11100000 ; line 0 2951 | db %00000010, %01000000 ; line 1 2952 | db %00100100, %00100000 ; line 2 2953 | db %10001100, %11000000 ; line 3 2954 | graphic_letr_x db 4, 4 ; "X" 2955 | db %11100100, %11100100 ; line 0 2956 | db %00000110, %01000000 ; line 1 2957 | db %00100100, %01100000 ; line 2 2958 | db %11000100, %11000100 ; line 3 2959 | graphic_letr_l db 4, 4 ; "L" 2960 | db %10001101, %00000000 ; line 0 2961 | db %00000101, %00000000 ; line 1 2962 | db %00000101, %00100000 ; line 2 2963 | db %10001100, %11000000 ; line 3 2964 | graphic_letr_u db 4, 4 ; "U" 2965 | db %11100100, %11100100 ; line 0 2966 | db %10100000, %10100000 ; line 1 2967 | db %10100000, %10100000 ; line 2 2968 | db %00001100, %01000000 ; line 3 2969 | graphic_letr_g db 4, 4 ; "G" 2970 | db %00001001, %11000101 ; line 0 2971 | db %10100000, %00110001 ; line 1 2972 | db %10000001, %00000101 ; line 2 2973 | db %00001000, %11000000 ; line 3 2974 | graphic_letr_k db 4, 4 ; "K" 2975 | db %10001101, %10100100 ; line 0 2976 | db %00000111, %01000000 ; line 1 2977 | db %00000101, %01100000 ; line 2 2978 | db %10001100, %10000100 ; line 3 2979 | graphic_letr_i db 4, 4 ; "I" 2980 | db %00001110, %01000000 ; line 0 2981 | db %00001010, %00000000 ; line 1 2982 | db %00001010, %00000000 ; line 2 2983 | db %00001100, %01000000 ; line 3 2984 | graphic_letr_s db 4, 4 ; "S" 2985 | db %00101100, %01110000 ; line 0 2986 | db %10000111, %00010000 ; line 1 2987 | db %00100000, %11100000 ; line 2 2988 | db %00001100, %01000000 ; line 3 2989 | graphic_letr_t db 4, 4 ; "T" 2990 | db %10101110, %11100000 ; line 0 2991 | db %00001010, %00000000 ; line 1 2992 | db %00001010, %00000000 ; line 2 2993 | db %00001100, %01000000 ; line 3 2994 | graphic_letr_v db 4, 4 ; "V" 2995 | db %11100100, %11100100 ; line 0 2996 | db %10000001, %10010000 ; line 1 2997 | db %00000110, %01000000 ; line 2 2998 | db %00001000, %00000000 ; line 3 2999 | graphic_letr_y db 4, 4 ; "Y" 3000 | db %11100100, %11100100 ; line 0 3001 | db %00000110, %01000000 ; line 1 3002 | db %00001010, %00000000 ; line 2 3003 | db %00001100, %01000000 ; line 3 3004 | graphic_letr_n db 4, 4 ; "N" 3005 | db %11100000, %11100100 ; line 0 3006 | db %10100110, %10100000 ; line 1 3007 | db %10100000, %11100000 ; line 2 3008 | db %11000100, %10000000 ; line 3 3009 | graphic_dash db 4, 4 ; "-" (placeholder for input area) 3010 | db %00000000, %00000000 ; line 0 3011 | db %00100011, %00110001 ; line 1 3012 | db %00000000, %00000000 ; line 2 3013 | db %00000000, %00000000 ; line 3 3014 | graphic_hand ; hand cursor (the only graphic with odd width) 3015 | db 5, 4 3016 | ; lines 0 & 1 3017 | db %00000000, %01110000, %00000011, %00111011, %01110001 3018 | ; lines 2 & 3 3019 | db %00001110, %11111111, %01010000, %00001100, %11000100 3020 | 3021 | ; never accessed (partial duplicate of hand cursor) 3022 | hex 03 3b 71 0e ff 50 0c c4 00 00 00 00 00 00 00 00 00 00 00 aa 3023 | 3024 | ; --- Interrupt vectors ------------------------------------------------------- 3025 | 3026 | pad $fffa, $ff 3027 | dw nmi ; NMI 3028 | reset_vector dw init1 ; reset 3029 | dw $ffff ; IRQ (never accessed) 3030 | --------------------------------------------------------------------------------