From b60ac006b74b6a44e43da3326d8cf7d10d79ebf1 Mon Sep 17 00:00:00 2001 From: Bobbi Webber-Manners Date: Sun, 13 Oct 2019 22:08:08 -0400 Subject: [PATCH] Fixed stack top. Added CP/M memory definitions. --- SOFTCARD80.ASM#040000 | 2 +- ...CARD80.BIN#040000 => SOFTCARD80.BIN#041000 | Bin 20724 -> 20723 bytes zapple2.po | Bin 819200 -> 819200 bytes 3 files changed, 1 insertion(+), 1 deletion(-) rename SOFTCARD80.BIN#040000 => SOFTCARD80.BIN#041000 (98%) diff --git a/SOFTCARD80.ASM#040000 b/SOFTCARD80.ASM#040000 index 3687512..ca572c0 100644 --- a/SOFTCARD80.ASM#040000 +++ b/SOFTCARD80.ASM#040000 @@ -1 +1 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Z80 code running on Softcard ; This is invoked by the companion SOFTCARD65 6502 code ; Bobbi 2019 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOSADDR EQU 05000H ; Keep below 32K for now (Z80asm bug) STCKTOP EQU 05200H ; Top of Z80 stack SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400) CMD EQU 0F006H ; 6502 $06 AREG EQU 0F007H ; 6502 $07 XREG EQU 0F008H ; 6502 $08 YREG EQU 0F009H ; 6502 $09 ADDR EQU 0F0EBH ; 6502 $EB (LSB) ADDRH EQU 0F0ECH ; 6502 $EC (MSB) ; Addresses of 6502 routines, in 6502 address space COUT EQU 0FDEDH ; Print char in A RDKEY EQU 0FD0CH ; Read key, return in A BELL EQU 0FBE4H ; Sound the bell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Entry point when Z80 cold starts is 0000H ORG 0000H JP BDOSINIT ; Initialize BDOS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BDOS entry point must be at address 0005H for CP/M compatibility ; Function to invoke is passed in C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0005H BDOS JP BDOSIMP ; BDOS code is at top of memory ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The application program proper starts at 0100H ; in order to be compatible with CP/M .COM programs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0100H STCHAR EQU 65 ; First character code 'A' ENDCHAR EQU 91 ; Last character code 'Z' ; Print signon message using C_WRITESTR PROGSTRT LD DE,WELCOME ; Address of string LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call ; Print the alphabet using C_WRITE LD B,STCHAR L1 LD E,B ; Character to print LD C,2 ; C_WRITE call PUSH BC ; Preserve B (and C) CALL BDOS ; CP/M BDOS call POP BC ; Restore B (and C) INC B LD A,ENDCHAR CP B JP Z,S1 JP L1 ; Loop until there is a keystroke waiting using C_STAT S1 LD C,0BH ; C_STAT call CALL BDOS ; CP/M BDOS call CP 0 ; Anything? JR Z,S1 ; If not, loop ; Print a couple of spaces LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call ; Read keyboard and echo to screen C_READ, C_WRITE L2 LD C,1 ; C_READ call CALL BDOS ; CP/M BDOS call LD E,A ; Prepare to echo keystroke LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call JP L2 ; Forever and ever WELCOME DEFM 'ZAPPLE-II TEST STUB' DEFB 13, '$' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Implementation of BDOS ; Function to invoke is passed in C ; C=00H C_TERMCPM System reset ; C=01H C_READ Console read ; C=02H C_WRITE Console write ; C=09H C_WRITESTR Console write string ; C=0BH C_STAT Console status ; C=0CH S_BDOSVER Return version number ; C=0DH DRV_ALLRESET Reset disks ; C=0EH DRV_SET Select disk ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG BDOSADDR BDOSINIT LD SP,STCKTOP ; Initialize SP JP PROGSTRT ; Run user program BDOSIMP LD A,C ; Prepare to check C is in range CP 41 ; Max syscall# for CP/M 2.2 is 40 JP NC,UNIMP ; If >41 then call UNIMP LD HL,BDOSVEC ; Start of vector table SLA C ; Multiply C by 2 LD B,0 ; MSB of BC is zero ADD HL,BC ; Address of vector in HL LD C,(HL) ; Read LSB of address to jump to INC HL ; Read MSB of address to jump to LD H,(HL) ; ... LD L,C ; Address needs to be in HL JP (HL) ; Jump to it! ; Vector table BDOSVEC DEFW C_TERMCPM ; C=00H DEFW C_READ ; C=01H DEFW C_WRITE ; C=02H DEFW UNIMP ; C=03H DEFW UNIMP ; C=04H DEFW UNIMP ; C=05H DEFW UNIMP ; C=06H DEFW UNIMP ; C=07H DEFW UNIMP ; C=08H DEFW C_WRITESTR ; C=09H DEFW UNIMP ; C=0AH DEFW C_STAT ; C=0BH DEFW S_BDOSVER ; C=0CH DEFW DRV_ALLRST ; C=0DH DEFW DRV_SET ; C=0EH DEFW UNIMP ; C=0FH DEFW UNIMP ; C=10H DEFW UNIMP ; C=11H DEFW UNIMP ; C=12H DEFW UNIMP ; C=13H DEFW UNIMP ; C=14H DEFW UNIMP ; C=15H DEFW UNIMP ; C=16H DEFW UNIMP ; C=17H DEFW UNIMP ; C=18H DEFW UNIMP ; C=19H DEFW UNIMP ; C=1AH DEFW UNIMP ; C=1BH DEFW UNIMP ; C=1CH DEFW UNIMP ; C=1DH DEFW UNIMP ; C=1EH DEFW UNIMP ; C=1FH DEFW UNIMP ; C=20H DEFW UNIMP ; C=21H DEFW UNIMP ; C=22H DEFW UNIMP ; C=23H DEFW UNIMP ; C=24H DEFW UNIMP ; C=25H DEFW UNIMP ; C=26H DEFW UNIMP ; C=27H ; Unimplemented BDOS call, just ring the bell UNIMP LD HL,BELL ; We are going to call BELL LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; System reset. Jump to $0000 - doesn't return C_TERMCPM RST 0 ; Quick jump to zero ; Wait for a character from the console, return it in A and L ; Also echoes the char to the console C_READ LD HL,RDKEY ; We are going to call RDKEY LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! LD A,(AREG) ; Grab the return value PUSH AF ; Preserve A (and F) LD HL,COUT ; Echo the character using COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! POP AF ; Restore A (and F) AND 7FH ; Mask high bit LD L,A ; Copy A to L RET ; Return to calling program ; Write character in E to the console ; TODO: Handle tabs, ^S and ^Q C_WRITE LD A,80H ; Set high bit OR E ; ... CP 8AH ; Check for linefeed RET Z ; If LF, don't print it LD (AREG),A ; Pass char to COUT in 6502 A LD HL,COUT ; We are going to call COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; Write ASCII string to console. '$' is the terminator ; DE contains the address of the string C_WRITESTR LD A,(DE) ; Fetch character from string CP 36 ; Is it '$'? RET Z ; If so, we are done PUSH DE ; We are gonna need E LD E,A ; For C_WRITE CALL C_WRITE ; Sent char to console POP DE ; Recover the pointer INC DE ; Advance pointer JP C_WRITESTR ; Handle the next char ; Returns 0 in A and L if no chars waiting, non zero otherwise C_STAT LD A,3 ; CMD=3 means peek at keyboard LD (CMD),A ; ... LD (SOFTCARD),A ; Do it LD A,(AREG) ; Grab the return value LD L,A ; Copy A to L RET ; Return to calling program ; Returns system type in B and H, BDOS version in A and L S_BDOSVER LD B,0 ; System is 8080 CP/M LD H,B ; Also in H LD A,22H ; Pretend to v2.2 LD L,A ; Also in A RET ; Reset disks ; Makes A: drive the default ; TODO: Empty all disk buffers, reset DMA address to BOOT+80H DRV_ALLRST LD A,0 ; 0 means drive A: LD (CURDRV),A ; Store in CURDRV RET ; Select disk ; Disk to select is passed in E (A: is 0, B: is 1 etc.) ; Return 00 for success, 0FFH for error in A and L DRV_SET LD A,E ; Prepare to compare disk number CP 2 ; Support two 'drives' A:,B: JP NC,DSERR ; If >1 LD (CURDRV),A ; Store the requested drive number LD 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 RET ; BDOS data CURDRV DEFB 0 ; Currently logged in drive \ No newline at end of file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Z80 code running on Softcard ; This is invoked by the companion SOFTCARD65 6502 code ; Bobbi 2019 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOSADDR EQU 05000H ; Keep below 32K for now (Z80asm bug) STCKTOP EQU 06000H ; Top of Z80 stack SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400) ; 6502 zero page, in Z80 address space CMD EQU 0F006H ; 6502 $06 AREG EQU 0F007H ; 6502 $07 XREG EQU 0F008H ; 6502 $08 YREG EQU 0F009H ; 6502 $09 ADDR EQU 0F0EBH ; 6502 $EB (LSB) ADDRH EQU 0F0ECH ; 6502 $EC (MSB) ; Addresses of 6502 routines, in 6502 address space COUT EQU 0FDEDH ; Print char in A RDKEY EQU 0FD0CH ; Read key, return in A BELL EQU 0FBE4H ; Sound the bell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Entry point when Z80 cold starts is 0000H ORG 0000H JP BDOSINIT ; Initialize BDOS ; See obsolescence.wix.com/obsolescence/cpm-internals for info ; on low storage usage ... IOBYTE DEFB 0 ; Maps virtual to real devices CURDRV DEFB 0 ; Currently selected drive 0=A: etc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BDOS entry point must be at address 0005H for CP/M compatibility ; Function to invoke is passed in C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOS JP BDOSIMP ; BDOS code is at top of memory RVEC1 DEFW 0000H ; Restart vector 1 RVEC2 DEFW 0000H ; Restart vector 2 RVEC3 DEFW 0000H ; Restart vector 3 RVEC4 DEFW 0000H ; Restart vector 4 RVEC5 DEFW 0000H ; Restart vector 5 RVEC6 DEFW 0000H ; Restart vector 6 RVEC7 DEFW 0000H ; Restart vector 7 ORG 0060H ; Standard addr of 32 byte FCB FCBDRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc) FCBNAM DEFM 'FILENAMEEXT' ; FCB filename and extension FCBEX DEFB 00H ; FCB extent field FCBS1 DEFB 00H ; FCB S1 field FCBS2 DEFB 00H ; FCB S2 field FCBRC DEFB 00H ; FCB RC field FCBMAP DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; File map (16 bytes) ORG 0080H ; Standard addr of 128 byte File Buffer FILEBUF DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The application program proper starts at 0100H ; in order to be compatible with CP/M .COM programs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0100H STCHAR EQU 65 ; First character code 'A' ENDCHAR EQU 91 ; Last character code 'Z' ; Print signon message using C_WRITESTR PROGSTRT LD DE,WELCOME ; Address of string LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call ; Print the alphabet using C_WRITE LD B,STCHAR L1 LD E,B ; Character to print LD C,2 ; C_WRITE call PUSH BC ; Preserve B (and C) CALL BDOS ; CP/M BDOS call POP BC ; Restore B (and C) INC B LD A,ENDCHAR CP B JP Z,S1 JP L1 ; Loop until there is a keystroke waiting using C_STAT S1 LD C,0BH ; C_STAT call CALL BDOS ; CP/M BDOS call CP 0 ; Anything? JR Z,S1 ; If not, loop ; Print a couple of spaces LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call ; Read keyboard and echo to screen C_READ, C_WRITE L2 LD C,1 ; C_READ call CALL BDOS ; CP/M BDOS call LD E,A ; Prepare to echo keystroke LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call JP L2 ; Forever and ever WELCOME DEFM 'ZAPPLE-II TEST STUB' DEFB 13, '$' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Implementation of BDOS ; Function to invoke is passed in C ; C=00H C_TERMCPM System reset ; C=01H C_READ Console read ; C=02H C_WRITE Console write ; C=09H C_WRITESTR Console write string ; C=0BH C_STAT Console status ; C=0CH S_BDOSVER Return version number ; C=0DH DRV_ALLRESET Reset disks ; C=0EH DRV_SET Select disk ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG BDOSADDR BDOSINIT LD SP,STCKTOP ; Initialize SP JP PROGSTRT ; Run user program BDOSIMP LD A,C ; Prepare to check C is in range CP 41 ; Max syscall# for CP/M 2.2 is 40 JP NC,UNIMP ; If >41 then call UNIMP LD HL,BDOSVEC ; Start of vector table SLA C ; Multiply C by 2 LD B,0 ; MSB of BC is zero ADD HL,BC ; Address of vector in HL LD C,(HL) ; Read LSB of address to jump to INC HL ; Read MSB of address to jump to LD H,(HL) ; ... LD L,C ; Address needs to be in HL JP (HL) ; Jump to it! ; Vector table BDOSVEC DEFW C_TERMCPM ; C=00H DEFW C_READ ; C=01H DEFW C_WRITE ; C=02H DEFW UNIMP ; C=03H DEFW UNIMP ; C=04H DEFW UNIMP ; C=05H DEFW UNIMP ; C=06H DEFW UNIMP ; C=07H DEFW UNIMP ; C=08H DEFW C_WRITESTR ; C=09H DEFW UNIMP ; C=0AH DEFW C_STAT ; C=0BH DEFW S_BDOSVER ; C=0CH DEFW DRV_ALLRST ; C=0DH DEFW DRV_SET ; C=0EH DEFW UNIMP ; C=0FH DEFW UNIMP ; C=10H DEFW UNIMP ; C=11H DEFW UNIMP ; C=12H DEFW UNIMP ; C=13H DEFW UNIMP ; C=14H DEFW UNIMP ; C=15H DEFW UNIMP ; C=16H DEFW UNIMP ; C=17H DEFW UNIMP ; C=18H DEFW UNIMP ; C=19H DEFW UNIMP ; C=1AH DEFW UNIMP ; C=1BH DEFW UNIMP ; C=1CH DEFW UNIMP ; C=1DH DEFW UNIMP ; C=1EH DEFW UNIMP ; C=1FH DEFW UNIMP ; C=20H DEFW UNIMP ; C=21H DEFW UNIMP ; C=22H DEFW UNIMP ; C=23H DEFW UNIMP ; C=24H DEFW UNIMP ; C=25H DEFW UNIMP ; C=26H DEFW UNIMP ; C=27H ; Unimplemented BDOS call, just ring the bell UNIMP LD HL,BELL ; We are going to call BELL LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; System reset. Jump to $0000 - doesn't return C_TERMCPM RST 0 ; Quick jump to zero ; Wait for a character from the console, return it in A and L ; Also echoes the char to the console C_READ LD HL,RDKEY ; We are going to call RDKEY LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! LD A,(AREG) ; Grab the return value PUSH AF ; Preserve A (and F) LD HL,COUT ; Echo the character using COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! POP AF ; Restore A (and F) AND 7FH ; Mask high bit LD L,A ; Copy A to L RET ; Return to calling program ; Write character in E to the console ; TODO: Handle tabs, ^S and ^Q C_WRITE LD A,80H ; Set high bit OR E ; ... CP 8AH ; Check for linefeed RET Z ; If LF, don't print it LD (AREG),A ; Pass char to COUT in 6502 A LD HL,COUT ; We are going to call COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; Write ASCII string to console. '$' is the terminator ; DE contains the address of the string C_WRITESTR LD A,(DE) ; Fetch character from string CP 36 ; Is it '$'? RET Z ; If so, we are done PUSH DE ; We are gonna need E LD E,A ; For C_WRITE CALL C_WRITE ; Sent char to console POP DE ; Recover the pointer INC DE ; Advance pointer JP C_WRITESTR ; Handle the next char ; Returns 0 in A and L if no chars waiting, non zero otherwise C_STAT LD A,3 ; CMD=3 means peek at keyboard LD (CMD),A ; ... LD (SOFTCARD),A ; Do it LD A,(AREG) ; Grab the return value LD L,A ; Copy A to L RET ; Return to calling program ; Returns system type in B and H, BDOS version in A and L S_BDOSVER LD B,0 ; System is 8080 CP/M LD H,B ; Also in H LD A,22H ; Pretend to v2.2 LD L,A ; Also in A RET ; Reset disks ; Makes A: drive the default ; TODO: Empty all disk buffers, reset DMA address to BOOT+80H DRV_ALLRST LD A,0 ; 0 means drive A: LD (CURDRV),A ; Store in CURDRV RET ; Select disk ; Disk to select is passed in E (A: is 0, B: is 1 etc.) ; Return 00 for success, 0FFH for error in A and L DRV_SET LD A,E ; Prepare to compare disk number CP 2 ; Support two 'drives' A:,B: JP NC,DSERR ; If >1 LD (CURDRV),A ; Store the requested drive number LD 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 RET \ No newline at end of file diff --git a/SOFTCARD80.BIN#040000 b/SOFTCARD80.BIN#041000 similarity index 98% rename from SOFTCARD80.BIN#040000 rename to SOFTCARD80.BIN#041000 index 94177f88edd9acab6819dc82bb1c6003426d7de3..05013799e06e870844792fd442822c283725447e 100644 GIT binary patch delta 230 zcmeyekn!_EM#=vS42Ky4fCL+mKn4>9z3Lg`*fB OP(A>|?odAeZ^;0@>lrry diff --git a/zapple2.po b/zapple2.po index d56df0aee3b5f3da09c5e5695de2315bf4e3b0c8..3104c6d86107b1d2064a530f60e32627566a0a79 100644 GIT binary patch delta 1554 zcmchXT};zZ6vw-c{XEq*}wL!Qk$iq#nDv%Yf zV?RhTTxkh{kW8vr(?H7#BDQWDsM2XbKgOU8Yf2hxB9=uQ7?Z{WqLQiK{;*!mX6hta z$Eqx(HApEc$(d4!lp;e~83RpMR3Zfs0j|3@dU+tmvK_I`C>vzSXoI+r)nHUo z^${Trx&kULC5t2;l|-zWWl!1aBN|R)QO8M0s?sQi;F(A(U|lr7b#sq3I z5`(e!5KVrG4U*AC^Cz&T3#tyIBn2rk#Mv0XAI4LxVLz;aVvQ6t_E2r4T+@E(nkW|D z4+~SQxd1cTpkS}f97inZO`KziAK^_u`gK803Tl#e9+VU`@GyKs$I#gxPr5qSz7)cTqrh$E_sW!(=Uh_w2FhbV1EEoTs{ClaSG$5~%z_k|d_jm3rre49B^ zQX0!b1_P-8@VJg;O;TiAG&Z!8CPj2eNjROfss4O-3o7dmHCfNgL_@}w#b$sBV?f_WQ3`YTJ3h%TXu6j#J6nPZfA&&q*7SrY|o+>JO57I z?rr`@%GYS@R%#*mk2vEGul|$prf%uhzUawdeV;)7QnNyJ)mpXFXxVX;VHTJ^G8L0) z`!FjWqw^kS+ZRiY(&=Lr5l1=u!Oav$hRU69$>NRc?A6!rk2@APvuX;hTHaZ-5ucj( zc#A^JHM4jPEqh$dyIVh;pT73-F2?$@h7P~5;2~OZnNQ}~Dy!-dyUIE^E8=0R9g}nH Lb{e#>&ISmX9Og*MYHFKCvUsT6oxBXH( zhaT6q37ijDxc@US9A*gk|Nk&s!1Tna96zK59U1vJ&$2SGIY#g?9R)HDve-rMI3>e) zm}|P+RE|fCp3500aWGAu&7-@$U@FH|UuHvwpy_N+IBxKK2sl~&kLl8z0HY59+h0B5 z5L>|l*E$6=?+aC@`5?}>FKE{3YmEdDMguOsl2?~9eFqpGfw|k z%i+MtHQla`Lmo)Q)^S+yasf5xCnaSn7#SE^PQSl^(~*gnYx+Z=7lpWjL!7-s`~x^S zT?0dbo`i&gBr_LKfoDC33D5{&fXF9uIR?1`0|BDQ&A`Ci0~jvW3TCDTMhYqh=Db`H zsPYyl@<1m