summaryrefslogtreecommitdiff
path: root/home/decompress.asm
diff options
context:
space:
mode:
Diffstat (limited to 'home/decompress.asm')
-rw-r--r--home/decompress.asm343
1 files changed, 343 insertions, 0 deletions
diff --git a/home/decompress.asm b/home/decompress.asm
new file mode 100644
index 00000000..13613f64
--- /dev/null
+++ b/home/decompress.asm
@@ -0,0 +1,343 @@
+FarDecompress:: ; b40
+; Decompress graphics data from a:hl to de.
+
+ ld [wLZBank], a
+ ld a, [hROMBank]
+ push af
+ ld a, [wLZBank]
+ rst Bankswitch
+
+ call Decompress
+
+ pop af
+ rst Bankswitch
+ ret
+; b50
+
+
+Decompress:: ; b50
+; Pokemon Crystal uses an lz variant for compression.
+; This is mainly (but not necessarily) used for graphics.
+
+; This function decompresses lz-compressed data from hl to de.
+
+
+LZ_END EQU $ff ; Compressed data is terminated with $ff.
+
+
+; A typical control command consists of:
+
+LZ_CMD EQU %11100000 ; command id (bits 5-7)
+LZ_LEN EQU %00011111 ; length n (bits 0-4)
+
+; Additional parameters are read during command execution.
+
+
+; Commands:
+
+LZ_LITERAL EQU 0 << 5 ; Read literal data for n bytes.
+LZ_ITERATE EQU 1 << 5 ; Write the same byte for n bytes.
+LZ_ALTERNATE EQU 2 << 5 ; Alternate two bytes for n bytes.
+LZ_ZERO EQU 3 << 5 ; Write 0 for n bytes.
+
+
+; Another class of commands reuses data from the decompressed output.
+LZ_RW EQU 2 + 5 ; bit
+
+; These commands take a signed offset to start copying from.
+; Wraparound is simulated.
+; Positive offsets (15-bit) are added to the start address.
+; Negative offsets (7-bit) are subtracted from the current position.
+
+LZ_REPEAT EQU 4 << 5 ; Repeat n bytes from the offset.
+LZ_FLIP EQU 5 << 5 ; Repeat n bitflipped bytes.
+LZ_REVERSE EQU 6 << 5 ; Repeat n bytes in reverse.
+
+
+; If the value in the count needs to be larger than 5 bits,
+; LZ_LONG can be used to expand the count to 10 bits.
+LZ_LONG EQU 7 << 5
+
+; A new control command is read in bits 2-4.
+; The top two bits of the length are bits 0-1.
+; Another byte is read containing the bottom 8 bits.
+LZ_LONG_HI EQU %00000011
+
+; In other words, the structure of the command becomes
+; 111xxxyy yyyyyyyy
+; x: the new control command
+; y: the length
+
+
+; For more information, refer to the code below and in extras/gfx.py.
+
+
+ ; Save the output address
+ ; for rewrite commands.
+ ld a, e
+ ld [wLZAddress], a
+ ld a, d
+ ld [wLZAddress + 1], a
+
+.Main
+ ld a, [hl]
+ cp LZ_END
+ ret z
+
+ and LZ_CMD
+
+ cp LZ_LONG
+ jr nz, .short
+
+.long
+; The count is now 10 bits.
+
+ ; Read the next 3 bits.
+ ; %00011100 -> %11100000
+ ld a, [hl]
+ add a
+ add a ; << 3
+ add a
+
+ ; This is our new control code.
+ and LZ_CMD
+ push af
+
+ ld a, [hli]
+ and LZ_LONG_HI
+ ld b, a
+ ld a, [hli]
+ ld c, a
+
+ ; read at least 1 byte
+ inc bc
+ jr .command
+
+
+.short
+ push af
+
+ ld a, [hli]
+ and LZ_LEN
+ ld c, a
+ ld b, 0
+
+ ; read at least 1 byte
+ inc c
+
+
+.command
+ ; Increment loop counts.
+ ; We bail the moment they hit 0.
+ inc b
+ inc c
+
+ pop af
+
+ bit LZ_RW, a
+ jr nz, .rewrite
+
+ cp LZ_ITERATE
+ jr z, .Iter
+ cp LZ_ALTERNATE
+ jr z, .Alt
+ cp LZ_ZERO
+ jr z, .Zero
+
+
+.Literal
+; Read literal data for bc bytes.
+.lloop
+ dec c
+ jr nz, .lnext
+ dec b
+ jp z, .Main
+
+.lnext
+ ld a, [hli]
+ ld [de], a
+ inc de
+ jr .lloop
+
+
+.Iter
+; Write the same byte for bc bytes.
+ ld a, [hli]
+
+.iloop
+ dec c
+ jr nz, .inext
+ dec b
+ jp z, .Main
+
+.inext
+ ld [de], a
+ inc de
+ jr .iloop
+
+
+.Alt
+; Alternate two bytes for bc bytes.
+ dec c
+ jr nz, .anext1
+ dec b
+ jp z, .adone1
+.anext1
+ ld a, [hli]
+ ld [de], a
+ inc de
+
+ dec c
+ jr nz, .anext2
+ dec b
+ jp z, .adone2
+.anext2
+ ld a, [hld]
+ ld [de], a
+ inc de
+
+ jr .Alt
+
+ ; Skip past the bytes we were alternating.
+.adone1
+ inc hl
+.adone2
+ inc hl
+ jr .Main
+
+
+.Zero
+; Write 0 for bc bytes.
+ xor a
+
+.zloop
+ dec c
+ jr nz, .znext
+ dec b
+ jp z, .Main
+
+.znext
+ ld [de], a
+ inc de
+ jr .zloop
+
+
+.rewrite
+; Repeat decompressed data from output.
+ push hl
+ push af
+
+ ld a, [hli]
+ bit 7, a ; sign
+ jr z, .positive
+
+.negative
+; hl = de - a
+ ; Since we can't subtract a from de,
+ ; Make it negative and add de.
+ and %01111111
+ cpl
+ add e
+ ld l, a
+ ld a, -1
+ adc d
+ ld h, a
+ jr .ok
+
+.positive
+; Positive offsets are two bytes.
+ ld l, [hl]
+ ld h, a
+ ; add to starting output address
+ ld a, [wLZAddress]
+ add l
+ ld l, a
+ ld a, [wLZAddress + 1]
+ adc h
+ ld h, a
+
+.ok
+ pop af
+
+ cp LZ_REPEAT
+ jr z, .Repeat
+ cp LZ_FLIP
+ jr z, .Flip
+ cp LZ_REVERSE
+ jr z, .Reverse
+
+; Since LZ_LONG is command 7,
+; only commands 0-6 are passed in.
+; This leaves room for an extra command 7.
+; However, lengths longer than 768
+; would be interpreted as LZ_END.
+
+; More practically, LZ_LONG is not recursive.
+; For now, it defaults to LZ_REPEAT.
+
+
+.Repeat
+; Copy decompressed data for bc bytes.
+ dec c
+ jr nz, .rnext
+ dec b
+ jr z, .donerw
+
+.rnext
+ ld a, [hli]
+ ld [de], a
+ inc de
+ jr .Repeat
+
+
+.Flip
+; Copy bitflipped decompressed data for bc bytes.
+ dec c
+ jr nz, .fnext
+ dec b
+ jp z, .donerw
+
+.fnext
+ ld a, [hli]
+ push bc
+ lb bc, 0, 8
+
+.floop
+ rra
+ rl b
+ dec c
+ jr nz, .floop
+
+ ld a, b
+ pop bc
+
+ ld [de], a
+ inc de
+ jr .Flip
+
+
+.Reverse
+; Copy reversed decompressed data for bc bytes.
+ dec c
+ jr nz, .rvnext
+
+ dec b
+ jp z, .donerw
+
+.rvnext
+ ld a, [hld]
+ ld [de], a
+ inc de
+ jr .Reverse
+
+
+.donerw
+ pop hl
+
+ bit 7, [hl]
+ jr nz, .next
+ inc hl ; positive offset is two bytes
+.next
+ inc hl
+ jp .Main
+; c2f