From 6f6e36b231c6845b4881ae9f9ec65f89a421c935 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Sat, 4 Jun 2022 00:52:41 -0400 Subject: [PATCH] compression: doing some tests --- compression/comparison/Makefile | 50 +++ compression/comparison/decompress_fast_v2.s | 370 ++++++++++++++++++++ compression/comparison/hardware.inc | 102 ++++++ compression/comparison/hello.bas | 2 + compression/comparison/level5.png | Bin 0 -> 29227 bytes compression/comparison/lzsa_test.s | 26 ++ compression/comparison/zp.inc | 131 +++++++ 7 files changed, 681 insertions(+) create mode 100644 compression/comparison/Makefile create mode 100644 compression/comparison/decompress_fast_v2.s create mode 100644 compression/comparison/hardware.inc create mode 100644 compression/comparison/hello.bas create mode 100644 compression/comparison/level5.png create mode 100644 compression/comparison/lzsa_test.s create mode 100644 compression/comparison/zp.inc diff --git a/compression/comparison/Makefile b/compression/comparison/Makefile new file mode 100644 index 00000000..41515bca --- /dev/null +++ b/compression/comparison/Makefile @@ -0,0 +1,50 @@ +PNG2RLE = ../../utils/gr-utils/png2rle +PNG2GR = ../../utils/gr-utils/png2gr +PNG2HGR = ../../utils/hgr-utils/png2hgr +LZSA = ~/research/lzsa/lzsa/lzsa +B2D = ../../utils/bmp2dhr/b2d + +DOS33 = ../../utils/dos33fs-utils/dos33 +TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft +EMPTYDISK = ../../empty_disk/empty.dsk +LINKERSCRIPTS = ../../linker_scripts/ + + + +all: compression_test.dsk + +compression_test.dsk: HELLO LZSA_TEST + cp $(EMPTYDISK) compression_test.dsk + $(DOS33) -y compression_test.dsk SAVE A HELLO + $(DOS33) -y compression_test.dsk BSAVE -a 0x6000 LZSA_TEST + + +#### + +HELLO: hello.bas + $(TOKENIZE) < hello.bas > HELLO + +### + +LZSA_TEST: lzsa_test.o + ld65 -o LZSA_TEST lzsa_test.o -C $(LINKERSCRIPTS)/apple2_6000.inc + +lzsa_test.o: lzsa_test.s graphics_level5.inc + ca65 -o lzsa_test.o lzsa_test.s -l lzsa_test.lst + +### + +graphics_level5.inc: level5.lzsa + echo "level5_lzsa: .incbin \"level5.lzsa\"" > graphics_level5.inc + +### + +level5.lzsa: level5.hgr + $(LZSA) -r -f2 level5.hgr level5.lzsa + +level5.hgr: level5.png + $(PNG2HGR) level5.png > level5.hgr + + +clean: + rm -f HELLO LZSA_TEST *~ *.o *.lst level5.lzsa diff --git a/compression/comparison/decompress_fast_v2.s b/compression/comparison/decompress_fast_v2.s new file mode 100644 index 00000000..fb2f24ad --- /dev/null +++ b/compression/comparison/decompress_fast_v2.s @@ -0,0 +1,370 @@ +; note -- modified by Vince Weaver to assemble with ca65 +; in this case, A = page to decompress to +; getsrc_smc+1, getsrc_smc+2 is src location + +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +;NIBCOUNT = $FC ; zero-page location for temp offset + +decompress_lzsa2_fast: + + sta LZSA_DST_HI + + ldy #$00 + sty LZSA_DST_LO + sty NIBCOUNT + +decode_token: + jsr getsrc ; read token byte: XYZ|LL|MMM + pha ; preserve token on stack + + and #$18 ; isolate literals count (LL) + beq no_literals ; skip if no literals to copy + cmp #$18 ; LITERALS_RUN_LEN_V2? + bcc prepare_copy_literals ; if less, count is directly embedded in token + + jsr getnibble ; get extra literals length nibble + ; add nibble to len from token + adc #$02 ; (LITERALS_RUN_LEN_V2) minus carry + cmp #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + bcc prepare_copy_literals_direct ; if less, literals count is complete + + jsr getsrc ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$EE ; overflow? + jmp prepare_copy_literals_direct + +prepare_copy_literals_large: + ; handle 16 bits literals count + ; literals count = directly these 16 bits + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + bcs prepare_copy_literals_high ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +prepare_copy_literals: + lsr ; shift literals count into place + lsr + lsr + +prepare_copy_literals_direct: + tax + bcs prepare_copy_literals_large ; if so, literals count is large + +prepare_copy_literals_high: + txa + beq copy_literals + iny + +copy_literals: + jsr getput ; copy one byte of literals + dex + bne copy_literals + dey + bne copy_literals + +no_literals: + pla ; retrieve token from stack + pha ; preserve token again + asl + bcs repmatch_or_large_offset ; 1YZ: rep-match or 13/16 bit offset + + asl ; 0YZ: 5 or 9 bit offset + bcs offset_9_bit + + ; 00Z: 5 bit offset + + ldx #$FF ; set offset bits 15-8 to 1 + + jsr getcombinedbits ; rotate Z bit into bit 0, read nibble for bits 4-1 + ora #$E0 ; set bits 7-5 to 1 + bne got_offset_lo ; go store low byte of match offset and prepare match + +offset_9_bit: ; 01Z: 9 bit offset + ;;asl ; shift Z (offset bit 8) in place + rol + rol + and #$01 + eor #$FF ; set offset bits 15-9 to 1 + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_large_offset: + asl ; 13 bit offset? + bcs repmatch_or_16bit ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + jsr getcombinedbits ; rotate Z bit into bit 8, read nibble for bits 12-9 + adc #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_16bit: ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + bmi rep_match ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + jsr getsrc ; grab high 8 bits +got_offset_hi: + tax + jsr getsrc ; grab low 8 bits +got_offset_lo: + sta OFFSLO ; store low byte of match offset + stx OFFSHI ; store high byte of match offset + +rep_match: +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression - substract match offset + + sec ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + sbc #$AA + sta copy_match_loop+1 ; store back reference address + lda putdst+2 +OFFSHI = *+1 + sbc #$AA ; high 8 bits + sta copy_match_loop+2 ; store high 8 bits of address + sec + +.else + + ; Forward decompression - add match offset + + clc ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + adc #$AA + sta copy_match_loop+1 ; store back reference address +OFFSHI = *+1 + lda #$AA ; high 8 bits + adc putdst+2 + sta copy_match_loop+2 ; store high 8 bits of address +.endif + + pla ; retrieve token from stack again + and #$07 ; isolate match len (MMM) + adc #$01 ; add MIN_MATCH_SIZE_V2 and carry + cmp #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + bcc prepare_copy_match ; if less, length is directly embedded in token + + jsr getnibble ; get extra match length nibble + ; add nibble to len from token + adc #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + cmp #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + bcc prepare_copy_match ; if less, match length is complete + + jsr getsrc ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$E8 ; overflow? + +prepare_copy_match: + tax + bcc prepare_copy_match_y ; if not, the match length is complete + beq decompression_done ; if EOD code, bail + + ; Handle 16 bits match length + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + +prepare_copy_match_y: + txa + beq copy_match_loop + iny + +copy_match_loop: + lda $AAAA ; get one byte of backreference + jsr putdst ; copy to destination + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- put backreference bytes backward + + lda copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + dec copy_match_loop+1 + +.else + + ; Forward decompression -- put backreference bytes forward + + inc copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + +.endif + + dex + bne copy_match_loop + dey + bne copy_match_loop + jmp decode_token + +.ifdef BACKWARD_DECOMPRESS + +getmatch_adj_hi: + dec copy_match_loop+2 + jmp getmatch_done + +.else + +getmatch_adj_hi: + inc copy_match_loop+2 + jmp getmatch_done +.endif + +getcombinedbits: + eor #$80 + asl + php + + jsr getnibble ; get nibble into bits 0-3 (for offset bits 1-4) + plp ; merge Z bit as the carry bit (for offset bit 0) +combinedbitz: + rol ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +decompression_done: + rts + +getnibble: +NIBBLES = *+1 + lda #$AA + lsr NIBCOUNT + bcc need_nibbles + and #$0F ; isolate low 4 bits of nibble + rts + +need_nibbles: + inc NIBCOUNT + jsr getsrc ; get 2 nibbles + sta NIBBLES + lsr + lsr + lsr + lsr + sec + rts + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- get and put bytes backward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + lda putdst+1 + beq putdst_adj_hi + dec putdst+1 + rts + +putdst_adj_hi: + dec putdst+2 + dec putdst+1 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + pha + lda getsrc+1 + beq getsrc_adj_hi + dec getsrc+1 + pla + rts + +getsrc_adj_hi: + dec getsrc+2 + dec getsrc+1 + pla + rts + +.else + + ; Forward decompression -- get and put bytes forward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + inc putdst+1 + beq putdst_adj_hi + rts + +putdst_adj_hi: + inc putdst+2 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +getsrc_smc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + inc getsrc+1 + beq getsrc_adj_hi + rts + +getsrc_adj_hi: + inc getsrc+2 + rts +.endif + diff --git a/compression/comparison/hardware.inc b/compression/comparison/hardware.inc new file mode 100644 index 00000000..122551dd --- /dev/null +++ b/compression/comparison/hardware.inc @@ -0,0 +1,102 @@ +;; HARDWARE LOCATIONS + +KEYPRESS = $C000 +KEYRESET = $C010 + +;; SOFT SWITCHES +CLR80COL = $C000 ; PAGE0/PAGE1 normal +SET80COL = $C001 ; PAGE0/PAGE1 switches PAGE0 in Aux instead +EIGHTYCOLOFF = $C00C +EIGHTYCOLON = $C00D +VBLANK = $C019 ; IIe *not* RDVBL (VBL low), IIgs opposite +RSTVBL = $C019 ; reset VBLANK interrupt (iic) +RDVBLBAR = $C019 +TBCOLOR = $C022 ; IIgs text foreground / background colors +NEWVIDEO = $C029 ; IIgs graphics modes +SPEAKER = $C030 +CLOCKCTL = $C034 ; bits 0-3 are IIgs border color +RDVBLMSK = $C041 ; read VBL interrupt (bit 7) +SET_GR = $C050 +SET_TEXT = $C051 +FULLGR = $C052 +TEXTGR = $C053 +PAGE0 = $C054 +PAGE1 = $C055 +LORES = $C056 ; Enable LORES graphics +HIRES = $C057 ; Enable HIRES graphics +CLRAN1 = $C05A ; clear annunciator 1 (if IOUDIS off) +DISVBL = $C05A ; disable VBLANK (iic) (if IOUDIS on) +SETAN1 = $C05B ; set annunciator 1 (if IOUDIS off) +ENVBL = $C05B ; enable VBLANK (iic) (if IOUDIS on) +AN3 = $C05E ; Annunciator 3 +PADDLE_BUTTON0 = $C061 +PADDL0 = $C064 +PTRIG = $C070 ; Analog input reset +VBLINT = $C070 ; Analog input reset + reset vblank (iic) +IOUDISON = $C07E ; iic +IOUDISOFF = $C07F ; iic + +;; BASIC ROUTINES + +NORMAL = $F273 + +;; MONITOR ROUTINES + +HLINE = $F819 ;; HLINE Y,$2C at A +VLINE = $F828 ;; VLINE A,$2D at Y +CLRSCR = $F832 ;; Clear low-res screen +CLRTOP = $F836 ;; clear only top of low-res screen +SETCOL = $F864 ;; COLOR=A +ROM_TEXT2COPY = $F962 ;; iigs +TEXT = $FB36 +TABV = $FB5B ;; VTAB to A +ROM_MACHINEID = $FBB3 ;; iigs +BELL = $FBDD ;; ring the bell +BASCALC = $FBC1 ;; +VTAB = $FC22 ;; VTAB to CV +HOME = $FC58 ;; Clear the text screen +WAIT = $FCA8 ;; delay 1/2(26+27A+5A^2) us +CROUT1 = $FD8B +SETINV = $FE80 ;; INVERSE +SETNORM = $FE84 ;; NORMAL +COUT = $FDED ;; output A to screen +COUT1 = $FDF0 ;; output A to screen + + + + + + + +COLOR_BLACK = 0 +COLOR_RED = 1 +COLOR_DARKBLUE = 2 +COLOR_PURPLE = 3 +COLOR_DARKGREEN = 4 +COLOR_GREY = 5 +COLOR_MEDIUMBLUE = 6 +COLOR_LIGHTBLUE = 7 +COLOR_BROWN = 8 +COLOR_ORANGE = 9 +COLOR_GREY2 = 10 +COLOR_PINK = 11 +COLOR_LIGHTGREEN = 12 +COLOR_YELLOW = 13 +COLOR_AQUA = 14 +COLOR_WHITE = 15 + +COLOR_BOTH_BLACK = $00 +COLOR_BOTH_RED = $11 +COLOR_BOTH_DARKBLUE = $22 +COLOR_BOTH_DARKGREEN = $44 +COLOR_BOTH_GREY = $55 +COLOR_BOTH_MEDIUMBLUE = $66 +COLOR_BOTH_LIGHTBLUE = $77 +COLOR_BOTH_BROWN = $88 +COLOR_BOTH_ORANGE = $99 +COLOR_BOTH_PINK = $BB +COLOR_BOTH_LIGHTGREEN = $CC +COLOR_BOTH_YELLOW = $DD +COLOR_BOTH_AQUA = $EE +COLOR_BOTH_WHITE = $FF + diff --git a/compression/comparison/hello.bas b/compression/comparison/hello.bas new file mode 100644 index 00000000..133a44bb --- /dev/null +++ b/compression/comparison/hello.bas @@ -0,0 +1,2 @@ +5 HOME +10 PRINT CHR$(4);"CATALOG" diff --git a/compression/comparison/level5.png b/compression/comparison/level5.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d1e38b271b8ec4e3ac2ac0f6d6b7c67c9740d2 GIT binary patch literal 29227 zcmV(zK<2-RP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+NGUYmfSeDW&b&fHv|Gi$KjykS~Y_je(eoY#>un=hyu6o;&iRhrgfSx2^nn<3E1>=M8^du=7>^rwg?|R|?}_|KUx4uHXK4 zp+6t=ct?>h)}IT#e=hXI-#>&u7wEsoy35<@KR?3QzrWW%o}~SLU;gpbbM3z_^7rpk zW%X9(hfj?A3D3=+&-3R79)HK{>3osSpRX_EW&O!~*ZC^`xf2%T->uVM|GnJN=dGrH z`=Wo@mA}6F+ivym&l>%Gw^ID`lIL%WT>kp|-~Rjx`QPu#S^9tawH?{lULxSF>tYPiMuK)B6-=`{s;99cv;kUmry4c)?JLjLgj6De7 zpGD1Qf&cOA@Bg{44?CD$WmV*5VaM+hBZvRC1kc&%hZko4{;5l->(2#PB0f5EGvQ$e z*tPKFP{LhftRb+FjScGDdVEqG>_EslE^acW=PB$>i+(h3so}gd=E#pPevUC?C-PIC z%HF(BXR}hu$WKiT8S44TDd${r%`LyVm#3tXODVNz6nlMYx@xWkQ`zcUdwW`Hxs_I1 zYj5jsd>@Qve#=|m_P%fLd}im$op*O$(EEtv8ENEelu<_;@8}cpnQ7)(W}R)mv#+?G zh5cA%)z#L!`VJdW+G*!qcHM2iyMKt<_gr84@>jn4weS7&+Zmdite0ycE1s=$dQ}O?%4|N7(1AD zi1`cu*t;wKcGR8w|Jt{!rhnPD`2V_dms|HA?%e;}w|}>5OE)ynj{UUIMZF)iZ`6|e zySXQxy1z57JM(Rb{mtLFFkbJ|FhpEoufQ^~mb-64;0F7rNfp|pAIRYr3W zd5Zb2`FwoQ));N?`G#2|H}av@2v{y5?UA;NQ_9MbRq16!lyJp6f0wc8r~h=$;W>z z5VF4d$bBKA@7Sn5YXf4dY}&+^xojDQ%bTsLWwW>s^2ieHR8f$)>P%0q0q0rRaFnpD z_>2gEKv>@Edxn;&?c==I)rF2dGoxrM(Vi?Z%J8y+`TP0^+&^NL&O5piMdTTCxU9m^|&3Di5LU>Tt zU7tDb#TKnkjKqB}RBnM-y=|8Nc?Oo`!G<9M>3eE^Sh0Ss5#Br}uODxDCM)$JQA5o@ zX~#f?xu5)|9JvzDryQ+{$qfswRq!I#XJ0Lr1`y3yf2k2Vj<9wIiGjAbMZqyqE~90S3PPa$~&L&0SYmI_u?Xm zNMZXH&oNnXR_1!Y-2)$h?1#3nH~R*E@Qb&G{Wg3=M;yOC*#wQ(!w*IZ&zLnmY#2|A z1C1ApFls;qN6c$=Oj=`kS^8GK!kXY2M!=?EfE3rFA3!(U3ZDDU?Q1W)hhYnWcX6AT z2t34`ZG`lV_q|yml=ivb%ti459VvNd#kL`!JE4(ZDU%Nc>d$?#8sY7^_+#kHyH+Y1i(Krv+2o5KVrcmj`;R`b?P1l#S^@0vNoXnZ zw*(iMW=$S8Z*sefC+`n0n0a4fg%*LXGu-WLx0c5H{3w zmyg@pm}%VpX7@0=do`BqVWUC7zAfO?QYtv;cfG@XOlZf)Dk6IL{@FfMJah%SVGkF+ zkwpZ(b$l*%2m=;EeA}vXk z@kFdM;@8}|Z>_6;58}ig$GK-lN%4U@y?xd_?r#Bc3icz{6zafAgwkRb%IN(Urtt~j z9`5*RO4AdE=xgvQ(EWqmY8w{43%<8?Nd;tQiVSeb37;|B{wiLD$9-TwPaju;^M~Ah z*1ST{EpH5W?8hBP1P`aIM6ac@#i|Ab4C90NR^Y%yj~3SZDxH^tAq%Jq<-~yEWMEW7 z`}SZKH`uTd*v_TGzYymjZOI+cxyLm>OcA+}bZ zjqnfU`@;uutBBDK9VVYeFBpq;Wncw9gWb%iTF(thUl9|A%-rb322JcGnnp36y@Zn7 z9&)e&TF2AlQqp@tAThLGh*S5=%GShFa!dGn{1fnu2R%`e@_}E5#sF$3u;Yf}Csx7J zLbKp2GnJC^x94D4FsOU$-wWm+YE(dsycv-cMWG)&PHOBXjuATo^+moO9u37$>{aJ3 zu@~A_tYAbd@i*K+(f_5YMBY1nfLJkN07HEkrd3%IG-H;F=LQ*rXrJ%JKnwusKnbvb z>&tqYmT?p<0;U$K+;OL%p1pU(Yv4rrG#GK+Z2w~rq+>lJWwPp#ZIyL`g{mO2BLnN(ino>-XEO!(fJAhOWd}|~yok(ds<>D#fC~2P zf--=la9|&5n7K(77Z3FPR0IqU;sAi5BH;r)JiH&9ma&1g^}Z{Xmi74VYau(35ulfE z?4m-vEMWGVo2PtGMa%&h1=qH4g1q>CJ9qmad8`<_g1Ro0c0hJ(!d@y)0KzqSXfPH6 zIS~yLRP?#5flLk@fNO9HrDb;Vb&8Z>~iWIM$@2#<$m5H*7X}} z0Z&+eeu|YJ4;BjP1;Tdi=@kSG+Sc83df|6Zndm4xi(%Ef!UI8r+P8)LJn9oBzkct* zlnrs$lqcZv>n5X_EYx&i(bJJ3C*Vf_W!ab>OfZ*^>@VxP<6W?engNHu<9eQe z5$q3A^AyPia0Xw1^kKZ95zD|^Y9%Zll09H;*ejMtT2cyPjBP-1LAAI*(1iRggIWi^ zlo#@xI13Op0PoQQVBv8E+=EWw-B^_X3+SM9#O-C*gyc8s^>iH`=xf%--w|{bjJ~4l zAU^;lKH|kD1F+xK8bT7^1n%21ZRGh{aEVe8+8gC6{xdR`-VY|Vaa#+kjnnDRG_E;d z&(L>P1+0%2;|h4-qa<+J#nV3+koI(KUy=}S@f**<_fT;xVZx?@cA$*F;Q*y*We$w= z(*gtv?ZAr!t{FDuY)PqVLD_FTKkK@6pAi6y?fozvrM%iA9z%?K;*tymVwgT-#(isy8+UB)r^2QEa+NuXrfkDUQgHo~XX!gi)rw96w zIv4^Ofz4OAJPvQ&xHs5GVdcu*KdddkuI~*SS)3v^pxPWbpF?@!D~siVw>IE{K$4Fs z3HXdHLGds>{B`r}NLFZ9H{1bDn=rL6psZbP4#>b-87OIB>dv$IAPaQH$9+5;Jox<{ z#PS1dv=8GC2l_%qV>{&7DB6p825f?6--c+SytosPmr-rN*W~i>DbQ zX9v+VmOj0i1)SJ{4`hcu#$U|-wS4V!RWuuDjcJWY4J;hD27>25pD;H0=(kuTtPu{= z9e?y*Zakcq<${ZVfpDl1lEjVUn!){_#Dm>wkU)$862*#?|Ngfffr@_?tEsUnEvtup z!ntoG0P4z{ZuDG8l{Kj>GNysOM?xbMmc53Dv7Y0}6JTB1&1$`qJNn>Mn}>#DRh%G; zf^R@okvR+oz#HTP#DB^{-!X|_2VUcmfV*I%ZDgwKcA-4d02L8bP;?Lh1d{tNt};=@ z9O3n`h!HYo0EmVA9|IB*co>xDf<=?zfu-z-Aw*GB||z0BDSYgo#pJ^D_wHlY^JB zCW5lvwQMZ>t7pRFqqZ=jCOqsxk^8hTT#79KykdCr73-0bPb1s6@yld1@Q5i`a}|i* ztvbKO{8KG5ULgO6TktjEP>@tF_=K@IJQ`Pu4JGQV8Lodoeo=i^7y;(F@QvUoITx!c z{Q?)KSHc8Oi0`$fJ1|qU1SgC02W@V^$%G>gh|~&VMoeP{pYpuf%b?_=o_k?;xHBL) z4ibI=_Q7(pqd_+fiVM z-;R+RP~=|s zcdX9l1~STkg~f%*W?_}cKR#I0D~K2gk^uBs?G9{xk_`>80C0Vr0532f(vNq;#f@c5 zDk0E#;xKb;IulUshSMY-q*pE?{{ebH!JOKZhS#%hXqY`dCPHJEi z!~=3XjKKuBUhj}qa9w@zs;Ga8!vKFf7@2R3Ru+7uF*q!j(Tf5ULyZFudjSSPAD|ta z0~*N+@G;MVI+qjJS^!Ty0LzvPSG_2m-)c0Eh8p5U* zq~ivg_f!mVPl+l&Kztg;FDlh>0(x(t%`>lMWEk*{q!kPz5TC`$+yJVD-{i^$+0db1RVSF znLcBoFtt;bI$1&70;UT4@W6Rn)&1ZVmvCud0Fj-1;WW!1(z0)tfc--m=LczD`Y%2h zy;MDPV5nUDqJqzVzj%65&zci^l5ol>OT@kQ`~b46eoq{@ z0q)oFySStD`$B&E`x^`OftZnREM8hW#tKr|T<0qoP-G1AyL%UrVMs3A0+tcQEa0sS zz*^uJkQn%UFN*{4;133s4FEGGF%(`LJ{0)-Q8}fN zh38ZN-UHKlJKDqI8X!XNuuM8tEl4!i0L0;eWhsam4y3Uv9mInCU+|v+pAKvbo+}v} z=dl`El8<(7J!IFuH`R$RpH|-uB_2fLhlH(ihX(SQeE{WL+XY0tkk&!^9YA$F!z-;wAwa z4zvRwHB=}JjSfWWf^WV22orE30hp2Z!6KsKsp5%m8IlPf>8ybf@7jyx!s9M_41FXDC#K!KoP&JAx@UkV@134t~P3%o~O@SqRc7>C)meSQ5I zJpRZ*(`dqW9DstA?+ZAx?iIE9Lo9(l-(q86YKRDHM=x%+x9sm?P0?FCJN^m?$MtZk z`-$OAP4f=s+UN`b7pBUdbNf6!kMR~R4weW@gMshoRS?&qFC8Cp0Km&y82OuCU}NmS z+X-yE#5ed)_?EmshFn1$rRj&RljnG>hWg-?;M+WmT}13(g%L8ujRx_Y|^Sfffh;JNf?_=ZKk>V46LPcRF z8Ow#MkS5gzWW}=eh+SI`cY%%yNuP|WaiLiU3vzcvFAN-1ffd7>znk^E)fgD&Q@`Q^ zqLhZ|kku5Pp3O`p(J~z6V#~ozY{}$V4HR zq31F^R4z~*4yBqU25a)WYxJ-|a3_H5R7@@PGQG-8i$bsoT#uW4R5tJ~`@BuB;@H{v zWRXFi?cSk=VbH&^k|y4Gh>AU7Kw@M}!1Gzveuj0vmy# z+`vC>CYXLo*5phOZ%5XFLC7B%LMDe3C<(i!)v0a;vW-1@5fkk4S~!ipAxdofo?tdW zcQ9dy&*bibaTq7r$ju&49>dV~UmyaP{T`H|-2N-bz}$1e`yj{nw|Mt0+ywcuDBx3W zenDf$)I47@_f~c{K!qD>M%(}pQ?Lx>t76Uc>eE>NP|#?e9d+!0blB!w*+H1ZywdSl zvamKSCNa|0tXy~+tP24Dncu`^-*^r@2zL2Jfx}Q_@s&Emjfh;_g@uaEdD8*y8AvNTDcDLgBLOEiT(6fV6&TnEX7xAJHMx_Tur|C$rF3KPoB>MhhZTD>5V1&qyI&ALC&f#)zceheP& zK!PrzaZ%y@70$9H3!u1+bR_OAoJ=432EqYq>~HL1+_OxJxJHO@esg1OCSwTT@>MUu zv&Oi9vflZ*6iX!pbBPpDP;?OAw~`D~1i(kgjNaY4H|YA3I|~kd4~(6i3^5i{E7b^b*+znZ zm?KW~3hN4NG&}Nrjcf3D56J6D*8{g!1rinxOUrZVK0bEx6QHxs&?NJ84KxV`Ww{x)L*XYq7JkHM`4Ar5$e473J;@Q48dR$ip?gJTFVthPkfn{jp?jfKiS(XFL` zY@;U2vJB{8ABX%cqh*CnF{uUxM%%C)(27+=m{TW{>QyY;0itp{fMFbm9()gsiZuZF z#{8=#p&=*56*NYHcQ8zCT7y5pt$l!MaivJTDK8O>MN1F79?Js$f)l{60)9NLA(A+N**Ik`;lA(@|G;)HilMWeZRiznyyh#%cW`Gia zeCDwOX4P)1@{9(uEG0V4}b#!wE zr((b&!q2KkOKRCrvwgs7rT=8Db#S&##Q zdw{JstB*imT@Z-spp^JI7Bf_J_8HRO_GaMIfH2grTBZZ&hK7bB*J2&mLlLIP8-d(L zTOzI%8*UkdF(@4w(AJuk1aT0knQf`_O|A|64CnQ|391m{04l9(SOT}x@zKHZEFeV3 zeGW`k9-YNDAc@n8NGR0L_%Tv&ucj>r_|{<-6ElJ#)Ch$a{1o5`Qy*=WAda~~A`;ep z0-0J;kfzQy9f7J=$2`@@di(k%yBeRQRz`krL(+;v1!-z2kZ%04DrEZDb_gLTz>9?p9;jGmtQqCyNU{_CN<&(i9<+-p$ zND*8FeNWw5U?Yc!(KElb9(;?O={Xv)aPp1InEiNO3ysRD3m?yG79x&ebqRPhn0v!C zLHeinFdYqU&o_O;ye2g|l( z-cUETGJqH#QzLilY~j=%TX=MdG4oq)bDe1QnwDS8`&TDtEpfEW_}zwSF-N@{5brED z4ma0)vk92=hRyC!$!Z44G!Baiv0*=piD%YkO~}^`jp>n*qGh0op+7Wj_3YP9odmz@}SC!Ci%JUIQ?dayO+3ql2c$ zz-B~SdG>`R`wYhvAiPzll4Gz~wJ7{HgYkJ-m9iC}a7Y~G=po?w zAZ5mcLCjdqCjpqmHP)xY9X@`%5P1U#_jg$ZaGPJt7*WjpM3`}-;NET|%8fJ{^oh`i zK0n+z)MM5VMe7vj{mr$mb}~ZkJJD!F2dA+uox};w6QmP{qt;Ie=6@piDRHYyjG7UQ zx-bH;n`>JrKCRCLhV+d0`LO~zSq#auX_ff#ixOi6lC1)T6eWf z`F+-3V!uptL@OXG`-hE!o>~IVV~wwvi@09P+MDq?E{z$! zaf{gT+ZwJ@CxR>^(N#l=QiC8+n*!1{J_u24n4O!8Tp(Ay6rv3RM^miK?FG_d2@W_2 z_F+tJusy}6HfxC3#ZwvH%TyR3V9~xKfdU_I89R%Fpnk(9n}SQpNHXG(t>Kg_yz(v6 z^pQpIrMTYDGB$S2g2?YFecBV#^9I+{_yZ6UJ}@)}rs1xj?E|~gjXz|(7W5o>eSjig zvhl>o;|`y_S>P{FHI87NVn##&T!W5U&1QsUvK^2qx9`KC-VIxZ2BE_X2WcH$!Rp|b zfu$C(q;9YRT|wsM9`Q@3!^f(qlPX5I@tpwZYO4qKJG|ox1KhGw2hZ4DQ1l}iFW$F( z6g-#>MlgRUSjCT^iE7EV#<+Y2WGW^CoMx%#XC~*=XNh57lUSl^tB$0N4XhD*9|>gt z2@O#2WWJtFv7-lc2&1~byby?GqJR(XR(oL&xlbssf%It()!wng!ErEryQ@XW`@gFy zA3?;Qs!Du1#1EkSoG3w$hT^ce_@Ol`oMW1U4i6v6N*3dc{e?wMGfB9*tD$hO4YsJx zBRXOm03EjZ8RWEu$dVaMRYB@mpq00^soPvL+gA2@VY|3P10nOaQKV;!d)8MGJF^$T zFv|E{+j30oJRTj$0w=qX638XyxLLVvW>KEKcToT8`(WlSa9>~^R&$AOjQs5ihCSo2-46t?yo_KMTl?a>m}pY|I{=KZ*N zsrmX70>{~6XILkcjvtD)F_zbayP*6h#%$B!$#JqpAF7L;%aXlQoo@E8M7tS4N5-vf zajk(&35P0xtU%u!sz{q(d9c$8B)80Kr_SdmHQnhY{^X*L*(RRcCf5N;ejUg%hP z^M``~=`Wlol42)DvC1(+OuO}?!9ohlzo(s6TjM>Wkt}Q`Fak$>VIFKj9oD%u)F7vI z8mC#4bp{&Xbjr5%Log5hS}(XXbuFNG)VW*Ze;2q7iR2BnZx}$2&~yA7I*XA)`nZn0 zJ8w0tY5|x0*-oG#hc7HE@5j&3Hw##vLnEa84P`E1v)vXS5QSaJk$Zu>CF_>) znwtOfco=QiZUq|SLBQSnUKTUPcPTC*3vS(nV-8`xb@wtprH;>37pId zJZY`rILsPK|HXEl%BVYujf_!saR+h&xzaAzQyj0w$IPi$@Zr(Ln5fl3D%f~)+4cH> zJnfY8*&3UMP!YcbPQYeNYa`!N%rdMQNmlFR{SG@etYF^gaQhHL;PD&=FSZRMx2<9m zhit1Q>^>-JV3lZH)iyU%ZI0Yll{U1b!m-2wHUwVwwpo!T*avmacC)YuXXHNPf1my`9_zmVcVS$mJFeKf!K4YkmM9;};t_h0_VL9JOQL zSF2=72m*n^%dy#Z_hgIz^RQDYr`uk)2SB&6>fNSqRD>--*&>WMTRXT;Jvd@#gaQqi z@3I(GYBsD|XA$8SfZOLS{nUe5-t`sXG3x!(2pOr)w4yePInCWjMmP z5kkv#0Cu1y&|S3IC>E|ZY->(3xcr)zc#;GMgu?cWnhdO6UDsa3wYGQ#I1WA?A%su@ zK8|e?%tB5c+tyDq-~RkNw^Mwv_4aYDt6=7ADFuchsb~?Oj(th;0!J<^6YH+*5|BR` zs1-0VpoIt6-;xjZU<&DVA;YKtn6SJd$&gu8ve>$4<5*-Z{*@(i>(~xCUe;?Dt>I73YxXf5;%py=DiD(k`l}l z18(n&;puIf41?56sMk{k)IY>m3(+k$i%=$HBALhjbqSntGq1x@Q125faxlSL&>2V% zq%=B10X>DMr&FwpU4SEn(1K0$&D+Kw7CR7y|Pod&2gHXc%7l>>^Kty)s`MU z0P~EFc05;r1Fai+81BNw$^{PVEIRTCH%oKDMNlAIS<}X$Qua%N#du4#Y zQ@>`HS7RA3=qnmF0v@A$;4gg92dv$8@L2lz+A(5?sV@tyvQ-1NWWC0iz%5VsQJ4#y znd~i#i93LxwMXyO1k5bVZ(0QFq9!1#X|w#mHN7_%G=2c8624z7;8Sc~dY;plX58#E zf{l_#>#&`RkK~$1_z*FI}7}=4)C#d?`M@mkLSiKh zFI<|-N*c569w!?BtU-?Uy$vr|Qf@!yVfguPG{1z!1;O5Nv35 zx)irFyI}LRKHbI?R?1GekS$z_>ARX`^QJ{L0&59uz<5T*@FcdYymlQ-`(ChmVhqbQ z9C3wJvJthXxWHhS(q;Yf=Bes(Faug-c%)m;j+I8!EEg&kHbX~F)onaMaGOy>N4OE+ zbYY_UucXH@RR4E><3tdWUq!3MwuVkFOfH)Yw6#c{pNy{?Y^-EpKM4dF^(jQTbdRF0_$s zq|p%N>BW;v+kxVRasDRi4#_OuRp(KlI&j9q+@R-!snPZWea&iCGtQiybfP?+y1a z)9>7co!z%-mgkJCdL3G`zHz*&MRRzQ#}N-0FH`&WcbYC8SH`&F^5E<6JpiIvH2f3d z*-il4Xp>|=>CG^jsL+{yBk970u)=I3yL6pq4nra4>Ug7B6f5W~*g*p!1ME3hx!LE( zg3nj{gMt4AO5aoem|)p_3eM-S=6X8YQ3!N*svEE?lSmdHJ6nN|uz#^8Ax$~DgZw1rt4^$^Epe$h zV5V8)*mIgzeIkA^M=JwYwpBRVS~k0bp#b8;+Wa-0frT^2W+Qf1-*i)6$Y^y!(_}|8 z0QZ@2f_`SX-~!CgvQ^y6Hw@dbr51#n{ae?ZNT`CBtb1^V1v=DhywAm4I+V|5bnDfF zqkF!5%C5u#u7s6uIOz3VrXIBj83A$UKw!dIXKwvz)bOCU&UeCM|HXQ@ok`1beQM_ljo4=eb z;sLR0Y}4bgIP8OK>9B`oixieACPC3RA8htMp;Hhow9rOy@P|2MY-*~{{q9qiimp5h zVdyL{E-ed3{TZ9{K^n77ULm#vVmer7BSD|Vk7hAZz|yco*26~Y2uwx=D-3M;1J|%8 zxNAf<qVpUImU25oP59jPd-(JFa$qJ< za9i6l@82vByHsj+1`LmCfHxSFD)_XBWkA9n8`#p24H|9%;Lgj!QN+OMd&^0Hve<1Q z%b6JgUPV?WydS52YJ%h8ayR&WB3xEgXLBgy!h<^KASlQtSK4BM9b|_a#3Ll3e9@$% z`BTCN5V*OzRrXnCYiB7K;3}tH!Fd-pNTf4m1Z2%(vKW@)p$o57(SsMT;a~;U`Q?r* za0F2YD;c`UzSbFgNoqvFg=s-YvdN%zl^=8QYq z@JlPgZi5-fE_bS%yiM2m>D;lUSGFOUdV1yYG5bpVKAh1~MKo^cj7%VO~{`%O8 z&8XJ>JBAH2Vu{mYJ@NoD7wh1|i^-H^v-jpsQSa`I3K+;R%K{>lci0F6W5i@&G@>PX z*%>XiACj$C9|E=hX|i3E`aNH>I@({Z58W+9K6-fbeN29bk^LnxJP^8hVj32 zvAa+l1OOgX9f$XJXE0`?W68oz%)LON%_gz_^o`TJF-8T{x^20$6X(KS8Y1GZ*}s_p zKQ<15CfXd%ymhcD`fH^(nA#?HSRUZZKK;XS#uKOG9r(?@3i%+5akV1e`4Vf67jh_0 zFaxssofJVJ@V1_{*>{TDwz*~9wi{pXetsi0ZESl_N(%Gb>KdygKvr7;WdM)*ExQ+- z{}M0-aCNAMBQ{aqu*_!t;6b(tuUH_Du|~roj7gmJ=3o@TO3t3fUC!*x>SQZhI;LHJ z=zqXvJ41)tuoXx>EE@KJ`oKDt?}B@CO3i0jIe-8mt#V(->FJQuWucu#FJE28 zW_nlh`>FDF+?Q#EQ&2V&%mbiqIWyndsg2d;lafPVUvw?nNLK7yV);=AP{Ns|l%0Md z=+K<->0%cICmXG`gg2O2vI5N_`P{D0e&=Q+9t#rey)MsworVdfYj$a_=qw=2ua)Kf zkW#1>&LxZKmW6%d?1h=OL}wfMuFwaU$e`F4Y(~Q^8jpJsVc@*Of1E?}OH_meSxRky z9tNluYZdr7cBJI5I?(d#4pvG4`em&U{%)eQ&yy@TaCEK%6Uu!AI9sW;ouCU1tDBbs zIG!`jBiQ|59?y|8LewQ~LS}3ou*B=B(MY>S?)!}RZHJ42A#Ly5hH`D%HoXIniQh6D zYC_&MK4pxG-z~GMuVseK!j~7LWgEN`nZ|pj*nB*<4a+9zKdU>DP6*_B%}+rwkO$lo z_J4&4ldCcw))eJ%^3(TTkR803o$3#yC@e>|CUB|=*i)`$SEh|Y2BGJQRv?-c+ZH=o zS7HYkfawx9JWk5CSsj38+VcSpm($O`wC#@h9#S-;v5oCAcqH^V6~kb)V=x3llNCR`YN^WZlvew&YhpU(;c#{`z?Fe`4VZh{UvE4?9gv4Jb3jBry*|w1YqPyUXtcVF3)GEZ17u3{#0GX0s2p!pTt^x(KA zI$e@c_zKp;xCMUl0mo6hEICC*J}$DX5Zcn@Wsk;k!eTc#z?#{i^I7)f#Rx!R#6>_ADR=J+(98UtO*^qB%V4w3G zp5TPzQ$>suwo%5$gKeMPsrw&BGe4{ZPxwr;B0WhO%Z+K(ubUrn_?^z22Op{-TMh+l z&e3+-4Ib3$!7|mvVH{}KR)zt@cPIPOmW4C+CSf6+gz|Z~z>h%T!}x%>&UX_O4aLUI z;s+Gr*T}NtY_Oc4Q+J9`caCh8hLc)?n;+`kaTD`Q*LJF4cnqgl0TIrO!`2%h9j|3| ztC}4RPRys~`{JfwC+p$VJh0)$UACXB?lTZJ3bY+(KTk&Z#up(3#YR0t|J(8-2dZ3% z>}xg0wj=TJ*o4!PDi1j0K9z-TzZf>9SY}`eq>~><3rnSru>v5C#ajcWv3*@|mHyoSX;f zainChHxP5d0}X3OFN?Nb$1s=A*&jiNX={=?8o33h2B@mG@tzsa*=eWIBJ_<{$_`*v z7A}m} zHoqH1v}-(+V4DXDn@`bGM^Zfqq-I^7Bq?})2NurBBDid4k_N|2{N~{Qyh*UA$*COM z4h)BTEiN;hYXv)RUF-zqblUtQyQ;~IAJ@8lT8R!`3<O;HG0;`9kAc`;WA%(&sZwQ#j+&AYXI0~{LO*BsE>n+ z_y)AqPYFbHw6WpCVP~n+fCj1x@J<%Yg0hT|*4O#BHC`<0f|cT$oE9^!0>ghM%;B5t zqh%kqeellxGW%=>3V3XxOE~=n=r)!jUs;E2B=!4VAd$|~JDwJE*%dWJl@mYaEIKpV zY`H~ArglF^1DcmC&jk5*YyLD~vpC#JkY<>-IG+e;Y-iUIisKbh!xcFh(+O3u3r#8y zS>9`yV={imQ%hgB^BPRFJNOejcRL%{N~2}p6+T$wB8USEZ23qU7_Sko;+%4cwgDR? zBshF*hrmzokB48|`hP=Z&Wl00t$E7%oG`E);Aw%GaY*aq54>O85tb9AZL`T&bR2k` z+d|XBmM|*L?E&UvykLBQ0BhY}=c=S}#d;>t+13}bfnulp|2P~Q2c-_CXQXrK^IU}R z8{GalR_?VlH8|jHmdp2@k=v&a(N^`(V7947Bgq>w;j9~c^Yp}>(I>Z>t(d@I1KMF_ z#VXIs5-cO>N?vp-0g7wIzn}TlcQ8&d={+?Hq(On`@z!|7y5}N zj50RsF%-c=+obd^c1?R24ZZWp!MRZQM8L*o@0O#ElSf6g%Z{35zlE8VozbK-j(MO( zB}Pa9nE16d_LoCwg7fKccIS>*a0@J}Ag1dymM3g}HceSr9!x&GUEr)XYNxnUgG6JG{7hlq0T z)*~;$84jnfd700*tEk~+)^woBWA)*k_*~d01nQ~y9yr09w1#K4kWR;UK zcphLI-NapiZTjb2-d~i&My3|bmY}M}LYxIWP@;J8qL8fr09*DU=X!S3OB(R^N z+}y8Z8!pqZamiMbYW^{4FbB;0zWUZ~@OEfL;|aikEn9D~PVG0hpKp(1yR#FFhC?#a zV}#L?>w4ywImszq-NE|o^gf5R;8M(j0tM|N2oBS=UdA~d2Y=WB|JnwzjE?^*-7)&9 zr>gdj=$#jQ8{g<8mvbYt9fKefjT)w#U2Vo&Dw}> zzYbXHb=ZZ=J0Ia6@My6$20+i;JC6%#Ck%6)m}iRIaEEo8^m5eldoZW7<5y^ZX6$~w z|BmYpnG~|5eO5QFSC_q3u;_C22KK6hPj>KHRAIl}%JS zP*K+k@wfo)7=G@Vslly^(2ppex=jX9Sk4Xf27WZmRYrZ!Y^#+gnq40MmxD@dP;4q}}PGLP=t~&;DB>y18}K$pAoT zWHsCsbhD0@uR5C3quKA zas^f7Bw-;{DJTR8NNL9s#T8)7?`IhPbO<3SFwaRsJ1y@|7XUx@A=Ql|E#( z=aiN6ycrfgzhuWUPVhb+IC{*vOUdUH=F>TOHR@*_*T4^tJ;~|0Rj2Kw6~dVjgDi?$ z49&%24EeR$5+o4>RY=HgQNyZTZ2xRAZ22uTV#5u{ui(Nl!eW{uleytmb>W|IcL-U1 z1NdZxoRfmz%_nGDc*5$9>w@hG{ZOKGgY$HTk>j>levvtDXq{^DxOeBtO6n1%)$+)* zbES&P%_qA(FFOUc3A|gFSa)qOwid%Qs$y4de6}BvNc%&CUE>NrZP#!Cp$=(nBqlm% zxXMd)y?AT8^m z!|HC-lR#$wJ`|AM&L)&)4BkDrzv7dkXpISoCf>h9^z$CXeulTJU!vckROh0YBST9# z0{CUK;O4QZq~PJ@g+ogj*`#R*tDI=iI{iZtsSUqt1X8+u4v;55S4ZwSNqrHCe1|Qy z@as$Ef8Q-NPi@f@Y9+0!mML(9k|Lx4L1e2ujEkdMcQF*sWF#r3z5hi>x?nlA(+Ge>dpL~WeJrglgKDPNv*A>p0I@2U9({@Z!9@H#JxpsH% z77a1>hlCQxmFlOpN7rJn3UNnkJORpHdYh3Tvl}z3*WgzjAj*9Efkk744&_>81Il;h zZpvt0Q0wBT1fh+Gc{YV{<#?TTCquWkgtVcZ(&~^2Kgr|P)2A#C2BW+;GxEm|pZD&) zVA&@`OEDjnn`gWWRgkHc-dQTW>@#f&3mY|W#jxfbdK0>_9J^0kjr!UIk_{)6hlQ$! z`cK@ILcL<*Vq1IZL%n#Og>*H*9 znPqMt8|TLxl^7(Q&I&Z#b$||fshDeD{d=Kim z-4c-n80LO_SIVW_yiR3)-~~IFiNbR_iKBc#D7o80dY&$@TRFZ`XEjSl?G>5Bs33Ee z$_n`5W%#>+eHVXIF>b&kMqyLRsck=>=0ol5l<}9mpLJ-BK8Br2POo{6jiXj~DqHVRu6T6JwPm6jGmXOf-Yqu; z!I~Z^)GfscV3uwa2eTNv@cR3~j$lSQqt>Bftdmr1d z?53W=zMi%l?;epD^stNY<4mAbO!8N@YT4M;_76Xk{kRY$?)-YOEP<)@oQe@Cx2D{F zK|G!C=5>(bXJ+A7@1NH4#>TQH%Dk<_b>u15S&t0rJifs#gvRc`m=a0PT?Tuqf9SqOi z3eJ~?TYq?}Wha#m5nBv_R9?Pn7eFRB*-%ewX!8pt1g(Gczf9~j$fPh~pS2r)yfIQ! zisvM^dSJcZ?U12L@ad}bk@kzMb*~4!7={h>;B3SJxn||ZciWn`uFumk>{3vZ z)#1EteUDa}0Uk=0Um`2)dEG9wj2$Ym0E@MHXXm6E>on<2-;`Aqr#jmIQQ6*b=e-zFbx(2Ox_I>% zjm~pYInJ}}P$tLz%f*)Kn=b|BF7~^IuR(8gTP>u$5B6_6-|!!;iv9KxZb;ET75m7T zBh4dt^btu)x87}Xq{hb$q-yN8RZ0W`X!+Fg)zIFRbN5~oq7JTGtk8ZAm2%gIeLz&C zm60mF&xZXT&~?a*@G8-D&jM}Sk=hud%In*gsZbtA>7jj~WB&WNg*9K5pAYtp1emwp zx{jG$dG}@*R zm)JR%wGc8y$i_P2h3P46Zr$eWnykG=El|h#7T9Apj!0Vk-X%eVQTu-6tCal^$rF57 zXEB7Gz;u}#4T*2nIC zMLE%K5X|5FDFQh$z?U&8I7gEGanISRDxWvQCS$H9GvLwi_01g-N4xUHNqrerX%khu zdrX$b*4Q6x8B8yjckX(**U|C8ua$`8Q)nN;!kb^T0jT4JGpi`nKBiDTeE3kY;zhD8 z>9;hCN@@!PfpyEZJ3S^&#PBRlG36drgKOc*y|Z)Z;tB-k)%zP~;Tw(VJD*Rs#gr(V zSt&Dzb24$Uk9r@V0`#WV@32yV=gI?ZBIx3}``WeXsw+3IQp|ychU!mcrulTG-wqcU z#m_xHlCfcbtY{ls{jmcjcptW9t$$E6OI}D7PkdFohb;R#iXw_YqFAmADcQDe+?Tld zS&)+ZwK4L>)q0ZBoa`Msp$KtQe8J%SSrOo**+~=}Y^!~JTE$Z;Swwwo1)|8mm7Q(j zG{uzp-Ke`^cxz2ih%rrydViqZ`_A!%Zv{$~OJ|i#y6tH)tl?C`3r_ot?)lhDDwfn{RB(>t-O*jYVG-Q!|%4 zvT`_ls58EpN)+FGARzYjxU6AHlC|A%5LdceU~f*B(|s2~OTC(ML{`hxlS@ooQUZfFIoPrzPliGr=+p zs@>f05)v5f|IYYiTg(wKB`&d;#~M#MWSpHUxy3FZ_)3 z$mHu%`qH3(vrIX3VkJnY&O6U&XdKrs(}XaC9$&{vTDzB|@;7BJL@QG01~OXDv&X$K znvO#(nC8TiU)t>gIs9tvH&<+&XA8XrmbK8y)4R!` zCYkJw1a0FyK~0Ork_MHlZd}@8ekMdn+!$ZUBIMy2bKv^e6~!mRqvy8q`VAS22mX|i zdeiJ1>i7reBe>hdTTK;u1aq~sX)T+G-~GTRKKg5}4xQB-QhT!#Fk@eQ%r_x&YQgb-wMl+}t#biS<1%7^4oA^^q z_UDAUNMOnk1Ruf7wIrvneADG7(~tI271EEr0Ko`n4I8jMwV4H{>RELD&7{Z-duYPO z(Gvhkw09sEm;NJlkb!fQin3>yd^ZrEs0?>JqM#rwVw3aSAIa#bY4T)RY zm(Yo#%ZUPNf4%f0PT3Pqc5Wk~zr&cV{V6S|#QKlz<3Tn^4{=FbsDrdCIhgd3VIa}}vML7d{ZZ;vqdDX4~%agJ3V+C=` z#d{aKt2Q-Rut^a{d%L|;+m+7*WHJEgq1JNeysk2GEbryfW3S>c7$hxSvk%Jj`F6cr zIB^~6kjgc24DAFo-nlG=-SvJoM37Ep0O*A*JZVpGLOtIgm!OIsy;XJjph7dLwev;T zRM6;d33imNeME>3s~dBs_9lWSRm2D87jx!b@49;JkrhY$n{#Qmlg!X?vohV>dly0i z`7F*ehnc3Ig*cd(NlRa@+w48pK3{!mT(O%qdP_&H92@K6P%FLK>_?yb=*r+8t%Mc7 z2R-xh4t+RT@mh-1M^3>0522l5NE$ta^FWI=R)=eTq=h^TPG#bFc~^lXEJ&V5EMBWd z)b0CBr!>rWG1pLYTlR5-?Ml0h5OEE(%~U?+GYWlD!5XZI%IOm$1Xfr?tK9LqljP8s zG$5U1g~+?0dvnI2Un9hMO z9@FkjDX;v2Vqwd($NFzdbDp2 zJ#}Ucydl{u^G7y^C5UJOg2%WNmLjbddFj`7teHm-c`5|TyS{W;@tG$aKi+)QnM{lKu>xudV-iAW$2vNEB~t=K`DNZh7W-d%qmop3h!AdjL4zcK4!a zqFw?3E_d4D(={4vX-ZnTI0>VyT`bYUI44(pI!6FNS`O!mvT{Isa9W~m?3`t|cV4w} zbJ|(Ua2ttgfwf!}(YAJKzV2v!Uu^>`Uk59MHMgAXC25=_9>59hf#SqDIXYt{aWdS$ zaV7EFzlue;Ie(jYILL4tYu({gba6*>!iC|&V30D-&I`&ddx=xp-5MjQr=;>H1b$D3 z+t$OwRZ>L6+uK{%8z$`HZX*IgAP^#8s0b7a!drl_KF%H}9LO2V^9$k+3?($y%H7V@ z!_LK-^A{${(#6w5hMOB-&-su3Ik{?S{S)39`=<(cJw$LQR}qLXSj5Rm&`zteo0_s=j8g^3orKHEIsTn{}StOefw4Oo6cW5f_MKX?%%Bc!Tq-}-bzbL zQpv^2^H=xOm1MYowJ&MyVr6G7`Fj%vv$nDVLogtexRnG54imEmq0nNYAWIYkg@#*+ zLSZn-U!c^Tu^uRAEA%fYJh-qO9tVOJMWC%Q2oM^Ku?E4RRxpqyRLl}20TZ=ESczL> z(4wfnKnma4T^*3~nWc z{tabqC8^@#?u5b*r=1hZ1})<1Z1cO~m*A4}chqILp~B#QmE3Ved0_AkGTfSW&Yrk` zRT$Vgq4hmbztn_?!4aaOqHqWd4n=?wV*fHSM7v}0k@yP}0v3k-f%|J%B=KbM!lHh~ zDIVar2cCdO;gQ8}K#asCNiugZcULWoD$KD@P zz|rn^6({HKu$4qv{m}>(<%PEX-4NdIk18u$l(P*QKfnJBsDG5({WpUp21COnB&?wz zF^n|?1Q)kNfDm9&agZ1q1;$9g#nGZxmVZabx?nuKQSNAY8$44y8+-u$X2U7)TS~#d zTYKB0f9V9q&uuUa1c4erVUiHIBpeI^i%NpQ+#>(`6v0riB?5)PfG}V%8U(k3qd_P* z!V)A7MPN{3;#OcQ81(O*{{NyVY5)p{l-%{Y;GX76={YS2UOM!pO_&?G0 zKPK0ue_ffQo$;sZ-uUb8q0esD@z?CMcQg%@@xQF^2$ta&KY#vwe~*Cm>BZw5MHYN1 zxvQE976722`}HLNJk9*|?+v6L>RQUAtH4VHP%7Y&Arb(%BCM_?Z-ASgHS3F8lAtYI z9GeZP{Tg~fIIi>~JAC5PIQa9=K(yN1+xDK1s@P-9S3rX6>kwwvP)Y(K*{v`mywy}EwwbNT zP6pB}6jWo6w~``<#7W6LeF;J+OGSr$KJhY(an>&_Jr=%1I554U5n>i4YsTPzDv7gC zyM#!)Ol+C*Ky&{fZ|j2hJXd^`^cS23;}ew*)dWB3uVpT4zZjSMN#9o<2V><4{lAKtpV;_@IXq=*KU3B3O1aAsQP0H-G(Nj=bI ztwJk(?>q6x>BE9XnKUPiYtg-_Q-FdWSIUDRb5TxAWF<6WelUh((fT`#n_b7nl4Kz& z4*25Gvg0QWCKGM-T&?ow4yzXarh7KOm?3OcfZdWW146cX!xER2J(bKtLw9{A^?rvO z4{WW)gzT;{T}RE}`jz4;|IcFzm|k$%qG7zWBioz|hw;*s3ib&LnIi>f+I*=R(})sg zEzelNJ1G791*!@WiL~(FYqRy2$54}jZ%TRHc{l8obM@RKh~-l%%Px&e>E1^<*~_bq zPJ~QL^vK>F)1uYFb`M*4-X(@m;U#hER#etiG{!zJSYzL~^!~Li2iu~};cM;g21G=r z%!R=(KT$6{rS#^7>C;iMv2kq0O_r80*sHa!`QcL;3pZZxxx@xbo?|vCBRSHh-iJWR3<$e3q1mUCkv)v8JwZx=~NL z{(66`Wn&1K7IO4f*X)pSjP1xq!sy8?AayY87Q0&qHYH<87CZ&`z$E5=y)q^8hl#z# z!;Cr?$-{$Ty}*JX?K}j#c%e4;JB=}1GJg^Qe<|WR%vh4DFVNre@uaHyT8_egh|{A< zsLOYv;gz$JNHwaQ;m#g7c*P9H%+1$KtSrogmR+G2?eRzU6=(&=y7X3ql}b?56+yY^IAS~ zCR7)wM`BcoL^L#u1wl%8vvB=9a=F2;N)2!tcGurOX`Lcnm4BV+{Y)J^EKo)O%Rm-6 zqs^qqoqGoi#kFQX+$mEs*2o=H->o94de~0O9HJX4Tp|6FnQ_Un{X?YdN<_R}ise!e z3Gm8B&m}IxMh;9Mjev$Z>V-OCl7D#5{vKP$isAd4WH7Ujg`$pUai*YdVg`X)6@ftD zB=ug?@N6a_IWQu>j_h805bLKc+-g+ZtW{{i&&NT46b3LSdDvO!u#63|rR%`p$ML{e z1(ePMT9OvNxFwmw;%w%aGT*#@x)TTzxKE-Oo~#@6T+ugWK#iAvw=0jMTugtZzvXtl zNq~l&$50PEntZICXW~a|;B`!MX{MAwn%!R1kr`D<2elIBM{gUwUu{Q6Gu@MPG1suG zD($f6l0%A=*QcC|w&*I=ba*^2V&dAr<@D{uG8{TNLFPO@c!u*FjWUHUTqhXc&* zU4}z>_+uZt29xJ|B(gefWnlVx2+L3X-jvbyVe4l-8t*xS$-k6Vq;ekVZSWs-QrEMO zs(e4n@pk~``#pd#SD2Nty`xQ2u)P(Ll$JldkbY>)rlV5>FisU&e)z#n-c||Il|f)+ zzD+sav9qtE*U+`JMxGTSm|6CT2w3Ddp`bXB5KQfU+lR-~F zWFt`SUdSLMAFacG-qO?@YkCYhA?v2;yl@8wgp+EIkEIPj#R)rAAm4*giu|nx^80E$ zf~Nhdd3N78?R#VFTfQhp?Qsz#*YjMDo|SnGEcfL0u1ro$R|v=4ICLRYlxU1q12^Z* zmSvPUWMBSTUH)=$jAkmFxdvvY-5fNCuB+Enk7aY6w|P{wV(Xr*Xuvb|>WK}(=iqg5 ze?xsM0Xn2pZkK!qPlICV2I==)1eK;XNuMi=G=Y@SHf3QW`pZ0bk~5raJ}gYiQbI?7;9c7(8k%XVtR9I?>=MzhFlAaYU6 zA-{6Km9}ikl<_b`_t5}m0GAhpbcnGQbg1>TH?Mx8IrJ1G3~h;HcDxpJpN?YNFhZS$ zudFchAti#1hM&1l<~N`!&NHD&i{kz{WKn7V0=Wvn_6d2r=nn3QMuR|#vPKZzt7 zI@vWVCY;=f)v-zG8{XZ0@eo+Tqz)$tt?0l&m$t6=pEdVa4>X7qDb`NBHn_HwNW_zJ zYh%W9g5%aCQ5`noG09O#&rmc}^FbS9Y`!(mw7ORD$>P?x_p3BC?K--bVBG!Hbv0*C zJ^{;-HsZXw(y%~b!mvb4% z-4bPg)jqiANN2uE#WSo6G$yNxik!dbsQj6-UYWdd@Z6H%$~hNLzfE-4g+x9O?Z(@` zCaa@G8&$er3yGR@Tdc&tmCa$?%wWw5kZ4|e;6gxC4(f8}2Z#qbjWgte$dgvywTP<) zR%9Sul#EDnr^&huyZa)@jEy$cH<&K>0-aSN!==KLKcin`W~k|JZp$-?uqpX|YWr>q;Durw)NcgUENKm+M-y)n-ZO2?598XZ_?!#ezr2UIw?G;Rp0#Z%!<6@s-gU@)$8u~ffzA0ud&?Xlz@+Lz+V>lWEY7GyE zExlMm=2a-*PevG|*-*jyuN#|SWRJ>B=8cI{Bjq+?GMvqqQ+Aor4P0$0(F;&%*0Q%p zUEI$bHZR=-WzmY}0~!ju%N(CM9n_TBPo#~6$kw@W`f4X--MCmg;wVjhYV<}3Iif?R zET>RncJx%fa`?mc-4!^_c>{NJkUqS2UZ-oUl|>5adbBUC>s4gvMUK zqI@^vSrqxevquT9*7T!+@?SKUuLDfqFu>oJvg&b~enT{7`PPL<_kO*(F!A;_Ep=NR z4I=@`J4&1r?;>$y$mKCec}&n@p+o76fAHIs&+C1q$#xcQc1eo_Tc4*%>Z@Y|t|!d4 z*6ok6_ZJitthSesroN+H4Td3~DBrOR8OI(k3UD;$IRePyn9Iv*Ek+3R`IJMPCjre5 z3U7>!UbFIeHoi`-&z!5gnA>GbtGoZ|PQf(^uMu`}i(>^}7Qk)$#@Nr7EF~g)IntQ< zx=X295zTk=(2BOB{U9`GYOAO{U&|$>BbQVN(RWt*$7ikWU zd8CeZ1MvC8n&^?wD#nm{fwMpKk!qNZb2Rwd;Wz#EE(~Amq!HFbB)EwIdCHRZ4n#hL zBt4ubt`$vVY+qaGKOf(hBVF)ACT9Dv44{bz$O+zX0I@ltBZ()JtAog|Hf?8HE=+4j zZP#U%+CJr{shlsjS<57~XO6&hC()Jlq^qm#X1+Gm%#<@luvJb96T>$dzC|QN#A$P( z2oyCmqM}}S5hq@lPofVOpVYFc`2KU2r|;9;5~e-(b?ed|&%Ch*`WXshbda&1oBhRu7`m{D>)nBY9>`4PtYaM7+|$Kf9XJFWk8)!>V@^ znG2x9|7&1B$MuNK;8(HJm+xzAcq`){rCnP|X+MyZKUKBIvhL*Enq0jrw;oWCiwHfr z(tkQL{^j+gY7#^*ST_w2VA{eY!exINk9+*C=a|XAXrFp-deWb2pexAtft;V%`<;$%mL|tVi7eE z)B1MmAmJzb8SM>%&WjndA^*#o*|aVG{sFNCL7u&vtfVn3jF&Zdg+*cI*>g`xR!{ok z$!tD0SjMk2fHR!GVC>|#isFCZSl@1A{Hl6Tk!OKl_U?~7owZL29mU^E007jLwUw$A HkRksCc{{QQ literal 0 HcmV?d00001 diff --git a/compression/comparison/lzsa_test.s b/compression/comparison/lzsa_test.s new file mode 100644 index 00000000..2d051b08 --- /dev/null +++ b/compression/comparison/lzsa_test.s @@ -0,0 +1,26 @@ +.include "zp.inc" +.include "hardware.inc" + +lzsa_test: + + bit SET_GR + bit PAGE0 + bit HIRES + bit FULLGR + + lda #level5_lzsa + sta getsrc_smc+2 ; LZSA_SRC_HI + + lda #$20 + + jsr decompress_lzsa2_fast + +end: + jmp end + + +.include "graphics_level5.inc" + +.include "decompress_fast_v2.s" diff --git a/compression/comparison/zp.inc b/compression/comparison/zp.inc new file mode 100644 index 00000000..0fd986e0 --- /dev/null +++ b/compression/comparison/zp.inc @@ -0,0 +1,131 @@ +;; Zero Page + +;; LZSA addresses +NIBCOUNT = $00 +WHICH_LOAD = $01 + +;; Zero page monitor routines addresses + +HGR_BITS= $1C +WNDLFT = $20 +WNDWDTH = $21 +WNDTOP = $22 +WNDBTM = $23 +CH = $24 +CV = $25 +GBASL = $26 +GBASH = $27 +BASL = $28 +BASH = $29 +H2 = $2C +X_LEFT = $2C +V2 = $2D +MASK = $2E +COLOR_MASK = $2F +COLOR = $30 + +SEEDLO = $4e +SEEDHI = $4f +XMAX = $50 + + +FRAMEL = $60 +FRAMEH = $61 +;CURSOR_X = $62 +;CURSOR_Y = $63 +XPOS = $64 +YPOS = $65 +LOCATION_STRUCT_L = $66 +LOCATION_STRUCT_H = $67 +IN_SPECIAL = $68 +CURSOR_VISIBLE = $69 +IN_LEFT = $6A +IN_RIGHT = $6B +BTC_L = $6C +BTC_H = $6D + +; ym player +CURRENT_FRAME_L = $70 +CURRENT_FRAME_H = $71 +BASE_FRAME_L = $72 +BASE_FRAME_H = $73 +CURRENT_CHUNK = $74 +TIME_MINUTES = $75 +TIME_SECONDS = $76 +CURSOR_X = $77 +CURSOR_Y = $78 +DISP_PAGE = $79 +DRAW_PAGE = $7A +LEVEL_OVER = $7B + LEVEL_FAIL = 1 + LEVEL_WIN = 2 +DOOR_OPEN = $7C +CHUNK_NEXT_LOAD = $7D +CHUNK_NEXT_PLAY = $7E +LOAD_NEXT_CHUNK = $7F +OVER_LEMMING = $80 +TIMER_COUNT = $81 +UPDATE_POINTER = $82 +NEXT_LEMMING_TO_RELEASE = $83 +SAVED_Y1 = $84 +SAVED_Y2 = $85 +LEMMINGS_OUT = $86 +BUTTON_LOCATION = $87 +WHICH_LEVEL = $88 +LEMMINGS_TO_RELEASE = $89 +CURRENT_LEMMING = $8A +APPLEII_MODEL = $8B +PERCENT_RESCUED_L = $8C +PERCENT_RESCUED_H = $8D +PERCENT_NEEDED = $8E +PERCENT_ADD = $8F + +DOOR_X = $90 ; location of door +DOOR_Y = $91 +INIT_X = $92 ; initial lemming position +INIT_Y = $93 + +CLIMBER_COUNT = $94 +FLOATER_COUNT = $95 +EXPLODER_COUNT = $96 +STOPPER_COUNT = $97 +BUILDER_COUNT = $98 +BASHER_COUNT = $99 +MINER_COUNT = $9A +DIGGER_COUNT = $9B + + +; done game puzzle state + + +WHICH_SLOT = $DA +JS_BUTTON_STATE = $DB +CURRENT_DISK = $DC +JOYSTICK_ENABLED= $DD +SOUND_STATUS = $DE + SOUND_DISABLED = $80 + SOUND_IN_LC = $01 ; $01 sound effects in language card + SOUND_MOCKINGBOARD = $02 ; mockingboard detected + +HGR_COLOR = $E4 +HGR_PAGE = $E6 + + + + +; rest of pt3_player +LOOP = $F4 +MB_VALUE = $F5 +MB_ADDR_L = $F6 +MB_ADDR_H = $F7 +DONE_PLAYING = $F8 +DONE_SONG = $F9 + + +TEMP = $FA +TEMPY = $FB +INL = $FC +INH = $FD +OUTL = $FE +OUTH = $FF +