From 539b3c571ea9525e33de6202290b40840ea3cf12 Mon Sep 17 00:00:00 2001 From: Bobbi Webber-Manners Date: Sun, 20 Oct 2019 20:20:03 -0400 Subject: [PATCH] Added F_RANDREC. F_OPEN now sets RC, seq rec num. --- SOFTCARD80.ASM#040000 | 123 +++++++++++++++++++++++++++++++----------- SOFTCARD80.BIN#041000 | Bin 32771 -> 32771 bytes zapple2.po | Bin 819200 -> 819200 bytes 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/SOFTCARD80.ASM#040000 b/SOFTCARD80.ASM#040000 index 3d1cf18..d95a4a6 100644 --- a/SOFTCARD80.ASM#040000 +++ b/SOFTCARD80.ASM#040000 @@ -15,10 +15,11 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; -; TODO: Populate FCBs properly so PIP etc. work!! Do this in MAKE2PATH. +; TODO: Populate FCBs properly so PIP etc. work!! Do this in PATH2FCB. +; TODO: Note that PIP uses random reads and writes. ; TODO: F_ATTRIB needs to work with FCB with wildcards and leave the FCB at DMAADDR ; TODO: Implement missing system calls: -; - Random read/write (F_READRAND,F_WRITERAND,F_RANDREC,F_WRITEZF) +; - Random read/write (F_READRAND,F_WRITERAND,F_WRITEZF) ; - RS232 (A_READ, A_WRITE) ; - Printer (LWRITE) ; - Other (F_ATTRIB) @@ -96,7 +97,7 @@ FCB1NAM DEFM 'FILENAMEEXT' ; FCB filename and extension FCB1EX DEFB 00H ; FCB extent field FCB1S1 DEFB 00H ; FCB S1 field FCB1S2 DEFB 00H ; FCB S2 field -FCB1RC DEFB 00H ; FCB RC field +FCB1RC DEFB 00H ; FCB RC field (# recs used this extent) FCB1MAP ; Map of blocks in file (overlaps FCB2) ORG 006CH ; Standard addr of 32 byte FCB2 @@ -106,7 +107,7 @@ FCB2NAM DEFM 'FILENAMEEXT' ; FCB filename and extension FCB2EX DEFB 00H ; FCB extent field FCB2S1 DEFB 00H ; FCB S1 field FCB2S2 DEFB 00H ; FCB S2 field -FCB2RC DEFB 00H ; FCB RC field +FCB2RC DEFB 00H ; FCB RC field (# recs used this extent) FCB2MAP ; Map of blocks in file (overlaps buffer) ORG 0080H ; Standard addr of 128 byte File Buffer @@ -521,13 +522,14 @@ B_DRV_ROVEC EQU 1DH ; Return bitmap of read-only drives B_DRV_DPB EQU 1FH ; Get Drive Parameter Block address B_F_USERNUM EQU 20H ; Get/set user number B_F_SIZE EQU 23H ; Compute file size +B_F_RANDREC EQU 24H ; Update random access pointer B_DRV_RESET EQU 25H ; Selectively reset disk drives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG BDOSADDR BDOSINIT LD SP,STCKTOP ; Initialize SP - LD A,0 ; + XOR A ; A=0 LD (IOBYTE),A ; Initialize IOBYTE LD (CURDRV),A ; Drive A:, User 0 LD (FILEBUF),A ; Zero chars in command tail @@ -591,7 +593,7 @@ BDOSVEC DEFW C_TERMCPM ; C=00H DEFW UNIMP ; C=21H (F_READRAND) DEFW UNIMP ; C=22H (F_WRITERAND) DEFW F_SIZE ; C=23H - DEFW UNIMP ; C=24H (F_RANDREC) + DEFW F_RANDREC ; C=24H DEFW DRV_RESET ; C=25H DEFW UNIMP ; C=26H (*nothing* in CP/M 2.2) DEFW UNIMP ; C=27H (*nothing* in CP/M 2.2) @@ -680,7 +682,7 @@ RIS1 LD A,3 ; CMD=3 means peek at keyboard AND 7FH ; Mask high bit LD L,A ; Copy A to L RET ; -RIS2 LD A,0 ; No chars waiting, A=0 +RIS2 XOR A ; No chars waiting, A=0 LD L,A ; Return in L also RET @@ -718,7 +720,7 @@ C_READSTR LD H,D ; HL will be the working pointer INC HL ; ... 3rd byte of the buffer PUSH DE ; Put DE into IX POP IX ; ... - LD A,0 ; Set number of chars read to zero + XOR A ; Set number of chars read to zero LD (IX+1),A ; ... CRSL1 PUSH HL ; Preserve HL CALL C_READ ; Read a character into A @@ -782,7 +784,7 @@ DRV_SET LD A,E ; Prepare to compare disk number AND 0F0H ; Mask out old drive number OR B ; Replace with new drive number LD (CURDRV),A ; Store the requested drive number - LD A,0 ; Return code meaning success + XOR A ; A=0: Return code meaning success JP DSRET ; DSERR LD A,0FFH ; Return code for error DSRET LD L,A ; Return code in L too @@ -802,7 +804,7 @@ _F_OPEN LD IX,PATHBUF ; Destination buffer PUSH DE ; Preserve pointer to FCB ; Work out which IOBUF to allocate for this file - LD A,0 ; Looking for FRN slot with value 0 + XOR A ; Looking for FRN slot with value 0 CALL GETIOADDR ; Returns FRN slot in A, IOBUF in HL CP 0FFH ; Check for error JP Z,FOERR ; If no slots available, error out @@ -823,9 +825,39 @@ _F_OPEN LD IX,PATHBUF ; Destination buffer LD A,(FOMLIN) ; Get ProDOS file reference number LD H,D ; Pointer to FCB ... LD L,E ; ... into HL - LD BC,14 ; Offset to S2 field (reserved field) + LD BC,0EH ; Offset to S2 field (reserved field) ADD HL,BC ; Compute address LD (HL),A ; Store file reference number is S2 field + PUSH HL ; Keep pointer into FCB for later + + ; ProDOS GET_EOF call. Convert bytes to records (ie: divide by 128) + ; Assumes no files > 64K on ProDOS filesystem + LD HL,FO2MLI ; Pass address of 6502 JSR instruction + CALL PRODOS ; Invoke ProDOS MLI (GET_EOF) + LD A,(FO2MLIE2) ; Most significant byte of length + SLA A ; Max file size per extent is 16K: 128*128 + LD B,A ; Stash in B for now + LD A,(FO2MLIE1) ; Least significant byte of length + AND 80H ; Keep most significant bit, mask off others + SRA A ; Move to LSB (shift seven times) + SRA A ; ... + SRA A ; ... + SRA A ; ... + SRA A ; ... + SRA A ; ... + SRA A ; ... + OR B ; Leaves file length in records in A + + ; Store records used + POP HL ; Recover pointer into FCB + INC HL ; Advance to records used field + LD (HL),A ; Set records used + + ; Set sequential record number to zero + LD BC,20H-0FH ; Skip ahead to offset 20H - seq rec num + ADD HL,BC ; ... + XOR A ; Zero the sequential record number + LD (HL),A ; ... ; Store ProDOS FRN in slot FRN1 - FRN4 LD A,(FOIOB) ; Obtain IOBUF idx (1,2,3,4) @@ -836,7 +868,7 @@ _F_OPEN LD IX,PATHBUF ; Destination buffer lD A,(FOMLIN) ; Get ProDOS file reference number LD (HL),A ; Store in FRN slot - LD A,0 ; Success + XOR A ; Success LD L,A ; Copy to L RET ; Done @@ -854,6 +886,16 @@ FOMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: pointer to path in 6502 addr FOMLII DEFW 0000H ; ProDOS PL: pointer to IO buffer FOMLIN DEFB 0 ; ProDOS PL: File reference number +FO2MLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code + DEFB 0D1H ; ProDOS GET_EOF call + DEFW FO2MLIPL+OFFSET ; Pointer to parm list in 6502 addr space + DEFB 60H ; RTS in 6502 code +FO2MLIPL DEFB 2 ; ProDOS PL: Two parameters +FO2MLIN DEFB 0 ; ProDOS PL: File reference number +FO2MLIE1 DEFB 0 ; ProDOS PL: EOF position (LS-byte) +FO2MLIE2 DEFB 0 ; ProDOS PL: EOF position +FO2MLIE3 DEFB 0 ; ProDOS PL: EOF position (MS-byte) + FOIOB DEFB 0 ; Local variable to record IOBUF idx ; Close file @@ -881,17 +923,17 @@ F_CLOSE LD H,D ; Pointer to FCB ... LD B,0 ; ... LD C,A ; ... ADD HL,BC ; ... - LD A,0 ; And zero it + XOR A ; And zero it LD (HL),A ; ... LD H,D ; Pointer to FCB ... LD L,E ; ... into HL LD BC,14 ; Offset to S2 field (reserved field) ADD HL,BC ; Compute address - LD A,0 ; Zero out the S2 field + XOR A ; Zero out the S2 field LD (HL),A ; ... -FCSUCC LD A,0 ; Return zero for error +FCSUCC XOR A ; Return zero for error LD L,A ; Return in L also RET @@ -931,7 +973,7 @@ FSFL1 LD DE,DFCB ; Use this FCB to read the directory LD BC,DIRHDSZ ; ... ADD HL,BC ; ... LD (CDBPTR),HL ; Start out at first file entry - LD A,0 ; Set file count to zero + XOR A ; Set file count to zero LD (CDBCOUNT),A ; ... POP DE ; Get ptr to search FCB back @@ -942,7 +984,7 @@ FSFL1 LD DE,DFCB ; Use this FCB to read the directory JP Z,FSFS1 ; If so, return JP FSFL1 ; Loop -FSFS1 LD A,0 ; Match +FSFS1 XOR A ; Match RET ; FSFS2 LD A,0FFH ; No match RET @@ -970,12 +1012,12 @@ FSNL1 PUSH HL ; Preserve HL LD BC,DIRHDSZ ; ... ADD HL,BC ; ... LD (CDBPTR),HL ; Start out at first file entry - LD A,0 ; Set file count to zero + XOR A ; Set file count to zero LD (CDBCOUNT),A ; ... JP FSNL1 ; Loop -FSNS1 LD A,0 ; Match +FSNS1 XOR A ; Match RET ; FSNS2 LD A,0FFH ; No match RET @@ -1019,6 +1061,8 @@ FDMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: Pointer to path in 6502 addr ; DE is the address of the FCB describing the file from which to read ; Returns error codes in A and L: ; 0 OK, 1 EOF, 9 invalid FCB, 10 media changed, 0FFH h/w error +; TODO: Use Sequential Record field of FCB to determine from where to begin +; reading. Increment it so next read advances to next record. F_READ LD H,D ; Pointer to FCB ... LD L,E ; ... into HL LD BC,14 ; Offset to S2 field (reserved field) @@ -1038,7 +1082,7 @@ F_READ LD H,D ; Pointer to FCB ... JP Z,FRBFCB ; If so, return invalid FCB code (9) CP 0 ; See if there was some other error JP NZ,FRERR ; If so, return code 0FFH (h/w error) - LD A,0 ; Zero for success + XOR A ; Zero for success LD L,A ; Return code in L also RET ; Done FREOF LD A,1 ; EOF return code @@ -1065,6 +1109,8 @@ FRMLITC DEFW 0000H ; ProDOS PL: Number of bytes transferred ; DE is the address of the FCB describing the file to which to write ; Returns error codes in A and L: ; 0 OK, 1 dir full, 2 disk full, 9 invalid FCB, 10 media changed, 0FFH h/w error +; TODO: Use Sequential Record field of FCB to determine from where to begin +; writing. Increment it so next write advances to next record. F_WRITE LD H,D ; Pointer to FCB ... LD L,E ; ... into HL LD BC,14 ; Offset to S2 field (reserved field) @@ -1085,7 +1131,7 @@ F_WRITE LD H,D ; Pointer to FCB ... JP Z,FWDF ; If so, return disk full code (2) CP 0 ; See if there was some other error JP NZ,FWERR ; If so, return code 0FFH (h/w error) - LD A,0 ; Zero for success + XOR A ; Zero for success LD L,A ; Return code in L also RET ; Done FWBFCB LD A,9 ; Invalid FCB return code @@ -1158,7 +1204,7 @@ F_RENAME CALL F_SFIRST ; Search for file, create FCB CALL PRODOS ; Invoke ProDOS MLI CP 0 ; See if there was an error JP NZ,FRNERR ; Handle error - LD A,0 ; Success + XOR A ; Success LD L,A ; Return code in L also RET FRNERR LD A,0FFH ; 0FFH for error @@ -1264,10 +1310,10 @@ F_SIZE CALL FCB2PATH ; Populate PATHLEN and PATH POP IX ; ... into IX LD (IX+21H),L ; Store LSB of recs used in R0 LD (IX+22H),H ; Store LSB of recs used in R1 - LD A,0 ; Store zero in R2 + XOR A ; Store zero in R2 LD (IX+23H),A ; ... - LD A,0 ; Success + XOR A ; Success LD L,A ; Return in L also RET FSERR LD A,0FFH ; File not found @@ -1291,6 +1337,23 @@ FSMLICD DEFW 0000H ; ProDOS PL: Create date FSMLICT DEFW 0000H ; ProDOS PL: Create time +; Update random access pointer +; DE contains the pointer to the FCB to update +; Sets the random access record of the FCB to the value of the last record +; read or written sequentially +F_RANDREC LD H,D ; Copy FCB pointer into HL for arithmetic + LD L,E ; ... + LD BC,20H ; Offset 20H is the sequential rec number + ADD HL,BC ; ... + LD A,(HL) ; Put sequential record number in A + DEC A ; Remember F_READ/F_WRITE increment this + INC HL ; Offset 21H is LSB of random rec number + LD (HL),A ; Write sequential rec # to random LSB + INC HL ; Offset 22H is MSB of random rec number + XOR A ; A=0 + LD (HL),A ; Set MSB of random rec number to 0 + RET + ; Selectively reset disk drives ; DE contains bitmap of drives to reset (bit 0 of E if A:, bit 7 of D is P:) ; Returns A=00H if okay, A=FFH if error @@ -1407,7 +1470,7 @@ PATH2FCB EX DE,HL ; Stash HL in DE so we can use HL here LD HL,(DMAADDR) ; Set all 32 bytes to FCB to zero LD C,0 ; ... - LD A,0 ; ... + XOR A ; ... P2FL1 LD (HL),C ; ... INC HL ; ... INC A ; ... @@ -1417,7 +1480,7 @@ P2FL1 LD (HL),C ; ... LD HL,(DMAADDR) ; Set all filename chars in FCB to space INC HL ; ... LD C,' ' ; ... - LD A,0 ; ... + XOR A ; ... P2FL2 LD (HL),C ; ... INC HL ; ... INC A ; ... @@ -1496,7 +1559,7 @@ RDDIRBLK LD HL,(DMAADDR) ; Save existing DMA address CP 0 ; See if there was an error JP NZ,RDBS1 ; If so, quit with error code - LD A,0 ; Return code for success + XOR A ; Return code for success JP RDBS2 ; Successful return RDBS1 LD A,0FFH ; Error return code @@ -1529,7 +1592,7 @@ MFS1 INC DE ; Advance source pointer LD A,8+3 ; Compare character counter CP C ; ... JP NZ,MFL1 ; Loop if characters left - LD A,0 ; We have a match + XOR A ; We have a match RET ; MFS2 LD A,0FFH ; No match RET @@ -1558,7 +1621,7 @@ CHKDIRENT LD B,0 ; Hardcode drive A: for now. TODO: fix! JP Z,CDES2 ; If so, return CDES1 LD A,0FFH ; No match RET ; -CDES2 LD A,0 ; Match +CDES2 XOR A ; Match RET ; Search a 512 byte block of a ProDOS directory for a matching file entry @@ -1585,7 +1648,7 @@ CDBL1 CALL CHKDIRENT ; Match the file entry at HL JP NZ,CDBL1 ; If not, loop LD A,0FFH ; No match RET ; -CDBS1 LD A,0 ; Match +CDBS1 XOR A ; Match RET ; The following variables are used to preserve state between calls diff --git a/SOFTCARD80.BIN#041000 b/SOFTCARD80.BIN#041000 index 1c9323f46e7e30e07098a282a0601bd893103bb2..5bf5fd15723d66f78125ce71db8df35acb6d7853 100644 GIT binary patch delta 1514 zcmZ8hUu@e%7(XW^#NGODnW8My);O-{nyu?(9wtm%9`>g^OjH{1Frcb^wr`mVRBO_@ zrKJfgLTDOOAxJz@2_%rlH2_Q>Y)os+gxee&J; zyYKtm@Aut(o=SVB(w^E)gmOrN?&y!T&v|S0xV=QWHwT-><_>r_-bopP7YQ{;NFyN; z7m{&dI4+FDxipFf5rpV8)!bm1&9Vmhzkrv9Iyu(!XCzGt!bY?Y5G{%SYsP z`DIy^FUzCySF#~@;VFCT&f&^`xq()6zwC0Q$OV=?k@>{smKL znT&Lv`7g6BwK9=Ry&P;a22Ih62F-C`X@;Ga1@_LGC zLad>SotPVn`%o_n;X3Fj&`p8WGO+TNb%YRn6iyCF9+)Cj*m+{F?N-R*ND4NfR%?Xh8z88S9I=PB?Luw9xB70N?}N4vr^41$19aQ zHY?jye~Bd|rd-u9DZHn|g2+w#`wFMI?&quvigU{9Q-HUuP>~G0>^6e+ZbGVo-O}(@ z6DQoN!lc;fZH4>6#W`ioD9)?aw-o1l7l&eawsG=vJ2+c}i&eq}#Wv)v?B4w&Aw6H# zbFd!+FUX`S_p8;Q5+}rgM;0co!<(vj5DW*A83QBtH!;V7!N+P(K2m8e z09^1)zuI&TD9l$W+)`ur)HbM;(lA#_MxkE#9ZtV4CZ$^UGFEg^ZEi{!Bsai zv+6NVK+F889!e#&Nhe~R(fnght#w-Smvpq3FY&o!njaRjy&9i8qjevr<`VBi)H$K~ zM;)&X`BMwpzM8t+HyT|ER3D!|cbmqq!F7jg4*xE@ogR9SP{WhmhKWnNJ)Sifxe)vz zSHZ;$xSUYfdBy4=XXmpb38eVF-Fp_YEySQD0~*&Gpj6%>YejohE3CqBh!kjD^#O=! zm{3qqR(2QhGp%m%fuR%X&kru2(sFa^vRcIzQr{yc;`7!`A|_#&BjPX&3q%}&VUap< XDDw#kvG>V@{X6;4zCZUJIdR~BR-R~Z delta 1466 zcmZ8heP|nH7=JE_S(2uofzoA8dO5W$7CR3FJ=)vq*00%ppxgeL*1PvUnlYhhDru=} zGR6?WsafV9PXBQD&loy4ierK>mn?I4_0UxEj}W0^E2Fd&!GXnHecrpI8@=QGdfw;x zJ>T!?jOTR56Iur-fP_=$wa{bU(8~$yQ(asiYu-Pz)w}#!S_Iw;ybt&c@B;~cFu@Nc z_Fo9nk+_2on4COTW>2y}MzE_QQxM}&{mKd0_o94ce2LvB1u3kmwMJQa8F(`(0 zlvLQ_sP(g27QvErd)-`<`7mLiDHsb9cAetbkYAHaLLDI|m<9-@gfLL^8_p5nYgU=cz^{A2y#;>^=n$t6{8H>y0Zx`p$x8vjz=f%RT_hpmjq zXw`F%6wB?;#-$L}Ud<7@0m%KKc6#R3T$8pv-K$O8?Pf#^Oxhu{MGI6?5audeVNDI- zMSi=+6(XAWEG1VsKTzggEihqw&D}1uk8*dQ{DTx2OPJjT?4-B2ZWKPC;gM@-ch7bC z5p8LDvGL&Yu~wL9@dFo`v~!oWQ2KqX#aCZQYV;yf{rrK#87jVjg^dM`?~vuAnf*(n zgkLrj4jtdj@hAaX`<^DQ&m)f!acqL9y~_l>|3*Hnrz2cgtottikLLCUdb!w$J<+Kz zl&yZPG>^*)E=cOaR})_!16v0h$ZFIBOo!8HNa)613n0%i+E{jd{)H+p27EvmNZc?_ r>B1l`@9Dx2E@yRNA1*mv%w5qR4_?yWwZ76%T9;;i)pu^q?(F;@Nm)Mp diff --git a/zapple2.po b/zapple2.po index dcff6125100e2277823acf869e25c397e171546b..d85250529a107cc85aba63be062badd7fad153a7 100644 GIT binary patch delta 1590 zcmZ8hU2M}<6uxfKklWHfT7^&q#BsIKKw zbIaPDZ`Pfs+BoZVyuXEqu5Q9NbxKE(SGpGJH#!4D?*Qwe@3!KP6(gjj^4 zVH9meS;UOE9rIHn+b!nU0K#u_+VMZX5-*B(#3^qSMNZl-bxN;EigZ#MmJG?1y2v=W zO?+~vJSg9kAINRWc4e3Hin2$Ml_~L<(hu*tDe(&>ulx#27{-?i_z?4tQCh(5OvI=! z;82Ho!V-MwgmoGcwOZY_dxfLU9WmdW?UYDA#}$f;W>IIBoQpi42V^*M5r8L%tdyR@Mj47VI}?$5zdfuenZa?O}%Vw zQrrscPajjVg;qZGgfu-NPM7!oPNHjD1tQ-Qb~M54Ksb6%Gx_sce#tqiEkeATBzu9F zfMXS6=QQEG<{jkWWZA>;ydSUe&fYD%pvdY&X*lC40;Sw0S8B_0a=DJ%63Zz4k@feZ#&)aV~o}6r<`nPVU+&&a;FIm4x%MV@l7ed;904xNb$yLOccpXi}Cf zO2w(j@(I!(u}YEUzh(mW@!xeRrV-)8N8om`VWnJw7G?EoWws59RVz9_91A0hUd;yU z(OV&8u`8MtNPCL7i{}q1xyG){K1FCn(|jwk>VuzC#oLOo85En56$2&vn`WJcY$lYR zT%}Vb44z@Qw_pFrdQ>%`_=AUR9)l( zh#Du=;IQkn2g=5NYTXC^|LMqj2sfY}nJ2k8_@-3YarF^zTbr3wAC8q8dLO+TLc<#Z zh-)5k(LH1I4;)O*sT=%t*{@Z4)?9f4!R!qRuml$$EAo$y;K{h*(0P6rmKOxKM1&jZ!)(mXPD8iH6Ct zF|wq%?8UJ8p~kQERdtPQNsyCU64X*tj0d(DSQoHyVB5lMdzkGAvwOmH9D+4K0|aX! zSO+N}2CeNgqXNA{n4;Z4?Pathw?7fi3D<>D=lxB1R1ArYVzbyQ_KQQ}S@ELil@3WC zNWV%hxlzeNsZyGiQK3cIt&CQn@vHdr1q!~2GdHQ1i2o9EH>vvPX_5Ba z4KA+m1nXX5;Sa8KB^T?{dFa!*V*CuD=X#y>$c@Y^=5=A(pK27PqYRVD&hn6r5_9Ic zL}!MoB{~vnhI%YEDw^LadH}AN?$-qZhAiLAmtq^|g51gkCV+?3S0ZLdHxEf!I^Apj zsAPCBMa^0>Rz=n$5#XbOlti!DJQgHZ#c8fwl8zD;Kt(x^M7M_Bu#ZA4YP&dXbK!0` zaJS-g6k-67S01*&*e$UKCAv$>Q*}t(K`Fmg$2t!W(Y!iNY`y~B58bx&nL5^xstn2W zI^Rp8JwL0WS7#)8f0%e2s9=wVXDevab0(|JS`ahdl~TPD56GlvLOmKMXwK3!x^vB; zaR|QOOpyAxU<84C4+aC6D9p{B^=#3rg=p?b8{S?eA_9?i0*L`N%L| zJNi!JY1!IkJR_s?&&f`t1lm&@9L5U~!G+t!Q3?rUPi^p=mg$oY&n{z7w%UzjHqY@q zkIhlE&J(*b_0Bz_!U@wC99u}9m(7dfdXB&QIwF*&qbb~wKq&;eEEkNjv^$KB_<-tu z8LNc;tH80G-!*^i$$uk4Q99}l=()i1pMhHolhiNNKi;l^P%Y?o(N*e82Wmj4)0*y% zJ3x~~%`_-ezUJh^3RedcY#rzox2a6ZukugYaF%g{t=yuL16HxIGq3Ejwd>LTZWQ!IJnstZ zt=E_)G~TP?FH;f7|Cc9w)WzXk<-VoEH84=^2F5>VCC<-NWwE!_YL_<^QSB9f;c=6x zleYaF);iWWUQedWKJ0nb27cNM>_4(u<9-0U{kF=l&7zL(cbtMcYli_^>*b_Ji}@K3 zU;a(`$K=LPDC S2NO??kqsxxPLzM