diff --git a/BootROMs/SameBoyLogo.png b/BootROMs/SameBoyLogo.png index 4bc9706..c7cfc08 100644 Binary files a/BootROMs/SameBoyLogo.png and b/BootROMs/SameBoyLogo.png differ diff --git a/BootROMs/cgb_boot.asm b/BootROMs/cgb_boot.asm index 6ae869b..f07fbcf 100644 --- a/BootROMs/cgb_boot.asm +++ b/BootROMs/cgb_boot.asm @@ -6,31 +6,20 @@ Start: ld sp, $fffe ; Clear memory VRAM - ld hl, $8000 - call ClearMemoryPage + call ClearMemoryPage8000 ld a, 2 ld c, $70 ld [c], a -; Clear RAM Bank 2 (Like the original boot ROM +; 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 @@ -39,9 +28,24 @@ Start: ldh [$25], a ld a, $77 ldh [$24], a - - call InitWaveform + ld hl, $FF30 +; Init waveform + ld c, $10 +.waveformLoop + ldi [hl], a + cpl + dec c + jr nz, .waveformLoop + +; Clear OAM + ld h, $fe + ld c, $a0 +.clearOAMLoop + ldi [hl], a + dec c + jr nz, .clearOAMLoop + ; Init BG palette ld a, $fc ldh [$47], a @@ -67,9 +71,7 @@ Start: ; Clear the second VRAM bank ld a, 1 ldh [$4F], a - xor a - ld hl, $8000 - call ClearMemoryPage + call ClearMemoryPage8000 call LoadTileset ld b, 3 @@ -87,20 +89,24 @@ ELSE .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 - ; 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 + sub $20 + sub $3 + jr nc, .notspecial + add $20 + call .write_with_palette + dec c +.notspecial pop af - ldi [hl], a - add d + + add d ; d = 3 for SameBoy logo, d = 1 for Nintendo logo dec c jr nz, .tilemapRowLoop - sub 47 + sub 44 push de ld de, $10 add hl, de @@ -116,6 +122,19 @@ ELSE 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 @@ -125,38 +144,40 @@ ENDC 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 [hli], a + ld [hli], a + + ; Mixed with white ld a, [de] - inc de - ldi [hl], a + inc e + or $20 + ld b, a + ld a, [de] - inc de - ldi [hl], a - + 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 - ; Two blacks - ldi [hl], a - ldi [hl], a - ldi [hl], a - ldi [hl], a -ENDC - dec c jr nz, .expandPalettesLoop @@ -170,8 +191,10 @@ ENDC IF !DEF(FAST) call DoIntroAnimation + ld a, 45 + ldh [WaitLoopCounter], a ; Wait ~0.75 seconds - ld b, 45 + ld b, a call WaitBFrames ; Play first sound @@ -182,10 +205,6 @@ IF !DEF(FAST) ; Play second sound ld a, $c1 call PlaySound - -; Wait ~0.5 seconds - ld a, 30 - ldh [WaitLoopCounter], a .waitLoop call GetInputPaletteIndex @@ -198,6 +217,9 @@ ELSE call PlaySound ENDC call Preboot +IF DEF(AGB) + ld b, 1 +ENDC ; Will be filled with NOPs @@ -206,7 +228,6 @@ BootGame: ldh [$50], a SECTION "MoreStuff", ROM0[$200] - ; Game Palettes Data TitleChecksums: db $00 ; Default @@ -510,29 +531,30 @@ Palettes: 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 + 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 ; Right + A + B - db 52 ; Left + A + B - db 53 ; Up + A + B - db 54 ; Down + A + B - + 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.pb8" + incbin "SameBoyLogo.pb12" + AnimationColors: dw $7FFF ; White @@ -545,9 +567,6 @@ AnimationColors: dw $7102 ; Blue AnimationColorsEnd: -DMGPalettes: - dw $7FFF, $32BF, $00D0, $0000 - ; Helper Functions DoubleBitsAndWriteRowTwice: call .twice @@ -594,8 +613,11 @@ PlaySound: 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 @@ -634,30 +656,28 @@ ReadCGBLogoHalfTile: 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. +; 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 + ld de, SameBoyLogo_dst - 1 ld c, SameBoyLogo_length -.pb8BlockLoop: - ; Register map for PB8 decompression +.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... - ; 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] + dec b + jr z, .sameboyLogoEnd + inc b inc hl ; Shift a 1 into lower bit of shift value. Once this bit @@ -665,26 +685,53 @@ LoadTileset: scf rl b -.pb8BitLoop: +.loop ; 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 + jr c, .simple_repeat 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 + 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 @@ -711,29 +758,6 @@ ReadTrademarkSymbol: 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 @@ -771,30 +795,75 @@ IF !DEF(FAST) .fadeLoop ld c, 32 ; 32 colors to fade ld hl, BgPalettes + push hl .frameLoop 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 + dec c jr nz, .frameLoop call WaitFrame - call WaitFrame - ld hl, BgPalettes + pop hl call LoadBGPalettes64 + call WaitFrame dec b jr nz, .fadeLoop ENDC + ld a, 1 call ClearVRAMViaHDMA - ; Select the first bank - xor a - ldh [$4F], a - cpl + call _ClearVRAMViaHDMA + call ClearVRAMViaHDMA ; A = $40, so it's bank 0 + ld a, $ff ldh [$00], a - call ClearVRAMViaHDMA ; Final values for CGB mode - ld de, $ff56 + ld d, a + ld e, c ld l, $0d ld a, [$143] @@ -818,7 +887,7 @@ IF DEF(AGB) ld c, a add a, $11 ld h, c - ld b, 1 + ; B is set to 1 after ret ELSE ; Set registers to match the original CGB boot ; AF = $1180, C = 0 @@ -835,6 +904,14 @@ ENDC 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 @@ -847,11 +924,7 @@ EmulateDMG: 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] + call GetKeyComboPalette jr .paletteFromKeys .nothingDown ld a, b @@ -860,8 +933,7 @@ EmulateDMG: call LoadPalettesFromIndex ld a, 4 ; Set the final values for DMG mode - ld d, 0 - ld e, $8 + ld de, 8 ld l, $7c ret @@ -933,17 +1005,16 @@ GetPaletteIndex: .notNintendo xor a ret - -LoadPalettesFromIndex: ; a = index of combination - ld b, a - ; Multiply by 3 - add b - add b - + +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 @@ -955,7 +1026,8 @@ LoadPalettesFromIndex: ; a = index of combination ld c, a add hl, bc ld d, 8 - call LoadObjPalettes + ld c, $6A + call LoadPalettes pop hl bit 3, e jr nz, .loadBGPalette @@ -969,76 +1041,39 @@ LoadPalettesFromIndex: ; a = index of combination ld c, a add hl, bc 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 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 + ld [c], a + dec d + jr nz, .loop 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 + 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 GetInputPaletteIndex: @@ -1077,24 +1112,12 @@ GetInputPaletteIndex: ; 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 + call GetKeyComboPalette + call GetPaletteCombo + inc l + inc l ld a, [hl] ld hl, Palettes + 1 ld b, 0 @@ -1108,58 +1131,84 @@ ChangeAnimationPalette: .isWhite push af ld a, [hli] + 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 pop hl - ldh [BgPalettes + 2], a ; Second color, first palette + ldh [BgPalettes + 6], a ; Fourth color, first palette ld a, [hli] 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 pop hl - ldh [BgPalettes + 3], a ; Second color, first palette + 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] - 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] - 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] ldh [BgPalettes + 4], a ; Third color, first palette - ld a, [hl] + ld a, [hli] 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 + ld a, 45 ldh [WaitLoopCounter], a pop de pop bc - pop hl - pop af ret ReplaceColorInAllPalettes: ld de, 8 - ld c, 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 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 @@ -1169,27 +1218,20 @@ LoadDMGTilemap: ldd [hl], a dec c jr nz, .tilemapLoop - ld l,$0f ; Jump to top row + 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 + +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: diff --git a/BootROMs/pb12.c b/BootROMs/pb12.c new file mode 100644 index 0000000..878dd0d --- /dev/null +++ b/BootROMs/pb12.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/BootROMs/pb8.c b/BootROMs/pb8.c deleted file mode 100644 index 4ee4524..0000000 --- a/BootROMs/pb8.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -// For setting stdin/stdout to binary mode -#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) -#include -#define fd_isatty isatty -#elif defined (_WIN32) -#include -#include -#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 \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 : ""); - has_ferror = EOF; - } - fclose(infp); - if (ferror(outfp)) { - fprintf(stderr, "pb8: error writing %s\n", - outfilename ? outfilename : ""); - has_ferror = EOF; - } - fclose(outfp); - - if (compfailed && !has_ferror) { - fputs("pb8: unknown compression failure\n", stderr); - } - - return (compfailed || has_ferror) ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/Makefile b/Makefile index afc8d89..78978e4 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ else EXESUFFIX:= endif -PB8_COMPRESS := build/pb8$(EXESUFFIX) +PB12_COMPRESS := build/pb12$(EXESUFFIX) ifeq ($(PLATFORM),Darwin) DEFAULT := cocoa @@ -382,21 +382,21 @@ $(BIN)/SDL/Shaders: Shaders # Boot ROMs -$(OBJ)/%.1bpp: %.png +$(OBJ)/%.2bpp: %.png -@$(MKDIR) -p $(dir $@) - rgbgfx -d 1 -h -o $@ $< + rgbgfx -h -u -o $@ $< -$(OBJ)/BootROMs/SameBoyLogo.pb8: $(OBJ)/BootROMs/SameBoyLogo.1bpp $(PB8_COMPRESS) - $(realpath $(PB8_COMPRESS)) -l 384 $< $@ - -$(PB8_COMPRESS): BootROMs/pb8.c - $(CC) $< -o $@ +$(OBJ)/BootROMs/SameBoyLogo.pb12: $(OBJ)/BootROMs/SameBoyLogo.2bpp $(PB12_COMPRESS) + $(PB12_COMPRESS) < $< > $@ + +$(PB12_COMPRESS): BootROMs/pb12.c + $(CC) -Wall -Werror $< -o $@ $(BIN)/BootROMs/agb_boot.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/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb8 +$(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb12 -@$(MKDIR) -p $(dir $@) rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $< rgblink -o $@.tmp2 $@.tmp