Skip to content

Commit

Permalink
Add stripe-based RLE encoder
Browse files Browse the repository at this point in the history
legal_screen_nametable compresses to 294 bytes, vs 221 with Konami RLE.
If twoplayer wasn't also including the two-player playfield, it would be
a good choice.
  • Loading branch information
ejona86 committed Jan 23, 2024
1 parent 2725a12 commit d956dd3
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
5 changes: 5 additions & 0 deletions nes.mk
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ build/%.rle: % rle-enc.awk | build
# basenc isn't as widely available as xxd since it was added in
# coreutils 8.31
xxd -c1 -p $< | LC_ALL=C awk -f rle-enc.awk | xxd -r -p > $@
build/%.rle.stripe: % stripe-enc.awk | build
# 'basenc --base16 -w2' and 'basenc --base16 -d' would also work, but
# basenc isn't as widely available as xxd since it was added in
# coreutils 8.31
xxd -c1 -p $< | LC_ALL=C awk -f stripe-enc.awk | xxd -r -p > $@

build/%.s: %.bin %.info Makefile | build
# Strip off the first two lines of header, which contain variable
Expand Down
71 changes: 71 additions & 0 deletions stripe-enc.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Encode input using Stripe RLE compression (Tetris variation)
#
# Intended to be run with LC_ALL=C and 'basenc --base16 -w2' as input and
# 'basenc --base16 -d' on output

# variable addr can be set to change the destination PPU address
# input is pairs of hex digits, each followed by newline
# output is chunks of hex digits, with newlines between each chunk

BEGIN {
runlen=0
if (addr == "") {
addr=0x2000
}
}

runlen != 0 && lastbyte == $0 {
runlen++
if (runlen == 0x40) {
printf("%04X%02X%s\n", addr, 0x40, lastbyte)
addr+=runlen
runlen=0
}
next
}

runlen != 0 && lastbyte != $0 {
printf("%04X%02X%s\n", addr, 0x40+runlen, lastbyte)
addr+=runlen
runlen=0
}

{
literal=$0
runlen=1
lastbyte=$0
while (getline) {
literal=literal $0
if (lastbyte != $0) {
runlen=1
} else {
runlen++
# Optimal is 4 if next stripe is a run, and 7 if
# literal. Assume a literal typically follows a run.
if ((runlen == 4 && runlen*2 == length(literal)) \
|| runlen == 7) {
literal=substr(literal, 1, length(literal)-runlen*2)
if (literal == "") {
next
} else {
break
}
}
}
if (length(literal)/2 == 0x40) {
runlen=0
break
}
lastbyte=$0
}
printf("%04X%02X%s\n", addr, (length(literal)/2)%0x40, literal)
addr+=length(literal)/2
}

END {
if (runlen != 0) {
printf("%04X%02X%s\n", addr, 0x40+runlen, lastbyte)
addr+=runlen
}
print "FF"
}
2 changes: 1 addition & 1 deletion tetris-PRG.info
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ LABEL { ADDR $AA8A; NAME "copyCurrentScrollAndCtrlToPPU"; };
LABEL { ADDR $AA98; NAME "bulkCopyToPpu"; COMMENT "Return address should contain data address; ret addr+2 is actual return. $80-$FF end. $4C recurse, 2 byte cpu addr. $60 unrecurse. Otherwise NES data stripe, 2 byte ppu addr, 1 byte length, data. length controls RLE and write direction"; };
LABEL { ADDR $AA9E; NAME "@processStripe"; };
LABEL { ADDR $AAB5; NAME "@setDirection"; COMMENT "Bit 7 of length: 0-right, 1-down"; };
LABEL { ADDR $AAC2; NAME "@checkForZeroLength"; COMMENT "Bit 6 of length: 0-literal, 1-run. If bits 0-5 are zero, length is 128"; };
LABEL { ADDR $AAC2; NAME "@checkForZeroLength"; COMMENT "Bit 6 of length: 0-literal, 1-run. If bits 0-5 are zero, length is 64"; };
LABEL { ADDR $AAC7; NAME "@clearLengthFlagBits"; };
LABEL { ADDR $AACA; NAME "@copyByteLoop"; };
LABEL { ADDR $AACD; NAME "@copyByte"; };
Expand Down

0 comments on commit d956dd3

Please sign in to comment.