1240 lines
25 KiB
NASM
1240 lines
25 KiB
NASM
; SameBoy CGB bootstrap ROM
|
|
; Todo: use friendly names for HW registers instead of magic numbers
|
|
SECTION "BootCode", ROM0[$0]
|
|
Start:
|
|
; Init stack pointer
|
|
ld sp, $fffe
|
|
|
|
; Clear memory VRAM
|
|
call ClearMemoryPage8000
|
|
ld a, 2
|
|
ld c, $70
|
|
ld [c], a
|
|
; Clear RAM Bank 2 (Like the original boot ROM)
|
|
ld h, $D0
|
|
call ClearMemoryPage
|
|
ld [c], a
|
|
|
|
; Clear OAM
|
|
ld h, $fe
|
|
ld c, $a0
|
|
.clearOAMLoop
|
|
ldi [hl], a
|
|
dec c
|
|
jr nz, .clearOAMLoop
|
|
|
|
; Init waveform
|
|
ld c, $10
|
|
.waveformLoop
|
|
ldi [hl], a
|
|
cpl
|
|
dec c
|
|
jr nz, .waveformLoop
|
|
|
|
; Clear chosen input palette
|
|
ldh [InputPalette], a
|
|
; Clear title checksum
|
|
ldh [TitleChecksum], a
|
|
|
|
ld a, $80
|
|
ldh [$26], a
|
|
ldh [$11], a
|
|
ld a, $f3
|
|
ldh [$12], a
|
|
ldh [$25], a
|
|
ld a, $77
|
|
ldh [$24], a
|
|
ld hl, $FF30
|
|
|
|
; Init BG palette
|
|
ld a, $fc
|
|
ldh [$47], 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, $104 ; Logo start
|
|
ld hl, $8010 ; 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 $34 ; End of logo
|
|
jr nz, .loadLogoLoop
|
|
call ReadTrademarkSymbol
|
|
|
|
; Clear the second VRAM bank
|
|
ld a, 1
|
|
ldh [$4F], a
|
|
call ClearMemoryPage8000
|
|
call LoadTileset
|
|
|
|
ld b, 3
|
|
IF DEF(FAST)
|
|
xor a
|
|
ldh [$4F], a
|
|
ELSE
|
|
; Load Tilemap
|
|
ld hl, $98C2
|
|
ld d, 3
|
|
ld a, 8
|
|
|
|
.tilemapLoop
|
|
ld c, $10
|
|
|
|
.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
|
|
ld bc, $0107
|
|
jr .tilemapRowLoop
|
|
|
|
.write_with_palette
|
|
push af
|
|
; Switch to second VRAM Bank
|
|
ld a, 1
|
|
ldh [$4F], a
|
|
ld [hl], 8
|
|
; Switch to back first VRAM Bank
|
|
xor a
|
|
ldh [$4F], a
|
|
pop af
|
|
ldi [hl], a
|
|
ret
|
|
.endTilemap
|
|
ENDC
|
|
|
|
; Expand Palettes
|
|
ld de, AnimationColors
|
|
ld c, 8
|
|
ld hl, BgPalettes
|
|
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, $91
|
|
ldh [$40], a
|
|
|
|
IF !DEF(FAST)
|
|
call DoIntroAnimation
|
|
|
|
ld a, 45
|
|
ldh [WaitLoopCounter], a
|
|
; Wait ~0.75 seconds
|
|
ld b, a
|
|
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, WaitLoopCounter
|
|
dec [hl]
|
|
jr nz, .waitLoop
|
|
ELSE
|
|
ld a, $c1
|
|
call PlaySound
|
|
ENDC
|
|
call Preboot
|
|
IF DEF(AGB)
|
|
ld b, 1
|
|
ENDC
|
|
|
|
; Will be filled with NOPs
|
|
|
|
SECTION "BootGame", ROM0[$fe]
|
|
BootGame:
|
|
ldh [$50], a
|
|
|
|
SECTION "MoreStuff", ROM0[$200]
|
|
; 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 | $80 ;
|
|
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:
|
|
palette_index: MACRO ; palette, flags
|
|
db ((\1) * 3) | (\2) ; | $80 means game requires DMG boot tilemap
|
|
ENDM
|
|
palette_index 0, 0 ; Default Palette
|
|
palette_index 4, 0 ; ALLEY WAY
|
|
palette_index 5, 0 ; YAKUMAN
|
|
palette_index 35, 0 ; BASEBALL, (Game and Watch 2)
|
|
palette_index 34, 0 ; TENNIS
|
|
palette_index 3, 0 ; TETRIS
|
|
palette_index 31, 0 ; QIX
|
|
palette_index 15, 0 ; DR.MARIO
|
|
palette_index 10, 0 ; RADARMISSION
|
|
palette_index 5, 0 ; F1RACE
|
|
palette_index 19, 0 ; YOSSY NO TAMAGO
|
|
palette_index 36, 0 ;
|
|
palette_index 7, $80 ; X
|
|
palette_index 37, 0 ; MARIOLAND2
|
|
palette_index 30, 0 ; YOSSY NO COOKIE
|
|
palette_index 44, 0 ; ZELDA
|
|
palette_index 21, 0 ;
|
|
palette_index 32, 0 ;
|
|
palette_index 31, 0 ; TETRIS FLASH
|
|
palette_index 20, 0 ; DONKEY KONG
|
|
palette_index 5, 0 ; MARIO'S PICROSS
|
|
palette_index 33, 0 ;
|
|
palette_index 13, 0 ; POKEMON RED, (GAMEBOYCAMERA G)
|
|
palette_index 14, 0 ; POKEMON GREEN
|
|
palette_index 5, 0 ; PICROSS 2
|
|
palette_index 29, 0 ; YOSSY NO PANEPON
|
|
palette_index 5, 0 ; KIRAKIRA KIDS
|
|
palette_index 18, 0 ; GAMEBOY GALLERY
|
|
palette_index 9, 0 ; POCKETCAMERA
|
|
palette_index 3, 0 ;
|
|
palette_index 2, 0 ; BALLOON KID
|
|
palette_index 26, 0 ; KINGOFTHEZOO
|
|
palette_index 25, 0 ; DMG FOOTBALL
|
|
palette_index 25, 0 ; WORLD CUP
|
|
palette_index 41, 0 ; OTHELLO
|
|
palette_index 42, 0 ; SUPER RC PRO-AM
|
|
palette_index 26, 0 ; DYNABLASTER
|
|
palette_index 45, 0 ; BOY AND BLOB GB2
|
|
palette_index 42, 0 ; MEGAMAN
|
|
palette_index 45, 0 ; STAR WARS-NOA
|
|
palette_index 36, 0 ;
|
|
palette_index 38, 0 ; WAVERACE
|
|
palette_index 26, 0 ;
|
|
palette_index 42, 0 ; LOLO2
|
|
palette_index 30, 0 ; YOSHI'S COOKIE
|
|
palette_index 41, 0 ; MYSTIC QUEST
|
|
palette_index 34, 0 ;
|
|
palette_index 34, 0 ; TOPRANKINGTENNIS
|
|
palette_index 5, 0 ; MANSELL
|
|
palette_index 42, 0 ; MEGAMAN3
|
|
palette_index 6, 0 ; SPACE INVADERS
|
|
palette_index 5, 0 ; GAME&WATCH
|
|
palette_index 33, 0 ; DONKEYKONGLAND95
|
|
palette_index 25, 0 ; ASTEROIDS/MISCMD
|
|
palette_index 42, 0 ; STREET FIGHTER 2
|
|
palette_index 42, 0 ; DEFENDER/JOUST
|
|
palette_index 40, 0 ; KILLERINSTINCT95
|
|
palette_index 2, 0 ; TETRIS BLAST
|
|
palette_index 16, 0 ; PINOCCHIO
|
|
palette_index 25, 0 ;
|
|
palette_index 42, 0 ; BA.TOSHINDEN
|
|
palette_index 42, 0 ; NETTOU KOF 95
|
|
palette_index 5, 0 ;
|
|
palette_index 0, 0 ; TETRIS PLUS
|
|
palette_index 39, 0 ; DONKEYKONGLAND 3
|
|
palette_index 36, 0 ;
|
|
palette_index 22, 0 ; SUPER MARIOLAND
|
|
palette_index 25, 0 ; GOLF
|
|
palette_index 6, 0 ; SOLARSTRIKER
|
|
palette_index 32, 0 ; GBWARS
|
|
palette_index 12, 0 ; KAERUNOTAMENI
|
|
palette_index 36, 0 ;
|
|
palette_index 11, 0 ; POKEMON BLUE
|
|
palette_index 39, 0 ; DONKEYKONGLAND
|
|
palette_index 18, 0 ; GAMEBOY GALLERY2
|
|
palette_index 39, 0 ; DONKEYKONGLAND 2
|
|
palette_index 24, 0 ; KID ICARUS
|
|
palette_index 31, 0 ; TETRIS2
|
|
palette_index 50, 0 ;
|
|
palette_index 17, 0 ; MOGURANYA
|
|
palette_index 46, 0 ;
|
|
palette_index 6, 0 ; GALAGA&GALAXIAN
|
|
palette_index 27, 0 ; BT2RAGNAROKWORLD
|
|
palette_index 0, 0 ; KEN GRIFFEY JR
|
|
palette_index 47, 0 ;
|
|
palette_index 41, 0 ; MAGNETIC SOCCER
|
|
palette_index 41, 0 ; VEGAS STAKES
|
|
palette_index 0, 0 ;
|
|
palette_index 0, 0 ; MILLI/CENTI/PEDE
|
|
palette_index 19, 0 ; MARIO & YOSHI
|
|
palette_index 34, 0 ; SOCCER
|
|
palette_index 23, 0 ; POKEBOM
|
|
palette_index 18, 0 ; G&W GALLERY
|
|
palette_index 29, 0 ; TETRIS ATTACK
|
|
|
|
Dups4thLetterArray:
|
|
db "BEFAARBEKEK R-URAR INAILICE R"
|
|
|
|
; We assume the last three arrays fit in the same $100 byte page!
|
|
|
|
PaletteCombinations:
|
|
palette_comb: MACRO ; Obj0, Obj1, Bg
|
|
db (\1) * 8, (\2) * 8, (\3) *8
|
|
ENDM
|
|
raw_palette_comb: MACRO ; Obj0, Obj1, Bg
|
|
db (\1) * 2, (\2) * 2, (\3) * 2
|
|
ENDM
|
|
palette_comb 4, 4, 29
|
|
palette_comb 18, 18, 18
|
|
palette_comb 20, 20, 20
|
|
palette_comb 24, 24, 24
|
|
palette_comb 9, 9, 9
|
|
palette_comb 0, 0, 0
|
|
palette_comb 27, 27, 27
|
|
palette_comb 5, 5, 5
|
|
palette_comb 12, 12, 12
|
|
palette_comb 26, 26, 26
|
|
palette_comb 16, 8, 8
|
|
palette_comb 4, 28, 28
|
|
palette_comb 4, 2, 2
|
|
palette_comb 3, 4, 4
|
|
palette_comb 4, 29, 29
|
|
palette_comb 28, 4, 28
|
|
palette_comb 2, 17, 2
|
|
palette_comb 16, 16, 8
|
|
palette_comb 4, 4, 7
|
|
palette_comb 4, 4, 18
|
|
palette_comb 4, 4, 20
|
|
palette_comb 19, 19, 9
|
|
raw_palette_comb 4 * 4 - 1, 4 * 4 - 1, 11 * 4
|
|
palette_comb 17, 17, 2
|
|
palette_comb 4, 4, 2
|
|
palette_comb 4, 4, 3
|
|
palette_comb 28, 28, 0
|
|
palette_comb 3, 3, 0
|
|
palette_comb 0, 0, 1
|
|
palette_comb 18, 22, 18
|
|
palette_comb 20, 22, 20
|
|
palette_comb 24, 22, 24
|
|
palette_comb 16, 22, 8
|
|
palette_comb 17, 4, 13
|
|
raw_palette_comb 28 * 4 - 1, 0 * 4, 14 * 4
|
|
raw_palette_comb 28 * 4 - 1, 4 * 4, 15 * 4
|
|
palette_comb 19, 22, 9
|
|
palette_comb 16, 28, 10
|
|
palette_comb 4, 23, 28
|
|
palette_comb 17, 22, 2
|
|
palette_comb 4, 0, 2
|
|
palette_comb 4, 28, 3
|
|
palette_comb 28, 3, 0
|
|
palette_comb 3, 28, 4
|
|
palette_comb 21, 28, 4
|
|
palette_comb 3, 28, 0
|
|
palette_comb 25, 3, 28
|
|
palette_comb 0, 28, 8
|
|
palette_comb 4, 3, 28
|
|
palette_comb 28, 3, 6
|
|
palette_comb 4, 28, 29
|
|
; SameBoy "Exclusives"
|
|
palette_comb 30, 30, 30 ; CGA
|
|
palette_comb 31, 31, 31 ; DMG LCD
|
|
palette_comb 28, 4, 1
|
|
palette_comb 0, 0, 2
|
|
|
|
Palettes:
|
|
dw $7FFF, $32BF, $00D0, $0000
|
|
dw $639F, $4279, $15B0, $04CB
|
|
dw $7FFF, $6E31, $454A, $0000
|
|
dw $7FFF, $1BEF, $0200, $0000
|
|
dw $7FFF, $421F, $1CF2, $0000
|
|
dw $7FFF, $5294, $294A, $0000
|
|
dw $7FFF, $03FF, $012F, $0000
|
|
dw $7FFF, $03EF, $01D6, $0000
|
|
dw $7FFF, $42B5, $3DC8, $0000
|
|
dw $7E74, $03FF, $0180, $0000
|
|
dw $67FF, $77AC, $1A13, $2D6B
|
|
dw $7ED6, $4BFF, $2175, $0000
|
|
dw $53FF, $4A5F, $7E52, $0000
|
|
dw $4FFF, $7ED2, $3A4C, $1CE0
|
|
dw $03ED, $7FFF, $255F, $0000
|
|
dw $036A, $021F, $03FF, $7FFF
|
|
dw $7FFF, $01DF, $0112, $0000
|
|
dw $231F, $035F, $00F2, $0009
|
|
dw $7FFF, $03EA, $011F, $0000
|
|
dw $299F, $001A, $000C, $0000
|
|
dw $7FFF, $027F, $001F, $0000
|
|
dw $7FFF, $03E0, $0206, $0120
|
|
dw $7FFF, $7EEB, $001F, $7C00
|
|
dw $7FFF, $3FFF, $7E00, $001F
|
|
dw $7FFF, $03FF, $001F, $0000
|
|
dw $03FF, $001F, $000C, $0000
|
|
dw $7FFF, $033F, $0193, $0000
|
|
dw $0000, $4200, $037F, $7FFF
|
|
dw $7FFF, $7E8C, $7C00, $0000
|
|
dw $7FFF, $1BEF, $6180, $0000
|
|
; SameBoy "Exclusives"
|
|
dw $7FFF, $7FEA, $7D5F, $0000 ; CGA 1
|
|
dw $4778, $3290, $1D87, $0861 ; DMG LCD
|
|
|
|
KeyCombinationPalettes:
|
|
db 1 * 3 ; Right
|
|
db 48 * 3 ; Left
|
|
db 5 * 3 ; Up
|
|
db 8 * 3 ; Down
|
|
db 0 * 3 ; Right + A
|
|
db 40 * 3 ; Left + A
|
|
db 43 * 3 ; Up + A
|
|
db 3 * 3 ; Down + A
|
|
db 6 * 3 ; Right + B
|
|
db 7 * 3 ; Left + B
|
|
db 28 * 3 ; Up + B
|
|
db 49 * 3 ; Down + B
|
|
; SameBoy "Exclusives"
|
|
db 51 * 3 ; Right + A + B
|
|
db 52 * 3 ; Left + A + B
|
|
db 53 * 3 ; Up + A + B
|
|
db 54 * 3 ; Down + A + B
|
|
|
|
TrademarkSymbol:
|
|
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
|
|
|
|
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
|
|
dw $7102 ; Blue
|
|
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, $FF0F
|
|
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 [$13], a
|
|
ld a, $87
|
|
ldh [$14], a
|
|
ret
|
|
|
|
ClearMemoryPage8000:
|
|
ld hl, $8000
|
|
; 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, $f 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)
|
|
|
|
SameBoyLogo_dst = $8080
|
|
SameBoyLogo_length = (128 * 24) / 64
|
|
|
|
LoadTileset:
|
|
ld hl, SameBoyLogo
|
|
ld de, SameBoyLogo_dst - 1
|
|
ld c, SameBoyLogo_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, $104
|
|
.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,$08
|
|
.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 [$4F], a
|
|
ld d, 26
|
|
.animationLoop
|
|
ld b, 2
|
|
call WaitBFrames
|
|
ld hl, $98C0
|
|
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, 32 ; 32 colors to fade
|
|
ld hl, BgPalettes
|
|
.frameLoop
|
|
push bc
|
|
|
|
; Brighten Color
|
|
ld a, [hli]
|
|
ld e, a
|
|
ld a, [hld]
|
|
ld d, a
|
|
; RGB(1,1,1)
|
|
ld bc, $421
|
|
|
|
; 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
|
|
|
|
; add de, bc
|
|
; ld [hli], de
|
|
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, 1
|
|
call ClearVRAMViaHDMA
|
|
call _ClearVRAMViaHDMA
|
|
call ClearVRAMViaHDMA ; A = $40, so it's bank 0
|
|
ld a, $ff
|
|
ldh [$00], a
|
|
|
|
; Final values for CGB mode
|
|
ld d, a
|
|
ld e, c
|
|
ld l, $0d
|
|
|
|
ld a, [$143]
|
|
bit 7, a
|
|
call z, EmulateDMG
|
|
bit 7, a
|
|
|
|
ldh [$4C], a
|
|
ldh a, [TitleChecksum]
|
|
ld b, a
|
|
|
|
jr z, .skipDMGForCGBCheck
|
|
ldh a, [InputPalette]
|
|
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, $11
|
|
ld h, c
|
|
; B is set to 1 after ret
|
|
ELSE
|
|
; Set registers to match the original CGB boot
|
|
; AF = $1180, C = 0
|
|
xor a
|
|
ld c, a
|
|
ld a, $11
|
|
ld h, c
|
|
; B is set to the title checksum
|
|
ENDC
|
|
ret
|
|
|
|
.emulateDMGForCGBGame
|
|
call EmulateDMG
|
|
ldh [$4C], a
|
|
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 [$6C], a ; DMG Emulation
|
|
call GetPaletteIndex
|
|
bit 7, a
|
|
call nz, LoadDMGTilemap
|
|
and $7F
|
|
ld b, a
|
|
ldh a, [InputPalette]
|
|
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, $14B
|
|
ld a, [hl] ; Old Licensee
|
|
cp $33
|
|
jr z, .newLicensee
|
|
dec a ; 1 = Nintendo
|
|
jr nz, .notNintendo
|
|
jr .doChecksum
|
|
.newLicensee
|
|
ld l, $44
|
|
ld a, [hli]
|
|
cp "0"
|
|
jr nz, .notNintendo
|
|
ld a, [hl]
|
|
cp "1"
|
|
jr nz, .notNintendo
|
|
|
|
.doChecksum
|
|
ld l, $34
|
|
ld c, $10
|
|
xor a
|
|
|
|
.checksumLoop
|
|
add [hl]
|
|
inc l
|
|
dec c
|
|
jr nz, .checksumLoop
|
|
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
|
|
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, [$134 + 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 [TitleChecksum], 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, 8
|
|
ld c, $6A
|
|
call LoadPalettes
|
|
pop hl
|
|
bit 3, e
|
|
jr nz, .loadBGPalette
|
|
ld e, 8
|
|
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, BgPalettes
|
|
ld d, 64
|
|
|
|
LoadBGPalettes:
|
|
ld e, 0
|
|
ld c, $68
|
|
|
|
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 [$4F], a
|
|
ld hl, HDMAData
|
|
_ClearVRAMViaHDMA:
|
|
ld c, $51
|
|
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, $20 ; Select directions
|
|
ldh [$00], a
|
|
ldh a, [$00]
|
|
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, $10 ; Select buttons
|
|
ldh [$00], a
|
|
ldh a, [$00]
|
|
cpl
|
|
rla
|
|
rla
|
|
and $C
|
|
add l
|
|
ld l, a
|
|
ldh a, [InputPalette]
|
|
cp l
|
|
ret z ; No change, don't load
|
|
ld a, l
|
|
ldh [InputPalette], 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, BgPalettes ; First color, all palettes
|
|
call ReplaceColorInAllPalettes
|
|
ld l, LOW(BgPalettes + 2) ; Second color, all palettes
|
|
call ReplaceColorInAllPalettes
|
|
pop hl
|
|
ldh [BgPalettes + 6], a ; Fourth color, first palette
|
|
|
|
ld a, [hli]
|
|
push hl
|
|
ld hl, BgPalettes + 1 ; First color, all palettes
|
|
call ReplaceColorInAllPalettes
|
|
ld l, LOW(BgPalettes + 3) ; Second color, all palettes
|
|
call ReplaceColorInAllPalettes
|
|
pop hl
|
|
ldh [BgPalettes + 7], a ; Fourth color, first palette
|
|
|
|
pop af
|
|
jr z, .isNotWhite
|
|
inc hl
|
|
inc hl
|
|
.isNotWhite
|
|
; Mixing code by ISSOtm
|
|
ldh a, [BgPalettes + 7 * 8 + 2]
|
|
and ~$21
|
|
ld b, a
|
|
ld a, [hli]
|
|
and ~$21
|
|
add a, b
|
|
ld b, a
|
|
ld a, [BgPalettes + 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 [BgPalettes + 7 * 8 + 3], a
|
|
ld a, b
|
|
rra
|
|
ld [BgPalettes + 7 * 8 + 2], a
|
|
dec l
|
|
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 7 * 8 + 6], a ; Fourth color, 7th palette
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 7 * 8 + 7], a ; Fourth color, 7th palette
|
|
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 4], a ; Third color, first palette
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 5], a ; Third color, first palette
|
|
|
|
|
|
call WaitFrame
|
|
call LoadPalettesFromHRAM
|
|
; Delay the wait loop while the user is selecting a palette
|
|
ld a, 45
|
|
ldh [WaitLoopCounter], 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
|
|
ld [$9910], a ; ... put in the superscript position
|
|
ld hl,$992f ; Bottom right corner of the logo
|
|
ld c,$c ; 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
|
|
|
|
HDMAData:
|
|
db $88, $00, $98, $A0, $12
|
|
db $88, $00, $80, $00, $40
|
|
|
|
BootEnd:
|
|
IF BootEnd > $900
|
|
FAIL "BootROM overflowed: {BootEnd}"
|
|
ENDC
|
|
|
|
SECTION "HRAM", HRAM[$FF80]
|
|
TitleChecksum:
|
|
ds 1
|
|
BgPalettes:
|
|
ds 8 * 4 * 2
|
|
InputPalette:
|
|
ds 1
|
|
WaitLoopCounter:
|
|
ds 1
|