; 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
    ld [c], a
    inc c
.loop
    ld a, [hli]
    ld [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
