Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/ce/include/compression.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#ifndef COMPRESSION_H
#define COMPRESSION_H

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -28,8 +30,27 @@ void zx7_Decompress(void *dst, const void *src);
*/
void zx0_Decompress(void *dst, const void *src);

/**
* Decompress a length-prefixed block of LZ4 encoded data.
*
* @param[in] dst Uncompressed data destination.
* @param[in] src Compressed data source.
*/
void lz4_Decompress(void *dst, const void *src);

/**
* Decompress a raw block of LZ4 encoded data.
*
* @param[in] dst Uncompressed data destination.
* @param[in] src Compressed data source start.
* @param[in] size Compressed data source size.
*
* @returns The uncompressed data size.
*/
size_t lz4_Decompress_Block(void *dst, const void *src, size_t size);

#ifdef __cplusplus
}
#endif

#endif
#endif /* COMPRESSION_H */
206 changes: 206 additions & 0 deletions src/ce/lz4.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
.assume adl=1

.section .text._lz4_Decompress_Block
.global _lz4_Decompress_Block
.type _lz4_Decompress_Block, @function

; size_t lz4_Decompress_Block(void *dst, const void *block, size_t block_size);
_lz4_Decompress_Block:
ld iy, 0
add iy, sp
; DE = dst
ld de, (iy + 3)
; BC = block_size
ld bc, (iy + 9)
; IY = block
ld iy, (iy + 6)
; Save dst
push de
call lz4_decompress_block_internal
; Calculate decompressed size based on final dst
ex de, hl
pop de
sbc hl, de
ret

.section .text._lz4_Decompress
.global _lz4_Decompress
.type _lz4_Decompress, @function

; void lz4_Decompress(void *dst, const void *src);
_lz4_Decompress:
ld hl, 3
add hl, sp
; DE = dst
ld de, (hl)
inc hl
inc hl
inc hl
; IY = src
ld iy, (hl)
; BC = size
ld bc, (iy)
; Skip size header
lea iy, iy + 3
; Fallthrough

.local lz4_decompress_block_internal
.type lz4_decompress_block_internal, @function

; Input: IY = compressed block, DE = decompression buffer, BC = size of block
; Output: IY = after compressed block, DE = after decompressed data, carry flag clear
; Destroys: AF, BC, HL
lz4_decompress_block_internal:
; HL = compressed block
lea hl, iy + 0
; Set IY to after compressed block
add iy, bc
inc.s bc ; BCU = 0
; Read the first token
ld a, (hl)
; Check for the special case of zero-length output
or a, a
jr nz, .L.start
ret

.L.copy_backref_long:
; Save the input pointer and restore the backref pointer
ex (sp), hl
; Copy the amount prior to the last length byte, minus 1
ldir
; Copy the amount of the last length byte, plus 1
ld c, a
.L.copy_backref:
; Copy the backref data
ldir
; Restore the input pointer
pop hl
; Read the next token
ld a, (hl)
.L.start:
inc hl
; Check if the literal field is zero
; If so, this cannot be the end of block, go directly to backref handling
ld c, $10
cp a, c
jr c, .L.backref
; Save the token in B
ld b, a
; Check for extended literal
add a, c
jr c, .L.literal_long
; Multiply the token by 16, placing the literal field in B
mlt bc
; Set BC to the literal length, which is known to be non-zero
ld c, b
ld b, 0
; Copy the literals
ldir
; Mask the backref field
and a, $0F
; If the backref field is non-zero, this cannot be end of block
jr nz, .L.backref
; Check for end of block quickly
ld a, l
sub a, iyl
jr z, .L.maybe_end_of_block
xor a, a
.L.backref:
; Read backref offset into BC
ld c, (hl)
inc hl
ld b, (hl)
inc hl
; Save the input pointer
push hl
; Add 4 to backref length field, and clear carry
add a, 4
; Calculate HL = DE - BC
sbc hl, hl
add hl, de
sbc hl, bc
; Set BC to backref length
ld c, a
ld b, 0
; Check for extended length
add a, -($0F + 4)
jr nz, .L.copy_backref
; Save the backref pointer and restore the input pointer
ex (sp), hl
.L.backref_loop:
; Read the next length byte and add 1
adc a, (hl)
inc hl
; Decrement the length by 1
dec bc
; Check if the length byte was 255
jr nz, .L.copy_backref_long
; Increment the length by 256
inc b
; Loop if B did not carry to 0
jr nz, .L.backref_loop
; Save C
ld a, c
; Set B = C = 255
dec b
ld c, b
; Increment BCU and set B = C = 0
inc bc
; Restore C
ld c, a
; Set A = 0, preserving carry as 1
ld a, b
jr .L.backref_loop

.L.literal_long:
; Save the backref field and zero flag
push af
; Start with literal length of 15
ld b, 0
dec bc
.L.literal_loop_outer:
; Set A = 0, preserving carry as 1
ld a, b
.L.literal_loop:
; Read the next length byte and add 1
adc a, (hl)
inc hl
; Decrement the length by 1
dec bc
; Check if the length byte was 255
jr nz, .L.copy_literal_long
; Increment the length by 256
inc b
; Loop if B did not carry to 0
jr nz, .L.literal_loop
; Save C
ld a, c
; Set B = C = 255
dec b
ld c, b
; Increment BCU and set B = C = 0
inc bc
; Restore C
ld c, a
jr .L.literal_loop_outer

