pokecrystal/home/decompress.asm

368 lines
5.2 KiB
NASM
Raw Normal View History

FarDecompress:: ; b40
2013-08-20 07:34:54 +00:00
; 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
2013-08-20 07:34:54 +00:00
; 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