Merge branch 'bootrom-2bpp'

This commit is contained in:
Lior Halphon 2020-05-06 23:30:56 +03:00
commit 8f69703726
5 changed files with 415 additions and 619 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 479 B

View File

@ -6,14 +6,12 @@ Start:
ld sp, $fffe ld sp, $fffe
; Clear memory VRAM ; Clear memory VRAM
ld hl, $8000 call ClearMemoryPage8000
call ClearMemoryPage
ld a, 2 ld a, 2
ld c, $70 ld c, $70
ld [c], a ld [c], a
; Clear RAM Bank 2 (Like the original boot ROM ; Clear RAM Bank 2 (Like the original boot ROM)
ld h, $D0 ld h, $D0
xor a
call ClearMemoryPage call ClearMemoryPage
ld [c], a ld [c], a
@ -22,15 +20,6 @@ Start:
; Clear title checksum ; Clear title checksum
ldh [TitleChecksum], a 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 ld a, $80
ldh [$26], a ldh [$26], a
ldh [$11], a ldh [$11], a
@ -39,8 +28,23 @@ Start:
ldh [$25], a ldh [$25], a
ld a, $77 ld a, $77
ldh [$24], a ldh [$24], a
ld hl, $FF30
; Init waveform
ld c, $10
.waveformLoop
ldi [hl], a
cpl
dec c
jr nz, .waveformLoop
call InitWaveform
; Clear OAM
ld h, $fe
ld c, $a0
.clearOAMLoop
ldi [hl], a
dec c
jr nz, .clearOAMLoop
; Init BG palette ; Init BG palette
ld a, $fc ld a, $fc
@ -67,9 +71,7 @@ Start:
; Clear the second VRAM bank ; Clear the second VRAM bank
ld a, 1 ld a, 1
ldh [$4F], a ldh [$4F], a
xor a call ClearMemoryPage8000
ld hl, $8000
call ClearMemoryPage
call LoadTileset call LoadTileset
ld b, 3 ld b, 3
@ -87,20 +89,24 @@ ELSE
.tilemapRowLoop .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 push af
; Switch to second VRAM Bank sub $20
ld a, 1 sub $3
ldh [$4F], a jr nc, .notspecial
ld [hl], 8 add $20
; Switch to back first VRAM Bank call .write_with_palette
xor a dec c
ldh [$4F], a .notspecial
pop af pop af
ldi [hl], a
add d add d ; d = 3 for SameBoy logo, d = 1 for Nintendo logo
dec c dec c
jr nz, .tilemapRowLoop jr nz, .tilemapRowLoop
sub 47 sub 44
push de push de
ld de, $10 ld de, $10
add hl, de add hl, de
@ -116,6 +122,19 @@ ELSE
ld l, $a7 ld l, $a7
ld bc, $0107 ld bc, $0107
jr .tilemapRowLoop 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 .endTilemap
ENDC ENDC
@ -125,38 +144,40 @@ ENDC
ld hl, BgPalettes ld hl, BgPalettes
xor a xor a
.expandPalettesLoop: .expandPalettesLoop:
IF !DEF(FAST)
cpl cpl
ENDC
; One white ; One white
ldi [hl], a ld [hli], a
ldi [hl], a ld [hli], a
IF DEF(FAST) ; Mixed with white
; 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] ld a, [de]
inc de inc e
ldi [hl], a or $20
ld b, a
ld a, [de] ld a, [de]
inc de dec e
ldi [hl], a 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 xor a
; Two blacks
ldi [hl], a
ldi [hl], a
ldi [hl], a
ldi [hl], a
ENDC
dec c dec c
jr nz, .expandPalettesLoop jr nz, .expandPalettesLoop
@ -170,8 +191,10 @@ ENDC
IF !DEF(FAST) IF !DEF(FAST)
call DoIntroAnimation call DoIntroAnimation
ld a, 45
ldh [WaitLoopCounter], a
; Wait ~0.75 seconds ; Wait ~0.75 seconds
ld b, 45 ld b, a
call WaitBFrames call WaitBFrames
; Play first sound ; Play first sound
@ -183,10 +206,6 @@ IF !DEF(FAST)
ld a, $c1 ld a, $c1
call PlaySound call PlaySound
; Wait ~0.5 seconds
ld a, 30
ldh [WaitLoopCounter], a
.waitLoop .waitLoop
call GetInputPaletteIndex call GetInputPaletteIndex
call WaitFrame call WaitFrame
@ -198,6 +217,9 @@ ELSE
call PlaySound call PlaySound
ENDC ENDC
call Preboot call Preboot
IF DEF(AGB)
ld b, 1
ENDC
; Will be filled with NOPs ; Will be filled with NOPs
@ -206,7 +228,6 @@ BootGame:
ldh [$50], a ldh [$50], a
SECTION "MoreStuff", ROM0[$200] SECTION "MoreStuff", ROM0[$200]
; Game Palettes Data ; Game Palettes Data
TitleChecksums: TitleChecksums:
db $00 ; Default db $00 ; Default
@ -510,29 +531,30 @@ Palettes:
dw $4778, $3290, $1D87, $0861 ; DMG LCD dw $4778, $3290, $1D87, $0861 ; DMG LCD
KeyCombinationPalettes KeyCombinationPalettes
db 1 ; Right db 1 * 3 ; Right
db 48 ; Left db 48 * 3 ; Left
db 5 ; Up db 5 * 3 ; Up
db 8 ; Down db 8 * 3 ; Down
db 0 ; Right + A db 0 * 3 ; Right + A
db 40 ; Left + A db 40 * 3 ; Left + A
db 43 ; Up + A db 43 * 3 ; Up + A
db 3 ; Down + A db 3 * 3 ; Down + A
db 6 ; Right + B db 6 * 3 ; Right + B
db 7 ; Left + B db 7 * 3 ; Left + B
db 28 ; Up + B db 28 * 3 ; Up + B
db 49 ; Down + B db 49 * 3 ; Down + B
; SameBoy "Exclusives" ; SameBoy "Exclusives"
db 51 ; Right + A + B db 51 * 3 ; Right + A + B
db 52 ; Left + A + B db 52 * 3 ; Left + A + B
db 53 ; Up + A + B db 53 * 3 ; Up + A + B
db 54 ; Down + A + B db 54 * 3 ; Down + A + B
TrademarkSymbol: TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SameBoyLogo: SameBoyLogo:
incbin "SameBoyLogo.pb8" incbin "SameBoyLogo.pb12"
AnimationColors: AnimationColors:
dw $7FFF ; White dw $7FFF ; White
@ -545,9 +567,6 @@ AnimationColors:
dw $7102 ; Blue dw $7102 ; Blue
AnimationColorsEnd: AnimationColorsEnd:
DMGPalettes:
dw $7FFF, $32BF, $00D0, $0000
; Helper Functions ; Helper Functions
DoubleBitsAndWriteRowTwice: DoubleBitsAndWriteRowTwice:
call .twice call .twice
@ -594,8 +613,11 @@ PlaySound:
ldh [$14], a ldh [$14], a
ret ret
ClearMemoryPage8000:
ld hl, $8000
; Clear from HL to HL | 0x2000 ; Clear from HL to HL | 0x2000
ClearMemoryPage: ClearMemoryPage:
xor a
ldi [hl], a ldi [hl], a
bit 5, h bit 5, h
jr z, ClearMemoryPage jr z, ClearMemoryPage
@ -634,30 +656,28 @@ ReadCGBLogoHalfTile:
ld a, e ld a, e
ret ret
; LoadTileset using PB8 codec, 2019 Damian Yerrick ; LoadTileset using PB12 codec, 2020 Jakub Kądziołka
; ; (based on 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_dst = $8080
SameBoyLogo_length = (128 * 24) / 64 SameBoyLogo_length = (128 * 24) / 64
LoadTileset: LoadTileset:
ld hl, SameBoyLogo ld hl, SameBoyLogo
ld de, SameBoyLogo_dst ld de, SameBoyLogo_dst - 1
ld c, SameBoyLogo_length ld c, SameBoyLogo_length
.pb8BlockLoop: .refill
; Register map for PB8 decompression ; Register map for PB12 decompression
; HL: source address in boot ROM ; HL: source address in boot ROM
; DE: destination address in VRAM ; DE: destination address in VRAM
; A: Current literal value ; A: Current literal value
; B: Repeat bits, terminated by 1000... ; 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, ; Source address in HL lets the repeat bits go straight to B,
; bypassing A and avoiding spilling registers to the stack. ; bypassing A and avoiding spilling registers to the stack.
ld b, [hl] ld b, [hl]
dec b
jr z, .sameboyLogoEnd
inc b
inc hl inc hl
; Shift a 1 into lower bit of shift value. Once this bit ; Shift a 1 into lower bit of shift value. Once this bit
@ -665,26 +685,53 @@ LoadTileset:
scf scf
rl b rl b
.pb8BitLoop: .loop
; If not a repeat, load a literal byte ; If not a repeat, load a literal byte
jr c,.pb8Repeat jr c, .simple_repeat
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 sla b
jr nz, .pb8BitLoop jr c, .shifty_repeat
ld a, [hli]
dec c jr .got_byte
jr nz, .pb8BlockLoop .shifty_repeat
sla b
; End PB8 decoding. The rest uses HL as the destination jr nz, .no_refill_during_shift
ld h, d ld b, [hl] ; see above. Also, no, factoring it out into a callable
ld l, e 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 .sameboyLogoEnd
ld h, d
ld l, $80
; Copy (unresized) ROM logo ; Copy (unresized) ROM logo
ld de, $104 ld de, $104
.CGBROMLogoLoop .CGBROMLogoLoop
@ -711,29 +758,6 @@ ReadTrademarkSymbol:
jr nz, .loadTrademarkSymbolLoop jr nz, .loadTrademarkSymbolLoop
ret 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: DoIntroAnimation:
; Animate the intro ; Animate the intro
ld a, 1 ld a, 1
@ -771,30 +795,75 @@ IF !DEF(FAST)
.fadeLoop .fadeLoop
ld c, 32 ; 32 colors to fade ld c, 32 ; 32 colors to fade
ld hl, BgPalettes ld hl, BgPalettes
push hl
.frameLoop .frameLoop
push bc push bc
call BrightenColor
; 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 pop bc
dec c dec c
jr nz, .frameLoop jr nz, .frameLoop
call WaitFrame call WaitFrame
call WaitFrame pop hl
ld hl, BgPalettes
call LoadBGPalettes64 call LoadBGPalettes64
call WaitFrame
dec b dec b
jr nz, .fadeLoop jr nz, .fadeLoop
ENDC ENDC
ld a, 1
call ClearVRAMViaHDMA call ClearVRAMViaHDMA
; Select the first bank call _ClearVRAMViaHDMA
xor a call ClearVRAMViaHDMA ; A = $40, so it's bank 0
ldh [$4F], a ld a, $ff
cpl
ldh [$00], a ldh [$00], a
call ClearVRAMViaHDMA
; Final values for CGB mode ; Final values for CGB mode
ld de, $ff56 ld d, a
ld e, c
ld l, $0d ld l, $0d
ld a, [$143] ld a, [$143]
@ -818,7 +887,7 @@ IF DEF(AGB)
ld c, a ld c, a
add a, $11 add a, $11
ld h, c ld h, c
ld b, 1 ; B is set to 1 after ret
ELSE ELSE
; Set registers to match the original CGB boot ; Set registers to match the original CGB boot
; AF = $1180, C = 0 ; AF = $1180, C = 0
@ -836,6 +905,14 @@ ENDC
ld a, $1 ld a, $1
ret 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: EmulateDMG:
ld a, 1 ld a, 1
ldh [$6C], a ; DMG Emulation ldh [$6C], a ; DMG Emulation
@ -847,11 +924,7 @@ EmulateDMG:
ldh a, [InputPalette] ldh a, [InputPalette]
and a and a
jr z, .nothingDown jr z, .nothingDown
ld hl, KeyCombinationPalettes - 1 ; Return value is 1-based, 0 means nothing down call GetKeyComboPalette
ld c ,a
ld b, 0
add hl, bc
ld a, [hl]
jr .paletteFromKeys jr .paletteFromKeys
.nothingDown .nothingDown
ld a, b ld a, b
@ -860,8 +933,7 @@ EmulateDMG:
call LoadPalettesFromIndex call LoadPalettesFromIndex
ld a, 4 ld a, 4
; Set the final values for DMG mode ; Set the final values for DMG mode
ld d, 0 ld de, 8
ld e, $8
ld l, $7c ld l, $7c
ret ret
@ -934,16 +1006,15 @@ GetPaletteIndex:
xor a xor a
ret ret
LoadPalettesFromIndex: ; a = index of combination GetPaletteCombo:
ld b, a
; Multiply by 3
add b
add b
ld hl, PaletteCombinations ld hl, PaletteCombinations
ld b, 0 ld b, 0
ld c, a ld c, a
add hl, bc add hl, bc
ret
LoadPalettesFromIndex: ; a = index of combination
call GetPaletteCombo
; Obj Palettes ; Obj Palettes
ld e, 0 ld e, 0
@ -955,7 +1026,8 @@ LoadPalettesFromIndex: ; a = index of combination
ld c, a ld c, a
add hl, bc add hl, bc
ld d, 8 ld d, 8
call LoadObjPalettes ld c, $6A
call LoadPalettes
pop hl pop hl
bit 3, e bit 3, e
jr nz, .loadBGPalette jr nz, .loadBGPalette
@ -969,76 +1041,39 @@ LoadPalettesFromIndex: ; a = index of combination
ld c, a ld c, a
add hl, bc add hl, bc
ld d, 8 ld d, 8
jp LoadBGPalettes jr LoadBGPalettes
BrightenColor: 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 a, [hli]
ld e, a ld [c], a
ld a, [hld] dec d
ld d, a jr nz, .loop
; 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 ret
ClearVRAMViaHDMA: ClearVRAMViaHDMA:
ld hl, $FF51 ldh [$4F], a
ld hl, HDMAData
; Src _ClearVRAMViaHDMA:
ld a, $88 ld c, $51
ld [hli], a ld b, 5
xor a .loop
ld [hli], a ld a, [hli]
ldh [c], a
; Dest inc c
ld a, $98 dec b
ld [hli], a jr nz, .loop
ld a, $A0
ld [hli], a
; Do it
ld [hl], $12
ret ret
GetInputPaletteIndex: GetInputPaletteIndex:
@ -1077,24 +1112,12 @@ GetInputPaletteIndex:
; Slide into change Animation Palette ; Slide into change Animation Palette
ChangeAnimationPalette: ChangeAnimationPalette:
push af
push hl
push bc push bc
push de push de
ld hl, KeyCombinationPalettes - 1 ; Input palettes are 1-based, 0 means nothing down call GetKeyComboPalette
ld c ,a call GetPaletteCombo
ld b, 0 inc l
add hl, bc inc l
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 a, [hl]
ld hl, Palettes + 1 ld hl, Palettes + 1
ld b, 0 ld b, 0
@ -1108,47 +1131,73 @@ ChangeAnimationPalette:
.isWhite .isWhite
push af push af
ld a, [hli] ld a, [hli]
push hl push hl
ld hl, BgPalettes ; First color, all palette ld hl, BgPalettes ; First color, all palettes
call ReplaceColorInAllPalettes
ld l, LOW(BgPalettes + 2) ; Second color, all palettes
call ReplaceColorInAllPalettes call ReplaceColorInAllPalettes
pop hl pop hl
ldh [BgPalettes + 2], a ; Second color, first palette ldh [BgPalettes + 6], a ; Fourth color, first palette
ld a, [hli] ld a, [hli]
push hl push hl
ld hl, BgPalettes + 1 ; First color, all palette ld hl, BgPalettes + 1 ; First color, all palettes
call ReplaceColorInAllPalettes
ld l, LOW(BgPalettes + 3) ; Second color, all palettes
call ReplaceColorInAllPalettes call ReplaceColorInAllPalettes
pop hl pop hl
ldh [BgPalettes + 3], a ; Second color, first palette ldh [BgPalettes + 7], a ; Fourth color, first palette
pop af pop af
jr z, .isNotWhite jr z, .isNotWhite
inc hl inc hl
inc hl inc hl
.isNotWhite .isNotWhite
; Mixing code by ISSOtm
ldh a, [BgPalettes + 7 * 8 + 2]
and ~$21
ld b, a
ld a, [hli] ld a, [hli]
ldh [BgPalettes + 7 * 8 + 2], a ; Second color, 7th palette 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] ld a, [hli]
ldh [BgPalettes + 7 * 8 + 3], a ; Second color, 7th palette 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] ld a, [hli]
ldh [BgPalettes + 4], a ; Third color, first palette ldh [BgPalettes + 4], a ; Third color, first palette
ld a, [hl] ld a, [hli]
ldh [BgPalettes + 5], a ; Third color, first palette ldh [BgPalettes + 5], a ; Third color, first palette
call WaitFrame call WaitFrame
ld hl, BgPalettes ld hl, BgPalettes
call LoadBGPalettes64 call LoadBGPalettes64
; Delay the wait loop while the user is selecting a palette ; Delay the wait loop while the user is selecting a palette
ld a, 30 ld a, 45
ldh [WaitLoopCounter], a ldh [WaitLoopCounter], a
pop de pop de
pop bc pop bc
pop hl
pop af
ret ret
ReplaceColorInAllPalettes: ReplaceColorInAllPalettes:
ld de, 8 ld de, 8
ld c, 8 ld c, e
.loop .loop
ld [hl], a ld [hl], a
add hl, de add hl, de
@ -1159,7 +1208,7 @@ ReplaceColorInAllPalettes:
LoadDMGTilemap: LoadDMGTilemap:
push af push af
call WaitFrame call WaitFrame
ld a,$19 ; Trademark symbol ld a, $19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row ld c,$c ; Tiles in a logo row
@ -1169,27 +1218,20 @@ LoadDMGTilemap:
ldd [hl], a ldd [hl], a
dec c dec c
jr nz, .tilemapLoop jr nz, .tilemapLoop
ld l,$0f ; Jump to top row ld l, $0f ; Jump to top row
jr .tilemapLoop jr .tilemapLoop
.tilemapDone .tilemapDone
pop af pop af
ret ret
InitWaveform: HDMAData:
ld hl, $FF30 db $88, $00, $98, $A0, $12
; Init waveform db $88, $00, $80, $00, $40
xor a
ld c, $10
.waveformLoop
ldi [hl], a
cpl
dec c
jr nz, .waveformLoop
ret
SECTION "ROMMax", ROM0[$900] BootEnd:
; Prevent us from overflowing IF BootEnd > $900
ds 1 FAIL "BootROM overflowed: {BootEnd}"
ENDC
SECTION "HRAM", HRAM[$FF80] SECTION "HRAM", HRAM[$FF80]
TitleChecksum: TitleChecksum:

