crt0.s (13106B)
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 0x01,0x10,0xCE,0xEF,0x00,0x00,0x44,0xAA 136 .byte 0x00,0x74,0x00,0x18,0x11,0x95,0x00,0x34 137 .byte 0x00,0x1A,0x00,0xD5,0x00,0x22,0x00,0x69 138 .byte 0x6F,0xF6,0xF7,0x73,0x09,0x90,0xE1,0x10 139 .byte 0x44,0x40,0x9A,0x90,0xD5,0xD0,0x44,0x30 140 .byte 0xA9,0x21,0x5D,0x48,0x22,0xE0,0xF8,0x60 141 142 ;; Title of the game 143 .org 0x134 144 .asciz "Title" 145 146 .org 0x144 147 .byte 0,0,0 148 149 ;; Cartridge type is ROM only 150 .org 0x147 151 .byte 0 152 153 ;; ROM size is 32kB 154 .org 0x148 155 .byte 0 156 157 ;; RAM size is 0kB 158 .org 0x149 159 .byte 0 160 161 ;; Maker ID 162 .org 0x14A 163 .byte 0x00,0x00 164 165 ;; Version number 166 .org 0x14C 167 .byte 0x01 168 169 ;; Complement check 170 .org 0x14D 171 .byte 0x00 172 173 ;; Checksum 174 .org 0x14E 175 .byte 0x00,0x00 176 177 ;; **************************************** 178 .org 0x150 179 180 ;; soft reset: falldown to .code_start 181.reset:: 182_reset:: 183 LD A, (__is_GBA) 184 LD B, A 185 LD A, (__cpu) 186 187 ;; Initialization code 188.code_start:: 189 DI ; Disable interrupts 190 LD D, A ; Store CPU type in D 191 LD E, B 192 ;; Initialize the stack 193 LD SP, #.STACK 194 195 CALL .clear_WRAM 196 197; LD (.mode),A ; Clearing (.mode) is performed when clearing RAM 198 199 ;; Store CPU type 200 LD A, D 201 LD (__cpu), A 202 CP #.CGB_TYPE 203 JR NZ, 1$ 204 XOR A 205 SRL E 206 RLA 207 LD (__is_GBA), A 2081$: 209 210 ;; Turn the screen off 211 CALL .display_off 212 213 XOR A 214 ;; Initialize the display 215 LDH (.SCY),A 216 LDH (.SCX),A 217 LDH (.STAT),A 218 LDH (.WY),A 219 LD A,#0x07 220 LDH (.WX),A 221 222 ;; Copy refresh_OAM routine to HRAM 223 LD DE,#.start_refresh_OAM ; source 224 LD HL,#.refresh_OAM ; dest 225 LD C,#(.end_refresh_OAM - .start_refresh_OAM) ; size 226 RST 0x30 ; call .MemcpySmall 227 228 ;; Clear the OAM by calling refresh_OAM 229 CALL .refresh_OAM 230 231 ;; Install interrupt routines 232 LD BC,#.std_vbl 233 CALL .add_VBL 234 235 ;; Standard color palettes 236 LD A,#0b11100100 ; Grey 3 = 11 (Black) 237 ; Grey 2 = 10 (Dark grey) 238 ; Grey 1 = 01 (Light grey) 239 ; Grey 0 = 00 (Transparent) 240 LDH (.BGP),A 241 LDH (.OBP0),A 242 LD A,#0b00011011 243 LDH (.OBP1),A 244 245 ;; Turn the screen on 246 LD A,#(LCDCF_ON | LCDCF_WIN9C00 | LCDCF_WINOFF | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_BGOFF) 247 LDH (.LCDC),A 248 XOR A 249 LDH (.IF),A 250 LD A,#.VBL_IFLAG ; switch on VBlank interrupt only 251 LDH (.IE),A 252 253 LDH (__current_bank),A ; current bank is 1 at startup 254 255 XOR A 256 257 LD HL,#.sys_time 258 LD (HL+),A 259 LD (HL),A 260 261 LDH (.NR52),A ; Turn sound off 262 263 CALL gsinit 264 265 EI ; Enable interrupts 266 267 ;; Call the main function 268 CALL _main 269_exit:: 27099$: 271 HALT 272 NOP 273 JR 99$ ; Wait forever 274 275_set_interrupts:: 276 DI 277 LDA HL,2(SP) ; Skip return address 278 XOR A 279 LDH (.IF),A ; Clear pending interrupts 280 LD A,(HL) 281 EI ; Enable interrupts 282 LDH (.IE),A ; interrupts are still disabled here 283 RET 284 285 ;; Copy OAM data to OAM RAM 286.start_refresh_OAM: 287 LDH A,(__shadow_OAM_base) 288 OR A 289 RET Z 290.refresh_OAM_DMA: 291 LDH (.DMA),A ; Put A into DMA registers 292 LD A,#0x28 ; We need to wait 160 ns 2931$: 294 DEC A 295 JR NZ,1$ 296 RET 297.end_refresh_OAM: 298 299 .org .MODE_TABLE 300 ;; Jump table for modes 301 RET 302 303 ;; **************************************** 304 305 ;; Ordering of segments for the linker 306 ;; Code that really needs to be in bank 0 307 .area _HOME 308 ;; Similar to _HOME 309 .area _BASE 310 ;; Code 311 .area _CODE 312 ;; #pragma bank 0 workaround 313 .area _CODE_0 314 ;; Constant data 315 .area _LIT 316; ;; since _CODE_1 area base address is pre-defined in the linker from 0x4000, 317; ;; that moves initializer code and tables out of bank 0 318; .area _CODE_1 319 ;; Constant data, used to init _DATA 320 .area _INITIALIZER 321 ;; Code, used to init _DATA 322 .area _GSINIT 323 .area _GSFINAL 324 ;; Uninitialised ram data 325 .area _DATA 326 .area _BSS 327 ;; Initialised in ram data 328 .area _INITIALIZED 329 ;; For malloc 330 .area _HEAP 331 .area _HEAP_END 332 333 .area _DATA 334__cpu:: 335 .ds 0x01 ; GB type (GB, PGB, CGB) 336__is_GBA:: 337 .ds 0x01 ; detect GBA 338.mode:: 339 .ds 0x01 ; Current mode 340.sys_time:: 341_sys_time:: 342 .ds 0x02 ; System time in VBL units 343.int_0x40:: 344 .blkw 0x0A ; 4 interrupt handlers (built-in + user-defined) 345 346 .area _HRAM (ABS) 347 348 .org 0xFF90 349__current_bank:: ; Current bank 350 .ds 0x01 351.vbl_done: 352 .ds 0x01 ; Is VBL interrupt finished? 353__shadow_OAM_base:: 354 .ds 0x01 355 356 ;; Runtime library 357 .area _GSINIT 358gsinit:: 359 ;; initialize static storage variables 360 LD BC, #l__INITIALIZER 361 LD HL, #s__INITIALIZER 362 LD DE, #s__INITIALIZED 363 call .memcpy_simple 364 365 .area _GSFINAL 366 ret 367 368 .area _HOME 369 370 ;; fills memory at HL of length BC with A, clobbers DE 371.memset_simple:: 372 LD E, A 373 LD A, B 374 OR C 375 RET Z 376 LD (HL), E 377 DEC BC 378 LD D, H 379 LD E, L 380 INC DE 381 382 ;; copies BC bytes from HL into DE 383.memcpy_simple:: 384 LD A, B 385 OR C 386 RET Z 387 388 SRL B 389 RR C 390 JR NC,3$ 391 LD A, (HL+) 392 LD (DE), A 393 INC DE 3943$: 395 INC B 396 INC C 397 JR 2$ 3981$: 399 LD A, (HL+) 400 LD (DE), A 401 INC DE 402 LD A, (HL+) 403 LD (DE), A 404 INC DE 4052$: 406 DEC C 407 JR NZ,1$ 408 DEC B 409 JR NZ,1$ 4104$: 411 RET 412 413 ;; Remove interrupt routine in BC from the VBL interrupt list 414 ;; falldown to .remove_int 415.remove_VBL:: 416 LD HL,#.int_0x40 417 418 ;; Remove interrupt BC from interrupt list HL if it exists 419 ;; Abort if a 0000 is found (end of list) 420.remove_int:: 4211$: 422 LD A,(HL+) 423 LD E,A 424 LD D,(HL) 425 INC HL 426 OR D 427 RET Z ; No interrupt found 428 429 LD A,E 430 CP C 431 JR NZ,1$ 432 LD A,D 433 CP B 434 JR NZ,1$ 435 436 LD D,H 437 LD E,L 438 DEC DE 439 DEC DE 440 441 ;; Now do a memcpy from here until the end of the list 4422$: 443 LD A,(HL+) 444 LD (DE),A 445 LD B,A 446 INC DE 447 LD A,(HL+) 448 LD (DE),A 449 INC DE 450 OR B 451 RET Z 452 JR 2$ 453 454 ;; Add interrupt routine in BC to the VBL interrupt list 455 ;; falldown to .add_int 456.add_VBL:: 457 LD HL,#.int_0x40 458 459 ;; Add interrupt routine in BC to the interrupt list in HL 460.add_int:: 4611$: 462 LD A,(HL+) 463 OR (HL) 464 JR Z,2$ 465 INC HL 466 JR 1$ 4672$: 468 LD A,B 469 LD (HL-),A 470 LD (HL),C 471 RET 472 473 ;; Wait for VBL interrupt to be finished 474.wait_vbl_done:: 475_wait_vbl_done:: 476 ;; Check if the screen is on 477 LDH A,(.LCDC) 478 AND #LCDCF_ON 479 RET Z ; Return if screen is off 480 XOR A 481 LDH (.vbl_done),A ; Clear any previous sets of vbl_done 4821$: 483 HALT ; Wait for any interrupt 484 NOP ; HALT sometimes skips the next instruction 485 LDH A,(.vbl_done) ; Was it a VBlank interrupt? 486 ;; Warning: we may lose a VBlank interrupt, if it occurs now 487 OR A 488 JR Z,1$ ; No: back to sleep! 489 RET 490 491.display_off:: 492_display_off:: 493 ;; Check if the screen is on 494 LDH A,(.LCDC) 495 AND #LCDCF_ON 496 RET Z ; Return if screen is off 4971$: ; We wait for the *NEXT* VBL 498 LDH A,(.LY) 499 CP #0x92 ; Smaller than or equal to 0x91? 500 JR NC,1$ ; Loop until smaller than or equal to 0x91 5012$: 502 LDH A,(.LY) 503 CP #0x91 ; Bigger than 0x90? 504 JR C,2$ ; Loop until bigger than 0x90 505 506 LDH A,(.LCDC) 507 AND #~LCDCF_ON 508 LDH (.LCDC),A ; Turn off screen 509 RET 510 511_remove_VBL:: 512 PUSH BC 513 LDA HL,4(SP) ; Skip return address and registers 514 LD A,(HL+) 515 LD C,A 516 LD B,(HL) 517 CALL .remove_VBL 518 POP BC 519 RET 520 521_add_VBL:: 522 PUSH BC 523 LDA HL, 4(SP) ; Skip return address and registers 524 LD A,(HL+) 525 LD C,A 526 LD B,(HL) 527 CALL .add_VBL 528 POP BC 529 RET