.L.copy_literal_long:
; Copy the amount prior to the last length byte, minus 1
ldir
; Copy the amount of the last length byte, plus 1
ld c, a
ldir
; Restore the backref field and zero flag
pop af
; If the backref field is non-zero, this cannot be end of block
jr nz, .L.backref
; Clear carry
or a, a
Comment on lines +194 to +198
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would have been, yes, but it became set again from the pop af

Copy link
Contributor

@ZERICO2005 ZERICO2005 Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(my original comment was deleted)

Copy link
Contributor

@ZERICO2005 ZERICO2005 Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading it over again, it looks like the carry is always set:

	add	a, c
	jr	c, .L.literal_long

; NOT FALL-THROUGH
.L.literal_long:
	push	af

This means we might be able to do something like. This does change it so carry is set if HL <= BC instead of HL < BC so function will return with the carry being set

	; or	a, a
	; carry always set
.L.maybe_end_of_block:
	; Full check for end of block (carry is clear and A = 0)
	lea	bc, iy - 1
	sbc	hl, bc
	add	hl, bc
	inc.s	bc	; BCU = 0
	jr	nz, .L.backref
	ret

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That won't work because it would need an added inc hl, plus this is used by another path where carry is clear

.L.maybe_end_of_block:
; Full check for end of block (carry is clear and A = 0)
lea bc, iy + 0
sbc hl, bc
add hl, bc
inc.s bc ; BCU = 0
jr nz, .L.backref
ret
1 change: 0 additions & 1 deletion src/crt/cttz.src
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
__bcttz:
cp a, 1

.section .text.__cttz_common
.local __cttz_common
; Input: A=byte, CF=(A==0)
; Output: A=cttz(A)
Expand Down
2 changes: 1 addition & 1 deletion src/makefile.mk
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ LIBLOAD_LIBS ?= $(wildcard $(CEDEV_TOOLCHAIN)/lib/libload/*.lib) $(EXTRA_LIBLOAD
LIBLOAD_LIBS := $(foreach l,$(filter-out %libload.lib,$(LIBLOAD_LIBS)),$(call QUOTE_ARG,$(call FORWARD_PATH,$l)))
endif

# check if there is an icon present that to convert
# check if there is an icon to convert
ifneq ($(ICON_IMG),)
ICON_SRC = $(OBJDIR)/icon.s
ifneq ($(DESCRIPTION),)
Expand Down
7 changes: 7 additions & 0 deletions test/graphx/background_lz4/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
obj/
bin/
src/gfx/*.c
src/gfx/*.h
src/gfx/*.8xv
.DS_Store
convimg.yaml.lst
45 changes: 45 additions & 0 deletions test/graphx/background_lz4/autotest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"transfer_files":
[
"bin/DEMO.8xp"
],
"target":
{
"name": "DEMO",
"isASM": true
},
"sequence":
[
"action|launch",
"delay|500",
"hashWait|1",
"key|enter",
"hashWait|2",
"key|enter",
"hashWait|3"
],
"hashes":
{
"1":
{
"description": "Test fullscreen image display",
"start": "vram_start",
"size": "vram_8_size",
"expected_CRCs": [ "69A1B0DB" ]
},
"2":
{
"description": "Test fullscreen image display",
"start": "vram_start",
"size": "vram_8_size",
"expected_CRCs": [ "69A1B0DB" ]
},
"3":
{
"description": "Test program exit",
"start": "vram_start",
"size": "vram_16_size",
"expected_CRCs": [ "FFAF89BA", "101734A5", "9DA19F44", "A32840C8", "349F4775" ]
}
}
}
15 changes: 15 additions & 0 deletions test/graphx/background_lz4/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ----------------------------
# Makefile Options
# ----------------------------

NAME = DEMO
ICON = icon.png
DESCRIPTION = "CE C Toolchain Demo"
COMPRESSED = NO

CFLAGS = -Wall -Wextra -Oz
CXXFLAGS = -Wall -Wextra -Oz

# ----------------------------

include $(shell cedev-config --makefile)
Binary file added test/graphx/background_lz4/src/gfx/background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions test/graphx/background_lz4/src/gfx/convimg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
palettes:
- name: global_palette
images: automatic

converts:
- name: background
palette: global_palette
compress: lz4hc
width-and-height: false
images:
- background.png

outputs:
- type: c
include-file: gfx.h
palettes:
- global_palette
converts:
- background
29 changes: 29 additions & 0 deletions test/graphx/background_lz4/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <ti/getcsc.h>
#include <graphx.h>
#include <compression.h>

#include "gfx/gfx.h"

int main(void)
{
gfx_Begin();

gfx_SetPalette(global_palette, sizeof_global_palette, 0);

lz4_Decompress(gfx_vram, background_compressed);

while (!os_GetCSC());

gfx_ZeroScreen();

if (lz4_Decompress_Block(gfx_vram, background_compressed + sizeof(uint24_t), background_compressed_size - sizeof(uint24_t)) != background_size)
{
gfx_ZeroScreen();
}

while (!os_GetCSC());

gfx_End();

return 0;
}
Loading