4504de828a
The logo is compressed using PB8, a form of RLE with unary-coded run lengths. Each block representing 8 bytes consists of a control byte, where each bit (MSB to LSB) is 0 for literal or 1 for repeat previous, followed by the literals in that block. PB8 compression is also used in a few NES games. A variant called PB16, where 1 means repeat 2 bytes back, is used in the Game Boy port of 240p Test Suite and in Libbet and the Magic Floor. Switching from logo-compress RLE to PB8 decreases the compressed logo data size from 287 bytes to 253 bytes, saving 34 bytes. The decompression code is also about 10 bytes smaller.
1203 lines
23 KiB
NASM
1203 lines
23 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
|
|
ld hl, $8000
|
|
call ClearMemoryPage
|
|
ld a, 2
|
|
ld c, $70
|
|
ld [c], a
|
|
; Clear RAM Bank 2 (Like the original boot ROM
|
|
ld h, $D0
|
|
xor a
|
|
call ClearMemoryPage
|
|
ld [c], a
|
|
|
|
; Clear chosen input palette
|
|
ldh [InputPalette], a
|
|
; Clear title checksum
|
|
ldh [TitleChecksum], a
|
|
|
|
; Clear OAM
|
|
ld h, $fe
|
|
ld c, $a0
|
|
.clearOAMLoop
|
|
ldi [hl], a
|
|
dec c
|
|
jr nz, .clearOAMLoop
|
|
|
|
; Init Audio
|
|
ld a, $80
|
|
ldh [$26], a
|
|
ldh [$11], a
|
|
ld a, $f3
|
|
ldh [$12], a
|
|
ldh [$25], a
|
|
ld a, $77
|
|
ldh [$24], a
|
|
|
|
call InitWaveform
|
|
|
|
; 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
|
|
xor a
|
|
ld hl, $8000
|
|
call ClearMemoryPage
|
|
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
|
|
|
|
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
|
|
add d
|
|
dec c
|
|
jr nz, .tilemapRowLoop
|
|
sub 47
|
|
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
|
|
.endTilemap
|
|
ENDC
|
|
|
|
; Expand Palettes
|
|
ld de, AnimationColors
|
|
ld c, 8
|
|
ld hl, BgPalettes
|
|
xor a
|
|
.expandPalettesLoop:
|
|
IF !DEF(FAST)
|
|
cpl
|
|
ENDC
|
|
; One white
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
|
|
IF DEF(FAST)
|
|
; 3 more whites
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ELSE
|
|
; The actual color
|
|
ld a, [de]
|
|
inc de
|
|
ldi [hl], a
|
|
ld a, [de]
|
|
inc de
|
|
ldi [hl], a
|
|
|
|
xor a
|
|
; Two blacks
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ldi [hl], a
|
|
ENDC
|
|
|
|
dec c
|
|
jr nz, .expandPalettesLoop
|
|
|
|
ld hl, BgPalettes
|
|
call LoadBGPalettes64
|
|
|
|
; Turn on LCD
|
|
ld a, $91
|
|
ldh [$40], a
|
|
|
|
IF !DEF(FAST)
|
|
call DoIntroAnimation
|
|
|
|
; Wait ~0.75 seconds
|
|
ld b, 45
|
|
call WaitBFrames
|
|
|
|
; Play first sound
|
|
ld a, $83
|
|
call PlaySound
|
|
ld b, 5
|
|
call WaitBFrames
|
|
; Play second sound
|
|
ld a, $c1
|
|
call PlaySound
|
|
|
|
; Wait ~0.5 seconds
|
|
ld a, 30
|
|
ldh [WaitLoopCounter], a
|
|
|
|
.waitLoop
|
|
call GetInputPaletteIndex
|
|
call WaitFrame
|
|
ld hl, WaitLoopCounter
|
|
dec [hl]
|
|
jr nz, .waitLoop
|
|
ELSE
|
|
ld a, $c1
|
|
call PlaySound
|
|
ENDC
|
|
call Preboot
|
|
|
|
; 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:
|
|
; | $80 means game requires DMG boot tilemap
|
|
db 0 ; Default Palette
|
|
db 4 ; ALLEY WAY
|
|
db 5 ; YAKUMAN
|
|
db 35 ; BASEBALL, (Game and Watch 2)
|
|
db 34 ; TENNIS
|
|
db 3 ; TETRIS
|
|
db 31 ; QIX
|
|
db 15 ; DR.MARIO
|
|
db 10 ; RADARMISSION
|
|
db 5 ; F1RACE
|
|
db 19 ; YOSSY NO TAMAGO
|
|
db 36 ;
|
|
db 7 | $80 ; X
|
|
db 37 ; MARIOLAND2
|
|
db 30 ; YOSSY NO COOKIE
|
|
db 44 ; ZELDA
|
|
db 21 ;
|
|
db 32 ;
|
|
db 31 ; TETRIS FLASH
|
|
db 20 ; DONKEY KONG
|
|
db 5 ; MARIO'S PICROSS
|
|
db 33 ;
|
|
db 13 ; POKEMON RED, (GAMEBOYCAMERA G)
|
|
db 14 ; POKEMON GREEN
|
|
db 5 ; PICROSS 2
|
|
db 29 ; YOSSY NO PANEPON
|
|
db 5 ; KIRAKIRA KIDS
|
|
db 18 ; GAMEBOY GALLERY
|
|
db 9 ; POCKETCAMERA
|
|
db 3 ;
|
|
db 2 ; BALLOON KID
|
|
db 26 ; KINGOFTHEZOO
|
|
db 25 ; DMG FOOTBALL
|
|
db 25 ; WORLD CUP
|
|
db 41 ; OTHELLO
|
|
db 42 ; SUPER RC PRO-AM
|
|
db 26 ; DYNABLASTER
|
|
db 45 ; BOY AND BLOB GB2
|
|
db 42 ; MEGAMAN
|
|
db 45 ; STAR WARS-NOA
|
|
db 36 ;
|
|
db 38 ; WAVERACE
|
|
db 26 ;
|
|
db 42 ; LOLO2
|
|
db 30 ; YOSHI'S COOKIE
|
|
db 41 ; MYSTIC QUEST
|
|
db 34 ;
|
|
db 34 ; TOPRANKINGTENNIS
|
|
db 5 ; MANSELL
|
|
db 42 ; MEGAMAN3
|
|
db 6 ; SPACE INVADERS
|
|
db 5 ; GAME&WATCH
|
|
db 33 ; DONKEYKONGLAND95
|
|
db 25 ; ASTEROIDS/MISCMD
|
|
db 42 ; STREET FIGHTER 2
|
|
db 42 ; DEFENDER/JOUST
|
|
db 40 ; KILLERINSTINCT95
|
|
db 2 ; TETRIS BLAST
|
|
db 16 ; PINOCCHIO
|
|
db 25 ;
|
|
db 42 ; BA.TOSHINDEN
|
|
db 42 ; NETTOU KOF 95
|
|
db 5 ;
|
|
db 0 ; TETRIS PLUS
|
|
db 39 ; DONKEYKONGLAND 3
|
|
db 36 ;
|
|
db 22 ; SUPER MARIOLAND
|
|
db 25 ; GOLF
|
|
db 6 ; SOLARSTRIKER
|
|
db 32 ; GBWARS
|
|
db 12 ; KAERUNOTAMENI
|
|
db 36 ;
|
|
db 11 ; POKEMON BLUE
|
|
db 39 ; DONKEYKONGLAND
|
|
db 18 ; GAMEBOY GALLERY2
|
|
db 39 ; DONKEYKONGLAND 2
|
|
db 24 ; KID ICARUS
|
|
db 31 ; TETRIS2
|
|
db 50 ;
|
|
db 17 ; MOGURANYA
|
|
db 46 ;
|
|
db 6 ; GALAGA&GALAXIAN
|
|
db 27 ; BT2RAGNAROKWORLD
|
|
db 0 ; KEN GRIFFEY JR
|
|
db 47 ;
|
|
db 41 ; MAGNETIC SOCCER
|
|
db 41 ; VEGAS STAKES
|
|
db 0 ;
|
|
db 0 ; MILLI/CENTI/PEDE
|
|
db 19 ; MARIO & YOSHI
|
|
db 34 ; SOCCER
|
|
db 23 ; POKEBOM
|
|
db 18 ; G&W GALLERY
|
|
db 29 ; 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 ; Right
|
|
db 48 ; Left
|
|
db 5 ; Up
|
|
db 8 ; Down
|
|
db 0 ; Right + A
|
|
db 40 ; Left + A
|
|
db 43 ; Up + A
|
|
db 3 ; Down + A
|
|
db 6 ; Right + B
|
|
db 7 ; Left + B
|
|
db 28 ; Up + B
|
|
db 49 ; Down + B
|
|
; SameBoy "Exclusives"
|
|
db 51 ; Right + A + B
|
|
db 52 ; Left + A + B
|
|
db 53 ; Up + A + B
|
|
db 54 ; Down + A + B
|
|
|
|
TrademarkSymbol:
|
|
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
|
|
|
|
SameBoyLogo:
|
|
incbin "SameBoyLogo.pb8"
|
|
|
|
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:
|
|
|
|
DMGPalettes:
|
|
dw $7FFF, $32BF, $00D0, $0000
|
|
|
|
; 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
|
|
|
|
; Clear from HL to HL | 0x2000
|
|
ClearMemoryPage:
|
|
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 PB8 codec, 2019 Damian Yerrick
|
|
;
|
|
; The logo is compressed using PB8, a form of RLE with unary-coded
|
|
; run lengths. Each block representing 8 bytes consists of a control
|
|
; byte, where each bit (MSB to LSB) is 0 for literal or 1 for repeat
|
|
; previous, followed by the literals in that block.
|
|
|
|
SameBoyLogo_dst = $8080
|
|
SameBoyLogo_length = (128 * 24) / 64
|
|
|
|
LoadTileset:
|
|
ld hl, SameBoyLogo
|
|
ld de, SameBoyLogo_dst
|
|
ld c, SameBoyLogo_length
|
|
.pb8BlockLoop:
|
|
; Register map for PB8 decompression
|
|
; HL: source address in boot ROM
|
|
; DE: destination address in VRAM
|
|
; A: Current literal value
|
|
; B: Repeat bits, terminated by 1000...
|
|
; C: Number of 8-byte blocks left in this block
|
|
; Source address in HL lets the repeat bits go straight to B,
|
|
; bypassing A and avoiding spilling registers to the stack.
|
|
ld b, [hl]
|
|
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
|
|
|
|
.pb8BitLoop:
|
|
; If not a repeat, load a literal byte
|
|
jr c,.pb8Repeat
|
|
ld a, [hli]
|
|
.pb8Repeat:
|
|
; Decompressed data uses colors 0 and 1, so write once, inc twice
|
|
ld [de], a
|
|
inc de
|
|
inc de
|
|
sla b
|
|
jr nz, .pb8BitLoop
|
|
|
|
dec c
|
|
jr nz, .pb8BlockLoop
|
|
|
|
; End PB8 decoding. The rest uses HL as the destination
|
|
ld h, d
|
|
ld l, e
|
|
|
|
.sameboyLogoEnd
|
|
; 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
|
|
|
|
LoadObjPalettes:
|
|
ld c, $6A
|
|
jr LoadPalettes
|
|
|
|
LoadBGPalettes64:
|
|
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
|
|
|
|
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
|
|
call BrightenColor
|
|
pop bc
|
|
dec c
|
|
jr nz, .frameLoop
|
|
|
|
call WaitFrame
|
|
call WaitFrame
|
|
ld hl, BgPalettes
|
|
call LoadBGPalettes64
|
|
dec b
|
|
jr nz, .fadeLoop
|
|
ENDC
|
|
call ClearVRAMViaHDMA
|
|
; Select the first bank
|
|
xor a
|
|
ldh [$4F], a
|
|
cpl
|
|
ldh [$00], a
|
|
call ClearVRAMViaHDMA
|
|
|
|
; Final values for CGB mode
|
|
ld de, $ff56
|
|
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
|
|
ld b, 1
|
|
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
|
|
|
|
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
|
|
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]
|
|
jr .paletteFromKeys
|
|
.nothingDown
|
|
ld a, b
|
|
.paletteFromKeys
|
|
call WaitFrame
|
|
call LoadPalettesFromIndex
|
|
ld a, 4
|
|
; Set the final values for DMG mode
|
|
ld d, 0
|
|
ld e, $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
|
|
|
|
LoadPalettesFromIndex: ; a = index of combination
|
|
ld b, a
|
|
; Multiply by 3
|
|
add b
|
|
add b
|
|
|
|
ld hl, PaletteCombinations
|
|
ld b, 0
|
|
ld c, a
|
|
add hl, bc
|
|
|
|
; Obj Palettes
|
|
ld e, 0
|
|
.loadObjPalette
|
|
ld a, [hli]
|
|
push hl
|
|
ld hl, Palettes
|
|
ld b, 0
|
|
ld c, a
|
|
add hl, bc
|
|
ld d, 8
|
|
call LoadObjPalettes
|
|
pop hl
|
|
bit 3, e
|
|
jr nz, .loadBGPalette
|
|
ld e, 8
|
|
jr .loadObjPalette
|
|
.loadBGPalette
|
|
;BG Palette
|
|
ld a, [hli]
|
|
ld hl, Palettes
|
|
ld b, 0
|
|
ld c, a
|
|
add hl, bc
|
|
ld d, 8
|
|
jp LoadBGPalettes
|
|
|
|
BrightenColor:
|
|
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
|
|
res 0, c
|
|
.blueNotMaxed
|
|
|
|
; Is green maxed?
|
|
ld a, e
|
|
and $E0
|
|
cp $E0
|
|
jr nz, .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 to bc
|
|
push hl
|
|
ld h, d
|
|
ld l, e
|
|
add hl, bc
|
|
ld d, h
|
|
ld e, l
|
|
pop hl
|
|
|
|
ld a, e
|
|
ld [hli], a
|
|
ld a, d
|
|
ld [hli], a
|
|
ret
|
|
|
|
ClearVRAMViaHDMA:
|
|
ld hl, $FF51
|
|
|
|
; Src
|
|
ld a, $88
|
|
ld [hli], a
|
|
xor a
|
|
ld [hli], a
|
|
|
|
; Dest
|
|
ld a, $98
|
|
ld [hli], a
|
|
ld a, $A0
|
|
ld [hli], a
|
|
|
|
; Do it
|
|
ld [hl], $12
|
|
ret
|
|
|
|
GetInputPaletteIndex:
|
|
ld a, $20 ; Select directions
|
|
ldh [$00], a
|
|
ldh a, [$00]
|
|
cpl
|
|
and $F
|
|
ret z ; No direction keys pressed, no palette
|
|
push bc
|
|
ld c, 0
|
|
|
|
.directionLoop
|
|
inc c
|
|
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 c
|
|
ld b, a
|
|
ldh a, [InputPalette]
|
|
ld c, a
|
|
ld a, b
|
|
ldh [InputPalette], a
|
|
cp c
|
|
pop bc
|
|
ret z ; No change, don't load
|
|
; Slide into change Animation Palette
|
|
|
|
ChangeAnimationPalette:
|
|
push af
|
|
push hl
|
|
push bc
|
|
push de
|
|
ld hl, KeyCombinationPalettes - 1 ; Input palettes are 1-based, 0 means nothing down
|
|
ld c ,a
|
|
ld b, 0
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld b, a
|
|
; Multiply by 3
|
|
add b
|
|
add b
|
|
|
|
ld hl, PaletteCombinations + 2; Background Palette
|
|
ld b, 0
|
|
ld c, a
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld hl, Palettes + 1
|
|
ld b, 0
|
|
ld c, a
|
|
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 palette
|
|
call ReplaceColorInAllPalettes
|
|
pop hl
|
|
ldh [BgPalettes + 2], a ; Second color, first palette
|
|
|
|
ld a, [hli]
|
|
push hl
|
|
ld hl, BgPalettes + 1 ; First color, all palette
|
|
call ReplaceColorInAllPalettes
|
|
pop hl
|
|
ldh [BgPalettes + 3], a ; Second color, first palette
|
|
pop af
|
|
jr z, .isNotWhite
|
|
inc hl
|
|
inc hl
|
|
.isNotWhite
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 7 * 8 + 2], a ; Second color, 7th palette
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 7 * 8 + 3], a ; Second color, 7th palette
|
|
ld a, [hli]
|
|
ldh [BgPalettes + 4], a ; Third color, first palette
|
|
ld a, [hl]
|
|
ldh [BgPalettes + 5], a ; Third color, first palette
|
|
|
|
call WaitFrame
|
|
ld hl, BgPalettes
|
|
call LoadBGPalettes64
|
|
; Delay the wait loop while the user is selecting a palette
|
|
ld a, 30
|
|
ldh [WaitLoopCounter], a
|
|
pop de
|
|
pop bc
|
|
pop hl
|
|
pop af
|
|
ret
|
|
|
|
ReplaceColorInAllPalettes:
|
|
ld de, 8
|
|
ld c, 8
|
|
.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
|
|
|
|
InitWaveform:
|
|
ld hl, $FF30
|
|
; Init waveform
|
|
xor a
|
|
ld c, $10
|
|
.waveformLoop
|
|
ldi [hl], a
|
|
cpl
|
|
dec c
|
|
jr nz, .waveformLoop
|
|
ret
|
|
|
|
SECTION "ROMMax", ROM0[$900]
|
|
; Prevent us from overflowing
|
|
ds 1
|
|
|
|
SECTION "HRAM", HRAM[$FF80]
|
|
TitleChecksum:
|
|
ds 1
|
|
BgPalettes:
|
|
ds 8 * 4 * 2
|
|
InputPalette:
|
|
ds 1
|
|
WaitLoopCounter:
|
|
ds 1
|