summaryrefslogtreecommitdiff
path: root/home/decompress.asm
diff options
context:
space:
mode:
authoryenatch <yenatch@gmail.com>2013-09-09 22:44:48 -0400
committeryenatch <yenatch@gmail.com>2013-09-09 22:44:48 -0400
commit84a9b3907b9db08ee38e873554d8a6b4ac1b72b4 (patch)
treee804b9ed15832481d632904f42355743d9232515 /home/decompress.asm
parent6439706943b53ad756d481dea683fa8ee97f4981 (diff)
rename common/ to home/
Diffstat (limited to 'home/decompress.asm')
-rw-r--r--home/decompress.asm367
1 files changed, 367 insertions, 0 deletions
diff --git a/home/decompress.asm b/home/decompress.asm
new file mode 100644
index 000000000..8d39cd968
--- /dev/null
+++ b/home/decompress.asm
@@ -0,0 +1,367 @@
+FarDecompress: ; b40
+; Decompress graphics data at a:hl to de
+
+; put a away for a sec
+ ld [$c2c4], a
+; save bank
+ ld a, [hROMBank]
+ push af
+; bankswitch
+ ld a, [$c2c4]
+ rst Bankswitch
+
+; what we came here for
+ call Decompress
+
+; restore bank
+ pop af
+ rst Bankswitch
+ ret
+; b50
+
+
+Decompress: ; b50
+; Pokemon Crystal uses an lz variant for compression.
+
+; This is mainly used for graphics, but the intro's
+; tilemaps also use this compression.
+
+; This function decompresses lz-compressed data at hl to de.
+
+
+; Basic rundown:
+
+; A typical control command consists of:
+; -the command (bits 5-7)
+; -the count (bits 0-4)
+; -and any additional params
+
+; $ff is used as a terminator.
+
+
+; Commands:
+
+; 0: literal
+; literal data for some number of bytes
+; 1: iterate
+; one byte repeated for some number of bytes
+; 2: alternate
+; two bytes alternated for some number of bytes
+; 3: zero (whitespace)
+; 0x00 repeated for some number of bytes
+
+; Repeater control commands have a signed parameter used to determine the start point.
+; Wraparound is simulated:
+; Positive values are added to the start address of the decompressed data
+; and negative values are subtracted from the current position.
+
+; 4: repeat
+; repeat some number of bytes from decompressed data
+; 5: flipped
+; repeat some number of flipped bytes from decompressed data
+; ex: $ad = %10101101 -> %10110101 = $b5
+; 6: reverse
+; repeat some number of bytes in reverse from decompressed data
+
+; If the value in the count needs to be larger than 5 bits,
+; control code 7 can be used to expand the count to 10 bits.
+
+; A new control command is read in bits 2-4.
+; The new 10-bit count is split:
+; bits 0-1 contain the top 2 bits
+; another byte is added containing the latter 8
+
+; So, the structure of the control command becomes:
+; 111xxxyy yyyyyyyy
+; | | | |
+; | | our new count
+; | the control command for this count
+; 7 (this command)
+
+; For more information, refer to the code below and in extras/gfx.py .
+
+; save starting output address
+ ld a, e
+ ld [$c2c2], a
+ ld a, d
+ ld [$c2c3], a
+
+.loop
+; get next byte
+ ld a, [hl]
+; done?
+ cp $ff ; end
+ ret z
+
+; get control code
+ and %11100000
+
+; 10-bit param?
+ cp $e0 ; LZ_HI
+ jr nz, .normal
+
+
+; 10-bit param:
+
+; get next 3 bits (%00011100)
+ ld a, [hl]
+ add a
+ add a ; << 3
+ add a
+
+; this is our new control code
+ and %11100000
+ push af
+
+; get param hi
+ ld a, [hli]
+ and %00000011
+ ld b, a
+
+; get param lo
+ ld a, [hli]
+ ld c, a
+
+; read at least 1 byte
+ inc bc
+ jr .readers
+
+
+.normal
+; push control code
+ push af
+; get param
+ ld a, [hli]
+ and %00011111
+ ld c, a
+ ld b, $0
+; read at least 1 byte
+ inc c
+
+.readers
+; let's get started
+
+; inc loop counts since we bail as soon as they hit 0
+ inc b
+ inc c
+
+; get control code
+ pop af
+; command type
+ bit 7, a ; 80, a0, c0
+ jr nz, .repeatertype
+
+; literals
+ cp $20 ; LZ_ITER
+ jr z, .iter
+ cp $40 ; LZ_ALT
+ jr z, .alt
+ cp $60 ; LZ_ZERO
+ jr z, .zero
+ ; else $00
+
+; 00 ; LZ_LIT
+; literal data for bc bytes
+.loop1
+; done?
+ dec c
+ jr nz, .next1
+ dec b
+ jp z, .loop
+
+.next1
+ ld a, [hli]
+ ld [de], a
+ inc de
+ jr .loop1
+
+
+; 20 ; LZ_ITER
+; write byte for bc bytes
+.iter
+ ld a, [hli]
+
+.iterloop
+ dec c
+ jr nz, .iternext
+ dec b
+ jp z, .loop
+
+.iternext
+ ld [de], a
+ inc de
+ jr .iterloop
+
+
+; 40 ; LZ_ALT
+; alternate two bytes for bc bytes
+
+; next pair
+.alt
+; done?
+ dec c
+ jr nz, .alt0
+ dec b
+ jp z, .altclose0
+
+; alternate for bc
+.alt0
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .alt1
+; done?
+ dec b
+ jp z, .altclose1
+.alt1
+ ld a, [hld]
+ ld [de], a
+ inc de
+ jr .alt
+
+; skip past the bytes we were alternating
+.altclose0
+ inc hl
+.altclose1
+ inc hl
+ jr .loop
+
+
+; 60 ; LZ_ZERO
+; write 00 for bc bytes
+.zero
+ xor a
+
+.zeroloop
+ dec c
+ jr nz, .zeronext
+ dec b
+ jp z, .loop
+
+.zeronext
+ ld [de], a
+ inc de
+ jr .zeroloop
+
+
+; repeats
+; 80, a0, c0
+; repeat decompressed data from output
+.repeatertype
+ push hl
+ push af
+; get next byte
+ ld a, [hli]
+; absolute?
+ bit 7, a
+ jr z, .absolute
+
+; relative
+; a = -a
+ and %01111111 ; forget the bit we just looked at
+ cpl
+; add de (current output address)
+ add e
+ ld l, a
+ ld a, $ff ; -1
+ adc d
+ ld h, a
+ jr .repeaters
+
+.absolute
+; get next byte (lo)
+ ld l, [hl]
+; last byte (hi)
+ ld h, a
+; add starting output address
+ ld a, [$c2c2]
+ add l
+ ld l, a
+ ld a, [$c2c3]
+ adc h
+ ld h, a
+
+.repeaters
+ pop af
+ cp $80 ; LZ_REPEAT
+ jr z, .repeat
+ cp $a0 ; LZ_FLIP
+ jr z, .flip
+ cp $c0 ; LZ_REVERSE
+ jr z, .reverse
+
+; e0 -> 80
+
+; 80 ; LZ_REPEAT
+; repeat some decompressed data
+.repeat
+; done?
+ dec c
+ jr nz, .repeatnext
+ dec b
+ jr z, .cleanup
+
+.repeatnext
+ ld a, [hli]
+ ld [de], a
+ inc de
+ jr .repeat
+
+
+; a0 ; LZ_FLIP
+; repeat some decompressed data w/ flipped bit order
+.flip
+ dec c
+ jr nz, .flipnext
+ dec b
+ jp z, .cleanup
+
+.flipnext
+ ld a, [hli]
+ push bc
+ ld bc, $0008
+
+.fliploop
+ rra
+ rl b
+ dec c
+ jr nz, .fliploop
+ ld a, b
+ pop bc
+ ld [de], a
+ inc de
+ jr .flip
+
+
+; c0 ; LZ_REVERSE
+; repeat some decompressed data in reverse
+.reverse
+ dec c
+ jr nz, .reversenext
+
+ dec b
+ jp z, .cleanup
+
+.reversenext
+ ld a, [hld]
+ ld [de], a
+ inc de
+ jr .reverse
+
+
+.cleanup
+; get type of repeat we just used
+ pop hl
+; was it relative or absolute?
+ bit 7, [hl]
+ jr nz, .next
+
+; skip two bytes for absolute
+ inc hl
+; skip one byte for relative
+.next
+ inc hl
+ jp .loop
+; c2f
+