95
BootROMs/pb12.c Normal file
View File

@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
void opts(uint8_t byte, uint8_t *options)
{
*(options++) = byte | ((byte << 1) & 0xff);
*(options++) = byte & (byte << 1);
*(options++) = byte | ((byte >> 1) & 0xff);
*(options++) = byte & (byte >> 1);
}
int main()
{
static uint8_t source[0x4000];
size_t size = read(STDIN_FILENO, &source, sizeof(source));
unsigned pos = 0;
assert(size <= 0x4000);
while (size && source[size - 1] == 0) {
size--;
}
uint8_t *literals = NULL;
size_t literals_size = 0;
unsigned bits = 0;
unsigned control = 0;
unsigned prev[2] = {-1, -1}; // Unsigned to allow "not set" values
while (true) {
uint8_t byte = 0;
if (pos == size){
if (bits == 0) break;
}
else {
byte = source[pos++];
}
if (byte == prev[0] || byte == prev[1]) {
bits += 2;
control <<= 1;
control |= 1;
control <<= 1;
if (byte == prev[1]) {
control |= 1;
}
}
else {
bits += 2;
control <<= 2;
uint8_t options[4];
opts(prev[1], options);
bool found = false;
for (unsigned i = 0; i < 4; i++) {
if (options[i] == byte) {
// 01 = modify
control |= 1;
bits += 2;
control <<= 2;
control |= i;
found = true;
break;
}
}
if (!found) {
literals = realloc(literals, literals_size++);
literals[literals_size - 1] = byte;
}
}
prev[0] = prev[1];
prev[1] = byte;
if (bits >= 8) {
uint8_t outctl = control >> (bits - 8);
assert(outctl != 1);
write(STDOUT_FILENO, &outctl, 1);
write(STDOUT_FILENO, literals, literals_size);
bits -= 8;
control &= (1 << bits) - 1;
literals_size = 0;
}
}
uint8_t end_byte = 1;
write(STDOUT_FILENO, &end_byte, 1);
if (literals) {
free(literals);
}
return 0;
}

View File

@ -1,341 +0,0 @@
/*
PB8 compressor and decompressor
Copyright 2019 Damian Yerrick
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
// For setting stdin/stdout to binary mode
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <unistd.h>
#define fd_isatty isatty
#elif defined (_WIN32)
#include <io.h>
#include <fcntl.h>
#define fd_isatty _isatty
#endif
/*
; 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
ret
*/
/* Compressor and decompressor *************************************/
/**
* Compresses an input stream to PB8 data on an output stream.
* @param infp input stream
* @param outfp output stream
* @param blocklength size of an independent input block in bytes
* @return 0 for reaching infp end of file, or EOF for error
*/
int pb8(FILE *infp, FILE *outfp, size_t blocklength)
{
blocklength >>= 3; // convert bytes to blocks
assert(blocklength > 0);
while (1) {
int last_byte = EOF; // value that never occurs in a file
for (size_t blkleft = blocklength; blkleft > 0; --blkleft) {
unsigned int control_byte = 0x0001;
unsigned char literals[8];
size_t nliterals = 0;
while (control_byte < 0x100) {
int c = fgetc(infp);
if (c == EOF) break;
control_byte <<= 1;
if (c == last_byte) {
control_byte |= 0x01;
}
else {
literals[nliterals++] = last_byte = c;
}
}
if (control_byte > 1) {
// Fill partial block with repeats
while (control_byte < 0x100) {
control_byte = (control_byte << 1) | 1;
}
// Write control byte and check for write failure
int ok = fputc(control_byte & 0xFF, outfp);
if (ok == EOF) return EOF;
size_t ok2 = fwrite(literals, 1, nliterals, outfp);
if (ok2 < nliterals) return EOF;
}
// If finished, return success or failure
if (ferror(infp) || ferror(outfp)) return EOF;
if (feof(infp)) return 0;
} // End 8-byte block
} // End packet, resetting last_byte
}
/**
* Decompresses PB8 data on an input stream to an output stream.
* @param infp input stream
* @param outfp output stream
* @return 0 for reaching infp end of file, or EOF for error
*/
int unpb8(FILE *infp, FILE *outfp)
{
int last_byte = 0;
while (1) {
int control_byte = fgetc(infp);
if (control_byte == EOF) {
return feof(infp) ? 0 : EOF;
}
control_byte &= 0xFF;
for (size_t bytesleft = 8; bytesleft > 0; --bytesleft) {
if (!(control_byte & 0x80)) {
last_byte = fgetc(infp);
if (last_byte == EOF) return EOF; // read error
}
control_byte <<= 1;
int ok = fputc(last_byte, outfp);
if (ok == EOF) return EOF;
}
}
}
/* CLI frontend ****************************************************/
static inline void set_fd_binary(unsigned int fd)
{
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#else
(void) fd;
#endif
}
static const char *usage_msg =
"usage: pb8 [-d] [-l blocklength] [infile [outfile]]\n"
"Compresses a file using RLE with unary run and literal lengths.\n"
"\n"
"options:\n"
" -d decompress\n"
" -l blocklength allow RLE packets to span up to blocklength\n"
" input bytes (multiple of 8; default 8)\n"
" -h, -?, --help show this usage page\n"
" --version show copyright info\n"
"\n"
"If infile is - or missing, it is standard input.\n"
"If outfile is - or missing, it is standard output.\n"
"You cannot compress to or decompress from a terminal.\n"
;
static const char *version_msg =
"PB8 compressor (C version) v0.01\n"
"Copyright 2019 Damian Yerrick <https://pineight.com/contact/>\n"
"This software is provided 'as-is', without any express or implied\n"
"warranty.\n"
;
static const char *toomanyfilenames_msg =
"pb8: too many filenames; try pb8 --help\n";
int main(int argc, char **argv)
{
const char *infilename = NULL;
const char *outfilename = NULL;
bool decompress = false;
size_t blocklength = 8;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && argv[i][1] != 0) {
if (!strcmp(argv[i], "--help")) {
fputs(usage_msg, stdout);
return 0;
}
if (!strcmp(argv[i], "--version")) {
fputs(version_msg, stdout);
return 0;
}
// -t1 or -t 1
int argtype = argv[i][1];
switch (argtype) {
case 'h':
case '?':
fputs(usage_msg, stdout);
return 0;
case 'd':
decompress = true;
break;
case 'l': {
const char *argvalue = argv[i][2] ? argv[i] + 2 : argv[++i];
const char *endptr = NULL;
unsigned long tvalue = strtoul(argvalue, (char **)&endptr, 10);
if (endptr == argvalue || tvalue == 0 || tvalue > SIZE_MAX) {
fprintf(stderr, "pb8: block length %s not a positive integer\n",
argvalue);
return EXIT_FAILURE;
}
if (tvalue % 8 != 0) {
fprintf(stderr, "pb8: block length %s not a multiple of 8\n",
argvalue);
return EXIT_FAILURE;
}
blocklength = tvalue;
} break;
default:
fprintf(stderr, "pb8: unknown option -%c\n", argtype);
return EXIT_FAILURE;
}
}
else if (!infilename) {
infilename = argv[i];
}
else if (!outfilename) {
outfilename = argv[i];
}
else {
fputs(toomanyfilenames_msg, stderr);
return EXIT_FAILURE;
}
}
if (infilename && !strcmp(infilename, "-")) {
infilename = NULL;
}
if (!infilename && decompress && fd_isatty(0)) {
fputs("pb8: cannot decompress from terminal; try redirecting stdin\n",
stderr);
return EXIT_FAILURE;
}
if (outfilename && !strcmp(outfilename, "-")) {
outfilename = NULL;
}
if (!outfilename && !decompress && fd_isatty(1)) {
fputs("pb8: cannot compress to terminal; try redirecting stdout or pb8 --help\n",
stderr);
return EXIT_FAILURE;
}
FILE *infp = NULL;
if (infilename) {
infp = fopen(infilename, "rb");
if (!infp) {
fprintf(stderr, "pb8: error opening %s ", infilename);
perror("for reading");
return EXIT_FAILURE;
}
}
else {
infp = stdin;
set_fd_binary(0);
}
FILE *outfp = NULL;
if (outfilename) {
outfp = fopen(outfilename, "wb");
if (!outfp) {
fprintf(stderr, "pb8: error opening %s ", outfilename);
perror("for writing");
fclose(infp);
return EXIT_FAILURE;
}
}
else {
outfp = stdout;
set_fd_binary(1);
}
int compfailed = 0;
int has_ferror = 0;
if (decompress) {
compfailed = unpb8(infp, outfp);
}
else {
compfailed = pb8(infp, outfp, blocklength);
}
fflush(outfp);
if (ferror(infp)) {
fprintf(stderr, "pb8: error reading %s\n",
infilename ? infilename : "<stdin>");
has_ferror = EOF;
}
fclose(infp);
if (ferror(outfp)) {
fprintf(stderr, "pb8: error writing %s\n",
outfilename ? outfilename : "<stdout>");
has_ferror = EOF;
}
fclose(outfp);
if (compfailed && !has_ferror) {
fputs("pb8: unknown compression failure\n", stderr);
}
return (compfailed || has_ferror) ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -20,7 +20,7 @@ else
EXESUFFIX:= EXESUFFIX:=
endif endif
PB8_COMPRESS := build/pb8$(EXESUFFIX) PB12_COMPRESS := build/pb12$(EXESUFFIX)
ifeq ($(PLATFORM),Darwin) ifeq ($(PLATFORM),Darwin)
DEFAULT := cocoa DEFAULT := cocoa
@ -382,21 +382,21 @@ $(BIN)/SDL/Shaders: Shaders
# Boot ROMs # Boot ROMs
$(OBJ)/%.1bpp: %.png $(OBJ)/%.2bpp: %.png
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
rgbgfx -d 1 -h -o $@ $< rgbgfx -h -u -o $@ $<
$(OBJ)/BootROMs/SameBoyLogo.pb8: $(OBJ)/BootROMs/SameBoyLogo.1bpp $(PB8_COMPRESS) $(OBJ)/BootROMs/SameBoyLogo.pb12: $(OBJ)/BootROMs/SameBoyLogo.2bpp $(PB12_COMPRESS)
$(realpath $(PB8_COMPRESS)) -l 384 $< $@ $(PB12_COMPRESS) < $< > $@
$(PB8_COMPRESS): BootROMs/pb8.c $(PB12_COMPRESS): BootROMs/pb12.c
$(CC) $< -o $@ $(CC) -Wall -Werror $< -o $@
$(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm $(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm $(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/sgb2_boot: BootROMs/sgb_boot.asm $(BIN)/BootROMs/sgb2_boot: BootROMs/sgb_boot.asm
$(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb8 $(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb12
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $< rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $<
rgblink -o $@.tmp2 $@.tmp rgblink -o $@.tmp2 $@.tmp