diff --git a/src/ce/include/compression.h b/src/ce/include/compression.h index 05a67e1cd..edc009d4a 100644 --- a/src/ce/include/compression.h +++ b/src/ce/include/compression.h @@ -8,6 +8,8 @@ #ifndef COMPRESSION_H #define COMPRESSION_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -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 */ diff --git a/src/ce/lz4.src b/src/ce/lz4.src new file mode 100644 index 000000000..2c30ae20c --- /dev/null +++ b/src/ce/lz4.src @@ -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 +.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 diff --git a/src/crt/cttz.src b/src/crt/cttz.src index 8f4e051a7..7f29a126b 100644 --- a/src/crt/cttz.src +++ b/src/crt/cttz.src @@ -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) diff --git a/src/makefile.mk b/src/makefile.mk index 7b06a5456..2b453fc38 100644 --- a/src/makefile.mk +++ b/src/makefile.mk @@ -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),) diff --git a/test/graphx/background_lz4/.gitignore b/test/graphx/background_lz4/.gitignore new file mode 100644 index 000000000..4cc0a8af5 --- /dev/null +++ b/test/graphx/background_lz4/.gitignore @@ -0,0 +1,7 @@ +obj/ +bin/ +src/gfx/*.c +src/gfx/*.h +src/gfx/*.8xv +.DS_Store +convimg.yaml.lst diff --git a/test/graphx/background_lz4/autotest.json b/test/graphx/background_lz4/autotest.json new file mode 100644 index 000000000..e903b0c75 --- /dev/null +++ b/test/graphx/background_lz4/autotest.json @@ -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" ] + } + } +} diff --git a/test/graphx/background_lz4/makefile b/test/graphx/background_lz4/makefile new file mode 100644 index 000000000..b869526cc --- /dev/null +++ b/test/graphx/background_lz4/makefile @@ -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) diff --git a/test/graphx/background_lz4/src/gfx/background.png b/test/graphx/background_lz4/src/gfx/background.png new file mode 100644 index 000000000..d24c31ee7 Binary files /dev/null and b/test/graphx/background_lz4/src/gfx/background.png differ diff --git a/test/graphx/background_lz4/src/gfx/convimg.yaml b/test/graphx/background_lz4/src/gfx/convimg.yaml new file mode 100644 index 000000000..699ed5919 --- /dev/null +++ b/test/graphx/background_lz4/src/gfx/convimg.yaml @@ -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 diff --git a/test/graphx/background_lz4/src/main.c b/test/graphx/background_lz4/src/main.c new file mode 100644 index 000000000..bc998945a --- /dev/null +++ b/test/graphx/background_lz4/src/main.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#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; +}