crt0.s (13278B)
1 .include "global.s" 2 3 ;; **************************************** 4 ;; Beginning of module 5 ;; BANKED: checked 6 .title "Runtime" 7 .module Runtime 8 .area _HEADER (ABS) 9 10 ;; RST vectors 11; .org 0x00 ; Trap, utilized by crash_handler.h 12 13; .org 0x08 ; --profile handler utilized by emu_debug.h 14 15; .org 0x10 ; empty 16 17; .org 0x18 ; empty 18 19 .org 0x20 ; RST 0x20 == call HL 20.call_hl:: 21 JP (HL) 22 23 .org 0x28 ; zero up to 256 bytes in C pointed by HL 24.MemsetSmall:: 25 LD (HL+),A 26 DEC C 27 JR NZ,.MemsetSmall 28 ret 29 30 .org 0x30 ; copy up to 256 bytes in C from DE to HL 31.MemcpySmall:: 32 LD A, (DE) 33 LD (HL+), A 34 INC DE 35 DEC C 36 JR NZ,.MemcpySmall 37 RET 38 39; .org 0x38 ; crash handler utilized by crash_handler.h 40 41 ;; Hardware interrupt vectors 42 .org 0x40 ; VBL 43.int_VBL: 44 PUSH AF 45 PUSH HL 46 LD HL,#.int_0x40 47 JP .int 48 49; .org 0x48 ; LCD 50 51; .org 0x50 ; TIM 52 53; .org 0x58 ; SIO 54 55; .org 0x60 ; JOY 56 57; .org 0x70 58 ;; space for drawing.s bit table 59 60 .org 0x80 61.int:: 62 PUSH BC 63 PUSH DE 641$: 65 LD A,(HL+) 66 OR (HL) 67 JR Z,.int_tail 68 PUSH HL 69 LD A,(HL-) 70 LD L,(HL) 71 LD H,A 72 RST 0x20 ; .call_hl 73 POP HL 74 INC HL 75 JR 1$ 76_wait_int_handler:: 77 ADD SP,#4 78.int_tail: 79 POP DE 80 POP BC 81 POP HL 82 83 ;; we return at least at the beginning of mode 2 84 WAIT_STAT 85 86 POP AF 87 RETI 88 89 ;; VBlank default interrupt routine 90__standard_VBL_handler:: 91.std_vbl: 92 LD HL,#.sys_time 93 INC (HL) 94 JR NZ,2$ 95 INC HL 96 INC (HL) 972$: 98 CALL .refresh_OAM 99 100 LD A, #1 101 LDH (.vbl_done),A 102 RET 103 104_refresh_OAM:: 105 WAIT_STAT 106 LD A, #>_shadow_OAM 107 JP .refresh_OAM + (.refresh_OAM_DMA - .start_refresh_OAM) 108 109.clear_WRAM: 110 PUSH DE 111 XOR A 112 LD BC, #l__DATA 113 LD HL, #s__DATA 114 CALL .memset_simple 115 116 LD A, #>_shadow_OAM 117 LDH (__shadow_OAM_base), A 118 LD H, A 119 XOR A 120 LD L, A 121 LD C, #(40 << 2) ; 40 entries 4 bytes each 122 RST 0x28 123 POP DE 124 RET 125 126 ;; GameBoy Header 127 128 ;; DO NOT CHANGE... 129 .org 0x100 130.header: 131 JR .code_start 132 133 ;; Nintendo logo 134 .org 0x104 135 .byte 0xCE,0xED,0x66,0x66 136 .byte 0xCC,0x0D,0x00,0x0B 137 .byte 0x03,0x73,0x00,0x83 138 .byte 0x00,0x0C,0x00,0x0D 139 .byte 0x00,0x08,0x11,0x1F 140 .byte 0x88,0x89,0x00,0x0E 141 .byte 0xDC,0xCC,0x6E,0xE6 142 .byte 0xDD,0xDD,0xD9,0x99 143 .byte 0xBB,0xBB,0x67,0x63 144 .byte 0x6E,0x0E,0xEC,0xCC 145 .byte 0xDD,0xDC,0x99,0x9F 146 .byte 0xBB,0xB9,0x33,0x3E 147 148 ;; Title of the game 149 .org 0x134 150 .asciz "Title" 151 152 .org 0x144 153 .byte 0,0,0 154 155 ;; Cartridge type is ROM only 156 .org 0x147 157 .byte 0 158 159 ;; ROM size is 32kB 160 .org 0x148 161 .byte 0 162 163 ;; RAM size is 0kB 164 .org 0x149 165 .byte 0 166 167 ;; Maker ID 168 .org 0x14A 169 .byte 0x00,0x00 170 171 ;; Version number 172 .org 0x14C 173 .byte 0x01 174 175 ;; Complement check 176 .org 0x14D 177 .byte 0x00 178 179 ;; Checksum 180 .org 0x14E 181 .byte 0x00,0x00 182 183 ;; **************************************** 184 .org 0x150 185 186 ;; soft reset: falldown to .code_start 187.reset:: 188_reset:: 189 LD A, (__is_GBA) 190 LD B, A 191 LD A, (__cpu) 192 193 ;; Initialization code 194.code_start:: 195 DI ; Disable interrupts 196 LD D, A ; Store CPU type in D 197 LD E, B ; Store GBA flag in E 198 ;; Initialize the stack 199 LD SP, #.STACK 200 201 CALL .clear_WRAM 202 203; LD (.mode),A ; Clearing (.mode) is performed when clearing RAM 204 205 ;; Store CPU type 206 LD A, D 207 LD (__cpu), A 208 CP #.CGB_TYPE 209 JR NZ, 1$ 210 XOR A 211 SRL E 212 RLA 213 LD (__is_GBA), A 2141$: 215 ;; Turn the screen off 216 CALL .display_off 217 218 XOR A 219 ;; Initialize the display 220 LDH (.SCY),A 221 LDH (.SCX),A 222 LDH (.STAT),A 223 LDH (.WY),A 224 LD A,#0x07 225 LDH (.WX),A 226 227 ;; Copy refresh_OAM routine to HRAM 228 LD DE,#.start_refresh_OAM ; source 229 LD HL,#.refresh_OAM ; dest 230 LD C,#(.end_refresh_OAM - .start_refresh_OAM) ; size 231 RST 0x30 ; call .MemcpySmall 232 233 ;; Clear the OAM by calling refresh_OAM 234 CALL .refresh_OAM 235 236 ;; Install interrupt routines 237 LD BC,#.std_vbl 238 CALL .add_VBL 239 240 ;; Standard color palettes 241 LD A,#0b11100100 ; Grey 3 = 11 (Black) 242 ; Grey 2 = 10 (Dark grey) 243 ; Grey 1 = 01 (Light grey) 244 ; Grey 0 = 00 (Transparent) 245 LDH (.BGP),A 246 LDH (.OBP0),A 247 LD A,#0b00011011 248 LDH (.OBP1),A 249 250 ;; Turn the screen on 251 LD A,#(LCDCF_ON | LCDCF_WIN9C00 | LCDCF_WINOFF | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_BGOFF) 252 LDH (.LCDC),A 253 XOR A 254 LDH (.IF),A 255 LD A,#.VBL_IFLAG ; switch on VBlank interrupt only 256 LDH (.IE),A 257 258 LDH (__current_bank),A ; current bank is 1 at startup 259 260 XOR A 261 262 LD HL,#.sys_time 263 LD (HL+),A 264 LD (HL),A 265 266 LDH (.NR52),A ; Turn sound off 267 268 CALL gsinit 269 270 EI ; Enable interrupts 271 272 ;; Call the main function 273 CALL _main 274_exit:: 27599$: 276 HALT 277 NOP 278 JR 99$ ; Wait forever 279 280_set_interrupts:: 281 DI 282 LDA HL,2(SP) ; Skip return address 283 XOR A 284 LDH (.IF),A ; Clear pending interrupts 285 LD A,(HL) 286 EI ; Enable interrupts 287 LDH (.IE),A ; interrupts are still disabled here 288 RET 289 290 ;; Copy OAM data to OAM RAM 291.start_refresh_OAM: 292 LDH A,(__shadow_OAM_base) 293 OR A 294 RET Z 295.refresh_OAM_DMA: 296 LDH (.DMA),A ; Put A into DMA registers 297 LD A,#0x28 ; We need to wait 160 ns 2981$: 299 DEC A 300 JR NZ,1$ 301 RET 302.end_refresh_OAM: 303 304 .org .MODE_TABLE 305 ;; Jump table for modes 306 RET 307 308 ;; **************************************** 309 310 ;; Ordering of segments for the linker 311 ;; Code that really needs to be in bank 0 312 .area _HOME 313 ;; Similar to _HOME 314 .area _BASE 315 ;; Code 316 .area _CODE 317 ;; #pragma bank 0 workaround 318 .area _CODE_0 319 ;; Constant data 320 .area _LIT 321; ;; since _CODE_1 area base address is pre-defined in the linker from 0x4000, 322; ;; that moves initializer code and tables out of bank 0 323; .area _CODE_1 324 ;; Constant data, used to init _DATA 325 .area _INITIALIZER 326 ;; Code, used to init _DATA 327 .area _GSINIT 328 .area _GSFINAL 329 ;; Uninitialised ram data 330 .area _DATA 331 .area _BSS 332 ;; Initialised in ram data 333 .area _INITIALIZED 334 ;; For malloc 335 .area _HEAP 336 .area _HEAP_END 337 338 .area _DATA 339.start_crt_globals: 340 341__cpu:: 342 .ds 0x01 ; GB type (GB, PGB, CGB) 343__is_GBA:: 344 .ds 0x01 ; detect GBA 345.mode:: 346 .ds 0x01 ; Current mode 347.sys_time:: 348_sys_time:: 349 .ds 0x02 ; System time in VBL units 350.int_0x40:: 351 .blkw 0x0A ; 4 interrupt handlers (built-in + user-defined) 352 353.end_crt_globals: 354 355 .area _HRAM (ABS) 356 357 .org 0xFF90 358__current_bank:: ; Current bank 359 .ds 0x01 360.vbl_done: 361 .ds 0x01 ; Is VBL interrupt finished? 362__shadow_OAM_base:: 363 .ds 0x01 364 365 ;; Runtime library 366 .area _GSINIT 367gsinit:: 368 ;; initialize static storage variables 369 LD BC, #l__INITIALIZER 370 LD HL, #s__INITIALIZER 371 LD DE, #s__INITIALIZED 372 call .memcpy_simple 373 374 .area _GSFINAL 375 ret 376 377 .area _HOME 378 379 ;; fills memory at HL of length BC with A, clobbers DE 380.memset_simple:: 381 LD E, A 382 LD A, B 383 OR C 384 RET Z 385 LD (HL), E 386 DEC BC 387 LD D, H 388 LD E, L 389 INC DE 390 391 ;; copies BC bytes from HL into DE 392.memcpy_simple:: 393 LD A, B 394 OR C 395 RET Z 396 397 SRL B 398 RR C 399 JR NC,3$ 400 LD A, (HL+) 401 LD (DE), A 402 INC DE 4033$: 404 INC B 405 INC C 406 JR 2$ 4071$: 408 LD A, (HL+) 409 LD (DE), A 410 INC DE 411 LD A, (HL+) 412 LD (DE), A 413 INC DE 4142$: 415 DEC C 416 JR NZ,1$ 417 DEC B 418 JR NZ,1$ 4194$: 420 RET 421 422 ;; Remove interrupt routine in BC from the VBL interrupt list 423 ;; falldown to .remove_int 424.remove_VBL:: 425 LD HL,#.int_0x40 426 427 ;; Remove interrupt BC from interrupt list HL if it exists 428 ;; Abort if a 0000 is found (end of list) 429.remove_int:: 4301$: 431 LD A,(HL+) 432 LD E,A 433 LD D,(HL) 434 INC HL 435 OR D 436 RET Z ; No interrupt found 437 438 LD A,E 439 CP C 440 JR NZ,1$ 441 LD A,D 442 CP B 443 JR NZ,1$ 444 445 LD D,H 446 LD E,L 447 DEC DE 448 DEC DE 449 450 ;; Now do a memcpy from here until the end of the list 4512$: 452 LD A,(HL+) 453 LD (DE),A 454 LD B,A 455 INC DE 456 LD A,(HL+) 457 LD (DE),A 458 INC DE 459 OR B 460 RET Z 461 JR 2$ 462 463 ;; Add interrupt routine in BC to the VBL interrupt list 464 ;; falldown to .add_int 465.add_VBL:: 466 LD HL,#.int_0x40 467 468 ;; Add interrupt routine in BC to the interrupt list in HL 469.add_int:: 4701$: 471 LD A,(HL+) 472 OR (HL) 473 JR Z,2$ 474 INC HL 475 JR 1$ 4762$: 477 LD A,B 478 LD (HL-),A 479 LD (HL),C 480 RET 481 482 ;; Wait for VBL interrupt to be finished 483.wait_vbl_done:: 484_wait_vbl_done:: 485 ;; Check if the screen is on 486 LDH A,(.LCDC) 487 AND #LCDCF_ON 488 RET Z ; Return if screen is off 489 XOR A 490 LDH (.vbl_done),A ; Clear any previous sets of vbl_done 4911$: 492 HALT ; Wait for any interrupt 493 NOP ; HALT sometimes skips the next instruction 494 LDH A,(.vbl_done) ; Was it a VBlank interrupt? 495 ;; Warning: we may lose a VBlank interrupt, if it occurs now 496 OR A 497 JR Z,1$ ; No: back to sleep! 498 RET 499 500.display_off:: 501_display_off:: 502 ;; Check if the screen is on 503 LDH A,(.LCDC) 504 AND #LCDCF_ON 505 RET Z ; Return if screen is off 5061$: ; We wait for the *NEXT* VBL 507 LDH A,(.LY) 508 CP #0x92 ; Smaller than or equal to 0x91? 509 JR NC,1$ ; Loop until smaller than or equal to 0x91 5102$: 511 LDH A,(.LY) 512 CP #0x91 ; Bigger than 0x90? 513 JR C,2$ ; Loop until bigger than 0x90 514 515 LDH A,(.LCDC) 516 AND #~LCDCF_ON 517 LDH (.LCDC),A ; Turn off screen 518 RET 519 520_remove_VBL:: 521 PUSH BC 522 LDA HL,4(SP) ; Skip return address and registers 523 LD A,(HL+) 524 LD C,A 525 LD B,(HL) 526 CALL .remove_VBL 527 POP BC 528 RET 529 530_add_VBL:: 531 PUSH BC 532 LDA HL, 4(SP) ; Skip return address and registers 533 LD A,(HL+) 534 LD C,A 535 LD B,(HL) 536 CALL .add_VBL 537 POP BC 538 RET