Skip to content

Commit 46fc4d8

Browse files
committed
Implement LZ4 block decompression
1 parent bc12b16 commit 46fc4d8

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed

src/ce/include/compression.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ void zx7_Decompress(void *dst, const void *src);
2828
*/
2929
void zx0_Decompress(void *dst, const void *src);
3030

31+
/**
32+
* Decompress a length-prefixed block of LZ4 encoded data.
33+
*
34+
* @param[in] dst Uncompressed data destination.
35+
* @param[in] src Compressed data source.
36+
*/
37+
void lz4_Decompress(void *dst, const void *src);
38+
39+
/**
40+
* Decompress a raw block of LZ4 encoded data.
41+
*
42+
* @param[in] dst Uncompressed data destination.
43+
* @param[in] src Compressed data source start.
44+
* @param[in] size Compressed data source size.
45+
*
46+
* @returns The uncompressed data size.
47+
*/
48+
size_t lz4_Decompress_Block(void *dst, const void *src, size_t size);
49+
3150
#ifdef __cplusplus
3251
}
3352
#endif

src/ce/lz4.src

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
.assume adl=1
2+
3+
.section .text._lz4_Decompress_Block
4+
.global _lz4_Decompress_Block
5+
.type _lz4_Decompress_Block, @function
6+
7+
; size_t lz4_Decompress_Block(void *dst, const void *block, size_t block_size);
8+
_lz4_Decompress_Block:
9+
ld iy, 0
10+
add iy, sp
11+
; DE = dst
12+
ld de, (iy + 3)
13+
; BC = block_size
14+
ld bc, (iy + 9)
15+
; IY = block
16+
ld iy, (iy + 6)
17+
; Save dst
18+
push de
19+
call lz4_decompress_block_internal
20+
; Calculate decompressed size based on final dst
21+
ex de, hl
22+
pop de
23+
or a, a
24+
sbc hl, de
25+
ret
26+
27+
.section .text._lz4_Decompress
28+
.global _lz4_Decompress
29+
.type _lz4_Decompress, @function
30+
31+
; void lz4_Decompress(void *dst, const void *src);
32+
_lz4_Decompress:
33+
ld hl, 3
34+
add hl, sp
35+
; DE = dst
36+
ld de, (hl)
37+
inc hl
38+
inc hl
39+
inc hl
40+
; IY = src
41+
ld iy, (hl)
42+
; BC = size
43+
ld bc, (iy)
44+
; Skip size header
45+
lea iy, iy + 3
46+
; Fallthrough
47+
48+
.local lz4_decompress_block_internal
49+
.type lz4_decompress_block_internal, @function
50+
51+
; Input: IY = compressed block, DE = decompression buffer, BC = size of block
52+
; Output: IY = after compressed block, DE = after decompressed data
53+
; Destroys: AF, BC, HL
54+
lz4_decompress_block_internal:
55+
; HL = compressed block
56+
lea hl, iy + 0
57+
; Set IY to after compressed block
58+
add iy, bc
59+
inc.s bc ; BCU = 0
60+
; Read the first token
61+
ld a, (hl)
62+
; Check for the special case of zero-length output
63+
or a, a
64+
jr nz, .L.start
65+
ret
66+
67+
.L.copy_backref_long:
68+
; Read the last length byte
69+
dec hl
70+
ld a, (hl)
71+
inc hl
72+
; Save the input pointer and restore the backref pointer
73+
ex (sp), hl
74+
; Copy the amount prior to the last length byte, minus 1
75+
ldir
76+
; Copy the amount of the last length byte, plus 1
77+
ld c, a
78+
inc bc
79+
.L.copy_backref:
80+
; Copy the backref data
81+
ldir
82+
; Restore the input pointer
83+
pop hl
84+
; Read the next token
85+
ld a, (hl)
86+
.L.start:
87+
inc hl
88+
; Check if the literal field is zero
89+
; If so, this cannot be the end of block, go directly to backref handling
90+
ld c, $10
91+
cp a, c
92+
jr c, .L.backref
93+
; Save the token in B
94+
ld b, a
95+
; Check for extended literal
96+
add a, c
97+
jr c, .L.literal_long
98+
; Multiply the token by 16, placing the literal field in B
99+
mlt bc
100+
; Set BC to the literal length, which is known to be non-zero
101+
ld c, b
102+
ld b, 0
103+
; Mask the backref field, affecting the zero flag, and clear carry
104+
and a, $0F
105+
.L.copy_literal:
106+
; Copy the literals
107+
ldir
108+
; Zero flag is currently set according to the backref field
109+
; If the backref field is non-zero, this cannot be end of block
110+
jr nz, .L.backref
111+
; Check for end of block (carry is currently clear)
112+
lea bc, iy + 0
113+
sbc hl, bc
114+
ret nc
115+
add hl, bc
116+
inc.s bc ; BCU = 0
117+
.L.backref:
118+
; Read backref offset into BC
119+
ld c, (hl)
120+
inc hl
121+
ld b, (hl)
122+
inc hl
123+
; Save the input pointer
124+
push hl
125+
; Add 4 to backref length field, and clear carry
126+
add a, 4
127+
; Calculate HL = DE - BC
128+
sbc hl, hl
129+
add hl, de
130+
sbc hl, bc
131+
; Set BC to backref length
132+
ld c, a
133+
ld b, 0
134+
; Check for extended length
135+
add a, -($0F + 4)
136+
jr nc, .L.copy_backref
137+
; Save the backref pointer and restore the input pointer
138+
ex (sp), hl
139+
.L.backref_loop_outer:
140+
; Carry is always set here, set A = 255
141+
sbc a, a
142+
.L.backref_loop:
143+
; Check for a length byte of 255, and decrement the length by 1
144+
cpi
145+
jr nz, .L.copy_backref_long
146+
; Increment the length by 256
147+
inc b
148+
; Loop if B did not carry to 0
149+
jr nz, .L.backref_loop
150+
; Save C
151+
ld a, c
152+
; Set B = C = 255
153+
dec b
154+
ld c, b
155+
; Increment BCU and set B = C = 0
156+
inc bc
157+
; Restore C
158+
ld c, a
159+
jr .L.backref_loop_outer
160+
161+
.L.literal_long:
162+
; Save the backref field
163+
push af
164+
; Start with literal length of 15
165+
ld b, 0
166+
dec bc
167+
.L.literal_loop_outer:
168+
; Carry is always set here, set A = 255
169+
sbc a, a
170+
.L.literal_loop:
171+
; Check for a length byte of 255, and decrement the length by 1
172+
cpi
173+
jr nz, .L.copy_literal_long
174+
; Increment the length by 256
175+
inc b
176+
; Loop if B did not carry to 0
177+
jr nz, .L.literal_loop
178+
; Save C
179+
ld a, c
180+
; Set B = C = 255
181+
dec b
182+
ld c, b
183+
; Increment BCU and set B = C = 0
184+
inc bc
185+
; Restore C
186+
ld c, a
187+
jr .L.literal_loop_outer
188+
189+
.L.copy_literal_long:
190+
; Read the last length byte
191+
dec hl
192+
ld a, (hl)
193+
inc hl
194+
; Copy the amount prior to the last length byte, minus 1
195+
ldir
196+
; Copy the amount of the last length byte, plus 1
197+
ld c, a
198+
inc bc
199+
; Restore the backref field
200+
pop af
201+
; Affect the zero flag based on the backref field and clear carry
202+
or a, a
203+
jr .L.copy_literal

src/crt/cttz.src

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
__bcttz:
88
cp a, 1
99

10-
.section .text.__cttz_common
1110
.local __cttz_common
1211
; Input: A=byte, CF=(A==0)
1312
; Output: A=cttz(A)

0 commit comments

Comments
 (0)