homebrew/lzss.baku.asm
2018-08-19 00:43:04 +02:00

139 lines
2.7 KiB
NASM

// a decompressor for the variant of LZSS used by Bomberman 64.
// a matching compressor is available here:
// https://github.com/notwa/baku/blob/master/compressor.c
LzDecomp:
// a0: pointer to compressed data (must be RDRAM, cart is unsupported)
// a1: compressed size
// a2: output pointer
// a3: maximum uncompressed size
// v0: error code (0 is OK)
// t0: current pointer for reading
// t1: end pointer (exclusive)
// t2: current pointer for writing
// t3: end pointer (exclusive)
// t4: current code, shifted as necessary
// t5: 1 means raw, 0 means copy
// t6: either raw byte or part of copy command
// t7: current pseudo-position
// t8: match length
// t9: match position
beqz a1, LzExit // nothing to decompress? nothing to do!
nop
blez a3, LzNoSpace
nop
or t0, a0, r0
addu t1, a0, a1
or t2, a2, r0
addu t3, a2, a3
lli t7, 0x3BE
LzNextCode:
// READ
lbu t4, 0(t0)
addiu t0, 1
ori t4, t4, 0x100 // add end marker
andi t5, t4, 1
LzReadCode:
beq t2, t3, LzReiterate
nop
// READ
beq t0, t1, LzBadEnd
nop
lbu t6, 0(t0)
addiu t0, 1
beqz t5, LzCopy
nop
LzRaw:
// WRITE
beq t2, t3, LzNoSpace
addiu t7, 1
andi t7, t7, 0x3FF
sb t6, 0(t2)
addiu t2, 1
// SHIFT
srl t4, 1
xori at, t4, 1
bnez at, LzReadCode // branch if t4 != 1
andi t5, t4, 1
b LzReiterate
nop
LzCopy:
// READ
beq t0, t1, LzBadEnd
nop
lbu t8, 0(t0)
addiu t0, 1
// extract match position
sll t9, t8, 2
andi t9, 0x0300
or t9, t6
// extract and prepare match length
andi t8, 0x003F
addiu t8, 3
// prepare absolute match position
subu t9, t7, t9
subiu t9, 1
andi t9, 0x03FF
addiu t9, 1
subu t9, t2, t9
// preemptively move pseudo-position ahead (don't need it for the loop)
addu t7, t8
andi t7, t7, 0x3FF
LzCopyLoop:
// repurposing t6: byte being copied
slt at, t9, a2
bnez at,+ // don't copy bytes before the output pointer
lli t6, 0 // use nulls instead
lbu t6, 0(t9)
+
// WRITE
beq t2, t3, LzNoSpace
subiu t8, 1
sb t6, 0(t2)
addiu t2, 1
bnez t8, LzCopyLoop
addiu t9, 1
// SHIFT
srl t4, 1
xori at, t4, 1
bnez at, LzReadCode // branch if t4 != 1
andi t5, t4, 1
LzReiterate:
bne t0, t1, LzNextCode
nop
LzExit:
jr ra
lli v0, 0
LzNoSpace:
jr ra
lli v0, 1
LzBadEnd: // aka Unexpected End Of File
jr ra
lli v0, 2