Last active
February 20, 2021 23:39
-
-
Save ISSOtm/d0005f1b653e19c1c39f80756521fabb to your computer and use it in GitHub Desktop.
CRC self-test code for Game Boy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
INCLUDE "defines.asm" | |
IF !DEF(BANK_BYTE) | |
BANK_BYTE equ 7 ; Plan for the worst case | |
ENDC | |
IF BANK_BYTE > 8 | |
FAIL "Unknown bank byte {BANK_BYTE}" | |
ENDC | |
NB_ROM_BANKS equ 2 << BANK_BYTE | |
SCREEN_AREA_WIDTH equ 16 | |
SCREEN_AREA_HEIGHT equ 16 | |
IF NB_ROM_BANKS > SCREEN_AREA_WIDTH * SCREEN_AREA_HEIGHT | |
FAIL "Impossible to display all {d:NB_ROM_BANKS} ROM bank CRCs!" | |
ENDC | |
CRC_POLYNOM equ $1021 | |
SECTION UNION "9C00 tilemap", VRAM[_SCRN1],BANK[0] | |
wCRCLowTable: | |
ds 256 | |
wCRCHighTable: | |
ds 256 | |
wCRCSuccess: | |
ds NB_ROM_BANKS | |
SECTION "CRC check", ROM0[$150] | |
CRCs: | |
ds 2 * NB_ROM_BANKS ; The CRC16 check values will go here (in continuity with the two checksum bytes), **LITTLE-ENDIAN** | |
; Performs CRC verification | |
CheckCRC:: | |
; We know we're at VBlank, so turn the LCD off, as we're using VRAM for work RAM | |
xor a | |
ldh [rLCDC], a | |
; xor a | |
ldh [rNR52], a ; Also kill the APU | |
ld a, AUDENA_ON | |
ldh [rNR52], a | |
ld a, AUDTERM_1_LEFT | AUDTERM_1_RIGHT ; Enable CH4 on all sides | |
ldh [rNR51], a | |
ld a, $77 ; Max volume on both sides, disabling VIN | |
ldh [rNR50], a | |
xor a | |
ldh [rNR11], a | |
; xor a | |
ldh [rNR13], a | |
ld a, $F0 | |
ldh [rNR12], a | |
; Compute CRC table to speed up later calculations | |
ld de, wCRCHighTable | |
ld a, $8F | |
ldh [rNR14], a | |
.computeCRCTable | |
ld h, e | |
ld l, 0 | |
ld c, 8 | |
.bitLoop | |
add hl, hl | |
jr nc, .bitClear | |
ld a, l | |
xor LOW(CRC_POLYNOM) | |
ld l, a | |
ld a, h | |
xor HIGH(CRC_POLYNOM) | |
ld h, a | |
.bitClear | |
dec c | |
jr nz, .bitLoop | |
dec d | |
ld a, l | |
ld [de], a | |
inc d | |
ld a, h | |
ld [de], a | |
inc e | |
jr nz, .computeCRCTable | |
ld a, $80 | |
ldh [rNR11], a | |
ld hl, $0000 | |
ld c, l ; ld c, 0 ; Low byte of pointer to result | |
ld a, 1 ; Load dummy bank for bank 0 | |
.CRCLoop | |
ld [rROMB0], a | |
and $3F | |
or $80 | |
ldh [rNR14], a | |
ld b, $FF ; CRC low | |
ld e, b ; lb de, HIGH(wCRCHighTable), $FF ; CRC high & pointer to table | |
; Skip 2-byte "skip hack" below | |
; Carry is known to be clear from `or $80` above | |
db $DC ; call c, imm16 | |
.skipBytes | |
ld l, LOW(CRCs + 2) | |
.feedByte | |
; Update CRC in `eb` | |
ld a, [hli] | |
xor e ; XOR with high byte | |
ld e, a ; Store back | |
dec d ; Switch to low byte table | |
ld a, [de] ; Get low byte mask | |
xor b ; XOR with low byte | |
ld b, a ; Store back | |
inc d ; Switch to high byte table | |
ld a, [de] ; Get high byte mask | |
xor e ; XOR with high byte | |
ld e, a ; Store back | |
; Check if we should skip | |
ld a, h | |
dec a | |
ld a, l | |
jr nz, .dontSkipBytes | |
cp LOW($014E) | |
jr z, .skipBytes | |
.dontSkipBytes | |
; Check if reached end of a bank | |
and a | |
jr nz, .feedByte | |
ld a, h | |
and $3F | |
jr nz, .feedByte | |
; Now check if the CRC matches | |
; Get pointer to current | |
ld a, c | |
add a, LOW(CRCs) >> 1 | |
add a, a | |
ld l, a | |
adc a, HIGH(CRCs) | |
sub l | |
ld h, a | |
ld a, [hli] | |
cp b | |
jr nz, .CRCFail | |
ld a, [hli] | |
sub e | |
jr z, .CRCSuccess ; a = 0 | |
.CRCFail | |
ld a, 1 | |
.CRCSuccess | |
ld b, HIGH(wCRCSuccess) | |
ld [bc], a | |
; Prepare next bank | |
ld hl, $4000 | |
inc c | |
IF NB_ROM_BANKS != 256 | |
ld a, c | |
cp NB_ROM_BANKS | |
ENDC | |
jr nz, .CRCLoop | |
; Now, time to display the results! | |
; Tile 0 = success | |
; Tile 1 = fail | |
; Tile 2 = padding | |
ld sp, $9030 | |
ld de, $0000 | |
push de | |
ld de, $FE00 | |
ld c, 7 | |
xor a | |
.writeTileRow | |
push de | |
dec c | |
jr nz, .writeTileRow | |
ld c, 8 | |
cpl ; Preserves Z | |
ld e, a | |
ld d, a | |
and a | |
jr nz, .writeTileRow | |
; Draw results to the tilemap | |
ld hl, _SCRN0 | |
ld b, SCRN_Y_B | |
ld de, SCRN_VX_B - SCRN_X_B | |
ld a, 2 | |
.writeFillerRow | |
ld c, SCRN_X_B | |
.writeFiller | |
ld [hli], a | |
dec c | |
jr nz, .writeFiller | |
add hl, de | |
dec b | |
jr nz, .writeFillerRow | |
ld hl, _SCRN0 + SCRN_VX_B + 1 | |
ld de, wCRCSuccess | |
ld c, LOW(NB_ROM_BANKS) | |
.copyResults | |
ld a, [de] | |
ld [hli], a | |
inc e ; inc de | |
ld a, l | |
and -SCRN_VX_B | |
cp SCRN_X_B - 1 | |
jr nz, .dontSkipRow | |
ld a, l | |
add a, SCRN_VX_B - SCRN_X_B + 2 | |
ld l, a | |
adc a, h | |
sub l | |
ld h, a | |
.dontSkipRow | |
dec c | |
jr nz, .copyResults | |
ld a, BCPSF_AUTOINC | |
ldh [rBCPS], a | |
ld c, LOW(rBCPD) | |
ld de, -$294a ; RGB555 (10;10;10) | |
ld hl, $294a * 3 | |
.writeColor | |
ld a, l | |
ldh [c], a | |
ld a, h | |
ldh [c], a | |
add hl, de | |
and a | |
jr nz, .writeColor | |
ld a, LCDCF_ON | LCDCF_BGON | |
ldh [rLCDC], a | |
ld a, %11100100 | |
ldh [rBGP], a | |
xor a | |
ldh [rSCX], a | |
ldh [rSCY], a | |
; xor a | |
ldh [rIE], a | |
ei | |
; xor a | |
ldh [rNR52], a ; Also kill the APU | |
halt ; Lock up |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import sys | |
# Generate CRC table | |
crc_table = [] | |
for i in range(0x100): | |
value = i << 8 | |
for _ in range(8): | |
value <<= 1 | |
if value & 0x10000: | |
value ^= 0x1021 | |
value &= 0xFFFF | |
crc_table.append(value) | |
crc = 0 | |
def crc_init(): | |
global crc | |
crc = 0xFFFF | |
def crc_feed(byte): | |
global crc | |
crc ^= byte << 8 | |
crc ^= crc_table[crc >> 8] | |
def crc_range(start, len): | |
in_file.seek(start) | |
for b in in_file.read(len): | |
crc_feed(b) | |
def write_crc(bank): | |
in_file.seek(0x150 + bank * 2) | |
in_file.write(crc.to_bytes(2, "little", signed=False)) | |
with open(sys.argv[1], "rb+") as in_file: | |
in_file.seek(0x148) | |
nb_banks = 2 << in_file.read(1)[0] | |
for bank in range(1, nb_banks): | |
crc_init() | |
crc_range(bank * 0x4000, 0x4000) | |
write_crc(bank) | |
# Now for bank 0 | |
crc_init() | |
crc_range(0, 0x14E) | |
crc_range(0x152, 0x4000 - 0x152) | |
write_crc(0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(BINDIR)/game.gb: $(SRCDIR)/tools/crcify.py $(OBJS) | |
@$(MKDIR) -p $(@D) | |
$(RGBLINK) $(LDFLAGS) -o $@ $(OBJS) | |
$(RGBFIX) $(FIXFLAGS) $@ | |
$(RGBASM) $(ASFLAGS) -D'BANK_BYTE=$$'$$(xxd -s 0x148 -l 1 -p $@) -o $(OBJDIR)/crc.o $(SRCDIR)/crc.asm | |
$(RGBLINK) $(LDFLAGS) -o $@ $(OBJS) \ | |
&& $(RGBFIX) $(FIXFLAGS) $@ \ | |
&& $(PY) $< $@ \ | |
&& $(RGBFIX) $(FIXFLAGS) $@ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment