Recomment lz deecompression.

This commit is contained in:
yenatch 2014-06-03 14:08:23 -07:00
parent 20444d2f63
commit 21708a2271
1 changed files with 220 additions and 246 deletions

View File

@ -1,19 +1,14 @@
FarDecompress:: ; b40 FarDecompress:: ; b40
; Decompress graphics data at a:hl to de ; Decompress graphics data from a:hl to de.
; put a away for a sec
ld [$c2c4], a ld [$c2c4], a
; save bank
ld a, [hROMBank] ld a, [hROMBank]
push af push af
; bankswitch
ld a, [$c2c4] ld a, [$c2c4]
rst Bankswitch rst Bankswitch
; what we came here for
call Decompress call Decompress
; restore bank
pop af pop af
rst Bankswitch rst Bankswitch
ret ret
@ -22,170 +17,158 @@ FarDecompress:: ; b40
Decompress:: ; b50 Decompress:: ; b50
; Pokemon Crystal uses an lz variant for compression. ; Pokemon Crystal uses an lz variant for compression.
; This is mainly (but not necessarily) used for graphics.
; This is mainly used for graphics, but the intro's ; This function decompresses lz-compressed data from hl to de.
; tilemaps also use this compression.
; This function decompresses lz-compressed data at hl to de.
; Basic rundown: LZ_END EQU $ff ; Compressed data is terminated with $ff.
; 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: ; A typical control command consists of:
; 0: literal LZ_CMD EQU %11100000 ; command id (bits 5-7)
; literal data for some number of bytes LZ_LEN EQU %00011111 ; length n (bits 0-4)
; 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. ; Additional parameters are read during command execution.
; 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, ; Commands:
; control code 7 can be used to expand the count to 10 bits.
; A new control command is read in bits 2-4. LZ_LITERAL EQU 0 << 5 ; Read literal data for n bytes.
; The new 10-bit count is split: LZ_ITERATE EQU 1 << 5 ; Write the same byte for n bytes.
; bits 0-1 contain the top 2 bits LZ_ALTERNATE EQU 2 << 5 ; Alternate two bytes for n bytes.
; another byte is added containing the latter 8 LZ_ZERO EQU 3 << 5 ; Write 0 for n bytes.
; 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 . ; Another class of commands reuses data from the decompressed output.
LZ_RW EQU 2 + 5 ; bit
; save starting output address ; 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 a, e
ld [$c2c2], a ld [$c2c2], a
ld a, d ld a, d
ld [$c2c3], a ld [$c2c2 + 1], a
.loop .Main
; get next byte
ld a, [hl] ld a, [hl]
; done? cp LZ_END
cp $ff ; end
ret z ret z
; get control code and LZ_CMD
and %11100000
; 10-bit param? cp LZ_LONG
cp $e0 ; LZ_HI jr nz, .short
jr nz, .normal
.long
; The count is now 10 bits.
; 10-bit param: ; Read the next 3 bits.
; %00011100 -> %11100000
; get next 3 bits (%00011100)
ld a, [hl] ld a, [hl]
add a add a
add a ; << 3 add a ; << 3
add a add a
; this is our new control code ; This is our new control code.
and %11100000 and LZ_CMD
push af push af
; get param hi
ld a, [hli] ld a, [hli]
and %00000011 and LZ_LONG_HI
ld b, a ld b, a
; get param lo
ld a, [hli] ld a, [hli]
ld c, a ld c, a
; read at least 1 byte ; read at least 1 byte
inc bc inc bc
jr .readers jr .command
.normal .short
; push control code
push af push af
; get param
ld a, [hli] ld a, [hli]
and %00011111 and LZ_LEN
ld c, a ld c, a
ld b, $0 ld b, 0
; read at least 1 byte
; read at least 1 byte
inc c inc c
.readers
; let's get started
; inc loop counts since we bail as soon as they hit 0 .command
; Increment loop counts.
; We bail the moment they hit 0.
inc b inc b
inc c inc c
; get control code
pop af pop af
; command type
bit 7, a ; 80, a0, c0
jr nz, .repeatertype
; literals bit LZ_RW, a
cp $20 ; LZ_ITER jr nz, .rewrite
jr z, .iter
cp $40 ; LZ_ALT
jr z, .alt
cp $60 ; LZ_ZERO
jr z, .zero
; else $00
; 00 ; LZ_LIT cp LZ_ITERATE
; literal data for bc bytes jr z, .Iter
.loop1 cp LZ_ALTERNATE
; done? jr z, .Alt
cp LZ_ZERO
jr z, .Zero
.Literal
; Read literal data for bc bytes.
.lloop
dec c dec c
jr nz, .next1 jr nz, .lnext
dec b dec b
jp z, .loop jp z, .Main
.next1 .lnext
ld a, [hli] ld a, [hli]
ld [de], a ld [de], a
inc de inc de
jr .loop1 jr .lloop
; 20 ; LZ_ITER .Iter
; write byte for bc bytes ; Write the same byte for bc bytes.
.iter
ld a, [hli] ld a, [hli]
.iterloop .iterloop
dec c dec c
jr nz, .iternext jr nz, .iternext
dec b dec b
jp z, .loop jp z, .Main
.iternext .iternext
ld [de], a ld [de], a
@ -193,88 +176,79 @@ Decompress:: ; b50
jr .iterloop jr .iterloop
; 40 ; LZ_ALT .Alt
; alternate two bytes for bc bytes ; Alternate two bytes for bc bytes.
; next pair
.alt
; done?
dec c dec c
jr nz, .alt0 jr nz, .anext1
dec b dec b
jp z, .altclose0 jp z, .adone1
.anext1
; alternate for bc
.alt0
ld a, [hli] ld a, [hli]
ld [de], a ld [de], a
inc de inc de
dec c dec c
jr nz, .alt1 jr nz, .anext2
; done?
dec b dec b
jp z, .altclose1 jp z, .adone2
.alt1 .anext2
ld a, [hld] ld a, [hld]
ld [de], a ld [de], a
inc de inc de
jr .alt
; skip past the bytes we were alternating jr .Alt
.altclose0
; Skip past the bytes we were alternating.
.adone1
inc hl inc hl
.altclose1 .adone2
inc hl inc hl
jr .loop jr .Main
; 60 ; LZ_ZERO .Zero
; write 00 for bc bytes ; Write 0 for bc bytes.
.zero
xor a xor a
.zeroloop .zloop
dec c dec c
jr nz, .zeronext jr nz, .znext
dec b dec b
jp z, .loop jp z, .Main
.zeronext .znext
ld [de], a ld [de], a
inc de inc de
jr .zeroloop jr .zloop
; repeats .rewrite
; 80, a0, c0 ; Repeat decompressed data from output.
; repeat decompressed data from output
.repeatertype
push hl push hl
push af push af
; get next byte
ld a, [hli]
; absolute?
bit 7, a
jr z, .absolute
; relative ld a, [hli]
; a = -a bit 7, a ; sign
and %01111111 ; forget the bit we just looked at jr z, .positive
.negative
; hl = de - a
; Since we can't subtract a from de,
; Make it negative and add de.
and %01111111
cpl cpl
; add de (current output address)
add e add e
ld l, a ld l, a
ld a, $ff ; -1 ld a, -1
adc d adc d
ld h, a ld h, a
jr .repeaters jr .ok
.absolute .positive
; get next byte (lo) ; Positive offsets are two bytes.
ld l, [hl] ld l, [hl]
; last byte (hi)
ld h, a ld h, a
; add starting output address ; add to starting output address
ld a, [$c2c2] ld a, [$c2c2]
add l add l
ld l, a ld l, a
@ -282,86 +256,86 @@ Decompress:: ; b50
adc h adc h
ld h, a ld h, a
.repeaters .ok
pop af 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 cp LZ_REPEAT
jr z, .Repeat
cp LZ_FLIP
jr z, .Flip
cp LZ_REVERSE
jr z, .Reverse
; 80 ; LZ_REPEAT ; Since LZ_LONG is command 7,
; repeat some decompressed data ; only commands 0-6 are passed in.
.repeat ; This leaves room for an extra command 7.
; done? ; However, lengths longer than 768
; would be interpreted as LZ_END.
; For now, it defaults to LZ_REPEAT.
.Repeat
; Copy decompressed data for bc bytes.
dec c dec c
jr nz, .repeatnext jr nz, .rnext
dec b dec b
jr z, .cleanup jr z, .donerw
.repeatnext .rnext
ld a, [hli] ld a, [hli]
ld [de], a ld [de], a
inc de inc de
jr .repeat jr .Repeat
; a0 ; LZ_FLIP .Flip
; repeat some decompressed data w/ flipped bit order ; Copy bitflipped decompressed data for bc bytes.
.flip
dec c dec c
jr nz, .flipnext jr nz, .fnext
dec b dec b
jp z, .cleanup jp z, .donerw
.flipnext .fnext
ld a, [hli] ld a, [hli]
push bc push bc
ld bc, $0008 lb bc, 0, 8
.fliploop .floop
rra rra
rl b rl b
dec c dec c
jr nz, .fliploop jr nz, .floop
ld a, b ld a, b
pop bc pop bc
ld [de], a ld [de], a
inc de inc de
jr .flip jr .Flip
; c0 ; LZ_REVERSE .Reverse
; repeat some decompressed data in reverse ; Copy reversed decompressed data for bc bytes.
.reverse
dec c dec c
jr nz, .reversenext jr nz, .rvnext
dec b dec b
jp z, .cleanup jp z, .donerw
.reversenext .rvnext
ld a, [hld] ld a, [hld]
ld [de], a ld [de], a
inc de inc de
jr .reverse jr .Reverse
.cleanup .donerw
; get type of repeat we just used
pop hl pop hl
; was it relative or absolute?
bit 7, [hl] bit 7, [hl]
jr nz, .next jr nz, .next
inc hl ; positive offset is two bytes
; skip two bytes for absolute
inc hl
; skip one byte for relative
.next .next
inc hl inc hl
jp .loop jp .Main
; c2f ; c2f