summaryrefslogtreecommitdiff
path: root/src/home/decompress.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/home/decompress.asm')
-rw-r--r--src/home/decompress.asm159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/home/decompress.asm b/src/home/decompress.asm
new file mode 100644
index 0000000..dda25f4
--- /dev/null
+++ b/src/home/decompress.asm
@@ -0,0 +1,159 @@
+; initializes variables used to decompress data in DecompressData
+; de = source of compressed data
+; b = HIGH byte of secondary buffer ($100 bytes of buffer space)
+; also clears this $100 byte space
+InitDataDecompression:
+ ld hl, wDecompSourcePosPtr
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ ld hl, wDecompNumCommandBitsLeft
+ ld [hl], 1
+ inc hl
+ xor a
+ ld [hli], a ; wDecompCommandByte
+ ld [hli], a ; wDecompRepeatModeToggle
+ ld [hli], a ; wDecompRepeatLengths
+ ld [hli], a ; wDecompNumBytesToRepeat
+ ld [hl], b ; wDecompSecondaryBufferPtrHigh
+ inc hl
+ ld [hli], a ; wDecompRepeatSeqOffset
+ ld [hl], LOW(wDecompressionSecondaryBufferStart) ; wDecompSecondaryBufferPtrLow
+
+; clear buffer
+ ld h, b
+ ld l, LOW(wDecompressionSecondaryBuffer)
+ xor a
+.loop
+ ld [hl], a
+ inc l
+ jr nz, .loop
+ ret
+
+; decompresses data
+; uses values initialized by InitDataDecompression
+; wDecompSourcePosPtr holds the pointer for compressed source
+; input:
+; bc = row width
+; de = buffer to place decompressed data
+DecompressData:
+ push hl
+ push de
+.loop
+ push bc
+ call .Decompress
+ ld [de], a
+ inc de
+ pop bc
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ pop de
+ pop hl
+ ret
+
+; decompression works as follows:
+; first a command byte is read that will dictate how the
+; following bytes will be copied
+; the position will then move to the next byte (0xXY), and
+; the command byte's bits are read from higher to lower bit
+; - if command bit is set, then copy 0xXY to buffer;
+; - if command bit is not set, then decompression enters "repeat mode,"
+; which means it stores 0xXY in memory as number of bytes to repeat
+; from a given offset. This offset is in the next byte in the data,
+; 0xZZ, which tells the offset to start repeating. A toggle is switched
+; each time the algorithm hits "repeat mode":
+; - if off -> on it reads 0xXY and stores it,
+; then repeats (0x0X + 2) bytes from the offset starting at 0xZZ;
+; - if on -> off, then the data only provides the offset,
+; and the previous byte read for number of bytes to repeat, 0xXY, is reused
+; in which case (0x0Y + 2) bytes are repeated starting from the offset.
+.Decompress:
+ ld hl, wDecompNumBytesToRepeat
+ ld a, [hl]
+ or a
+ jr z, .read_command
+
+; still repeating sequence
+ dec [hl]
+ inc hl
+.repeat_byte
+ ld b, [hl] ; wDecompSecondaryBufferPtrHigh
+ inc hl
+ ld c, [hl] ; wDecompRepeatSeqOffset
+ inc [hl]
+ inc hl
+ ld a, [bc]
+ ld c, [hl] ; wDecompSecondaryBufferPtrLow
+ inc [hl]
+ ld [bc], a
+ ret
+
+.read_command
+ ld hl, wDecompSourcePosPtr
+ ld c, [hl]
+ inc hl
+ ld b, [hl]
+ inc hl ; wDecompNumCommandBitsLeft
+ dec [hl]
+ inc hl ; wDecompCommandByte
+ jr nz, .read_command_bit
+ dec hl ; wDecompNumCommandBitsLeft
+ ld [hl], 8 ; number of bits
+ inc hl ; wDecompCommandByte
+ ld a, [bc]
+ inc bc
+ ld [hl], a
+.read_command_bit
+ rl [hl]
+ ld a, [bc]
+ inc bc
+ jr nc, .repeat_command
+
+; copy 1 byte literally
+ ld hl, wDecompSourcePosPtr
+ ld [hl], c
+ inc hl
+ ld [hl], b
+ ld hl, wDecompSecondaryBufferPtrHigh
+ ld b, [hl]
+ inc hl
+ inc hl
+ ld c, [hl] ; wDecompSecondaryBufferPtrLow
+ inc [hl]
+ ld [bc], a
+ ret
+
+.repeat_command
+ ld [wDecompRepeatSeqOffset], a ; save the offset to repeat from
+ ld hl, wDecompRepeatModeToggle
+ bit 0, [hl]
+ jr nz, .repeat_mode_toggle_on
+ set 0, [hl]
+ inc hl
+; read byte for num of bytes to read
+; and use its higher nybble
+ ld a, [bc]
+ inc bc
+ ld [hli], a ; wDecompRepeatLengths
+ swap a
+.get_sequence_len
+ and $f
+ inc a ; number of times to repeat
+ ld [hli], a ; wDecompNumBytesToRepeat
+ push hl
+ ld hl, wDecompSourcePosPtr
+ ld [hl], c
+ inc hl
+ ld [hl], b
+ pop hl
+ jr .repeat_byte
+
+.repeat_mode_toggle_on
+; get the previous byte (num of bytes to repeat)
+; and use its lower nybble
+ res 0, [hl]
+ inc hl
+ ld a, [hli] ; wDecompRepeatLengths
+ jr .get_sequence_len