; SameBoy CGB bootstrap ROM include "sameboot.inc" SECTION "BootCode", ROM0[$0000] Start: ; Init stack pointer ld sp, $FFFE ; Clear memory VRAM call ClearMemoryVRAM ; Clear OAM ld h, HIGH(_OAMRAM) ld c, sizeof_OAM_ATTRS * OAM_COUNT .clearOAMLoop ldi [hl], a dec c jr nz, .clearOAMLoop IF !DEF(CGB0) ; Init waveform ld c, 16 ld hl, _AUD3WAVERAM .waveformLoop ldi [hl], a cpl dec c jr nz, .waveformLoop ENDC ; Clear chosen input palette ldh [hInputPalette], a ; Clear title checksum ldh [hTitleChecksum], a ; Init Audio ld a, AUDENA_ON ldh [rNR52], a assert AUDENA_ON == AUDLEN_DUTY_50 ldh [rNR11], a ld a, $F3 ldh [rNR12], a ; Envelope $F, decreasing, sweep $3 ldh [rNR51], a ; Channels 1+2+3+4 left, channels 1+2 right ld a, $77 ldh [rNR50], a ; Volume $7, left and right ; Init BG palette ld a, %11_11_11_00 ldh [rBGP], a ; Load logo from ROM. ; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8. ; Tiles are ordered left to right, top to bottom. ; These tiles are not used, but are required for DMG compatibility. This is done ; by the original CGB Boot ROM as well. ld de, NintendoLogo ld hl, _VRAM + $10 ; This is where we load the tiles in VRAM .loadLogoLoop ld a, [de] ; Read 2 rows ld b, a call DoubleBitsAndWriteRowTwice inc de ld a, e cp LOW(NintendoLogoEnd) jr nz, .loadLogoLoop call ReadTrademarkSymbol ; Clear the second VRAM bank ld a, 1 ldh [rVBK], a call ClearMemoryVRAM call LoadTileset ld b, 3 IF DEF(FAST) xor a ldh [rVBK], a ELSE ; Load Tilemap ld hl, _SCRN0 + 6 * SCRN_VX_B + 2 ld d, 3 ld a, 8 .tilemapLoop ld c, 16 .tilemapRowLoop call .write_with_palette ; Repeat the 3 tiles common between E and B. This saves 27 bytes after ; compression, with a cost of 17 bytes of code. push af sub $20 sub $3 jr nc, .notspecial add $20 call .write_with_palette dec c .notspecial pop af add d ; d = 3 for SameBoy logo, d = 1 for Nintendo logo dec c jr nz, .tilemapRowLoop sub 44 push de ld de, $10 add hl, de pop de dec b jr nz, .tilemapLoop dec d jr z, .endTilemap dec d ld a, $38 ld l, $A7 lb bc, 1, 7 ; $0107 jr .tilemapRowLoop .write_with_palette push af ; Switch to second VRAM Bank ld a, 1 ldh [rVBK], a ld [hl], 8 ; Switch to back first VRAM Bank xor a ldh [rVBK], a pop af ldi [hl], a ret .endTilemap ENDC ; Expand Palettes ld de, AnimationColors ld c, 8 ld hl, hBgPalettes xor a .expandPalettesLoop: cpl ; One white ld [hli], a ld [hli], a ; Mixed with white ld a, [de] inc e or $20 ld b, a ld a, [de] dec e or $84 rra rr b ld [hl], b inc l ld [hli], a ; One black xor a ld [hli], a ld [hli], a ; One color ld a, [de] inc e ld [hli], a ld a, [de] inc e ld [hli], a xor a dec c jr nz, .expandPalettesLoop call LoadPalettesFromHRAM ; Turn on LCD ld a, LCDCF_ON | LCDCF_BLK01 | LCDCF_BGON ldh [rLCDC], a IF !DEF(FAST) call DoIntroAnimation ld a, 48 ; frames to wait after playing the chime ldh [hWaitLoopCounter], a ld b, 4 ; frames to wait before playing the chime call WaitBFrames ; Play first sound ld a, $83 call PlaySound ld b, 5 call WaitBFrames ; Play second sound ld a, $C1 call PlaySound .waitLoop call GetInputPaletteIndex call WaitFrame ld hl, hWaitLoopCounter dec [hl] jr nz, .waitLoop ELSE ld a, $C1 call PlaySound ENDC call Preboot IF DEF(AGB) inc b ENDC jr BootGame HDMAData: MACRO hdma_data ; source, destination, length db HIGH(\1), LOW(\1) db HIGH(\2), LOW(\2) db (\3) ENDM hdma_data _RAMBANK, _SCRN0 + 5 * SCRN_VX_B + 0, 18 hdma_data _RAMBANK, _VRAM, 64 SECTION "BootGame", ROM0[$00FE] BootGame: ldh [rBANK], a ; unmap boot ROM SECTION "BootData", ROM0[$0200] ; Game Palettes Data TitleChecksums: db $00 ; Default db $88 ; ALLEY WAY db $16 ; YAKUMAN db $36 ; BASEBALL, (Game and Watch 2) db $D1 ; TENNIS db $DB ; TETRIS db $F2 ; QIX db $3C ; DR.MARIO db $8C ; RADARMISSION db $92 ; F1RACE db $3D ; YOSSY NO TAMAGO db $5C ; db $58 ; X db $C9 ; MARIOLAND2 db $3E ; YOSSY NO COOKIE db $70 ; ZELDA db $1D ; db $59 ; db $69 ; TETRIS FLASH db $19 ; DONKEY KONG db $35 ; MARIO'S PICROSS db $A8 ; db $14 ; POKEMON RED, (GAMEBOYCAMERA G) db $AA ; POKEMON GREEN db $75 ; PICROSS 2 db $95 ; YOSSY NO PANEPON db $99 ; KIRAKIRA KIDS db $34 ; GAMEBOY GALLERY db $6F ; POCKETCAMERA db $15 ; db $FF ; BALLOON KID db $97 ; KINGOFTHEZOO db $4B ; DMG FOOTBALL db $90 ; WORLD CUP db $17 ; OTHELLO db $10 ; SUPER RC PRO-AM db $39 ; DYNABLASTER db $F7 ; BOY AND BLOB GB2 db $F6 ; MEGAMAN db $A2 ; STAR WARS-NOA db $49 ; db $4E ; WAVERACE db $43 ; db $68 ; LOLO2 db $E0 ; YOSHI'S COOKIE db $8B ; MYSTIC QUEST db $F0 ; db $CE ; TOPRANKINGTENNIS db $0C ; MANSELL db $29 ; MEGAMAN3 db $E8 ; SPACE INVADERS db $B7 ; GAME&WATCH db $86 ; DONKEYKONGLAND95 db $9A ; ASTEROIDS/MISCMD db $52 ; STREET FIGHTER 2 db $01 ; DEFENDER/JOUST db $9D ; KILLERINSTINCT95 db $71 ; TETRIS BLAST db $9C ; PINOCCHIO db $BD ; db $5D ; BA.TOSHINDEN db $6D ; NETTOU KOF 95 db $67 ; db $3F ; TETRIS PLUS db $6B ; DONKEYKONGLAND 3 ; For these games, the 4th letter is taken into account FirstChecksumWithDuplicate: ; Let's play hangman! db $B3 ; ???[B]???????? db $46 ; SUP[E]R MARIOLAND db $28 ; GOL[F] db $A5 ; SOL[A]RSTRIKER db $C6 ; GBW[A]RS db $D3 ; KAE[R]UNOTAMENI db $27 ; ???[B]???????? db $61 ; POK[E]MON BLUE db $18 ; DON[K]EYKONGLAND db $66 ; GAM[E]BOY GALLERY2 db $6A ; DON[K]EYKONGLAND 2 db $BF ; KID[ ]ICARUS db $0D ; TET[R]IS2 db $F4 ; ???[-]???????? db $B3 ; MOG[U]RANYA db $46 ; ???[R]???????? db $28 ; GAL[A]GA&GALAXIAN db $A5 ; BT2[R]AGNAROKWORLD db $C6 ; KEN[ ]GRIFFEY JR db $D3 ; ???[I]???????? db $27 ; MAG[N]ETIC SOCCER db $61 ; VEG[A]S STAKES db $18 ; ???[I]???????? db $66 ; MIL[L]I/CENTI/PEDE db $6A ; MAR[I]O & YOSHI db $BF ; SOC[C]ER db $0D ; POK[E]BOM db $F4 ; G&W[ ]GALLERY db $B3 ; TET[R]IS ATTACK ChecksumsEnd: PalettePerChecksum: MACRO palette_index ; palette[, flags] IF _NARG == 1 db (\1) ELSE db (\1) | (\2) ; flag $80 means game requires DMG boot tilemap ENDC ENDM palette_index 0 ; Default Palette palette_index 4 ; ALLEY WAY palette_index 5 ; YAKUMAN palette_index 35 ; BASEBALL, (Game and Watch 2) palette_index 34 ; TENNIS palette_index 3 ; TETRIS palette_index 31 ; QIX palette_index 15 ; DR.MARIO palette_index 10 ; RADARMISSION palette_index 5 ; F1RACE palette_index 19 ; YOSSY NO TAMAGO palette_index 36 ; palette_index 7, $80 ; X palette_index 37 ; MARIOLAND2 palette_index 30 ; YOSSY NO COOKIE palette_index 44 ; ZELDA palette_index 21 ; palette_index 32 ; palette_index 31 ; TETRIS FLASH palette_index 20 ; DONKEY KONG palette_index 5 ; MARIO'S PICROSS palette_index 33 ; palette_index 13 ; POKEMON RED, (GAMEBOYCAMERA G) palette_index 14 ; POKEMON GREEN palette_index 5 ; PICROSS 2 palette_index 29 ; YOSSY NO PANEPON palette_index 5 ; KIRAKIRA KIDS palette_index 18 ; GAMEBOY GALLERY palette_index 9 ; POCKETCAMERA palette_index 3 ; palette_index 2 ; BALLOON KID palette_index 26 ; KINGOFTHEZOO palette_index 25 ; DMG FOOTBALL palette_index 25 ; WORLD CUP palette_index 41 ; OTHELLO palette_index 42 ; SUPER RC PRO-AM palette_index 26 ; DYNABLASTER palette_index 45 ; BOY AND BLOB GB2 palette_index 42 ; MEGAMAN palette_index 45 ; STAR WARS-NOA palette_index 36 ; palette_index 38 ; WAVERACE palette_index 26, $80 ; palette_index 42 ; LOLO2 palette_index 30 ; YOSHI'S COOKIE palette_index 41 ; MYSTIC QUEST palette_index 34 ; palette_index 34 ; TOPRANKINGTENNIS palette_index 5 ; MANSELL palette_index 42 ; MEGAMAN3 palette_index 6 ; SPACE INVADERS palette_index 5 ; GAME&WATCH palette_index 33 ; DONKEYKONGLAND95 palette_index 25 ; ASTEROIDS/MISCMD palette_index 42 ; STREET FIGHTER 2 palette_index 42 ; DEFENDER/JOUST palette_index 40 ; KILLERINSTINCT95 palette_index 2 ; TETRIS BLAST palette_index 16 ; PINOCCHIO palette_index 25 ; palette_index 42 ; BA.TOSHINDEN palette_index 42 ; NETTOU KOF 95 palette_index 5 ; palette_index 0 ; TETRIS PLUS palette_index 39 ; DONKEYKONGLAND 3 palette_index 36 ; palette_index 22 ; SUPER MARIOLAND palette_index 25 ; GOLF palette_index 6 ; SOLARSTRIKER palette_index 32 ; GBWARS palette_index 12 ; KAERUNOTAMENI palette_index 36 ; palette_index 11 ; POKEMON BLUE palette_index 39 ; DONKEYKONGLAND palette_index 18 ; GAMEBOY GALLERY2 palette_index 39 ; DONKEYKONGLAND 2 palette_index 24 ; KID ICARUS palette_index 31 ; TETRIS2 palette_index 50 ; palette_index 17 ; MOGURANYA palette_index 46 ; palette_index 6 ; GALAGA&GALAXIAN palette_index 27 ; BT2RAGNAROKWORLD palette_index 0 ; KEN GRIFFEY JR palette_index 47 ; palette_index 41 ; MAGNETIC SOCCER palette_index 41 ; VEGAS STAKES palette_index 0 ; palette_index 0 ; MILLI/CENTI/PEDE palette_index 19 ; MARIO & YOSHI palette_index 34 ; SOCCER palette_index 23 ; POKEBOM palette_index 18 ; G&W GALLERY palette_index 29 ; TETRIS ATTACK Dups4thLetterArray: db "BEFAARBEKEK R-URAR INAILICE R" ; We assume the last three arrays fit in the same $100 byte page! PaletteCombinations: MACRO palette_comb ; Obj0, Obj1, Bg db (\1) * 8, (\2) * 8, (\3) *8 ENDM MACRO raw_palette_comb ; Obj0, Obj1, Bg db (\1) * 2, (\2) * 2, (\3) * 2 ENDM palette_comb 4, 4, 29 ; 0, Right + A palette_comb 18, 18, 18 ; 1, Right palette_comb 20, 20, 20 ; 2 palette_comb 24, 24, 24 ; 3, Down + A palette_comb 9, 9, 9 ; 4 palette_comb 0, 0, 0 ; 5, Up palette_comb 27, 27, 27 ; 6, Right + B palette_comb 5, 5, 5 ; 7, Left + B palette_comb 12, 12, 12 ; 8, Down palette_comb 26, 26, 26 ; 9 palette_comb 16, 8, 8 ; 10 palette_comb 4, 28, 28 ; 11 palette_comb 4, 2, 2 ; 12 palette_comb 3, 4, 4 ; 13 palette_comb 4, 29, 29 ; 14 palette_comb 28, 4, 28 ; 15 palette_comb 2, 17, 2 ; 16 palette_comb 16, 16, 8 ; 17 palette_comb 4, 4, 7 ; 18 palette_comb 4, 4, 18 ; 19 palette_comb 4, 4, 20 ; 20 palette_comb 19, 19, 9 ; 21 raw_palette_comb 4 * 4 - 1, 4 * 4 - 1, 11 * 4 ; 22 palette_comb 17, 17, 2 ; 23 palette_comb 4, 4, 2 ; 24 palette_comb 4, 4, 3 ; 25 palette_comb 28, 28, 0 ; 26 palette_comb 3, 3, 0 ; 27 palette_comb 0, 0, 1 ; 28, Up + B palette_comb 18, 22, 18 ; 29 palette_comb 20, 22, 20 ; 30 palette_comb 24, 22, 24 ; 31 palette_comb 16, 22, 8 ; 32 palette_comb 17, 4, 13 ; 33 raw_palette_comb 28 * 4 - 1, 0 * 4, 14 * 4 ; 34 raw_palette_comb 28 * 4 - 1, 4 * 4, 15 * 4 ; 35 raw_palette_comb 19 * 4, 23 * 4 - 1, 9 * 4 ; 36 palette_comb 16, 28, 10 ; 37 palette_comb 4, 23, 28 ; 38 palette_comb 17, 22, 2 ; 39 palette_comb 4, 0, 2 ; 40, Left + A palette_comb 4, 28, 3 ; 41 palette_comb 28, 3, 0 ; 42 palette_comb 3, 28, 4 ; 43, Up + A palette_comb 21, 28, 4 ; 44 palette_comb 3, 28, 0 ; 45 palette_comb 25, 3, 28 ; 46 palette_comb 0, 28, 8 ; 47 palette_comb 4, 3, 28 ; 48, Left palette_comb 28, 3, 6 ; 49, Down + B palette_comb 4, 28, 29 ; 50 ; SameBoy "Exclusives" palette_comb 30, 30, 30 ; 51, Right + A + B, CGA palette_comb 31, 31, 31 ; 52, Left + A + B, DMG LCD palette_comb 28, 4, 1 ; 53, Up + A + B palette_comb 0, 0, 2 ; 54, Down + A + B Palettes: dw $7FFF, $32BF, $00D0, $0000 ; 0 dw $639F, $4279, $15B0, $04CB ; 1 dw $7FFF, $6E31, $454A, $0000 ; 2 dw $7FFF, $1BEF, $0200, $0000 ; 3 dw $7FFF, $421F, $1CF2, $0000 ; 4 dw $7FFF, $5294, $294A, $0000 ; 5 dw $7FFF, $03FF, $012F, $0000 ; 6 dw $7FFF, $03EF, $01D6, $0000 ; 7 dw $7FFF, $42B5, $3DC8, $0000 ; 8 dw $7E74, $03FF, $0180, $0000 ; 9 dw $67FF, $77AC, $1A13, $2D6B ; 10 dw $7ED6, $4BFF, $2175, $0000 ; 11 dw $53FF, $4A5F, $7E52, $0000 ; 12 dw $4FFF, $7ED2, $3A4C, $1CE0 ; 13 dw $03ED, $7FFF, $255F, $0000 ; 14 dw $036A, $021F, $03FF, $7FFF ; 15 dw $7FFF, $01DF, $0112, $0000 ; 16 dw $231F, $035F, $00F2, $0009 ; 17 dw $7FFF, $03EA, $011F, $0000 ; 18 dw $299F, $001A, $000C, $0000 ; 19 dw $7FFF, $027F, $001F, $0000 ; 20 dw $7FFF, $03E0, $0206, $0120 ; 21 dw $7FFF, $7EEB, $001F, $7C00 ; 22 dw $7FFF, $3FFF, $7E00, $001F ; 23 dw $7FFF, $03FF, $001F, $0000 ; 24 dw $03FF, $001F, $000C, $0000 ; 25 dw $7FFF, $033F, $0193, $0000 ; 26 dw $0000, $4200, $037F, $7FFF ; 27 dw $7FFF, $7E8C, $7C00, $0000 ; 28 dw $7FFF, $1BEF, $6180, $0000 ; 29 ; SameBoy "Exclusives" dw $7FFF, $7FEA, $7D5F, $0000 ; 30, CGA 1 dw $4778, $3290, $1D87, $0861 ; 31, DMG LCD KeyCombinationPalettes: MACRO palette_comb_id ; PaletteCombinations ID db (\1) * 3 ENDM palette_comb_id 1 ; 1, Right palette_comb_id 48 ; 2, Left palette_comb_id 5 ; 3, Up palette_comb_id 8 ; 4, Down palette_comb_id 0 ; 5, Right + A palette_comb_id 40 ; 6, Left + A palette_comb_id 43 ; 7, Up + A palette_comb_id 3 ; 8, Down + A palette_comb_id 6 ; 9, Right + B palette_comb_id 7 ; 10, Left + B palette_comb_id 28 ; 11, Up + B palette_comb_id 49 ; 12, Down + B ; SameBoy "Exclusives" palette_comb_id 51 ; 13, Right + A + B palette_comb_id 52 ; 14, Left + A + B palette_comb_id 53 ; 15, Up + A + B palette_comb_id 54 ; 16, Down + A + B TrademarkSymbol: pusho opt b.X db %..XXXX.. db %.X....X. db %X.XXX..X db %X.X..X.X db %X.XXX..X db %X.X..X.X db %.X....X. db %..XXXX.. popo TrademarkSymbolEnd: SameBoyLogo: incbin "SameBoyLogo.pb12" AnimationColors: dw $7FFF ; White dw $774F ; Cyan dw $22C7 ; Green dw $039F ; Yellow dw $017D ; Orange dw $241D ; Red dw $6D38 ; Purple IF DEF(AGB) dw $6D60 ; Blue ELSE dw $5500 ; Blue ENDC AnimationColorsEnd: ; Helper Functions DoubleBitsAndWriteRowTwice: call .twice .twice ; Double the most significant 4 bits, b is shifted by 4 ld a, 4 ld c, 0 .doubleCurrentBit sla b push af rl c pop af rl c dec a jr nz, .doubleCurrentBit ld a, c ; Write as two rows ldi [hl], a inc hl ldi [hl], a inc hl ret WaitFrame: push hl ld hl, rIF res 0, [hl] .wait bit 0, [hl] jr z, .wait pop hl ret WaitBFrames: call GetInputPaletteIndex call WaitFrame dec b jr nz, WaitBFrames ret PlaySound: ldh [rNR13], a ld a, $87 ldh [rNR14], a ret ClearMemoryVRAM: ld hl, _VRAM ; Clear from HL to HL | 0x2000 ClearMemoryPage: xor a ldi [hl], a bit 5, h jr z, ClearMemoryPage ret ReadTwoTileLines: call ReadTileLine ; c = $F0 for even lines, $0F for odd lines. ReadTileLine: ld a, [de] and c ld b, a inc e inc e ld a, [de] dec e dec e and c swap a or b bit 0, c jr z, .dontSwap swap a .dontSwap inc hl ldi [hl], a swap c ret ReadCGBLogoHalfTile: call .do_twice .do_twice call ReadTwoTileLines inc e ld a, e ret ; LoadTileset using PB12 codec, 2020 Jakub Kądziołka ; (based on PB8 codec, 2019 Damian Yerrick) LoadTileset: ld hl, SameBoyLogo ; source ld de, _VRAM + $80 - 1 ; destination ld c, (128 * 24) / (8 * 8) ; length .refill ; Register map for PB12 decompression ; HL: source address in boot ROM ; DE: destination address in VRAM ; A: Current literal value ; B: Repeat bits, terminated by 1000... ; Source address in HL lets the repeat bits go straight to B, ; bypassing A and avoiding spilling registers to the stack. ld b, [hl] dec b jr z, .sameboyLogoEnd inc b inc hl ; Shift a 1 into lower bit of shift value. Once this bit ; reaches the carry, B becomes 0 and the byte is over scf rl b .loop ; If not a repeat, load a literal byte jr c, .simple_repeat sla b jr c, .shifty_repeat ld a, [hli] jr .got_byte .shifty_repeat sla b jr nz, .no_refill_during_shift ld b, [hl] ; see above. Also, no, factoring it out into a callable inc hl ; routine doesn't save bytes, even with conditional calls scf rl b .no_refill_during_shift ld c, a jr nc, .shift_left srl a db $FE ; eat the `add a` with `cp d8` .shift_left add a sla b jr c, .go_and or c db $FE ; eat the `and c` with `cp d8` .go_and and c jr .got_byte .simple_repeat sla b jr c, .got_byte ; far repeat dec de ld a, [de] inc de .got_byte inc de ld [de], a sla b jr nz, .loop jr .refill ; End PB12 decoding. The rest uses HL as the destination .sameboyLogoEnd ld h, d ld l, $80 ; Copy (unresized) ROM logo ld de, NintendoLogo .CGBROMLogoLoop ld c, $F0 call ReadCGBLogoHalfTile add a, 22 ld e, a call ReadCGBLogoHalfTile sub a, 22 ld e, a cp $1C jr nz, .CGBROMLogoLoop inc hl ; fallthrough ReadTrademarkSymbol: ld de, TrademarkSymbol ld c, TrademarkSymbolEnd - TrademarkSymbol .loadTrademarkSymbolLoop: ld a, [de] inc de ldi [hl], a inc hl dec c jr nz, .loadTrademarkSymbolLoop ret DoIntroAnimation: ; Animate the intro ld a, 1 ldh [rVBK], a ld d, 26 .animationLoop ld b, 2 call WaitBFrames ld hl, _SCRN0 + 6 * SCRN_VX_B + 0 ld c, 3 ; Row count .loop ld a, [hl] cp $F ; Already blue jr z, .nextTile inc [hl] and $7 jr z, .nextLine ; Changed a white tile, go to next line .nextTile inc hl jr .loop .nextLine ld a, l or $1F ld l, a inc hl dec c jr nz, .loop dec d jr nz, .animationLoop ret Preboot: IF !DEF(FAST) ld b, 32 ; 32 times to fade .fadeLoop ld c, (hBgPalettesEnd - hBgPalettes) / 2 ; 32 colors to fade ld hl, hBgPalettes .frameLoop push bc ; Brighten Color ld a, [hli] ld e, a ld a, [hld] ld d, a ; RGB(1,1,1) ld bc, $0421 ; Is blue maxed? ld a, e and $1F cp $1F jr nz, .blueNotMaxed dec c .blueNotMaxed ; Is green maxed? ld a, e cp $E0 jr c, .greenNotMaxed ld a, d and $3 cp $3 jr nz, .greenNotMaxed res 5, c .greenNotMaxed ; Is red maxed? ld a, d and $7C cp $7C jr nz, .redNotMaxed res 2, b .redNotMaxed ld a, e add c ld [hli], a ld a, d adc b ld [hli], a pop bc dec c jr nz, .frameLoop call WaitFrame call LoadPalettesFromHRAM call WaitFrame dec b jr nz, .fadeLoop ENDC ld a, 2 ldh [rSVBK], a ; Clear RAM Bank 2 (Like the original boot ROM) ld hl, _RAMBANK call ClearMemoryPage inc a call ClearVRAMViaHDMA call _ClearVRAMViaHDMA call ClearVRAMViaHDMA ; A = $40, so it's bank 0 xor a ldh [rSVBK], a cpl ldh [rJOYP], a ; Final values for CGB mode ld d, a ld e, c ld l, $0D ld a, [CGBFlag] bit 7, a call z, EmulateDMG bit 7, a ldh [rKEY0], a ; write CGB compatibility byte, CGB mode ldh a, [hTitleChecksum] ld b, a jr z, .skipDMGForCGBCheck ldh a, [hInputPalette] and a jr nz, .emulateDMGForCGBGame .skipDMGForCGBCheck IF DEF(AGB) ; Set registers to match the original AGB-CGB boot ; AF = $1100, C = 0 xor a ld c, a add a, BOOTUP_A_CGB ld h, c ; B is set to BOOTUP_B_AGB (1) after ret ELSE ; Set registers to match the original CGB boot ; AF = $1180, C = 0 xor a ld c, a ld a, BOOTUP_A_CGB ld h, c ; B is set to the title checksum (BOOTUP_B_CGB, 0) ENDC ret .emulateDMGForCGBGame call EmulateDMG ldh [rKEY0], a ; write $04, DMG emulation mode ld a, $1 ret GetKeyComboPalette: ld hl, KeyCombinationPalettes - 1 ; Return value is 1-based, 0 means nothing down ld c, a ld b, 0 add hl, bc ld a, [hl] ret EmulateDMG: ld a, 1 ldh [rOPRI], a ; DMG Emulation sprite priority call GetPaletteIndex bit 7, a call nz, LoadDMGTilemap res 7, a ld b, a add b add b ld b, a ldh a, [hInputPalette] and a jr z, .nothingDown call GetKeyComboPalette jr .paletteFromKeys .nothingDown ld a, b .paletteFromKeys call WaitFrame call LoadPalettesFromIndex ld a, 4 ; Set the final values for DMG mode ld de, 8 ld l, $7C ret GetPaletteIndex: ld hl, OldLicenseeCode ld a, [hl] cp $33 jr z, .newLicensee dec a ; 1 = Nintendo jr nz, .notNintendo jr .doChecksum .newLicensee ld l, LOW(NewLicenseeCode) ld a, [hli] cp "0" jr nz, .notNintendo ld a, [hl] cp "1" jr nz, .notNintendo .doChecksum ld l, LOW(Title) ld c, 16 xor a .checksumLoop add [hl] inc l dec c jr nz, .checksumLoop ldh [hTitleChecksum], a ld b, a ; c = 0 ld hl, TitleChecksums .searchLoop ld a, l sub LOW(ChecksumsEnd) ; use sub to zero out a ret z ld a, [hli] cp b jr nz, .searchLoop ; We might have a match, Do duplicate/4th letter check ld a, l sub FirstChecksumWithDuplicate - TitleChecksums + 1 jr c, .match ; Does not have a duplicate, must be a match! ; Has a duplicate; check 4th letter push hl ld a, l add Dups4thLetterArray - FirstChecksumWithDuplicate - 1 ; -1 since hl was incremented ld l, a ld a, [hl] pop hl ld c, a ld a, [Title + 3] ; Get 4th letter cp c jr nz, .searchLoop ; Not a match, continue .match ld a, l add PalettePerChecksum - TitleChecksums - 1; -1 since hl was incremented ld l, a ld a, b ldh [hTitleChecksum], a ld a, [hl] ret .notNintendo xor a ret ; optimizations in callers rely on this returning with b = 0 GetPaletteCombo: ld hl, PaletteCombinations ld b, 0 ld c, a add hl, bc ret LoadPalettesFromIndex: ; a = index of combination call GetPaletteCombo ; Obj Palettes ld e, 0 .loadObjPalette ld a, [hli] push hl ld hl, Palettes ; b is already 0 ld c, a add hl, bc ld d, 4 * 2 ld c, LOW(rOBPI) call LoadPalettes pop hl bit OAMB_BANK1, e jr nz, .loadBGPalette ld e, OAMF_BANK1 jr .loadObjPalette .loadBGPalette ;BG Palette ld c, [hl] ; b is already 0 ld hl, Palettes add hl, bc ld d, 8 jr LoadBGPalettes LoadPalettesFromHRAM: ld hl, hBgPalettes ld d, hBgPalettesEnd - hBgPalettes LoadBGPalettes: ld e, 0 ld c, LOW(rBGPI) LoadPalettes: ld a, $80 or e ldh [c], a inc c .loop ld a, [hli] ldh [c], a dec d jr nz, .loop ret ClearVRAMViaHDMA: ldh [rVBK], a ld hl, HDMAData _ClearVRAMViaHDMA: call WaitFrame ; Wait for vblank ld c, LOW(rHDMA1) ld b, 5 .loop ld a, [hli] ldh [c], a inc c dec b jr nz, .loop ret ; clobbers AF and HL GetInputPaletteIndex: ld a, P1F_GET_DPAD ldh [rJOYP], a ldh a, [rJOYP] cpl and $F ret z ; No direction keys pressed, no palette ld l, 0 .directionLoop inc l rra jr nc, .directionLoop ; c = 1: Right, 2: Left, 3: Up, 4: Down ld a, P1F_GET_BTN ldh [rJOYP], a ldh a, [rJOYP] cpl rla rla and $C add l ld l, a ldh a, [hInputPalette] cp l ret z ; No change, don't load ld a, l ldh [hInputPalette], a ; Slide into change Animation Palette ChangeAnimationPalette: push bc push de call GetKeyComboPalette call GetPaletteCombo inc l inc l ld c, [hl] ld hl, Palettes + 1 add hl, bc ld a, [hld] cp $7F ; Is white color? jr nz, .isWhite inc hl inc hl .isWhite push af ld a, [hli] push hl ld hl, hBgPalettes ; First color, all palettes call ReplaceColorInAllPalettes ld l, LOW(hBgPalettes + 2) ; Second color, all palettes call ReplaceColorInAllPalettes pop hl ldh [hBgPalettes + 6], a ; Fourth color, first palette ld a, [hli] push hl ld hl, hBgPalettes + 1 ; First color, all palettes call ReplaceColorInAllPalettes ld l, LOW(hBgPalettes + 3) ; Second color, all palettes call ReplaceColorInAllPalettes pop hl ldh [hBgPalettes + 7], a ; Fourth color, first palette pop af jr z, .isNotWhite inc hl inc hl .isNotWhite ; Mixing code by ISSOtm ldh a, [hBgPalettes + 7 * 8 + 2] and ~$21 ld b, a ld a, [hli] and ~$21 add a, b ld b, a ld a, [hBgPalettes + 7 * 8 + 3] res 2, a ; and ~$04, but not touching carry ld c, [hl] res 2, c ; and ~$04, but not touching carry adc a, c rra ; Carry sort of "extends" the accumulator, we're bringing that bit back home ld [hBgPalettes + 7 * 8 + 3], a ld a, b rra ld [hBgPalettes + 7 * 8 + 2], a dec l ld a, [hli] ldh [hBgPalettes + 7 * 8 + 6], a ; Fourth color, 7th palette ld a, [hli] ldh [hBgPalettes + 7 * 8 + 7], a ; Fourth color, 7th palette ld a, [hli] ldh [hBgPalettes + 4], a ; Third color, first palette ld a, [hli] ldh [hBgPalettes + 5], a ; Third color, first palette call WaitFrame call LoadPalettesFromHRAM ; Delay the wait loop while the user is selecting a palette ld a, 48 ldh [hWaitLoopCounter], a pop de pop bc ret ReplaceColorInAllPalettes: ld de, 8 ld c, e .loop ld [hl], a add hl, de dec c jr nz, .loop ret LoadDMGTilemap: push af call WaitFrame ld a, $19 ; Trademark symbol tile ID ld [_SCRN0 + 8 * SCRN_VX_B + 16], a ; ... put in the superscript position ld hl, _SCRN0 + 9 * SCRN_VX_B + 15 ; Bottom right corner of the logo ld c, 12 ; Tiles in a logo row .tilemapLoop dec a jr z, .tilemapDone ldd [hl], a dec c jr nz, .tilemapLoop ld l, $0F ; Jump to top row jr .tilemapLoop .tilemapDone pop af ret BootEnd: IF BootEnd > $0900 FAIL "BootROM overflowed: {BootEnd}" ENDC ds $100 + $800 - @ ; Ensure that the ROM is padded up to standard size. SECTION "HRAM", HRAM[_HRAM] hTitleChecksum: ds 1 hBgPalettes: ds 8 * 4 * 2 hBgPalettesEnd: hInputPalette: ds 1 hWaitLoopCounter: ds 1