From 7b051113e1b33a1be2b3e3845524adabb35338ac Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Tue, 13 Sep 2022 22:12:03 +0200 Subject: [PATCH 1/8] z80_unpacker: initial working version with screen-slideshow example --- z80_unpacker/.gitignore | 4 + z80_unpacker/Makefile | 11 + z80_unpacker/example/example.asm | 49 +++ .../screens/Grongy - ZX Spectrum (2022).scr | Bin 0 -> 6912 bytes .../Grongy - ZX Spectrum (2022).scr.upk | Bin 0 -> 1813 bytes .../screens/Schafft - Poison (2017).scr | Bin 0 -> 6912 bytes .../screens/Schafft - Poison (2017).scr.upk | Bin 0 -> 1393 bytes .../screens/diver - Back to Bjork (2015).scr | Bin 0 -> 6912 bytes .../diver - Back to Bjork (2015).scr.upk | Bin 0 -> 2834 bytes ...014) (Forever 2014 Olympic Edition, 1).scr | Bin 0 -> 6912 bytes ... (Forever 2014 Olympic Edition, 1).scr.upk | Bin 0 -> 4317 bytes z80_unpacker/readme.txt | 19 ++ z80_unpacker/unpack.asm | 310 ++++++++++++++++++ 13 files changed, 393 insertions(+) create mode 100644 z80_unpacker/.gitignore create mode 100644 z80_unpacker/Makefile create mode 100644 z80_unpacker/example/example.asm create mode 100644 z80_unpacker/example/screens/Grongy - ZX Spectrum (2022).scr create mode 100644 z80_unpacker/example/screens/Grongy - ZX Spectrum (2022).scr.upk create mode 100644 z80_unpacker/example/screens/Schafft - Poison (2017).scr create mode 100644 z80_unpacker/example/screens/Schafft - Poison (2017).scr.upk create mode 100644 z80_unpacker/example/screens/diver - Back to Bjork (2015).scr create mode 100644 z80_unpacker/example/screens/diver - Back to Bjork (2015).scr.upk create mode 100644 z80_unpacker/example/screens/diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr create mode 100644 z80_unpacker/example/screens/diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr.upk create mode 100644 z80_unpacker/readme.txt create mode 100644 z80_unpacker/unpack.asm diff --git a/z80_unpacker/.gitignore b/z80_unpacker/.gitignore new file mode 100644 index 0000000..01edc46 --- /dev/null +++ b/z80_unpacker/.gitignore @@ -0,0 +1,4 @@ +*.bin +*.tap +*.sna +*.lst diff --git a/z80_unpacker/Makefile b/z80_unpacker/Makefile new file mode 100644 index 0000000..f4fc974 --- /dev/null +++ b/z80_unpacker/Makefile @@ -0,0 +1,11 @@ +all: unpack.bin example/example.sna + +# binary is positioned from ORG 0, not usable, just assembling to verify the syntax +unpack.bin: unpack.asm + sjasmplus --msg=war --lst --lstlab=sort --raw=unpack.bin unpack.asm + +example/example.sna: unpack.asm example/example.asm + cd example && sjasmplus --msg=war --lst --lstlab=sort example.asm + +clean: + $(RM) unpack.bin unpack.lst example/example.sna example/example.lst diff --git a/z80_unpacker/example/example.asm b/z80_unpacker/example/example.asm new file mode 100644 index 0000000..e42d0e4 --- /dev/null +++ b/z80_unpacker/example/example.asm @@ -0,0 +1,49 @@ +;; Example using upkr depacker for screens slideshow + OPT --syntax=abf + DEVICE ZXSPECTRUM48,$8FFF + + ORG $9000 +compressed_scr_files: ; border color byte + upkr-packed .scr file + DB 1 + INCBIN "screens/Grongy - ZX Spectrum (2022).scr.upk" + DB 7 + INCBIN "screens/Schafft - Poison (2017).scr.upk" + DB 0 + INCBIN "screens/diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr.upk" + DB 6 + INCBIN "screens/diver - Back to Bjork (2015).scr.upk" +.e: + +start: + di +; OPT --zxnext +; nextreg 7,3 ; ZX Next: switch to 28Mhz + ld ix,compressed_scr_files +.slideshow_loop + ; set BORDER for next image + ldi a,(ix) ; fake: ld a,(ix) : inc ix + out (254),a + ; call unpack of next image directly into VRAM + ld hl,$4000 ; target VRAM + exx + ; IX = packed data, HL' = destination ($4000) + ; returned IX will point right after the packed data + call upkr.unpack + ; do some busy loop with CPU to delay between images + ld bc,$AA00 +.delay: + .8 ex (sp),ix + dec c + jr nz,.delay + djnz .delay + ; check if all images were displayed, loop around from first one then + ld a,ixl + cp low compressed_scr_files.e + jr z,start + jr .slideshow_loop + + ; include the depacker library, optionally putting probs array buffer near end of RAM + DEFINE UPKR_PROBS_ORIGIN $FA00 ; if not defined, array will be put after unpack code + INCLUDE "../unpack.asm" + + SAVESNA "example.sna",start diff --git a/z80_unpacker/example/screens/Grongy - ZX Spectrum (2022).scr b/z80_unpacker/example/screens/Grongy - ZX Spectrum (2022).scr new file mode 100644 index 0000000000000000000000000000000000000000..6a6ce26b09265de39364db85825c15014955bad5 GIT binary patch literal 6912 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 e;sh@tyjfk$IOSalEko50PsV91Js=u8FX6 z^@>|pmN%+N7hzd-A;yLctO-+z)Jo~ zDbniX)z6%@;01Vf0V^h)!pUGXVW2A8rsT|ds88zqUNV0&j@ev$!^7s?gO?hh!pxcH zwdP_9b7B0o*gi7~$C2-sJT&r ze3I#7T%%=!;&mK9W1!laZpy$IO=bN;KqP@&BjtE`>LnrGjWy%Mg}*S|kRda~AMLiL zY0+yX2`{UA?v!4Ub2}hy$TznZK+y*yS(JOSQPIW`;|^FOUZ&${iQ=6j@L|q`^t45Z zz6x!c2Yo5`1s}0i^(@Oo+HS}&y9;9+8B4%E!;e<%Om+5l0bnh z=u8J);lDwh)12kh10|S$yagIdH~3^ucqo|Ut*-q!kpAnjacOsKs&XBY^;{vQh#Jw_ zfpZpSD9ME8E(m(br_$(UH_|Ea{m--lAq8xXQY#|)W^t{&EJ|~BCT)IpzUX@**6!&G zw(s@u*x}^2yW$+SR}pmzuL(rnCd^VTA2Q4kr9&wRSLl+s7mS%yO|}=|Q$D-tl9flR zaj*feI0%68z0ltrT<_03-7@iA2q*n2l8b(o1z=B-@En7QpT7yejxU#gwiq3?43jCv z0fCd^hWwRZT{@*%8qq5HGK}PJgjH%-7YQ1WcVE6YB{|+MzDpOBDEXI>=%txoL|6o2xwdol4CiWP^dFnI+&m|kX2JN;aq*^@_1@U%3arPj3cJEP)>cau8(;N? z1UABzKD&3xYTTqqeXsGTcldE7$2L>V8)v;$rqsxv+R zuRf=1W`6Wm9tk!ffwPjs2a`kq;tzcgMQ79ZYz|N`hr#Q!d>4jztUhV>YFEwY*r(sR z(j?&i1?t3pv$(+2bf=3~f=t0xg3A;QI(BYy2~ZU7 zuV;lR-L6g#Hk3!@Ti+xF08f=@?3*3kX-Q^ zSjgMV$@e+Qy`629?n(`lg$Jb^*+Fcij!%}z<*85sdc^sC&~9$!)}$?4w;711Zq?dS zJk43osJkL?QwMk14Ez7Q>Ak=jqUZvVV5f~MDCeRi;L-L5d52Oc(_Be}2q?6UPu9va z@jNxq&X@n$I0_Qnyy2-|h{1@4#tYl(>tCNotGhymZ;XFBQyz%V2*bSAz_03&IWC$? z#CYn#XS7=-aKDTsr z&mEU?ei%;{AYNTpP#rmjhsrV!{u3EgVNG~8Q8EJAlCC-h<@cVq_uWiP5jC0tW}S6mH`^kTO@oFdnrTMe_uYxFHHuI-5t*zGv1) zrj{Y(;KyyWi2nDWQmExe^Al==DWd=p+){!aMaLz`_8F9bR}+#UzgX0Mf(Ldw+K7EHLwRA{>OM@+Rq_FZOEqBwJwqSmlO zD?n@y24@!ED~%@-V1joG{nb6U2NNRvl$K}&sKoof4@(lu-i28RZDW&3xjzv=QzFs@ z*Ia-KX8cCQV}n$OI*+at#mNfx5q^L_MSm-zYW`DKX#&i3E;zgfI_>hknJBtb`De0!%>~l60<1_fKo6`XCY8gSL_m?<%ZdU}T z8K?uKd{Cs;eI1W%%$@_qsG^qq-?ZgY&Nt!4D{!e-Xgtsi#T5N~0Dyz#s-#nzEQHVK zFP|&G{9Zp%mzsy^VWnHBMh6K|u|wWMx87w}I9wKO!2&DEP0ZF5DE&HWm>i(;po zPEqkN`JmJ#j9Lb4ONd*1^ae{Ly9SRrL@eCT<-3!BSUQn|rV=teGLj~2EbEuNm;H-t zU%MioHTQ{_5jET(!pg+$-K-Oz^X2RLAZf$A;AP)c#80IJycR}yY8AnXi!sP zTPGb$vCM{$*JZ0;H+xIQysK9&!XjU#xq~70xEk{>BF#KXn75^taMC8A;)`fBtkH!@ zZbM>DJ-CB#2g86NT^90RY^n=ou^O?4MrJS8yNdd4yj({bpvZpEeKcl>P1Se8xn_CU kb|2r=121nRU~euLs04*GNs{jB1 literal 0 HcmV?d00001 diff --git a/z80_unpacker/example/screens/diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr b/z80_unpacker/example/screens/diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr new file mode 100644 index 0000000000000000000000000000000000000000..549041c2038e285c55b58798864e428eff585366 GIT binary patch literal 6912 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 L9Q~&U5bP?zRPslV literal 0 HcmV?d00001 diff --git a/z80_unpacker/readme.txt b/z80_unpacker/readme.txt new file mode 100644 index 0000000..b7fff1b --- /dev/null +++ b/z80_unpacker/readme.txt @@ -0,0 +1,19 @@ +Z80 asm implementation of C unpacker, code-size focused (not performance). + +**ONLY BITSTREAM** variant is currently supported, make sure to use "-b" in packer. + +The project is expected to further evolve, including possible changes to binary format, this is +initial version of Z80 unpacker to explore if/how it works and how it can be improved further. + +(copy full packer+depacker source to your project if you plan to use it, as future revisions +may be incompatible with files you will produce with current version) + +Asm syntax is z00m's sjasmplus: https://github.com/z00m128/sjasmplus + +TODO: +- build base corpus of test data to benchmark future changes in algorithm/format +- review first implementation to identify weak spots where the implementation can be shorter+faster +with acceptable small changes to the format +- review non-bitstream variant, if it's feasible to try to implement it with Z80 +- (@ped7g) Z80N version of unpacker for ZX Next devs +- (@exoticorn) add Z80 specific packer (to avoid confusion with original MicroW8 variant), and land it all to master branch, maybe in "z80" directory or something? (and overall decide how to organise+merge this upstream into main repo) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm new file mode 100644 index 0000000..de82b66 --- /dev/null +++ b/z80_unpacker/unpack.asm @@ -0,0 +1,310 @@ +;; https://github.com/exoticorn/upkr/blob/z80/c_unpacker/unpack.c - original C implementation +;; C source in comments ahead of asm - the C macros are removed to keep only bitstream variant +;; +;; initial version by Peter "Ped" Helcmanovsky (C) 2022, licensed same as upkr project ("unlicensed") +;; to assemble use z00m's sjasmplus: https://github.com/z00m128/sjasmplus +;; +;; you can define UPKR_PROBS_ORIGIN to specific 256 byte aligned address for probs array (386 bytes), +;; otherwise it will be positioned after the unpacker code (256 aligned) +;; +;; public API: +;; +;; upkr.unpack +;; IN: IX = packed data, HL' (shadow HL) = destination +;; OUT: IX = after packed data +;; modifies: all registers except IY, requires 14 bytes of stack space +;; + + OPT push reset --syntax=abf + MODULE upkr + +/* +u8* upkr_data_ptr; +u8 upkr_probs[1 + 255 + 1 + 2*32 + 2*32]; +u16 upkr_state; +u8 upkr_current_byte; +int upkr_bits_left; + +int upkr_unpack(void* destination, void* compressed_data) { + upkr_data_ptr = (u8*)compressed_data; + upkr_state = 0; + upkr_bits_left = 0; + for(int i = 0; i < sizeof(upkr_probs); ++i) + upkr_probs[i] = 128; + + u8* write_ptr = (u8*)destination; + + int prev_was_match = 0; + int offset = 0; + for(;;) { + if(upkr_decode_bit(0)) { + if(prev_was_match || upkr_decode_bit(256)) { + offset = upkr_decode_length(257) - 1; + if(offset == 0) { + break; + } + } + int length = upkr_decode_length(257 + 64); + while(length--) { + *write_ptr = write_ptr[-offset]; + ++write_ptr; + } + prev_was_match = 1; + } else { + int byte = 1; + while(byte < 256) { + int bit = upkr_decode_bit(byte); + byte = (byte << 1) + bit; + } + *write_ptr++ = byte; + prev_was_match = 0; + } + } + + return write_ptr - (u8*)destination; +} +*/ +; IN: IX = compressed_data, HL' = destination +unpack: + ; ** reset probs to 0x80, also reset HL (state) to zero, and set BC to probs+context 0 + ld hl,probs.c>>1 + ld bc,probs.e + ld a,$80 +.reset_probs: + dec bc + ld (bc),a ; will overwrite one extra byte after the array because of odd length + dec bc + ld (bc),a + dec l + jr nz,.reset_probs + exa + ; BC = probs (context_index 0), state HL = 0, A' = 0x80 (no source bits left in upkr_current_byte) + + ; ** main loop to decompress data + ld (.offset),hl ; offset = 0 +.decompress_data_reset_match: + ld d,0 ; prev_was_match = 0; +.decompress_data: + ld c,0 + call decode_bit ; if(upkr_decode_bit(0)) + jr c,.copy_chunk + + ; * extract byte from compressed data (literal) + ld e,1 ; E = byte = 1 +.decode_byte: + ld c,e + call decode_bit ; bit = upkr_decode_bit(byte); + rl e ; byte = (byte << 1) + bit; + jr nc,.decode_byte ; while(byte < 256) + ld a,e + exx + ld (hl),a ; *write_ptr++ = byte; + inc hl + exx + jr .decompress_data_reset_match + + ; * copy chunk of already decompressed data (match) +.copy_chunk: + inc b ; context_index = 256 + ; if(prev_was_match || upkr_decode_bit(256)) { + ; offset = upkr_decode_length(257) - 1; + ; if (0 == offset) break; + ; } + ld a,d ; A = prev_was_match + or a + jr nz,.decode_offset ; if(prev_was_match + call decode_bit ; upkr_decode_bit(256) + jr nc,.keep_offset +.decode_offset: + inc c + call decode_length + dec de ; offset = upkr_decode_length(257) - 1; + ld a,d + or e + ret z ; if(offset == 0) break + ld (.offset),de +.keep_offset: + ; int length = upkr_decode_length(257 + 64); + ; while(length--) { + ; *write_ptr = write_ptr[-offset]; + ; ++write_ptr; + ; } + ; prev_was_match = 1; + ld c,low(257+64) ; context_index = 257+64 + call decode_length ; length = upkr_decode_length(257 + 64); + push de + exx + push hl +.offset+*: ld de,0 + or a + sbc hl,de + pop de + pop bc + ldir + ex de,hl + exx + ld d,b ; prev_was_match = non-zero + djnz .decompress_data ; adjust context_index back to 0..255 range, go to main loop + +/* +int upkr_decode_bit(int context_index) { + while(upkr_state < 32768) { + if(upkr_bits_left == 0) { + upkr_current_byte = *upkr_data_ptr++; + upkr_bits_left = 8; + } + upkr_state = (upkr_state << 1) + (upkr_current_byte >> 7); + upkr_current_byte <<= 1; + --upkr_bits_left; + } + + int prob = upkr_probs[context_index]; + int bit = (upkr_state & 255) >= prob ? 1 : 0; + + int prob_offset = 16; + int state_offset = 0; + int state_scale = prob; + if(bit) { + state_offset = -prob; + state_scale = 256 - prob; + prob_offset = 0; + } + upkr_state = state_offset + state_scale * (upkr_state >> 8) + (upkr_state & 255); + upkr_probs[context_index] = prob_offset + prob - ((prob + 8) >> 4); + + return bit; +} +*/ +decode_bit: + ; HL = upkr_state + ; IX = upkr_data_ptr + ; BC = probs+context_index + ; A' = upkr_current_byte (!!! init to 0x80 at start, not 0x00) + ; preserves DE + ; ** while (state < 32768) - initial check + push de + bit 7,h + jr nz,.state_b15_set + exa + ; ** while body +.state_b15_zero: + ; HL = upkr_state + ; IX = upkr_data_ptr + ; A = upkr_current_byte (init to 0x80 at start, not 0x00) + add a,a ; upkr_current_byte <<= 1; // and testing if(upkr_bits_left == 0) + jr nz,.has_bit ; CF=data, ZF=0 -> some bits + stop bit still available + ; CF=1 (by stop bit) + ld a,(ix) + inc ix ; upkr_current_byte = *upkr_data_ptr++; + adc a,a ; CF=data, b0=1 as new stop bit +.has_bit: + adc hl,hl ; upkr_state = (upkr_state << 1) + (upkr_current_byte >> 7); + jp p,.state_b15_zero ; while (state < 32768) + exa + ; ** set "bit" +.state_b15_set: + ld a,(bc) ; A = upkr_probs[context_index] + dec a ; prob is in ~7..249 range, never zero, safe to -1 + cp l ; CF = bit = prob-1 < (upkr_state & 255) <=> prob <= (upkr_state & 255) + inc a + ; ** adjust state + push af + push af + push hl + push af + jr nc,.bit_is_0 + neg ; A = -prob == (256-prob), CF=1 preserved +.bit_is_0: + ld d,0 + ld e,a ; DE = state_scale ; prob || (256-prob) + ld l,d ; H:L = (upkr_state>>8) : 0 + ld a,8 ; counter +.mulLoop: + add hl,hl + jr nc,.mul0 + add hl,de +.mul0: + dec a + jr nz,.mulLoop ; until HL = state_scale * (upkr_state>>8) + pop af + jr nc,.bit_is_0_2 + dec d ; D = 0xFF (DE = -prob) + add hl,de ; HL += -prob +.bit_is_0_2: ; HL = state_offset + state_scale * (upkr_state >> 8) + pop de + ld d,0 ; DE = (upkr_state & 255) + add hl,de ; HL = state_offset + state_scale * (upkr_state >> 8) + (upkr_state & 255) ; new upkr_state + ; *** adjust probs[context_index] + pop af ; restore prob and bit + ld e,a + jr c,.bit_is_1 + ld d,-16 ; 0xF0 +.bit_is_1: ; D:E = -prob_offset:prob, A = prob + ;FIXME and + 4x rra will be probably shorter! + srl a + srl a + srl a + srl a + adc a,d ; A = -prob_offset + ((prob + 8) >> 4) + neg + add a,e ; A = prob_offset + prob - ((prob + 8) >> 4) + ld (bc),a ; update probs[context_index] + pop af ; restore resulting CF = bit + ; TODO: check if it's possible to `cpl` instead of neg, have +1 on original prob, + ; and get correct CF=bit from `add a,e` then (without extra push+pop AF) + ; !!! I think this will **NOT** work, because clamping of prob ends with +-0 at both ends (cpl 0 -> 255 -> CF=1) + pop de + ret + +/* +int upkr_decode_length(int context_index) { + int length = 0; + int bit_pos = 0; + while(upkr_decode_bit(context_index)) { + length |= upkr_decode_bit(context_index + 1) << bit_pos++; + context_index += 2; + } + return length | (1 << bit_pos); +} +*/ +decode_length: + ; HL = upkr_state + ; IX = upkr_data_ptr + ; BC = probs+context_index + ; A' = upkr_current_byte (!!! init to 0x80 at start, not 0x00) + ; return length in DE + ld de,$8000 ; length = 0 with positional-stop-bit + jr .loop_entry +.loop: + inc bc ; context_index + 1 ; TODO can be just `inc c` for 257.. and 257+64.. contexts + call decode_bit + rr d + rr e ; DE = length = (length >> 1) | (bit << 15); + inc bc ; context_index += 2 ; TODO can be just `inc c` for 257.. and 257+64.. contexts +.loop_entry: + call decode_bit + jr c,.loop + scf ; will become this final `| (1 << bit_pos)` bit +.fix_bit_pos: + rr d + rr e + jr nc,.fix_bit_pos ; until stop bit is reached (all bits did land to correct position) + ret + + DISPLAY "upkr.unpack total size: ",/D,$-unpack + + ; reserve space for probs array without emitting any machine code (using only EQU) + + IFDEF UPKR_PROBS_ORIGIN ; if specific address is defined by user, move probs array there + ORG UPKR_PROBS_ORIGIN + ENDIF + +probs: EQU ($+255) & -$100 ; probs array aligned to 256 +.real_c: EQU 1 + 255 + 1 + 2*32 + 2*32 ; real size of probs array +.c: EQU (.real_c + 1) & -2 ; padding to even size (required by init code) +.e: EQU probs + .c + + DISPLAY "upkr.unpack probs array placed at: ",/A,probs,",\tsize: ",/A,probs.c + + ENDMODULE + OPT pop From a19ec2abb795bf1dddfc25bfb7078edfe714c836 Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Tue, 13 Sep 2022 22:53:15 +0200 Subject: [PATCH 2/8] z80_unpacker: optimisations: remove .offset init first offset is mandatory in packed data --- z80_unpacker/unpack.asm | 1 - 1 file changed, 1 deletion(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index de82b66..2e5f9eb 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -81,7 +81,6 @@ unpack: ; BC = probs (context_index 0), state HL = 0, A' = 0x80 (no source bits left in upkr_current_byte) ; ** main loop to decompress data - ld (.offset),hl ; offset = 0 .decompress_data_reset_match: ld d,0 ; prev_was_match = 0; .decompress_data: From ea5c0b1b15cffcde2736c752adfaf957b18a8a1e Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Tue, 13 Sep 2022 23:15:18 +0200 Subject: [PATCH 3/8] z80_unpacker: optimisations: shorter `>>4` in probs update --- z80_unpacker/unpack.asm | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index 2e5f9eb..cdd800b 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -239,19 +239,16 @@ decode_bit: jr c,.bit_is_1 ld d,-16 ; 0xF0 .bit_is_1: ; D:E = -prob_offset:prob, A = prob - ;FIXME and + 4x rra will be probably shorter! - srl a - srl a - srl a - srl a + and $F8 + rra + rra + rra + rra adc a,d ; A = -prob_offset + ((prob + 8) >> 4) neg add a,e ; A = prob_offset + prob - ((prob + 8) >> 4) ld (bc),a ; update probs[context_index] pop af ; restore resulting CF = bit - ; TODO: check if it's possible to `cpl` instead of neg, have +1 on original prob, - ; and get correct CF=bit from `add a,e` then (without extra push+pop AF) - ; !!! I think this will **NOT** work, because clamping of prob ends with +-0 at both ends (cpl 0 -> 255 -> CF=1) pop de ret From 919a892ef0f4eec37883db5e6634e3cbff1841fa Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Tue, 13 Sep 2022 23:25:03 +0200 Subject: [PATCH 4/8] z80_unpacker: optimisations: -1B by decode_length returning CF=0 --- z80_unpacker/unpack.asm | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index cdd800b..d103a43 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -135,8 +135,7 @@ unpack: exx push hl .offset+*: ld de,0 - or a - sbc hl,de + sbc hl,de ; CF=0 from decode_length pop de pop bc ldir @@ -268,8 +267,8 @@ decode_length: ; IX = upkr_data_ptr ; BC = probs+context_index ; A' = upkr_current_byte (!!! init to 0x80 at start, not 0x00) - ; return length in DE - ld de,$8000 ; length = 0 with positional-stop-bit + ; return length in DE, CF=0 + ld de,$7FFF ; length = 0 with positional-stop-bit jr .loop_entry .loop: inc bc ; context_index + 1 ; TODO can be just `inc c` for 257.. and 257+64.. contexts @@ -280,12 +279,12 @@ decode_length: .loop_entry: call decode_bit jr c,.loop - scf ; will become this final `| (1 << bit_pos)` bit .fix_bit_pos: + ccf ; NC will become this final `| (1 << bit_pos)` bit rr d rr e - jr nc,.fix_bit_pos ; until stop bit is reached (all bits did land to correct position) - ret + jr c,.fix_bit_pos ; until stop bit is reached (all bits did land to correct position) + ret ; return with CF=0 (important for unpack routine) DISPLAY "upkr.unpack total size: ",/D,$-unpack From d30baaa91f0c5b2aafcac96f3297d2ffb9a59376 Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Tue, 13 Sep 2022 23:57:59 +0200 Subject: [PATCH 5/8] z80_unpacker: optimisations: -1B by keeping write_ptr in DE' --- z80_unpacker/example/example.asm | 4 ++-- z80_unpacker/unpack.asm | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/z80_unpacker/example/example.asm b/z80_unpacker/example/example.asm index e42d0e4..80fd790 100644 --- a/z80_unpacker/example/example.asm +++ b/z80_unpacker/example/example.asm @@ -24,9 +24,9 @@ start: ldi a,(ix) ; fake: ld a,(ix) : inc ix out (254),a ; call unpack of next image directly into VRAM - ld hl,$4000 ; target VRAM + ld de,$4000 ; target VRAM exx - ; IX = packed data, HL' = destination ($4000) + ; IX = packed data, DE' = destination ($4000) ; returned IX will point right after the packed data call upkr.unpack ; do some busy loop with CPU to delay between images diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index d103a43..587d88f 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -10,7 +10,7 @@ ;; public API: ;; ;; upkr.unpack -;; IN: IX = packed data, HL' (shadow HL) = destination +;; IN: IX = packed data, DE' (shadow DE) = destination ;; OUT: IX = after packed data ;; modifies: all registers except IY, requires 14 bytes of stack space ;; @@ -64,7 +64,7 @@ int upkr_unpack(void* destination, void* compressed_data) { return write_ptr - (u8*)destination; } */ -; IN: IX = compressed_data, HL' = destination +; IN: IX = compressed_data, DE' = destination unpack: ; ** reset probs to 0x80, also reset HL (state) to zero, and set BC to probs+context 0 ld hl,probs.c>>1 @@ -97,8 +97,8 @@ unpack: jr nc,.decode_byte ; while(byte < 256) ld a,e exx - ld (hl),a ; *write_ptr++ = byte; - inc hl + ld (de),a ; *write_ptr++ = byte; + inc de exx jr .decompress_data_reset_match @@ -133,13 +133,12 @@ unpack: call decode_length ; length = upkr_decode_length(257 + 64); push de exx - push hl -.offset+*: ld de,0 - sbc hl,de ; CF=0 from decode_length - pop de - pop bc + ld h,d ; DE = write_ptr + ld l,e +.offset+*: ld bc,0 + sbc hl,bc ; CF=0 from decode_length ; HL = write_ptr - offset + pop bc ; BC = length ldir - ex de,hl exx ld d,b ; prev_was_match = non-zero djnz .decompress_data ; adjust context_index back to 0..255 range, go to main loop From 511ddefc08161125b78e81d7444900150ad21980 Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Wed, 14 Sep 2022 00:01:51 +0200 Subject: [PATCH 6/8] z80_unpacker: optimisations: -4T per offset/length bit decoded making the 256-alignment of probs array even more baked-in, but there was no real chance to get rid of that any way --- z80_unpacker/unpack.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index 587d88f..7167242 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -270,11 +270,11 @@ decode_length: ld de,$7FFF ; length = 0 with positional-stop-bit jr .loop_entry .loop: - inc bc ; context_index + 1 ; TODO can be just `inc c` for 257.. and 257+64.. contexts + inc c ; context_index + 1 call decode_bit rr d rr e ; DE = length = (length >> 1) | (bit << 15); - inc bc ; context_index += 2 ; TODO can be just `inc c` for 257.. and 257+64.. contexts + inc c ; context_index += 2 .loop_entry: call decode_bit jr c,.loop From 02d20867eecb2d1f1d769f6f966a84923806ec68 Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Wed, 14 Sep 2022 01:01:07 +0200 Subject: [PATCH 7/8] z80_unpacker: optimisations: -2B in unpack implementation = 185B --- z80_unpacker/unpack.asm | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index 7167242..d179888 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -109,12 +109,10 @@ unpack: ; offset = upkr_decode_length(257) - 1; ; if (0 == offset) break; ; } - ld a,d ; A = prev_was_match - or a - jr nz,.decode_offset ; if(prev_was_match - call decode_bit ; upkr_decode_bit(256) - jr nc,.keep_offset -.decode_offset: + xor a + cp d ; CF = prev_was_match + call nc,decode_bit ; if not prev_was_match, then upkr_decode_bit(256) + jr nc,.keep_offset ; if neither, keep old offset inc c call decode_length dec de ; offset = upkr_decode_length(257) - 1; From c7ea11bce3184f101543d127b644dad221007fa5 Mon Sep 17 00:00:00 2001 From: "Peter Helcmanovsky (Ped)" Date: Wed, 14 Sep 2022 01:44:04 +0200 Subject: [PATCH 8/8] z80_unpacker: optimisations: -2B in unpack implementation = 183B --- z80_unpacker/unpack.asm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/z80_unpacker/unpack.asm b/z80_unpacker/unpack.asm index d179888..cd2d507 100644 --- a/z80_unpacker/unpack.asm +++ b/z80_unpacker/unpack.asm @@ -89,13 +89,12 @@ unpack: jr c,.copy_chunk ; * extract byte from compressed data (literal) - ld e,1 ; E = byte = 1 + inc c ; C = byte = 1 (and also context_index) .decode_byte: - ld c,e call decode_bit ; bit = upkr_decode_bit(byte); - rl e ; byte = (byte << 1) + bit; + rl c ; byte = (byte << 1) + bit; jr nc,.decode_byte ; while(byte < 256) - ld a,e + ld a,c exx ld (de),a ; *write_ptr++ = byte; inc de