From 09b85c08c5f8c9d938d40130e09b69d6abc477b5 Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Fri, 27 Feb 2026 13:57:24 -0600 Subject: [PATCH 1/5] add gsplus project rigging and partial Makefile updated --- TODO.md | 5 + gsplus/lib/2mg.icns | Bin 0 -> 74738 bytes gsplus/lib/525.icns | Bin 0 -> 76671 bytes gsplus/lib/MainMenu.nib | Bin 0 -> 8242 bytes gsplus/lib/kegsicon.icns | Bin 0 -> 70755 bytes gsplus/lib/kegsicon.png | Bin 0 -> 184541 bytes gsplus/lib/make_mac_icon | 57 + gsplus/src/AppDelegate.swift | 365 ++ gsplus/src/Info.plist | 52 + gsplus/src/Kegs-Bridging-Header.h | 5 + gsplus/src/MainView.swift | 412 +++ gsplus/src/Makefile | 112 + gsplus/src/adb.c | 2265 ++++++++++++ gsplus/src/applesingle.c | 363 ++ gsplus/src/clock.c | 397 +++ gsplus/src/comp_swift | 13 + gsplus/src/compile_time.c | 4 + gsplus/src/config.c | 4614 +++++++++++++++++++++++++ gsplus/src/config.h | 36 + gsplus/src/cp_gsplus_libs | 39 + gsplus/src/debugger.c | 2173 ++++++++++++ gsplus/src/defc.h | 364 ++ gsplus/src/defcomm.h | 129 + gsplus/src/defs_instr.h | 759 ++++ gsplus/src/dependency | 31 + gsplus/src/disas.h | 209 ++ gsplus/src/doc.c | 1091 ++++++ gsplus/src/dyna_filt.c | 17 + gsplus/src/dyna_type.c | 330 ++ gsplus/src/dyna_validate.c | 460 +++ gsplus/src/dynapro.c | 2303 ++++++++++++ gsplus/src/engine.h | 84 + gsplus/src/engine_c.c | 959 +++++ gsplus/src/instable.h | 1555 +++++++++ gsplus/src/iwm.c | 3753 ++++++++++++++++++++ gsplus/src/iwm.h | 206 ++ gsplus/src/joystick_driver.c | 469 +++ gsplus/src/kegs.icns | Bin 0 -> 858711 bytes gsplus/src/kegsfont.h | 514 +++ gsplus/src/kegswin.sln | 31 + gsplus/src/kegswin.vcxproj | 169 + gsplus/src/ldvars | 2 + gsplus/src/macsnd_driver.c | 273 ++ gsplus/src/mockingboard.c | 666 ++++ gsplus/src/moremem.c | 2275 ++++++++++++ gsplus/src/op_routs.h | 143 + gsplus/src/paddles.c | 188 + gsplus/src/protos.h | 14 + gsplus/src/protos_base.h | 895 +++++ gsplus/src/protos_macdriver.h | 43 + gsplus/src/protos_macsnd_driver.h | 18 + gsplus/src/protos_pulseaudio_driver.h | 22 + gsplus/src/protos_windriver.h | 55 + gsplus/src/protos_xdriver.h | 49 + gsplus/src/pulseaudio_driver.c | 365 ++ gsplus/src/scc.c | 1390 ++++++++ gsplus/src/scc.h | 111 + gsplus/src/scc_socket_driver.c | 1236 +++++++ gsplus/src/scc_unixdriver.c | 225 ++ gsplus/src/scc_windriver.c | 258 ++ gsplus/src/sim65816.c | 2104 +++++++++++ gsplus/src/size_c.h | 269 ++ gsplus/src/smartport.c | 809 +++++ gsplus/src/sound.c | 1004 ++++++ gsplus/src/sound.h | 101 + gsplus/src/sound_driver.c | 547 +++ gsplus/src/style_check | 147 + gsplus/src/undeflate.c | 1451 ++++++++ gsplus/src/unshk.c | 549 +++ gsplus/src/vars | 9 + gsplus/src/vars_mac | 9 + gsplus/src/vars_mac_x | 10 + gsplus/src/vars_x86linux | 11 + gsplus/src/video.c | 2927 ++++++++++++++++ gsplus/src/voc.c | 221 ++ gsplus/src/win32snd_driver.c | 307 ++ gsplus/src/win_dirent.h | 32 + gsplus/src/windriver.c | 1073 ++++++ gsplus/src/woz.c | 1139 ++++++ gsplus/src/xdriver.c | 1512 ++++++++ 80 files changed, 46804 insertions(+) create mode 100644 TODO.md create mode 100644 gsplus/lib/2mg.icns create mode 100644 gsplus/lib/525.icns create mode 100644 gsplus/lib/MainMenu.nib create mode 100644 gsplus/lib/kegsicon.icns create mode 100644 gsplus/lib/kegsicon.png create mode 100755 gsplus/lib/make_mac_icon create mode 100644 gsplus/src/AppDelegate.swift create mode 100644 gsplus/src/Info.plist create mode 100644 gsplus/src/Kegs-Bridging-Header.h create mode 100644 gsplus/src/MainView.swift create mode 100644 gsplus/src/Makefile create mode 100644 gsplus/src/adb.c create mode 100644 gsplus/src/applesingle.c create mode 100644 gsplus/src/clock.c create mode 100644 gsplus/src/comp_swift create mode 100644 gsplus/src/compile_time.c create mode 100644 gsplus/src/config.c create mode 100644 gsplus/src/config.h create mode 100644 gsplus/src/cp_gsplus_libs create mode 100644 gsplus/src/debugger.c create mode 100644 gsplus/src/defc.h create mode 100644 gsplus/src/defcomm.h create mode 100644 gsplus/src/defs_instr.h create mode 100644 gsplus/src/dependency create mode 100644 gsplus/src/disas.h create mode 100644 gsplus/src/doc.c create mode 100644 gsplus/src/dyna_filt.c create mode 100644 gsplus/src/dyna_type.c create mode 100644 gsplus/src/dyna_validate.c create mode 100644 gsplus/src/dynapro.c create mode 100644 gsplus/src/engine.h create mode 100644 gsplus/src/engine_c.c create mode 100644 gsplus/src/instable.h create mode 100644 gsplus/src/iwm.c create mode 100644 gsplus/src/iwm.h create mode 100644 gsplus/src/joystick_driver.c create mode 100644 gsplus/src/kegs.icns create mode 100644 gsplus/src/kegsfont.h create mode 100644 gsplus/src/kegswin.sln create mode 100644 gsplus/src/kegswin.vcxproj create mode 100644 gsplus/src/ldvars create mode 100644 gsplus/src/macsnd_driver.c create mode 100644 gsplus/src/mockingboard.c create mode 100644 gsplus/src/moremem.c create mode 100644 gsplus/src/op_routs.h create mode 100644 gsplus/src/paddles.c create mode 100644 gsplus/src/protos.h create mode 100644 gsplus/src/protos_base.h create mode 100644 gsplus/src/protos_macdriver.h create mode 100644 gsplus/src/protos_macsnd_driver.h create mode 100644 gsplus/src/protos_pulseaudio_driver.h create mode 100644 gsplus/src/protos_windriver.h create mode 100644 gsplus/src/protos_xdriver.h create mode 100644 gsplus/src/pulseaudio_driver.c create mode 100644 gsplus/src/scc.c create mode 100644 gsplus/src/scc.h create mode 100644 gsplus/src/scc_socket_driver.c create mode 100644 gsplus/src/scc_unixdriver.c create mode 100644 gsplus/src/scc_windriver.c create mode 100644 gsplus/src/sim65816.c create mode 100644 gsplus/src/size_c.h create mode 100644 gsplus/src/smartport.c create mode 100644 gsplus/src/sound.c create mode 100644 gsplus/src/sound.h create mode 100644 gsplus/src/sound_driver.c create mode 100644 gsplus/src/style_check create mode 100644 gsplus/src/undeflate.c create mode 100644 gsplus/src/unshk.c create mode 100644 gsplus/src/vars create mode 100644 gsplus/src/vars_mac create mode 100644 gsplus/src/vars_mac_x create mode 100644 gsplus/src/vars_x86linux create mode 100644 gsplus/src/video.c create mode 100644 gsplus/src/voc.c create mode 100644 gsplus/src/win32snd_driver.c create mode 100644 gsplus/src/win_dirent.h create mode 100644 gsplus/src/windriver.c create mode 100644 gsplus/src/woz.c create mode 100644 gsplus/src/xdriver.c diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..0092fa0 --- /dev/null +++ b/TODO.md @@ -0,0 +1,5 @@ +# TODO + +## Backlog + +- [ ] Remove `#ifdef INCLUDE_RCSID_C` / `#endif` guards from header files and `#define`/`#undef INCLUDE_RCSID_C` from `sim65816.c` and `sound.c` in `gsplus/src/`. The `const char rcsid_` lines were already removed; these are the leftover scaffolding. diff --git a/gsplus/lib/2mg.icns b/gsplus/lib/2mg.icns new file mode 100644 index 0000000000000000000000000000000000000000..1e612e0d21cddc3d735e7928d8f6fc5d06c6be9e GIT binary patch literal 74738 zcmeFa2Xs`|)jxVi9Z7UBHg0he_qg{YUmSNQjvXhCV>@=7I4*IwQ5i`iX>{(43J6mr zO)u(Y`rLbGMh!#RJO+h@rCeaTuM3G21>)_T&ETh7^i?{n@xWj=ZT z%XC5Yqw}APa?COr%yV5zb+BCyePeF2IA>IsRn7Cvb}f?S%BseCue+&EYN)OYH_OeEEbAm8 z$mCIxW;qfF24y+G{BH*&Dbj-T(1EU{dk*d2x4|EXgo9yUq<@k3#P^rJKN;{xT7rr< ze7?hbba-(1sNbhV0*X7-+uC$wsDJQ?$0LP;P2RxC?Oi7Z1_qA={9#21c%|-BC)m#T z%^LzPuROtJudb`Bsj920tgEf6s&-Y?SBcdzIAT==&T_l6%I2`y99DUMV(pi|pZC|ycfWB}mCyQZ?u*YlAAkMLwAwObt)*_xo7PXB`qS%gKVN8@HmlKY z{KdOP?|%O1v{$}*apqIG0ZT<%nm&K_%#vISOVe1ZO;rx3-EOtn>=wJj=5(5zW{cTs zGaHR2ljUy~XQjnyc62JPC7ml)gpDRgrO9Ec+|y{-aQMLC4JMRc=N-*e*MKKf600O&A;T9XtXR($YmOJZgy^7ZhlFAes)fd zMw6YT&dSQk(H0ftXXjVXEwNTP9CNd>@{98Gvb0i>y0InP(tz{n8jU*NtIw>JgL17} zom-Nt$;fLeNUI9?{FNCQIbwcVW_F!nR*lE&bK-uXP*70l@-;e~mF9|qoV>#H$G`YY z5L8LGeD?lFAARuQM<0Il(T9Kg+h^B)_|Km8tA9EF%}4)y|CLYw`TVzE|MU5)|8oAj ze>s2c-<<#9U+(|A^Vie<8UJfP>l^v*zg>D=xa;W$fByWhfB)vYAH4sEH$S-kSI@kH zn4(H57#TT*H$|5J$KHhRNxTK&$y6Nwzl#5p&nYIRe33A+qI%zErD9P{xORG>H>@aOvu>U@;+{RnFmHw_6k4|=tjw8JSCr#x zHhxtRQ9^;dOjo2K&#Z()l||vul0{+31ITjazmgx9A4Sc z5|o09UE_@y=Vcqi!6HMJ$CK%Zh@dcL^@3Iwyur_mO2}ZFoh#X9RjWOX8C4+B7YN9q zRSQ~J@Xw`l6*+9MxiXE#jWuewS6voSBwLBz8(!Pq9F*qF1#cmPIZ~Q2Gg7T~hi2q> z!b-8>oTfkQ+lQB)#uw9Ygti9>1v7iMdM8sRShOny<`qH zn#^X85_HU+JKy3~LL5AoJBd3eOOmW8QV?1 zj%Ef`lEi|kk}P$$HV6CxG)a;!dQp(Xpn#j>`OS#mA3y~v;`g&a489rN28MXKa{+Fn zWN7u8)vH&nSiWrOqJ;|&+l0=Jh%W~-4_Q3#KD-0#{)Xw zc>n5%?DlxQ?&U-MZC<};&oCQc{V_ww{LDKMP*+7Hx7*`!Eg$S_^>{tI2gUwZ`uYZY zydIC+9q=!W1YIt-yJ=bfg>a**ad&^;g$sRs7y1w5w#()5tnBpqU2czi<%J77HmvPD z)Z2Uh-1&38XZL$>5fzsTVJ~w((bRGH=*bhu4tMX~x9`BAg9nc9bbDMiO@7Z3K^W;= zziUKTad1m__l`}QH*VOlsnfq{llm0YMc4znQJ|ED~_zprn1|aEgpsJcHC#S0;}0zS?ocZj@G-{v_ zIdSRsw}dZj-`qC!eO=ZM?y=k2>`sIDryE}Ty5Q}%^R0g|6~6QCyR)2zkDq_>rN2(M z+1l+6vy)X$t#a5aEA18s)Kggri90KyEvTj0=G0qMR;y?c&Bc}kv)N(+L}nH&P?W{2 zx5UvUGqW&roW$$kYVab8LVt4G`1MAJAakAOGu+my#Fqta6d-v2DOchJ^ckkZAy5sh*H5rY} z@S3sBW~ne3jTN5VyJ`%^ip6_(@7%p>=Z@|!qrp&7ZZI|*%=j{tyLaxWsxVe8+_huJ z&K=vg@9H#ERFsz+4YJi#R$g9F=Gwl^F|TZX$M)@8*|tx&?pTStWw;Ym3`X29_iWj^ zAT0Y~YjELpl@`O-CQxLrEeSmA>w zD;=vwgyt0;ZC#5y+7`F9w8@6%h~oF!D{DOkNnLZNp3+L(E=$!0K{(VaD*w_N9R zG|ruA7;)yY{DfR|SPQgTRxm-EuhkadupmD_H(x7?ZPr|tBWPG|q9&LBXETi|H%G6D zS5M0goAOvrtR^Q{Bj&0!xuRxjZk{Fw|C-#d-$+x8dFY4c=He_5|4f5UYF=S(K~7el z3%4}+IeFULtn9q}Je0`K&(Y-N7i4}lEmxbDUuo9netgH2oBkxeed`Th&o);A9~=uA0&OHds_H~n3tC^TKG`>Tk3Nft@aN$YxAq-<>x;C z75l63p6m21QuQ-2H~ZxuUjI33DADHRKKxigehz!;Mcp03UHUgvpR(H%K7|DCzwNrb z{Q4rT<}u-iKfd9~oChAz{6%;x?^)sX+^?pN`^|^%O#VTBzN;uNLwxqmU;OcJ&prE| z=Eba!o`2!RFY~fqefZJGfAv{jUVTY^R$e}I1#0tiH4rq*kITu{T(jZvvcsu$!0l;Svh&G;{2Q}mK}pyVh+>9 zq9X2$ik87$Eraf3va_>v*{UoQWI3Yd;at?4q0P?9hWcjDoilspjOiuCg*s7WOp}#G z(DNE+Y3Vy_dUK@wo0+9`kr0#Pq(IQ3W?2)nK~!lTs%NW9BOzTzR;E3o533@zV%81( zUM@jV&kTq3S=nk^b4WoSE);3VW?7GBYx1g!HJNzI$t+c5y*gWM4a?}D$a1(o8xKFI z?=Q{C$jr*lER}gm~O=^5#1C0=*lw6rvn z7wtD*UT1oG21}1uSGqKrX)Jx>3|CXd+*vd14UMiQS5sqyH9akzr9Gvt7F@Y%$fmTm zuCcKmq1kSCR66Z722k}?T2^+|W&a*&UZJ$L1<|E&x$3G4t1D5+JbSLG;B)@)6_*tM z%pYEP>V@CD^wLYeLhQ%)uYdKMS6+PSrDvac?ias%^S@L-`=1jXxYIN7IXtcW&wreDr7PCIaO-cbc)#zqm#=ug|IW9reE;@8U-ACHU01%} zf9E?_u7CHH?+@PdZ|>iF<@-bTU75ar2Y=}PE7$)&egD3HL;w9(zCZkL?hie1W%`F7 z`Zw_ZP5pl--=X_{e?|Qd-h1t;D-{^JPng_(a$onF@PUKJ&YU^de{uM`Xn8!*TpAha zyG;-ht{pcfA!fpaYj3*gmOCDK@aIoI`@(ClzVguh_dWLfZ+;Q?$@?E8`4IDB60(}Y zwFOl9`~Ryx2^ddh{D0!}zjOU0r~GFANrGX*|1X~cE{*8tll=H*{u!0eCm&Pt=DLrP zgzd|hFWa`ed#7&eT^lxU*|>4@){PrBZ`izk?Ya%?H*VXyW$Ugzdv@>c-m-kj>K($q zc0~>;&0&Q_gfNT5Af3aSStJhM&G?RCc}=zmEz4U$e0lhc{Eu~5#m)=%H1JQznY%@ywXiUDS#ZA5~|kD zP18d#p)zen+Kh0-IIS3Jhxk*7b1<;H6}m)UO{q$s4q?~iHRfhih7_O1V4sHa-%Ohq z2_gS?r=-Y{)-B81NS^^IsL!qk&YIld+_XYDV$AVdr%exqOQ#`g#^e-A`h^vczq~ad z2Je>VWH`XLQk@%gWoK3g^X4^MP#nNZkqnd-dzsl{z!(V?XTdnxkZD2Zb#kaW*ALT} zZpn5>tkY&FVN+VZA2%k{+w#AjEk)XPEpH)pnzAe*QJxI&z7SIF*|S4ofL9c^CcPrU zOYh=lzho?#&F0)Q zyRasN>w7q zAUP@q1jIi_nM9=H`31HHKOco!z}iAM{Q`&?h0_o`1-VjvFad)ocxLc3A4iNjRJeJh zt6B8lL?ZStN9OLtv13P%9PT-O>{!pi1ADu>cW>Rgqr=O5$$l>^T4_Gk74b3u1d@(# z$M+X6Eep4_v^MYj?$V{=Z{xq~k%_v%^#B|>wmjlPB9-X%?fh=|Vh7Fw-c8?5ACA3v zg7SHz{eCE;xu+}Q1!2geruudw^NDhSpm#IM#auisGk;3J??Y&coLCgKFp{Q&LrAU0 zUOcS?z_t&94xd;O!ebKht3JPX*To@9p`!R^WN^j79}S`W@WoRy-Srck$jL6n<@S0( zvCp&X;^1Jrm-*v7o6$RB$hyW23|%~dwCorF^LhNru_X!~!@Q7Sf_vxi08+5bpXf&E zK@4K*25uf4J`sdgP!_5QjVzXtSM_=^zUAIIIMCOEdP(k0DBIsZFd+8F4h$U+Fz;xO zmk3ry+9Z0!W1dM~6z}hg0FlqViOS;L&-%vo4<7Xc8uGgyza;l8@rmwnNY{E?I|lkM z@Zzq`{kp!S{(dBNFAVhfm}d;Ky&j)WIXm!XoIc%q?p!a@!DmjLIe~#F8)2C0I+^zGdT#d}F3LIoY zN()-ca3rGa*njv4J9y)P{oT7ZZQj0Z>$a^sc6F`Vjqzj{w7aSjkHq1yD5#!vl?XQ% zn7dpny7s_|w{6+DsavXDw{>%O&#}WhcXzH{y`~F2uw|ac#`eyx6>JGxc;}*J?d^+} zw6-8`)gEeF7z!_5vUthz)vLSKZ)kVBr7oef)`qNbrNbsx#ycq4jD+S79S%DZ&p72@ zmAH!}vEA(7k<-K-W=*n~O$JN7tHX}FX3AZoSPTm3ZF*}`G?k5_q9xIq_Qx!jtJN-A;sDEL zEwSlPC=LKQGJI-2%x&mswuzP)+>au&+w3}9EXsp~SbPF#A`si^N7?`gS12DIXSF-* zD935Er%(b9MUfqF*hT9&d(}t_abb6w%B@w%jI)aOO;m)+AiZla;{<8)>AJ!>wn~T9 zUOm!cN6uRJU)NuE+n>x1(^EgZ@vgZTmIpG&Ge5ZFuCz)=W&SU2x#x-dZn^zM=CId} zgfXOEc2mj|4(1X0JL-QasRenPpb5>@i zj*I=U3e=QNyeZ}pXJyH_6oykUWO=p3f$}~a8|GOozqXZ49W%pGnLhP7;SP)4TJ_4* z*Mvu$Nbui#SpZfC()YC^?n<7LHdjtlJ&a_1#uPR&_VX(HobhjbBs^SM{d3{P8qxX_ z(iEl+)NHA(#H<0c$!ffPG;>;=8Iz1J2zMhbKd$U=!oyDIqr%J8&|k8%vZ|`mYOgxe zP{}O!n5~ZI;y$fJT0MP|p=eA(x^c=2jekM;s{axmuOjOI{+mDl<@LFa>fQz?Qsr@# zZ^Zw#l36FCe0k+Bg-6~^EvZ9!NA+96l-Uku{W|Z1slwAHTg@()(`2?*zBB5*YUteR zNS`#XvLGcn^_NvOe+7I;$#~VP)j;5=c})1U(pGz>#$mBIKT3LePU(z-d`orKxS3AJ z6RJ`9m34m;9;vc9-;5dcffG|DsvZ{p?zGh$LDt>=MS?1sCEXo!tNEKh#C$zV(|Lo7R?N(>u@9({RELJnT3-rm~kX(~w2dWng9uUg#_|E`egvqKPy(0jzc!&af2HPzTV#p#^Ipiiu{y z#4NE@M{6C-tTJKrfrx>-Q#E?&Q3&w_fD=JlRZo=zWpD|^GeAk;M4QdiF|>lJ31&-W zH^fI^C`?$v8{FSw1IC|I?8>5?Uj+S?Y?Vf2C-5=nYh-7dQbuw%@Ijw1&T zxSg1xQ@QZ)A$D-;p@WAoJb3WXO5i0#7_Onx_y*DAEyj++`}f!5%xs7p*uQW8zM}nO z*IH2&gCqc4-Q%?z0oj~jSafLr{stT+8k!I8+rOViV%Ywe0~@V6(~VTrx!+}@(HIV^ zfRr_l#<-{w-o(vuP_wK2~VO&LI@4mf--SK<(?q6>)i709^7@Y@P*7Ax9 z08Oaq*w?+MhAN@t-tIlSc8lF{d-m>IXJ*FH816At80^P5U;`>wEbQLBtJ(mj(RuHV z*|oEq?T+8MbI;zjW}V@haujyoZBDRjD35I0wtYK)Z{NCY=PDx=zh-_#dAVgr3k@KZ0et!Vwyj&Y*ygjc zaq}Wuw`|+Ch2C4YY~Hcbz{-=$%I1~NpKm)dQd1$$zmW!|S~qUo+#U|g0q^1s>o;uP zya}(38`o`IRw0&+oe9_9THJvL~u2``It$lkNYZ=|XFyLCaL@~mW=a&?- z3YC+369xahH?e7R=c+|Zx~gV0w=d}0)qQyV=14f)+$E$4t)_Cbv~*E(3la%|yD{0r zD+SO~Ltm}F(cRdH$3vxUc}M%wPM@jVj_$;aJd88x3i2^Vp&QGXPSoM3Kr1p4S9RjG zOw?<2#hAJ!8iaN&1{bnv2n9n_82rExn5c=P*;V+S5FK90QRn1hd_(iih^)n+3d>ER zF)w;y)NCrRicv5ch@omU@{*;_E*(({bU9;j#>dBKj7)<`VxlGigE_c_@fVE40Ojp} z`~3%{BW^|*P@f>>=H(G^y!hx^F3RH$M(I#J@$c_^G-Jf4#UKyMopQNwZa!7u^)bAK zW0pIPQ$rM$j(D{+{37Ni&_EtW;>5f}`p(7pl9)$Be{mS1BiepYIO5h~xJZ+qm7ysB z+Dr_sM2GJ*7{klYMA-t2;eGniM;OJ^=H?ZHaB%VAJ>$nreO9B*zHiEe8>eXrc%Jsb z$v51DfjIWJd#}0qu3N6T{?QNebBjkBS+*u)V(gu_3c?fG-0WXril#^c(S8&!T=#WO ze*V*{8{Sdp=6v>;@B+&%8L7cA+}}t2i4{CAjQTQIO*`O!?w=qwA_4#M-zn4?+PSO|J{JVsh_q2sC3AeLc_KXnoGRq&QEx1*9wLnvJ zWM)BD?jJB=mZ|O*uFL-Nm%__m6}%(-FgN@2`-O?`r|0E8n;_i!yZ7GuS-kLvg1qA1 z*)XbSfBYbO=$?n~_^JA5_dIy_b9p&;zE<$%L-#%Wz)$|1m!tjrFK(Q8?X^Gn>04QO zfImB5tp<6y7}L(fbjaL%xKLac6oSc@x8W-X=Bm!ff=9rYN_u9)zcF^neG7sjM~8Rh zB;X3YbQs}Gg}*}Px}Lc-im8KRsWccLRb}TCW#5p)M>wN3VN*H!-kB6Ja25W=T^NI< zEC++8P_rtFA4Z2;aiwV2Yz(Q!WaR)O(3bl+yiI=%Ch4TtiTV;8CM)7mscm(2Z~YAFa{jpYci_QKbc`pPEq�uEWE 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 literal 0 HcmV?d00001 diff --git a/gsplus/lib/525.icns b/gsplus/lib/525.icns new file mode 100644 index 0000000000000000000000000000000000000000..94e5a90d7a8aa4a7f50feaeb45b8d50a5988b561 GIT binary patch literal 76671 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 E8xhrT)&Kwi literal 0 HcmV?d00001 diff --git a/gsplus/lib/kegsicon.icns b/gsplus/lib/kegsicon.icns new file mode 100644 index 0000000000000000000000000000000000000000..72ea1720399aefb0e22a255031b9c00afd333727 GIT binary patch literal 70755 zcmeFZcT`l_@+f>zM;)`0p~;P)DCR)Uxd{zT&RKF&Py{_q&Ph;I5CsG!NRCZtpxX}Q zB%mTu5z|C<92LGg%>3TF-u0aC-nG89-ajAMEvKu_u2Z#Z?~1#V234lY+O*ANK= zL3A&#!0+n!epN8&Kf=92jvYmi^*b&jN1HmkI=VW$sWjTdkrN0K*IZFfA1F#FsLU>& zA|ptAJujD%k)3C8)UJRRiEvHh!go5GyC>b>9<-Q@U2`98GH`W`zOnt>GF->fcpItO zX7u+x5rNqCWg7pfW>Y&_Se?B$Ee$~;Y22yJdTQ3x=XsVNX^1f5OB2v|7;aDNtlV~yV@gR@x$g6CL0p(cjrCg%=FF0Mpl z5_sUMr9;3U-e=eo>Im0;L!+sddPM8Q%^tw^D{H80LKlD^gVv|Y<3=g zTdfd8pt_>6qWtdNI|X@pXODvf!eVkt>nPnc2D48JlYlyFV6cC%e|TVUc=(A5g5*7H zZSJS%L?qS~HO%HC$jzQZ`;Ms_*yqOzkY5%eNIw6;V?x^MR>`$Iu}gOWpYPxaVI@6c z)D51bUrG_=2H)XIX=Nk9-}ui3yuODZnS94&MN~9!fjT4N&Q-9J2> zN>U|QpMN;ZzKBU+^wHy|Gc$AZv$OPYkihVx!6(ztX69z+=ICJvBGFRc+}K)E``|%Y zSuznM5SLR_-$`Tl_YaOJAV|n*KOY3)*u0&#`Y%7{H*A0TFPE#U3fq^zxi0&r@ny){lNI<+7Hov{jVQbASVBMxf+F-eRG|};rq>X%DRky zz5W`3Y*_mE<$peUnYdd|!w7a4j?F5otAD`j3%pkTzj%qjIDfxZ;s0-5DscVxYYk`9 zUv&Sk()aSkcE_!|b|c81bvp!)$>|!px+kZm zI;m8&XG<52)OTJW8iTjCqSDiZMhk# z58@-zV*G9O)K!#~sui_u@<*1_F=!K|oSTGv@$LpC1)B#&(bP1_x@1pC3!B1 zyoDDT5!le}G;barf)1_I$k@ce>tcOR2$55=@cHx180@jyXfC|G2J$qDu7#D0Z(>em zlnTnMr@8RqND8zzJ%9$3cQr;u?2)-U$!fx&O%#AuM;xQIscPukJVd*h zw5~f+jT5h~(Eq-d|DZ__t=-Zkk^b<>U_H&6Tk7G=o2ypW zh<16`ek$$p)mVVGQDNl0Ms75Uo~2nUJtV1=W-BClk4~c(M7z@0H%u*3eK?(b6ep&}e;;^5G4X z0#&aw033kLZ;bA%r$e;%a0&@IuWuXEHT`VX&)pJ$LxaO(qmLMsIq{K!XFLMQ1=O+G z`T2!FS1SOH43CbDJ(`+&JUKDk+cz>bw*VGmb|Ki=9Du{a<6{#Oj~_pIHaj=FxUje| zKM&jIe1J1)?as-@vbM)hpUym+o0|tqJNJBUW`4ofo)C@fdNejZ_4w(tnP*^v76CYm zF8nYzx9DbJ2x8ckm6M%YP*`}kwDkV{it6f`>ZX>C&W?6REtM#QpV>b&G%`9eIyOEw zHaZUfj}8s>_YFKC9*;)&diw{4z$Oh24G)bB4UGgnlb4fI99l0}<0wKOy|)jz1NN1L`a*Vom;nr&kaOvUd3KAKzGZf0fF#GOwi zds`c5sVXTqsu?=n8h(+q8=#c7auH!+Z7nqe_b~snS!J!@E{V$Cc(j-z3(%Oj^P(a` zCr`=4e43KHZ_=NTSq8-iN#BZG)Bnd_o`~>8H*<}4&h}^o>0$! zm9-b3?eTaCvBSn8cxh|5RH>6w!v%OX{-femud*)yv@;Rz-KTGW-)nAdB_ZYP6OPyA zJ|%-s%)y?jBMF}BfSwJ0ub!EqG(Ie|7H=nhqymo@2Yv;q02TE#G{8$)>Kj{1%4F(X zN(z#={k}>ZzX?_(H3e=yLC^!1%p9(rTKxQblaZ)^+56Ae#YF(z9frq?NUNLT@%s8^ z`xI=v+)Pwy6kz+iOMR94Cx zo02T8jg;)qgSIXCRBW^F3>=Erm`z|9QpbFsu#*`}?K$wA3_|)Ugm&Gv$J#L`Au^( zGqbb79@sJ;8W?J==z9F*>GU+L{W;7DnuSH}=WG&<>=+pye>5=(AaD=?LZ00?KRZ3^ zWo-;x?o7?fx_0Z%ox8C6+SCZCh5W#Vy=8O^9p@OEK*zUYYjtFJ zaIn9^{8TjZBZy~sXc!QN(BZA4BO}8jV}LMTEemUvW5cGcy9I?M_Z?T#BM?n(9NiDC ztySC)d-kbmncAE?TU1z7@t~ox0jy+wLtSf!0+?6WJ=yHu-oE~Ufgu?i^A>Ozfb8DPFPi0 z!^k#}AfSkeLclcV&5;h^~@`fe`+RT133>iHZ*u0=OSw zHL_B1GW!mxE8~Pz^{jJ`EPnc>T2p+V@gHC71b6|=K9AdX@Yo3r98O75L*$TwjjjGk zE_vRS)q2_8uvmR~`1tng$O;Jx?pIaYAC23Hx91mZ4+ZTI7#L!=Ha5wsXlm%_svjl# z1)jH+C3b;7-dqIG!TzBeqhlvds;DTd9~ZYclV*S3+;{4il`qxU*3~zJSqddp9VI1| zy*Q$apNWEK%+uvxS0mM7b!Z2-G`E~SA$RPgyn>XV0N+u2rRW}Jjb~gUwjjZ_j?z3; zm2^yf4e#VWlKRosUNL^bZVLtaGq90!==^2~RAy%9(k`Ec6*)ZU|FDz& z`0@0!X$V;6(Ya0Yb2GE^2@%0CESUO7lasKNpFNwNorN_HYj$>KE+#Yp80EQs>&ET! z>e|M}y1M#S%&>KK)0y<1OJ43#$j0HZv2nN;ors#;0w({_qw&$v_5iTv=;-?KM~`47 zBkpv}eb?fn4VyWnQX4vG14Cou z^9#TI_S^gSAO6?RuMzy~5sm|?;LU&MCsbm?Kf=3gcmE@NC`|Al;WH6J90$_ALmWQ- z953~c_kYbk`j7B0xhMY-zH`3Ev-owL*boZ`?n?e^EjtWBvjDND0CJ)BSl$h)CE!+@Gh2>`?ta{BibighTA> zcM)F1OCcQigxQbZ!$^mi4*w4f6zKvy`g?b;uHH965Xso@!^_S88D43Ptdso*1pxSV z)8c;^-wNUOYWhC>z8SJj{k!1LO&RjYx^3Ts|I(1Aw+Awd8bX0&*5>?|*P$fBz+u|6e}em5FO78jlm z_BYgmRF0~=qH`Osi1^-=vfy-~-FXo-Jp_#3IrO&6I{hE*>r}Zhd8CEkh$a7duB6&+`Sv*DIs^M0WFu1yoMG zc$1an9D_(dpmtMRuU*d}JGzKb`QeXO1s9yRaf=1j%q>38Nr(pAgoaict&NL^7q6k* zL1VZWo10rWy88wtl{elGwBE)e5mYz7uy8dA1>D5OW*Y524-c=jiFyl-Wl1p8wy?7I z4^O3a<)882$|HHMWocGF&oeJBPYxti}>-+(v9%KHl7LxnoTq9Dyt))jKxn6t7SbYEaCs4|Z zN5li^bIWh@lW7=kYYPo<`SF@YMj!_lH;?dZ3|dcnN#5md8XCM+@*J)A?b}D4e!iXNhvBxh0WKd8|7kN@z%@0o zwKHean7(1>qHAf_?04dQIw2cbp9bSfTx#pUaQSeG1WyW$r9&VXo95DJ0a4kdZ8V3_ z&0-!sER@Fa@O}&;8A-v$Gd_FD$M?M`4H1k+ z_CyFah2lq55U0NIb)4H+_i0bUJF8_?l!Yg>DdUK*pjvy)Pt=fJm9 zB!Ko}Wu-iq4pAj?pG@1x7_67`ZyP|_8+%zL76eCDENx4mnCdNh7h|r2RRQAlrS)_- z7Tzf+^j0Uj+1feSTU(o1((FT`lKgTLTNvhr?Qg*msZ0Si`yrIZpmlemT_T}s`j!N1 z6H9YzGgh#LOVH)G1KviBp@WOBzpkv5MT^BCQjuNV4{7h1ee81vdPJg`fuV^FgXMbW zLR^YX6!~2H<>AGjK7C%PB*!8;Xi>VahyN?umTE@CYmmxB~pPHK2!iY48tMBOUyAXWH`g|O+ z*Y#X-_LZzF=hd}z^^{c?$eh(iQMp!Nk}&LhBk z3_;Q?mW71e<>K?jMM#P)%+9s@xYz;W4`V}v6JulOBXmjtqA)Cqf+XhB!t?o=+2?Sz zIN#w94o0J?d;|8+rN!!Y&BcAl13B2*t#O`9+A$AnV!YXJ-R)-#a=wUNq6z z#eRszb<+^g&CJiu0~#b`AbUCs*;UFJdvih@vhK;qI6AfQ$&+b_>t@hd9t;kHE-cK# zpz{lhv-6#-ufcxEy~2OPqr+I*6%sn5!{g%+ zF0LONHIhw0B$C3yE|6oBbFSyzEvu=ohk8aEEW?KnX-r6Zv-<`hkd`b!wsCCQzJ2#@ zPF_*zqbE6Ik-rSS;Le?#4o+5an z(OCn`!RTUhBvdr`=SFab>4NT4_M}WCcq_nkAZ)T+?#t(Xg zCf&Ug7~pN7t)Zr*Bq#6OEi855Pejs#^L4GaGYT2lw-Lif`8^_=|Ik z$^<`{oPU*(;0m}WboKQ##3aOpL=PO4IVgMR$PqIl$CUq0@7>RADp$?C44Bf9u`h^JcgS@X%oY6g%25O?Zr!&=o56z zNOl2{S^caMAD^ALBVkR?7v`@frenArJs7UA@DYNV6kb9{*I3`g+B@t{b7jWW=u10s zhb~Z_FD+h8x(=IbQhOKRq9P)vG}QrD&%o5y#nr(hG~s4NLF9$qLPsyOFTH+sJrT2? z$!#>i6%`dz)mM?mOX%ww5)5n{-K6+&hi+t_J0W~3ywjCaj891o!)?XJ6&1b}sh|Mk zs;g;g>Kh%Fh%0Qy;qk)4Ck~(AdvfW`HE%rjye(7=SH!&e*N2Dk654vE79M_h*`@b? zPK)8Y!;c+w!8g8meKYL<;Kp~-FylDgSmz) zDsN&8ycrpi%#6hG;$C6rQ;hIB)||2dRv=QVRPfUz&~^-0R9Heu%W6MfLRa5Fhj0Rq zKOb|wQWJkBWcvYMh8&9L5P`XnBvUb55eXAj6H_U?xEaaB(8&AJ;zY&DO{%K41HT)Ji_XjcFJ!c z&>>|F19)OBLmStyo?Jh$_rK76`qqG}peC|*F@f@Ezcj+cMa9Wt8;+Xl8;Hl^w+Kn<#z_gtq?_Oz zVh|(%rQ=&liwcXqR1EDctt>4qNJh329${qE>b%8X35UY=cVO?UFncc@%4D)0Lg3LE zs;X(CPckGDElkCY&0NpLMIH3flMEkPTn2kz5-kq)K8DF;vu^hFvCrzL6O0J@1_Vn9 zF=w}Pv6BiyOkb84uZgy@P!b1TAwDf`PH2g*aZ_!#^U)-N{Hy zKXvH&%Xfc%Ndp@J_FgTxqO$UVE7Tjbw6%?N4K*b2k|$u{@0au<#5MHvu)~5*I$elE zwtIx8XJ==nUeM6g)>BbMRk*cu@nT{k0tz==B-H~muU)&M?Gfk|1cs-tx370-aCD@> zQAlATmZAbzjqKT+VxeYoprJxn&_Jto7(zY znYx|NeDH8`7D^2rXT4k8{#u@UnjWf@vA!qC@-A(PIlT&~OT_ZD5b313}ux1i0-#XV4=wwffL-vdej|Gpk z(EAxrz|)upn?Ey;&hElI32a1gJ)X7rxL6s(Lp~aTy3ORoPQ03PBllL`?SlNfrDb&u?JcccU`WxP?Y)Ejqm$Smr5*-)TA+4( zb=5UhmC(mi39epQMaBJ+yLU?O78TsSbEn`={>}W{{2MtTB>iJx?)UN!_6_#;4MH~$ zX1<|_GzzwRdNs~Uz(4GAR6<62MtWL0{Da?=%&XaeCC#BueaK`(6>Mm55KQ{OAoL-D z>o)>Zf<4#p$iT=LNV-@h7U9jKGnl<>@GW4Ku)sZmf)l9M2$Y6)Ub_K0h6VsVkbw}-Zh z{OMDtkDhX&9aYpe&ToFa^tS94aF)kp^)$<$Ja+V`$_d#cDi$H}F_8($RqTbKvMU+f z6329-nx>z>E4$4BQwzA=)h7?{-?v}+*f9kaH60@YW<2xrZnVXo*(-2B18tsN{;9k$ z6mXAeXltp8iwR3g!gh_{E4f!%SIfZ4wP5I%&x7%9-22p{I_6)!Dlg28LAK_zq3|Mj zKw9d^;S(qhx-ZP^eUmF$Wxu~)x*KB7vsWvs{rSt+6@@u4Za%F=04H=%df&0*$AoZ# z+N$arn#NYvXOiowwf<(@(%P}@OG~dF6k`EZt~BY>-{tfpr~B-#Z;<(AwG_2J&DmqdU2^Dp%!MSz>f#Kt{* z;J|5x!yfmKVhds;C(#s0!fl`WB8}CvY;BJeubOaQogjm4X46&R~rJ?opWohm=pj z^Qx+E>4xir*hdGhvRd&djpJp#@e`tW%i#qt_0)CD=RA~?Z@FlqVmgC zaA&;tC`Jq&LvbA3g<$N{)^HCUGCr!R2IFdKt7xd`N#pop3-fu%e(q+6gP8CCIEvyB z4lv1?z1Tw>I^YnkxHfU}r{%QONQyGjhdm#?evxY~?MQk5@k5iv1w{C+tx zoSF}=2$0hSH%22I(!40wMs98az0)H6sK7>k0ckCH6@ggX7NNtAsUmyuDQ28!K)x3T zhx>YJt12J3A5|de=<4WbYO4_hZGs}t8~d9I3%FJGzg<~f?Yj>$la7WkXt1}xmmPHU zw2GpJqPm)nmVl0~W#DC0#8pN3^2jol3Fyk-3)Vgjw!#s$vC+{$#p6mUN{UKKx`O=X z*4_~b)?t@D1e3?!{`Pg{ODDFIB(VGYhM{6O9;U9KucfZ6rbZCJ$=SMv#yXp-3m9Zh zyn6G~*VS@wu=a)qdeK-K)Ai^nc?AWi(P}CP;)M1~NlS|hxtnIthleLp#)M z#MsDiFQbdvLmwD>G6!}UjQtENJu3Vh*io){u(&h(^+i@|XSFE}nE<8+vPEUy5iS!kEQimzbuw_=#!=Fk%&L;ZpF z7*-GkV?X(18hTE_f50*U81f91ae?}?Sr$1sBmgjTiwo}-S3qAyb$v559W#39Y$hFg z9QsGVJU@o$exe2S_5pu?gG2|!q~v;zrUtZ8psBUKuBN=cwz{&iw&uYD$n=zzmz5+3 zc$w~uK{iQ1vjy1iv8eG4kHFa(kDA%B3l0U?W^{yO zLLLJ9b(?nX;^Gz%l0BuWFCL4WS*sBLxEa|hd%mEmx~8rkI;mP(A3kI*y#%}Q>h+tq z|N8Uh)%-cx9qTv{Wc%@G8Z5T|;OG3IJsSZ3WX!isx!=Fj;n?o=82+2@qkIFo;ViS| zzZt*!qT;Uq2ERF0gY&<^?@Z9){%`PqNhR90Qk}Wss60q_C1JfJ0A64;cq=g{%`RYzduFa zKvuBlf$_g>BL4>E+lxY<1OFX9$Y<*@^gkZI$X6EGdi3(lw^YA>RpoyO*>dDR$6xU~ zjBGg+@gI-B>VE|JQTF2W_n+Y3AOS~^&9dQB|Muy3FWZ6u|KNpRzKig0fD#OUU+~z! zef-_ayMVt@*qZrWgl_?3MSil#`kluczWw;SS3UWz(s1TSFzdS*-vYX>kc8nh0H1E{ z)7O7~3;I7_U4i`s;SeNV{rP_#D)>Jm{{3m5y9|OLg2eQH#QNXB`{D9y`2Wyk^uLke zuLn`8Yxw`r6!Z;#Rg4DUuTzmM__q$b4x&Xf`2D!%#j`xiCI`k+4cS8KkH@(rXaK?hmSNBri{$u|h! zT&yt5B9su09mhPr`S`mR9d%jGpg$XS%6%8*UjScQDl+U2!+{QD-TJ!mm+wCO@#klV zGQRh-+LxNY(jSmY}>hOH>akZHPjL ziwYi+J$#a2?r3Fg<>>Dpcs@QhE+OH1;oZB1w+bsOp%J2^9jk;;dl(E>AM`Kx^`Qgn zp}Nyg?n8Rf{@vJT*vF8;=^wzdDggg+Xl!y~YWneXi*q)@&G7@m8(8q50-E-rSE2(e zM8Tm3l(~ULW3p&y*S_v}E{qzqTfi~8Zp@(v&k>R?9Z^SFqn`p8iVr*kX@Xh z#>IPWxq@sx(cawL)Z7RMbedbyHno?x?^9 z##DC?l!dz?lhjUyt^l9}FDT71Q5p{r?df4a{0BADQ7D~Gb|*w9`fz0-TQ%Y6Ph(R< z1I8Gc@?%R2utn*hwzr`jXeXZ1)Y=I(uC@*;R_DSRT7anz=w$|4KoYuEpooRiwqX1~ zgEMx*2<_p68W;@)!@Z zb-?6xcDA*`DL71^P%i6$W>HK*+S|!gq&=#W2g+tZz6Ztz&ahd;*rUP3?V&N)?0z6N z&^t1@#7K&c4>*#ADCxI1Ha9k7Z8MnIV7j1b2C9Iq(0l_7wF1jnWeviZw)T!z3Oog+ zsihs8Q;am6!1$p9MBLGVQrE-$QOR9=FrRBwAt4C)KnSCsyy!`ej}Mg2LUxfT4N&Q8 z#Yjc}6{T>rQP6fSm@_)7cVM6oPLxhOe#J|)(F(q zLz%G|(|}I)Ajoli;_38Tc5-}NutXNZq1}cyax}M)o7cC%v!Zp%)o@G^BE;IdIxIy| zQwwcKkDe{R`}wDz-z~pf!MxPiJ5CW+Ww^7t2DpblMu$qyuj?W%usiD=986x{DT>-Yvdc(%8uy znwpq-JUQCeT5~V|T6+A&ATLjQ8*>topbv!$f|Z+}zi)8t)$GKa_U^)rn7G8ml$7+W zE4erF3hxzE!g3y&oL+eOj+veqe?bK1Oovhry;ZF(jcr{GB?ULGUqiFWIg(ee<#$dD zH0EU|UARn+4vV-LhDHPgd%Jo0x|`dW8xZugp<7A|Ro~com;K=y&i{+o#87WDZM6(cYosMQ81#?TGbkyC+y_S)hk`x~cZDes7 zz0YbroP*B>C#FL9onM-FK8zfEF4V)-(8N#+I#$%wHJ~|5O+`^&UO`dK!ndL=doQn$ zu&9{0Bz~_-Tvl2c3&c3R@cixj-ptfQ6toej!Dn z&C4e!j1|jVQj+eo`o|`pEzU3f+@F@15G8|A>%ZEvWrxK|Xw8O3YK!^O?Z z!!56)s->r+sjg|LZ)m8ct)oveaf0p$JsopLXDiYL@9+qR?Bs}ws|ESjZr{l+&khSb zdur3Rot!*COI*S(CcX@^`;VT?Exvl+pP3Y&U@x4B`0BJbHh~!1>gy|uZh3O@0AF0( zAVzL3Ic;Sn6%|!wZGAnWxsj=c|le8Zk63GEzb*& z%8m#z+4bYjJ=}aa5pi+r*o4YKQ2hDj<+r~LWhTTYCp&RufEYX1^bt0XvOD=MTztH| zC^rWeH_BxyZ>*@KsGuw_uckrJ(lgZ7H#ar5AvswY*?T%V*;<@&yXcltbf@@QRzk`B z2Wc_!J`QI*WH;>qdY~i{RgSQH#Dvrsmmf0_LA4Ip4qIEA>g!AM zZ&+{Ny=%vgo!fT+ivow$Q8~j?3a8{%72rUrp|ZM>rlyIWwY9OWyO)Kf?AsrFln8gGW z2^%(*+TPl7|JwEIxi|8Qvcz}ur$A|{2sV}?v{?II>HX4@(u%UG5?f1qOB)YIlDUnG zv!`F=^~UC@#djY*{rPFOHkW+yf~Ux4m_R{cQE|KYq}oT5GYij`UN65MxRMy3loy$S zY}%LwIXgO?My7Kz7;tWTWP(+9FH?LMUvha1b9gYim!AziE(62#=9)rVf`O?8$=)*{ zEcRw4z2SNEs?^Hr$K|ET_Ut5Y(akV*0wQ8~`-FsQSZXu#FP4{I4QHn$rIM{SW*|Rk zbik(G6-V3ETV{V$Y|nO(q1b*y*Nc_VCdS9AB?tIgTe-TsxcOa7$|-Jo^vmkX)cEQu zx+1;$b@l7V`HtJ+{??pZcJATAh}b2>*G)Z}n}4zNV&V1V)s*DK3^EYeq)WxjV;4P| zwufCVxn&ooki1;`<>ciQG>h5MG%Z%OF`SIDvv+ZIa`KM66n*v1-FC*4h028wAO85` zw~w#7D>Gs)ID0u`l(_kY#PBu=2{mw{YiV(D>G{jCoTP-r8__=iC7pK2#nn@Q(xw5| zZP1a)!;SK6I;5?9QsKA08?Av+7m_lfkBhsjnUc4 z%fm0LrG8relzuCdOw*#%Ld@aZG0B2NGPklYw{dWC_VDud@bnFfNzW~*Z>?=AzmEEO zdlRLBEgpVh@x4~@@pX@%&c9e#e6{#|G$$nm*vdpUX?HX>G_|&N11(m)3?~n76wi;i z1A1C|dRn?wY%*;{AHC7>-lSo!JmiCU$o*tf_{(-?^mt!)EYU;Zv4Yid?A#Pqy zN4MV!a)JHxL9@jIC*(^;A}Rx zkc^==)bow2SY(+-sYd+%UTctzXZV7M34fO-f3Rwpp7kY9s8v%@|+3_r*XbFd1@-9WpXe z(ok2w2b^iKYHiI-2)YJlx|(_f3v&xATU*C7{$U~EvGG}@w6TG%s^Y8X&o~G8pZIb6 zE*=39$-NG#sV%dh7B5~dExsAgO-_oxb2Ss$tWBxIe5Wotna0&yg5%=l;pOAOh#6Zc zsVbi=>Lt@ov8qT!BGgDsbksDB&26o0Y#p3jy@EpnE?z=&8U_aHDr;&g&wB@5RQhq- zZXSM-y^?mxNlml!tO-@S6xd4o<*Xd|%L0dWgCMFDY!0Le*PuZAg>u9Rp z1!AXJRc0ncJuPixJ#Ae>k}dFNYwcj?c;-UFjYi7E%U26y6P-mD-Fz<^Z`-z;TTl!y zWuFMd7G9!2?d9_5wUmU^MCVNz$Yz~R@ZDOPsDH601XhIy`f7PZPgxkKt0HkKI61*4@6j9-{`yda##&^9I!TK$N^ZQpXLIRaBKu7XvkI)&m_aT>}FX zD|;IUXD26@GoJ3Q9?s5QE*>6k!55MX+XlKSu0;5_M4E2h4%9>>q#cry8yA*eF1=b_ zT3UW`Eio-6<-#_grrp`n0-iNS?T6kHVQwBi(0wU+Yt_@Ijvpz+Xt68x4Gm0;tejl@ zLV`liUpOD+?&yBTKQx@2Sy)h3S$I1Gjl2}*<#ow$3(&%eO6+q;Nuj)0T3Uo{YWd~k z>uG68aREEmQG4}rerg0A*vx)#BRxJL2*;5Qu069KZm5IKdwzeseK(w+lBUw2(Il6nE&1<2dY#AnlMs2CB zD$k1}p9>}vwr=GT#>*UVNJ{B^IXgs$pcmpN7PGDTW=V}Gkb`+^1snZ0cQ|f7J{}%H zG3gUF#&AGYNev^X!>TqnGBh-E0NuB>w6=x=cy8`7&1@EnibbJVCpwc_m7gASDeA1{ z2~k0*!!q{CX>Cs!-QDb|`Nf5iIx36F>}BAAoL1*rxbi>3$QU)>O|r1Iw7bq?(z|XXX5K7mg4mD7OJ}ken3ZSrT5^i*m9bAq zYi9QJ49ug;Kfa#2akY*~V^GD{xPwTrWld9HGZE(Ht@=Mru4D`+orBSzJ4>6mzJDHVRd0b z)NF5RYG|zepM3;~@!{otE9W5v{L_=##5%BP0>G7~j<%kWp02JAoNl&oZ)8E{qq%ut z<}JFq{_Dz{HDXNKgM=HzT>@fKrb!UXch9_f`3i!%CwZx<2{#%22QXqCup2iw{6(x( zl!uR>4^Facc-|g-{_f@DjtBu@zN3?a_-xF3Uf>Gn! z3)MVTO}pUO+`Lu>nYIhmk!WZFw%W-h@N(j{N;*p0(LMOv`!8QVuYUdd=Z9aPfr^OH z>8?s90WzDp1SF&hw}AQXr?20O^YGzi z6$wNmeS(o@Da_g~R-LPpgPWVDM?mPM_-pwM@B}-0x;ok$?ibz9yLsjEDtJ&un`mln zYG#aCQDDu|!pag>nUxhRG}8<=O54f4Z)Ia;V@)zQGbQS4-Gh&|_ue2yxN>jh6p}t@ zSOBLac6N`yd-MA3?@I-#X(>qw^+(c?^@loIL6peftbv^YE#cp%tY&O#WlS_J!>F+< z)s@siOEfjrA0hi_!Cq|cfSBmMLxzbxpj_L!`{sUr|LS&fdTN{> zSkX;~s1SBiFk0B0fr7w}%zs!-N!P&C&P4xzXjxjjIC%R)+rE#F^Bqi#jFu2@-yq-M z0RI60pmVv99^1jLFb=ccvx5^SDMLi-m{GK?Jv}3@mJ8rJby72TW28Ev(Y>V|^Bvbn zVSW$4f~u^Mv7L>D0j3@sSv9t%rWR(X?M4SMqQ+SmDLOsW#Va`Q+=a6jz(>9X`mvK; zWaeYewUbj&Vn3051M{$d?4}Qwq@<=~6$)aMpvSI*LII{Qi^j`O=H1GFLiwnXrJbF# z?O&=8YriP64XKt`5A>1^xl{2!&@hX9bDU-p!;Daqd6)uj@=#@(zc0(_fOkxBKc8)AxTCH*(W$NJ1+G~T5cd-p3TsuGP!kdDF# zakA>1Y;3K}O-ZIiSk7jamZ@x*k-ev65GBg*KgXQST4D)cY571N=fUk+sj)-gf z<)`0%U+nJ!>8)e89fPGL1G(ikYTI8d3Gwjp2<`<>Mp@I$(vW1Qhj|;|@H<%B+mK92 zW~LTk{>?1xb6CKX6stHqGBWeZohmAw-d&W8dY^L+u{BeH5MBL3H07t?K7RQ4>s)Ua z7_@bL4T5RNrh^@DGy#&yYvL0ESDRnV`KIRy=(;vG&?npnu2fmo)|Sq82Kw4s+J?Xx zkz{EVOM%Us&TP7qS6bNtr;I4~^Kvt;7ZxVmI#JM7=7+cQ@SX}d-|*&_j~{>gEI9) zQ_1S>rgTkBE&cZ4^QS-1_j}$=*OH0HwRA*(+{MXtIE_hp^B(>9<0p*V-PBv%VCgsp zGExzagI!Tn1hysE0Yxyco{L}RxS|&LyQYSkm=7huu29p^*Eck=Ftv8|KATufdptMH zoc#I2rXcp=4R%$_I8#w&dyGDu7wPCjGc{* zm6@r&4tDNDTSr3+T19nF9ox1U5`NsgCo@^}m!KtNp!U8rBQv>z3DjbNIgy4=SZ^3} zT&#wJJOY?FMfac3)X*V1*xT0vIeBJ_@iAp}HDx6QOf#*F5_@UvSR+d-qLmHF+R@(5 z-qOYn=Z^VhE!cfXfpq$QLAy&>yVWFveN8djHzrg71G zJ^k94P&q10xDu4)(D( zH72OQ`I}R!n;}$!{FKr)7LESX${KgS-^)mkt!C0#?fgKEry48RbjQ&43^?u<6c7~T z@Bb{eOgSn43x3RJU7u&(v(b?$&cmNyeY&NaEtr=|)gu}%p*R$jNZL~Dx z>mK~HN?zHr`msDS?FyAn8}Ud(HtsI%>44G&P-2%zaq{sC3h+v6 zX=rIdz*mY38#cy+|4W9N7%#269nNUOH}17H#zlu*FE6XC zD7|~{UKv`>4GnXZ6%`Na?s>X6S(_T0nmIvC#UQiRF`0}QECn}s^N50ve%YI7SIG)ie=@%{{C-LxccjIJBG8SXNY6RCpVD{NZ#;lD>kvwl>kh)!ok4 z(b3M{!O_;<0aVJ;#@dWzVHwZtI&1Ih6B0&_j!Qx>`4zRD3r&p8ZDG@TQY zSZ}|6`MMIdy6Ml#jEuynWYG-7VNV%+RUMQWq4?L?4nn(OG~VhP7n^pjZZID1EX*C4-h!QReJUYD*CWt}e=>-rV3*!kXR6+PoK zi_hPF{PZLax^I&p?9p z)z8021F_F#iSbe1%HY8|aaO`sN5@`REfaPF$RNXa0WgYn6g6zjpu60{+`!by(GCb% z*_vBg+u7S&g;qTAT%U0IU_a6+AvSeBF)O$PA_XH2dItE#$Ua9`_s?p7miOK z(bWx=(d3JVp)ABzfd%H>T?{%%a^~Fty|)ia`0RmFb$J6T__{Gu6ZlFvGcyxIJ$*=U znGo%)!muc~!qM~08Na{_m!p!>Q!XahkLv31L2N{-I_X`u=y>y^+L#N~N8NT@7&F`PTtge1(wUm=S zqG_$aJ`)kc`9(%tjJz0mA-Uid{1z78DJm+=M^DL~REDI5vA&)js=ZxRUE9#qNLO7; zQ`h)*KLlHh-mdCfNuj9Cerp>Czqq_wMqh7x%4ua&Px7_9wGhff=KypnbVJ8R;%R|> z@)mxV&TPJc@RK^~VGC|Bu zy1To(yStIJ>%D*?f;0#ck_Om?MZ+WAV1OWL$Ba5X+wTNkruEiRC-Ec^PXURrz2@mO_JkJytHm+ufLLRatj z<1}3<8A*OolfcNxhy=T0Hu)7RR|btV-EV4ddeC$qK#CGn{0;T=bhL1*29AQ3u8zL3 zDHyXhR#vvoIURkEkndw!sV*^ShO}Qe5_Z_u0A7^e>KK}SF*7y$_T#%OH8F8n1K*g) zkg(vyNcMcT2zJw;grq&YGr;sI%+D*x%go8oNe?pvP9HZ6X1HX~)7974)z#M4(bL7Z zD9J?Ev?6XEk^KedL|gmN?9AC{|3FPqMI8(8xFhKWMaAW1B~?{L(Wctw{!!84sQY?H z`>o1nliqrHjI1S8pg>jvL8%v!-g_y?#@xsV1tn((J1c8D8|+lJcGx#47rHlgso1oN zD|hkM*VQ}n?B%DK#uG6ywpwcXR_+l;3hJ)iOL?&6ArL#M@o}+nkwJmJo-E=<&+Zn6hXlD98d}(UM(oR|Y3v>YKORW<>F3igelCoOjR^~)0#N7z6qa|7ypwC{Go9zO?M?IM5;j4^8P2u1#Yps2Vo|KNx?Dm4{X%DJZ~kp1k< zjqx7j9UOP$RK<<9!O7_vVCo_3Ju~~dC^n1?pP#przX!1-+%QB9tb$&_2>5GT0j7s< z6Jvv2ck43u1iIMU*)pwH**Up*cvHUGeu0tu!+l^ZILOP+**h*ZImX7w1TXu6k%>pr zPuJXNA9zdv|Fh3$z!F?|U6dHLD<(9+&%@EhdTl=2Dv|opfg!5@2e8k;pBM(=VT>Ag z8tH4jRaca@FDA&#)ydY{+S0<>*3R0-!QIEtN>@Wu&&eG+ z>}|Vy35@jABfI0G@RIB0?re{@Dx95xq0v#{p_DJ5D}n`lq{#UFDVar8^|zWk`$wK) z1VNmbo5Q}e@bW|9?wHWPpx{7H2M;?Kix9amIt+z7C}P7iLgbhvbO8vm_CIR6eW|Xz zI5*>1@{t4k_9P|5M1~=#kdAwMdboRf`v-+Z#qB+EEVHoU!nM1tkNU@+f}Jr3W2i|( z5qAFNyP~-8=;-hOZ&xQzdopDr)x*@#S_noGrAUmR_QdEY#OfZ8jSTibYI}J5>ZQ8M z^3vjhysXUhw3H(U_w7zhK$$;r&;Em;5@zO>Rb9N<*!*a4Y+@2uk<<$=*a6jJeqmvD z@m;~51OQ5WyfKXN+w$4gh*XUL^)^I-n?tOz_zw`ho0xb!IWaOk*xT9i;LfevH;BFu zNR?+!7v$z-XJ_T)6`d}rsH&?652CHRe-uCo5ITrn<`RVXX6C7d&GXCe3-%;N2M31) z5Zw=YKHEl-nh`=~gk$65BXCT80>elI_8_tM^aXxH!Pc-TVL=vw#T^PA4H& z0^8Kq)jbF$bHv2VJacyCix=}t)EwvY={bbr;;X`4p%GCL!N^wJ>^QNUg{q;u0sbR9 z0-ibk!vq#ItPx|(XKNtn_w>p5C}^Bx<5(WTy~q-3R(TS&6QFBCkV(|rn zV*bSfyaT@m;rL?dZDHK5*cc3#RS3BI+%c5FYHhg?2rqlo~(cR?Aa51 z*7K)NsOgoU<4#l0*zpw9IL8=hxI4m&%mWw2%0WHil`tl zDTd@g-Y%~GMhK1_4Y;a7aFCD~A7@Uig+$@RQ;<}sXV&1VfZOs63lz^eKQ}}8x}e=W zn}!SX3(E)t7+PLjSb}5+XhI|a!TE(K2R5p?xtB`?@sT0H!9jjr4sO;;u*G$GG-Y@t ziHYQK<|O+Qc=0TGifx+td?hT*Q?s1l??9}BeA40)J{lI57GF?{>lTO&LPnzIRxQH6 zh2$5T<`+*V#>9jpNp`Vz@x{PxlzBLam;>nu0|)t$i4~Jin2%S(3+Qf8Q(V|-5LVBp zadSUIs7Ux6OvuvGGIL3K5h5Y*VsUW^VGZ+eXKsELzsJyl7rk5*9~&DH?C<0342G`C|Sh>U{FPwTUcKDJ}3BwG;H|yH*jkKhVduif1&!!N5GqW>@W;`>DAS~buwZM*e zpyoL-X4n?UKw|cw3PXMmueXDs^&BXZ#pTn{Q4wJQ?mk|QcD91~Y-$`ACm?__I^1;W z>VvT-Z7p3rw_C^RI!4>ark_CodukfT9bCgO&0<~R!^wVt8KM?tA?JdP9KtUM7+73f z#+o8gNC;>cP5jTo=$Pl?@H~gn^8SzWSRSks2^o5OSvnHjL#@Ed$IU<7bv0faLw3;d6{ndOD$f~csF zKrcUEXDcfknS3^Jk$Xg)cWAu5fBePs(bmc6>k5X*$KsD*>5@Q1$joAkLbT3fbu8tFM}+u#`+GQ{#EeZaV#|4QGLB5% zPE0JCoSf?KYy&S3JhK6W1%d-q`R6c+&;ULSWIlu;R?;k)CWwu}Zl#~BDnYs+D>+kG{YOt*t4&(J(u1rl#PGFOmX3qRT1_Sg%vOrQ6*5U6X1n?_!juQb5 z7|zo2(h>yOS@DfeV=k;i_>quY#NNb(KY)0|@F5_JgMy*T4vA|MQ*$`GGIXg+>S*p^sY@b3v}sX`QIv7oT6Vlb8f4R{G`(aXh`z@fgv+s3QqrPpuX;Ja7L z%$IzxU*Y}~4}s{wQ@vR#@C))HS&4;_k;RYsY`WaXvWrd^L*A(POfj%x6~NI$yr8D4 zqO792`rP^RDA-)SehcDzO|1ZL+;47egSK;9E1=7${B(9wT^oCP`iK${#0e$6v1rS_SDz;jVVJ)~0%@ z+$#W+7i}Gdt^!fp7)N$N#}Q1vo8n zhKP1It`oYta7PWvJF0CfBplm1+JUOiiSTiNu8XPmjzTsr(Pj`3@YdZo*he@B{R2aT zBjY&yN2uXVqu58rz^-Mr1{fJj8Q(ODr0vOLWFB}3lIT3%_H>FIIiTO-FhJZAb&Ds| z(+y9aP3HT%H~^NVv#p4YU8BE`>R&ZD2m{oRG+wv~zZWuBq`koC2>E7o2rSoO`~v&f zlR-Vkp@SWBkcroq&Col+*Z3&uL;x);@L)INZ{lmL?U0MTjis47=V>-}-QEFapAZIs zh-l#zfkn;5Fpdrm;@2^JA}cwB05j=lKn<@O0}UHPjL#brh29yGgwbJ==aVRBr9b4q z_=~VTblld)(n6i1m~EA5?;vqw1I+Y8H3O^zD5MXq?Z>zHcZeEh8%iEt3qP>qkKvt& ziyYM?b0eRF>4~w)WF%~B#xiW|%w;!}u&prcp?ZZuFc<=*0mAOk zg~5{!6ICyw)B$^#AGinqnIi(M1c%6oz#2(EFn<_Q_=T1YVA=h!P(Ula!?UbadC{v!t7PP$1l$B!j)zqG+F0QP{gHShC0Wf*zE{idM-3Y7(RD(HDrf5Y?7WyH; zp>{ud)YqRXwBice`jxBK{;+o4`VBvBrnao)*t&yE#;`6ds$9xbMKeqf{z0CGA-1iL{ec#;UBWoSZzWaMkIeygkxWY(GFOf8BE4yyMkLD6JMVIe=o{?vzuE$cvN&$ zT+;5n`;R22BvUDClbI9~-=$E=Jj`VLAUXATN^&wYh5Yc?u@fgwp2|28%K>T;9n>PV z%>p}ddm*Qw1@$F&FAu-yU9r1UjvYUqcBZE8TusH<`YYg&G&euQeI{<=P|zkNGnBQd ze)a)U##u?Gjfrn|l7b}aTj;FN-AmMHuzNgv{In6wqD^2Ham80&y8?#9-G{AhRQpO& zpiS=Lz<=6V75`IWa6V*aBuLh3@2Ljc)v8|1;eGnpqp3{=t%G-u5rM<1alOUwP z=OC8)28Rd7?xYb`(dr*gwKv@ddno1p3PSu?2|^Kmw-&d|;KAGj3uHwzEU@F&jcQtj zk7#LOwr#{jlY<90o2U!o;RIk64~1uI!()K^)YA(#3>Ns{*u&(saFB{ts5GVC<-rX( zP~Q&;5E~bHATj|;NPZ;|1VacTT~@uj33MO0g9mQ!AoQtb3_%+lM@1Y#)&Xxium&LI z{s@wEcz4Arn0Sy(9gp6DN~~!!U}VXS>>%+lAFzYw!)$_Ac>WeKf~JSJ;Ml-&6HMX9 zgaJmlKM>9(-?b5f5!EIP$`&eBFx5eZ4OhQVnj9SH?(QFY+JwMJ{FhF&*18463gD8! zx50?P;%a<8E*zQ7Tbdr?rT`2Nf=6MEC-Wi3IHg%0EFV(Uhm%;#Ef_dV6>$y!fm>}| zy**^t>gyXCn{PSBObM6EW7}>6ygcx6__(|GFls>R-veAf`Qa)ozk7|;9XU{W8u4RT zW6d1I1#s>WyZ9BfBbX!q!cQ_5EiFw{Gv~vH?WpI1-_ws>V{)$LL~3fJNFLidR}jEh z3;DpfK*_lQG&R-81qK=6 zJ>iezx`dEHKxQJrhfV5-fsx6lFFVtZrN)4BV{G5@U0)CeKuc;Qmn*k!g0FP*792ve znJdtAy3lax$_-QiZlj|VyutJ#32K4oIL|L%yLRI$b(wzU@{Ma(ucLnsc?Phn?%r#` ziHWiXUgY2Qq#r*P4<5`~ixzlt{~-}Gy4MH;Xkv5y`i<*X;lZ^V*REW-cB8p-;PLDm z>XXRFcdwV`pFbTR#_ApDY461uG5c)bQO{7{t;-kA=7aBe`gCy#b!JCtDYByr7aIu) zZ2Z}?*|$9>(^3<`@L5GDV0Wqefe-H9y?*t|r3)9%pF4NHuI~J~y1H|>+Xs*=Og)(# z?!MDdS)7-dvL`wy7l_TOr(_M?Nh^dN;9eW-3OC* z@86q1Q3T~k1X@{`n_8L}7z0;G0+C0SKVaSwYi1q zk+wVab*)#f*VdleeKc*?o}-5kC+q>)eWQ=3Ud$}Ne{|}^@xx-c@Gx&8dYuThd)Ka9tuo~0=i{aLocZ|$_zg`A z9AZ*WpSyIqxd*vT@8EFv^vv?dufKl&<@aAdfBx_mFEB4Zy#DF;Z-0G$`>d)oDPJb5;|xcv4LoI93&A`nc3Xv=o&eK2?Tc765PNSK3;C)5r3`1y5g4Gi^l z4Rv&kOstGd?MzIODmq!a*&DkBd-}M$ga#c7&abPeDJjgpeycJ6_{m*DE$_sOlV&cWW;@TF*#wPlP zdd8L}7RC;)W|od-W{#$)6}kt7IypPX`5*9)JxS%}l%2c#sQuo9@;x!(o)#Qiz`GF^ zla%pK%eYQ>XQ)Cge(cLhPdjlef*l+IQ4wD4fbjDVRB+IOON$?CKQ+By4JR%Y&!$pT2(m<&XK>WycOt5wwllxCG#x zOu&hZn`7h8=a-gVExqo`O;1ZJJ)Fz7TFg+1zMYGS@L;1+!W%_|8BkE=;}a2)6s6H9 z20NV&``c+kG*L+fdpk=zM|;o6q@(D?H*tTN`X>3$KY#!I4>(0rt;Ja}j1Aj%@-rl) zWdqY;3v8)5LeCm3NKeb8Ja7-a#`@9q>Qn>4Md zkp2p*HtH^WTc$v&@M=2rUitZ${A*QA^|cKQRbj`3u5E5=Zf@fMkiG@EatVozN!XK| zo0$on;D$SQ&tEuOn6<|{!d>V`3>jTa26j%~d^!b+)I913L-{99X0g~r>#W;=g}z@e zPfHPANf#6pp!hck@CzxKnPORKh{BYSqnov@t)qjTJy5m|t{$H7EjTbZa$j0u#igc> z`wy?3J(&;@YcBzh1Q_D7O8)8TH=j+tSeThbHD;(VI%AbztXhzN0IcBD|U= zfF%V}>qQi-P0TD!^duNeVH1Y9y&EpYfYr0LwX=2h@bdQ$2n>mc-Ax@oeeuq{N3Hko z+$h=;xGO-3#2j5hUO6x$^VV}xczm%u_i8*3F;}u5rYzg;-)m}Nnc7MdB7+BeA_{h9 zz*Cq?!jvgP+{(q)!or%M0zoe428-$Q`zq=)6ZEt5~ zZjQQ%31~ztqy;YA_Qt2Aq*go_9cx6lkvmj$!hXGt96R{wQVJ>|ncytFScLR8>RY2A znWg9LCvL$OUdcKwVQU3VkSXvZjEQQw14(UWCPOD9C;CiErNx)aU`T-om~krE7mIeCc^fnV z9(E8@>=?mhfgkwAwA`&p5+Ti+D@pK&%*`z9fUvN&haLZL2wERLS#|Gz_6>~;PM~t?TF3itl~7UsdmJ`%?Bo}f zl2;AO%)0+#>GdM@a{bc67$hyzPwgZ0A#;Gb0gbm{O_q=#IYa~nWOQ5&b@g@hq*#7Q zn1IJ-WAEY`5E>X35grok=i>*~)#2p4y2?v8FJ354OHSS&8MfPM(^lA$kyj^vLGgd# z0#r&w(pZA#1#BdKtNbt4;R2@W=7vlNts(>1Z`}wxCluv zz{fKH79(Sm682LkD;uu2wB9Ob#)Tg8-cH<-R@MqTb*lN*^2>McK79P}(^PrRsbdv& ztjTI8`VIdZLbxTsC!%2CVrynpCprW~#fdF{6>&)6o7W&;UBu;RPAt|Ts zZiOUM-+~x3V@Q*_kd3n&(9m|yt{^b@1euEo3JR%$>m$a;pz#Z<+l1_j3Jh`A;o}ui z(AEga$m*_>;pWoIZSCkd=PJo3C`c1oO^kKowXx}cgD5CKR||*%2OEOJ+APnuF(gck z4L||618c($AY~VKA47`1Sq-r~t3?#79io z^RmN6g2KXdV$2-ikq1rR1CdTVlkusj%8sr596 zh+?ewKAu&2@ygxa!+a?a`^HKU5LCfNs2P-bL=>I4N8T;Geg{@r&M8yD?}kiU$Xj^} ziFh3KLYwH40bL^lbAuI;wpz;KFtnB-sjp|KZ(u|j?l3m7w6k_FW-x?>w0Be44ZY(F zAOG;!#BwVH#iJ zNNas+f#Y_DjEaAP7(b`HfU> zZ_n-AG-+iYPs|VR{ZFre$No4|o{@PfMT1U^A?T5MvVtDQkXj4L$g2V{;tRG=osj^H z(WRYS?43X<4+!6za;p5i2Kgk%&wwQT{`X&h{r=^ba^7TNL0tpeIJu25rQ&TXgg;RB z`5iz~KfNf+Je6J?BS=hXx1)T}-1VGds%`%3sMs8yGTLhBmgiT(XzL>e_ms$<5Kl5tIf;4djYz zY06&Cj=8CRCb2eh*fyEi0S5i7jN!*ycCTlnk`NY;m@*COgy?hNP9fvyZ!9 zfQOTvudnxaQ&PSmAyJ{bBBNpx_opNhFK9|0@ow9f-2X3tq1pJz038LvZk} z==eQJ`>C|T!vgSr4taA7FIR}@NyH)5D?BIN12C@q~}%~fuSuz4u0PDd|P=K zvRYm#MJL3NVXmX|iaDj`Cx?S0MC@ReSOd3$E<_1#g#3Yuw!XcMqrI!IB8`buUebby zub>wt0dhLpkqKbX<%435q@7C5I8}1~b~?YXFhj!ABRZ4;a-p=kXG-Qsy)a*@@EW>+ zlvj2I2?jZ$xbxi((oS-Q2r<-jfbDg1a`#riv%`*=xd~2UE8Gj(Slc_R!1xNfJ+)6T zr?liu-Q}iem=UxLcMnQ2-N-E@t>luD`RMmM8eEK2;Z<~g%WOe1J*=AFzwwQb{AXJBoQB*L5=PMaEuE0UVYxzd2g_75O6a}cgBxDa#mtUf z%ndX%&@(h-EmuQ^l$D*Wl`**MhGyW!+uOVQ274KaUAgFJ>MH!#2W3EZD=zkdGm_1(;kLyk(u7Q!1jx%l<%c*^m! zufP5N+n?V)RTadGW20Ol$^~=MC~UX3^;xryUyLL_KV42q!w?WkY^h}9V@##A3Jo4)=zkL0j zh|_(j%qj}R&bUGp1e0}=xST+#-V85v|Gp&fE2x7HV2DG}TpH#q$XNy62SEA(VR4DM z=WgC~*`a>3t@+`dyNy@QQ zJFbYBVwsbG>REI913he7SP$5peiV>bP%|_(b#QmIB+<`ch#6QTK0Lg=9PHfOJ={DM z_-NKX4o;4?w&uph=4M8whPsr|Rzp(_)lJ|%?c}GKx(P}Q{{1J5J@dIXH#f@|8Gz_c zI7h#adx(w)Iin6(fBkD>rw0lD=y)#B??FbWvE<(FJ?l)>eZiOxJS+ zS07YYAnASmU7Z2x0$N)|apR91oIErUa}OHVk#B!stU(R>xeAazGh%KN%<0L_r&QcurtmxI7D;VojKqGDpgG({xu z#&$kl3UpXv$T;|Vx;VMoxVt<12b#k;y2YR?3dwxvG*MYCRr&@FaCqbtoI~;K++J_K z5oXZWx`KRjL17WDY>>Xb8H@l;Oj;3#pe<#&(n^ue$QS0LbMcADX`2U^Byfw#^73rw z(%Y4tchX*hM?lgS2gNFGLvu@O%0>yVVAfV91|)cJ8Xd}rlNFayP-N8-6%`Z}6_k_} z6%^!^ok5ug(azP=N{YrvrLog#G$xKWUNL1Om&n7}=MF36PTXcXs_fi?YgsN4HOGXD z;0CSSa$|UqIYfi@R?q!&WhF(0h57h8eZW+k==XU0yCKE;P7U(b^9%9|^l-2;v$1zD z7n8NKGu2U3QdE#vQjwyGNx)4yf{dYR;hm6@4>(Hi-x&Rc-m49l&m~jY*~}Ok)QPr% z2-VZW>~ikBcLmoiHPr-1Qd@V@Tpg{lZBXHJar5#-WO;x->g4L|1Z@W}C5-4I&Y=+y zzd4$mLM6qzrX`vxYzy?Zy`!gtCkhhYK7LNt<`!0Jbeg%phgU#s+@AQD zi13*7dS*R`| zVjSoh;cOu;q`9}?=HT?gEA-9znb3lMtxG$e9H~c&1>3K}P%or1sGgPNKA-Bc0XVj! z^PGv6zPp`~nU$@*nKhUt_NXtq0Hf~e?%|?=$*}>_+TADk;K4&VXBs+VWi;e%lcF5; z>|}(r50^X`9eVoi&6|&(|N4hT3;N^y@l*=5pv^mO;Cj3V(j6Ge&c0FHjJx&W2D!i9 zRKwKS*Z{dJ6gZq*0KI{SPIk~M^3uWPrr_`G=j#(3m9&S-E6&aIuqxJd&2|T(MOx4_ zx%}?X@Z9US?_uq)KfnI+_qTt}r=%POEr@-`&0c2r4`2{<_p}z4HV@!l-lea1=z_kw znTw$z#4rrW%Z(L^aaN={;pS$EcU_uNKtK?m9CDB ztDB{{i<`AIii{A)u(Wb;S0Qbll!E<2BBFQgJAC9QMWs+Dd=7P11v%Kq=n2S29sef$Tl(kLW^+xjrB;AYR&w)(kaX#wu!R8m9K{ zPFD7=4#>xxkp0=%Ir%7%WK7I6EEF0GkuiG?9zK?HwCwWClVD>%9|IwVb?nKymcdCt z))p7u{rU&>&)PqK{j=uK!Dy)NDXlE)2U-cx1*l)*Zk!+)h6p^VhUg)hm{>TuA|odQ zX=!T*h$YIxO2`b9kb8!PLTo4Qz|rF;_wNeXm0)LN>#9YgYk0<;y3jm418c8dz5Vd{ z*T290^~a~IC|}(z`D|NMuj7^)=oa8>zC(JbApu;GR~zdXS>m&OJRKb!tc>-H4UJ8V zkpWt}8REcKp-Gx~g@uJjc?CtfIv@)`nM6xmKv>m^kb>HVrhow?+WY7_@$K7hSDm#b zl`TE2K?>p!h&~Kb(1F7TGFbpLpy8;hAIq5<@)|lurq)(yi2+g&Dvs86q+$zF5P2I} z%McP%)V46!pmaEOb@dI@WkiGokZhF{C7>syH z>?k!eG&9iAqqKME=xC~|YwPIf8yVxG(Z zmK2|^sK}4BG_ns(Op1$&4EKxoBkZH?S4JQqJJ9^p`Rf%8*4N72D#u(16hHUojrsDJaK^eMeYp&Mk_2R$Oqy&-)TX{R!;7I z;R%OM6jtA8=^F=38u0;0`}4V1x$$vHiBX^txcS%uuls{UJ!Xtx)rJPCAq(LBS@^x- zp4OYSMQQs%3vwYUsut#;JfVtj@9bu#sivl5=H|QW*pXOQBERS69~QemHLIlN>b=fE zaybep_w(n}?54#xxx05KMuh~02D%4E5M$!?15mAnW0G{f_eZFs4U7% zJ+wPEBE%oUDNeW+0Ta&IFC>aZ3z7r&*TKyvC?akj^JHPg#T)n9`bHiDEDb&j{sD4% z{#{N|LR3UF@Z1ibPJjrn7P&~^Gt5B?j3L235=0>op@Y304{l$sJ6oKSaf~`hNI`Lc z(FX_kczbz6;TmU{Ur>HkV3KRc!aRHBO=}m52OhQ6z2yUpo zoZ#3>@j9FzLjgeRkltV5R}o+-fh-R7bhJLWbL~Q1Wob!Keh%+|>*I5oi_E40tcVe9v;e0Gu4RV{dc7i4FxQ-rLE;l>rur=s5rviB~MJDK#nt zj3>~LFvfx`g7pMa5W%svwmi6Z=jQb*4a|DM1_J$oeltLJ1HN2#$DVEs(4C6u410OV3rE2pQPO);mrfXf9V1qjViKmf-l9zUG|iGt7y zsClqZ$VXGND~PdegaI`grJSV`7fhtDp-6=vVn1Wg^F@?uxkr!s?sfG3PK-PneF}JWl z&2J(63=(-H6o^$k%x2iii;0bojS5DKVNZVy9fxcaPJ04oB@2p0N~6X&VGW}<`3$&! zjNEf@O@P3sX4b!8bt0U_LlasHhy`;i5N`9rEbqRX1382c1jquLU&@V3OpJ{P@^f?b zaTLS4;;bLRwzQEnRc0|30oop)U_NGNA*HEjE1pkNQydt@=it(jiJyl-gc>!!8pMLd zd3>`Oqll3N9RSgX5qLq(a^O$SFXhK4CG83e!pY$3EPzNCxQSMd2=(ESUiiiw+d&k_0ydGU$y z(P4hxK92S-SXDxdyTEP3)<9=77>i`CKz4dEi8e|sAovvcVlo&*Y}wDTnc|a(G0=qI z8a^Lpz&Am-;kWobF@_}tx)+8J6t(==_?W0*FF#LQbAi*M%5f3F0b4`U(=SHH+S|K& z8rw!Mc8_%o!x-j^b>hJmq8T5=GDf^e##GIN4Kj!Aftq6{LH{BJ8wFekKwv&MCN4VC z-xu^j2PX-vD8U;zhWUp3FJHR@u-$_T*RP+h>PfqMb$36)590?r$e@z@J5 zUNGm@k`+t*Va_sGKV!`l88yNhnVSYhC>xOuj-9_7PCR)S5_gCVl-P%nNYBSn}vtRFDv)_~E1Aw`4E?T9;2 zdY;cO=7dK^1q1|pxq)^olF!D;QBU@o(ea1nCAA~tkNdkH0hmucSv`Poc!JI02TTn* zJQ7~jbLQ+8R+zyGaR1vog`R8Q^zMFe303gnaN?<*HE2ZunZB5b>v7uUHc!p1 z!f?*cF<)$(#Wn>?*%8rTCnCb_?5%K2{m9WUNr3HOJ|N_%C)=?-fOGT&+#`5`QDAKl zIJ)`B#>omigf@l>3+q2(6BZaXyAI(`R@E$tJJ=$>d6gd-78n945l0Y+VN7i6CBnF1 zQI1$E2V85yog&eSKx1vy7#S>9JUVmXNAQ^N1?~?3LU3g*J``47AnF7VFCbikphR{` z1k1vFL2yX0haFf)*0vTn>(_0%0Lj$uzJcN7Q465!;Uvk#OE7=HyIfj^0P)iDG6+@JImr52T%?vZEx*VK4E43MwF4T{$`Wj#mE0-m zc~sFBg7yT;w7jCUysW&kvf?aNZd+D+4$V+6)n5Sy_clN*Xf6(b4sgVvx;$(KZW@rZ zj<$Bl1Ar?sG6dT9_&AFZh6y{`|%D{!@ zsqDNWFnvHiV%CX**i&0qi!#c&nlpt+?$4YqEGRw;bZJ>-RV8Fs%Fd#f`k6CS2}f~B zDR@L@%gZayR@I%WJ6BspRc@*%&58^3wu6j<36g(aV0^a3ozBTCC^~(bS-j$OadP4M zqT(|k6oL73Hn~y--8rDOUsVmAc*u z-4X82jz9-#@!*YwU924}jtxlr$m;i!|cr3`W zN%lgGpw}Ll^UJkwu|HAxW_DMUArIZ*Bu|n5ET#?0aPBI0PYVH9!Jd zZH03diTUsr*hU6M9swT=>Jomo6?-m7O01Nfq+NJLKx1FFv6NAE{Fph$j{(Jp;$z8R zlJ7{ajYK5V-qBVO1fVS@JuG<_ykh*Dr7%a=k=>Q#dU&XvBxwNyh-De$!E%aB5_2>a zxip4~8rwk{3bX7WVI!YEK9*%~E4!wYZHrIM=`(}|S6y@d{KfjqSFTVuI7q)h0;6y1 z=mg0Iue_ijtwM=saDWWsD&hr3WHlKhkTPoWv|P^HEGZ~rZU^N@ zR*quZ%q1=)EFx@VVjmK{7fAL!6m#ndvNu)S0OsR95L})8I9jO@K8!rr*GLe^+9F3b znDg`wX;N_s?E15Zk}4s03Zmtkl%kwO7kJ7nE+;YVl+i&(~Si$nsO z?951QSq+mLU4;AwQTYU!5{6JeUQGyBgmJZ^w~yH`)Hi?*odYO95T+cqO^5(sr@$r# zem)`o4JVJVVaMTMxk=vP!P0~8*o~-3jwe%3C&nK?nH#SyEz7b0-b9E!c&Ll{XccSJ zs2*_)AE=qA*m9X*SiAe2*_5KDz z0TwE9B_w4tuP5rts!kf@u?dEZ-~=Ow0*cmT4DreM^qwA8XR5y5Zf4hdR@wJoVW#yx zUETfPN1N)AMs1c@$5?~HiATc#U>xv0EW#Ko3a95@kJMFEWojc8h#lyF#ts=t6xQG% zq=BH>^QfD8L?gk{Njh3#wL|I+2P$}2onS4Y{S)|0*aoOJwhrbasOfdHVSx;j`SHL6_|pG!WN)$a=Au%;$l z$)igbc`Ez|^V!|k)ztGa><`f^X>A)6`+9l@#;L~}p5g^!;lty)>gp_9 zGpq_2?7)Ra*BYb(-FSYgRfo`^2p0>#fT7j&uzP6y*}{j9XeIP|>BVD+MRcP#SUVv; zJ?xtrYinvB9JtkR`BHVo+1hjGFHj9^m#E9zuHLx&pc}mUXU|bg{`9oIuC`DKmH)tj zj;`cK0oWItz}h16DK~GDuj@CPdq$^UJbgMb*57=&x+wGbk)((qcXtLhgbe9^ABL#R0Vdzm8l)7MhhRM*y0 z)6i6-6h#!&Ok8)}>nzhzRM&*yIdB}IC6$nJd;Dx>8Mih+P1e^{mm)+W1NvE^kJ?Xb zXIo?Ck@zqlS7*RGP~Z)%8+$f6*mC<~U30_LsxG%zdQzbmPe6G#DgH@4h@aUtL*g2$CJF;p(IpN>eRO z_b&OVt3sMxQIS%1P%&4QGxkkP$t|h5-O@8S^ayKsW@_pEPhX&3_T}@}k1z3>w(xHG zXb*D>OUrM6pT1aKRc)Tf78Njn<$-&l zjt($Zaxn?YN=gdyxNB9k&{I)UQIVCEP*By<)3^4IPCR-r4eCOvhmWS6V4ln@x!QmY z;N{HZaO;Koa|i3wazZr09TAmL(l9W$Ov))}9Dj^yTYmfD%T#@B^;sKmf3^ko6Kxv^ zL$*J>b1hvyymMP;TEn}#ys-mH(rLF}yfsrQU$Skcqee7(_O`&XVZN1BHSG-Hs ziM?mmDT$h=qH4s1Us{* ztE2P5^#-Q8s-m2fFR!d9uPg^Fh`yS(F&czJpxe;KRNKzW(b>kr z$2-wKuePMLu;^4{Q%io@@esEFALT6s`L3jCXc?Cab2D?xuim`*^y%rP+N#=e(qtk! zbdscfLj%1%O}DNbfH_zb7ZVc|QPek9S5;S3RZ`c}(9<+E)6@p*$WYD9Qs3I$8??R% zj|BIK)cqNml{NPU`yMpa?TZTXu-eYa&yZ45*SCnvDQ1tgCVX3EW8Q0{kAS%ott`^mk z<=wcM$w_Tz<6`pu2(pZzh?p2%kS@T@ox;oHG2-UpL0=4GGqew|c8^FpT6FQ@_??As z-~RsNk6(ZL^Ov`879O2B9wfGfTTl$3TbrbUl7~-Gxda^e)%&T2y6W09yFj5@6FT{z zK3#X`alXy?d~s0>pqPe|j=qL4?=e1OUb>20%*_tPLFM>K$$~+L6My znpcp-1tqp#LR?c-URe`OB2xHFc!e}ce-0g9}``W<%wvM)e?Cr#ogggQs zmc%8rwG?EPR3u?(C9i_E4rQQgqL14g3mZpAukf(QxPvE;ohYqsxOC_4h5DMZ%xJeT z7dm_*mh9qli<(hWc>Qwu&CAy_@TsmWj$F|P4&ngry)Mq3!najUL0VE=0{=*9;*v{2 zQBDA+j7=G~VrGH+69A9ByxhI~gW{9(&t7ZkY-+hxn;sn!Z7sExTTq{_Wg2pAb(~4q{Eq)L~yyNz*Lax4e4! z?(Mr5S8MBPu4J*S4dD*Ew?mURh3m~u(pwxZVaH7rnZ5$rjDUCWvOE8 z;2sc}7=I`$tLWe2LX}udUfbMde@R*U0@_z#(^!57YqjU@)e{;^ zKtGX37&hnpZcR=~5@RVTp`xN=te_~bNN4#*$2*yxrl!7*hPH*BGwy!voIL}h6XH_R zj+Zw-o@~4UE#$pHVY_s;Zs!-1)3J2gTU^=(^Z^OQH*a5D0obJ>8xg{e9hmB2A2oxu z)$mPDT7r_=AR(ckWs0L%Ra5x8HMCvOQCHE{(a?dKAUJ8ZHZG1X!DtM2@8QJq+vUek zACxC}`$buC!kU7vx%2+w()LB1(U`S2Z=YYQt-V;ef8FVIXHVkV9+}v>16f&!S?GV2SeQ>ZMJRa7gd692SJox zeD&_#`*+mawe#0eh&q>p76R)628l+?z`xd%WTj<*zLJnsQ!!VRlarPsQL=`YrlqHC zXh0ch+oBf^utiP|_Fn#ByZ0Z;s4PE!sk$mZQW*8^wDI>DZSC@I?f zytr0(;e6FembD=m`~TMDq@>{;$YsjL>Po6=%EX!mFI^Y?7%ac;@?OLFn#Kp4;G5q7 zsWq|YjjRB9a7ZO3#1xdY&Gb~1l~n$XNpPREk*65|d~Zrn0=6#RL6M=pPzVl+%BZ`3 z_w2E~dk)1X#7S=x5LeN)@j7zmY|pDtp9cy;tZ_VqGt2AGXA-2f-w;XQ|Ffo|APd^6 zq_mQ(p}v-my1Y7xNi+DSr>&)~qooEtemzqw2Q>Wg@-~+f5do}V81(>JW&#=UdnrtPve;l?}Jfl&4xBB|=|e_gzN^q5BZW}KR z+nA=VfxfmbkSeC;!1HQQJo17rX=m%N*Lb5=!VAfMZtx!Y==|VXSi0>$eIbp1$2R-t zzx?#`m)8%AO}6v#@xUBA!jgK#{y%dHveHs&p%?4(LyZ*`|d^FC)C*4=6lgfMk zQI%FzQB?QLe)#O=!sAYIHqd#6v#tw_`s+7jSHFDzrD$6+pP-V8 zairo_At`O^I6E%cho_2(|;@~(tA!$G{xJR17$oscp%W^5)>lFEwOI)<8BhN3XHj#o%tQCUSrU71o@ zrLL|j3|BVuGSCk=7@%68djTbJ~m8NWkF{2^K@Be2^ zm36>LNy@1zXyIi-L-+eY(li18Hnz4kH`2AVFq0t0cLiyHnyYZkxEJ z9m5=XxP;{N9piFMIFtD{aPIW({M?N4K;1cXu32XUO_T2Kv467?RV8wmN=j*{h(Itw zU72+PVA9~0kshQwjCD+m^`v-``8JxkyMvP-84-Vo%G!@1+sv)%?qko#O_SAih|4=6 zxHFmW2i|SG;T6~FYcAekz>?tr5mz1mcfM3qUl~FrmdoBL?VNOmkup#9H5q_$PkdW3L-QZJmNkP zhmL2ark^USznzMmmY3${8tf;=Nte-e*p*%J>^vW5D&HC&PSLRX`(*SC2XLbG{x?&~ z3gk6cN>WxuSw&e#UB{5j6bH$BG<0!VYoYiJ`E5!3P?*OoF=Rc#~r0Dys3PvxVD*B%d(~kHNYNLI){BjmE?1h3K9y+3hHWb!-yE# z%1Z}`8Vaz$X!I@r3AIS*J5|3-W4pckt~{NCUSt3$W=d@Xi_T{v9PMuE6QPN@;AxlanWNFiL z+DNn_Bud)%kg}Cxj6{@H*}{BgFk=`qcKzP>DaL31_`)J~HlVX=$v> zT4iWp$>Eq9afnNBF*eyaIapwZwzS6VY-i`>&YjEvn7sbFU8yMnGo)or(@y1N9L*|> zPxH4`)izlemDup=k2i1Me(d`xdaA5qq>`wL4A3~pN!8fcIxxx`8)|E4X>fOzr!Gd= z#gN4&1FwzZ6u3Ia%m|S;YbP9#+RmBpIp0#5AulhB;zhEOGFFKlZ(cmO|Mb<%qpO`v zm>Mj%9SOC+{{GirZ{EK9+}GwV#{it-aOgF-w-6L=xdfw zuG>=fnb&^^l|&!Nj95NoZd>x|D z!S-y+?%XRqZS$mj-auUYrLXVf+m|nTua`zGb1=|VGj))XLhV0o*LmYY{y-doP@5&* zoy)EC$Jo-3XLTLa_r@DGnEbJMVqs%Q%^YoEVPI-uguW~^5P)R4xNylbq%-#4vcKq5 ziStXeVLNHG%mgJhy}1jdU5T9NBT64hzQ2%PoTZ1E zlo&XY-HcHC^{@M0mZ3IQ{hAggx@P94Tt1gg*5PJ~DlE2*4G)5OCoq{21D!E}!JF#< zW+>O(%+!K|-IXcn`BvKcvJgv6(llPYXyWANFCS3QMDl%YVP2`zM8J&)obFW|{xn@S z>)&P@8R?N>%g|WU*vQz@fzKsy=87u%T8KvK=;|358nd}0d)z1_?0K^w^#r3gbhd@cAt0ll)LukYD6X@W$Z%X*5txVz+BF6(W^nm04y+ zx?0$1m}u&;jI3>VR+G?gOqDry&@piB9TAqB?P9BnL^{eEh!kKp^YRPuTDKHD3@#hS zaiyX1(J6dWw(vgmMkBp+8r76o0p-xK{q$F5h zm?1PWj%hT@hKuRU#?i^%mTv^_kpKgE>eT7fOeM`3TIw@d=H`}cYXeJE0h*#?;o!SQ zUP+dZO4fqDQ6K6H%lA)XDyc|8Po!Aae5f@{vRGcw~^7?2RzIQS+4Abh9Uw$^;Ut(rozk{oV7 zX^I@Qu4a69YQsbrCSzLgyP$9Q^|aZjDk+cXo`SK4{OEDf6J+GD_$M{!r_Wm8jKlus!W+-=YXuLY_`=*rs6&&MHMwYE7#>Axm(7os>{kqNt^oYNsOC2ZTy63 z>$ITqk;m>ZhCdM}%50QRH8r-5P1~e~WJWWl61^OyE;j=kW&~u_HSCdz)(ZQr+168; z%xK0iCW9fsJHgy##$Of`d$5AiJkWkBY_|5e5!A@xQpzlkxH=O=K$#8gIEL%!#5rcm z<Bnr&o+4ekAdcxM8R3dSO&>j*>Gb?MJrOGs09*3okU9Gy7_Ed)I zWTpa>NwPGVxULCLI#ya%d5eyo8B-TY38@f{mE`)a`OlAHOPoIs!VLQUM`>%Hq?BhGQ zo2jcXl^8~=!_!JGpdiJs5HCje4R0zfIdSCpOkgQr6y{lN#_1f+ws6Kq(i=ium|>%# z3&uK5GPpJ{!e$l>1#QP=F1Y&~t`3u7y#V6wwd+^s~(eKv^BZ( zO6OA){(SR}sFps~<>VeaRHcU)rOHK|-{Jt2YMzUeL7XFk<%-bF^(?M|J{QXd8y?r% z#)e~QX^P^^2%qrn9c=Bim=@4edF&v!@aeoeWFCQIB`I@(W>Hwiy@rsd(! zsQL5zpI-p>sqRGf;YbGrp;jo>;z$%=pdpcyV;nmohiv1GmOOJkL%tQu%-qrnBz(R- zrgy9>c@BJR3-s`IY~5XE!L-1w;Ys^)YJDf`PP6e}&1X4l$eRj|U2N@s^4sf|FaLrn zQ_v^zr9La;FfT>KOXVUA!dWLWLf*Prk$nYh9GoQIYAv)teA7_h#LU79D+xO&B%wjD z<<4@P<*3J0V$PT+jQuY6*{~xuB|g^K=Aen|Zg+d0>kK*DnBx~(9^gRg)gOO;`TX(2 zd!(SaoN?$7j%9}_*WIR?Mv=U@H(Rbdb8D$P3jx^LS!kkTIm?KJ#ibFJ%w*MPfmj}! z%ja9d>lm5~kln}A3)JtZ_+24!i}yxsh>37=TDwG<;SqnL_RfPBFG+nwOj@6a#@+r*N?bJD~r ziJ<{`9Wtx>Z{D&kG-OA_&IOw<9A3ufuC0&%XQ%l>#if_lUb>!JuLk?qWg_3bl6{1I} z%<=G8yae?w0)m30c5P29>~8fmcb#LYz;q7Un^)i7`}~)mpCbtM4)WgNA3mN-ONq6H z0Bm$|D@Xx22}hDa!kD32Cn0TZ+nsEXxvbH8eB>(PAu}P_>~lC=q*oz3D?_H*>?Ix^ z%U7=p3XX``?6cBmjg^s&gD#U{;u(}&bh+c{i=W|3AV)`pWNDzfG*-@dQx${mul2$6-~{`^eFlB)p=`1AZW%!ia{tkw>&@#Ew6#HCkM zoI-BQsxj54t19C4rs=ZGP=uLcOIui40tiB7HYRW@TMdRlX@nvJHEu20dX%A@k)g4f z?sOG}iE0aZyoLU8$I35)Q9)gkz7B3%!=(%R-OQc6!jg_qqYsOS`L1!LX3{9{lpe)9ckU7r|$*dry(_~c23$VY`~Ltbtw&wI1g?3KZ3 zrL|3{C;^_uPmgmwo!nfkXZuI*j^CZ)EqXv?6sk0i-F6qJ>^G1E8Rw-pGTFu!ESy{# z7~mvKM@Qe#fMsHe-4D-(ZEd5g#8BL=q%JQbr|Y`HbE^w)o=?p2^Nk%OGyCITU;Ucr zxnSO6pQ!j~L2T^)7zv1pB$sa5os^Q6Q;JjeJTM8fj$!cfGBeUQFvd>X(u!?rY+`C+ zVgwj$?M+NAY%R4^qfnn&L4Ja)%mmi@j0E4s%dE7_?B;rh?>(GXM3u;tR+i=M3RoQw zowz3^Dmo&3PY^UPGbYvFLbBD{*Rh(tL0A>wN!++q&%tgJy6_$R}2>O%fh`0dr(9yVn;|^ z9P;gsw7&wn2f7r>3oRw3c;M#kEsdv=0~Ycv%}mWLaLjAPLr4pGgGd%SACCo|tNp{H zVv>@QcSmjY1>t%YazW1XSQQW&f291SA`sX!Pe%^@>N zGfB3J+|z{pN3}||wcNN|bs|1!nX`i}&x+#MLi6S924=(j#p|{&pSx(8*W!h4_71K- zl)xXEE}h&LukhI%zH5J0#igb@T|`L)t03g~C4bMZg!pJu;3jxI4D;ch1AzrZr^f3T zDgcIo5Z{}x)t<^DN+RS?aYh0+WGlkaGA2$tdox6tSsd3ze$h}8*+VHyltdv!NmN?h zaHH++Jwk0J#W5Z{dX}4zk`P0Mhld7*$BYJNno|QO2W^IMcWya=djvGpl)3R^-I=1y z1AC)GgEoLQ?zv=vs}pj$@f@5Nd#!_@Yni9#0+d5rxYTQ%P)T$wzw%st(`~RD(1xHQ z-$d}>B+=^4})y4`-46j^wLM2lcD<52IH zl7yI@&=Lo4+7!A8SmK=1paN0Nqre~oZ4k2w-kA-u#zQ65^kZGksfxlA$BrCIOHEFQ z-x))sL_tJJ1St_Hf|0TDNry7?%BwCkG&Z*q-3=ky3+p<7s6F=4zw(p!B<`fbLPEBN zr9krlFmSu5=COp;MdTbX3W#k~hi+RdxL?=mFPyJBRapiKZvF{Ki6CG~OaKcREX0H9 znI|A6sz#BKn|Io~dZ_!uAya`U0YEZ@w*BOJUSdKVg(7_0HwJIjO%*ZZoCc|i(BII4 zgf&WRYe5Qk$R4^oA+I5a_E%BL4pO4?L`qarTvS9AGKv6t$1^(1qmob9~l}J zymiY)Qk+D-oRI7YjT8vN76j+Lm8^%sDF$JT$VMRSfuf^F29z$0EMy*qyOM%4cOkJ6 zc|<*?9!-K7gcL7yc;JEW{`lwo#KgErDpC-#c?;!EszIGc?;>PF2B08IY5`Fhq(-v# zCK3x^fxp;eLsTwsXHf0$1DuROIHrahkPlQ|0jr*92k>vG#sOIH@gF{T+?$sW8xWflF!__aMC7@ioJ&JfCdKX*+^?%x-f$9c|EYSC~7WVcQ>_z&(*og4W{#(K{;e@t?c1IDSUyCRyG&CN7gt(7_ z2&j|;g~h#YLC++jND!(L&`*WW50gOXheZ%}Kz#EkvR4TbA0n8+BauJ7kMk1}_QXYn zhHeVjwiz<1F{(9W8A?{YL`8H*6qSZLhJpSDAyIofh1yBfT}j9nh3$u1;KA|UAITFW z0KlLpXg8t8gA_}7BzOq*2n-@>qTIcE;&%x`w`|?IIY6M!poNhKZR;E{>m_VzbZA^P7gjj$fx-Yz{i3AD*X%D&#+72%W z_k(x?3O4c~cq9eo5|NS+^$?zgbYRkYPx9iBX*7IuNHB&MbYL+?H3}HqVl;<4dn96W z4Q+WtW82A_ZC6_0e)y*+`u=dt3}n=y9!Wh$;T8h;^fA;SXhs>lE%L7L9W}^=UW31t zhN$UD-p<{z(P7)eLN;yMY?LaZs#zx_UN*1NGdT@#EFR?3qM#UbRV9Qm%F7^}J&7`;XU|G<4+}nBP~C>_Zo^ z>n4a_iGUjOG#b3El@t>bYPcX7!?4+s89{CI^Ywvl+Q&mWS%f*=p7l<oHYY2}^N(le2(m4*a*9ie3X3W#$}2GOSDdUYDK0H3 zC@d_-JdD39tE@PE=1euFXYxN4=j;s+@k8ZC@3pI!tAU=Tz=+O2l8(wK&>7`rLl%{B z44R-5IjCk`KovTXV%C_>Q9z))^kjKaaZy3;@e_q5g@xs1#bw3$dHF>p5d2``FGfHI z(|=x0US3gNQF#fftdx}IrSIPz>L1|eyLRPLHZt8SEWJ;rQ;ZeZ5)%@86L`@?oO6}B zB!)@-Qe>?Nswr6a7h9;QMoolM=c>+BS7S+oN(rcCfIk&wC(BC9OG{9EsG_1QA2K7z zjq-C(r0=DI0{z#oTeXCR{fLCgP0ZaLf_7=FSBQUiVI(J$F4V$ZBHeRe3XoE7L4Q_v@ zJ*sUYph(S0{0;=s!pWJ`=Ywc^0x7J7XTr?W)_F4(2^!Y>ELBSrk(-M?L)MbSaH62; zAWJ;Vw~_=6V+9%qYp~(C84+U%+D757olscADym~7#>M^x0M)@D>nIfUx=TKY>KvvY z+YlJww@3-1l4TH7NdN@5jJzX)2O0)IL8~YV7z6DTC}uv$sWd$N;$q1^^#+bmndFa~zW{A`y5IN(kQ$S--Tmq3|OF>Ox5T)Y$qB4@lCsdgutiF|2P8TpotiF?DqWQ6C^MiBh~H$)4y|OF9d}qEQeQjYRbyVJ#u6 z335!NEmzmlH8jBqsHF{(p3QchGjHi~FYf^4%nU=y1LSm0OhskkwEc$;9YHa#Y!Ju@ zExw>QFVW(z$mn6X-|u?!8)0>6mFbQEO6i9=ywQE_o`3AtcYIWBZ=e4?aCNKvZf$PkFc2%7! zFF2YIx@NAGrsDXKVxpoV1DEJMbANKVLhK(4nYB0+y;doX_ z)fEyTyxw-F`^nGG0Q2nE7r*}Y>NPH8Kl<|d)0e*g{%T*&HJZ*CCpkiV-~on>keSSy zy)ODt&gI__W*hYDmKT?$#*~rz`T(LMWTzO-@r^oA@#mmp3jywW@l0o`P8>V3zXb@s z@f_di{XfzJ-`969(9u$Niu^b{0DKVmlZ{<~f157+zIsnrzTuRKQX|Ch0N?CyqYb}n z>zcV--AS^eC58t7!S3KSZfp%j>5*dJ+22K*{k?~GZ(ZrgnleFB{5$*(+VG#`rbYQL zV9#VoNqmP-pa1<-nzeV^ayuPm>EYkux6o$))027ocC2wUm^6OGkl;T#Q+P1aXO7Vn zSu+299e?Pv|9N$BdW@f|$yC{)vH#`SlJr=AH`8eohQ|Ka=Sz>o2DlFy{-3p_8M`*j zHJ>gwH1>aPDm@w(INw5TqU4bHU*EIx%=pcVI5Xsj2><=r%!I8TTutVf@9d{<{Ofyn z_IPp#20*2;-|(Z-k@l9Z>+QLmeLGgqGEkKs8vGA+d1;ZpbBw3Re8Z=6{nPj9a>3!) z0Cd0!-{I3Y{sO-!BW~jYw)(`O!GB+0a%@k~V$MwYp}~K5sf=_$YYl}V!mlsOO4zoP ztH~G|{PzuIS&7^K@8W;@L1@3tP__T3t7XU0{zHcUS%|-E$nd}XSb97$cqsA9jwb~# z<7o}_KkWab_t7~1enI;u1$z$Q%LwBSwBp~W$R_yOOc4>pU#1v2`$o};-}ml9X7b%08z{UiRXYUt=KNTKun6a0bx_YJ?1uGK$yo524%{(Qr) zqYwYXr_0B_jlW;p{}O%ppKhP}d;A{){Juu4Kfca?LxJBk#Q4qM&A&ro{{Vjf_&d=5 zY4pFo>*d+W!Ts~!K>wps=)}Jbe2l+3ie&zgRyA<+rc&zZ>wkUsN>3z*uCV(Xe=U`A zg+~03KV+tat+fA!KWj}yGL7RODU6W>eBFWe*QXu7?_qM7m%Xl%@cpak^H)bC(Q1F+ zm%{B{4tfM%Vw|#`!>S!gwKTqeKs9Y&^$lOw-YYzjX8hjZReU{Vq5Vp__AA5a#(x5Q zec+2oj8)RHTN##ck>2)y61@65{1u^e<9B_<7l-|}z&}T?{k0*hX9?S1TnhNhLlUZK z#s82Q_7D83{|$P{k@pUr8ta^Sso5;+N5g|FY@?3BZ4~U)y$h@ct+N zaohd(zt~=u`)~L>&)}5a|NgfB@f|wK34VY77e`tW?$T|$OK5EW_V4h;#xQ1b7Hx_t zqJ#Z1&_ZqH6%-&LNI8s0zuh=`4noovWozTpsU_+Ng$RFO^A zUu6A*0GPC*Cfj|*USuzz=X!PNTm|-Dh`$khQHe2fQ}lT*5wzj|UVpwkbI%qJYZCt! zK_>2TjOi>+;759_N5ZFub2xwwT)>`z^C!Fj3CZzFGxXNdX8-Gkv&Dy^d|gea$r5}~ zvEgInCaEudPnZ3FT&XTR5V6L|aME~!j|Uhjt*FLDqD^|PXO~XrrG~9^&{r8J#D@z? zG1Lury`u-d@6)U9f|QUI_Ik=+@x{j|s9VfV=z9M0_1kwJJ`DOu@Ub#E^gH`S#HBC> zFWwk`;>-`N-4A=;4t7JlD#q~Z#G2gy{zZV#)UsI?vag`}hnAk+p9cL!ygP;SZ#T1P zas%&QTuM=!w><1XNzdGRAme_${ioWjq~Mi& zeU*Ou(f-8#&?LHIARjN~?x)txF`oQAzGq0{`L7Q+$e$k@3J%ABfH3Xf@oC!we7Rbj zv3v7Ew)(%}2k)h84}t^8%)MKeSkD|Hd<0;H1OSbp!oMyIz}jejhcDa%4u03~_v~9` z!UM=}_+$@E_Mmj(ck~A!wFc}L#vrf0?0m|EH03RA0B^?rjJWbd8?*hKS+sUBcew;xd2E8gg z>FglSyWei#buvHg8$Q85kmgr#Jjr_2A^DhPhR{|y@{t)y+UIB@6T zygIttzp+B-|HA$!COHA#w>mT_tGIzK{A(4t2L{F;q4!zN4~RaPU-1K7_>C39@t4?- z%`a2a+9N1FqvUkMhi^S}aDTXo0BFQ&;rNfi51Wr=+x8wSuDDKD45q0J0XQ$={7dk) zY?f}>eXy{TF8F(A%RzuxZcEl**!&RzW&oA(^We7mx4VDttf?wF8jk>&aQ!Wg?r-Ak zwc+HqUl`mUKHRLS!SEC9KhHvq%s*nIWK?yyb3Ie(asEFWt7}db9Zd*cg#ZM>A2wpF z!Zbr0ehe-6FB__>PZnpTP<{)nHA(yv9Z+&SbGn{>*x)vM|J{GNSW{hDl$9D8=;6Sc zB8-0^{x=$>bCd&Uz<+eU=1gV5v6RTbWsWB5jQ;qSsQ3uUak5ft1|9!%_t_d0l+Q?_ zHY}ZGI#X%!4a(;?e+u&!fBf|6 ze<3FJ{PGz%esB?P?%Wj@w;KgLQBQO) usqu*m>FvnVlE?k~VGrV2ap7sx@w7*eqPXy(A>AlQmxK&mLpRcmbPoN|IWTlc_s}(T4c(oW|GoF& zJnZvw_SwH$d#x4zT}2iPgB$}12?lw6?ak%*@@{+1b(2(ca$P($bQ_6^WF(u)@NyiQ*fR` zaD`WNMO0>4QGHq8blJgmIXGgitEZ!)a;h3K%&XYZ+ScFGyE3y-%WEqyE4K2m`!X*|&y>KZ>( ze<&*|8|WG6Xz8r2tc{I~4Gjzp^bPcM^|Us(Ha9jmLK~rV(0H*#AB8NS`hV)`8j6a_ zMn=YZdInlrI&pFFY#u$T-+$o5O(Lf*s%vN{Dl6+57-;F}SXf!PxVlP8OAU++{W3BB zH`YfNAW8O}l~qfaUW85jr>tF=TSIT_;M^EZfigpq0*!?rZ;lR6s4Baolxn3t885AT zqKTlXtW`~*VXR$HTT1dkk)WJtAq>(2?;Bg2mT>yPCc`_2SdvkY)zsDcnya8(%j&b_x zY$9ZBqO2+}e1KTdR+IUzEUTv~WU3{hB=^l$i|3oD;7O%ourWu?U*kw;_1*56>v(hv z8RGp0hn>+<8A-8?h0#%HMv%Mp=2XYV^4z%}^0^Q4c@X-T6W&qmmm+f-erAgD^pJBe zfh1S*8 z)zs8fR8*9dloS*cfWhGO^z_u!)TE@O`1ts^xVY%(=!l4j(9qDpz(8MLUoS5&4-XGF zH#cWzXGcdzdwY8u8yibYOCS(vW@ct=Y;0&~sH>~1si~=^rlz8zqM)E4D=RB4EiEA- zAtojU000C81bBIQxwyDkSXgLiXvoOOu(7dUGrx!6Vg?CG97$g4+fR4o!#0E(G;(%-XY|sb0dI@en^RS(6f2+-0SF0s*Fg-ksuC3n_x4P3 zIcMN-;f1Lwk(C9(*@=k>!&L!Yc%Q_9&2(Re)v{Q%@^k~ZnNM&~RTQGkJA)yX&wDlK zK{Df;W>O;{AR^L9hdp|~oz_%%wcLG^{qhL&dp_uRJ`j7kgJm1t-q;`Q`NupWR$rbk zhWp(@eKN`vZl`E?@0nt>(2Y_Z`pQy1U)<^q8GY6Y{gTcnxhk z{Wf=Xb+z+-jfkUViEagZReAY!)r(eH*WC;I2_?AI%fU%W_|4dZDqkrk_qLkluV!sl zB|(6y{`vwP0EgqM-wN+i^7=sRzd^b^)du9@u&Xfk6w(@i&3|?=M2eu9==M!jBg&AM zixaUI!c5CQ5Z-U(QeurA4fTu#$r0)zn_0dRB<5b8m~U}Mx2m-b%-3$JDp*Ig&@j|R zKG6OPiG;n~^%UrM?{M6V3gFlW+>&`@@Hj(F$8X-8K&YA>m55}3>et>q>V&pD(0iqwdu7p7!;HY?=|Hqz$tB?dpt z@#`jMlibHVdwBopWb+kWT7{R6jau}Dfb;8Y-9=HxRDLyefzWs?Wgd;6Mwq-GR31gi zq$reCNt%V5y%aH@9*Vx#F_{rSu*R@qLag!6kL&mA$9)Pexo>3MV&#b-Kd5V(y-Z34 zzB6l?BXC=gJNFmQB|LIC7b+46J}|N0+}a`o=rqhuGn=`>Tc0A=JaX=L;c#~_dRG0p$m8!dIU819;^=rqDx#34z536?@wmVFHC{* zM2ez~*SQ4X+R5q5gin%3#zrZFye)iF`vpf!C_hkNkV9&!@(x0c;*=!LDmH2S=%X!y zI6sI7B2O|V5n%)t-pwp;s>&_Xn$npC*3k&#@mgX6<_Pn;*b^>`xsMseiUGJR(n$AfO0ZFkN)sFw$k)#r%2~d$9g|%cl~a| zRd*%1QN+NBugi&Hu^YFcCe#>P(#KB6qs4P`jTF$4(CJgx0r`=Y!p3R=({hi|E1D{3qN|FIgj}oy)I&JB+l`7DUQoF|V(n~~Fgr7-A;eD|GWGHknx1BkoeS}2f zEp3dUKUptb-B7QFf7DIy)-E)sXw7OS|HkJ~iFR{#y-}jQNtna z6YfF&7t8tp;TA6izvPPV4SyJ~$R4hjM0#ukA$6g|eIQHIo73AZs-vyj)7i1U^Nn(S zjoVT*kMVF9;as5K2^Igr71ITX`_Q`=hS!oF;1k*36=1l;rS3EkM7%cAUSLnwHpTGTOpr87=`_6M-~UV+L_k$Gm+=R#ICP4ByAU{*lmmr2?^^w5?Jhi^?`?R`a#KgSTln(6vF^-CMSss?#QPW z*&_TF{NNvEDcE!VC6)4`%>41U!m3>0Kb=L0JRA-2`^0Bu-bNWfq7~y0 zc}KqOVg1{Y4+ZfB0OKK}dol>kuR489=>a8nE9djIkx*#mw%D4G$BW*k-d543&v2QN zQIp7ah;?N)hXIxkP7hIqsIl@er5Bn>aGU2I9`HtMoHtwI5kpUygRj?qO%rwlx`4{Pw63ryP8_yZTu!Qpue{lot8C%IXl}uniy?o|0G0hHEmoL6-oAGDD@t*?GYwir!_D)p$fJqWtf5 zR$zZ#%6%72e2{q;>_Y3S6^r=%o5>xjh?MP)0Z4I>RIYQoCLG2*cwowA%Y@&R(iY5c ziR;z&h33ohI~c8}G7r!c$~=ahcCiMGi*T#z^S850Lp#5)&z*K%mg6 z`&cdz^LlBic_RK%fA2(C$nzaI7W*BBcB;#zl0*CMYsS&;8ki>mH$M};b#>*g_Od(U z4N4F&-Y=)N%OK;}XL!&57h|Hj-U=WuSQend6tWMm-LsV<=cbN=R-#V4^SEF?XNzZrgDDCOKc$=02! z17lsmkBF&-Iy&K&s#lh*iN&D$sQ4yxvDSoG{x@+8nypTR480So5NVlaF0e&-uPiOv z$pc{aV))?IEXreB*0IsCUs#auun%B1g8Z57qbe0lzfXYBy<*1Z3StPl7 zPrg9&X$<`w7yeJd1<^w}i9A7XL;rGfQ_rt*;Jc9~MWz_~6DmeNSfWX}?QHnx^acNAlKBz3Jl*89_h9LbHo0`2B z8)KplqN0K;9W^G0hiiuf3|t}M*;S{MO&rUEz5S{psn3^?haD5b|E$A=ZpsSjPaRey zmpHNHF=qnT76iPie;VrhI1$A19J4T)QshYDCkux?>|ca#24?jS%PsFt;KQjc3oV1D zS(KKkJr~IO&>ST(VG-=ek1p=lfGo8XI+1KGzw0q(k^>Jua9d+m1eW5dQaNHrGx8i% ze}V*@xjK*)wpOeSZjw`Fvb&+R6?W81i`RP8kIdnG=CIRyz&gL71EZ)0+8Crgh*-xQ z2qjE4&7js~i0og-D;t=^|>Sa1Zs+%1Hr#@$6-2M_g9$A4T# zksaog@vJ-NxgVSoXU~@M?qcQ3T|3qVApH3i7OL|04u0noR|%nm0~@I-UT<#-N#3n9 zdG73$hpRnLkGOLXeGP0F3Qh7B28Fi7$hcSD(_$JGpxD5D3*WY!qt`~`< z+J(b@`EgCj^bg0Oey9aA&94K|B{;pR+7wb+yiBN>X31JW{1;%NVo4kqgCWhhsvPj# z-0uC-KS38{cTG>Z@7aMKEoiXhumIzgiHqpu>1;&`^w1Xpp>{uOpo{U?)CJN|{0Hi` z1bk=~=#=RURF@!MT(sx$Tz7pll^va}ysOgP@h__TxBgi6#Gw98@gkw(-ee-z7mn)_ zN}n)#EbsxfZE*O?>WO*?PWuj6rHnEsU5e2e$Aq*@AjYuf4WoMeFpU#8DF2(R03yzD z3*PcK>14XW{rGpQXlOPA*?v1r+3oEn$XlA+@)XC0zqw&!9by1TnOKNP)% zLL&{@cFyXxLj(>E=|B?8rhr|ysEYyKz<==2wFwA{iUvHC`kiDkN8jyS?)LD7FtbVd zd4^WNlK|gG!l)xV$X1EY0gH`F%PiIu=k|D!eMDKr9J4XVY@Qh$NU-Yyf#Ani*JHl9t=ZHTkuakuse6Oab#F& z25LH+IzHm!8lFYmb8$KOQ4l68>tl`a0rVKvc=Q;p<^F7a4ibs){wpt2-wj$6=5SzP z0KTcvvL%n@F)a|6nv=?{s{?(IeUM_wDZdY}LUR-@&#Acyb-2`jugJf1aE&b?$D>8i z9_><50Eph6sn@ux#Gr0cUpFz0$u;_0?d{>Nat0|gja$2uXW3}_xr`1&_sW>@@P_rl z(11mvMYm*M_JH#ZbjWiqy205~s2WpMKe+O>?+Jt_?J_K|1D8XPZ4%6Me*w!~}PGXF@KADAmOKArKMn;1g+71W*0ra+s;Y{Cf zp$K(tW%m4yZbvtS+bu_wg>MAjZHD#W3+RvIS}o~zxECje?QsLIKxXGsU6m&cbbMe% zY>&@8EAO!CM`a!xTBqB8Jp0CMH=ntE0DiMQ5_bCBHD}a^#ksb@JH*q%rB8=h9ql2u zPxL;*(#CwR?*3nHiGA)r50REx(~NGMLx!m9<}hv zh2pG`V3W#LvdYk82qP>x?nqgGbP#S%Gfdt)Rvp^3YVJ_UYd=i6lPCWjtdZJNZY*3e z0ONjYDBf*?C@fvuge_fP9UD+1TZ#wW`IuR0-f*+lK$~)v8yQSlA{%CzD@Yz?4;AIfu1^ll#w45ux z%kBF=#1y9Rw zuH<^>Sb4Y}+&mPdoeL4O$vj7QRG>*(M^|FxKC$WjO$@Z@ffF=HGhZ}Na@v?OW~~( zEQIakq<+x@(Sew0@cQf!1QKvSNpO`|@r|%4y^3aT{x07Z<$_1m+cW*8`Q=r~#aBRN zVEzt}UqS|^N<6v(ojIxf{`_D4j8)3 zjDI6UDQ#0-X>UkyELA7JM*Ud1nV5|baF3{GT*insv%19Pl=;~(LrQmIrRk60aWo+b z0_q$XSjNl2%s=Gg=KN{U`v{xTYw=Rm#D$7vO2hDla)85$i7A|xt09QbGE7vQ7I%Dx zyDFmJh7#@Ht7IHy5x|;=vvb7xf`dBH#0Z#7%AJ|SlmwvWqdZb)G+!gFx_|t6$=i7wVAvCuTlh?;%=0vW^ z*HQQ0!9cOns?*H3j#eBSIt@dD)jtcwMVV%QQOlNc`q)>%6g~>%^TqK2ig0Z`FTFCg zD)Da`G09QJw*pQz85+h9F(N1Oq+{EDOc&AU4uOd4yFk}+=CHMA7r<~I!rLMIL@;>| zD^j7dyiOGC<_i71Y<_uIZp=;~36=hEyD&_l?454Q$Xil19}o zbmq3dy8;r)^KuXVl{+pfS?l=n{YWK|)LY?@_bon=Z?bB>>zKG&m-&MKQ$wlT+{@VC zd5D6h5*}ptqYhzFWB?*mzhQf$v}VBxp9%hwaf)9?@mZ0720PAChfmj)3nUr1Co?{} zCIB(bi5YK6Q zc^jl(Ne}2mI{Kex(~3x7Q+ie`^J?)cqW*;2XEw)Ex*Ys$_sw2{=TK&VR8d=*m}R)- zgv-S%7jAa^&ay~e-F)PY(s)87fxX#K@)ahVt|n-PRPXy6)XK8GlH+Ush)pyY9UWbR zWq$s3e85qfuu(6>t#gk=%27BM;R!HZw)+9-U`RAW_Qf7!=IhX% zY`6IqcxDIbrz1l+JE7Tq9XY7ZaLY+Lvtj1q@K}Y1I-gq;B_3rg$6Rcti9z_syHv3! zl7}THWqv!ho#YZ7CTB1s2^Gp^kVgxXzhER+ebi|#gz5IAZYau4#hqALBG1kKL|9+^ zd=Ing;>oeAY)9Lo(m};%$E7?Yzq(+p+`NN0EFPX!;m8=A@%MnJXFi1hmt|-cWm5;dX7Yth zV|5tOhoOH&a(BaaduE>yB&`!ar>IkE8z=-YXQ;^)8@#o2yRdWhTx*V@!fu9WzA>ql zdt33>3+}U9H2n<{)>$H~eIfEI*zU8Xq?*nYC)_R~g(CT)4YXj5rY~y?b%^Xp1d<;5 ze6?1~m4h8|9ad#a7f4h&Ba!t-<`iEBuUr0D`w%!lvQB;(HR{V)oQqTP#}hFJUZpL! z=f}N-&*bsgVQ2`oe76UZ?G|$D6k4ni{ctWbJCF>Ax@|ts4DY4Wr7M=dwJR(Xu~zv8 z#%`;0?yCKBax+axG7lL08o{*d)&{?_t6LA<$g}vcbyImWaNRFh;xnbCplf?zHi}d5 z{CL&sEAhcW)E$$Ri!*02NXRhI?7#r~vN!-{UNyzwLTpn*2(ahds8cFrV>@ZwKp5jm168T0ptW%+&7Bq2<3| zq;+gOveXYMQUVuCceumTFOomo6yP)b6#&JZdvFwGv!*~vZcX$+%Ryg zJSZia9Sw;33}Zg@^X2(seY56|-hF3>W3_E)Rin)x#9(iY-&MP}tYJ6Eeo}>T4&8Qa zUOTE_>y0PP)We0TUtnHn_H_zp(}9T&Jl?xs7aYA`?2r2`Gk)jVC1>0Ebg(X1-1|N~ zchlnWJTr4`VNOG2wQXjm-aQ1V+A{pU#)1rvWzm+gGP#j}Zq$0RkLmDy`S(RkSg(O; zPfQHFv}o9*#b;Ef#tUGB==f~>Ue?Sp&o2P8N6uuO)OKEg?&kOJU&MW%F*07hx?=}WI9f-f zTDfPoWPLFKs_-z`d&vt$?|e2yC-f4VLOrNg8yfb%qdNushw(N97`_|?Tw|pf{j<^T zg+X=^!bSabM(5N(O-0+*;VmSRSIfb&b?KVG@ILU(D{?qCv|Mkjl$wZ4+8cyUz`MIS zOd@TvhG)VreXs$*t)rfY@?Bdk3%N`0gSG0bQvm15IanG-liDL|ADK-sKZ6NZb23q6 zf23&(Lx5bILQG8!v4I^)+17QMM>|ag-2RVaStp&`;xQ(Ck7Rz%Mb2`oPIkuU@0?T? zv-cW1hS(iTn}UAJFYiqDe&rKsX$6@yC4P1Bd%g@nQyli$Rx*9J{FB~f(1dk43)Pm6 zD9{1}KUb#Ad$3J<+Ct%iWMzqr%=c_5UDoB#o_?jurDXuQhj17?`P~NVlHro;pvIVy zv|Un_(#8@@%BysIT(1cna4|~U14 z_Wg#E(SRV%??m}5$s`RioKa-bbk3OLpF~kh;8cvLmpfzR!Hq?I@D;gX+kQ^-NK1c< z&&_`I&DxBqHqJdL_jebp1szzhUD|662G!3P1`}ZX1{83h4}561ffGEa#0^mV{ceT% zeMAJdQ0oB%096wA^KozZMu})yg{Y?8A|bV>C;nS zVE61Mu;=|WaQf}(q3Iq~W}&Fj`km4=*k^;}A0)6YqaU9q-*~2>%75n+B2d{RjdEYL|RrGC*ybs{W1ab<(A8s_94zA$k(O< zJ8IWl$Cv^GLAVXDAV|;AE1$ifb)__~^B3NECvnD_A6idp5+9WUy4rA86sq6k^?Nx9 zMf}1kYx^{q71_3h$Ky6oIae4#oR0^mwS<-H2KJU551OQnu;^W4pv-^y@U-^Ni<+$j zZf1w8taPIy#=2D2YEf}GM*3HH@q_6_xvMmqEMACA`18?XJ?aF0OLPGQ-;EByqP?`a z!0LvlT^@ZOfr)lHqeV{d2@!Bnc~ukaA3nEoVs2y*mLt)%d7OWeGd80 z{14BE@yYype{6SXJbGZF@a`8S7;A}t_;_Ud<5~9u`iaU%fB>(Qu$+3yL#_tL7Zn%R z);)6aS}6s^Xt3rP{?IYU==bCoY~4%V`c>Q(*nhi_v+~}P5z?J$?|6o+d&&x9V~xQu zV9K41d;6-%Ip4oyXrFR2m?~X)>3QLrlc5@@+x2#J67nt4r(`?(@u`DUEJJ2(>o9%p zR&wuF56@J>M$GHR<|I?KO9Uo9`FT>;W0q7Gj}awsU`e;y%5hV4#iH!yZm;`+VD^)k zuV4DtXrH^wwVmU8wL%`=XY<|d;W^?dtR&rDam+n)s_4CNt!~=gBJA;h(^Qwca+APy zGUZq+G@9g|DEI_9UzvixikIdgRPW+0M6-#@QSZ1kf3}NT+TiCvXQ*BOKs5^h6E(Vi z3j#BQK2zScpbgsHn4EnmW^e{61_7Jf-s)I}*{25OKfFhNJ8>@L=kDk2{IYvXPpxah zpLfeevkcnOs8_F=^VT{Q^=I_2tF7))h?NSxZvW_U_5pw{mf-Ubgqx_Yj#5@~xP)G);ikc*ZY|OUK z)P8*TjbEi;%m7V_w`ipZd8o&mKM*|l?a21VLe6IUuXA-Zk%!!`!C8S(3H=1w65Wyu z2|1*CnSBXySfn#O6YrJ}TKXeMB@Le6Nq%~{*v%H#ji{SJ>CL`c+ez?c^X0z2Kib2tKPfupwc@%wg5-taEgZfCU}T=Sn06K%%lut5cNr$tyHXR*@L)SQt+S}s zaHk>Js!vx_tRlOiFM(Rr?WgZ-s2>&VK6kYT1{4KztL*0X0*wJ!c^ch4a=KUIz?6p3S0;O=2#4wA zv47o|*2VNVdBo?`H7Trm<$4ZdvGqaML+h2#P1KPK^bByXT$D8=%?aN7McVC>#OH#z z&sO$%mI178l|hNYI!^rMx2y<=8TH!`L4cm!3@oYPOp5wH*^Fy4(=IAIjIqJ_@W9=Z zZ9nkzCMH{2`ZPfZ!!?G+o*-M3dSwbIpF%V>glRKl!3*Ahz6ZUfOp;X~2lY-)Mw=3? zrnGxM(R@9e=-wo-S;ipG_j#7%&BMZE|B<&fK&8`cFe?6;+^DYfST%N8X6tz~RxGL@ zun_(>T0VhTwjZ}fSRNpc22)wuu}*8iaHOz=Z;R5|>78GgrZDf-0U!RWFvDSi$$7=3 z!7Q(IU?M~RUy3zvnMx$y9=*XV;q#R zRa$#~O2xgi&4-1vbgJROm8w|awl%ZUrj0K&XTNW2WOS%07|Y(HJ7ImaF|6`quw7Ff zxmry)ma#NeKymGOhu^(U`>yJVYRqxIbiPc+?-7hN@4Pp=d}rATi3M%+Cm7uC#SqH8mI7%v z=cpTEPUnQYZ)|yc&~?O{I!9-;3FL9ZV7o9x&%@!@vVvU2Mb}x*G9{Ce_$`wlg5HjmW{q4~{KKd`LLk;kax#F}oP5R8%X?g;vNnv6aJKg*@dqvJd=b#0MT1=htB$mT{e#{yTn= z%tbXj-e$JQ_7r)faKDaoRcnB_Vos>#M(3P_trkHv<&ABOw z2IVEYFCHyQNlx5$Ez_lu{h<8twqyO&y+Hi$ZUMr$9FbXvamw_EHYal_4ag0F7isP? z14#mQP`sA|2!H?eMzo?3aOr>%JM<gN84GyfT#b}uLB0_~bnccQkrw$lHYp9B1S#=%=theL*G z;s$bORGU0BZY;}$3=v@yWVZ2saMW9bOe69)XD6~1At|G(ug_R;)!|)w+zQ%aDVD?8!Vr^(A{b<-zSw19f^I}~)QjC?e71A*{rJQ4WNr6A zubsb{ZUPsJhvBIGxhdRcM{#}5PV6zK?+z20gz$7sIfC;dkNZyIoX=!P|%zbq#mgw)2w_o?4bYM=5O&@J2nvoFXFgWlB z0;NNBY*Nd(>5p`RyTh({JT*DPFA=lr2#j#p3S~&EPcVYB=am$EQ+x7^ggVzM{rG_z zOHmk*P-NZClD`|vd70nVZlLjoYk1zPw%`+G&E$-Uho^~!Gb(K62<}!L>Ec}Hf=_H{ zawMcVk(&1GC+jP@b4{29jU>x4-UY3bS*OB!zf(QU_Ff!Lc9rigBw=4@Tvm*IpEYvv z^@Yl&bXU6;?=*pHGA`zT`Z`_$*opJuJ?c|<{t1%EV-e6(w%m?PLs2@zfr(7}1V1@D z8sA7?atgobr^Z|B1PWZQvp$Bb#f-fj8YQdVy|muwCpXeQ&d6o4`Gcua z`}t-^qc*P>WG?TQ!Ddz?TcmW?j5xcko*28wg|E&WS|#c{SLbB9uQgg{@oupx9}9-x zl;Dm}FwZ8cl%3@$23wB&EkdMdk`&@?MRa(0i3nxuhV=?|3}N|e9zzJ+D_?ym{~+sp zkwD>TPxn3rSrAtaE0o3-zFj76U(d@_bFSiL2`pgc#f z)U>i}b0!1|w$z=cs!0W>aDXvj4t3=wHPm>t4crlJtrYuS3dpZ!l!XTC9$QFAiAq%_ zZ1?gb3@26(4OZv@TvnOj#)#6&PHQh*4Qht-Mx2wifYob$6mRRB^;;LY3hx@lgZA?4 znLuN`l7qtZZE|mC!+FdM6Ar;OnqPM>@KrZwg{G&egolR~Vf$>0`eIph*}2s=FSn=LP9%ffM#Vux*5zupwH~h- zZfTy|4UEDt=BcCZ7Z!%ax?Srp!fu33SNx?XwtG~$wM@ybCzW3hph;Mu;U$Ztd2N~1 za^uv708rT)*>BT{Er)rSAidGck;L`XI0S zXAysv4ucVqi%3?T?Io*e>Y8_(^rHs1x52*Wy#@XhSR`{hd&|-9lUQ~%Fel}YuK8Z{ zbU12G4ap1h$H9Z1GPSG@bTG;ln#r}^Tg?!pL^;@hY83w^yl1=q+Aw@nV7G&FJT%5e zq)Rw@FHrjP*E&P+o>y7R&ETMbh-gr*y^Uq`ELDM>)kj=WC}s{zAVF2{cuYQ9J8Db1 z0v?|JSFL?AAG@y&d*eG1t~%u4+~{eDJJjFlLKJwaWTV{)arPi+7`4Whh0xn`fsgtb?x!xkk&cr5(R3!3~xnik5Sywn=L_!*D)> z9idJTJh?P1sLoyoHZX^9+gWSKcQA+40W~YmFTI}%q)+vA z!bP?~H%a zdWklgfapSosH$~;dPhLNo$Y*gh21(RU~^U3t=3q+>>TW0l7lp||AC5?-iQeE2pd0S zI*b9gGxHH9+gm3Y50C-OvvcHi7N7-ZQ(eVXh zKd)8_EaCc_Q?Kize+4VzBLGoGHl+`vK3K_iJ*$)YS7-V8ZstVr6@8MN-=>Vdo*ni; zYbtKHT-&X;*X&3l<^Eh1gu#eaFXuzZ&JI=35KA@>6-cT7<9Kd615cxub_^zK|Z$cn3>ekM)Gg2A}xmZf#-| zA=ngZW`eE$pP92J!|zKJ@TXS$_W+m)79COd1cWV;Mise`&4zI<@cCdPEqAN0G8%Ck zPj&iU!0J1Jn;tpri^e@kCpWAckY5{}CKNq?^rnwAe)_}GQj3ST_e+i{t=kK8cw{VB zbct;hNVVO#F5sAX#5dDr>=`J_@!)~@PQ9^3O2$?FJ?&dH5Lg-vF2 zF_sYLAb@f!n>w?$-1bG&z=ES2wgAMQ^j)bL7N~|kSEWh^>Y}cJ{8@TTU|(Nx{yf9$ zz5VP>JbyGIUb2P+w(YXflbsnAs+QF$gEzJlt-|?ay(xHiJxd_TBsS`K<`zmZ;hxb9 zT3uHDsB{>7lQ@VL;E~yzU-SKM_7CRxN1gKTo-f*Vh@<|eGD0MM9fQ@Mc!yb8716(E zbANUb5j6(y{+*bswJ#ZQOQRf`+4r`j?4LR5c)9V*V7V_Dq75nT?HMidANGu8EH6c{ysfiAVdC)Pg{om#Od32h*z}Tta2WH_S5O}^+r=`3o*(i=^f?WHT80#e~O0 zj&CLP9KuPOf`OLf(=S*5^XB~NwmP%Pg+rM`*dlY8F-f}#7beLcqpWH^12sOQS_FUi z{_3-;IxRl~`{#xvZ`vuIUl_fCX{N}15V7<~3`$v($^sktIJvtMs9u^*n~;YY66Afl z;oumpo!t67gJhZ7Yq#ogWYO^JZF5Q)_X+_Ok}Yk+T+WpH#}A8;aCo`yHVYORSVV+i zBCpw42FCUWL91wz!8;-$qJciH9Eb~ z^}3gLn_{I+l)d-_6n#WR+PoiJ!{uNeBzu-)%s7akY(EdcaHUf);+-XOeZ6Q2V& zZ}Su{W?T*zCv#Isd#0OH=A(NlsG9Y6IYW|1o|IheC^VLdpPHB%Ja?P!D*j+sI5AXO zOr$EN1Z9slW~~Xn&b!`?eI(sGI*nyfP1g5|AeK|^8hU{;LbBvs;3wrj&o)zqxs0@I zq&;=f=zeNUzr%zd2x@bnZDz@C9n{~QodA1pg(5bE{x%$ae-nTdv1t3VAmiXs&P5nO zVBJCWFsK^Vwl&vh_`m!gjDC1FJl}HiTt9juFw=5e#-3=C0;ZUkQMC97^WT>#elSTl zvTlnQ5D{R{Pn|hv3(+~$(DQ;*D8EVa;8cAd#BS0{ZGmJR#JzfPD@^MdH*ZF5G zUr7%Ne!U^sq?$C~H3VMUY+Q#brZgztBoluvTXLt%s{l!EU8{D2E?^H%PFFu+ zGCV-qC{v~=bol*Qyx@*o*JcP25aknSFq}3eWHwz#(s8>s9KZ` z$EuH3ZgfzE!P?cT+RCPZDS8Zmho4ce#A=6%TNgzGfiUwuH7J4hfA$?Z>;Crx;^gY{g0IKq72VaEKW;$OXxO3`L?l2iS9@ zhyY?_;49(x998dflPlQcd`*wG7tsw3dq*EyHf;^KNQ&v)`?mkw%rX`4MhI?l3X?!U zUw7}A6?XE%>I)U|fk$pq@AYuS?*~nI1tT1o$nDhKj{jF-gL}Phv4|ib7Y|+zihHFH zyx&VPvMfV*GPh=bGVCd}zoCxP2zgvrU^W8zjfIrmb7SBe84N_<4}<-%nBAfXMsD#A zi8Yne@-XcjdxjR2*Su|KI%N<8`oV%>?j`L{-`uG*^Y%W(uM+nHM zWpR4`|Kr~u^Ks*+<*g!E6#>`Laq%!un#-qK3 zeNq5D&&Y1m+f)6Xk&`{0RnY=GEY*EI}$ zxwj9Fp!rK78H-CT#25}Age&x==6-GaCy4iIdP!y~;pNgEot0?N3&>VvCwlF~aSgve zC~l@blnRpv{FSy_!gN=#KcXK2Y{?PxbdC8h#90c*R2Sa5awY(^78qy25MZ;=jihr2eX0RKBL`b72W`JnpN}$wNM`$6C|W% z1iSxphH2{wPb^@j{==bY~w2jL=%K>fBR?Nm||p%GCf zNk)sa3+HHAf{B4m}iwr>W<+-Xzpz>7C zrSz7m#)MwKL~TtS68>*I2)tJ6`c{V{CzmXbb3?H-aZ&)U1&~f9DVnq>v`9 zx2RsSQEy{M{?@BKU;Swi@QQ?MY_X)HH(!b{|{nD$L0^l z_)xD(37A4XBox0Xv8AaKR53P>7wq*bmx}(o0&~27EAPGs=IW2OmjA z^e&;yWI=JX)2ySxTt;6h|AV(U*gp*5@_E_$K68Mq$e>ZmJ=Qhf#8xm4(fXpQ{gbW`Va{UGDaFX6i>KhO{3`iYOG3(#_XP_5n?fI z(u->Gm9+#gE6P5qkG-3CU%q=Z<4d$pB!U7Q`p`SiIS4PR8@|#~&PVVE0~Z>e+D3aJ zjXut2A~n24*t47iuyf|oR4PfK*&|Zb$R2$Gnh1HmKBOnM|B5qZYCFp z?q_GCDd3fV{jEkdmFBlyyu7SI`F^%ZKzfVe&*Dksvmf71>~y<2wb9wM7x#tQ+IXk_ zq6WahKntzWf5xx#6kB$*UFJ5cA^%(AT>OoE`tYz_*TcDK?J^e^luu~u#w0)d2x+CP+6jVQg;Y+&)^+q;L(Yg3!o z=Kx`!N&oURp?P~}Wj;!6rT!8@3i++-ytnC4Go1cIZfsN8y0ZF1+Ll#MxB7GHwHRR# zw%=>NOa5cs6!sxCqYy z+fE*@Rid*5eZ|iS#(Xb<-#L4ESk|%RZIaId&n6sCcDD}^tl2wVo>4RfPg*sn1!|1fQZ~(WWm%zK z4;fJ#oIM?jiWNjlkdB%XUT73ZCRCPEO;e_%NJpW-vBK#UPrphE44H4jFmx;$Hu&mA z2v!q{K`=rO?WR@(^RmxzY}aYW$45tp2P`zI`mSr@ZS%F42u0`}Z`YTVN+2U0rDhDa z&hjTbCukPDLpzJ4ukf5;>}TQGaBiSZK8xq#R@0c8>}`<);TA|wRuH9Y*>l{-D+23Ps^8;2mdU$kr)1g`dIVEbKY=s+GHkl$@oiA2D)LHSP zQ!BW)0-Vnz=_@=Z7`sS3Yo=AL0}?5FySqrJXqy@+$*#?mZRS)!%>(XuGis4yM$uBD zM}|6jdd9~|>6lwuS*LV&#|Q*2kz0Ts0?-7kh@hQ4j3H{`Hh5OaZ;xkNvdRUM7&%`q zp2-oCyt@DZKmbWZK~yxc(Ct9a0nZ91VTAj50|}rKwga4_jO~PM2WDBm?!Lt2H7wy8 zMh<)|Mpo%^Zqm!Paes%b%>pTGs5b4dS zdVUQe7Mhs|O}rK@!^y8YrUo^Ca9d;}q}Zuza4;(Sed^U9h_5R-XBL%9m=++h=zFodU4*Ui z8;AE-2&W{fzp^^JM8+mg~|}?HIcK z0;8A!ZA$cS!>`)M32KI+Rh*ku;k!q!F>K8Oe*4Z)2Q@)uBoFan%B*JS8lF8p|HDGV zTpfcjxGquNc8Sm6xh!crF@~AAtzbY0X0fv@M=+PT*?i1CB1AUqJEIQBs(CC$Sp;Yin!z-ZHzqj*tOZv64Xw5o`evK6FSL_Nx zw}Uk|CWq@pf()D7Rb~>l{JE^0ynFJAv6u6mv(#*Rao$Qg2|&-;i=aDi=X`i(>NP|Y z$0?z^^9>{NZU-g!iAt~lZ<}K$#@b#Pprt~4CHnRol`#U)#D8NID;MsFz>z1FF5Nz% zk!yf}@gSHvbGFcD!w`&Pp!chPiDaa7bIL8W0X3c?ndj$#8aZ{rJda@V7S}8(Evp zOa@X)owBBq^O!K21#4?Y)#7D|GfeisK0h~!)n+b>_&jgte0a8ZClHVscU)$%Nz7u@ z(;n@@TthsO5fe{`7=$5K&bTI#+Yk+I<+svulB)cvXl^QhN+d_PBy*#Kpb`L$#62_T zgiG!YK85`}y6PBslRYv6tT;o*KCYo4tvdUILtc$)=+%Tpak zvjRgA*(Qk~md9EGZxnDFb`sI}`+GSb^pl0_^8z&I`mbYFS0Eil`(*83gg4bJ>qTzT zC<(lSipjxv{(N+dsD`O-=3yQflX2IpJ0mjiSm-AN4WOToylU$vGeC~$DrWBaQrinv zG(o<+jT4#^02-IoJlP%^(vr7b?6w7a9$$Aw!yG$<=1asg3at+&DNB2Hcor39@XURP za0bs5VS0l_`{n4!3tNG}9LxNCGkOhm6G4Z69`V|oO`DGi=MV`Sg!wermJ}xvnm|qq zFivFch+qS63aII>*P_EFp5kx39$xd;4%umK+fWT(TOp|9cfqV7gW0>|BMfHSB-Xkx z|7qv?KotSc!LKDir|_JEt=vQ3kK*JVaz5a1hi7lvkZ`Rmx!CbNS_yF1&D2}ha7ScXz2OBrFUs2|MyM$sm3`*opm z@KtvxLF4!L>fYQ(0%bvM=C15Z&>B+PD0wvO1Zw<_Z7tj8?>EiQ_H||3P~rWaUi{53 z(FTV{-*n)n;aO=E58+XjvBxCtAaY-Oai^^thmLAEZaGAw@8t;IMh zM3fjmTdt(|S-BXdtpz+wd%jeDE&{72@a#<6hG^mW)yq4&BR0iJ#+K!4q|JhCHc%{x zp(fN)v@9&k+{UsSM2v(M@tgqJLm40&h$A!&a`6QVcB;k0`JMP4NyjeKu0y%&h{Mg+ zuCunnm(Azbm3=$7s=S*DYS!I&stHc@TSgFQyb+rBzwJhS;!!d~O?)}fcFl`bQVP>?G zT;VwY_AA?3Dq%z;IwWQrC-IfT@b@kvX>`&;mzHL#aF!G@f&b3 z1deM7QmIO$xeC^&2vmfkm|}hZV0ViGbaRRlW^%_IBZ7nUa)>c~D2Gaa%AxdU`u<;z zpOYCNP908w5Tv{D9#5*GJ{&ztg$v8z1B`jT zHCL5;7)??N^vpe6H5%)T3DxP9Z-hbNLrGHRu*p{0XO9yUK4j5APp`5Hn=&_t%dO z_WNw571Hx6c>B!oAg6%gCI?S;=%;`;=S_1y|L=fj1GXpWUM5;wH7{yf;V+;^kGD;OeB0q;;wwu5`GZ*k|*05VQ$&|#`>9l7T=xOZy z`I7KlAkf3g==<0(rHjTIA4U6!*sdd2dw_FN(DoZcIcMxZ zYL2jQHJd%$EaD7i+hl3VsEO8epQmUd7B7;(?;o%rTK`J*o&}&xjNv-*OzrSf11t}{ zX1!WDNNw&=&Bto@*Tu7h?u3j@1kWByPO1l}1!=1OO-_ux8HS;;RgaP~lu8yNl+hSk z9E}->$Px+`(X?RQ3dQ+n3U_1_`|ZW(8z*pEEQR>l(KFM#$Fn(~v4$C(iA=g2|8q$u zXI1kE;^|U3inWa3h~FqqQmRiawCvM|(_<;s>sp^dNqTa5DxF2<2U?u~Zg{-Hv*QX4 zu!b!Myg7&J-a?3$8xbj*dc6YluvNAaJ=0v8fyGTYC0dasM2O<8#rn0xd`^jjyZ_I% z-^d~wvcv;Lh~c~02)WPV=aje|jN617UJjlsSgT}x4uuS&_Zfd(C;4YM?fe~o{c^-s z0g^uPT&C=qh3Yy#OPR=hP&3wSvXp2{5J=h9RQwn?`>Kd3Xr^EaQ(H624m<8p-fOr_ z+t~cU4B`XCqqW>KvQX~0ThR2)Gqg$R4%p6zC77pJo38yee`V{OSauB|qbCXtf~85b zK2*Y%7CN+Stw9RCBi@)Z%gNt@u~DRqHON0Xh)xXFXZP%Fa>%!dL%$F|N5}?OsU;62 zM{9d^cvi@dbZ7uQ(ZMQK*I3w{q#!G=F$|nd;!bqv{}w~v#`L8|&64Gf^`)62HBGeE zEMaCC#H2rC-`*nk8SNQol^(w-V9pvQdj71y;mg7EJf(`~{Hj$_pIx-4xnYQu80#i* z*|%3LjQz*IB$YgU@n&##Wt$~BkGx0Ls`~_D2FdgDRQOUeJX>ZnR(>@>4j2MJ+X|5u zD#k337c=IS?PL?KcEMzg|3+`l$LM3_kaAcwv`Qh)G{@k)a@45WC|s8W;ufkcX~aBR z=H235uot{Fs#dtDV!)kO>-3?_WVROC)Ht5|HcBM8tQPZOnNgyX7^W*c2et^KEW1tc z>O&vC1<&$D6UjMPnz~D=f0Imj(3GhCYO*cTJjokpD~ve=ijPZ-;JgGy}#oFN6!hzBxGSgf*DiQMK=|0sfn+ zo1$ln@mq$G2Xb3@kJj$njg_TE)yNn?{$|y*4%S(5g11Q0xYWe%?917#oSeDh=LG9X zFFPC99%c;gw5_5sHKt|h6vF7MTnW#Xbr6l*02+oKdDBU*G-;9*DjaN3;-LG^@becV zuY1TVs>3t#w&!QFZhLyZ96V3Yz;H^%AT&8^+$K%}`cR=_wOkuP?VCI?X#tzhc zc>nO=);^NqKOyfGxWuvJkN)q-c!YeXgKv{4y+2H7xx(A!z4^7NDPvbWO^FG=U$s zALWS>2ct0bolYFn_@5O|68YK8+m^~>a5i(85%guq+Y~{h(2&QoQ01&)3f>0D{F|EU z?&xB2!rM#$*zny#WoiA%J|;cIKB^kmTfEH_?lCk=Zx)CQPN*5Ow8;`?ha`gQgM?y$ z7{F=h9aPCS(^(q7s0Nf8A_OITX`vK101xP9k)hClumlA@*b4xLX%iUC`z_gDP4c+3 zY}1Yn*(%xSmp~gZ& zG{y4t^vIu|YVH)TcA;MEkHVj8TPzd@X?X}YcuP1}GXgh&XNWvfw&l%;t3j}VHXu1? zYkIYlzgp}&L|d;U{^NR zh!>iCJu*DnHbrS22wJgIrax1r_n-1xS~^|`-8LJ_W#GBEKx8%IZ=SrZf{5gpwNbD5 zcN0OzOAG6JhsPAkr0N#j+$RSW3?d^QdKCG~s-`#el4D@1&tu+mHDC9z0tlfPU+ke9@DWUD$Q4_K4sN zL91RRl$GFFCg10yog_#OP89~qi~WQZ%$KL`jEvkFBST?vDUD~BI4|7_c9yj5(ERx_ z@Qm{r4psP;+|Oc9$Ik!{NED|!hen=XgPNa@c2CbPt=`zDX!wy;$b7Vi8SKWUl_fx8 zweS(}%+{o9x9+Uo5F}IVg$CMSSXc4HMxGnOIfKwnBxBm)msjD|P#gxod3@Z3REG)# zZivti{DBIrG zv8*e;9mer5YcZVO|7JWVf2g;UyAZ1dKbQ62r^eshCbLRf^tERsM|%xA3upzFunvw$ zsKko0t82?ki&lN4(#UgZ$p8>SAS*z4b z!Qts5OXe!&`;X^srKOEW2c#Jh-FRpPZ!v?xP&k}}*U1!8jLE3X5VH+dlZ&yYO^j>M zYV2sEqHWP8RF}`EQEUiJP#ooLjrS}ZLo&2tayMwuB#y0LbwBP-L{^sLA2S*WHLkjkSgx}&edqPFyDtT^+!GFPaobN zAMUKPMls8bPhR0Ujo;dS0?Q~;H|!+W$DX~V`TsR;x~=4X4@xvbCx|vcdxEZI=sVqv z7W@*$!P!*?`%2n5p*atkS<1a^E@j*6 z)yz`PWTw*o3Pq^}bcDMJ;c{vjQA-Y|fGJs8SjqPGZz}8ru?!R0`;#MViMTdN&XV+9 z#F-#J7C*>MXO5hst`R%#czDg;sHXSq&ShWX{ZBe#->FE zQ=OIDY|BG=dUhF+@EZiL-`@)*JGdEMzdkG)T#<}0da3K|xQ)s7!zjpp``OztU23#6t24U&c70C4g|X&&B^!lhF? zEnEt<1t5TGvZ+t8lTjx4VDHA-(!vaS+JvlQ5(@P#omwGyyB0h4n+(qW%vU#A+}d8| zj-I)by44N3&A~Vg(R1~i!!s#=t_jhuC4XoU-xkQNczW|h<6G_?8d1SN3_~DV?Wypb zQlZTcjk>JTpkx9s^b;SnR4I1ce$`PRnXrHJh72SYav*VAy0fwKCF;*4N_yjV;z2Sc z8dOiUvsCWWYr`Fc$m|?EJRv}WinjQuZYiA6N02zZ+1`T*K|EutII0YUtpUXiY%mnQ z_f#B-amdCaK*QR&Lx%$h<}+HPI-~~y+o9Un+Lp^gqjvHNU^W8Ri(45%erq|mR6I&v zEkW17ox-zHzpZgz{H!Lm+Con(M09We@aVdW`Bo{>qy4u8hu*7j^AwcVZSshHQ$>Y( zxW>*Eo&#Xh+h*}>O0*c7r#}8>1gn?{-A@CaNi#}p+z!y4Ffy539- zU`xWlosVZn&b(f@{v7e_WMqS~h>E4`$CNMn{O(kFarMc;p^6J@0VpC!oXZ(E)jBzm zwpfgdL~%whE59#0qN4cFS$uP|W7zvq+lbz?@GLL{Y26{jlvV2@nmY+xO1>J;%&caH zVX*8i;+V_~-V(^wi7O;n1!i=P#}2+m%Dx*m#?z)(J3F{xpA0Q?bV&FHRw z3iw`BxsxeSY&4?p?&cm~t$+yqnr~?F<|LhX;~I9FdrYm$&k5W;Kie2|{7luqDXPlI zDh4AXb3eUi|LPbT@l-D70j$JSELqzof_G-Hr;SRNbIbh>&pvkF@iWpkEdcrw@ysB0 zlD4R*QW+Q<{_T%Ho{g549zQxdIh9$Krlp^(C7fI z$lJ!xA|~WYR{NQFJwoDldvT_E1AjBIFTyjjwuEhkkf0W79XG$)6HzohHsqw92+Q^D zGWq#zJfo<}T4s0_TQh5WM4J)ZL~w~K&Q_*Z^_lOqbv$jJE!GWoI`Ua?HoQxlPD5JN zEu4uAmj3Ym{ks#&0`DTyseBQN3yaUwzL}OIlP9zsdwS!qxBADJcjOo3XFc{FiWb1p zT_eB!@w=JD49+!xX7LQUt;UOv?e39HxZS@rJAmu8bg$aKwPU4VC1=ST1~bzFIbVXj zO(d$YxW5wQ@Ho1&tGt59UtsG zURj(e4^C0KNE(YQI4BKuj=kjfH15Sak#ypXYuIV-@%(~#Hq)pUfyH3aGl~*T_=sDV zNh}RP`#5fwZG}acm@o>@3{q3|y>)cEl%m7ErKw`-UZ4bWJlKSHLzQDO$S7h}tx0oBf1kH3{ap2^6F6 zQZl2d%wt;1UC+X8k@a$;)xOanR&xWi8*fg-PIFHpdA^N$=kigdFHYP}i9%f8JD2`!;tr2k@dyP% zAYWn?3xGz4HWj)C(06(~ppmk1?W``6EN2F?-F8CipNnY@E5Yh&|;n)~50&A~%_k09tl#f=dv% z%?lkVTEh6#=dTLP;*O!IVY|JyG(+j$x#dmrhL3C-ZV^0NP?u_NQY3t7ab|jQ%xrfX zJ4*Ni&M3~u$0*NOYPMMy_a4Da(}f9xE{}zqZ4W2Xu{}Toc=9M+uerhTjaS7Xnfm*9 zEOz2>4kKR@EC+=Tfi}}jn-2M262!r_*Qt@UtQk=|Jq6*$Wn{`^22qQtEeFS;*_axc z8;16XcA$19GQk?jo20B@A$uT4mV(HR4|ca7uP(_zCYrYBVKp-;laD>$E1&t>qkmHDPx$j$J@JJ-R(->@>JhHOm1B5V{I@ZzW-Dl zmZ`Uj$6_ar^*THo?WIBO+1hPPt2VXT0PSCJY$*^NPB!hCxb+3ic32hAwmD2*IW9_T zB4a0fJ3NEww@)Z*Y)j@wlQ(oNhZmyvBaAE<$gq!n`SSVW`=cHCu}Z~WD4O8oNftGw zv>P&mG;7!5Piw9dZ%M;W>Si|R);{R`{nQvaU$-HiLsVFV^DD}0*EX$OaweW-6eB{g zZTRWG;9&!_7&_~<5<`!4PV=OhSj)33eN?DeUz%m9=>Bai#)7l(9O3-MdS5PBe5UhlgR*1 z3uOkG_kpx(oSYute6&N!e)D5tghU$v&6Xc7-9VU*Kdre=Lm0=CjqYl4E+49X}kTuQldrBz}bu3zPf?@&@34-PU16#qbjiSlfFmW{ngnDMYnct9XYE< z3eTT$RH$97{)~j}$_*eKF0UihO?P%rl@|JM>>Vh={2g_$5c#OBvHN(Lj9rT5NaG+4 zAA{Ff%?sWfd~I?!uny!c+nF_~)K*MEMZWh?c@*fad7s?r>-HRGFkVnEw4>Wu;7z#)(K$b@ z(V=)t8g`m{P_+*_fB&oj^VQ=&3(x*pWWT>&H#FJvUCqwc826C z7>}~dORNx4-RwJb?q}ioOZX=|Uobz*7$$C(sz*7b>b=sS*7)e(CAt_mBWv?NQ(=RSSsL~HBNJ`zE;hsj$kt&A6{)lDkvMFaD^fc^n6j$qHnZ|%|gq0;1$ z26#qo4lSt9UzNAz%}<$2WacFNptm?sB*0lH7L+5PQ+Ot4_+_EO)15<=s%}!DdV6(g zVQy)4`*LT>8C3ap8zLA3W$!_yTPeX^iM3f!t-h~%bGSC`iu zQRBVxICjvsR|`gQi00MGhLVwW^XQ>!hJK)(K9J43pGP8b2_Sc}Vxm{0bABB|H=nuV zEy?LL{d{U3aO2&a(bC+Zv+-E#tO0aVa_5J4WJUjl$b$yqsr!VzkG2G zky|8fu+Hkx3KZ)qRu+hgoST`)u)W{6O=QW^GEo}G&K@RnM`$K;Unridtl~ghT%wfl zCIv~0gru(T9vo^Fmv;{yU-Fh3KKF6Ld{;z_75)SaWs0QO%IbFeqkK5IV1j@ zc#e@LK^{j|^Jeb|Y8QK=MHI~3IPdDRNz*RR%wed2rDe?y&Fj)Kdpn3bM-~ya@@0|h zfz?})-VdF9?6U&BclwuH5whOyG3Ae$T`cF<{l)Pt#MYuX+0VtZM9egvEo7gV9bi3J zoEWlbZiBQ@G`$oqC$z=FeBxr1jnM^DHn1Z7szk+qJjv1mos5yh!Ez7s3X?)Wt(`HA- zk7Xr`2sW854=CCMgBW0TKQoK!FkNMjhHK-E$#j~&vu8c)+~a4Aov$Bmz|Y@}XXk}< z&rdWxes|ccV&Z6g(H1C{Ae#CH!yP0_61g`!J6DCF_n&O6F0X)^qj!q!OY^e;eWCL9 z{Ic;gbyK#tmS+*R7y92)I-0dRu1j}5K6=FA>4GAYSRw-jt)txuE!<#p0K`Y(@tpe@ z!@;Hp$xpa2j@H^y&3+j0P1wUEZzue0c$Njt${gu9Mitkj+Nk5Dj2?8~#^j ze`EN0*`rr@b{e!xm+fV>XG-aqoJB3)-#{_;Y?V2v#WTl-yTOS{1D0m1oBJ$4xV1uM zK>R@%IkI?`GJTQqHfu^QVF_Es0>i4+mCC~ETT0|Bne)WTMQ^SwRHmo86})LKpJ>*$ z2{VE@=>+KJZhrzy&-Ioz+G|JkH{dw|bdki#BnPc8JEaskm<`)7G3S?Uz~)nrE|$~?8Dxysxk*-8iG&h)RX6HEEN7S9(gZzK5=nYgyKi}`vML)hldqgw~c z^I<*X!`+SQYOzOzH&;`cHn9d~M6(uvb5pw+fK>exJ> zok`4&I&n>}DJ;jX#BaH+~2{QpJ}gL<_-*>@8Fa(vrJfsX@u+ zUYAycp#m5vg>Z7%x4BHP39H_XOEtuk#mcC+mYk^@1?Plo z_VNhcq!VMc6JvJNj1%pd6`g%c6zI?!O4-A*`m-8K1%7<`r6jsq2;kh{aM;G?E?AP!k43ukh?3o$jm%s%k*6`sG2XJ%(? zio|{vi4`lnF{_x#+#uQ*IszJorkq}NZIxvaSbB1ooWtF%^{t(Q6Bo^wLNt)RKs+OD zQ(j|>?8oIrYMX8C?pr43yX*KkWd$ozo-QBH4&82E<_d>xy@ty(+uE7^vi2h-OAL)W zdHkG^w6}RF*D5wo+$qY=Lb-m=$=(_%o~E7jt@`BNN@mqg5WStE>`i0}2#pqR7j2?a zWkNJz-W0~TkA3WewfFBx#@e)}Mr{+6`fjpiIx%|7EiGGF@_TE04IfAHAZss6&ivZ^ zEP2~yzq^zh=3Z8P=p7}pM0hreP7^f8{&+q#J&#C50f0qR=vy~$?Gm}W|L~Mb%xN@> zp)U^4Dto`Ywz<={fmLj2V{e~A3rhTYceJ~{NVYc1#3r(~LvZ%>?}TRnUE9uq=ZJ6y zi)3%=M##i%&VLnaat#w17BX-0Ho@EQ>=6ydX`mUBcD}A{YK7v&B5C^5moI<*`Q_8~ z{mtq-roY~d-E$=$nAh&+`hwiA8PQ7%zc4)Ual^$Zn}PC2Di*{RsWpWn`EHnf5vR2c44l6^9fdq z#rd^e#M(p3aNT-WgJ+O^f&5%u+uGUN-B@0Ror(NEK(X(;K~iU_plVfeHz$D3c}mFx zt?AjWd&wIsmwC*MW}!D3*AC3Q=HfUUOkf;)&EVN9-JYfcq6=mRN1qm=i5Sz0)WPBn zNh{B?N=}v>S*r0}i)Z#t?iQxy#iF7&i5m~!3DFq5Pfre>APQP-H&^-Bd9jk8Y`sR$ zA@vADxAt;7o--`I_fP`k*!!7y7CHMwH6SgIqvq5KmW}B)i~Tf_yN02yy213q%JvP_ zgsN`r-c%{By<5l9d@}9gaoRz}AwzUBow$-ALZHG6GY<$c;(t>GLb)Ox)X zE25?#MG%NmG8^ls4@a7w{X)~W?vN&~%)JZ+RX>Ye;Tka z&R>OXd+P}`Q~GU9>b<=_;zxWMp4PXp1o`j?ox#gLEgwo^dwQEihY?LM_P~Jj;Hj734uQ`O+V0thsS?YbQjSxB>_SoKRDcI{jb0{}HmGlKZ1%tlmE?2j9WlyfIZSS)nFiSzb_ZqZ=^9Azr?9#^W&d%n_%GTcA zE}5KfA1{_|jaWw46s2sK%HuQ7JUhE3zBY*-?iHRBbBb{5TAcB{Ko^c@w{aYK3A{Ks zpad+!K#3=$get3Mqs&nU3Pc-X#nKMSM0xM*?BLMya<_D8Qn6(5zV(lPCO)k9`x~TI z4l+ku+DJF^D$>^(cy?2qJ@<&)(78-J$7ry`z~y@9!Sm4TfakPDEQM#uOV~7wo?&Q= zV)OIUw-x#+JiEA(0B9ZgF}&%rIl&*3)(c24QfKbN1gNRFsC zB>NW=_K>r!9+_M6lc;()T8q{$VILEyWgVj_e9Yt7%iYc|jcu&&e~8+Zy<4jEC)T># zdCbc3Rx8~W?j^mlJDIG*4bk@Rhi6Ntv6)UIA2^$!iH0ZYZ7+^^u_4UFD93^b|3AqEHO&gz8*N~BA%WJ8HApb z2ktmH$U_mqsZN-}^Wn@mu?x}zrv>^E&Y6Zgq?wSa@5~ z9)rn>yD2bK7^h|tZY-9bRWTw^&4SWTl!z6aSVFaq>%@*L@svE{v-V#+w*!GX+XPz#I7M>%V@i~Z`3G$IMac*XMH0!oX z$=pw$wc&-{>my==z|Xy7N&=p$Hk4H7AaGv@o?i?P{{QT~=}ueQwzhkgEQ;HRxCPe! zq5vQ8D-z02(7Bnj*(*vU?k8{oGXWb2H5i-P1$A8{Kti&%&il63t6>it116BU(laM? z51YzkJmYOuZ@q}(qC+g$c@1^O?}M@;?(+(9M)ez7gw@QmU6>)%R|5JE&v7)HIE)cz z0h+UK>q@L zPGB#d^}~}R(tL6}NLw2Xqm^gYrn7^mJI8n8S%3~Ww$nd6*Tl1$+=8^&N-Q|RB?XyA zQ)MI|tHv&3?i8>3;t^@!Z zLKRp086H+GFfyb>LZLJKU{Snk7iKOQ_~qkzF`w`l(FICWp4xM0$W) zi`!(d#PF69Zx)cTtBDkvV70VF$G-KQaAD?D3S7!c|0cIt z$Y&>P+A08=VQ;R?Sl&81yEr-M&E@w_zkJ6Kdz&^l#$(&t32f^;mO&>HY|4JHzq6U| z!Mz$V^TL5JsI~1Zus=NClV_{ERnyPb2*-O7KcTSCqmV;{{sB`>Ms?9|!dj8o^%5#DY$zh&Yt0b_Za15agGSNOTr z>}_XxTbhH<{ zl4lBw|1(=9QE&C6X?o*-NnvrX4J_G!cZUt*uP)!c-`iq`=6U|D^r4%XiWRo;hK1*_ z^8A(y;Z9HAk!Mr+C6!@q6^AbzYi~z|7%R^7Le?u2ef6cAqqxs}cQMr3wtfwxk?2m3 z{gJa%2{E|=v@yBiLZhnWsU#3bc{W0 zHn=7GqN=mVGh<;kK!OwLPRSphEz$wTZW;ruCAB7=U4>crjf825dz;v|EagMl5<=W8 zHUZBt2ia2)eNE6Z5H+D(Z6(1mQtgl?(OL}pvK2jwxKvu|OkQF`wPiTcvO0E#O}tqa z$o^yhxjbuBn>Yhyz>Lv@hWW)pZgCtyt0o66h(Pynsv%Q6KEFb}{8q8$<%MZdf(8>m zemrvhHgQgjb}Z&k-yaR`V@oR9Ts83wuxnRnyg25jUO^|H>hQDB=aAphc?{3&hCmsi zN^><9^H{vhv)nlYOc*44!cb}?oLZxr};(wmmHzj8)m zPGH#~cek&@tI&vQ!RdeUI5=qOjWEkr1B$L7XKj#^O&Tp-0B% zSGV7zS{_T6x>$t-s?_7&M|Ka&j^tqJb@NpT>#+jyd zxQOE74U~^b77Hfg5?4$Yq0&;tEl8_G6J!};jPIl2FPGAp4A##dH&@tbgO$F}=s7II zp*CwQv<%*u19iaIeT(P9Gv7>yGghXv8!J=(2#WQOe)=c`18B=L&F|rniN*C6+$Ap# zVJ00xprOoc0LAh6JP%~@dnbpx=r@Yy?tggp>z`?BmJADV@YxyJ!6d}c%t@n+nCEIc z#IJVCIy8|b!7=gnkeG%@v;D7;Xk2$xX&Te!)s(y3$|I zxbLZCuux}0t3~v=7L(h-Scl0I@GS1L7L(BK5&xO)v%?u>^B8MfAcGp7t+lu92^id1 zh+%=2N7!FB)to6DpI`Q!VN2hA^!WtPh=&Q#kI1vibE2_l_uvSPQdTALQKLS)w;PjM z&R)#jTrZPn^YJ&dx0!#iV4K`Ddb(4ldsYzFgg1@QO)mx_-q!4ny3u-6ia}e6v?$5V z^~K3vp@&P~B_LKa)3sa(wZ6t(V1AjTo5j>`*C8HeaF(o1)(u$E z%%?Y(8KcDw6WamJI5%7D&#mm7TwZ^DbGV5HzL$Jk4bYFrv*0{5lI+SI9w4@f<#@OR z;EajAt+t$It88%?m~>~uz1@F!4s_X0V+2>l86RVwtL>2JvzD1K)D4(D&$>4YBi@K{ z!zP3yjd(YULAiUpSl(O^Edy~eE7G8x1Bk`Bp3(7PK_KO8m3Qi_e1Z-0-FRf z!fk<;Ix%cJIg!cjpI=_}o$T%IAD`ns`4a?os?I~n)WX&=46Jo*N=<7UICEW< zWS>!{#7+h_`}|yZ4lkkD`Mos%x6o&w#&&^jfX${SZb=YiMb=D?@gaENXPQSaq^>Ig(9&SR5^>wB>jSF-G*qC>|4j8*{@vC@7 z#!7xB0%FZrY-Zm_qtBLS!C84mt4105&F#INT=p|6KMl|Hp&1f`U=NRWEfu-I>fQ0+ z?%v78weU=$A4{K&u|1M%%WNNG1vc8xnP?bzCeAkdtbG89$RD2LS_E264nM~TZhiFG zeV4$K#$A_9a;VqTh=BF{XHavwAIO=JAv zk-fpWJ8SGjFN4U(d#R|ouSC94l?QkD8P8syqcXKPOcc+T9inOuRa3#oFUiRj?6A^m}#2<7zTrzRi z^Py#J`UJ1{n+RHQ6VX^vT|L_>gDq$*qr;YgI+xby1x_1L;cAXNU ze=*OlxsaRqE(Z1F83J9(Gu}3)_L1AyP?++JfCLBL+1|s`mcn9EOl$C)z~2u@~0+Q&gF-6!gdUtBW)2Xw3dZ*sl^bpmwQU znL0b?+T8i$^|PQu$-^Ey7PR7sRcH{MVQN2!F)C>eg@QU+{a_+cC-?)oq`lhCeNqH=Sv2 zxFIE>X!)Bj?(FWMjW{dBD*WKygveKD*-~Fk$ zb>;vrnfw}_=`M=B&H6Ajnde$biEjgE_JpzeEQzWp&IJru+=uT!tj;IFvjPpOsnD>v z6Qd3Nxx@4C-><*C|Il)H#!lQI`Z4Tnhx1T!G*c{YFbeAB+41aR+vYsUxiN3MdArS{ zThCftG_>?;sO7P{VtnQ-=4bm?)$%>fYyNgTGpPlUMz11v)j*PkCYbDqRopBVHhn8W z1=)P$LTM0<6<7>+YXa*hmtY%CN{31?b`O6!&uF;h zc;;G{GM)pR3whFvvAMnf{%~vc|6=vzUjX{kCpEc&HyZ4ZbuDeahsAw;Hn_ik^5r{o z(2v2h!#OdMLIX3@g+ZOYJjd`HoQx>Xa(An{KD+CpY!dt;w@u5wHt4J1pYvB^InVA$ zT*+8f%R5Fzor4?mTpbR%Z*MYM)EOOWcf;_e8z_y6C^jrKK`p3}>5yniVId{0Ybwu? zzmmE!q@=M2I7&g$>UR$9#^8RG5$)ero!xed_`CzSWKsjq;yn8$Q=C|bjG~g{ z#M`wGZ?0~>_np3OIl|_26zKZvv&T6x@@ioTJD)+GfwM_Izx~6rDf0Y1c^0ynyi%{J zyQ_;=D1&3LyP?wTJBDj?o#AdQ=rf+Q5Uq741X?0v$Vk7}J*B_#N`y$GYz!N0-~#DF z?FC~iw$Fv$7Jp}+k%|neNz{9`_O_OiFsMzZIbS5ro0~9lQ1scmbMW${nC)O)i3+s% z(0DMM7@f}Kk1oFdfOWHf$RzZm}8FKj8LO72j>WN%L=SBn%%lg z`UTp8N@sS~sWByOYmEfx-;-y^v&6!Pv-Ih;);6jo)qh^?QF$iJs?FFL5I4ikP3H7l zUcVpQ$$sYQNyfxza(`rVZid7bdUj4B(BCfJA03^%pg`AMp97qct>XSj_$PG-rqmM) zV8OFQ!_1bFw2SQc!?VKu%y|~7**%C0^l3(-)UUZ))?ra+5FA5Fqh}+($;xg!h%}dB zF^h#`c);Zv+0*mqD5D+RpAiw!%2f_m2t0lY`3$E_&)d_t6*=tci+Cr%-)$1f8xy1@C?^Bulnt3c(S&i;$G!1MnAI@`_I26&)zlQ zHleefb2PiPp9!L4vfZ8b?`C;-7$0+7)02Mv9^Hv9zA#Gr1J~!-0XLt;`YV)nH#aeT z`?um*qub7Zc030-TQ3Et@ea50w-tzDUQJ;Y`w+xeoEtY4584ll{y*S%l z?@YkVb}(~-W?paCc1{s`K;7rb;OW_7i4c%I>Q zC!O6`h6S@51{$}FXKW1Fm%xv9;%p{HqtD(T+RfUWMc+dd)bPuf!>dA8i z=aJMv#^oc$+Qz~-x3ubyK6{xq+c=TxzYEVZSb8weWmphtSr4cM6F_2wOqrE zrbc7$S=NCfW_yFS2Ny5VHhu-$BP}e^?Z9zH9Pu36Nu56y&qJy9g%zxj#m$+{GmA-3%;#1< zKPk`gCYn1MHPPH7_k6{PyUhG6+@S#9n5kE2JF7&t4L?0qJk0%kKMDP3f7FC?-?A+c zuU7;{k(V)Ru;OfP_CWyB?8N_H7hn2dcQbu;eQ~&nsxh<#Wpm7EMq1un|6aS9J7r}P zYg>0%~SeSc?FZ}#D5)X6sHeyE_-nPZtjD}g9dACz_mK&bkB#fJa zfz-1vS0~$9v5p$lHN6zXTHFmVP4^QgIGk=B~XeyfNu{`@|7*|RzzI8fI} zKU(bWSfZJoMjhz?Ic+^Ze{ouq>5mYiWm{ zxx%sq=H=<>={a^o{m$M*H`kY~``7`?H!?BrU|o0~usqwe^Tb4$tTOuS)c5vDb$y1* zY2sm^g6|5l^+7aDoM+za*!w}p(QDah`F-oVFIUI8^k@msW0P}P=ByAByFPz?^f){Z zjij0(&WLPEIWNp@7~Gy`6bdq}h|}_$@GRZ^l}*oT+04P~pK*s`APx>$iv8!`nrB(V zJUzp190+91BHpdd4?r`z(aJtpXnhdpMz`$m;qbxH$;kI4j*in*= zyJ^|!TUQ^RT^)&A_WYPUN9=9NbI3D-+a|mqeU72aBF)FLbNWeZ-}f(v8`H41has3M zpChT!@y0ZIiJ{o@=KGhEVdBYlGeO*mVIYv4I?cVi zK9_u_>JQz|_Y;5XF~@KKzqFK4Xo0s>nNXy~lV;~J)o4x07=30FLD<`<1x2X)^=s)h z_67-QSdx&QzIW^&xQ@LwSXzn<7Tz~`)6IvNbK;CWE<)B}wRqBtvBTLO5}Ijii!@i( z<5zA(UxP1$`&{Nf%W4c<{_F%8SzHBqw%IDJq!Vi!eg_S0ybX4ooL*e@ogXZ>r@%9D z9vT@MN+wfdlie&TL9yq}_4(=J@SJR$&w^)TZ!;c7eYTy$pw24KaxTI%;ZA`)0rjmX z34c_@dE<0b1AhDzDC9*WEBAQqm@qV^F<Sx9wVt=gqUqF`|+T>dbM`Dh6+@ zI%s6*;5q~@F`~~%O#--%XHUIV_E>H!9dsDG$#2RtcdC-ECWwteqhVpXh;9+<0i}hY ziH_NjE@D9p1|3R6&{pQJ*teSjA8cud6_x3QxlWuMymby~|8BdP6Nf;b9lcM*v+A>^ zt6*+3ybXCa)g+?NCK{&l44f^^CnsmvzWw@iC;MtdXamd`Ft{7Kv$_2XzC+*Ym4A-n zJTyAb-B18Ds<(sUZ9o7puw<4yh1!$h-hWKoDX6-pqD0|9iQa zdrl@?u)zihTrHRe(@Q-Fb7Pl{4c3^zV?>n+G^s?SfiyEUVs@W%11yU4&9{q_57?wK zJwMyoH9Hdk9dl0I#Z;AN2eu7^wYN)P`|kGkt5<4x*KBW#`^ zo+TP)FM5A2+$nfUo@pC*>MnqAbfj!i92_QEu#!zzrf#xX2ukY&@j2AC1E-v1kpT9YM zfBgDz5OJJs>0?hO<4$)*S2+x(B{__vNeXJNx|_EvI+$>|=tp8Z2Un zT0{j`%C>V|t(()jEesrE$Uc$k@1%D~tVu&;o z(vo{Mg;CDxHg5F6-~q{QK|9bREY)~(cJ}7tiXwzK?$IX7Kq1mxAst*xk&DRy{j7M_ z?6dML@@%bbz6T~*#VR@QtT;0hZwGijzqp2zyq_6QL7x?8@H{b-UdkW5VUXc5cpjV2 zuE7k&*;4P>`D?^kxNnz)0*Uq#{_SA z-JWJm&Mz`7af$}WQGvt|}Fv@eQ8Q}TNyUXw2 zzMmEP8x-f!QRws7c+-4lWBY__P3ysPi98RrWnf8xX5j1_douS7oRw#s0nO1u5QDe= z3F2Xqy04~ANONX+Emzz-IlsF3@o$X(<)A1-7jC{?(ZoK7RGhW8o}otm;>8!(;9Q1J z?C~lHI3=8+o2$MHw1L@?jj!dF#>Ko0gbND1xJwZB4<#PwKsFOki(|?2=k(c3Od7iM zuzhU4kl)B^yZ>I*X6{G(iT}OaEc&bCFn^4QwEuatUH^?+Cu3s>jFGQ=+ z%}bl(h$YP2MjAubU;RMw#9m>ItFLgEghZDIV;)PNUEaBDQYjwh?d^y@8}C_p#tltt zNlYn;;w&`dYLoF#TzInj?D{yLhB%Lnj*gBA&>gdjD}_UDOILTE5q4`tQ%~~D)+EN< zcHXn_tif%*-_L_*n%Rtk%^^d&mfzk#Ze{T>>4Z5Ad;0R-*(q%4gO=SbXbECqW>8qD zENDxMwLx^TspwP?>i9QE_T`s%XD^Qr_jh+-hUZ|1+d4T(bcwK7iup_E&{>oTJaV*D z)#vBJv;HaaM+nd0U;hBP5n#&goN)JKakqpUt<`PH#!O^PiO{7C{_I~U9JNu^$$TP06%Wo(IJ6>;UC(azHu~!{4 z>FmaCE7nccf#;|^kEF)tSFN>e{74v_wo5N~7Dp2AJl=i|cM3wo2=mh6URS?~{0LTl z0hU?p;gZg-753i4MfpLQg~%fK!tH_bYe>3f`VoNXwACaMW0`8eZ{IZCiKJGVp>XaL zfgb3)M{J(M9h%JGYPQP@EPvVEF3NUKTsjJKr5lxtdMD9JcFci+yfye-2DCfi$o3-X zG}S6FI}b^iozM7#Zib`gt1CTizNP;e6=v?Yu@nBDinIG8%v16g*r(WqhwmI>72HQ! z`t5MvSsL8}HLAtjkO~tduV8>;V-4BR4mVmfs+Eo5O%Z5tF-0yC7ZIx^9_E%|sMTQOtx55=M7O;@?})i=f?=?>4bE_z?Lb5f=E?V)tG(=e z8`YU(Y<#MDA+x%9{O-CQ@vtb*$?>Mly5-q70*vx(F52h7v*0{41F2ZdEa$e5X|#yV zEG-`nKVa8}w{s1j$6w(_h^S)BU?LD+%;>A}iH0U=iPw`W^dgeRKD5>hhoE4qq4TrT zu0F{L_E@INMxmdNrmzziR7fnqOSX@1I`QAzxK;g{K-8mymph_bh$% zVQ(`nD+wHy6sSI%lwl~(qLnVOP%6%N>`za9XS}9h`7LHwa=ov)PoG%X*Kdxt^F2$M zx3eANqbcDSb?_J{wo4>NSZNmys#L9G!wW0UFc z*H>RJTHhb;QL0z4`|;x2h5zpAq(D=u#SfHAzKmjjpc>CcRiVSevuJ9ZKH~*}OI!8| z^KI~{f-3vN?zgcM{-Tyz|4ZAMeMUb|PtSDoy<%-Q%1ba<_2^m$3d}6&R;`99N=F*& zDKC(|;#>Od3Vw7E%PF~1ql;yrF+kUsXQ#_$lUnszyNB8AGxgbo!*Cm8pBDa+PM)na zo6qmy(jmxy)mmI^Xv1$nc^;o^nuEc;12KBIyRe)MMMlSu`{0M|Yw;>T~EzZf>JLw+6-pXsI(%v~3T5h9t$2{`vFf z>&Z51##mtjk^UWcF15G$H%mA$;2E)Pah|Ep@7y5Nu(;X1<#=^|O7AG+;{y$y3z@ae z!}F^L^Bl!_B>8FqIGbpgFZC?ZXPij-I?q6#p^0$f(wUX~uJkX)<{owG*I;92CK`G%{e< zC6Q-!o`Lf_2KB@70wJ*k5 zT^+HD{9FGt_BKu2PKfhb5%qHPn}1xN_O32AAzx3K1y0M?{#B-1#(qGwqH2N(0?9!mvFR{1r$HD5x{fieos=H}-BU>ouCzG*| zXw6mmQ~2sF=!w-zvlhQs&h!PnIjzd?46yb;u$g4x7qdH;}<0C=Bjl+mzf<;LY8S` zYpW)t?m;s=W4Oj>I0aosji9LxoWN;gj2nA(V;PbD56={tC@i|1Y#)bO!xkTr;(25Y z$0w#2GRy15-J_T1m)x`o16i#XXYcnVO18w_cXCfqW@B*%0A_e6*7K(`-f9@%<||5B zwLt$`o}+$q_(=aq>Bg^72Zo4vffH#mtvD;s0yShB=|QG)P=qUyF*fJ;kY7VQLi+ng ziGF@O`)U%F(@4@2K?C3{#WQrC1DsLg;KvILY|44_^XB?ApIPVx(CxzWTO?=pAH4p# zgy+#2c#E*MSsE6stWus$J&7sNlgN(`Z9NUou(rEU{MvhRdGqfdH(%dxuV=bHQ=31M zX4=>+zu|z~8Z&yt^oH<%x<sLb}Iq;rsRA0Skm1pdXE%MBI zC5E$sZ^?M^<*RHva5cD*ouBT2w7_me?Zk3!=b`;c{58$l(9n1qDOkg^ZS09fN3lHf zj6ONf&8Y9}DIQ-TNp*7pZSEYSHX{Z`Z6?jH+CERTf2PjTn`W?!&z1~Y<)Y;@tj7?Z zz@iSgglHAjFH5fG=b9TkCdS8TfAgELG2&RriUC<^j zf{m=FvsHW$Fo>zsGH|f^I2PP%g%w2Xs)JI!`o=Ih&1A3@T0k>23B6NVh>f2 z$V%K?wjRh0QPxLOqB(GhRnzCE=9$?l;EWY1ANXfU03T^~$yAB&44A)01`j}>u{rGd z?)u_fBbu#%=UHTE){6%Z^q(UOHfGx(jX#&RXs%Ta41qlZcqU+sgE>(`Px$ zA`guXED2l1mR<-LULF^g7urWhfHVFL51V>-#I=~M0)uvAKV|tJb~v3pO{pSbs8huPZ z8YN`-U}9rjB+U&Y4ARc(2H5IGOJK~Dj3GpXbl~;Xi?i3eY#nHb=1)+dd3iykC#^!4 zud1^8T+(L9$ucKil;yC0s52adJezJq5q*{g;Oak9o>5I=TN#jN`72>=YpJKoGYjDu zARxV1)fbGuK0HPanrWD=Y`Uwlp#z0Y&E0Pio7sIB&k>xtf~#>!7FMxz8$3&~r;+C< z&r)#jd762lH8sv6%wIhD{^Q?2U+k@?KSP^AEjuWp%&iSIB$BZD>#u+O z1OL@D{`v=AEr`=%hYo}6v38^rbdyjtLSPWKsiW=HXfiQO_7fwclk-dYy;j-?lFPbk z#ZE7bc+bma(9QZ}@tGYQcJepkS*cdarN#tXhm&X|ueQ1994Tol(LrELt!`fW=NDh4 zAn*FWUTc)P#%`%)PquoN8NGLG6>|o>iVD8Wxz_?Ci-P4HQRi zoKL{BFTR2%Ah zbp1Jfy648TtSqaQXQ*-6xRPq(pFiI8u4fjyni>&GM}!*&_k(zj%5!4m)m&yB`i#;r z(`E=bvj9q>+l+?kVo9+dB~9pY*0=l@)Q1sgTx89(FlW0L(b%Ey+s)-sJ~K6zqH{}{ z3G+yTel)p^fLhxFgEKqMsU2ZEhH#TRnm%5N%DY2{<}O z!Rb##FW+GsWYj&Od<-{$SzMJdRVF3+OheiG&wPYY&E6K1TNX-0E$-V)?8x(hy-gOQ zgFO2qCFoXoh}R=Jh6aLfQ3#4HHHygOxIuH^35$AvlbpT#qgo}OXV7>O>T)Pkui*$^2(d!EHYWeF(P zX`G$DWXn9H0I%L1?=T&WH;#pOhG?oq#NPhBd4>m#J^POG+5Y(%$YH}wH*!l4;aRSJ z_W{j-8?gsi?>}V_22z9# zSzyy4X|%q5()ayrcXb|NZF;kCo5g1)&G%Af)o%c;PP8CRiFU!VVF4FW@K>J^l$>86 z)JQqcz@;8TntL2eS(kFVsKiBs^lz7^gIgOQTCPhmU85h2G@4IP;&4!@<*}FS*cl{T z93V|wMWnadgXquJRFzj^JG+lQOQeheZ#H9uMz3i$nnm53j)p)>>k@JeftF<&sOrYT zf2=>cki}Qm7bm;99%S=jbfdmU#?+J#hY8A*Wll6j<+D>}Mrp1l$7Roj`aC(=!5@n2 z4S{`8f=x-Laha-R{Oxx|`%GvjJKyZpE3YE)MwU)aumO1e_jNU+CKk!@9deD@+r~sWkQs6tnyL<;iXV zezf$;in7c{QKp1vLC|6BCb1;z!Dq>Hqx5kDidYGV$QS}+o4v5QRijyU!R93#vOrpv zFrX(9jMOvsoxj1t_3vjb=y!lyA-1Me?EufVU$2$}Rp*&FKY(WholALUHv?;LTb}a_ zhe-pq`|$kp=eHBM(CJz5G}DDT#|QB2lvyX(6Jr6nXQ|x|`wWHN^DM76;TiYuC(vhg zp1Gc?bGmc>?b62HdEeRK%2WzrFl4Au>p{o)ej%{Xv-UWv%`FrYWIEG98>72n%A4CY z==0<8j4X#K-Zs@?Og~$FW^ow*5RdZh`7gjv8F6{cUWu*~osK9d) z)m^WW&}PcAO0$S;2}*R7;kmKK zEb3ek8vAi|x`(ncT$bA6JCNxpQzgJT+q;T3TZN{{ty!^>Z&LYT^k>(XXR)|(%g2qE zy+@k6WOMj@5faUnSD>0i)6{0qK#7py0rt~iw}bOHt=P@(D=IE-uHWnznT> z=sM`Lwi1Zg+_(d{c-UlApJARNU8RdLh_fs`k+sj1XI8>}&}tIx+0cq~)jMrwWpGxS z1EjHAA68`$=tD&F)AI-vU=j5Fc`lLXL~>wp89Y;<9naC+vvek*l}Mf~$@=-1@Qe#Y zZB+0)H;eFo9too3_2$&azd$qVI!B3fDYIn6+4|A?xk5lY<}hC`i)GmA zp>djB8o60caf`*Dnzlmq#h~FCDA;vZP`1|7mnW_7`mSZ=6}pUJ*DDguYD6eBZEjP3 zFJW!@z_@m)|9p!+--YKn?y(}PF?8v(@NA>oz&Vs>-jpcMQcJ>GRty8P>qDu>xWxb+ zi`E@17sZw#s3OkRkLT1h7O+;5XGFv3JwpO;o}xG#l#MW#eR8DxN%U4w>mzGxLs!Ui zH#)TJoV`5A%@2M0>#yPD=kBKV(V>T%+5y}Vs~bR<5KWdb5&)V@s?i4Oa}$>h3e+eQ zK+OqKSUo!@m*_=yez>{Dl&Wbt9WY(uM2l>Ss#2dzq`9(WOJIW{%eSX|l!|fqIQ(6B zdTSmr7)KA`J2BRRfis3hr|7$yM>{9vsi4x7Xqefe&zi&HN<+vSaKK=`hdrOZy+B|L zmpd0;aic*3Z5PuJWyw&*rl}&*Tlzp~<|>_VRMKbN9z=LH;V@lX#dRmP2F{qnmgGk-ds^;Z)HZ~YL3H|A0B9q@h8Lx!>L zVlM(mNLm4ClZe(OSIkCBTnt1j(rEYuqx<{isRqW7L!ea%ANrT`%ny|Xeu2B=bv3$smQ zbqIt7>WnE(ZfA6dL>o8SZvPA zb6JqviR&Hp*@U^nJA(OEF*l6xQ0ebu&T#-^g=q}I(L=%4;hWAPY=|CjJsXA19md02 z(1%E%W{+;&XI5KROS2v*G^#+Q-zoxQ=uiPahLIZrJ)vnTso1`|J{xJigT38V%JY)t zIS-z(;g2--WIVvu#>m?4Qk=oD1Da4{7>oOCH@DQkfUa3@>&0_we0B-jy$H{KsiX^s zNta}axCqcs%`+k_SS4B9J%9sFW;YBppXhJiMF6%a6gcXo_a!_jk)dg znaL`VXL=9(z3DErp8eC;h6VC$Xf`&t@XSUfw78FWzhc{_`|?~O&xz3{=$-JqW|ux& zd)vsflx>@PcNh(O5`9LBD1)v7hlT8X!}t`Mdv=c}K1mGgVFZ_cb;O01pcp&) z@uWdD)>4jlG}F3VSwZA-t_i{K^_`>BR@95(s>C*Tkr74AgLof(cJTUd;|@iCj(-&S zW%NNHtW~B_F^+i@d@am6u|!ybgN6`!NHolC5ok5Lg=ehPKt|eF-KPkQonn_&Y)*M~ z`Qn64q%w3@8m9*0^f?j^V=OH6jKlez^clZ4g7l`^Q?t)Bx1;(jtw~zo{)_i73!f#p zt>(6Xs~<|`*~YlPo$vF?Km@&BJddaGCR3iJ*psaccq=lco+u6D(m%1cd5%JypTyqo zWCOtU;)lb{RYdwTE1B7`1nuEc93S{tXbvS>G+N?fl8I*4N@Ltcr-f()$JjqKHQIoT z8gewbmBpsi)WXURk}-pYm3~HiV;C}qw}2>duaa08A6AQpxv~=RN<7aV+~_R!S1<&em3zC7`VzLVh_n#&w8tb$eUd)?oP{f8|+;ZUZ)H9VYJc@(SE% z_|IZ*v!5w`Z{jR18vglD6WkW>S#?;o8Cx`PgoJ;GK>r&lO$Pna_2PMCWCBgMm1pjX z!A5|<**6Y*jy&gf_g{a=tu7&XkcK*wy9e@H_T^D|R@)o;90aYo@x6Gw_HAqJ(_K!+_fyl!E#46J$yfKd= zUyJAEJ_fKEUtp-4ZZssCt2JnC+X3I2JIzw<%s|Q5_4U`&!%c3df#OJXs3_+-z`0DD zEAc!U0UzJN=AUDDPCK62J1pec)RL&RO=DXcqZ#)Z@R~T7!YmIw(Oi~+);eE~kZA8u z-&db`$@wvqOg1f{l*9zLSC$RWbe~bW%~DTl11vWa4nr8sxzJ*7m!+xP0iULFvsyD$ z(bw^6;MGVSL)B4wxp;K^a!lCH5nC?U?r$u%SENny97#5S7G0QVPOR))nmOXM>Dbd()_7bIbxa!j=!W)Rh z!i+pe*fbBy6glRA`?~5pL!WK-d1-ldO?wT|f7aTtK{gsu)*y>p24*Y4RIT! zu@3qtHcb20x|e4Y@_X9bo@LJ9@O7<<_bjy}+L}amH|GK(@Qm_}=fpE?poPWN4+uOQ zBcxKyv?V@*TZn0$6`JylZyE%-h3k(BG@vHU7!+w^amy!_XHFACV@->6^z+&7@zhLq zeG~nNvI_|7l<_R;RX3jH-cSi;IbMQra z!LuitYHi)fsSnSi4U1TL zF7j;qdWyL%{9wfq&T(VXMW1kmVgUsXTmCQR;AgzCPJ})z+dk65d&_CNfmts^PCv^ji(VATZA87=-mOm2e7-b?ZQd3C!3xG`ev^WSAS97!hdF92fnHLaUFY{RE~_rWI(h*J*FJPfQF9NV%u? zpBbx?#Z?jw^R0SCpXokpgF{=mjUIrq@EN?)+%6kNoB=c&m9QxI+q=R1YG(0%JV)g@ zk^GGGiqv{a@|k^xfU_=rHmPS`IRVc+FHgoZ?p?q#7JUvS8aPv=1#6_o8y0g%DEUld=dpz)bkj+9b7LG_*y=%;2qO;sC-7{E#$S(` zT|^9b_K%K_2KTm2nAlW|k#YpgFn}~;-6w}f@T0K?b$5fg=%$9x=p zo-YB^W4OeF>cq5 zz!(ZYVRr8T4LfLa%OKD%5$8UBjdq}4a2LmhSE!Ff!zb!<1$8ztZP025%9R#@L=}>)?ikz`)(@ZJ(NQ z5p5Dm;;I2%PO(42uf2Vgu*`>*PM^_WoMgWXQSN!S_kSV z)$Be-$m#U_#l`txetEI0v0Z&<@tm#7I$2hXRhjKa2((37Cwzn#3edOO+csS#;ce^+ z#cWj&4wGGc%tA?xhN0F|O2W#_Z70ugpkZ-qQ3(=qx%;iJit>yw>_T2PIj20Yu4r3N zV{Shio_TTdT4#*CJG;Jh_?|XeKGT$ZM4ZKOj&Yw65K|Z0M#PA-q^?RdnmFS>{6EsZ zxL(*rEWTrGpqY0iy~O3x77oDne;m);OAxJ7GfV6FZDgbO_dc*&_l`$ev|4mp9O>gD zY?O0`#(}NaH#)O`Of>g@GCW%yj(j#UOl*Z@{UGz;bv5#=;V`kcdkoL6l>uwFxyy|% zd=7Yy^PipjtUNOZ4G;S3aq)ir45B{g$*)PRVa~txkyOv{R72b9_&*r`w+=tO$ zNTjt)mfZoTynYgM6L(L>JjX-4*nu-kDzGz6dUbQ}^)a_>U7kr{4TcQCuHIg>1G?1Q zwm`G3STIWB+(Nf`x0A1?vxtUnWSS;kwKsJ(@o$p2qNGaBhv`Ro@lW%pH-mIo(dH+_lbu^aZV)L7MAi_ zyUnE-R-S>gbQ)rKTY0u8m*>TtxL?P!;EX==vutFL+dqVLwq4jrj}Ct{rf|Jc*6&vh zXiY;$cov;5(dc2k-_m(B(YRDN+}~L2NRCcntq|6o+PBf%u$Zk9MCC)R@$KK3XL?ci z^`{$~kdI!=?;akX9PRC(UpF*bs>XzA@QmR1&Th-#aVx@N2fO=6XMLA%UKf}9yBj7% zo+Av051`MM+mg@rRq^(=;~65tHfUjZTU+Vd;xHH#S{)|x%ti*XvdZEdde7f=2;ovN zb}jm>y|~d!tndB3`fr!;oN7Yv=rVamBS6!p7hQUv1JB%Mc#b=&VAXdXH}Zqs&5i8z z$Vd9l9FL&S4rYGu$bh-+Z0ennzhTl2>~y8G}S?2Y$HHlxZH?B{hdg@zUl-R{Hb=!B8w%>xM!z z_T^}`oa!Ne+`Rx3zQ#P3n0FO@_VR4fRgh<=&+0wn&ckX?!}C7_o=cKdAh?bKTd!<06G@uN(vnfc7tUCO{531`Gb@7rt#6ycJPZNDhB^# zp;?5sHhuQ5;;lAj;NCzpT6l@KxXZ0Yttc>?iT3&m8qH)0zBo@+0`$((a-k zJ@^{8>g~-UniHAsfg;k<8Wc;685lc0JZRa+29;;Wh=(?!0XpeqtjqihSPdyInahju zm0-IX&nnN*Hdd3s74$axyx`SH{^7{;Eo$}{d)&{4tClUmK*_OGhW zGk~rn(VpJ&S#^%>v!#6E@m+bAq?gzc_@@8jlC+IYwmYCg+pX+~Gld!h%Qg<6(U9nU z2;xcW$>B~en{J+X<>b~gYoYc>;%C(A$~?tn(qLpWQ&@3wX{_Y+;)B5uxnQF*taNF=~qhZE*W)pp`tiq7lQn1{mbU&5nClYuH}i8H#(DfUM!vDH7{ zm1lQu-4`;2EwKY|jo%X+kD$e6ZWGyPE{?W+xs+uaAY&I03>auY`ve7o>-{tBgc2JZ zae&%dQ8q6%Ku7Xb;y>fh=VD2XhK1(#%BqC7ZL*4;Nf@ltrO$Mq`#jH1mV*h27JXKn z5fjr8_f6kJc^*l9o?j-2q}qo`@j`6WJ_J?Pgu>5&e zYQ4->o7q-qqD2c^*V4WCGjTSbY}vS*TWBtpn2hZm~F*>$3=SB+w0%B=!Jv4|Dksa3W#Bd)(9ChS-X2jC-09BcD6pW9Gx5%SJK9Z#%|vt(%i$$j}#tN zMw-StJ!lGab)IeZ*~l}(VYYLawkF|XNp>c2JR@5bnAvVxW^cP?RuAJDZDyyi@R_9~ z2yde)rCEw$lVcjn%i z`~f-HJL~NItY`fmNb=50KnZ?FNtB%iY|g+ZXLEfmz9g<5n*MQ*mGtu+f;`{<-vuI? z%-B8}x}h(zGsuNHs1qPGcuB&~5>0qjpRiY+{{$3t(rxv2#NnyI&WU!lz^}Z#Kc_}fV+0cAs z*}KMy=$ZaBVmt7kgGnD7{>F}= zb_fD9ts=kqq4bvBj_p2n=R?Wt+vaTRj=yd@%={pljxm2>w=L`Nk&zm%StaN47tF8s ztH#_2W!Q$wW@L&gXo#|Sa0$jqnF;f)*1x8?E`UKHjwRn%7L2HWrWT& z6ffN4u>4;ZKzR_zFsbxP1?9mF8(_8L@0JVl(v5w#oq2o<41k!J-fckzhAQ6lf*LH1 z`pH!JhNr?=MpbkaeRoWOnC+eeoJ&;+srcGIj>#(rLF8g&#zhU7^k?scOq^Jk<$o8f z&s~rB#OAPEks#52E7(m$cfbW(uxrUf0&deB`!~MC{5tLpWCV)-cV*T4;P+OAXGD8) zM>x3EV`h$6lJ2P<6Ve^zk> za!};cXFS70cs;@f}2#O`dmV#SLXz7H^c316WtCqV@fLA=C5dh z{s`2ckT`E|hEPrSfuTw2h^Od%c(6CdqN`1fnkAH$Hi|&F&u=BBNC4dqb@Y3T)MPE2QvvYzAsC+{_-Tp(aH3vf5n<9G6<^8CU zKSQqp9!a(+Oos$Nb$`_85HqkRfHcP&E4nFrIfm(=y|cP{Srj*{(#N$i_o6vR7-#~a z27 zkkgS+DasYu!%!^3uCZ_T+Ry%2YM1|2AH3+MxO>S4)8N2qGo1zHUtIXrHtGp@5sJVE z;#Oikfqw0W{Xmf?tVWOKH;xyC7xmxHXFP=Dau)v$xIfNRc?V<}2&l>)rPJE~(z(1o zFSWK5$A(R>5k%sa1gjbLR9)&bB0B#5z5jk9`h`=@yL2RNaw(oV$Y660orB=Kq(+u+ z#>L(t^FMxY(>LqLnG^@Ng&#__?0wk+ew{tTd+-O1tf}aA9lI0?AAK)S;MS2?k}Eo= z&{uWG(-P5*ch4K}(Za)8r(5gm>#^Gg1eqpio-^Hyj$Z2U{O+im<>z6QAvD)B9UCSurb%1;fknldOT_FVsQ^4fT_Qn433paD8*Cuv>DNUGPAyNQJ*H z%=Fl0c#r8QH|h(O;1T_p+90C^r|X>y#|NZ2_9%mhbEF(HWYfUyF>Oyu)=9T@bxyAA zO)BK?ewofm0gMf3Bi^YO=n-2b-ZNp#H^bYsxXE%Pe-RlB->Lr=Cni8}Su@XRx*!AZ zxw(8LOrCM-MgK7pi%}*u+CwV`AJEt!J!7v&9o#IPmHF@nAWkp(9h1xPEj4f+;YD-J z<2>N2P8nUo+p0@}CoNIAM9bYa_KGzPJcI}0(5*{@qzklp!vbx%ZR_tC$p@+p1w`aS zNwjx_@;OFBClu?Gmz8q5D0~y>L7!+rYd!*fG&Aq0pNNPvdrf(t_Ms_KJoIBgI)uzy zadX6MI}~PwHqMR1_dz2;v5p|}Qa}J9nMM62ds%O!I5VN~ix8+NS2DszFqk03((Tr$ z7X$|wZ)0O$JBX!M{yif)h{C?oABMQW!XWA%*8Vy$s<#4Gn0d2j)&`M}Sc zRH|tArIZ{3GDGR``N(oVH%a0jl5LRP(a4g9=QacDhev$UlfBV2`g zd@H`o|Hfny{wHD%@y1dRmw3}CFyqFpNIm>JYJ0S$0v_)EOu)awrp^AP&cWCs3Iv zCoR<8J0BnYQ**ji)p;7iHNiAShsGnvRM_B6?O;lHH3LZ!vZNgQy(dZ9I~LoJf2g6u z*XKf;D|Nwv+rsy#i6awU#{X??)(wFk>01+CsX?%L-<6?=t%|e=WHh z-(UQbU)7INOHGVy+*tr zvm)@vx>X;0lJqe>o}XS<*T`e@a>i0-ZOY}pMQPXW*Xw;UcT!##hgNEW{MZGxod{y~p6Rf}S_2wr`6rh&u*0=7}K7H)O08|U*ymNZDfB|Is^gf0j)_KXPbVV@p+x&Il*1+D}BLA3wjKS z#a@KJ;C{>f^ik|hl;01DjVeqdj_Vf;*7XTTrhA>vE9^LfC4K$wQfgYlV`g~Kw?UoN zf=}4h$r3vwh3IWO5uHd$k=C;Y^V-vY`=C*V<V?R=`DggB zdP#PB7u|74%V}i!T@f(x^3$5Z6a?aobp*n9dB)Lo3#4r0#!_%M9RsMQu(^K{+DFcc zHFA#=*zqXRN{A+Tn)!bo>JB}y-gd^!5# zscPjHl)Q0vtQF9GZ*-%zk9D$uCQZLM8!Uj?dCiEDqvk;VY)xb)D<3XNy(f+&+;oH1 zspOeH@5VE6D#%rvnXbE*a#codqw%rzr!`6a_SZ^LP#8{G8%(BCfwv)i#-ABHY|s15 zOj8;hHNS8-^szZhS7xlANm_MUt6*a+04w{bT%_}WelnSR+`WMI_Fi0VL#-SI;K7Y( zXaUD!AY!Nmc88^2lQpa4E&J5r($js0BBxCWC_vY}DI zHYLXEfV;`AI|=@IG~;|gkG_C-gC9Or@aC6v6uAafz z8XEt+OC~oCeCLt8ej~d)ZG4w>;xf)N5w7j$Dvgp(qTb(r)rDdGJ?#g_V8unwihy?( z4~>eUTIyK@=-R#<#4Ltneseh@M5Ia5oY;9UkhzCR;u`_Igb; zmNR}%WdaX10F|{5)>Oc_YZ|;MN^~=Q8Ws=vFMr_&q>EP^pHcJ|2}X!No(HZ-arprX zFt1ZX#y%8Dw*+!2R1091&cfOQK!KEMOvd62UU*|eJ9{n2ykC4zI3;o^lPffz^D%xj zSm~1Ny>^T17*-0Lde$Tgyw${=Q<3mVG187SU)-ucWuG_xoJg;(rD^??g;m`^1sorC z1#lT*j(p$>c7nGt(F)}z&D=7LS*6zx89iz(3}09+dv(Ul)J>c6HTYkt>&hU80-g>b32ia}<{k#dRW@!?exM=04Q-gSyOlz`w73Jq4fUPGfr+ zMvShr`YZ-KeE@yDJ-9=@OY6D#TNs(`!E3yK?ifF#My5RwCnqmwTGy&c2n}*|s-$b* zvQND}UfU$PVXlZ-n$MJv%R#+{dVKK?l$Nd;z|11h>BTpex&npTS`YV-9 z(MZ_61cSHYqZ3M!lP|z)R@A6rFQgOpR;8Z#JLUw*^)JLE&utZ@VsJUwm@E&RrXfUY zWs2SCpRUruIovIk{^w+n#!o1Jyl!LTayC7kmpbfoOMB4HJ7*W+<<#dHt105}w9ruy zG+wR|D*7;edyw+}W1hZNZK#+WF7*)Zdciq2AR)j# zJoGjib~A;cfO*h?k<4W6mM}j`JW`l>12Lo{Z#DC=A3W4Z82BcjZKI-lL$2nN#1YUe z6=0W#VJ*p<&xa&F>azZ{9M&>eUEdf!Sp5=4Oy;e@scsgCs;Bnp<0F}SHWu<7pBeoS zKWlSjhWDZg+fbi4K)-QFc_7W=Nb?3|nRIa)0(mF%T^r6(y6xe?14I=>nk7h${OU{L%#qP7KRlcCB7arN3_q{%904x8vKc1Iw( zEw(w1_NNef?8ATRV+$yVmwV2#8-Je|R4u+F^(SDFtI#l*hA=q5M)4yJJp;(WxML)n za{7uHhOt|-&#TP8zEUl4{o)hP8tZH6&kUoT7q?HL$rs^+8FKOU69^zfhXC5<{ik$eVUlF98EP z{AbsDg1RQaMi)=FMHLcrcCh5EvLLz?91dSC)dg3mu4_;8gE*%$vu+oj`X2`?@;8wXXRY$>tR6#VEJGeDtZi@ViU9j+Z>i! zFMl=D*X;ZX^DuN%xZ9xeB$C6K9eI*o3nepg@f#ef_1|=*uKL~s_5FqdnpU%Ax0aXR zvuMV@tDBaqI%-!hODP@={HNl zbe(AkvT#5yW=V(K2C2fYeF+G!R=SbEHu@R2O5;^*sqM`L1Iuj`y`I_G{NUrmgk-4k zdljz!>G)~X^25@-#OMDj2J4ZYMnq>YxUC7f@zRs~r79MOd7OL&G2sO}Y>) zPCQVyhPhnK&H?3J)VjOpWiz3|M-{&W2t)Z_E$x2L#@u_F^m|qqHlNe9+AE4SPdc$> z{zdMw;!L#63O+j>6_(li>SA*Od?^LXep*=Mj~Rx)q7KE);eVxrW|Qz>3*LXw)5;(hJQ|{8@J-2_ zDq7~vd55}UaSJYT&&jPDii|#2IU7dX?fw<;62_AA+J^&nhA!-d3)HBW<2se9)phM+ z0F!!L4A=o=JIs?1OSYj_gTs)A6%l7E;y zni-e=fjRLDLCHQuHa=^$>4b#5Xqk%Y6*CPYpxsddD_^GbuiHB#)TB$9g6~w8(<>EK zC>|m?_e1_Hko$MEF`&A3-;n+Xhk1FYvL1RN;1KI~hq}sgKH(ykvl_t47L~uBbns(6 z3emQbn?A%BStm3}MYp&hG;6|!iOJzFfiA93t8;Yzf-rLrb@+SY8I=`uJ%g=Z4UJY+Cq5j%>+91EkB*Q2 z1gw^_pGs0o52HK1XgPGp1MDZ^|4qp>de@-wQ=@hKH^UGazKD_zXi)qd|0-O7o}N^n z-1kcnL&WpvJ%N|2hjVIi=}q&$Pyn_wvWyyT){)THe!Y3ISWoZeY@Q;IszcA;pAjw%sr| zJfXgohw?_mHW}gaaWd49*LKtcMwP2yBKR{@y$eaq(+u&LVPCQ8@Lj24*2fSQ<{t;} zx!rK(9DS7u3x`H?rzeULr_bB0XEbRDdlyj{%czUc(VJztU9T=y3Z+^#f?e-H_$2`; zG-cG>II$vszzM?wNQC5890ko*jkIa5*F~Qm*0tcmSbr8980LqWnzzPU37gI0!~Ua} zD=I&wVFnByM>5902j+b~p0KwuhxK@82l`Zv1jh5cjAK;DWfYHgtjmIK{I?@03TrlH z)ifw2TtWiY0=JbfFx#g^=y3*{_6_=Dd#8?^7$+I;QN7 zY)@twmOsTTVOPRl|B3%>xp0|j$iUF-q%Y~{VZb?t4wvgKN29&U>VL)sTb1$4xZ&sY z`f)0sQ^|SDN{~9X+x%(5$vE7ad-Py|Z(9x@!pxIFom08cBQ#qewlIkkQj8w~o zJ0%#hwS?KGIX}<96eJxx#Gt*d+OkhP4?vhh$1qOU(8xj>7)V=fSl%=YQ! zisRuGS^OQ|->}9GZnY=bJ@^?XM!dN7i{KnNzMW``yKdQw4vEFXe4)l!M;J#nH=sIj z6hSshySKe9l1W{{WE(=d$10h+#_`>a?fW`Emtx|~j^#7QBOMu$zxx(^M02nn8KFSBew@-T*!}RYV z>|B=h%u6pDbx6jYUPi#qgP6u;Al@zHa-z|cO?FAqV6GHFD z?8|2)eZ4SEgW|Pve$12nlnWzTe*N9GbX4dy&+md#%&C^2&qq%bVuk%)5r#*ITk3xj zSd8<&A_RP;3YhiZG%J^|0*AAfIMR_eY;;*}cKfJViG1gkieR1%=k6WMF~eFcYf+%@}gQ&K|hYb)L0?Q^p&nKS`g zwz~k@|UVXSO+N(%-cA` zEmJvOLiNFtjdQMPyEDp9>8nnu zl@p^drk0T+?St^ZqGr}6fXtO?#LI5_qP_|Eur%XAHhegv8QXdW=KC*e>DfT-8Oo5p z`$ECr#(~7-fSo{kwjKvS0aOl=il_q{ap27j+q6lqWU=xnVWMHAt82e$tVCmTjWnIj zNf}0VvC>fF4*Z)54v53?K|bR~-KTe`zwxV*swINF6qE(sL%xMU*N#R&8B2!6Jv+Tu z-xQShNJRPJQ?F4c{C9>NM+YR)$~gTpSAvR%%v}~Vt_7RnhtGYpz|zO#D$6K0EDEX{ z+SoiEH@77xm!7k?4HEbd>HQaL zWaIh5s`Lw35BjS+b-p>hZ@hw(xIhli38jsogQOx~im@WcH+C4e42JE7i_nUGL9S@d zkVl+(bMgcY#`(3-x;e2QwJB~*iFn91-GuhrSsz0sF;rs=)pT-DG;?Y_hsTAFFKo@1 z-66ARvW+$-Sn*nbHL>;YMS47D-75_h$%XGrPf4<}BkLa!XTvJ>vs*%k#xFh1;sm`x z)m8Vn$Th@rSXIb1$n!N4tDh9$99U*0XdS>mN3j-4PRisGqAn(6I7{tahFgUF%QdBjh^ zy_OBrP3{b(o|5@&9p1U*0r%1yN8J*U?!@*dg%%?pnyA)X-dU>_rP%5dvB71KQXi~R{oYZhU68m10G8BYqRXPi@!u`8Zt&Z4b| zSp+J1V%4d&X>b?02z4H=Fe_q6)k)V-pQob(#0|?Y1uj%vA4krG`L1liXTVDCv>l}1 zVZf;ILq>t31*y@5BULZXtR;)j-8H3-E;IT!j+!;bA-4y!?+BoGMJ-;vAXAAvo1$sE zpB>$!*YmLeAbZ$0JWux$kk2G=eitDrh$nB?Liio$j`Ll(ENS{K@-4j3&Rbf7IHQBfE$L@(qj0&XjPt#c9Jj($iNFsjcKeO&yahVm=CV^$ z&$?F3ons`iLK0O1Gr5_Q3f7b2X2;N!f?FBIAfbOhH8`m{MSV@~sd?<~%7rJNQ+09` z4AG6QQzqDA_AoHnyn5Zk2(tBj*JvGGr;}o+?#rpdqAh>79cPqUG#D^^fT--*BxD$IS18hbEzL~ zNqlEOz&|W*&9iDnj{BwPiEl>QhdRWAL3H7?B5Y>|S(y_9N!#TXZjES$OE7MuXEcFt zQ*F%?Z1AFsbXupxqOpHGnE|EYiAA zSrYkGm*Q->BIc>f-8JQCM*`IJfkC8>HEdS`cQoFdurajD=C=;Uua9&!Kj)hpfj93S|T z9`M38z8~b4GI7Z3e+doM_;2$Rxj~is{i8@zf=7Ca8T;S3bQ~d5oG~XiqMvfX=~`(u zgDS_q&yrrO`iL^kOEl(veHCI@Z1yhguf<0eo!W5G3x9G|V8j0c-Kcm3ypmIeimRNc z4LRG^zRL~dmB{ek$;zL57jlXv2cDAuT zo89YO#0*3CRA?ia6=B10Me|`sk|e5G`P4=MNL%i1;{U?S59vkRV^kj5Ud|1=H;apU zSTg^n1s;9lH>uRwNGdKV@{Q?VEv&m~pG=IQVaS6_VvENMlMoE7W8{&wc()^gKJmOJ zg0NK`%?@iK)IhV-19P;pD~1V)x~+|2VNUKJ+qp*q@V0E!YO| zLXOwnz?n`KstzQmj20aSL>X!)qSfudbt0nLoqT5O)jc>{lAgePuot6rqXx>gG^#p0 z78p*w+K@H~%kP9MqjCj4HS^(|FNHZAz<7+po2cBWLC{aRJG9mg>48mN!oMn1DkWt;*}BCHj3g(qj_nEXM1N~V6QSUF6Mldk z<@{@Ia|rUPH)ED0PwJm%RXY7%b~~(V!t)SopGe|<_kf}zE2=3TQIR*6FU-8_zzt^s zqT$zCO_xt6cyuyl=6K34zY<{Vi<;SF9%1K?ubITeRK9mxplu$CWSN=Sq|nJ>v!v88 zgqqy!cR|V$D%2k;eY`#;K{gCz^7I$jKJ%Prr|L^Es`34h+w$_``n|w`!;#MR;x!M? zGmbZWhUgAjQ)fmD!0RRP&&TO!cwp;)sFB0J>|Ho@;$Jq+FV=+l=iB4Q1kO?OS=^Lz zcAq&u)@r-j2#_-y)XiDHk*Yyq7yQ`YML_-wDxwJc zeONbu)z%*5O@{d{fQcaa8D-t}MwOJcrc&LmhuJ z)y%8h=A>|~K)MgbsqJ+f1C|tn)92kpw6?@{+O{oYuXcVi$xNdX#Ma6H_RDSyMg6-2 z!z3ce@3R|utsLdULo=Rmau@fSLaTDMqe8$^lge>OA=mh4Nh-u7JRGt%JY<~4trrY| znCfoLwQsoe!QT~hf<9S<8YE7$W28Q#bI@Rjz@Ok*5fn{$Aqzc^*zV7sS*&{qg&EJ) zhbxcHBKS`aRGk&If)pKjRYEfLctKYC=taa`(HBV0CsYchrz)5?}#U zs2wbXb*W6hh1tcWC}q8Qwpcu-TqdY0w#bJ|RmUWYe<%T#Ux|aR@OZ&}Ph%NhPwWJ> zN6O@P@$fZbF@puP^UBPx|zXsK5WXtNp$?x_>&;h5KVQBL8L4T%4AChnV^)+9q`6zo#z6 z4!%K19L#O!j6?c&;cWOw9WRe!Cc!LQndWBJ*Lf;vzKSYNAI@19ZP9$~enJYa%^Sx+ zIj_kH=P`STHf$6eYomth8Q%07*IK>xo?|ATHZR4OKB5}uZURnc8a~@Qxae-{yLKeC zFSa+J!O~Gi32JwQb~rWw9w-7&`g`?ypzMLZ?`%2tyk}tlW-RF5x8`&3~7fnD1BL-V6V9+ES~{3=Mv5goplM|203#&q`7DBQ_A}T#zC& z>~zkAodx*}*@0`Yzs1P??G_Y3x)k0XXHRmP6SlqH+TA$GT^}c2Fgv-B&xgV}5BD<6 zKzo~$HUA3E`|&*<#$^j{_*)&szq(6p1sPooMw)!nHhqmOH2m75psSsG>Mlj{vH_xd zv+-WJd0J7~`(#dB`xv-Sc7D71_A~U2zwYMe;a2Zs>7%LeA@qav5)v8788%h8_MVvh zX4AnBY!A&`{YREV35cCLgotckAEQ6ksmA3Ee};0tpEoXbIySth;#IW22BB}hc{q9; zmrMX#K5%eA<(c#Tx4l_8`{Wc;KVQ3N_YR6o{Rg?r5_@=s%HI^mKzWC#2sL5m&X#1q zc^cV}jy=LD7J*|(N5~anY;%&yMQxM`RNM6S34T>Pq-wQ0e|hvSRv2Ggt#djv&MhdA z_~UhnFfHB7$dj8{&u7Lje9U@7zyRulF*SDVy$HZ(231%XhhAuT9*&ebvv3T_rJN1k zrRxp|;%j_-X4K-~8@~OUDu8JPfj2@k5HNc%V0tFjUO1%t_}%ua(O2CkE!YsiM}?S) zHot&*KBssa3C7B;{F?eFyyXLP$<`KRQyNZNB%Bp(1RC;&8yIIbDOc)cx%_dohK^M) z(T$Z64V|hq?9DV}A|zzosmDiLQ||D(N8R$9`QB`u-S_#xGH`?(5Vw=DeD z=#dlhE}}re&FiTWim9O{n4RA2{ws9hEW%*Bd22y^rB2F>cvmN2?K_ycy|5UIKTtk| z%l+6I%pQpGG+VH%fohg-LgpF4%j0EX2FZ*jdf%kjYC8!|zSmU0H}G4tg}Px+UZ(2) z(Oj+V0@T_TT^`+a<~eyMUw4{|ruRm-w3cIX+JX}`oJBy-ZnaZ2#wZuct_sCt z&wil;`W~V?pq7K%tO57`Y%e7KT?4`}o~D{6!>b%fih_nQziyQJ)*FpUA;IN~4&3l@q#FS&Rr`dP8H*OCf(sc;byA&w$uD=!!nA2SjhwxX1lR3F)?xaPe z45(^3=6~}SlPEajrdkxwHWW3gbkY&DY1JNanzqpTG8A_7^==f;nZ~w!*~`B;%j(Gs zE2L5LY;IHk9hQI9MGh2Q`Ictb$;M^5Zl|H4WjCSoVOcjSg5$DE0oLw?LSxEaDIFgE zrU#Uitstv8D8jFB6(*ewjlZQ4*TY3ByaIB+yaE;Soz%J89igfk!{oz>!^H=l;bW!! zoXRLo^Yga$b(}3BdOwDqK?RDA99Xe=v$AIKs}Td}M@H3KSgmqL)jmE^AB+^?w9#DQ~yjSpi?^TLYU3{gF)c?7N;Nk77IfiEtaH= z`63AacOo}qE$6H~3RoMVj@!bKm%q|jsOvb{FP7%@vIDml zZ`3|&4*c^DW0|r*{Lb+sPcVC3y`xv!fO}Ta@d)5%skqnZLP~&eO5YAkp5Ek3V;J4# z=tnN6ZhPFir37p@vy6`j$1kh8pu#`{(ApR!ZXqYb;#%Pz+|A8v)MHZ{=pNSg&S<3C z^*IS6KPEmCZl`|U83~ILm4IRty}IogQ;3YT^TTH&8bW>r>9%GA;@kI-uDK*_XD2Mb zyCEM~p9VL~+6Ax%`@IeLz>K{KySS~u3q1V>9ncNTcw)D{EDG@vZ`n2e!qmfDa(6i2`0}8S8f8tfrhgBgUUN+SoP4OK_A7p++i}r*M<=G7{Y?rd zP{UAqE-qI7eCV%qz}aa60e9Jf*2rkTjr^RxzjhWphj06at3+5Unc10|cEiLwsp|yg zJ$+Sm9Mr1pP1^eM&3ZhDGLFmr2I+wg>FeF=ydv}7( z++CCnrcd#+*ybE+Ls8{qL0AL<=Q$f$lwIu#cv3RW-s18SjwsA=%aL{=Ys(;#&w3E2 zMCh|bz3BP^?{f5@97%+IU0PhU8()ISWt;1_Q=Z(rHc&1yJvfGRfsK>JJAS_flvJ+m z<=igYuMfCd2zQKFbC<8N<6d%#FU_0Y{*Htw)}4(h=569Jll2K5S`21o5*<@94`Ssakf?-65{B`acm z`qDojUx2$(q*^%`_$AtICIAa_k96LS?)sX7wuuN-hk4>C6?9=|@*oaM>*05a5dZC) zGTueK9Qe$(*}4BuaR*TdmdFP_Q6sy5ywS~6XDn`(%KD71$)|3)>B+x&C&g?P*+9)$ zHBAykvR%PCAESJhpt-JTe-X0&qX$k+0=cB4JlbJ3**5sHS(PbPgjt50&`}>9Nb=*i zAomOvi?jRRA=?TbplJB9v6Q{T^5ZBn5Dd_p-0p5=%B8&AIUNx8Nz3u?cXVbONK0p2 ztFNo8ZEma=@RJt(#7g!+%2T83bS!6_OMSwAH9HV@>u7Z+y|8!p{$e3=yPfR>+)8<_ zpA-(hqA_6PPo5|+4>W`=x(O&8cJtzZY;ff(iG7P8vVND;nUkrHx7Q*W%Z-;MVOQ*6 zVJb)6&4zI#^}hQ|vSBq~x8Ze`Z+Md9R;-#EGEn&}2B%qg(=OM_zM430+fnW+>+qB8 ze|`?--o<|Xf)#fZl{#JC)yr=+qtd{R=TL`|gsMU79MaKZfO4@TTqVv24&QBjy@3(1yv;~1Ax}scMZltV32~r!DeA4`?$>l!u zTq@&D-DQb2m`yEBEmH2Q$;BxXxKGGlrDWsau>9-*LH*K}F#2Z-b>Q%zIHUQ0DOxtG z?rt+mzqySm6cmUp4fI>5$ruL^S)=Mzn} zeM|@sXpsWg**Vt0$~m3swC1wsgS~%_$m??)BP>7i7*xv5tK_~VAvWBHyw2Ts?}ckp z>tEiHKcFz>`Q{i1j$NeDaoKb@Y6hSy2o442I~dpXvDtRrrC5Gx!>SWytx_k=UT+>x zSPKm4?xu#S`euL1Od{H?{H@NF&jJWLUx5AXN*smy{SgF)qS@7F6*554AOCgcLW+s= z+=>L#l+-rxez=&q0RiBX6x376mO`nF>@Y~ z$EPCXQs0e4FGAorgJzQij#OX{7tOH{t?BFxaMjIvP}S1)!D_#|fNOshHWeV}AlBV1 zU)2xSe9JB1X9!vMhB@p!s&5LzJ`4n_Z71q4ob&$6Ky*6v*~CFQ>t`4`&18Zvvk zEPj?OY|otQYle&69e@$DPXD&wjnzN8|K(Uc2%T@u!b-(TFcyE2`LEaoapyv~Pi-7u z)cau1RO|asFG`zWHcYgFW_WJZ@Co~l7}7A{)Sd1p;pUkGX{n#Q!2x)UI%>`uF83Y& zIGtEK1R7kXgf&(V_*JFGPaE9LKNlWTpSZVL}OUD5v8t+M$~#rYK1uriMo1K;=z@e75; zhe?aZbXdrPE=70{;$J2Nd??^8w(lz>qBP^E*S)>on8J}{y*!b;E25IhJgF^*u|pNW8O;dCe>nj+F#=^E3~-^ zPHk8ByDJ#-{C8Kd3o@@^#w$K9%BIYyY3q8JzrDTu2#nfWk10t6`aZ6YE&8n&R`4{r z9Bx44Z;iMct-URsMJG5;?{j;6)z{}tid>m!bm)Lc5NO5aIWI}%+-5BrFroN^`p^kx zgX643J-B1qj;@9*r`4iyd^5&u_usc%kt79=J*mHEfU&%nY68vgT^yyuHz{8|s()=a zO9{c`JBK?6Ztf>sM~jo-Hp)=Q``uOX$(3uv}SvH6L#!CX&A$$CHBh9A`byUId^i;QH zyIxW~)20=hQxmKLq=XE&B(|T66z3=(IPD@~$u5nBK%0hN+}UNaKf#RGsNYO4<-yJ~ z&0svgM&yyoim(f-vQ)t;$2h+`6M^VeLfD-2`|Rq3@WOxQ0yk67DSH@S+2YZ@w;JXf zF)-E`kCfZITaf#nCt%Dgs}JNzX`r#kt< z=94Z76y|g9^R)uK*nzFA)dG8`$a-y7)DJ7dP&rwY^+7A)P7SfM&SHaQ;ffFnc|`zn z2Bkc_%s2eEodSh5>L|9$tkQTj|F}}&W@pH8%Asb%kCF0b0eCZmHHr2YH)%n-j7xBO zuZBqZG-Q4wX@+%y1N`t#X`k(H>4OZ;evOv~%qM@r$0w`+H9_-hU$t%R1l_YEw_D(p zIj;hg2*(`>3{=lJX3KUt4d!aD{{PoBqU4HA)K;ZLJiIAGoXsCuy-tNCa&EBcQ>E2w z7m+m{`phfD*ciV@x4XT;>+<_0R!VOK$A?QKvU8Mr9qeZPFhhWLRA9L-+ycmESv#1b zTuZ(H`MJJO>HugsR8d;|-z3M<32dKMca~+Rrf_yb<1#_TzFX($=wxhVMR_KMLc+xI z!B!cIYi%tvLF`o;Qvu<~>|Y(}q4=Ho*ZuPFpC3$!ds{Zuem6SkGJQa&JnWsMXvoS+ z+Y=Yp%p8M%i|T9~+x9r?o#gP&>FE2oYM<~Vx9cXxREc&Km!thuJNSm>A!hH*aJ%{` zcjq-30Xk4Th!bW#)W)A~8eP>Q7x=<|X0C*c-PwqLv@=UwdA?tkc#C{k$^hGh>B-4i zqRHQN1%LcWDz6&9{YI$s&+^g;8lJO{j<@zob0pl~GySQ;E@)79{2{iz(sgzdJoGIH z@`0E_*d5_!Gr2wf{i=RM^wS&$hjG>@Egv->(&v_qGxqbQe zs`YBKxEu841Q>M${s`QU$v?a zS$z&I9|+@@H0g9(n$=4^Y;QnCMEkjZZEJkg`&bkBuzxf3ZhPel%YQh&Oo!5++>TT6y}#LuD;`2Y>O^&3^sMMOC*8NQ_^Ub!^B&vGIgk9HOq?Wk{;W{{}LrroK!tiGzsNV2pR)A23e zPd2Xl^KeS4jmR{Src{0%bCBCuE6KjYfBKBi`KaroWwXq_yk@yI+ap0O_U z#-^k<_3eZF3C(S;qGZ8H9aA3rD|fi@ox!olP^fQvOEdD|WXClyYO(ThQ?cCddkF=f zEndjo&v~9#iz#E z9>1JBRPj1}xG$!{{Fpu}=ZTvnU+_-tjh(KV={BUKPd>s#r=WcP=xAx_acOjhN%RFL z2RIAXO@F&{C!GJg^ZZl4=?v$unAL=EYL}vP8xR4*1vrD^<@N^0i$)%%*laE>rbSyGPdC_Hs%2d47e?M zTXT;oTk-lt>gw;<1Dc6m4;e(iT#H*h?Lni{@vMR&f@EySrc>@mJJJl86DMGEZ2ajI z>U`^Z))kNN6U}s#>+bf@|7^KvRymaX$Sw*E5%R05c>u{?`YbUOU^?{`J5 z{8^yF`uc4nBc#RJ=yANjrPn=vdA`dR#&2R8r?buzEB%8Er@f^!QR=Gd{q&UA5iJVA zAZ{2S)tH>9(6T4K( zjrr}j-+uk&)7evy?p$zbK5of>8PT<^G-QcjEmTydIYrnq!WtBbB z;BTsVID0$NQStrylCQoU&$XIN2sT?$pcBtjKd8+r8Qhb$zr@tS>Uy=>-(O`5*^OST z?@euOb98pJyEMkm8DyacUyovOQx3Y{!}u`S+hxvkifw1ZhO%J>F3)jRua|q`Hg0ci zUc$4SXNwHWd$vhKmIzZO%tLAH;W=`*3&FPSxiwirIW8)D$jjkw^DcKBJ-Ya$ zck$x<1Pwlb`bShMSqz@ zhm=ZEXGycIQ0m~-4n}9X31?IORI}s4S(X8<>O>VTFxkL?j%iaVG=(J_!kJaY?(VG) zNT3PV49t%*f{3KmVAnX*_iybTYBRb9Je#3t)^^T$HhE_BHr-WbZT}!^+p<*(ZVzm2 zUcxhb+z=?1JR8nIp2f53HEQ))Z+rBchoKn*yD`Vkv(fA%Be!KQO09H5)Tscx7_>?v>Ja8{|pN05SW*SK78(TIUKt{^3e zm=xysSk;%8x99}W*`U~ouBlyJYQwUG1;s2ZGttdDkH)$l9e&mJae^HXsyheA7oW|g zmRJ4jFBd0A9eZ2rt4s4-iYLZLKUm!Rdps=r9PB7joE;=4zvQ41&t`EKLt<9At($0W zo5AJ@D!hL#vLHfBSydC7(1v;dvOrFKFZ7i9^wzH%w-g9-mDbEy| z(x@Fuw{tu{wgNXZx4l!6nyR)gmu8&iV(<%f4&mzPCA%c!$adcCQz^E!RebpH@#DvC z+NeL?w_YvF{^oBI*iO9DUQ)uPe6haig`TXhP45s&qm4!^T+-k~C@>|*Kl=Xi${qwmKK6g0^hyJ(x z#kch@D;Cq_UVDp&8OG`8OE6A9`;B;x1jkb#-N$eEy$iI}h*9gP@&ipZu#{CQW>Wp3 zrLESmxIuXvua=4?+wJ-0`N8H~J9D|oLf^pR?%z>rx`b!LTQw%E=sicCZR0BUo`XCq zS7qMw*5*cUQ=X}?@<~HB@T{;2orlA0%n<1uJjyqnrpzwQxjeELh?ui#9LqVDcN{ka z!eqI3?*r@peONY}?~CY~<;5Kt)0tq0^U%(;2yZ&=>K;(6#!e5j$ho9ju?uGYT@}BW z+ihWaK2lz3XJ7kA+X#Wml&xn+C+Eb;{95|U=ZmxB!)LqORZ`Wn9xLm<&vi||o%oF6 zC3Jd(&P%Q?5sor!63N-$mmd1l4oY5FdK<5LkSK0)kRWaCpop@4mBqt!R7hD#m=zK8 zIEs8Ot=dA8BCc}1Y~h*lpO%4bM|P7e?#zR>5H}Si-bXMqGs{AyXQ|PyZm|X+je4aA z%=7%=`1H8v*37*@jo2T#Us+g1pq~xb3}F%04T&t==kPL(0t(^1c4%)t#Xi&!2m+DTJ9 z-^To$x`d!#UL2IcR6Go*K)w;nBT@P+5NM$HAeI zw>L(gOwF_L!Qmq}zkg@(34Fe|I6Vx(G7o^`Q)8=O-oI^ENxO~nR>udgvILm$Q2GWh_hrQN6I1EO?2sz*KlXEKJ%Rfpu*nJ z>T=#fnyqY$0>ozrKO7yOT^w!By+iYuJ~TGH)W5smgGSWZ+XCCfxuDM|w9IW%&#EO6 z&v?%=weiQuCF!I=ase9SR7_Nr%mW$ttS)FMnAr9?|P+Zwv-v?IG2a=5OPpu zRa;}DTz_X|N|B9baQ=Ecb4r$+TumSHvl!LpSvu7$l-75bIvCnTg5AaENHHl&{n+O- zS`D8?GVD$N{OSDkX~+J9t&LS`MTIqKtsBg*FyC4hg1s(z0mf@R*@@ZjJSTo@53ay- zN>$0;vVf?|^JV%RgxEiIeW9bE)b_mwJgY`5M7XsJv}bl7&Mm6V`GNY9wdjQI!N5B` z+?wutNio#R>l3pp1YMtEaGTBd8-))+p{31M9ag5#c=Q zeYQdp8zk`YUDwE9jms>iNh-6OHK8T1tJc=`QFPtDj_Uk1dCsBIl;Z50foHS96*`l$ z$`n_&jHR>}vGJFRiU0WHwg17$xTC?VF4pJ2{QBGP|F(r%|3X#S9#v(k6dfPE-`(B* z=8bjshJQ_A0y!7zMJulFJlE1&|Mj22bGUxnox?1s4dv!M(}eE^_p~N(Yt2v7=e)MH zr&3HKdXJpdp+w+pWAZJA?DL@yB(}Ejpxuds<%qLU9rQUxx0AhH&ND_mIIpiX;dy=~ z;Oz6lW34AV6AmL7rs|6JHfgiUzjC^)0Jd4!L&J&$s$ZGu1MVHmwsvXu2axBlrq9y9 zghq=>9e9p;C&{z^|JEuLaLE)miw-praJypPd4>H=qI!Ob8Z%0Nydr>W>8FeHN5_Y^ zQEEBW)5=y;4okO+{d()?u<>b99a=XqAM z-Ym7nOB*}8&z|`(AZ>_YCC2A_E0s=M(-$LSQ%f7PQ)U7k3iS$p!Ooe*Dbt% z}@<`Y$d8#q#m(rv9_cZOXGrzIGb=?|%bI(V}Y(6+V zAy~$z!skN$_tF0DR{ttvnP;ae44Be{HB9aAJV(Tfz~;ZouBz$LKZECJSqG}&S4Z*D ztMbfFvTX0Z($C~S3NrVf?eA-c8+BBjo$btz4MED_`^v)lj?S7G5zAmU4?5r+TO}`= z#KTBFs~T;gG0(2f!r2Cgg~=phne`ZL?iwDvW;83q%;FVZyGAe=mX%f3tUejnDH){= z1!~t<(dVFQ`sZ}{&>c$|VHUEMgr+FQMzB33>C3+7r<0uMVpXsg25T?_2KZ14m+9iw z)qa1JywbQa0ff!Mibi?fR*f}-hct3q3Vpb>(Ea~C z|M}X;=+x5Y?c;L|G?4pTqt6j%@tn+US~0v;l44J*4)bD9AJefq(~xIMJ(nfVyg1`o zb9yZpW;{=fURM+>*x7PAk(-onlP2c>2TTlOol%jrGyVGI&p*F>H9oiY;OI;f z4P<2(Vq#`)Tk&?}nQRr+VKH4*;MwJQZF#aW&ngXbbK549#KE30Ju!Cw?eI$yRKhu+ zjOjTv()s3X`+EYrVI@yF51f%!$FrW{q~?AofN33R%z)4$>R^qgYBo*F=7IZHcCqmr zCCSw?qELKsSrv>K<;IWG1jsIwQgb__+&|NRaBJT23EnObsvT7j{FVBSYws5AEpw{bC!=*A%fT-yQJ<}{CK8FY1?pJT~Y9G3|=Ey^Hkj0rYLF{D5ahZr?>n#<>m7siW|g*j8E68&7yrx~^E+i~k`@2iW%~Xo}mynsS|{SL@eLr#<^yYi!av zU3o(1<@q-U9!;CFtFX_|stKABqs??yt;o{DEd1$M5OwEMNAF;&Nw}4iy{;e{lZe!F9z>)k*8Lf z5Fe*im63(8B2fmFRoBQ;^ImG4k?QLt)T#xRzyF=*EAbqjyNUy)~;pI4R`ov}WkT$L7M>~22NmXhbEJu8psf*Bu62QzOFGo>`xlEGe(0tbzg~ z#b#*RUIWr%Q&E`H+^>MJZF$eNg{zCr?Bv}!IN00S=8~j_G>zvaQm>Jndd1N>h`Y`! z!{+}P%QZJvxhg~6P4i@%^JgeDZ&ZV{Asa}xJDUdRKqsEHMJ5B5WPf|6nxI+dn>Kb8 zWa4uFg9AC$vZ_c!Ts%6uy+h@?b`!7-_>& zp3y*UT~Jt>9AdTPs~WKOpyTPylb(I=;ilx>`qBc?0vhkY6gt@4`ke-J9o1Old4n9? z#vC`78v}t?WupCoIYUOYm6&iQ>BP#^*`(XgFBF@s)@u)cN2NxrUk;A6jaFutNKKro zP0JEm@ zB{B`mt??W-b|3-GDXVGGtaU;r>?k>Kz+fwoW(T63?2+voJSZ8EZ$JsRolWGumUM@9p(>xP5>* zCNf)G4mC`JvhQ#)H8X=bFAMZBlkQpL6C{5KE66R@G$7s{)R)EE#7Ll{lcPh%3sAHQ z&L+|}ea)IjqJMzJ9Rg)VYU4Gd$-Sv|KpLonL0o%&+1GZO1-@%@c3*ruFZn7@ zbM$XKOlkTYX!Z>;{*b+0!m)jF{_*8`wss5+&QVV+K9cWBXCr%UqZY-(f<7~LsDDEe zP3Oh!!{evNPsOx_#BiZ+Zq0TNKOgLxnBUmLc7tFKhb56NR&QUT&sDA+)9wGS5zlNW zk+Zki$zx?{t}@yO%|HF5O!JGO&bIg6V%X$WVs@pa8T`!LWM{r9+q0fh@gbv9dXYrn@{_6*lGp6z<)K+E`EVe(SXQ}RCmZEYo0pO6|oXj$BDaz}wSpf6Wx z406Xg0&S5w@vIoUf>lJTY*_%@#3!@2mM|l51_TloEtE1FTnE(_0mU72?6LG%_!Yu%PRAqiE;-Sw~)LnZYNZl zD_WxvUv?jW_8ls@0(o>yrDvWV*M=aTJSJD__MbvCHuuaGn=erd?MiEDYG#UB^qX@- zbB%^Z+U`G@qIhWUDW4;CC%^x$Sw;t2J{*DWEVI0uF>NaTHG#KZSiHHiogIImnVlWK z7g4Rf&!L-rEB7FW!N1|ksW+YVrS^6sX%@@C9D#0`XZN4iHFHRXTcmy$S8hE!#L@<6 zReUNW#?9gK`0Hyg+bWApptKBh0ciK1LwLK)+_ur4bU$OK(p=&Dhqto`<>2{zl>ST+2eimSWeo;q^xKmIgQs?5HEVy1qC) zG1~ry*dzwc_4mx>n^EhTmbuXWS`-}H-0dLD*CQ92`Amv)dx*Oh=nxW1P?sDy+8!%2 z9T)nMd^615qZicn_Zr=*#wJrtUMuclZ<9?8Xq|=1O9!5H`pMnu>-0V?<+GUXFf_tE zBSw^MJ~+4;f+4?rIyu}>+J1veTHY- z5fPVgb!mBnyyY=xt$2O}&q`aJvD^F5%aI$?y?Z9m){c=B+V!~!&lcVW9i^YiJ}-$W#GUs#Sc)6|@2)sX=-miSxk zFu?LCncHriciR|eq|o;ODGR$mQ}=DXFT(#xpdhgQo#Jv%49vtfGPP|@vAjIv2=SGXr zmv)r)RGJpnb3ptD3X0=>4e{jB4u zT8+2id0=-t@EqnNWkE5;xTn~-;PR#Fv-;-+rd3u(@a!@F51ZJBzmD>ksMlY51Fc0 z186HX3BZ2mS;300#B)=7+e~h`&*ndu@|^TJ@w`GJ2A(wwkP#(pb-1yzvMGz(wqjw7 zzHnxJx;5E8c&%-6X}gE%7&Yd$($A9TdY*0aw)(dAENyE^p2f-ba-NsaXW6#Ex4uzb zS(uu*_lDEY#%MX4t>0p3gYrZL6PwL6*%?;^BdM~It6>G2H}Rksryq}Aw;|ql09sM* zDA1M#2t>2RZgpU6)xa!|X?w|hr{{$HWbym{fB%e{foZT6TWmrE{~CdxUeuddJ1;|784Ak{_x z`H%7ZfYI%Y@O_{y=uRI=o&~e&whvY(-@bhHaeD39iAobxoe%?JW^WtLH0rTzw5P|& zt+&ehs%36V&Z@n$xX-2WFz1=r^701MEl!TLGwpA92e?iqosfeH$WX(7)mxX^TpcO2c6 z3ysNbR|K=TEsj?RicL$RTUcK7?N20;n2CC0po2&g7>I8Hn{D1>JfdoQi%Uj#q}lE@ z;PkCe9+XE-Xo3;J-IclJTOF$S{x|FQ-#(x9>}b|EX*`A3BhMK4-ixZbR9_M25P;W% z>7mvRl9inuT-nZ!KajJt<6-gbX!?~I*TrYpLm>=ivK-#aFXvgh8^CRNcd5Nyug@x! z$n;r7T*Qa`dX3qPJR8rn>pVJG8AB?^7q*{T^L!@J_3kslZ3WcGc?F(PXLFw!O|r2v z^}Yem%ng%3Yx86Zm~d&Yb15W+)S9vF9NQ`G@h=9iP{k}y*@u@Z8kdQ4WZLh0mF0fd z)pepFDz|kRp+hYoF`g8QI2#> zObcORfw^m3a4(oJB&yUtC873ZPP9o%qWTSl%kUmn(KnlIF#&a?t$SipVQ=!0?oNL` zBaFYjvBbW3R%`Q7ToYc8S}kz360`ng;i2@srUx0u=_ulP<@*S6?moT|E!_X9JU4_m zeM7*D`Z{}?0a6;*%f~*R%?~U3&o+B|k6{M*xdT;3_|kJ{wzlysd;9!k=i!Iflzp!3 zYaCsMb4WjD=C+UaEa4e_r2JyJ@~WI?U>4A94zY8qcb=Ls2IXkG)g(eujLPrzJb&uA zMpsi3Wm_*>B$@%HD(P}T4J0cn#(sQSRV;3t_3rl9BmVw%c71pP&}MVb&%w0|v|MN@ z^uDfCv^Tgu2Ok=XJ9z(@$qlKOtr@NCN#P0*kuPaQAKX1)8wcvh#%h- zics#oyKd(uH<`9Xnby4TyS7W0m=0y%oJ7NS+v($2T-!c4;eyThB25}R+S^;62@q9R99uZ&fi{viZ1uBY2Nw6l{kBeB6E&i%?LG=^J(BahQ8}Oqicyk5 znLCA_@}IRl$n7S#$Ty&^*~)0W%8iljP`fLg5iOvt%3I`yT%8r1Xfq?|ju$>+;M9JH zW&X5IRC#}rXtWugZQm63;88?B&Jk5ku&ev=^y2!Sg5Fl{{maG8jt7pk2A@Njy{ik|6rjfhG;jh9R`A&}}h*_lxX1c=Kj&C)eceZO@wTVEvd;)Q*nZc9m~}~JIU(i!SeodfLbh*s!Gk0Rz$B1VOUlTVmCo<&2QFm1658Bb0XT(4-*e7 z;n_E^%IS0Tp9gl>8pDQ@9CqA(CQEYbfPt_Uu{NMl>XU=Tv9`{4(;K(Fu0KS?l0KW7 z$9S0A+dWEGS#y=DNi=wSY4&x~X!P;J5oJ0!5Cd=v@E+tt{kVH)=TH8Occl-8j z6>+h4wJaD`#`C%@ZE8;~4L1HeB5?M04v)OO|3rx{rU1-6lu)ZjLq%c)#nv`<2pAp< z=wNaupq18gg`U@>MaR}q%?N&&Uv!W0JXR6@7)^^vyG-lqmLxjxOlb+C8Csb+j8|^i z=8OZ3LM@+KCuWM^Do}yhJ(*Rxx6eeN*!$x6_CW8_!^y`! zxG34&-+9*9uri(_&bes@PNz&jR-(4ifu7?((VS-t?xfE+gwfs(JgfbgZO?A4_da0a zkbuVG#yJMo&ldwTqeH__7PrIZhpx{l9+vTZb{gVgiDw&TpyIH_N#R@)-(Ff>Tia0C z*4hFSqt+~+dL(6gAP%lMX`eJQq zmXP{g!bGg$;Z8~;C#Pj=1ExDCcqbBI-ZN&o^gKQ4xlL<^6rWWV6w?oiRYN&JtOv8@ z&DQDb>uI}46E&pr|%5^egdmJQLRK_7xk5onxiTf9HrJ{OVU1-{3XEN_f%B%1$2 zaVM6y<`H zvEK9`bDPsqx&~eKAUe&t`+Cj2$|GN(&rNye3~PmH$2sPo*+$X&-z^;G_O|PDu(wTb z*2Vpwv~F=@_mo9EiaC-JOMUxfxDto6OylH+vAEDJ;Qv}M}s*pE*o3U#48K+QM*+3uHY%ps-zpTW)>W9&zG>p(h99*bh{vx8f! zj6-0jl2Mz6Af&@pL^D-0`@6t!GR6y!`WK1f?BJ>xr|0K5j)zyFxBj^Wo^zSMoT)8& zPWl{e?VP#YK%Y%Dn%-~@x<1R=w)8W5L|E0(I~ie0aHQ}y@h}UAnLNX@Z7s?C4dG0l z356yTUEO<1qRL`n4m88%R>nBZ=XZ{n#hp^nx$bAPwpAWR`q}eU_~Rk}%$Z-UE?f4w zM4#7Iwe8Nc2i281(;~+?&v5S8rUS+7tJr?5HpgCm{`NtKs>Ow~?e~#j4m?Yx1J86i zhP8%`g3XQbFjMJ%P3wuh&%>S9KN97h^Em~o{6?AGoM!4Cd-U}jA93N!fDXOTvdCjp z%vp^j`+Y{Qm53K^?0Y;dn%}|fE)Z=%Tm7ei?iSPnIudP^hz&`##-cgW7CED%T3d34 z<-VZlGp8gx<5%|)BlA?2%DrC>k92*|JhB~Ytiv9od*g6_V4dCNNluT^qHP@__HQXl zR>G?u`oF?+4(H@Pm+*YKt?i%(d%GdemY20~Sm2oxR29wYPH=M@oR3)$O~J$_yNaI| zcs@JZo*irNn_PW%W^Gm$4=d`k)ERkpp3U6W&XU@T4$HJ!C2(D;&+8k#y#pA@TdQO| z8UH5*$N^k;n7*lm)??o+Vz0w?zS9*X@hp=&zf7S8w2W=Xd2MZt`6E(jVm{Lo?_Nos zw8e~eR|jY{T50l#DYS?N)|FM9bY?%0wjyC~26r;KRfKNXd)H;u+zewK6)4Vq5uPQa zF$!?JooE9(*xY7x+cul>putrN4bd{YS%E5m=132Sbvm>H*K8JqoviLGFLRRZ%`gN3 zt!NLa7sb3e2_+AzU?SMx*FLVMtYk;woD9<4rhWl)F)BQ_s zZrAzG8<}Ag= zRy53)%t($bTC0)pp30*2{N>2?vC6!458IZd7dRq!F@J!Ta)xprLRZYigfod1i6F3eSUJ-Um3nf&MNdl z=kw2j=STdw^Bk(T5lf41OBEL^`&@5tZ`|6!?cMHOBKo8at16s#PIS#}fLlg3-x*g# zdtrr~^}*o}q>!}JG%Fe0zGr4Zq~X~t?gTXQ%tE0de1#D1SlgY!7c9JJQ)ml{EzE_4 zz2aduAW*(KKJ=loR>_CPVJ{rVDCs5?0|d;$M{V%&Tg!l0SUagXlEp?(2Qx)*mU9$hl_odE@c=Zcx z=zt;Dh-Z;(OV7a6o>{6sU<19AW4n43c@BHv*6}RU-_Vmni)U4)X>f7x+Tz1Xci#}3 zPiQB2ny$%1leV%Tw|7sPLMz7ITit%9AU8ngedr+3rqT688?XhAnZsE4I$Qvp=oA=J zq}x{1Xa&XWQYWG<)tndUV0MS$2Vmh9pc>^#ZIMlG9_QzYky(qSCL$=>6Y{HmqN?VE z_)DLP+SxgaJQjo4N8YoU+0OCj-!!%8_uqaw?;#(-bdx8{EV|xql_uYLwrE(iwYiUE z%6x8Bn)ST!bI!Ax^8?TCKX|!sQ+*cCJsL7}0?smM$4RPoF-YR;R~0 z#}*$P6A`mz{_Cqb1w$}y_sB(f3eU-Z4hDBA&vBSv6lj-IoL*ykd2#0P{nr|2O-G^m(4?)*cjtKn4u>l7dY+d~t~<>cq1a z!pky|4sNuhGAf{uj!cJ~7C-R8CIid%)^|OaYgn6J%Vm7qMwg1&-7hKi<{cU7`ltZe z_5nFdodE1oMxHr>lxQ z;|V`vxD2veQCe!GNc__A2DTiX^?W@C&}p22y)<8XjU z`pmIrICP_uusqIB_-m#X7z%!@*QyIhkGFrAy-m_8M7U!DIz`2@5Vu@tQ)gh7UksxZ zZ4wi^!RFFGTBia&^jk_l>9ztw7z;jhc(S@DpFVwxWl;dS;6;~F%R8@!bzR7oKhdjE zWTXpd&tT_(mKW`QbUrs9i5AnDCoPeVV#$%UX<%Jb|0{gCQMe2PT%$xaw-@!kj$E5j ziFFCs5UqSQWhZxnkqKeG_r`%g6z9M#d~Dgu3pKEpHCnpKn7{w=EB#%-h> zLFwljo*C?UO5^_S*8bDen1IHdX12je$I{qnWp2kO0}}1T-WJYQdU8f=TfuGlvQkT{ z4ciGr1n8=rDCK!;mw$c#mO@P$w%G`0++|ztH!BQQ-8*{3OFBQ*07Lzo{F5#4?46S) z&`F&QtW|_{iD@P5dZ%u}U~;QTcn;-+u?3ZK1HLU3df*vBF--2=P)jO_CY5bh3J;4J zxdo^*ggLTbRCt8vN~L8E1f*38KAn8%7#AZG9ZG3rce_NZ#4`tU#u02A&_uOkPbKXx z_`90>stCD8lZml*W)`tc7R!j%y>eKn!(EzH$j$S%uxZ$E-)LoiWvk;X7J8pkY|^Vi z{cI*r^QJ6)D>X4lv)7R>Fd5H@#xFmJ5a;geDQ*3c9M8JGBeY;$H}ywZE~Q&t~lJiGsFJV$fea#eAko9f2WObCb7 z#JBfu6HaG1%BpSMkUve$MNBsuyknWyaeABCP;u|*CNGI=h#B}t=(7dH)Ky{?CCT0{ zde8%=rtU$j0)!Qa!scEfw>mZP{>~sSv;oaL1qK|L6D`CR78oaJKSOc=pozrsui`^n zR7_@w1MS5n={MUyV6%(t$bUiIt-p*wbVS+$V-SrGU2vj(2!DEWOr!;L42{X`&MnIn zBh0pRQfu^xN6E-%@kDL_5A1{G9gtQ-R;OxEVjfFWh!yk}C+(nFo*3(?#z^4#CibJVd*^m}cYp(GyW@zb=%+g|cMh`R>S_5yy0r{D`6 zFFM4=9MK%k#SmG*q}}7+ic4!!fhViqhv=D3XU*wn8PCd4+hPa2u(kZ)tB<6ys7A0! z2*3aO>G;{EuE~^nQ(%gtZaiDuTQ4P=+n4bCr3~kPzMj(7AIb5YBHMlt-iRymT%LF4 z49oqOJZ#6=qiPNPXHJ5=z1@K4M*(Q?eZ&qg2dH4Mr_n;Hxo;jUk9AG<9uVSIbwRMV z^E_wf_MYXc#4{z+b5Wj)Ja40L`#Tx{K2HlzJUc^*Is1|$1ln*Wal&9>wnfD&I5D1U zfp&eS<};+MlKX52#Kg>@hdLV+RDcbmU*k%&H7yFQT+K+A15NNZ>pfFjjjq zz=QIzq|XUw+16*rQs|o|(0nb0zIgO>b856QUDf2FR(LjfmS)X-2<~%{=RL|m_O`3N zj0{tvDh0K(qo~sWt?yG*ymMqm!#SDm9M8nm&4tF!_YyHH;#NqkjAvDemGO+rJ2y4n zHuQoFjhsb*ese=(eCOpt)0_d(pllM&##ZE^aiTj&2Bf?d)`(l(w*6HGv>!RTo*+|_ zEVo2BzZdMy<@UBNnfbOgmIfb0oP`rj zd$o5^D9>f(fX4h*=QLr%YtNtmsL6s;i<^hY^uIN(=+}#*5FXPGAMa_`Me>QhedoDU zq4V;LBNNwUJfm$s&EOKA!&Fb?_Cf~q$@Z9J?H z4wF8^vzwq(5$6KWR6=Y1vvMAU!ph4?Tz^FRI8747Rw;C`7pU|YOb3J8EN)_AnZ>QP zjG)muq}6Ms&=qF!T8Tlt#QwRHV_idJT_w>0Xa?<*6|k5yxq)DD_pVo^(1%?1812F@ z4MP($qKUZGESNQfsbHyHdfZ(4NVLVq>@woHXp4*Gjc$NOq1~#HK8Iv9M5hwa5}uf3&y!mO1pqWVX-Pgp#lD&Oblgh0w6vLY<9$SjteJ5Xf>$YZ_aEPLA-E&NuPPgX~(dj2NeX? zjFBt%^l3vmeP%cb%~iIoq{*|vV!*H=#O-Gt>}3Yr&NFQHY%WyZ&TQZO%x5Qq+s$!^ zKcm*OoM-KjmE7k%&j-xQM4tOe6tyyoWPD;~W#G^vbasu=Y~bWUE2Fkwlu~5e3K;A`6x`qB2ZP=;Xr2 z{v)pPzcH@}%bS!19u%*Mn(-UdkR$rG^b$%*_6NTb%IVLmA+@zH!+YX^{wz~?eP(lvEM0Ycd}77waU!y$g@?6bBwn4Pf89k^7Z} zjR%Z${!Q!GPp1dlYZQbk;;o#RhLCCfpX1pjx)kSz59()f@~pwEd@f`pG+#SLA3dTV<3V+4#(JvEqK@uz-TnAu>KJI_yddSxzh~E%sLvJ=Q&pM?H1ZsiR;JRi zlG_y88=)o8@CMH=(4opBIIauxGnKJ7gErDO0zFE*mF@R#0}e26Jf?-sz4Hu(?&)~u zUDia~ve1~h8cB$qW=0V@?`5}Dsk%AdTwS6cZ3$_BHlnlCl>@DotGvw(JeIf$CEYI1 z&GcERDWmzWFxB=sRwpc(mI=>5vg?c|N@*JFCz3+hb4V(Akz)>dC&}Q16s3& zUe)uQ8)ksYXm}2LKWpn3PgfltQtf$S$tq14ipzjLB*-m`TdbQKeSXwCS*gtLeWJ|@ ziyOQg{Zf6#twey<7c=)c&-3xwQ`(5v=cUe0b7PgBn0&Z#$mDnb=bUF*1j<+yd6oxF zP%N3;B|Lj>2@g7kxTCf0ybNmHS$Cc+14ApTZ6C0rkA#Q zVv+Z!J2?F38M5pmjR$?UyFB@!vgxyX-GdhSjd|W=25+>s^Zghd|2!sH)xR`N$1^6Q z&YpN@$M&^zK2e!o+SFLH4CnAK8qZcEmK=0+$PDyIHPbD}LMMjjf+M6KQf%s(dB9W`24v*HrX+B1xq zg=z7k^s>^x1=3xa}W($df`q+i1`jXz|l?`jZ&6zC;Y-b1h7k5}eawhm6hNI}A5 z+iTiKXTp4G{j2l|HH7+Wi8H^aTzM~j)W)pU>9c}{mc9Bz`dkmL!Q4on8}S^5d&cng z)D+%x;Ms~j*}E#M_7u+mjM_TQPW1UHbtP6PZ7!?1tq0rlV-w4G&?#jVd6rUwl}ax) z{KtAF5vA3c%ze)3^FP&Uy-D$JqPPA4+0lcQsl^@6kY7HDX!oAsnFw52MC`zZ52w6U z@}Q%^okkB?&Po=ymvA7?0cVoU)0HvpCC}n4CEa?b@11shr^=k5n7xVWASb}Jv9U?f zswztk80|8!H9&?eXmkvYnO4UeZM_5YWs4W)*BO6d<_iSNApnih?Ldb(cOcp?h#nA= zy{)A@bX<@fN;)Kwg2@;ap_XTs$QsZly)vfBUr}fh@?6AW^z-tjg)~*5>_*$wr>ZgH zW1VQaTBb)B??Qhi@73_zvB|~i!8tYFj4Jx=^ND z|HFh|N^a>B`1>rrZBfExXImMpE@t*d=?~V@yTuz|t9EEu6lU1uzmnBdprijhY|R*i zHPkCMalV`He7Tvzd6n(=v(TpiAcFvCWZ}+efIQ*E+O$Wr$ z%Eb8W`jOHTUQ?2ICNkwX9~=a^+QiIUiM>@)=@bt;zIlY#$rj9&dW^G2;F(uHv$S>e z>9ay%Uyr$ln&|c`CJH*w7Vg$59-FavyN26F74FcMq$&sfbjEmo&2$A@~zh)7j6Ub-AHhJ+%L3%kBxJ!JZ90v$YP#kds^OKll)qGQ%7awBHU zwgsxdjk(ZCp99cB2NQ8LO#1gN002M$NklKIn4;f`w3RN5*xW=FEofcc zA}V&s)KA$0+AgbR8PSR7ZA@+EgQpc=ot3_Lfw_%4QAK$^&>f9#-%uy;>_p3n#(|a( zZ7k=2CR$B+Iau5&zHDfkO{xTRfoD~CD$;E{pu^`j-stoAATAxjtQ@%?4 zh81|0wGGWz(B}-&{E^|@(1A`kGn|BM0$X{TKKri8u#yck$avOH?uv%NvlVX>4$Jg8 z@%&k&l2tl9euO-qo#I5xF32-Z{dL zVs@+49*%wTs+-d zsoYpR)b2Mtx4`oc@}7xEp^Y*;ne4f~I-is0q|Y5^r$>9e>^0MrXD&vS>E)e6TIls^ zJV>WYis!>K54upr%_}e;I*W1(8R=k9XiF6%m=WlLKpV|OpJt{em~S8-no1~b-p0%l zfM$0qS=_dZc2wwo1DenQZiZO^7UD`b2Dd{kntg}fNOS()g5+5bSZo<_r0rsp2Rc_; zqEazv7S0ND`wA*;1!L|3dk$E}t;h-bT;MsVE~1bd!2c>X*7sd!CnEd6M&Ge(HUzUH zR$Tf~Q*EDAmHNj&t@qBwtcSl_b$g#Lxy$q zpO4Q@4tJJlg!hP7BdTeSoZr^aZ~Z&GGSoT6!)*SbB4P@1TT#gm8ja#6w=OE4xQb@C zfDSyTkXXMpH!9T|Ozm9z@jT~Uci$kj+=jFje=?(k+E1(3GLxIP(rUh#l)6JL-Ay*D zvwuVXPR(+<^`LFI%@%-lOBX@?c#MnDMruBEw7Q*WE;UlA|=@N+|=zL1pYdX80Y>w}`WH(Mp$EaVVzuAbqFL zfByOTwc+cP`Tm0o4Ju+%(XZzno6B>P6GYDMy)!#9sqpbyxxC=soA;naQ3ctJt+Ig~KeoZ9FHO-DCay z%coO{HI8l)P3B2bMl9}yCvbK_tRsl{netWSjRX`tv;Qn7CgygvKW>Z>L^|?(LX3nq zN&-2JP}bv5CZ?9EhXPqV2Yq&+vHvws^vigTeHkS@L+uPv?(4(qJdj3H%&p1X1lu8+ znrc(%05sYBElmmJkIxYuJ>xvtJcg}Zpc!N0Haiaao`CkoMK4;@i=62g=hpQ`b*D4= z#1+?Tl2+bsHL5UoZW+%?UMa%uL|@Kx1Ul$cr#Z%)XCtrm_iG9h*Azx8vrk?7n+$0E zm|Y0o*iZNfglmICZDVXRz1?Gb-Tg|TiK1gRA(V-u#K!b`eEr4x+>B>H)*pH`o-;`E zM+UWcF7u$3tSa&>KPTgv4_fk^cDj+dt;*w8coxvCPiKq>?0HJaOiM(cANI~nO|Blx z;O6@n<&kGE(vtTK&T5w2SbmtxJ}3Vf7!PkP&sax66QCz5^P3bA@{btL#Onyc=Xp*k zt5j4{ug^iCqrn{`Vipp^gO*hrd6uoMg$Bax-2Ck1MElUo=jIMdpod>|y;qpqfi@r7 z=7W;9V%WHnRZqd!EcJiC(ZgZXmGXH6}4fc(HC2=uHM~SRY?d|x~5>ZVyq{Ww}59>ky z^86It(!{$np#Nf?gE`_?F-uRG-)y$F|CKd7%f;}ed{w~EO&OWZIbn95gFNFnN1FYS zJlF-CDGo!P1J9W}JI@jna;!LgI{Ao(JqXPhJ_}IsZRpM9*^b2X`J<;D9X~uhVT0#K zk1TCk?11OzQ>it5amiI zpi6j8sUoT_ECcQN=N!*2weCD>$2wca^XSCXJX>|sWqW(~RxdjRVPf5X*G3~&7!s<+ zj=PHF*qM&q%?RWlue}(0Gd{Dt8TXj}g?;YW6N`QB#=Tb>fwru8@S!~t_a(Qb>E)&m zmio^S{q=ZGRLk5>aObsKwVpY7F5_7jZ|yu`ll0Y*-<0QHYJrBOi?f@YaT@V+dg^7N zr-uXcqcdA-v#R0Q!(mvB_^8&Duvn1i#PjjNCY!_hriqP!eljt;vU{S-O2XL$+IUt% zJQ0lpZ2~O^I(pFVK+8#u0-f`qWAj86584t#rqIG!-49v-s}2kX{A<9$&+-Jr;7=yZ zCntK|!V!|u-d;wD@4G^);4~lS${jz;X>%gEM~mcuB5HA0kvM5kDlLsJN;EGoGr6VG z`jGj5G`nkw4h7vwq~%FVqaA2qlsPE?r81__idOsTyc2Miy?vi5?S<9NJsR(L|4(lo z?oy;ZHPPL!eDqyQWD^O0t)47?8;?bmWotc1O z6N^%^><>$JQ*;}A(}<4ptT^;kCv;DpPhSgP)7{c7>e?gP= zP;Ti5ooB1vwz#D0GutBInws~_M;$kol(}u$=iolutlnb_Z)-QnI(asfDEE}bO$!zi z_tEDIZ6Uk-WNKyaf}P6T-j*xNlZb{X7{+eu3hGde+&`Zy4nwKke|~g+w6`|P#&gZ| znX>bxou{8X=oaKzJUh-Y!krX4YqKgu#4?_(c~WMtS=?T`p%Ay)j@{zUXa;Dx$Phi_ z3T;3e(L*C`qmQTfmWZ9y^1Te-=d3~$VC-c{aLC)%y<UUV%-4QrVoE@o^zmb=-`?(+l@uTiciVO zbM$#|pS_PY$8*tr&UntMNr-fLc-zhGUooWhpKGJa#9&on@WAuM#mV~Q{J$GDR^OQ4mNngiO&@f#zSw<0K}Nz_A~ZQT^PsCrg5xg`J<=EjQ| zQ0b>af-;+HSAaRsa~|iXx*)}TpH->uWRdP#FWQ1)Tu)?j2QNCPw0Y9Wmo}>LqkW#Q z7l39=C!o!Pw*9MeULZPcO0Au-Jxnw5My;a{m(~aNdrmdx^*7~}378%*mP8HD9|(_; zWTD8L9ZtRU!Xik-$M_{Wi-)XPdfif3zoG^gH#-n%#)GPMOw$A9Pz&8Zo9AF{d+Bx- z-!_HL^f?$US)nHvKCNIQ>&~^A+Zt4bvkSD|2Z^*+UZ3?QM|lpp0_%7y@Qe{wqt9z8 z944NB@a%JeX9wBQmg8zwC7f*lZGBiUbuxgZG=HFgm?ZN|aptEM--RB)u~Sj;oau6& z_csSHgmik)L_0YJ*;jAt%)!QQ?S&jq5v*9bP0jpzc)2Di+h=sq{nXFl6pcsu7lEBlO^Sg$1VT+rv7 zJj-_l=tpd9WeOdVR*&|pv}J5sCdqgv37qk4#^S9_*|PcK?PPDCp6o1RZa3mtqW$DC zh8YobdB6?NI%H5`_Csw)j5V&G~!wA-^Aqn z=8>Fd%T-07bLO@M#8O1e8YhE7i|CNHsv(*7Dyg10+^T0pxGtuVJ*Aq{BGs@ypi z&$2rscdutW2mjgRSwO3mim3I$CKu|D?b;t+)lI&v|H@8(-{uY@laVIZNnvdrlEShd zJ?j`)XD8YxqaWl2l&rssXWwH$0$sp3(A>n{F20A!uueFqM{@6V;+gP;QihH*9c~JT zshUKeZ00^|iw6y|%V(={qYOM8M%$Dvh_>YdO-xLJVn7p%;;Y`u_`>#0h+}x}nGX-k z@yxc*)s5bz8u!`#XOb1GQ!&3%;z6rli19+t7+=G0S8)3(JbP73At;t&Q)N6esl=sE zG42A-W^pU>MG&^T&9=^Ppyda?y{{Puc-YD0t}?&?s~ekJPBh&atYBy-S&rJw6Ed19 zal?~wuR$^`r$JA|>edTZd5I;Xd7VbIjBb0WY5#X)S|wxdPU}-=NuxbRF&5L&ahrEp z;5G?J3QZ%U<*f{81ln+p=bh{k*A4VJXK%azY&=u>r6uF}m$QSdWv+jt?-6M$!HB&Z z06=&+3?5UB&b7hLv8ko)qfg|dWpg99xFhr`#&hO3p6L7w%YWi7H~WH)CHd6VZnb!na$wl zMlwyYh}t8oOS6^ndu^uDNy#Wu(Uw@`u#hgS9qpd;@$n4%BiQef$=<=v!LyU&-6azJ zSMh8c-@5}X^QCUJYHtT}MYR1-jQiV~13&7sTB}rQmj-)Ep2w_HQaq>DXUkSuYgH^J z$xSAyk>_A;qtUL=4E#h?OrRB4ezeErugNWnxIYsQW9J$9er9i@DV&4qm0X*vF}DNH z2h~Mc+YRh(UimmP%IJvaCGc2u_ zZ9?^;J_{6Ev$Ie#qcHcx{kv)%LZST|Y$1+JO$0C9m03QUc8x$6p#3SpWD!fdf*=%w=3$BVQHcVWQblmO`6O|JT2N`+Rh3 zX=dVsh8~1PBuVyT9}Na?UUNLG32oHrTzlCyF~9lG&nEQ0Q#rQVyD&L^zaHqnjAsvV z%YptPo<%MECV4iNg>)Y3Ql2$+)y!=h?3p*WH6~|$Bk)W#Own!X0z4Sz*=N#J(cF$1 zD=}qJ?Bj!j9_y9n)H|cFezEd?;a9Ejd><_Ix)Gl%RX6B zXO_PJ&sNpxGe8Rn@5;E8XTR2j6tp^(L*HbuxY4}82rwgjEQOF~xTjP5ewPNxN1g|V zUMU?rJ^N6DfV}uK5bX*b<67*wXR@5DACd2@XoT~8`bc$mwzyj953*0BG`*3Mvx8wLc7~B%*xEyVCn(oM_`&$~?JO1IVQH|VKkW3A|ioAIjmWwo7 z+^z0tMv+F|5FArqr4m?HS`x<-{U`981p?w_>MH(o5avL14aqLj1)e=w#cYzmGb*QW zSkZkh@T{_IIwfOpJ4D08v#3Uy!wNhj&~#ey6DB1+4^}7J7Y{!npZL@2#$f1|IP-dZ_4F9bF3;jw`xvl9vOWkdSoRlrfKm2^Uc2?T#EXxc++O<`s#9f4 zxZy3Lxdb`T)_o%xInET~vhR@!vXytzlEG~nts^XNI-V)t|8Skzfpg^s_zCIEsmF?SUuW`>W9?~asKz&Ff$gS+Bl&NJL{%yaO9wUe zVHVm`kUK-!j!NzAoCzHdjpj_9`BEz{&%m5`CLBhmWHh%ex*ft{e6Uo95!F%UgL`zDv4TWvSv5DkF8u^3ZVedFy1=>b9JSdiJM|h(p3QmY z{j^K!g=p5|?FPlB%vxtsJ2lLy*V-%0|U_PU@ z_tE}73|4qhN2R8BTeEcVq#^pwb%JAC9W>ReMuq+DtWla;P>n>vbfCbb1&Xt$FXVY8_?M0YkgUvOl>6o`rLfXTo7L;nFK<;V|*cDTMsVQRM^57C?t3 zq<9k0_TWKdp^N9^f9^cIH@Uob!NB%$cbL*)=iPGJhc2Y6jOUYs6^vJDv(|XY znwaaoO(u+gT{H)tQ!551e+|zDw56;f&t6xO9O!It+P3bL$86TNiL<%Q26K>S<2hJJA~{(h z5RDtH4i^iHg^u!+y2_c{$h3GiH^GS3%T{E}6uJnsJuMuT7Ac-C$=Asl7_r>!bR?CN5~ zINi(MKHgix@^hL?d8TlC;AxU)&T(5h-wox~c=lH85D;sDXU_#G6J#!Qp696051A@9 zey?x%^ye z)ZZC^#v*c}lgaHJ8Y>>=c8S*DnP7O!kA~>!XmUd}_O=!#w>!~!o>M>8IJN$YgpZB3 zzZ(1zbuQBUFGa3~x;{Rhnp<36*H(N-@C?wV01;^!-L?YICey$Fa<;#*Fh%W{rh6+V z#=LJObEVcMcJigK;h$f7ap!$yar;OUij={6bg;D~N1Cq6t~NRN#&c^(e;c0d<>v&t z5zj?=HmkY_vm`nrS!nADaEU(qQzS<7={eO6f~qe&FvRvou0rw`UB z=~+6a3Kx0C0GIphH4p>0IO}?s3!{dNY4LoxLv%ah?Dsj1wvl0L`(|#ZKv?cPPe>zS zVNvucg*H55h})7@!GR8CV#)_wF{)j0LQKr7K2@#}v(SqkWtyWrEDE#(&ATXtCKEmM zQt@8<4Co4GQ(3mpP%0Xkv@wBL+yJd{$SUh(qN1-K)J4S#RU}SxsG!9@63tqHXt%lJ z2xQAet49NOKluA|< zsr~xKxN5genl%gEttW2%oRLbKPP-qSzy?fm%RRg6%` z!J)ox>U`O!OgrcNO1OKkpPtOeW7KvtFF3EaS3EA0=S-b*W!p)lFW2W<*I6npBz0mG zSDiSULBezLP-i?N&z+ROyZaogNjw}D%x$Ym)K+TjbDFu&M7K0V$i%_y?abKzHS?bR zgVTAYMk`s|r^8IjgadPZ_JN~(HSBflY znMNB3vbs&Bhu%J>6Ng@2{_+No#!{2^P z?}b&9^$A+jBfFt4tnuy}?1_cx9gOZytZq(VuQE{;%gdoRA1nVqb8osE zWwNd7))6x1Hjri#CCNm9&qH3+EIWrR*?9A_rC*IM;h`GMqJMyHdGPxOBuxJcQ3&C%c<>Ie5>+@g8 zv*@g-v^Z`a`b!CSz0?rUiRYR=x8ym@ZK^$^QLkHvanMc4v%;0t!5@XVM(89wA<%3j zxbr5WHHZ5P!{f`BPgEkN?avY3-cj`fJTEpDhXv1mn(y$brg4k4kDHV+9a%yr)iRA1L$5fEZ}P^%Vjo>h z?bz{S)0DTshx)JC)&fgUetHr^@A;TK02LoF0+KhAy zc{b4gDXec{Z>wdZ&mKabUA^tQrPeti&xFIsrD$tav@%fonaOkpeU?INW8{%e)VN3$ z69KS0M@yq)3tMlf0-%EsJmck&S3yEBVEY^QVkCX$)=ZcF8(7zP=19zm#ru?ndwvu% zywt2y^!d;8+yL~P>#nLiBX;7M;tjU?wm*|Xn`ec^2Cl#Ijw|JXIk8UrGgP*-qs(BD zfX-E6pzNVgm~hq|sOYnK&KiwG2hq9Jf%l?PE9M2rKEF1TB%mWaCP#W!?G_q{2Hi9) z)(AGUgJNK96kAyC-((@)T$-C0xo};(0q(tQ?;Iu@hjRPlY?BkAAf5kRrVK$U4ZcZ? zMYOE%7`&E73uTS?2B_<#jQuSRzhNI0J0sgfw0&vXd&{q8P-uc`VRo~t@&fI<57@7P zUrw}}v+X#Bh}H)_nHt@#1jqiH@obbU56v)3tqb7Gzo~WL%RK9jxz$kOS=C{|GgwA( zNWpW1KK~Xgu9saav_ykmVqT#MLCNC2vod~bdihW_+{$Gop4Dmyhjd;5Hw^seOKW5y zMhkf^X?Aq=^~mk1mA#XEI#ywWqE|5ro_Qe}Vh|AvqV=Yd3*8%K+;X8)L@c?`4eXQa{D}*zHdBPVq+0h)LqrG9 z!0okJa&YqY&f(pijpaGwinsBl)ppDk-2?PMu>CFDJ887)@7RLHL4^7R+Pt;Dp?+nw zD%phnydKURQFItkAw9Ds9ha1@1# z$46^Z*J6 ze*@1^B-YGxRIo86b=qVT&$+AwA`m$tkq7RmUZQ)hEzt%I-87ceE6lNMF+bK9#>^RPCjqY2;j~3D{0WF9Q ziN=ebs@dFin)y6wa^N8B=%(H|+}m8IT4h;Hlvv?ZtKcr}tcej}S3W>ZUARg*l8?@S zM>pw~54{>8(_wc@nKhV8$N@74i%xrL5AJTQEicYa-Wu&5JdY>sO&i*MpbeW5CUd}> z{@oAHDK@s5>&8C4z1vS4joaPY6Wksh&{C(;jBlp3DHL9OBft3JJd0%yW`w3iIi%0c zF0{U%c<0%Jf1c&JOg|@{M{_t#%dA#lvQ;#fEqRX2Q!Xa~9p8p3&vl@gYUa83QWT=l zVgLAWV}4>}Y>OzTPJb_XM(tMXjr0@SKYjdo=?grMbW^Y}uZ_=M)JvX4a*V%{=jf$r zG!}A540?lT&3*%|ub_KWJzZzw<$Lht>GSq(JWcJmf+k1Bk`tXfsUBA1+fF>EcB$d- z2xUbb_2l7~`p_bpf?C1AVya$7?pzW^xAJU>=ty5BjSd4N6k47_@gGdbM+tKyS|+V* z^xzhQ+>y@7jO`yXT<{q(&)7JwQgc9#Rux)9mI(cAbo3nDxk-#Ht8{63E8_i0W*#`- zyr@tr7C_HV$J)x$0-I2!(HG8NzlP$eXsp6>5^0VldGfrI*w~H}Sbwks>u`5nIca&) zB&6xBWueYEs~9v!@s+<#pUpB4sBF+#@>&(>{JMoa7y7KNrCFa}T_|>AjN3t~@$41X z=T`Zk8`WZC?%927#JQTV()?n2iD!qg-5n5%`*4TTU`P6xfbUO^dlS!bjQh$$i}3bm zcs^Ld4Qg#~k5D==IDzjtck7hqj?U2mVv)nIw9K zeOI@J+n&XOsyGf&v9|w@EtE5kNNE9;6Yb-yu!vl~1JCT`)Op*Ch=t)j`v%BF%EL7K zL0^f_oPd_m9Y9yNT&+r^jhFLR+33blkQZIP^@3yTG#>>^LnbxyOhxuv{U(@Ji)hT1$7sTy# z>c(EFNl_jFbAD{ea)U6ZKNe}{;>9WX=Tq(NlIIG}ewh)jKkHjHefAHKwe573dG=-u zs<)}%b2uz{L8ZB^a9H46nA>*Wm^}(kR!2phQ+(Sz6ZgV`rHtEg*S)Rz;a7`q-hcdX z)8TEElX!0*epr&^+LL+aHo`L%-1@Jyw|O-(M4EGH?}WwP%X-Z0nAC!q(VAzXM$P{7 zne6SBJUhM=QKcD(kU$qa3ur<}-a<*}h%k<9B;Aas*V;OVZ%@b}l|s8~3Fo(SfO`h| zkUix8U_XX=w&x!>Db%h=5a6l81pP|y>Vwh5s3zC)Q?$ukiMZw(QYD`dU}^bNHkr|0M48~4i>br8+A zJO4Dldzo^UX4e6y^7DB~G$&3{piMz1yRJAJKKZ7G#=&kjhWz^Y>GN0PRDvothTtnU zrZaO!hAybCTj$SaoJ)N+()w~{yLkQ^crFAw2(H`O*@JEtXiXvUEP2+!Rf-bOYN*^S z$ui8`^F6D%&$>a1hLzc>W^0=zR!GGVSGv`jRu}KEbLo<&Gt5<-th;HF^T5U zndil(y^;d^rw<=4b4V;~o+lyNxn^687hXP#xCnQHT|}H9 zL2U_;1kgVE&;4MMLep&ll}4at8Y#9dbxy+ogt6_*uFoD8IDY}RL@$nA(JSN9RI$uRp>=d30hN~~Wn`fN+Tm|EISqX<} zHBY1JJhyh7lT3$nCyCY{_yHB1(>bxU8fuhhc}NLo%CzPSQ{UjTHSx&!1KmNOL-R0`|yl9x8QjMo`(pB9qO&d=4wQcMNWK~5@bR^sJSHonYtaf zRRYR0;3GU=-gQuutm9_Kj+P>iios1cX3op=!UvrM)phR5W)CVaM>l90e=HqmL30Z=W(#N)W?uirJV$Ih6uNF;H~gtdpIh+!>OyWEremKK_^5c#TyX9V+XTXw zOVRDbv+!x;xrTEwsod(5vSj1jd$c(@GBJ1mU6p6oJIqLqn}&HN5MrL2?Cp{6;qHm~ zO&yFCzdL}|N@BB3lm4&dSwfdR(Gcid_ldX_y2cB^po(p6xf+9Ljy$I-!U>FlYi)$Z zrf^F&&DF4t!?tTcv!{i%H0PeV{z;>avp=zJMjGW6kVH$N9TbadF(1jRFt;??Z(gyn z(C7$@#rv1bphd|~dXYXs90<-k+}gb?Y2~uZo;^7}*xQEJHQI_30dPdj+dd1PL=r&dmU9ekP+VER^^)#B#RPWiK z&lPztt4N526~=ZlEgCRRnhnhw;Xvz^@(oEwzRiB+wqdJTzZd3v&`6x~)`v3Cg& zptd}#H;HATr9787SNvxMOtkaD?Pihbe{9YW)ZI8S&q`^KN|6oN*Ic_TxPN;8{_aBN z`L;&Q-sZT|3Du`0o?|k5CHZ@KMtCBq6h5?JsPU{n$WNw|0YneKdhtRVeB*Q%I*ZYj zhpwMth%R$@8$heFB=)rR9{%GW|ETk9v%BCqvs^`*Z^C_6Xd)O9Z4QODs}N>)85%2r zRwjBzezbWG_dzc!^1PQDj=(bq(d_MjGe3%?m1oS|u((T}xyiI+Hqhj@l-y!!IoYlT zB?R;s34o-`+6k+f1VXd%=hoAYNMozwBN zkFIu4EV4yceG~rp?dE>}Jn1WKy-K4Xx5je?=+^4|ck-P1Zq(;So?j9hK%S!$pyWB# z1i*7Px3!OtX%)3$(K+aJd2a1LGxL~KSbVGq6p&mx?3iPHU3v5IL`IduVca+Mw6339 zm4zCf_ux7F=SF=V`9Bn@at`51;yD@(eGJP$+N{AFs0_!H{6K|^B4VoeWPG3hT-LglM^!`{W*`>Y@f7lGbK5+tgcF|M-8t9= zLW%DR{y!^GPnBnd;vDmI7l;*T7WRC==#rtH?#IQZ4fpWf+33Tq=Z6Vr!rIeZzKIxl z52sdj+-Nl~Ac95TBUP7RZ>QlJ6SsPLa`gfS+uiSV3qeeYgO25S%JqiST!plB3!aNF z`NiMBbH(DWY{AGFm(NL@CDBQsYx-=SbFM0Q#;lT681kH^m8>U6!!pmOc+a1Z=Tf1W zQBnv`JqIfxIe6Y#oV?Y&c*KqjQkQoLqhM`stT#2@PCy6Gw??(wRvvU!op0j|b&pIg z^}LI0m1nUYG|$a)JXCOwmuKKP1;kXb$S5kf%XpZO$R(7_Wa+aaN&&P8sM*`*IgB3> zjT=q7tHE}+>*1!**_OT~lQ)DjgVGXg?xfI{Rap|&5q-mW>-pp8!x`x2GXW!#;LES7 z3>K@@=Rm<`o&4v}l~h_Ox7b%we1rhnCazd!c;5(1kqkF^1ahA*h2#5&wjCet-`@=9 zn6i>20pUQqQ;L4)mKG5SG&CoiZGUL@iPP-IZ{E4ggt*u{F?T~{qg>K?1sxce*)dRN zvCPn^JoE>rO-j4N-2t>WCAt_(rQO~HbEWOpT>s^FzbS$B`^V$GzB#s~<9};^p)G2W z9jD>eZ0*yn?Qhg)uQHYLFxko#GuwFTp=9^8SH)k&-WJU{xUFM%tEJm+?HSoB7YuOM ze6A$*0mhXJVzb9ogeFR^}%;QCbtT0A(};jMJiQzI7m+l zT|xRF+D>%2Bq^F8f|NDU_~4DJ*V%RF13#WQI# zPU1tIqly^nS*>W1rU-#!-3UHM*6mQVt@xXR?FxZ0w4!UM%vYUJd};^LTw0kqfu-^i zNwk3WgYs93jnTjD>M$o&-aVo50YCoz(GJ0GY@|+An7qXAl<}R_xF*l1sq+uv*;H4t zJ`Lim+I(Lgv_V))rd3&!2;5^3TP+tSB@$JMM#r1-d2BHdNjR(ApOo8ZDb!J78S5Dib4!mT-p8pkE)!A$SIAQC;9{o>lgu zc$oTY{(+pnKcd8%lTGyDwe@o&8Z)?W5)L#O=$!jj#H=sWx!_sGN0hVCQR{!$b3ewtQkoV%pPPGKffXo-siy<&?}+7ub52%;KGS(SlF#MVD(`)sEW+FF z1ZXEG+-KEpt24fMW~Lwnb#2z4sz6&Eq>e zwUyShf@tE|xui;3u{;vr$2x0d8H zKLTi#irtW%-Ndt*b<+{Kq#XHpa%q>qm6rjnM130_@}IfW+-`oIcn%{UpPdM})j7Jc zDBNeBoy+9mj`vCp*!oO5Ncx;+oARJ4u(uNzJgep~6?>k}a~)`;gXt7JQ;Hi773aF_td0+i zuhBmF4LnnYATw;K#nxvV-kE3KphvAfY-H&5@W^!EDLhwlR$f;MbK5V%=4PV15jUJT zB9>04(@aEKJQ*d>r|L6rJ#TP`)T%@(lDIRHXi*%sRY0APj*K9@$c@J89%17UbMeLV zm+f6N5;xIF>1=X00nJ<2N8m$wB#G7<+b{xX-2sZltkK+tBIX=rdErTmGy`2RGBCM$ zlMt=v7`V?&4QVh2_Z@=L@ciM;{&xS0D!-zqjgncp%?j7C`s)O=yB`wlyRU^xYrKH= z(OiO>cF<@vAA`c{`#RYQ|9IHFHguh0NShJlRF56_<=v22%@G;1(QUVt90CmdirM}6 zO54cz{QAE3UtxB?BO^Ue)tCMc+UA>0o*QEni`IZzKY$D37 z1Vd72&WS~!4Rppi1;vU>UwPrXz~A#tkVY#c31U%&nNPq?BCTvv4(ll<7DKjnejVmE zJd4@=-kYlJ`tALlz0E#~tMMT;b(U89w|cZQQucj1A)u`J>{shUIyaE1@-kKOd?#Ih zS2>{kzx{Ue?&{RgD=4{s{z8`|IS-rH;qXe4HAw%RZ^!f|fpz{R+oZ)0|94gzkZg5B zG*u=8!_#YQlKw+Y{6F5=acd&-*!&z0lKXpkuIckva0Yk(FSMDQHUGFlpG%%c63?o) zkL+`jXR2|X1oh76+=wo$?Pi|+l{}eR@XSiBB7@8On^SFLv+KPzo|_u==J@uTH@nn_ zWt>~`eDM7|qgztw8qcoc{tliSfOb%9N(x;Fv|l*R6ML%Wm5P}l+9RSbFgIVkc=>;k zXgqAC7>F&UtPX}L_G|W`-9fA(&;c}8yxEWTZ4me(S{m&@w@i-|=}y(FsI(-y09u*w zg5vUp7oqokF?B2o(V%MFraOp;#mq{psmo!0kvFzq&5Yk3e$__(Gm$YuV^xE@l`_ZI3Rx~*)b#lp!`)PP^B$Gx zwyR01Wh7@-XF;76`U^Z~eQx48=Rn1?Hs8v~i)dIg&UFXc6RGr{rO%h|y8F!j-Hsct z$EKDKN_)Gn^ZW_oO zzCA#TX$f>Fbkdco#r+k1ZrD>-h&Xtj9y$Z} zB+(Np==OfALZKzlv8<++fP9?zWIXx<`=jUeG(Ypj4_HdR0Q#`GTr5Yr(|&FxumD=+ zSVdt>3!pR4WSKiS(^(Z_A8vAREAmYE4@UyKLOkO=6TdUiFsn)yx$J4}AZ$-_gJ=L9 zIP33WxccV*MH60w)%cWqW$fp&pcIl zj^1H)eRg%2mRK8ADO<&*w^@`r8C0U%t>R%KIt?m20x4!)Od}3BW}Chs+1r{M9h+F# zcRbAg^UC6B;jqs?9Ut#3(nPWt6@#1T@E|;6KBf3}Cfd`xAUS4z6U};KKbmKot8$_3 zO@=~)u!fl~c}^Eve{)uTHlFmzQ2Wbk1J|%FRnRSk#^R<{%xSCeHJRo#`b|pVI+D=rzY@;7v?7wtVR9q9?Jc2d6yrdPEQo_%nk6iZcfX+Xni@5! z%ja`cl>cZ`l0Yty(RUqIjPw)!Fq<{2#x9FSvFK2d#}cj&(&Z9 zmUz~hq49%wu8MQidX`*DoX_e#SLFFiJWHR)#|eieo^{8ZeQw}cGbm@Pngtr4rvO^A zkW?XfRzOVptnkS80zTv1Udi)v(=oBh+D4!6$j6DpVl&hBpcNY#9=w46OgzjhiNh#) z`o?OKwD}aGzr?d5VtTU?63hCmJWsYzT~89pTh~mws6;VtyU-4b$p%$S%q*t{0h3Y7 zt+E~UF$nfuGXc@vuQY+$+Pf((k=6mUVv-D69fTWA2a2FrqtWfR%Xd|MO^91(Qr$e0L|==*BFB5khVoRd{*Ni2=_zev3p@2fpo_q8W9@uZa^n)H-D)1~ZPaR9p# z`qI8ZwXH>}sJ?qq`Kv*;_GF|nnRgqs6iEEaElHdJi%ekV`Od5!X4Wh zQiwYNO>j8MAw+T-Qfe8dl$Eey$dx;?ZwZa5XiULY;yKTsx6yIG0NO1qKJnVkB+^Ng0rUo28dbDeNVL4@8q)eBE0AM#{hK}1 za3jwYb7^0)0;;O%GS8QKd6yYzEsPO?!_dw&M?@fZ7?-)NmQ)Z;FUMxpnuyk- zzkhtQ$BB08-!CrU+4-+#eJ(LBUPVAoLAh_?xfquvDcPlx0!*o$5niD&8a1)R-k>cjl2|6A~!TKLG0#{Uc#8iq5@Jez2M7SGNl zU=3*nz?0Ti4eCJKU$(t`?aUSFT&E2wrouw~w|`SWku!T$3h>4XUDBLP?vzZ-H$u;~ zs&6Op<%j=8uFI9zo=K22K zWwl&SK?z6Tw3d5alr-m+iL!Kg$ynxMzctUeV&ku$>G(<$4bqy_X+#cJaEoUTsHOD| zvpY(@T-U9*R6#Y108q${e#BvU<1@=!hbQdsV(;~(omBu8LF&G$#)i9G+0BNHv+A=@ z_V`Yo6}WC1t$5a7${$_vZFtrKmW`6oXCM5W(pBO)l89*2op-{OfDxc5_6GcP8K7qcI*9_VzdFbFSh})@n*a%i>OHXu&M7k!ZeJ0!@)5fhhPv zrb$VU4z*wB*g97HIi`WCvilZXew8mJzwItt zHNtan6fChUV_Wy3NuQS~6B~Jn2W_7Ha87ewQ#&Si=#-$25mWpc2`o;IMWkIdR%4_X9qTg=*PROb7(ZhoTvHCg({aXzDu9A1tX0nl>LQMc&^H`K1PAF zTz>5B&aOBZBi!exbWq6vod6?OLO4v85zDQj+ZF%070=;6XX`F-R@8~4P#D~pcMQ($ z9&aC8S5pgBdv0tlBr$IB^waL{X5YeOk$)E0<{6v`Ahlg!JKWCAf@lA3uO$MSk;B^| zWc8p}BhNVligdZQ>EQBr^PFR1DzA#N5`i2;`K!X>j%+`0I1)vJEggA9`sBg|DYQEC z;6w}PAQ}LHMZsg_EZq?(j{o8lv)nfN&~a6G43nERtl-8(`+Tmb0HvHe1-bLGq_|jh zguT6t1-H}t=H{EdH9Ba5^RR#}ckZ z?G;YQ7#kdT96VztgY-F#2Rac9qRULI=C_8tYDCK!ECsh-Dy?-5)4R}UUZB#d8sl({ zt6l84Rj~VamzbedSC66504ck!(E{XINa^;w@acuvW>aG%-yoHt`QNaI8a zR$jQzgdr))^SM=uZYwk5+0qJUzE;?GX33J+$TNA1m4)&1qkZ%PysPT4_4QNsVhHb_ zw25hHO8pFq5!wdbXyldx#tSaJC6=LD09mUd z0*wY}$HZc#N}p4o8E$nODno<^(_u5k0*l3D*|bDE(yeYp%33LDRSDHsX-*L_-qZ5R z&e6r=!w$8$rVyPbH{y8?ect7e8#+_PU@hm;{?kc7^__xcN5BMg1?0fFTxXL1OkMcG z?8M0Rr#85MdB}evIx%gamANS#YR&N&lIg3cbi2)G9hg%GT5fo3*3i)!IvbuH*fa#a zsiWaK(S<^(~IKk1QjCccss*8*eAArvLUz|H`xq zUW!rh{JM=@47BS#wmwUi@~qXou8dNpn-sd>SwQCmbRj3-$a4xMWofeuT_|)4a7&~s zK--6wJw$5{T#pT1kfr&ViQ6OH928B2NfKSjHfVtjFFHC9y$hQgo*8~<23kBPqy;oK zw~cOqcC}bGy2Ip_5AB#(Nwk6ic_7brps~1jj*bua*r~+65-KpeMn-SZm0_`ucDx*j zax-0Nt(d8LTw%CgTxJWi1X(6IL&u468yL+zQwqE=Gv0mgu^M+G&l(0=Oh1s-&1l85 zsV4guJ1ru8)j%{!;W%1@qOr+koynl|)raHVmDzD^lxdNzDlslz{aK!)@~e^RQ|#@m z&uIxuJVz~!aA!R?fNj%ZlUK6|;1iId#tGx4kj8P4+B>I$C0`To}0(!}|Z zMQQ@~cbL8_i>(^*qOJkbXnz4;e7?SRvW7+~#QL*q`IM zny%8uq?E2=Q?EvZh9hN_6`BI*3eaZR50iyLt2~-Dl=@oNjwl4YwZ1Y>k78R+H0XBX z%6CN7w~}ZFx>Xrj+T8khiDv;_muSgz0kr)f1rD5wc77P53E`(*$^h*a-TF9rWdi6F z9uv<7dU>_y_~L%Y5_z7X*Mk?@DFwJSJ~h9%wzWs|$8^jYYezU-Q6>M+f0ALAFsBrH z((wG0LUnTNvp#bu`0~Qc#K;R<+{QU0jkTPkV@z)kL#*78C%Pg<%2$)iQ7V~cV>LHg zwyHE5<6WJJCKfg?X~!7ptBX2+`gT`)vlm@ZT)y}&o=bUd6$}IJQ+TfU&-$#E=V#CP zd=7?X?>WhH;@NVL&24D`=gH~*Xu>dJs!Hu(f|Nisxdt89U2zV4vGonG_nt^AomR4y}^-BoOh$@9qgfleNI0n4m3=~vJSrrWyZ;KnQMpz?mPUv&KM}@_0m=n*sHNFl=w1KWlH0za3ZWgL^lustw zcD6J+;mjNA+k10yZ;c%d!`^QAN_(NE=9bndG(n!J*wpWdU&ZR7xMM&qVZO<UnTQ@t#5IDqWkJ2!~C4Aefh zHcUU^Gztf>>A&285($h31T>~x+9`NxJyF=e29E9*S7|AOL(`|Hb-uSXUr7`()v$>f{d z)(_%&Yjdu!we>>!Y@T1YQRO!>xpIkOTm`uE`0v1T&i7DNHCt4EW}I=ROOtzsL5nAq zYm4Z+te=MmJ8O$K)Wl@C8#8udO1u-(U0)Dc}K+M(_&(IPnsG_Mvnu&?%3IbCv?h!e03Djs%YVR>WsK*dKo zqS`|jJ&5IMYxob9TM;)#Urj5F?d=}71l=IoF2iSOab~RjDMG4&L>p)fZUY@oWsVRv ztAAzk)zB;_Ank9n`T0xoY@T+aC-%++#(dQ-yx<+zLCC@5m*D3L^Q+QV0g(f?r@ShubP6l_b$FM*L(7E)}GM!Cs zsk3$@&MOe9&epW@Ip)BL-Ss8aU5yT1pb3jCSrwP0bv7w<6=;2`L=aEr8H7D5@*IEW zyE3|~wIu>tL@Qt!`zRZL#@>h|oCTWp*&V3p7F*vUa@2NS{gS2>4x?3|ytDGnR4%x# zdi=3ibMjo#`#HfcbJ$%Ig)7ZLcHrzmJ!U6oXmftM6#AE>&jrsAVtJ;hBuQ`m351oB z#*$!Xr0Oy&yCq!M{pizpic4@TclNzt6!i4t9v0(i$p4k{u;+#sdYv`WNa|E|j z2=L21PiCHz5QMqyPQ4TdWOG{+tFoPAzXVMNw|RD9Nj-1nJaq6JrctyCIJ$V}=rT=wMD!SqriUP!T#(Df zd``50cHT;hZCQ$2GE`IOnn1@b;%^o}W0kmQn(Ul&SGgOJZ0=H_wMzE%&^p&l-SpLRQ4It=mT;m+ z+6PE6MVK3%Mo}AhJ_Y8CHm63@_yEBb(?PT?Z&z1SHOB6%2ag8+$F14b9@_AOG}$ZM z!Lea_2NFY8Os#o*k3N??hi%c4W*5!WzQZ4qp4)%+r{!cmcAjZ(RcFzfWSrnO=HE2t zcFI*J z;5N?iJpAhVv!~}rXEu)Lm|oTAJdt8PHJaLn`p@!QG{-y>&lzYcKPeFln_DlU>7Di2 zn=|AkYiltv;NHtU_cvFa-5q}YO66kCUCGNUIA7E6FoX!CJ@e!I9gvS9tdO zs+bS@{hCMR;E#GkSPTxF!n2Rhas6omy0346vcl(A9zT-J?Sa8vb)bu|cIH>l)Ou?0 z3K^#D;&}(}g6RAmeJ^%j$w5E&xb5~_|0OnGsV~u+y}lb0BOFw#ozXvt=VpPfVw=^u z&U2)-63&lpjkMx9ZGBeud8*{O;y%YI?wT*UE9pK+jcdawNA@{xO{}*m4APS4>9w7I zT;3*IB?Ufp1kX#PZz=*EFEO|WUtWFsa(HsZJ2H}QsJB=sgJ+T->KXyk23qcbQ=jsn zm4FTpnhi)%@LBhttNPr)^KuMrWk@C5q0b@EQfMFXFeQ^aS950{+6{4KaK~bb4}BNM zSoN`cgc=s8j=nuiW>NK5WTThd<-i+dwBu0s$Gy2_$Ce+LS8*YDEaW+TrEsEc;#P>Z z(e2z-4va~n6&A=lO_H&_u(-OpM=OAZ$vBAh)gZxb6_c!N?EKrMe==K&a7*wRkc37&H}EUeRl=g3-x z$qmRFmgxwJIrHyWq&wd3aAt%?FXzbgEB|^Mqnq3n8a=z9=tl14YoJrHSR+EZTfSWe zx+c%IqPS09)m+2wPwq&!t!{<76&FiRbg9t^XrkDAKD3d8q+W3X>(B^qZEj(CeXHm4 zKaNxMy7A(tx1$*pbEql_e91s_d|@wJ<+U%~xvGL^nT&nx&KPNXYU;olpi{=m@$E)^ z_Lk^qEdXW~X(Y|*S_R}^US|`vzb^EsorHdQ=;tu#&po{<>Dxo8 zCs7^e@WwOG`g#1K^i#dCNcm&8Wa7DWofFLhx*TWX*|7o#3Np_WlLXI2G>jtCJXwkx(oC~LVTq`?jel|#vpEl* z`{>!-tk1VcUY~#V>6VOcU6d0pK8fv71^xF@2$UOm6K}jasA&#m<$A!@2DFW^8 zpIissrLekV8G~pC#xO^eF;bIV#h4?v9zZW|p`hB1G}7HUI7nAUoM(=8CYr}e#5wO4 z=<3zhjfmBy70eb00nL&w|Wd z#YkSMlRiTSNu$v+yZs&ZRwL2geRa+ny%CWy+7fLq&yJ72R(IcqQPXGP{575nli4rO z=aOZ;`U1}qau0jYX>XN!lh}Q(@a%bIb30|LNIvJzVFl0S4QZq~-3{F+E&RdI8F=2h zytln_E*-*LOGM;y%djCqx&z==fF7=ousS; zIt9f*xbUEPC8DRNm4Z&osT0jQsZH4^GQ_?6_cPJjgmTN9j5(J(Hor-s9p_ezLpFEv ziwb*NH^o(+F}FD$M+*sKd6^ThXzRVV zw}V7a-|Bj9pe4}Re@;`p7Tm7#ObZ|-swA0ebK<545f}>r*LTovohYWJ2c){s{rcp^ zZ8q1R{6_YQnlaks+~zF##_{buHwbjq+HQoIKi6pwpsPGn0q6b=)nZS{g!Wdc_{Z6* z)U}EbDk4G6LJDs)1=0x z#FAEz4qtdqW2s?|$e@)KBcw=EHI0Jgc=0#zoM}$cV5e-eKv$yNS)fax-LHhpubmE3 zBNEZ7o20avBNw;S10;^rAQ?@hTdC^ehzYb@CH4wKiD*76tmaD)*Thh#FO{!-RT9vI zEt46A*&RUV3ear%CZgH$)xUSZK1t4)pcghZNw+2lZ7=n0Y-=lqH%mp;2tuv*#PHkV z_m?;;ew>HOffyX=9z0V=NYpfr?sQ;cg+$){9z`>DHkWRU4_%X8%&6m>%RFu9DilTw zqGWID1w0E|rLJQ0m5pxo_qD19&wPL2O6u5s?rA%_0}gc#&+ng3R0KLU+^LM$b-eH7 zOSj_LdY|4zlf7NWwnJ>Iz@CxkWQ^!HsG{{tJaez`&oa+8`kHyJvz%^)cw)q=&qU!4 z*6r<*=e0iUZFs)B`=zbV6?^;h=Ownx#}Nq?yPLiL*Pjktdp)tZ`_7ZNoWtprCv5ou z&t+MO=Na?O{&P`|7!GvupS2CYy1%N@;kH#gtY&Vfco<7$X>pf8qskU(xaMUFb6cRf z0RmdbOi1iF#T~`EY@!JYj!*-1TajeyLpR)r$ayOR?IS3KPU~xt<+*l|bZxj(wz?w` z5s?l)c{K5J!5En%X*9_zhsOFg?(dVx;W%Z^A7XnRiD!&$6jaB*9-cXr2`g>rg3)OnhBbx<{{bth9tgGNlz9`^%{CObfj>KGdu6`5g(sd(YG`CDWuWQTD4p&2y@qBK`bbJm<8P z4n9r8Sck>!tyT(1dayr3r$kYjp)w0fV>ed_jExNsX=h>pL?`R}+$ANVvAHp%zz01{ zw<<0{<#JSBrpv=qT%mloQua!LF*g)Zc?sLD(niYYxxc)!xqElNXMK_SH5pBGsuIqt zT7cY`gd$S+>r^vQ9D9+?W@s%mG>0vwPnBl9){II#EBnlG(cr8q7N(?vgznPc%bIm* zcXf7r5TiJ|lM&kve=-qmnp^TLnn964V{bJJqhw6U==2Zc$xISQf5>?BVsviv$fs)j z{^4M4?iN)KFLg?-jP@_`TuO7nGjFwIIbN0e{8gTl`>fz!BhQ1)JlD>lYv!37Z=8!8 zQbF40w$xcuranW;cv$ASgLcgm?XRbnJN9?hW>4Wbe>wB~88MK-UE`SpCfl)~+D4~0 z-vTp2Q+SPO!p3_k9{($OCTGQH@}V8(4uOsqfm%`pG!iX$^yv5|@}?ueaHHL+i^RJO zTxEx7o10I>NlUMY7SJWI<*VdCmtXL!%I!*SbkzQYZQ`2lR4}GB45VpH0_{6}3p7*F zNpWn<1LqY4)C%C@6t`SZX98!_w9y$&h&L93&>+v?>>jP5&kk?1>KkXdfY^+2>V|ke zI@qG&-1+A@0i?NIx3rTwH}k9ppaHX6cUxl>7{i3FckC9{Sltf0JsubuU)b!Wsg*ki z_Ag9PU~>JMS`Vt0Bx2j@(%Y!dt$1$6xr~Q>mFKh+w9@Bho^|}N-RE2;tnjuYr|yK2 zSkc{RCOW=)MxM9Gz>LH5%GUn^tx6gm}*QRwP$_xj%P(cO*}`Yzbah_0UNn>xXUgE1~?dj|uH0XN(o4B7E) z+d*btY2h^pw1BogA05K;*2a3A{;ZIWQm7d7%tv@;7YVlhgjwp2k=Y@`usk zI?pn_4Ksa5XvGhQM8A9x;Q0%x>wey%8|ag0boAhHyE`7xI*{{y{`dOa_$xXq(@5Ys zKk*xRZU(v#=&$me^tr}!3U4FNYBhxQ8s}C`M?+|7gZAa+;Hg8bRU-~&E4rLt>Z|@= z&3}$eh;q9MwCJ>0)hckS;JK}Hx^Ii4>AqO(S>>6=tt|p#+|{cC&pXGLbSP2qj7ewR ziHBA|YwBub2iic_w`6=j&k6y@Nb&99Ss}5xAbMs7qRW@n%nW*2XV2bxA{QdFAw~B?+;5gyCHn zc}$$TLaTD4$}{dW+o}|N2%ZTRWS&n>dUte!^M$r*&yj%GB-h+#VJwh6kW<1~@QggC z7T)-u?&#^QR}nA^n_DUAr)c!Khs3%cvyE?QCl1y4aJ0KTO#}YxS~R{tUkuUFW{aCS+r)H= ziIoCjsQhvSLT@lG3|Js9XmfXv%a;r`Zakj z?d{K>r{UQFm~3~C!t?8w&#nx-=$cx+{DIlf!h_D!w}2WSItRH+f&S}x4uQ5p7eEu^ zW`sT$5}h}>WhYuE%N!oxv?HN(&ZQmv6=^hXw5m%~04mp2wIz026VS=?DDCYQ%3QlR z`Ib=uYoL{jjxz{GRd>DN(uMo%^Z#(4D=|}Uerud1 z&HUd2&0iAFk*>lyZN+mxn_16y-yp)Vc$zF1UoZ9f^Ne{e)6m3)#;C%3Mx|cc_`=Rf zG7j<_ZFsKmocw36I3O(#`p5BnivR2)G27fA9X&~=Y;zYh1GMgg1=@iO1FhT9K^n*s z9U6V-V3$z0I{V65(k2b1ihSm>Oj|k8@{ss=6^$0mRkX|3ah-g5;)>{c0CcoE@Xlzb zu7oF{?(N-67mp6_-rwMeSUb?X=^H+^igJ(WZ5EPlnBzf6C!%#`dq}i2p%Lh;&%0Y; zZhH}=BO>82+;hcqbVBq8PFC4oQTCfE*iIeYObgHYFx`n~JI#)0>jXQ753qNKX5HJa}4v7z*6-)T|;ob4wO}esE&O%S7ILboVWQOF|;Vyw0Lq;~7CXkzW zHrBG&@1=&G*Y7!bg>I|*gz^%CW&;4N9t!6kchNcU-6why{r>)7b#nA2Q8AC!_IAd( zh{dN6wVtYtNlJ8EzN+x7C8034D?D>fc9DG!?>WU`+?Q^L*am?NbutjnUpBW{Xf@9Y zoDM?Y!^!YQ#SXFrz*P}z{pJ5L@9h%_0$^vLl)P{$G zXUUdqZVy@9=ut8wenfoxj67HKR)U#PEi8$hF-z1_NT9Vp^6up@x^e-z|-M0a)Lb^@KQz!c=ZtFTp`SvXA7}NEP2*n@Gj?6GKT4`!W8?$q`O@* z{%GLBts5(QZ$B{SIZJ?(A%@#t(C`GIRKy#q{n!0~|J=xPE&nWSPT8uK6qjGD_|K>C z>|^f{I7heP+1JhT?4~xsGtq6=;#Q(zt<<@|ImvTHpIh;~v$;4m);==dvHs;&24=dV z&!nJHgHoTzs4p4qqJo>Xu6=BQl25&n)8}nLG%vCTT`2Tl9uG_Ya}nmQIMIQ#AEzSB zooYYhet7dL#oGH6mq?75!d z{p;fw_|ZLTGxq7@(f!2w@x;V&k)r$dJ~7bkDO8eP~~`D%2#l7AN6(>0z&Hb(;9L!Tet>z>{`;+RQK+vch2uQc-P zWk&)Xa`c1Z+h6yeQ{t*r=&5j|1M8HxDtBjaQVA(3+Uf67Tq0}JK%>#e2Rmf`ZYZWk zg%E!UTdOXuZ=z}HPzW9%Y#Zd($DnKm$}WB zG#_oR_%@4G@Ql6v{^XA0{65xFHuuo$PR_Q`aqb?8Xm^X&VV}q0 zlnSyD8%zFm2sDqmbKs*V?PGHrmvm~{hd28xHZv%9(%4eP_aZpWkvf5N{#QAf# z2gep!+6uG!)E8BK{yd8o$$iFa$@nM8=?=ST-89+L$?gpT>9pf1HyJx$fViM9}ta)4r{AcH@l$1lC zKd@Aw&s!XYqfgHJpbKi3h`#WO0nz3;+1|yIct)A+UWZ0wibJt@=9OY$nBMo){Fc*( zRAHh&^DS@VFw_c+$?WDRyH~g7)(_rrS#drnKOLgJ?3oKpyhE$Xa{+T^SN?c%H@3yGlIoZ@q>wd0 zlF97*;)p9$C^Qo78wk*M)IMEjXZ2n@+F7AiPaNG=#}xOqQGf8YO&Jg@H_+_yhX(Ib zP`{&=I~oJSvyJX}Uv>ug8}UEbe|yY1;k$I0v$;Jpt%Z$MjhLX$5)jYqjO8#$cve9R zAN&?^Q4u}V_4;+EWIBk(=*|XsiF5ZW3DppYu89d6EA*^;i04KFv0a@yl6Sn zNUs|3KYaMhxhL(UqwlEC)yLkBh4HTT*rw%pcuk*6o=bhU^;|xjVq;goY%TYSz)z7^ z-qPMq*=MT(^6YGtHdm3(%+ZTvvSlwU5nO#LAkuVUN<^^9OMLJwc^1!UoBYn^0)dqA zrH-#h!y0+UY4d&z3-mZZvp9FYyox|S>6}BBhYyU zVQ%9-V{Q{YS9aT_u{x6vp2^5LVfBVzpmpr-76mOzn6bLkd5XYX_s~$6WI7>T*xpsF z5ot_m4L`~PhjGy?`L;rb6V1Oy>)A$yBb|Vj6RlIPOfUZddC`WRp*-KCIB^TN3E*SN5P^j?M2$ zh7ck>e3j>9ackN6p*)|>f0oT%)oAx2GR#$=V`d0O#zBUHB%srQ{^7ZnHmx0!Wk=d;yrWB{TtDw4eVQ63w zOnKX;3Qkqe!h~V-h-?Az{GS?T+HebN-mAKgc$0?8*s!_lb!_i3w~21k2XT#J5?7yb zbV%ZvUxLQ;zIhMN@-)OV5iu<+2HHr6N>`CK%{2o&@oa>7?dNc?1+)WUDgvd6((r{g zImHFfQfSfoBs!R#8yKQ>osPZwMDA*8l)ZjZ&1=_ANHorQS+Zw zBc?1By3Df#f?GNAx@~}U>sj|?Kbtc2ZcbO||8jkr^rkc37@WGn=e@{;@VqLD$ls^UiAWGC7#bmq75Ij&Fvw28*-K2fFV_Bvah zIWi(*PUwOHVUst=C&?Vv8lnJdw(ezh|B-~+V9TJ0C|TBgpVGimqhIGC1;^apCLE^d zwl7S`U97PEhN~v2PeoMtD%|0J!{XD>Mb9 zR9&e`!#209^4K74Cz_lT5t~QqN%-jL;P`z1{>2Yu#1FO>Cq~&#pkR2&bBb?EP-EC! z_CwO;{8R<^l-HT(>_1m{Zm;kxn~7+KdA7BkvQ<&vPb=6O&vL?=M+V5M$b3k*M!Hwn z+hj3|@HYC)@;<$=P{H=pmkm6B)+Q@J7S+O;7j*5thF5c?ZESJ>ZDRQh`W)}_@3pt1 ztYnr@R~{9hWpgVo=4U+sH21{|t*|Qdsj;%A@2C)(yCG!IJ@l%FbHHQ=b2nvl ztENn0G3y{vF+0&oqP6^{xL703{MkgOt3xasfhJ3F^6vOD$9B^J`)calrCkcYK7M+w zeRveB0LOi6FREE&60nBIB#`ATqC=-ctPM4$H)ed=7{D*dH-mWi;bm>E>XZ9?14kt5 zv%Nq*IS#tARhJ2wNy2ZilUy-U?IA6Iwj-_Zn3zsxckxt{>cX>50dSez_{@0C*Im+0 z-%OoPNwC{MtHk8$05#hRb<-aOqUi)mx9(%Q<9s~qXWx}~aaR53D$9kzok32|Rfcu0 zb&J8@EV~(CZ=2_k=XOqU*SV5xx>No~Gawa*MdT%$+q44o@klJVX>mVApR+(~asQq$&s2!D;<=*GOy4TcCb|l=649GsJ^2Jjhn^zjM8N4ln<+)K z52Re_-wrR@0?mhedlO4IOLRfBZdt>qUL8=2`*?iRv$ilXa{j3fZ(~p^MYR;?Nj!e~ ztgQ>8X}#U2H>VW12C3%bH}4#ZH`5mC|HKMU?^nqz>%M=1%z6eLk5o*u!wz|6t1{1y zw%Kfi=ePVrJPaf}kPn_!%$_7V+-PEBwz?aL7T=QS=8=S2Y3Wq^iCSytl0x0yeC6U2 zB%0&xu0`&O3-RFbGY)9kIK(o1f7rn#YNMqfoqvmwc`j^i1iDC5eG|_R&Ct&~h38~$ z6Go}<>~rYsJ~#2)8fTff$-=e&thLsZwBVU`y%ZNv_&Pm1`?a0V0d*tKpB4WQ(9&lV znwJW3KY8@{N!$2h54)gau3Di#U}IKBh31&Fm=b=cOr@MDcWx~@QCZa|jbr za7Dt4fvk$g7)h0z#JK_Iis5}$p2x>!ZWrfDHt-ysxAWd-&46a-nXm9M(lF1O2#xw| zp4Br_TXZ%#Gk9)!Art%M=O&)#_pb?D-+(6z&1=2-!5?U=kz zW}W?Bck!>uXDux_VrG3NE~>0mNVKD3DI1N=9iw;w+@ZMvbhF`I{fhsN|GG3f_Gu)Z zdAb6mj|hyN!;KyspIzBKe1nlh>T2xub-q^_n8_Jtp1;WRxsvL-K8O3PABy<5(C1to zhQBqFqAra*m+rII_15wX&}kTF&w!F=Vj~cec-CeNcEtBBa&FmKib+7dndd33#wm}%muVK? z<(de6*HqVWt^-|CE0S3q3Zl#JoK2r4bdu+EFowD`+1{4-ld{j*+)nA|*7{uHTn~pS zzLAPO*>%UEM0n5J(MV?L`*}_tv~adivowzkz2pp(M^COzaFUfU<{jz(ysEg+4M6`z z`dsFp<9yY$Te70kt)Q+%#VQszFEyuizLgMCy^r#V9b4YtV4$fk38ULqHzKWkv^yS< z%3s~#w*-goEW}(uWfMn_PAng+LsNc>dfni zmfCnitd6P4gCD&JPz%h9`m11{m;i*Y98m;0nH7v6~remwfKI52FdDbU~X9n9jmtQusi|;n;^JnF(xPeuD9%h#n z0{uiqpQJc7Q45|+qVqJ(`XgJ?Zxa#1Z?x)540PNW5v|pZ@ripHul!gTG{q(Q z@cwP@*24Jct1ji5wK0=gu>Ss?OLB@C{4*9!ylh*}Z`e$sgV-^d)y~iPVQ-IK;rKdM z6#ZhG-3e)#6at!E7_o(m9o8H4(TC?`$+MWnM_0H0z1?k2%`nYsMI?)S%GE8N4vLY= z=nkOiYYfo#@p)v3=@4njw0vrfD$y-oC$9Y+N7K$s`k+S# zNc08DP8g!PL7wYCmnz&+pDXaEABVjyb6aha?BeU7SQUrG<|>=pdGS}xf~!er%cyfm zz4y7~xurZ8^FfnA3wnYCue7Sq2a&DX;zVEOLeYBL_|gGW zkswh_QoW(mc-9;JOLrTHmoF57R|GsMFE`^i6NW2xFTGqGC_3 zhb~6S!E-<@jmrpY>zi>^{WpSV{SszF@}dQ_3cBS(cL{58-N17)ybG>R1-npc#gV%@ zFPvwaMlCRQ^%~hyC!-&pd(w7$dU@yY;>iIe-GldJZwG{a;=xJe3ZlF`E6-K`S&dWZ zzhInsTW)0#d!Mtpt(KCUL&w@5$p%Na6+@|cTrGi4)4_A$t31dYoN9dr*5g9>UR^u35Rh~IA1$i!b#-L&0_dJn2M|zW4g1Mb= zPWRroq3NMm-34?r&yE)p4`TQUshXy8 zp{+QcYJVHgP5!gRbLZ+8X^Oo@ISEkHA@`wvks~+(9+# z&JwNCPPkT&u}9|~{_^uLI`e8_>)_<%pZ~TzIpSs;Uoy9UB+mfB20a3Zo+QrMZ`#~e zX+5hxeP463%I56DGGNke8csqS`a~Tl{Ae{Vn=D&8Vm|e_~n((MYs`R#;47Wi`Am zBsz#rK;xIb4UL}nmP5s`=;f^nUXjNLs`6dUL>uVhGQB_9q3CIlqD}(a^NW3J*allY z2RD^aMq&BK@$Vqg(*O1%?K#sqcAMLKm+r89Ts)%l0__8NA2Jm<{7O}u_N4g{GPv2O z!9K}-+~dS^5@7WwiFBR*T|Yg``1s~Ic_&jzp9_g*buXjbVp+klNJtk@C)>N~P#2N! zY=`IPAUvd*uMu2}_C_!1{Vjbq&Af6p`hg=eg=X z*LapVGqPCwdtcY*DpixPa&K;O z`YA^9*+#2)82?hH)vdr;3eASO!52>-oqO>3`uM`W-%oyMz_`S^A-=6r^W2WZZ&+U7 zchrZK@i3I6W^pq|MRsdzdk(?Lf+U=qM*_M?EpoH0&`5MdJ5&Ic!c7VxOQYGi5c`m% zXigqEE|z!}($dL%KdYB48PJqUuT6{&b;^CFlj!2=Dm(B9G^-D_GTxtj^R;>^?c_SN zWAAu$gHHBt?%X|8hoX-k-`_l>3%>TdPE0TD9-SncPtLS><`f$al{C-pf|x~^tKv_r zUJZRK81D0ddI~nca~RyMfELPjr4!MZ-(s2}e>#Stu0+aYQddWC9Z4@TUzE_q4|yex z4xZ`v8)!P8@t^{D3ft_Dh9p_(v&7rU_@$6;6 z=C-Q03;+2-a-UPLp~zOr+;*eh!hLotz=}NU-nANO5(Li8_O|4i<`&LI&`D}rhnuZ0 zoMoqE@O}!<2sA3LrZ$2G0VT5-c>j0}qzH|Jn!DuM@G@ofQMF6IO9&8}B8y&6|V8 zv98Y790`j+)67tey{({b_p@-kZ z#O%_>E;}FTon(uGZ@qh~$T%tNb%n2;T-Lg$Wr$&$WR;d;RFDuvVC|cki3Lxo0NLA& zFxljOWq}sU8j@*gGy|kHf@*>NHJ&ru{(Fj+W5dgee{G;?=Bq=abI)#orc*cL(EzzC zKKWgM9(|?lScJVG+3~PUbdqL2Iqz(kPt|7y)9gP-^>)GYpw79$#}Lo1xm7*SS9p#a z-3n(d%^FQS%lcvujfzPcc-~r@8#|jg^S4gnnUaMl;1<$KQ`{cuy#7QU^s|x0!<%0O zI&tmfYH4r(D1EN^&pI9viW_CRn5Z#b;}KM)&<=`~>O7MsuD%iqoq>+!H;$#)mly4T zhb0;t7IQd={shs{URNs#8odqC6n7+~b1t%2t$jC>&HeGW563+VBbb@P)MqQettClI z|K|RikFLSS0IEP$zaUGoZ9>}unmZnrvlbCeD`m^HA56dTgnlX<*fWinH%HV+hjtSa zm#I#T*rmN~j&Qg4oG(~n+B~nYK+u7RkDo)|ToTE10(v|V9Z9R?Lg}zLwHs%~`81wCtBgDQ&x(lAIkx=@5s7n;+on3$?NW{=flfR(_|HFz z=hEIT%8 zdG>qDAjdPdCW=wK*}s5iqb)C5z4i5nn4+ifY_^j~S9$j91U0YCGe}ALNE8Io`Lr9C z07;^&IbL*BB_=&OaZ0Axa^EJ3srnLYw62D1X8uhY1<}8K`f#{0ec?I{@?_PyT^Qvd zM4T7bcJ7E`!rBOL0Il5>f%E^u6=ur>06i62@40Qw9vctbLgnx>c z`(F-4i!mR0l@G&9hiNz)O$BKCt=Xmit#$1ie<9AXxf^+o3eTE67aDEovvr=LY1w}+ z53bTut0r6-XBXuOXO>Y8A9b-O*(#lkQG~avcRSy8KNq(?X!dd);(SwpM73u+V?mvm zbpX=l%H*Hd=g;cJoq^`lkg}q+{$sqCU)#o)dn+z<;yDL+us34-<^Hn>F6Lx8HSIuG zg}M6LJlpJ6WY@zpxW=$glkI^sY;KFR9O*Zgb}2EDsZVi7 zhqfwF`<*X_h%9){A!~lfhlBOW_UA8Y0$?GKS!W+@ZgKrUhJbccRDh-{QSagZ`@eK* z0^4p#7W*1lEWDrM;EqegK5#`(8Z45V99Exb#zzws6SvYST!l$XfTE;L#OIBqn_XvMmp zz8YU39k97PJ9-G7E>4!Y67Rg&i@m~^eD%IWM9sqV-)W4pQEtJ~DLIP6rM zYu>ZxMo6BGZS$dbKU_ho;5Gi+_WeCReerSXSORq^D;tW0W_7=QF7lw zr$7hAeB->+S#-K!BZtPU(P0H+y!LHS)vc0hA&q6tsNLmQs6WwkW2*fb9d~@}8Er3L zzov>?&9-UEae_HU4;men1=^TE=&X3&-}?{kyIBucIFOWHto3ivW1;t$7QmO>@@$-+ zG{3yDOG`A2xJ&M%@6v6DzU#r}4% z^)I>+>GSGFR1EV>S=b}@eR%Nr<>>UnYTqiyVZI)aLMOQ`?Cn3H&*r&Pc9T<8Tuc>* zxnH^ESuI^uM5w(O7;f$hiIdf6jkVNg1yBS^fwStNeeCnmKcbk#)g;<23D0Mj=P&R~ z5<2U%@8E4h-0Zj~B=&S{vFBz?NIix?Yu4I)@C?o`ZEnlgw>j~(d=-1Q#j|iemFFZW zm8YuC$dF7tw+xDvMt2D{Ds7~h%Hml-heAuW%!yN1J@%phK~tGHFV>;s-?K>n;b8a4 zoy$A)-C9(DX?0_VYAxxdN4m|GyZakU!pGjt>GrG9Z0DKVRX=YV0_Yie(0AS`l7;Il zg$|yn3i;m$cmKop*$1*GPlL;$!+QwPsPC}$hY^RY5A+{i7mwY?WlZXRK86W}H( z#RnxC?@P!B#HH3)X4hC9XP4bfYB<0>!GzW#Uj@1mYM9o4-!rQEZNoc$$S{7iTXcs= zI}?qE3(=9gdh%j~HH)IRsgZjmV9r%`H4Eyq^IZ3z72eLR3?j5`p4FZy$+PkwuIeGQ zLb574o_~es42@<)9p`XIm94V1t-a55_%P1>OFxQdfR16H>8<}*B}FZ|oi(;XiA&r_CRY zhUl>3<%*BDx=NHxRJY_=CU;I*Mb=6Q4^<~rbh^>@4ywP6Z1Ze}gKWK&89&^0p$+?m z0JlUscqWU5(f#PxhZx-t2D--QsD@eO@H$UV2egCbTH4uubAHfwEaIp7&%v{6JqvT2 zgC)Plvu?Dp&9}MooMT~@z@S6{rcgVTXDC~xSeROGtJRRK?e+B^q|dUrW22Rl(YCl< zBzErH)e(C4GBN$W5$Gh*UUOt+C!NNaa6Aloj@I#RHGM~mkj+2pNiL^`L%H8jwzt`0 zRJXTRH0LBEMYEw|Di<$oZ7-N-G+I)8x<>1}F)Y*t(N^cI&;hh1y4*(-$5)}6h$eT= z>F9*CL^^g~sUHA>Nm(}?Ol)<>s#hagXrCM`bkTn{HQP{0!;^uwq0w=dklv?Pfrs1w zDIMi()&Kktj&J4yIr$XUa$d>eS zSJc#r-CAU;SkF=^VORjg}j&p8IJ?qQUne zHQi5M43E!pz30Y-( z_qlFv*Ou*)=SG|pAEHCnwzJO>+Xf5vz00Y>PVM_qJY!we1bS?=i%j1`a#k-UHcyI8 zR#2TMt7J&R9KN%#mOy`jX92B)n=;S2f7q9Jjtq1Quv_u`b)wC45h6=};z_hCawLg% zE;>S)1jR&j6yM937SPG+{z)QDXiPo$Rh!w238|P9iOyKO8R&-%}TP zq0D4_z^+sjnj)_ms!RI!@XYQUu>z7Oi|{tt=l}U1|Gq(8D1+u|ta4UnEn~4jW#?F{ z$u5zzmC5db2T!{uSGm+XbOOY?U!01Q>Q^8cyHSA(zTSdouue#a$&JljB|5OyVxI;` zYZN$_FA6}v=>;@%ObD^q!M0pf;)662)NJCF)qU>q^|86-&Gm)JZc;E08HC2Zk>`4R z`zt&zJFbGw#szj?^k`^-wQcv=0$p;PUvb;B0;aiOK59IZs`AMgJ`q?vudl6sgSq`B z|C#Rk<-PO$q8ImbwK{+FbYx-o7zZ-Fy&BI3T1$_|59hgvgcY(}zQ`&i4?5{{WtQjJ z-tb%!oJ{TX1^qSgoPbs|GaWky(Kfmj7n4R;CE8VEG_da7Cpjc5c6o*JO`F{|y0OM8 ztGD3anCN6{zGAk&T3d+G4$#t5D=7^~*x0P>CIurj-Dd^oSR5Me{`)$G-Vm*BiC6+$ z0e$rHL#KBgdoMW*U~&vD#;50r(?#o%8S1iq3a)nKZ^{-@3!=!)*A?1-l13+>n|anG zsoCBsPIemECF8YM=DhZm${$9CUsGS=UI&=ng-j=nmcooo%#v=K7;S(07}0+C?L2Ei z#X8EnpY1=R&x(h+G%Sb1oRX~ZtQ~lE!IJmv8?42;aa3`U@-dR!>EI*lvs2HQ+oUi; zpTA9>zsz&&%b<=s->=b;c0$~kIQKZ7PWvYFJFF3H-#OXbXXjZBJ{1sie7n%+qQPgj zw_E6P23m8PA(-=k$Wpc%K(79=gu3#o3Uo+xaS{R&ttQx2qdP~&@TBcXTcp*tUY$TA zGG^<=i+6dqf7_cIea`t{O1H37@_&H7a<9Fs^Hux#&XMtXZS(Na7{}alinrCFla4^E zeL}hqAG!oOe{?Pa*F@gQN}mq#Au={QN~7B@`pP*2&F-p6V&69wA%0UnTK@)nKeZ=X zjz?>tsT0d_F%jG}8j!9Fc1y{w@cQ+aY}sw|Hp*w5Vpr*SpbJNP;|EW!53^v+&rb|d zmyAF^NQSqrYU@$6w=>NJ&g%G2x~lVa=9%@(;cdFDMJ7}IprR|4thID_aF}0U(a17? zuE7$PY{h=%=F&z6d%HY{POaXEdj2qdwm@@Zh;zs7;XLSPkAHo5?%b2EiN2$E@NBi= zjTSz%*PXDp)B1B3dpkAkEqLC@{`23)Gw{mB&Nye-nIWOj8EA#YyeZl#3WuJ#2HdT} z>IUdP!jV3t!g3jNozojtz=hRqJ=fj#z12VZbZ33)6<(X{9{tC~RoCsS&#zJo^<+T# zn7(wr4l9U{eC|P?xh}>zc(&1!*D^gDXN&`Q)ggNa`}6}IdfwhWHW`k@oDbweu-C9U zDC^#!<)?Vos00UR(!lDL7tOFrE6~mVnn!D_>Ng$RyPqJ4shr`@C%+>vF zBN1l{mBw}}oOALy+|yHdwz;k7w!&fNdF5Mh{)#?-o^m8iJFVhA+#YFv_Lw>MXkcj0 zy#W)?1pH0# zhLj4&eb9UV&ZRwC^bEhCOWBBaTF<&Q7!_p8n@q}WY$CVt^{38#4l>LsGDqbL9?h-ytc0GW2F7nS=o+Di)o_*Yz!(n+MByY;Ip;@5$a@=@Xvvr=MIt(%# z-FA2zbJaY5lRSToXP_MxQj$kk#+UED(;G`~u?ULA8?BhzXW`jEhrunL zm4eO+ojd9)!mW9Fx&y75>>&oflV@hMkS=h}F0`X!Zd?)#qf)I{aduVQLTfbo{Gb1+ z&=?}EgI9MpsmvsYB#owDXi8^ry~6eW?fv};_M&1Qf-f~s9_-O&ivIlbzkYuB;M(Z) z5^-{ur^y`CzSw`?P!Nl-^lEWg3O5>cR^VDR#~E`RONpm{yt6cZ{t5??4UTHB^~?=+ zZA?usu5b6yx@c?58>k(0kkMTm20D!JDRjC9G=CsO)=H}r>CyuKuQmB!|Eeo3x{>RwF90C1ywhqPe_ywj@!m62P;fy>}g{%FW+FgZ($_M3g zu@LI=>R3r1D8katYN=%b)>kqrP~+HW_|LLmGtZH&s`30S+2=3RZ)cU) zm300s6nb>D`}w1DDD>mOiPb|gu=zdmthcEF(c$dBw|iiL{)#@g5a{5!JR$>;V>I&I zKz9mpS7|Q&=L+KWmnFR|URj`Rz#5WRmsMP1ADWffx6+hy%OJW)NQ-FpDsuMfecYf` zYR1rLDm$d-EQ4MOb(0SkM(^nub1ti9C4UwQXpXk2%-5NkA+v zTj6tPVbjRTJRvQhH5#D?>%VsR=ig#m&|tXH+2Yo7m^WOu2ag8^$9QXF-E0O-7m`bP z+Az_Pu#!GISLIeiZtbaJPquF=!cN+@BG1)Ey)vBSJ}Z(^;M|(#%yVW$?x+Y!@$H;_ zc2|HR9M-@7gL&3<^b!frDoY z6XT{67zyMG&#}5;3n0xz<5i64GIAB1^Q-RcE{@%kyY#r7X!o>wh|eta?ebIasu>y` zPBf}l!8}W_&Gih1cS&>+A4_bvcB#LS@=*Kgqke$??B|K`+as^8SHfX*9#FI!qGfZl z7!v6oXQ>)%dwlL^t&O^>{J-TWd9Iq$=i>?Lc674F+FKqP>>we$2*52ow%mj!bLCUo$Wu8nV z5}D`h-~QHGd+oJTif23{cT#B?3Wu5h409VkXN7r2Obqo&m{$^b9%}5a!innY8J)-G zyzG+r2KpXBp{dUn5);k);W-%G&NB?|$TQ4JlOgn8^A+hs+dm+tqY&a|B6l>p)5t#e zINgiW$NELRCI1G2Ze>MZOkGi;mCucA4-C$RsIKLgsaQ9sq`9K<^cpI=VU9`hDX!^| zBaQQhIq0pWF>EVINxp%RG9pcT8$^F{qpG)M93={rd&4*hJgfhN5O*t2h~xYQn*$90JGDp`Q@$8IY4IGqXpU89P)Kd8kgw%b^i6s&V7UY5 zwiM|io^7tmaQ0{zc^(PdxU)2gFVs;uj57!BKAVl^WBTqqixTJAv#x?Z+tR8S-d48f zNjOzY!BF1c{jTdJlV2}jLSK1e(r0+kO z5-8AZbB`FElgs&~MZ4)pL#CL-oYh183KNqU-9R)?$s2F{gvGhG?aB)jCABV$}@u7be>Upn|1V6E`{bc z;&HIIk@XIo8J$3E0-;gt^~qXPv1Z&}b5(Zwjc1JfCeKeG12nfEdnE%FCb4ACkR7(P zSb3huT%WNEFwJdb)MA5ojCUP9`Kbpgfgwv%79B-OpVs1G$HFt9ivOJ9^xZM?O!X%( zii>G&jgo^xbW{G5&IPaDm-3@&cB6r4lqNdAaqTLyKu8;t#w5`uoNM<+&vfCODg;J5 z(M6gzHy-8~6MF!CUPnbKt%(KBROVo8zwwhF{pd%=vrVi)JLuVI5jMveeIU$^J-v*5 z(QhNmud1`RnKe-`CB3qa(x2qHXrv5h5=?7s#1IFwThmz}nhi*2sV?JJ?@s1^eqzH# z_LY$6;if_C(bt&X@rpTiU7hTgSQ$N-zgQS)?!{I)DG!DM4X$}-&3R6xdIs;=1FsHsbaQKI`SV2#}}xB4mdFX378%tAw4*-oA%al#p+HqrnW6Jg!Y(LVH< z8{o)nqW8>&(D~cgZxZJ%*LP_ab#Y9a@gZq>6K6CVe=zz)q7R|Zw7EHHg}GKKG_2Wsdkv!1=8o z{e()bSQw^Ku=OQzHpiZQi;+5h2Gsi=&ksPNuXhj7=Ds_#uu5-0Dd!w0>B0o5PMu7i zO`RYQkQveCsWu9E8%a!_I{FcuY3xdlNOzJcW??a|x+2innyC&^>1Os*43RvSBv>~* z@K#Qr6LTB#%<|&4`WfHDw{fRFo4LK}^6a_4^tg=JGAQ$@Vw_>Znzh|#D<$Jv46biq zrJ0JD=YAkgy=Ff=hw{D7GX(l}AEGKaZT(}dn=l*EKyWe7lxQ1%cv~T!1vK?}_ZEX< z>CU+Oao|;b^?5uC=y^>BS>+O+i;nrbI1PSPc?AKGE!7Ajjnx_(C@msSniJBfD546S zUwpngG01ajF>SfgIKg-Vx1G6LgIG|pf@RDo1!%L93@Xes7)JEw=6>?lTNmCE&o)gC z?ZEb!iJKm9#s_-#Xl;I~P?tj|&(&~`opUoMIjOd2mb=D;Ky*CKI(>1Z3o3m#VLE6v z^?D-XULOo!`~9-F$s0{@OTLlj8mx;B26y5`V_`J#?dqw{L&s}-s&0Pr35dpNMAHoq ztUZTk!`Z7DM4o36-e%nzn%kCH#BkUXn9hm6#cN;MBF|7ep8%q@4Oi5HVYWy~D;l^F z9aj!7wWe6z&*1@h4#_{L4LFQo1I}7i&+z2(jwY>)=OEA0=Y(hb&AzokD-g=U?DuosyMbZZXMuWD+Tjzr7qmf8Ji8-)c>jsW|o zpTzK{GgRnR|1*pjHYF3kmxGV z%Pjc1=BDa1w7HFFj*a{mTb5@|SEcRkrsg|PXRl_U=_`_Orct zcADMV4*n?7;I>tsWF_wz0VVPboUbk&EIVu;^UoR-5H|@KVkXkV{oS1$1#b<{Y+$8T z)aPJuCj$@-?p=6hf>>BEnT>}fIbt-pm8R@Kqn9OJtjv*!JD@_Nr|v;A`!Rp4{>;Bf z@q#oW-_n7`E~`v0u~`d@(?Owe1&ID=87O`}+{|6udP6O<@X z>CyVfXTYbh|L|d8-D4@zNJ9sbRcgWWbStt8A1?bm0#CHN<@^f)Z=GwM1Dy^a9h#vn zwjJ$^ZkJ%W*Aw9l%HAixh@FbCprkM|)QBa~!RY2z8d&QX5vJlMN#>hzvRcG4<(CjUKU__fY z8dhTv4W{K6@qBg-TN6Rg4mex2U!GNbh(0uEX4R6>kvfZU=NbwNS03|&suKfeljvws zIL>9`+lS?OFQSPuC!zNiX?ulk!%KG~+PiS1Swxd(Bw%TnW-$=0m_S@4xY2=V%s3LJ zF9XpuyT4Mj8?mwV^;Il}K;Q}`f}VW#*|Y7dck}f*>;XY_M!P8^v9qhE9cheMzI-0} z^5Fg%_B1~Wi8h7iZd0>A`N><~M?*yHjXA~YAkUsh!an953v~-E?*8THZC93NC-c29 zO?&PX#_ug~28$Ql+4Nrxit$;M|4QnZOknFNl?)jws`YyNe zHv#rtj2QsB2{k~G&g~1XoM-oeUMh}nK@?DT8) zWpCpbz%r9)5g4;v+Wa7D520#mSJU{ysiz(jBhAJ$x+j_(XsR(lU&pGzr!KWb6lxS{wf=g>dwP|!_>WWDOxU@tX zFn_~|#st>m6X&t=k;};t?LLKQ_v5YA>ET;AtAS`r3Y_Pj{-M$FnWgoe&#(y&WEShI zw>Hi`RDKvv$SmbqJVT(lx2VmekmroO?fpwOH%#tj?4jJ(d7~;~ir?ye$#YD8Kyhx(y zxv+P3_x9H|HNrW(L8Aw!GedOHXUpE^^mB@5Z7b=?B%G~6pdp&uA@`2*tZObu%j|6% z!?QGF81@D}q;&}#kY~-~l4llqXS^JardU;`1!L&w_!Bj1lN= zK-t^N1a8_lUES?XgN^k9T2V1P7n%wVMSa%N=MtVreA_A}Z^zkZj^Ym(4ud>Lo`I=f z@?w_xIOwyJVQME=R6Hvn#`!AanQN;wz#bdV9^;lclj@OtUB4z+x_a}|D@f#ai(5SV z2qd51+-K;s%8?T1m{0P2p4G*q7ZeE!G(ch#&N`r6P^y#0h*&~i^8DUM_?73j_^$l> z^eka?yVV^EJ>v2f&qhXjH(1@I8VyQ|cAfMPuCZRo#+TrtfAVu*x918IfA;66wH8PTwy&;9)aSd4+np3DtXY9!@3 z;n^oE*RUlX69mKc-j_TFo?*H^)$9*fVxVCqtZN+e^I6J(JH;NKK5L6Q=rbELxkr#D zhFT>U1FL#RrdP3V>F3X6c2l2=c&5coo+-}z=b3A)cIBB6d*e~6cey-gp4sOE8wZMR z)mwdlNb9=(I*^t~XY$3&0CS@|YkkR+2Fl-HbFF=Z?Q-A^FCi0j10w3fCx+VL)41sUMUsG2>gX{gi3yFi_O$)S%KXvZGa$nC6Y zph7qFY3UV1-e@S_3&-UqjqU~{l3|ooSd1qX0&O6N0>9=zL)@j$$PP1}li)VxnT7Px z11w)hmAA!nT#T_#c{Vzj?J21^{RO7#ly2vw~1bpbgv#jks^cicQ8JI!8vW^vd66B#spNX*XEPciy?d~ks*$#K; zM?vc;`zfFko>9X`fiYGivJB8TgHdFFDI7&dTCOyBrrG_)vnM;qw_L;bMq#2=?19>Z zFgI~llS21a=g00X%7d1)o3(x(v&KscoU95wbIQsCVyMK;sT4-&u$0e;#>a7e{_|JQ zu&`rgZn6bsGJ6Y5_{N$^Zzo|c#6w+*WhN5CPLfldwv%`k(&o{j*(~>q#g{C+hNG)B z5j_E*hiXv*6s^Hn7SCKosfKzlFu(~knvIeWXeqN}y&KOco8n6)xmGgpj0gohEQGgV zZpUh`>2%-3;Kt=ugJmQ%=!;72BG0bRz8V82t11DiQ19{b{I7*PP)4)j1^~LL8s-}M zTW(-%e*H1MXB(nYJY#;vcxF0@{Qk>qur0W9yU5^HO$J&TQ7M8xTYgx|-Zp&26gfr{ z&&L6D;Ms&f)yM3wYc4tft^J}s=BXS5o5Bbdw=OLMQmOeX*w2V?!G1<|H!4OxT*YP& zc`SG#&VpKJ7}*g9v?;XmN1r{u%(d0a9t|VYZz06(wF}Q871at`q3{@qc8T`T+*i*| zBfgHU7Y2Jes)i8}!)d>E4NJ1P$I1h4Q;OzyK-zt4AZw!Gm{(!1WWFd zmmKOSdB1z8dNwgOI*baSNa^m=LJdI;n;TQn9n73%T&x%3BZW?Y2GP&qIZRg3+K#hT zw!n^)8XPgbvS*BeW(_jTCH^OeE5=aE>1PR@kg!U%TTL zFFgW{G_j`YZrC(P9Bdw2*aXnr2ZDZdu(xra;aML%@QhtO%JQr|Z(XAI#dEq-WyC^^ zlk-D$nO%R$-!}YNIMHG1iYpYk!rhieLm79Olr*GeJ5z4y5pyf)4P3|7HI(n37;eBo zNuDXu02)qU4P#A)!r5&8Da-QRKRbp>r z`Wcma6P_oHXIO=ZIKr6Gh4$+lv%@H9nX%hpPKCDv&%u3;JVWCc4f9e4IOD9xyq}16 z_BH#BZ}YeMnZU2cR52{fP`OW@t=_?j<*mo~to%kZ0)d9b z%?<>f-O#gH6itaH6IQ|4PG5aH7QVXfCfWWtc8SU!@Ndp1Kr^hRWr`x2^O8_S4|~Q- zYX=hPNVEmH9$(&6(dA`SlCI9#tQCP~hs5S<@Szu0x3J{gevP95>WKc;&)@cF7zO$Q zlDO&M$sRQ89~*kGFcL)?nP_sLIeeU2d5Cz<>80sQy-=^QrL$}}{ivL*Wo{|9so1(2 zqB>j=xt&;=XC$!L##Q9mqb2L75!^P9M$=VA&Xuln zl;_0UrrNPr0F0D8(`4noRpeROVVG=KJZN$l7(EEj-F?Fz-)2aRn-}G=0}jSN41f81 z*hN+$moNgENQZHV=5{v1jeR}J^Q;AdZgZyu+B=P1;WW^DbsFyvlISQ26=9n1EK48V z5g*OM-0-WEUXq%PCegamML-8f)?#A}j&V*J*_;^EuC2@z29S;klwop1q}d&NtA`8c z=2lKW{u~O;X>9(+vnSvC`2`DZ6K7c5%n?JiGJX)V73kwR(yDdDp7r#^=EM6dDDJ(p zwlL8IBWrjb@pa_rk!F-j@aBwjj5PyXgDkEIXGUpz`#g1sl;J#BD}(56PlaE}-0$|{ zlx^#(akXtXYdwsTmkeqKhZf2J~WUH{&R|FO+QsK&?z+@6HHMGlBw##1ZKciD*cqY!uCP{e489rv7bKTY0EX^8# z#xlx$a}8oQRA?;FUD;~Wnv9gWtu@;*9%ektf%eZI^f@en_PY8~Xww8nV$^SnvKkyz z+{J60JXP+_tKtMDNFeFK`CN1fz4N-d<5H727wWa_7ord^gcyOn1G z-F)^0Tf1%|+hgJq_b#xREA&PTnLT~~-{{25;`#~L+^+0jefj!(@1${75;wN2;OP}ffr+Ed*dWF*;-mh+-`kodbGN`qh_*o?G$UDP@lndrb)MDg3F_5t->7!S$esOkuxW2066Y8@PAyD%ZVeY~dRyP{Z z$~F7pYi}0u{F67Z-#CnJ)JXDWC~9z39=F>h`G62!VlTn%pcGh|`EMu^wtIpb_UbplNU$&MLhPdwUO_alw7M z3TNr`DF(Mgim%IaS@$^vw@C;1ptVhTCKwjp2GHg`yF4>n51C2_+8F~dACPC3TdVaT zH)v*72@341d^xO{ngZ6{!J@OK5s1dN;cOf9SUk-8l@CaJ@Qf*S0PP`|sL#%^2rj+Z zgJ$x#pMPcEqTH{Am+3RGqbXU8K)ccHtLaknlg==y#w9kjHj&m0RubrXhE3V8-ABo6 zB-O&^rnyLm4?8tA7ba(yuUvj=68()o|M1OMGdy!uhH7YVVTL{ox4#e{MvOMLxjT-+ zVauB*9zWiEfW?#5o%OfJuX56iI3rF*lrznkPFHPI031;cpIfwena~#9N=5N(lnkoH z55fzJ@A10g0l@&5c9Ulg=V71)&v2p{7Hekd63j+7Xfq8@8I#S8GuUaH*xmR9?FQ37 zHEji)DbLPxJF`iG`&?WK5Y`~)1ChLq`GYb%m&MsGw}x=z+2F95ANND4&$L#dY^)A< zl9SH|P7XUL&)B-5w`t^})n*H6B}0tG!olmV9i3S{&uM7IxWRD>XMJjSplNHfr2}FR z0;zyS#DcqKaa994SZ)czx}?d&coaZ;VfXfz1JKdvRz0FHrJ=}}PK{xlzICL5vsE); zq>J0ytuEaw)WG8d(1vuC!eWJUvnw0H=zi_hSIu#bw^#oHn0ylkSQ^(Y3 zEpA|3$!DLb&=P3QKC?6l#EsD~ncK)}km4Tf<{IvBzjp#-zC(xX%U4tfycT=EcMVs0E>GsyW$=gYC zZ{kj+7!HRr#s~$)&S;KsaQa!Ldefzb=sv?5IT*@%^#OTi@?3Lpp&8_c$$dwgPQsY! zZYsex&srts>7pVbfK|b808r^T*=}3JZQ7esO*Uu9q4mvwkp;# zu(ef;G5Qn+FXL_oHyteVo`WZ9Qm56=TrBC6&%xY2-0btg=BuF3I2pO>d>UxR#A>_Q z)jF!W;X^;%wxJ52K8``+SwQcLXBZrD%oorSXsyi^x;`@N&W{7qycc3G2gMxL=N zV|A{7e0~w=U|onu&;rTv-bLEY?j$}IWLoxjAUfPfOw(9$Nxhc>roq|9&QWsI9JF|vTAHkTT|xPqxJh!qu9X(BPQhqRbg`7_3cBWW9Q!Z<=fd{ zm^Avr+wXi&XL@TLf$Xt-HT)sBFx7**ZrwpKGFaQs5OzC#ZE*%RYHfG_*xY@s{3Op{ z_!L@1SqIVTwqOS*r+uC}M82id(XURN>ta^ZkM*iDH|f_T$LI~zX!oP-^5Y5#Xb7~N z=qY*8)uGG*^cm{R7My5EbX8RV8br(Fra&t_%)?>a7b8~c4awW$ndY_@ymPH2XRDsa zvk;ffN^84_XD>D+dCu^B;L>4-vbX6%ch`nlXf4fv&E3Qj`%n*Tno*d=d1A zUKBNB!YV#4(|wAF2G&`TwisrZ%}QbInw`2M=1EKa-~flP+T=%w=*Tq3Zvo9&D=PH8 zdz0g%O^v86=Cz2-i3ZjnnwARBU>CXk6>1T$+WNw@DY|$`F4kOOkO+l5K zG3X&32PPNo?Y;FGJd0-Yo-;htdp2v^qG1-^hCU-ya2WUbK>1-+YFLTB=42gyJIqxriR{~b!!vg4xXLZZJ3LAu^EG&Z|(`uxuJX{@kYE^Y9F> ztuYTK;7q1Vcn-mCUNN==YKAk_B9@kcvJ0HatNhnecgfFil>U7s_Xn$6T|hj8Xa^cJ zqurgrjyD6cxh?;*7x|yT=Cybvw8qC_hwCZ4&mWRX5k1aljc&nyM#l|8w7jplZU zhn0ny7nQ!0a8P9cde62@i8DW5+VgynXD{u^h|wXU+XwfbCDC2EzTv<#?-A%8R$d7Q zzV61tEHcH|FX=8kHH(`c-KMLGc|K(sC6Q-KMdBqS(e6Tf7{qbLWlW9|sIxo=wR{Qp z_#ldNFJ0wiG&GvkOPpx#VW-Gg+Da49M4BcUPaOMzRT2TrR{Y@0XWJ)d#%{B0n@P-n z|C?QTe)GHUA>6XPf#k0dxX-{DLWF+?bVqKWfTbN9o5K0rI4a-%um0xo*#)a99dJhZ z8>R@5=wgWu8qHDIsV;{#_{?xw^dRD!9udNE{9*P@)GyE}%n4ImMT@=Mf-u})h*xc4F()t`y3powKizkPIDKm_PWLkoA@T?u{LvtL5g*^}S_Pf7A z1_VB>fW}H#jOCYC=g!G}7SDoN0^QNofbBMx9;{wln!|k7Ma)nPwhy1<##(X=sLq6$jjLwR zijn4&bQWrAbzm`dpT@HIIQ6o<592SoR%@82M%%x1&n8IT4B!+AH;5;LbyP;oF zZ3*3oiYX9AXPT!5Q3X!ydpxXW@V4_@#(nlmfTyicZyT`OTb==P#F^%*)Oin{QB98G zbx3(WoIWEwnCq#@Ta;T_B~<8^>K^I_f(w|3o?hDAQP@vJb10(0O@a2+7@qHW>Q}$w znoqlgy0~s%rFm9JtSIF_3U$KNfvDZ}#mV~}=}5zl1|XW;nz}NLR`A%Y?zS)=O>nKb zAFa`xD(ySTS#%7+v9G>(y1lwEUHFIp_Fs3iw&_P-_z}`Xu;6HjRaj|MCJj7y_SBAy zO<|Kcy3YBQp~0Hlcg7ZY4OkKDYt3_<_Kq_j1p2jw4xjg1Mq| zm0)w6j|Y+H%v2_bb{E=KQ7W@UBUZj2)^-CosfMKh06+jqL_t(`qU5ZVsJ10nFmaSl zqA<5??(4ZTeFKgC{nb&Qi+DydiMB*B_t{GsFuW!2C|toJp7GE|XI@F#U7}egD-@0^ zB|~7CYb99&_dCi_LbH5mMa58{E4tB6 zv|HV7c6(0%&}{4n44~lpv#a14Okruo##di{_Pt*wRXq*i%q@8S>;KA?zv`{dGebZ4 z4?O4kZVw|{0yQ0nZr`q}tF5^>)p})z0Syi`#X$4fuMf{5B^cS}$}R~$bYgOI9-6sg zkW`o;h2gsDei+-FvpQpeF#xS%f#Mkho(vdFgOFQCNY++GZ+_>@R2ZLg4j2R2+5}uF3 z-+_NO4eaj6G$rtPWpcA@rd1fTj7ly^=6ZOKsnE6#CR>_lh7`@q4!$(!uYUO5a;agF z>9^kc!C(JR46M)~S7(x}#_85v!ytDnAL0Uyy4u>Nmb*(=wm;<>TrQJ70?&z!#p}55 zw!1!u=()+a-ac4w(c(sk6yrB|PNjZ2(73~@=;6Tu?n>3Cs=mwuwbYU-oFA$WftC-A z%sU`Yo`;wo7T02gY!b{?sdShM>0>R1+gBKK8NEa+hMC%I{xeag`^4cH>Sh8L<(bhi z@Qi4I@qG1glg}Xk&^$w;0W_wega0g%Zb40M@{BIi+cG}4j!+m&?ej}G&tO?5hS}Si z2-$__FzqOwnf3{uooN!1fm%czn91_LusfXx`blB{wLnV0Na)k@^Mhy>*|QaivbtG- zYUk0T;7G?VA%T`J?O{<`X{`Q5rorm zZs-F8jRUm+8Y|}JHV~?PwDT>qoQi;s1221Bs+Ea{y$GH)p~-PR3Uqm(A<+`*X-q}e zVH&D>pdZyggF+)&tgFg^uCfRNQ*96z>P>jI*{VsF8iG7)+h=Sz#tb^-hG8i7jbh2O zfG&e`Iu=&MbDJi7z(Y!&ZKE5+!w!`kc0`^rnl*7j8UN!A$j;wxKz1R4MxSXKo4?`} z4jgFD_f)Sj>rQsu>C=BDzbT1krh{U-a-a_N5Pm+lza#^qDa+G)Odax{YX{ zAwB$79O$LdHq1NG#xxyi4C%_c1JXzy`{8SQ;*3IKKmA((eM3l#WgKIYhPD%uCwkGd z86#>KsBJ;v(8aaOZ3=Mk0@>Axw>}BYDXWrcXc3*byp-8}YjXO9{53hq$$RZ`WbY3j znm{YN9|OB5bi;?16FoPpz?d9qPGDh$#%N1j^+5GNI|~NNho<$dnJaWvbXc0)XmCZk zdKyfhN9avvqhWDrl~-^@9sy^oJnp-jJY$e1$#k9AOrH@?3OK_k;)&&C6>#?Ly$?5A zbu@j34~@)E0S$33pds_KiRpz>XspBa0pcTzS4!8Vg_tw0Rm_(;LLkxQa zi@Q9~e_=dRqAf6HRX~)xp?ZDELY*+wlV~i+K!eqW#cH2EdIG4mTl5cq_*&`0s;JF& z@Z-P!+y8-nil&~*iU>|Ki`Ux5F!l8fDC|A^a0itQ9>YkWSEQX+BF(hEsn7JFzdk&x z<2%oo-N^!-S}aSXQQKi|W)c%usM={fGi!_??^_Mn28S@CyWZ5i6@rCTxmyFl-gchP zO--9T#~r^B(4?9r^6iGB#W)sg2H24qJwoN|s+@^SDs?5^jOW_$aQw;<< zte=EPqr8E3lEt7`b-lZ3Y!2)4BG6it!HAybth|@VfQxDVanvs1IqaH|Ho0kZzc`^i z#9yM(9nEbCG!@9~cx1Ui5ryb8`}Ehmq4j3iJT^>%l4Rsm+77-p7$ zXRIgfZLGcB!hPHe1#T+|b9=);A}ke7sPXrXRmV$ zMUy@=9JYLz%3&5ZJBt5|AU85U0d%3D5=0i}9vSZIs2$=C+MOj2Ac)N9hNnM!r=%=NkvhZR{=rZGmfh4@Mz=+)>Ok%QOYC&_ zoT+ZP#JUFSTWu&3WfNnuv$ZiuJR8d{&9Sb-NE7XCTH6iSkP^XesC3-?9p^@N%r_0_!8=~s zGo2i!Z6&eK4c%u|-i~XZrN4H?aG8u}g6!V26(%9iIKlFqeCip#4T9TIo)Ha$xs5)v zc*xo3Bih@Xw(4#t&|_mD+ISuvZQ|x%=t$^3omjTBy7h=bF~(_?--cd-7J5kN9NEi- zYqnpW1J`t;D$z_&&=|%(SP(FYYMJgAmdQ^1DLdSqSn@G#gfhDW2OX&la}^(av+6wj$8p@U!4R zYe0q-B)~v=(GpCkF2Q`062Mz>4sx-b{-?dra!?YAW|78nua$!UC6oD2rQ~!@rlML(`Hx&DNQb)EAGMX@D?u8vgdgd z$B%?)`OsRgV?{4yuwg)_;jIRwsnf>u-0Tc(?%ICjl(hG!cy@(`6$7F}RIKYvEd*Nj zc51C;R-fTM%iQM33z#F$rp~U>L7;VRCC{?9OL^9075Y0Wd%{CIY;?N66Vg5!bb^3M>{Hg z=Sn@3=Go?=p)zK4vxb1IZlAxhK#N9wc8xUAU%-3bYpN<*-rQ~S-V_UJsZnh1PS4C? z^NiJv^N*fdZn#N5twhrjhuUO>{mv_=!}ty|9aH<o8ph$b%e`}jp3Cse^iPJxFn2|p zTV?O1(abS<70U~~x*9oiRPy!U2yPmBm~(E%!n{qpQJ>@9ttAzv1PUwN>0<<#Xz zm~)^khwwQK`fT^ph$9_Vy%Ig1{SZLCj^=EN8A#q#iOp^;cm+EtWcZ=5d9(b z5Hq@Q7DLy-b;4zI$BRIlMpL2}kPymhYIW71IZj#8=Jpj=)>|#ieY2{&y=fTPQVGvo zD~T$P+M>Xdx1;-924_2=fo8LP_RwdHQdS9&$(~DB53$jWg~N_+Z^MP|%(V|+!uB_6 z&}UfQmxf_-GZI6jJ8KFH+jKR+^wTGv^cnnT>kw{H#Xb>$#uXFGcB92sa+wk`;xtx; zn5*pD#Lua>jgM$<3uh56FIq^uMvG`}38_=2@jp%UYy0q?g*FP6aGq;Nnc0Zp?4ZVw zuh?P%6t*ulA!cKRF^yYvLzig=a1f1^_3cV+Cb$OVQmEtF8Y^`mGVNw}wA1(}DEkPf zGa|ptZh}dGAD1BQdBHN&eBMJMy}-sIP_1(_<0DO|Eatwm-LY;7tFxi6R^gWUbeOj4 zscu49Hb?*4@RCCS;1?6bWf;5Z$`IZBuoS=tmgf&s*NV9O%TDii5oIj6RT_w&E~M zo4c6kfJuc=3~yJ_`%<0*&}MSe%1miA9qDwm8(oIogl6{_uNQBLQ6$UB!wavz`MS+r zX4I`pC4fmQqWt5hR|)BO zlVJBXdpTM7 z==^D|!bcFDMeHzx2y>ejcNwbF#v+9p7cDRy$p+FPR+e1>n;D&xP*%#tc3$_m3%?d= zKF{5^+@!NWoDCc4Zo-pkIiTtYAveLyZY_^RY;1Ps+*qDVD3f-k52X%JDzMY^M??c? zE<%euYptZ#Rw3TOY|n3{cm}ZLaJDhgOl~`gI*Y^^_Kx%!qoKAz$H~#Y7$|pm*h2c1 zv$qlCu7bb8#1h50aZ8{t-R`By%><e6YlI%f3_K}XVZ(5;8_(VlKYsyMM(}`AC3?ju&j?=;XBW4Jr$Z^<;J%nA}`|k7S zo`XbRc=NR%V(|vJ#TKz!Fn3VK4*w8*JO8QTVJV=&b;|gTG85Frvmp#^c1ZfvU>a0s z>O6WjnKV^CuM*2*FucsAX7D-ZXhQLMQ_kiFQq;k0vX_!7Z^R!}VN z(&}b6`!bOx({j`lPlQUp_uc2Gh6S8ac<9yFuzG_?V+;4T9j%7MzH=zayLA~^0kFMy zC7MC95GI31O`TRt^-43q+t5TdxOO|-OmXwE>13xN#~0J}PLnO7A<7BPcnOCD7}*hJ zI|w!%jk+)jL{A#yKEvG_JR#DIjfrRn8i^(NZ)O@Z(KShk8;dB(HYToE;+)NZhC*Y# z5r95-Z-(YJ*GejzWMeZiw{c2@Y=Uw6$Z6KuBhLhyGzWdgS5o{)yTmfOeUkco$feJs zzfwG7+UiUl3mm|S#oDz;w&(_1@jFuOr?Vs)EF^QqXpf2M`TjPKYr-LD4LJYpyuP0}rsJPRb? zc8w*kVI;Q;&o8aUAEB9%&EKaXBj?BHphdBv3fnb}jJl2~WnxH1n)K3;U5HBPZ7uCeGHzbu+Z#zZta zwQIDdq<;o$`v5$D|NC$L@Q1$P23UnqpjZs!k?crRnKliuVAlDiHx0fc%{&M)EwaHi zWm;;Jl4?c3nXKw?18Y-lPX+hrnM*cgH*@L1wJyChStv95WzsB=`Ri!@>xbdlmPpT| zVczQAWFddMzkx~I(d>rBja)G!TB&04pV>?J^BiMod1r)r52n>kNp%t`Gb>V-WFVSSt(?)|UuTGv z)XEk&3!1ttl0EdLpVQsQv=X4r=FaGIDrYR}GlRyQjBaJR7-p~NL_?v`VrUH0R`rZ? zYf?rgH>RyRa@C`gtR2Q}pSiEMHUaj++pv~+3dF3&y5bwBISzo)ea5M=Qa!u`7|9S0 zI}4qXxqbEOOc2{c+*eMYStRh*?GY3dvqqB}HN|Rst@auw>Th)1;%+z==LXQ^I9l7f zmKvtvMLEx5LL%gHv&&~m__y&W;W>yj6gv2lZgw-tvJEx`s#OI7pUe( z!y^{C*u3&tm^s|qW~vh~hstE2=7ZAaz19=T*rTC6!32Nm_N4eu%x|~86ZcwkT`}P- zjx0_wG&zGY1A{gylmhLY#S{M7A!xqQ`Uo$PfzbH z09^&3VROUe#zZuLu9H9qo)O)~;%AxLoUY-Q-<9W(a#!j;C-Uq(vv3&Yd4=x&*M(;) zG)fSSp%H2Fj1pCK^;{55jlS_oRd0S~ZHsvl&pvzl_{26#Z=>HPP)ne*agHEa?CNO$ z7LkrTCq_4cE+Km71U8gBAkP;ryz^bgz1*PY#0uPH#k_$wbQlIx`4Kjm?iwv zC}LDh(^eJ~^SNm3!@vaZC6L}ad$-VB&0@Zp#^o7Rpjq8j6%M+G3X?&fuURGi^I@_I z-&u*EejCMI5vGAHEi?`VIY zGW%ZgqQ!GD(f{#3zPo4Du<~O4_Pc-gcWstOB10KGORJq}>hy`r+gsEi>_poLV39F1 z@7Q;h6+$04 zT%)?&42s>j(b3a1xwO6`DUd?Lb71_3DsAD;c!3xZOE`{4HX)rwpaPE)^7i*;W-UDD zc7JbG^eT&r;hdmli9Y}SL9K0i&==l$_j~lfA3a6@+VjI4Y13Y=mZVO5UOg&_F~8e1 zhq`PW$J+=re-@#pU|aV^?@gM~9RwTqrF5IWEyM{hNanFUi(5WtY|9xBnob%>w1?7w zw8dKz1CF^RVdjcbjT9bC4TjNjW9=k%4{5f#pIQv9$QWz+s>XpdnzdbSR`oO&rX$ZA z=%;ROljoGV?UQ@Ka*3l{aG!Y+`0e?fc&_DJ;43iQQ@hYXo-yPs9A&zy(mY?k-qq8G zv>FGR3VrEPegNsQv|u#i-E5p(JOv}~F^mv6(VJ&6T@yBUVo>CHJX>_Eh=HR&JRr{@ z6di||vImD6d;F~}w}cZiJR@{Ue)axgF<2!=RZG$xs4F_+wUSD zk12(mb3OraV1WtBuG!r%yg5k)4l$Y421C%jSk1|x%c6H0vbdE$ol)rcBOX}Cj<@Mf z9ldNvJQD7G-5?j#;f5WAEfT&P&k_MkeZvW3n%$Led)K+b<(Dw@WCBvoF)&gP*H%w1 za<}{DnnuoBu^HJ+ED8Jax&mGA%}>rOTwQ^_E7{YQVkjJzGPko|9;2CSH}#p{G5IzM zZd)V(bMa}OuUOM1wpjg3{L>IU?BVX6wUBzmX02(pF-qD!{u$#Cj z`sw4#r#3K{I?yzqJjl(Sj8;nY@$d|w9O)EV;c$OyTtf3+PXw-YAO4Q+OL(T)O@HM5 zL*dLht9O6)ciX2n@Wv2npA~xi+2_PFAgvrSF-@J;3^kX`aE4koI-xeIO{8@zOS}Z` zNOS7Ijn9m9tH&4VH#$VB1t}NQ;@W`Lid&D2DLuukIw^tOkXjn!mXd#VNJ9_gIbJ*)X@{Dr=oaH~`Yv_xb>k%Sj{BoQz z;JxvT@5S$du?b%yR8ghJz@V_)XNJR;T4!Qx>o5lu_Mesg*_AspgrpMM+{!IMs5{@R zk|ie4pLFz&+*`eJN;6k*qPM=iuDqMH3%y53?3j7Zu;mYqf#)>q8MedUGJD$wHTu!N z_{IB&QD?@;F1+*J&)QC?XuEZH*rqJg=wUBcX-675ZQe9{zw9#yH4S4hv@t4@45+Gn`J7=_M3$7Xnn(-Rvwn2hOm!vA-2&q%Y-%8hfz)BzY8)GF@HXB+?m}}v+g{+WVI&z}K_ZChEM#D@yaQ;yJ2^4V zK8j6c>l)4APFdYyy%oJg_=q<$wo=hJp&2)lud8JlDxZz$FaNu180%yo6 z+o1)l?cPK?I;0TqV3g~SWlH%%y^H{{0_AyrTLi~!HVDeh68jTk8C?{ebhriz zk3*jk6zk%S?>JRV7UK#HFm1V?fLT0ubX6DbT}5~XX5Z5%C$KuxyQEn8kh4naGI{=g0T5zHXH1ou0QDSw*&8mjN zz5A;feU<=o{7JVsJh{!m&&5_opP9t&(XcrC{I%3)M#ZqjBu^P}R)a+68>*lD+(x3r1s(x>%| zeV!t|Z4~gq!G4jS8cmBYL#+aD4H}sKYETrx2kW8Y5X6`*( z?lxC}1*JXQ{qg8C!`z+RWSBss`I_iUgUBr5ZVepq0CawGesyD;YkZzO-odh5x{R3q zG@N-GfG#7^6-79_nFxM4$nN=9!85$)=i1eKyx`T>SgC}PEBz3mZA`e8N^>JEdWBEX zL+MWAg~6dFQv4kZ0_K2QUeh*aap6)cFNob*ojPJ>=#?kj7(LJ}2L=BZ|Ac=Lhbf|r z^`K1Eki#q+8z^gQXwp252pDLV-)!FCCGgCc?WN(tYHaBhftE;{Lc52Ed6}-p!raPw z3C|$}mgQQq&5z~AKRkdC-%j(43!u^@&ht@985~rqMkwq;sVpWcc_C4&L9~ZrnHcGrnn}0 zZeoGE%#y-=Hhi$MFAg8r?I~zj?JwW^9*otDvMxN2!J;!o}Y?mIM6Ac5h=1# zL-g*kRC0dyaIzeK>$vn8OoQlKTAg7q;}-T!)0wU+xFkG5l<2;~%!AESuYAh&8J}W` z6qKg2xeaLb_G9Havb-Bu5n=TFV`%a1{h;Jy{D&h1w~c3b(C?wo85Aa;G^EJ^ooRV- z3XnmnFQ0h)q)4Y}e1l8;x;$$7roNVdr$g~?WuG7#<()fVh~L>4(Aih%ktCUv$Cglo zze4Mp!7#;`xYsQQCRlXnLrR8THfc7WnKaARmOL}CZT>R_ni7q8?I56m%#G&q z40KkR+_1U3nkE(z#SQUoephS7PPBODkBnyq&-6)YiDr1#*n`h2o);JArX!h$J2*z3 zm0B{uR575eRg`q0wOt)~*2LBIp8V9}>ejcIGW_Jp3E140SfXqIrShP=$m)*FRYIau zZ^pwCeu|#pbkU1!!Wy3;aQ+{T<~@_>3z)NdT{s91XT%f)-t?zCm(O$bTLr4NO-^Su?$#a<_d_sLbv#}lKD9Qo| zZ%R-|Fdii!5AnX43n znA)mXDd0~^}vZmX47)WG6 z%K~A3BS>cDedGMJCbL|s?bq74Hj7rH1wT~Cke)Rswk)3_S_Onwm2grOlYj6wcy9zB6|daFctQCO7?ONDQLeSk9Xt_BK)&9qQ|E z?9cFwsF>$|LV_3+>l&UqiG}ECp7oi+sSO{LHaMGX2+)lGF!ZoReO7Rrt^rqhN}sPD zY3cI|;ThYyBUenPkEal2KS!GSd)zBwx)_$Djn6J$L2YP;xe*l;Qk;oqZ+1GoNEClm zF_P_#vH)ivMVup80kA#pE5>vBZyY|ABq18>px;9W^A0DF!wsIJ-oo}ip`b7lh+*eBjW{vaKmU~W!Hp3Y zXQ{Ky*BuuAq&_F|Oy@bR&mkJ-^UhqGPnuiq+-X+J!-+;_NmidB(EyqiIn3lncg;^< z+e8_>M4$PQ;(7Jc%QLDL?1-SwDW3I-1J5co^cTQ0Du4Fg8J!px%({W6Qq!AbGPk_w z8#k{ven2u0mI6%kMl1q58 zTb4R4Biu?Q0c>1cTH&GN+YGY|>!8iD!UNIaN!$mXJqpGzkWfNCyy_tIjt&FM&3UPh z1ZT#=Fi{m}ohi)u;XBPkO-;Aa>d?S*p^$Hcsmu=_Y=s+~A z9083}q^KC`igk}nEj&PO3Bsf{afKW$W+br%G)%XTfn$Hm0CDR60MI$ixH}G>DC7^%RA7^AuhNMr zYqL|$yDpBKQHoQ|A@;j6&a(l+Y)?zJvS1jD*VUMs!O*r0ZNf}5Tb)qCEPr`;xTWPz z3mXI)8|b2X{-q}0^F85N3N0Vng1A^sG`n#E@gB=WBe5J}(kSbgPnPfuoMBJs6GNUA zza!6Bs%^ClW)2qxOMdIP{b#t)DEv8)A9I&Q89A_PZn1hfG#Xc<+KX@FA}Yp7 zxPkj4&RkQmQ^O1NH%oDN6liup6kw7gs;WJVYn!DV;$SE#6sDXR1dA~+CRS3Yyl#K-2^XZQz4D`oIP{Ab3*uq3*A)b!ai#V~Kh1(ckLW@-tRP&SM&oV;>+ zbLT1a;lvj5dbom;>TJ!3X2iELG^bS95BFv&{4{)?;`=npIxc>}o{z)3m%snc5mQML zv-{O|zxOjz1dLJ~8AjB`01z!(YVBi$ueVNPMH0&QsOrQz%J6Hl=2^OKchqVg=v_1@ z(66R=s*Wg#iRtonyH<3BT9Q?iV==3oO>@ z2z{reIx>w2w#;Um)gpxyYm#kqTeP<9e>)k_Z>5hifz-F_(&rNa_!As^OyKw~fd}T3 zdwdw-7LRB%A;XfvQZrQN?%usSiAHNX`h4#D$ylUdBP`ZbSBrh`>+TE>->L1(N;HFF z-Cb3nSx)qg8@bx?tJ`fngTl3}!RFi=i*E^4WjNG= z+jzs}3Yivv@|=S*?_B&;WxQwceBqt%wV9zyqG>O(xv&J3=J%5$YJZ@=z{T#@SVH_`~0(s zAAH!O7vMirs5?8mdTY2H4i%aic4(KH8&K?+VKLg=pWN)eJvO_%ws{`0hR>cpJ%M@} zYiF72k$AT5NgoT@3B2*B7%V>4z74qXAWw$j!#p@2-Y|Wi{=>ie6&7MtN}n&h{cZxZ zBMk*9!*qaKzH1a}1(2Bl!Ik6msS)yE6jM+aN6IMKAp=EfEy0)78Dm%kJ4nAzlIeQ$ z8u(@QV0n0GQj^EltQBuLQw8B4c1leGA>|WXkyp`KN8Jf*c9u&dao@oUg!keq3>!TE-KBWgZ6 zI45qHacwRzs3zEnT>GfOkwaz4IfjHHZ>+D4qWM;6n`QKjW;)Jl@}6gu5k{czN}OdK z0%RE1&9`f-nK^=u+<|c?D{ixK?O#V%Pc62qYwU0Di!)cOUIOt()&v{%2h-3qr*{|; zv*8fm$uj>*L)VrXQ9ZJi3nHwm-o zH*)+;snxUbE2KKzh%_4COm@7WwvZW3XbjZUUIK09DiDS9y&`R6PF2%J{cJ0wjeR7R zu3lw<+r($KyiRiLVQnO9CyS|=KQi3hG^m~E05Sl*E|LkT8WX^7+^p&yChyoqHrm{0 zSW*myxvLClXb*zit?THf)L^wN&WSqHT|vVDDxO`Qjc0n#nqN~XgT>Z~!&035-V5Xz zZgh7|9=k+SpsCMH=Wc5E9fNpMZ*se!>1E7ZA-CiSb4wruDnx_}&HA6A9aW{j{yamr z)VALL<^Qf|`|$lgBJ9+r&gERQzhy$+RMC*|VJ!PCJFScB3F#{N^GXshmwhJ~*x&Ff zU93~n$}v|&NSjorq2`RWg@}x40!@fn3SY(WwFlLG%|)3G<2DAeDZ|oXn$^-`&Q2)_ ztYxSUv988;0DCjosJ5GSj)4&)tCyG?uTepMC{lv1dWJ@s7xuQj|lW%0?#J)zyI+6 zRJQ%^4>9AUAX6+`kIp8Y_5fL;G#QrhAgZe?wd!?Sh7)H{Pd>PYhzBR5UO@mHVvRTC z$Y8&TUpBR-NSjQ@7`V)D1;DM+m#l84HG^kbqRiaXey>Upv6-l8Z>dNZG=d4m%KaL? zGt^W!$e8s2B3)2p$Z=Pv1X&Rq2y!4;_t>n8>dYr^cJ}2_W4gK;$)DE1vnw=lra^fF ztD(o2w@@V&<5m&O9Q5Q>z}_~T1JAIw72GzSm7Pj+8$2)3^-pB>&_^$r=Ui`pt%b#S z?zu{1W_C&AlYq8Woav;@`m=31~&h0JQqKXKl)!HkRj1i&-y-It)WP zDlxq{OZj4ms|H$3Z6$;7kZD;HWN0fIyIB#+k!vt6Y3XdpU&^=C4Pd*v6wkJG2Hyd1 zgRI6}%hc7)CmgZLH0gFZo|QTm3Ju|$B|Oj09Gc?f7hf#TOfBiD^IS0`w@4!n!H%m`fg& z>=iOMgE@^g6+^d8iu?Nzt2Txy$erTYon(OwlwFr8$f1d7!c5{jux@`CM&zBwWZH@X zZMHho8u2o`8De5nCwHEffmwrH+T4ad;|w#n%>W>xVV+B(;@ zTb}8NvJRfha|UKUVC{|2$^aLiIy%l;mlWS`TW?6-E?@owaQ?>%w_pGIgFk$v310FX zc7HROmfc+%v{g;41mnadHoZ2a(Pc5cd|7*ja_ySrnr0Q#{4l8us@v#U+js#A=s;UD zrB|n?(QT>YxZtWtoz2~phs>_yl8iMKP-M(HAs*epFtkkTU|UxaPc%n4vJ8GBTbk)3a|)R(6J~|2=_ylG zh%#A5Gk2Mb1)$Ha&~%rX&%{^p11let zc`E&yBJ?63hdGGvrGxkmAN~Hr%Hs^R|Lq5VaElv%N+NAge;SZhyqghkiK&_2K}#*Z zWtO)^w(M50Q<=;cX2lbF)kK<&P-{)KXMl-nj2<3Z*QAb3?8tOicZYaEy_Pi}1>f1z zu|0825fon*!`O2i(;$G%2S6PP=(o*4YBfibNMvzvb+}#bBYlnt00aKWmn4FrK znZ#Dd(0nYP1kXlveLbA$t}5@T9UZxryDM$lk{~%fKKwb*0V1-=8 zp43O$Rx&!!FIu0$GwOUU!V2qzCF2<9RhPnjO{XIcxmQ|f*bw9_E!H8dF4}jPoKK$U<6*ji@cEA4Zhkq#k zurAX^bWrIqixq@gofc-Y2{m+CaV?KlOQsdErcw)QmgwWih)ZEkk#a_u??oD&c0K; z-e9UqjcLfa?)u(_{_5JgraM9zcJ}xrGRmfBW~L_!!`1EDdh%8S!fTXh0<8)}rajl| z?@X^==Gv6ReUSYf;@cb(ooC9kC6oBsz{!xgZIjQ7Gm*)o9J~acbuM#FFly)pU8E<3 zO>stAYR+^i)X<6X4|PjAYbO>~FsO5jQ)K>bU(wc|*Vf^8usb-r6#2&L#erGr#Pe3k!(SY5Qr8j zb1f8IMm2iZjD(AAk5BT1*Y`P?E9sd17*M^6t5*>1n2;s7;JEHP+K{KGVm^YrK7Z zJ)>e)qqYhIL{(RQX6*@KR*Zpuvz%w#a7;_4s~8PSc*ba@yj88O!UQ|q=Y^wI0<=-! zW%1mZtH;C@hgoVgp@u|r;>sH&8o4ER=a-Q!28o77Z*QVF`h$~nqLH^?EoF3C8$|Th`vzPl|6M#mS0+6d7*2R_1Q!qklOD@0kJ(5Bh`OTS%<*!zV;jrDYG3YRflC zkOtU^JMDTMpbkGE{OZ-pky5;jg*o8K;JUy^La+9zLq5cFba6C5+mA%Q&**5y1?!Du z4R3ZH&Qt;GAygTGFU`mp*%N$_Q%+g+fZd)RuqZYmwv7YTwY7t{hngYEtPKxQ7S13# z-Nwg8huVShnOh0Zd_NWaw2q2xUgkERe(te7;;i{-o~RHG<6IoZv^3B7ep#NETIZ*Z zBFpi2Ulz}BqkC#bQ2+)!Gp$6{6V_02=(~wwlx}nf%A?Lbgx7Uy8wGuze2QX$+?bnt z@bc=HZOI%{RNj(<6wk%HrXEWv{BguM)&ENlV`NE49QEdpAN~FVOjI3DLx}tLWk)2* zG(UO>j*)3KA?;IH1U0k(ukIQ7)&z5$r^eI%N_BAdZC}x^sVTpPWE+6%k+Uq=E&r2g z+e-c9n!;GI(-t>}Wso*^rA(3*mJvb?p+Rr8gLQ*-b+?85`c@INI|>DVT$Kny1o&esPRz|&iwu#|L|eu z=c>Z!0D(rD*lvyA$caWbbe^A3sx2n1PV1DC)(q0-RP)v&-~Lmdo-)Up0U`?zed}A= z?$tEgu4xKrn&+7L!hEGxVj!Jc&a`S4DA$t?^)%O8L=?#=QxHcl#l}b0E~4=TXyg?Q z00U=oY$V$c{dR0}a%^OXb@rn^lV|OO!!Qr}>GfQ9erEMi7_+3$hO}GT?mipOR*4BF zi1BQ;2NPPaF3d!UJ<`F;(`V$CmFKq6FNDP&oW;Vtoi=oWM_U`K zNa%$VjYYC5Qv^5@LdkVc~; zS_6V0I>ofa$UCy#C1AS?otA0+kgvnOoB}PQ6ihQhuJw_e z!GfSE33QR^+&-Db0^4wTr>D7^3R)W(6{x8j^*7w4Wmrz15H^*kxu`Y<-G&c6aHjWG z&}Z1}G~ubx3fm%|qGoJvhh8>juEs0WREhvITaCO>6u`fifOB;0OR|lR$GB=+TfVc($eRU-EC-3r`nWO zO@j0Qdf*z|N=j6O$Z|&WBStfB$0w&}#zzJl>Lbtez~P7k=ql-RC;D*9#PW$}vOl2A z(SNovOT!kwotxVlup-aMnSs5%ytKSLTY>&zQKv9#;)0&rL5HH1q$F{Lnye z7u;vr+`Q`=7@3@1TE)6(F1lh^3`6v_HLj&EgR-8gkUl3UM~pN4+JgjPZYN&cT-*+N z_s8G=GZH<6oF2pdZ!l-|QSt>#j`WTB1+{8J-wNM>+)1Xb3=pX%m_-i>PBpOVs%Lkxs{UoHBjnVM1hLWB%Sf-|_VXcPMG2qhYS*@51LVb=auGok%HQFF{ zS})2f$6CM8tUMqdF+8h(g~Hh6^u*Z6U|&1uxEW&W?L$T>^TK_06>*^M{*ikRE~A>U zOpnh=G+M-2;V{O-Akb!Squ+9nQZflMX09TeWada#cJj+FlV|zVo%PLya{yXFu>#y1 z=5yQNhiVbi9h+S_yZUhB{HIT`h1%9SatxLqte@I-u4;cgryS%`13YVHAC~8X zVjN%pyO00nx5q$n@|LMI?h=;yMA0|qpcOxyAuR~$QLFoUH!dL?PZ-VdC&HO3#>RdR z2}1+W==55DM1l4hXu`~X?+)~}hZxIAGuK>MGn&CNVK%cHx{Uflpjd4JCE;W$O0(>R;G?E+2nBk;^Zi*O&7S{Ex(nnX~)be=K3HL~=vop0bg zdMQ7IHI#^m@nlJF%KPrqFJSW={(1-f7k=yd@QLX zDWH)h25ig1T%PB))R&=5vk__~h6bw1wMgCj#>ap(&g5AMkuta=&(vl`!z{v0W#%Fc zG}zb+%rbFR_9_PEKZjajQe<{5i7_HH0cFzb^|wJaACYFoyB7&T{}} zO=)Js*rl3V^h_CjPNHDheqN+jW@!6u8GAd`@eg_U&&QNXlHwT__eVP(hKk=deo-Cp z=_F79%bQEF(ELjo(iZo2s1vM%YDcbVhl2G;G7q3)mU7|p9IAIHWW+x2rZ%G6W@Q6q z1;MylIIYNDR7iGp#;Qh-(#j3#v2KJeZh$cw=@ALd%Dy&3*e8f!65Pi9O(}SS+yh?&oAQA zMnRn>x`^J=hhJZw|MNJuigAeslRs5N5DI-0>nnR;aj!msXCan(vm4LohUluX zW;kQ@SD7XAZ;EFwqHMm1IRvwcVCjs)=B~GFF*qp5)9dPPL_#+zmuzfqKY~DPbK;7r zC0vc?>0)V`pR<~xlx{uqHPPpfk+^-F!7#&`Y217BEV?tsi1AFQqe?4EMk3w5#9hSI z^H0>jQ%@$H-AIPeLjapVWWVw)UPR}aU7oX5CeJdrJqkukTcPZ9=*wrQ0?uYATUWv? zbPDG^c+O~Zgqd^Gcc-TBF(_8sI6&8#`&w~{ubR?lemyje zRnhiP`gSn4F-R@7F3im)5v`*gy?p=KRJ!xlNMUk}kqS3;AkpnyM2V#tNVo06Kv-2b zIk$pEvFlqqPf#sz^WpL`W};EF3Qhj=zZIVU^v}nz>^UvXc8oOc5C3jYecp>_U%3%Y zZ|k{fs?%5}1RM3Nldt8GhO*7(c=Xp5tb}I-!d#%u){axocM3@THOdT@RqGhxX^sJ^U0gF*iV5pE<@6 zXP=C-xj5rl=C&rQkR4Nz(j;G#c$iz=UES@&ocQAj!VtMj}=UwD?ucxClXAMw$6wT1ASm{yppHqtEz5H-@fBGXb!;W8` zJ&ijHbo`>pLG~-hZ<|qQd{H{nf$4Z}@G2%0pThxPs|JphOcEw?D9q~aL7SBsX4=e1 zn9ohobM_6|y_0QQ-QF&Sc@i*ldB)7l?CeAyg+Wz+Nczm! z7^k8kIdTenk;l#221PvcTl1SLxQ$b7p?hS&AutdopBG!_D^i-|>%}un>fWKT2^Jv| z&j@3T45Q9L6=tkDkX45~yY5^s>KvTJBIxz?tv1yE+(bzlOhvDAt2D2NNv8M0)_xY@ zO%BRnyj!0x#+dFT+TrLe`K>?w@#Eu5+|H`>Z@3UT`IhC6cI6pphY!#1ZiJ?8Bf$VY z^)z0}z?zzIY=I1!SjzSE+$qcQ4Aae5uVRiWPEG}!H7_M;_Sxlv)H&ju7H2@4a+y7v-uyBum1yc}`}7sY zO0eSU6va6({epQ8oqEsvREK`f;h#P}#>DL+o`3tnKYx_v^?8pRM89j>>pREqsTXO; z;|+T@rkT+>C9*%8_NdO(a<%ZRLT*87hlv({r{RV5!dOKd?Br-O3W_B>bK8ttw|ct~uI+#r3L86Kogc|T*Iv7X&BwC zHHC1=LuHGV!#L63%uyLJPM(S~Ri|M-i+KLcZ~x^Z@m1!l zmVYWa0X!-)N_bAf>F^^yg%<{d9b@EatGRtmRd!KUA{fSKx3R74wm`-VReG*D=8Tx% z9GqwHY&0ua4b@F~&w115fN*lpd8(}2JbBm6?Acjls?0Aeoot<*96BQ%Lg#@wd#9 z4jkJOE!xy<*|~l*uw`Rw1r>9^g12l5oeF*>xg(Z3VotNNvy*jH#kRI&0G@ER{AWiJG<+~&IO=3#$G}K zXrHo6Fwcj|61`Yvd2y;0ak15h>)ZHgv}O>Y(#p^#Aa?b zQD*GR_r%C%blQ?BTP>BIq@uDyU!%2MhG&!F^eqEmVQxxg4KVMef1l}aW7(qBKG ztEzIhnnvsb`CvPezs-Wa9gy$t-u7F)4Yk9C$(j2YHIZJzeHd6t5+nTj=~EDF_N2d9 zoBy{hCp2W#6y+d-gRqs{0}W9*S^hnXEs5ilt;%ZHI> zv$lcr;^OiO3d=I&x^b=bZoaOsw+|u>_c`I2v(V_A*K-5;dk-%^l?R>jpNn|TPCt|9 znaO{n_I9wlt8z7{1LHO~Mo>&$-Kp;8rZ^o{Ih07Qt*xzVE=(_+)I#@-&GVP#M?d5; zO3qCS=d>OdAIHvC?%-wW^Z)(lW2zaJ5S%?iEwK;(^e4mjIky>j%RHcXJJAWwJo9pX z&ND+mGz(?XtU?80ia9K4RtU^IXUExd!4%Slp&isYEP{+YE4CdSXj;{kbOqXN3!lk0Ll?bS84 zHMRM%>G>7g3TI>MGJ4Rdb?ixuQYPo&K^FsD^tc?x@wu1C^Y4E50)t@=_OCzq`2T0` z-Jjw*?{wcYk(*}M*iIbFl6{gn8l-b}oH%ny3QG!?4g8RhsHk${K>0t}soIs&E)^;> zlgY&6yOTKEmy8uF@{DECb25`7Cp#ARJ2W&1Nn8a&;tm3IaWdz5zVG|4^{%EH8d!}) zvtB37>ZVzen)O-F_x`@H4$0QH6L4%^N`+^Me>r1Epw8NDzAkN$G__r0Tgx+IQnoy= z;@RNbD0ItPSlrNO z4)AyN89;O7CoS%V=&^J2>`7VWKjTKHcs9L~=m2GP7zc0rD9`t$y-k^}sSial=K#MW z#w%eoMT^7D+EJ;?YwajO1wgFtGAg?1Mnjfwu5cd>I6wH_<_QcwSjv z|G(E?Mlj5)pXq;woYl(&md@ThF24(q5E1d2%FI{DB%SSHi%>9mOX=_<#nptdZ z>1$>@TRdkOP@r6AtBcCuoJ^mIC6_Az36HGrtM?<4rovwww=L-@0hur}vJITCj-9)9 zsG3`GL7rJxJUMea7dhYqw2fw@02i%3y44 z(7$ZeeXGwP8s3QdC8@4eAl;Q+fR7F10&AO#qcFjXqurNACaR|notwBmvj_ucscdHQ z+T_XEG8GfE3HBZ*!q|B&Jv_&wkmxxIx3@1>W$qNnS-tl9x-`p`ndtA#>s76Fc~;3O zk}PgBJZFi63C;AJMVk}j%+Pi+o{c~&|JlPbAea9sfc0Y^Tse>|Lp|N_m#8umRI&i` z=vCyYD9_xkeui_8*uXgwYYR0Owqhq)#j@}Wpmpz1Ls8#Y*^~fkkuvFXQZL9qlEhz^j^F&Tz(BV z_{o##(pX-SopE3t&90fQN;u%}-t#P}=WW+#&Jug!h1YFqR?QOjqw`%LZH_IvU8HS& zR*o}vQl>Q0U9)Oo|q)9>Y6cosEnN` zOjl`~myF3mob{+7!!!P!{tW8XA+`IMXAs@kh3XO*N_2p_qupS-v!$W-6U4>pscn!Zi6b8f}xTUzBg#4_cWe%dE_7*xCG(7)u|eE@L6EJq~qdfClmaai+U~DYMh% zm%G(O1Dj_w7l~goVeGt? z7M|C4o>yLY?WfysZ41wT_v6=IK&PYC!5jy(JQd;vXNA5Y+Ys%Dv~riyJibWf2D;8t?^cggX7QO>pl#e`!I>b-8izPvn>ci%dJ^)i zc%I8GtDK!2?du2?#~-D9V@mR1IX?Oc4BnqxT;Vt(%-v_( zl}<+MY&3z@WIOC8I!Fu8=AuvdRgr<2VcTzPix-ViB^2n?mh(PlWAxJhDi6_AXvV^H z4T=KGiD*WcOh)U%+hcvcp-Ayq?a;|&8+d4%?h zu3+OP%4K-gwI#~p-Wt!Ay&cChMuxdabDn`-Hp495G)od)>VbEK0o^;LV%adWN9i<= zA)<}QoL<0L_9!+y!~30Im>oOTlZB9&#WTP*Nh^-G?7NnWGH!limOe|i%HbKwDv{^$ z@gYC6RrkL>v*`+>hE&%TtN7qEW7;B8SJ<(;rY@gEU+h1IYTJ=9-cpgdT0mRwB63&o z(O5o4qk9MNj0wH>#@etuR~z3%$G8QUH+zX@v6x)K6lh~`OW%cxZI{x6_WIB9 z`s~`ytqV(3p_rFp>lqAdMLi8{y+q@^IE&xR7#P(VvMdh$q3hhC_&MgD8QfO8 zYoQi#^6JG-YeDWk8+f^N{d1KME_l&AHncWiWou$J%Y$?e}*)j6O6s?M3RUxLXwjP@~~R zm$YMEU=940+Q#f|3`xE+Ff@WB2Qr4&r)J7>mR7h2{LyJ98$dMOWQep=HW| zT2G&iKsyB`UKd&^wA#qVBHBTj2XU&bafap2d^Tj?3TsQTHsu*sHWv0^sji08_ME9D z(LCb@H8<9OqRnxhU7pdHQOgUqvGejIW{J_&F_p|g1wEj#>mxi++NXXg>gjqG;hwquE9vqIrL!V z%%gEbyU+Jq{&{8j^><)x-=k=lkc}ktPk7~GN#%sHE?GJ&6S!q^fs=VwNPEbZmpnX+ z6Rk{c-B_hUVy2d4RU3w7Z@bg-O^BN@F)WV{Ep&T7g+=kN@mL!D})Fg+k08jaM|1!>VJie0?I+pTn~BFQj`if%Ry zEkL46r?t(EaR>J+&#%Am+K=xg%|QA`fA{mBBmBdQnAb5EJ~^gpWWNF$H=@=KvvG72 zXy{M_oplzF*uo7Nc-Lfo{@v_CTaI7A9XTa4WHQL73X8nkG z&kTa;PXQmp^z^rNZJ6*3Lj%n}GlvFa;nr0=qmH{yC9R4sO~B$d0&RGf&MFp%A-YX@ z2G1iS{<_aQz}{B+jLj%(8^SuA&4MvzuP|Or#=XFc?t(<)Haj{pboCng+a{|~QL=y` zbrnP!1$%U(O}5H5DIF`@J-7$gNs^XLhMY1rGpYM zEFkOPYtVLIP(Vvxk@le#&no#!a~sQbpczlr)g9R?R=C{b^jQQt5znq5%aO~m zg|Ty}a5lC!FB=wjN(BkcZ9_Bu<)zanCHstc7#YcM5MQZk%_0bjV2EUZA8luUJ^$71a!>w>J0j6fS7T2*|42`Q#fIB$SwDH5Zq zCKhPpNyni~s2zsIW0q%f8s$4{arW@c9F<k^b@|yK8bf_0bA|fPuIv`X#UiXE z!*uS!k#dzkJOq;o*(x~B_&6!i%w1usFRnvPwX4VRN2%$vQYk!E=xaCOmsJ3naSXACJlUiHXM=vQ7{s=((}iEEZ6iQHo58U?#tayio34?CRZ4wMGTSP4XMvXFcV z&Oe9;=HtNCEU`$&xbZ_$O`&;4&WdSREar!x&t@#iDt&e%Vuom|w8Yh7_sGTA90|qd zLDB3MYa?I9T$PH35ofv^&pr3t^z;$Odsg-~GbMBLIWuFIds?chkb|x&Y;iJIEaVn} zuF2~fnq9hc7XnT9*~qiGpCPIPa~o4jM+STjhwU7DTVZ`6l84k)UvEzjhq%GyCeej? zaHFBo`BAi6O;4gpeC+y3bWvVMx@vB5c?G+`ADu*DJ%$sdgJ<>?ahWUF{_45J)e{A? z?V8xjg$ZKFMs?awqL)CSdOIdr=TN*c?ct@;c*U+pury{7t8~ePL*Oy zZtfh0D;ysyn8rWc{r5Vb0wh`it$5ZmNfgp4^6bG`ncTqCrFYelff!#6!tPhZO;upq z@C=pKJnITi)C&vGrPD{^cxFfy6L1#iCP#V;(K(6fL)bOL#JZ`_5NH_OHTk_`Wp@qF z^6!`6wkh^hJdckYJ5EvcaozvATNzW>QrX!h>J_A4% z-zJp?)>Ra{FPOI^;+Z(pb>!$sfwtX#olV4>IJpFy@I$RisI3)dzCQ?i43dBmuh^6v>;(q11PmNzBSUk*@?j^ z)yuTFJsxz5JX`+r)_4|eUd=NKw^cZd#UvsG_X7)V6H*5xJ1iQYb*#X7yGg`O_Va`bmU z``N$%8p=_VGgrB|gss}|$nNZtd#P9`!_-B3)#K%`!PP=Rp7bq9EST56>VZKeij<oaiHiK;kxCeMfy;GaUCX>6~-Gx@B{9lsn4 z!_bcBnJzS>Wlw$UvFWATcW)=)S>&13VWWfHKAPHcs-39M*q)ACX#MJP&%W~jE5}`{YhWpGV zqYY~6W94(0^|`Vv0Y(lg*2amJ=X2v%ecScfy({cQo@MTSv}~qPxrCzQ<#{SLgjhnEptMZ_%p1kqa+yC@W z7jRqLgL`IubGS1ayNC_Lre<@He);rMM9Ua1gZW*VdvfynwK2V)6dX~;hpE(@RjqdX z6w8z!rrZ!YQY_Ae_Ra!aE55w}|JjSP9S)O|@IkrHt9VxKGjB>g?4@;u~sbbDvo+hTXuV4y*;z%WDvCn3!U>X>B?njzuv{li6HsNjRGZYs3%xHe)Vf>pa=g0e^;ihIV?eL5(>bQrj zL}xD)^bF-9tVn%cU@L~wXXF7nI1Fh(WUG8G_JmCDNPX6hbS?IP$80x~4%B zYDEbs?u`vuv7rf6eZn+?l{0g)bQ0mQ*-F@r7z4`ODy;4VKpT?P*Yxo07~VXP)U%7z zeQ@;F+wc7k*=?IptAiTs1z6Y6n1$g_@MKDJ7hSD}4ZgB~J0`Sx{Hov~h_H%Y+1@Hf zCP{yC&6a-dBq?>DG`C@JOZr()-G&dFs6NbNa2sMb%Cp80+< zV;eY|c$mUjZAD!6G?f7AFf|zKz(yJFV%{o0kF$q3Osp={SB;w=I)jjcM{r*zhjAM^ zM0aGc=kR6U!(ls`XVi4J6i30bEV$8#jup!|B4o9~C;+`WiH!4MSb4dspag?I;n<3* z7>%~n1HYs#@tnwk_Q0Fq$$e1h8FvqXK)-uRam>dW)aq~{W>MeRR2444Efkft(0Nz_odVAuoE`reTqi)<(7sBa4bD1Q zWt16Aqt=trFbV8Qbo)7|v&b`XR_!Ueh#l&Th3jMiUG_8BF9V;6BhOM-g02kC9>Q)e zH4WV(b4$$RTRcmd4tp_>`%8nly|Z~{Ho6Lf_j`LerJKfg4~*_A6pZcRW(D1SL)VWk zFkb+bW)Nm-3h8LZT`LynXECcAy_E9?VP0$oXy*XmOiiTMw7k6>t0a|9Rc-h7HgWJK zdiutj@1ClOL~5`iT-q2y{k+I^U0r=cV@tRLa~M&)GrzcuJH7pR3cSk2+f8s9a=zyTX@!^Ntmnx&gjMP zdGNLliS1;28&?`q&YTzsb6bft6uPT3+$00WYV+D+gXd=Eb0C}*+~VpaYD}hQ=NDPU z1fWsXjo2-1C;oc$2zWxxWia=jXN&IpqtG)R>bKs0*FdaUw!Y}^pgE?sq^A!N@UiPN zsMvv0OZ*%46SOV0Qqv`7g98xJwLH*gQE2*-dMq9)N_c@&)@3ELw=FzfW>e=m9%scf zVb-k}YWyDt7^qAYfyRC4+3yf%QD>)co!11n;+sD@7HhHN+uHJmM9T&^dm4`qJp+fN z0tKHca6nCAX%2ZV9~{gWB)k0C4J7a@m(+q7h z%Q7eFE6z8_!Ea*`&spO=%gu7A!?2;;wJki;+K#8`-amf}7Pm!k!o!-Hy86bJP*r0= zYc`@~7q1MJUq^fbw@O6I7%W=`*GyNV>K5gC6Kv;z=`CbD{j}Q(tTR^7^L2T43&d=T z+eO=RWWjF^70}vyCd~|m(bUG0(lD81K%QZ2fA7eVBj~KMaps*!f_u1BShbJRx%uj2 zC2goDiCYEIIC){uh5F+DNk+tON+6I?1Df098M(gl@?%<4{rs}yd4@*U}`aDLsMPyX=9o`#aXtFsmG^YE#Pa7-S$Az@kuD&}ytaeo~8LzHOXjBX8qdOLW2 z{eCI*jJt=t@#cF8Azt;OGRAR7tuMfk7^DbraHF<_ywj?KbM{u8#Vjt;zs> z=0z649(5fj)D%#9(kGzN%w6GIb$Q{~SePB{D(>pWo?@`UQG`;48zZk` zK#K-`ILa*4X2HpfZ0bv~L&R6_v*HZyUfyOt+WB zAhGnBJo`L&TZ?=K=rgW5G_*99puU8Qc6T>s9dv|Sq*7;3T~*JOYuIO-)$uvFOCouN zaR)PHH`(KKbAA?OO0WdsM<>^3WsuX&=91u-I@Hbm@`~Esoat!!WxEfS$7~;N_txz$ zu8;08ra6XBoGs0k4Q0HfU=!i4z zHlf*R4qeG99qnsF0$ML4(9H$(pfP~9wy}47b_F~mODEM~ytBarIfuQihkTywsesN5 zo&hv+R~=F5jV8~Mi|%N{_!5}owFS|Oqto-tXdIF;fecEZ*aXQd;02(a&MT|-!E0}O zdDbY)7e(@Bcz)+TwYCZSTW`LXnn1fy@#%#2iBO3%~NjU@O-~S$gFsL(lR%vJP z!{|Hne$D$bVg>Nnu5_Wzs-=ZYyN)z0ZfS%@o7KSN((PqogNXYL~gu@d?)4!j8qFLVE-{fj-Ck&mz5Ep5xWner{-n^Q?$gu`q|{ ziHYxjAI<<%RhTdIJHkwsLGv>|q&z#uwoGm?>b!IlR4>kr#5%KDTAiri&V=kCa+A%7-QXHSmAdkNYlJ#Hp^PJK5Oe+1lr)tdqnc3 zfL?;NeIM0%##`VBiLH^e@=%=MiFGGn8A@w$^mwfA(iP@_A*)xe3=NM>Fe{Br1Hwd- z!=VSQ1UdoFVk+B&@;G&#t<%P^jC zWnl_vv7UASA6l^P2sNQxr?#%Sq<82B-DtM+E=k=3i%Qor^o^LK;0QoYCKHCdgLal{Zu0A&-Bqkno zUGv3B)Z8qx$d!>n1_lpZL%ZonpK^G8e#|6&#^nSdZiYlND8>Px7{%!3n3;!3sosG{YW?fL7Eu&ffO& z4Ep6ws4-Zz67kH}SQvI+rmEa~N$N=}18(xKs%#Wqbwgu5v@Dc`a9OlF);}&&IZSy^fo z_~r42%+v@T!1Mf#foK7mCJR0Ob2IjcmObA0L^=j07tyqW9s;Qjlkhfp9y;N(GON>N z9M9+n5rZ#oU(SnX%E~!LER3b7ideHG)lMi=!wofj;*j8O;h473toyry&#@n3%hD z2SCHdCeh4CW3y=1buY0`L#FT3!V=w;=$M$-4$7KgAv#r_g=z!zJFncct)6XS*FJuO zNh>R|wcdxm{`XmUQdbX=Zft6TgV|Jw{SmH=-k3pp8as{MScB)J{Rv{4YI>x(3^UBTav%38tma^-k$b~rK5 zGV8M;Ixr(w8sr(-XGFu0ejXXRoE`AI{w1T{!IVjTbCGP1!`c#NqoW<+Lbx=zq#D~$ zUowN~$l^y=;=ai!1&QWPvrK2H>=hzqK-NofvS)asw#?~CC(&qAX!6cG_i1667jQo< zZuNVi56y~`f>3)`S66X+xV`8^-{1to+yojME~0@$B4Q%YYsAA`a1$I+nLVbq%d$?tbdm-0E!`O^5JdmDnSmyc9nrH;Z@;kVh2Et;pHx4It7l#^IR=cMb z(+;MV?qYb37~Pz)fg1TqOy5BF-z_8;;LdS7c=leUyc{=r4W0oy3V&bS0oFFr-dfTs z>CHD%+fY^1joz7PQK%`@)jM1|yMVju6ym-P&ji}#d6oa1fMVw*>dB%V=KmjLI<9my0?ZY1ToMBshcU*`Zi85zi?4WQe=AvF9p;A}1G95S2Wm z+H>eqdct9u$}=yfNL^lQXY>R->EldDqo}*9C`6~G78g{sZ)g&MF%+1f;k+^z&CpP2 z4(rD6O`0olH&so$sRB*&Tf5Oo^;vj+^)>8nCw6&c z5)B#z+KF(B2kr7KhO-0gItTJ*_rU>L$HElO@X=MR8be_Y&oW&|^L+SQx-&;LJ3DzQ zn%dGsMsY?7@tr&K<^A1AR;|)!G(xLlrlJ89C*GWsedrJ-0M6w@ebKa-+nLNWi$L?+ zPq3t0`_UNG-4<$*d2O`~*}X%hqS2q;Ve$%}HfwihFrx^02$q=OcFHOy#JW>7y2Z;T z(zLmg8{M2?urqjmZSfrM zKf6flu$L3ac0uNm4YRhjrA3l7t2x7Zyb#41wInR_WdGOv;ltnh*1~_stsM7g^~=<* zvx76tZKg@Eat;mjRIx&gMz*~W5v#97Y`>{@d}etGJVP{yGfa=cG|6*D^Nj1NroJg# zO(f!^ZZt=i6t#uWd4(&gA#~zc`N;~FjKRicAWX%&F`kwqOwhi|^cC$dL^EDlk@BeaBHBw^d@}f zIM2ef>UgF)>lO@AXQ5ekl01rVn8WkJf0th-Z_T(MY}c7B-XhPy`R>y6NN=dMH3833 z#U0tx&@(i(yfiOi8t{zT?a=|>2XAY+&$xV*bHV5XB(IQ-re(+K5&&J?QG}612vyY8 zx0LjalrBnO?Dn#Z0u_~(x^BcVrM;4xJ*yJZC={ddjXSnvv3R_CX`Yjc^b7xZ|EH=f zpf}cO760L{@tmQt+WMC0MW`reoK&y`_lBNp0N_^p&%l*lvGI&aZxUJ7U<=ApKw|RF zGHIJ-o?>jvrkSckl0A~J%|xCx&-m5K%{ej7%GwrvUY?ui&(3PK%eS>k!{X-dH+5Z? z4lOP7exw!05jD>MJ|Nc^0Tq zCgyX}%7Zw@zjgOxLyn2kd3^f0oP8iw+;Kc>J{>FDk!t;ogkQk0m3B6DBn*XVc?Ql* zJxj+kcm~ek8S;$zowT>b*5+;5$vU%&M2%k|&BolmyRtZSr4xQ!0)Z}&qEF1G9J{$( z$pKf=Wrl5^(=p0G?hqNT&%Az+qY5$lg1{J}+yoj#7c!kt--vMs<4148>c*~$D6@qh z%>_GK*L5ROqh=mFeb~$}S&;I-HIiAz=++`l2TWGQ%~!D-#tw0wwMb)ruTO=a9+K4) z3<9oi?7B2O0e!x?IFB8&67#GB+$nMFz%4*~$~W*H$Zqbk>sXnk*A&GXBz{CH=C!W@&^_cAdr)+ktMI6iPls(BV>r*EXjvr%e? z-PH%U1wf!p-L_PbNTP~Wo}5R5RFx_ULlpFTCr`>ol4^bdaaQu2!@Dqe_SBH*ytA0v zbe|D@DI4kOOw2QiK2ep}STs17L!Kee=U^QSr)8AE?%^3$H;5Svm?%<1G6>q(A6p}Ff7ruo9&%8~gRDmAGx(uZ~TTvC%;TrbCyrhSeD_xsfy zYRw6X+ehv!?aJTp;6n#uF+oUz_$sM(rl;OKJ`=akge z7al7`0qKnFx(l9%`qNXq4Wf62y{#^%n);Ax*rQ5!a}sD*F^6@lxXk5qv$r79tS~|9 z1R~A7W`T6s0{dK-QQpG|>$6W+(tW0*tYA*=M)NHy)-5!H=S3+b* zcU|@+?j(VfPPL=Y-hh}xbW(z?ZvbX%xj0LeC!J@es<6*38mdS$T<33p8_u%~^Q8Z* z=lxD;Ac~r7h_zR>@o_7(? zm@x)7x>%4#Z3#6RV*`t@EhTbTUvM;{cbYL zPJ7SNr~zcjc9MlVytNizdS$0M&nhMc7dqWQcMi>CEKlXkWD=ec*-E^$5Ua?Syvst_ z1i_58E%H3BW`f_~U|uW)wjs?1XPG{##!m5OoVbd_356+~@7`TO6Ld13rGBYCHZoPV zaFpgY`@4^&shDJU=`&>kW8hU;5?H3m4Wc`;Ig<$W4Q(g-N2eE6WDK?+ve7IsIf($- zESs;;tWhaVZx{!P%5EmDlJ#D>cw0E<=G6zxLOc6ptP)pWUZ{^vE?}=t;Yy9}1?n6{E;jxHEs(YA!PxfhSj3KO15v&LD?=9Rf--kNR7vp2G>L)%)OF~e$R zxHsI?l3)_IWT26k>xNk5%%Y+dJeObX3yZItj)z@jZ_9;OQ{PfVH@ds4tDCJFT%DMA zP|LFJg6?D2=5KRXR3stFpvQojyY)`ZV&1Qs$*h){0?y==5IK@j6|$$?K?9TChOBXbLD;Ef+RfGGepyc8o0$-RqHuA z;{Udi&aGj$@yuGoy2dOBGy62C&}fb>#%Nzmu&b?aYR9l{)Qll_g`q@1n(1h6*22vw z7ZK^ij05Dau%{8Xo6W(XA~TCK*o@&9WjI`V58EzOzYGDREYldG#(;Ds{m-wV|2X>;}S2+S$MiqCdS; zdFWCRhKRWXKAnJ=>>gWNm)$#jbdmauU7v@C(lOXm6TRDb#-(&&&z}0CD2v6Iwc=8d zv4&bqOs;Fdt#t0rr-+PwdV6tBMi9+l6Zol8Y#@!hnXey8*OX(y38^Sa)4rei0&LvrC-q%ew9|wdoLe zrrS({IhaWAI>`CHyp=0Ac`cq_dhy3Q*4hT!aG}#26RV3YAd9dT&!Wt9kJkYjHvq*m z33h;{_smifGdW)hNysw?=L^m<{g1m~IDu{OY_wUmJ6kk6`ur(Es7o`0(NHKE&$y~u zqF1ICW+$;rcKOJa{$j^G_WfWN>azkH8&hKbzFK5D+S!E)6N~~yqd->orK=~G?kY!` z-O!L{2(sAOmAq-g>Mlb9ODel_DA7QfR3{@k2chlzc|5OIjT1h5^X>P19aiGrUe!@e zbarVuXC|pW8xMLtds{4RJgCl#a))TZOrBL&73^&_kK_n(mQAY6wl;``!PGV#7!J!LVjDBKyFAG6)snIOt#uyVUh_=RfEuLR` zX$KaPc(8`Wo!+vNNc+^n(qeL+AkyrW>ud@=jlIp(GkF$umMvj9FD#yC z_EM>uFo$N8kr;V~I)mqxr$=H%;RN~&o-tUzuC}rL;sgZX26)Ew1K(BIJ>jm^XF^?5 z*MM2?XwyJjJ*Ni3k1oop;vKaiKf9+NefGC__r1gY4KVu&@lYbnd{qu2pV%*yq;oSn z+TC19^ckV;of``?SYx=s7DntV5nk&t>)Kq!P4euH)mI(RBie)3_I8}ZOg(m9GU5z* zR;^Uxhl{0+v@_~F#oTs=3{6inw$1VR&NfxR8L3D_KArx^1UNf1!_)@OxaS`(k0shU zmSebiIiWeXcMO-#gDUy%|%1L>aLK4Hpj{TZu7rJnC=4Qnf zd8Xs+)_1~XW?)SgYQhjYXDJD0hMA#Z)8E6uFm6=!Jb6Y%rcD1!#9$<+Av8wBeX5vGkJF8nW1e)!;G=*&@46E zI<~Fk`O{BtS6sW;-JXnRgjwqwPYh29&m&htbuNCsAMJXcaXHmBcd(fab?h9=g-N@JG(V3-<^GpldF}D%ec7T?6n8F#dOk0~QgJ$3?JHv7aAlzqK z+ZfR+Gd($KTdA|qY~!qaXN_?j%^c-E(cfLPx_(>iZR~u5w$SKc_4M4-IgBzW@Ev07 zgaOYNPz8$YRS7U=wEKjtZkgA;2Z^l~Ob)zWwgSTnel({Np=6Am8nB*n=ea5T@^VE^ zC1w-NVqXn4nTVFRNVS?M_h{H1Hvj-Y07*naRPt*-{`HT3@%Q|12mgeiU;p|SJAIx0#V^kMf=Aj%`8-+K1|$PFzptUti`j% zH_=v06_F^O=`+g`*V(AE8e@R$^RtxaZz`T?PI-COu`nIj#vR=vnzpt?!x%ALzJ826 z$1iMqlb=tX>+4I7jZNlGjt}*B*ZU5!b;5vWZ4=h!7lu2qs}fUJ#G0No=5-@BSyR&x zM$3H}IyIQS%HiA?-rebdL8Y;;2JhsuwT6jyi#~IJ^Wm3XeEFvz{QC5-cKAAt^^4QL zID_8FbSZS`+U)#%DbZ8*@fLYzl1j=u1!j2=*I7*^;gq2x*y{P&XStaqD@h>F3TK2x zD9`+zYUkPHsVtavAdI(ouG`DkFZUG1)69V-1x-!d_@=(0v;Qi29*lLQ(|z8(`R91o zu7*Yt`YCC2Gk+zu-JR`OEf8sFbaBtIk;%Dbsq9Aj3R|3FTFn&0DUfKC6mfHJ%mhUS zi-tEXZ~D%ahhKb+IG_E@4(It-zar76&zxaQEdHbOZP(`(Sif!a%<3+!xe#La z$Wmo}sJ|y)l&!7H!EiqDo8|JXa>$SwiCF{5N0jS zf-^=Af#>$-RX}qaE12B8f{Ob`Z_G}PU+%67c=qFhS^pfU-o}RT-D)R0h%|`iMmd;b zQXjc+;X-{@Y;dvyh25M>R5pDGo25+A`+`HwLHtX%@OxjW5C@yP6k-knZ0?s{0nTSX zlQHY`nUCH~6VIJfn5sXG0iTvWTRgjwE>8f=ke!%knM5KaAHoJG7-9fVi3@uUBBH5s z&*T|6f0qLQSs6xo*4k_h<+Zgrj%Mof(%L*@(=PCQeRguJzr7*gd3_fBcn2aqh(k8y zL!*yl0zWibN=%|1SuIRo)uSs5n`+FjNM$$4guQ+t(g=>d z^x|u9o->E%_fMaZN^UT4TSeQYRpzglmiJc~H9 zS{pmgsRf$hJhQP1i@&uvo>i*K;@LK~L35nz>X%yV+<@6--35+?V!g*Fkece52 z)=Hfdlm)zIG0-&MyUV5VSva*p+Wn7nso-~=A!kdQ zj=Py<={zMWa9Gv=o@*l|;tAY7KZk88V9w|E>b$jVyWrz zo|zd5ScsG{stC0EP_e`5p27}IhPJ&_RgX8 zB}%*7bd$n$UEY-Gkn_n+k0v01Hb2^dVt%Tp=9AT}e4=G|d-PSgB6}P~sUAC}Nd{&&_&vk{8s}bq<2A~x!_Ab!_CW_Tx zn5KBpbU2Hb(ucl2V75E0Sz`7fnWi#&w<_`K)t83 zDX6tmjT?;R3{aP6z8D5=IspV8GBr z$5@asY3M7_AvPl3d0dxfY!?WMV@U|2o!yqg02}>Y ziy|I0R8+(R8SM~+UWl+Y_7`(iT7stk|&J zv*yX}dQNQ@Z(|p4ZX?THY9%OCI8a%;WG_iBo3NgT5V|+lktIH#EthO`1XLqsJ2ZtU z2HRgD^70T8?6f??Hf( zowj3(br0DwbUY)fEU+>1qqMqCrL>Xl`4}ayW+mFY?@bgY9-`}VcoE5Y`Wpq!e* zEBz?8LWkWgh7=?9>V;ZL6=S~pOR1aQGF9*QJh{*eiNP1bQB;gmA%w)fWbCN?Wgu+7 za}e;OhH}J%T;D4(S?G0-=eqBzm_|A=e3g$mOBo8`4q`BJ*Cr!UVi^h#5oL(`2qo+3 z=<=t8I`JuyoK}IVDN{q57wWm8=v*B+T)vQH2|VDn^RWFT`Lm&;k|0V?*}M;_YMt@C zj&|MOv!bFcPftipGKB?f4XpaqAQGT@iN`e^Q3tz?^#nF$I0v84s_!zN2oU$KXIkLT1vKYsr5f~zNZ ztF)8}$^ARORB|HvcGq$7@JvO z>Skgfj|*;z*XFA#jwz4e6dbcSw<%vLu{ZAPbAlE1B`UZFz(Fm!n z#Un+bYOG0lQ_C;{wa=J9;3SQ>kl5+i&i!#~8?62iaJCa>A9|KsjGe}(f=Ya3;n?rP zi~}`ujg(s({ru-wLp&6t)zbTyqlPn7p*PUg6ezl|l3%>CgciEyOzC^J%H0ntK_sgP zc<<>7YFeZ1ftW%n>7o;jnvKT5WKG?snK+`cDXs9H|Ne+&%~C%5 zPK)S^;3n1S$vrH%Mrug+$FP*rc2^DkddpKA#+2k#=+^>e$l1)CrXj2=HD5ncns9^{ z?nNdS>{9;2Bcnbti<4e7yaI=XR?(wqYz=KWm6nJiU_Y=Q<}1Hq)}fmlaMxxNE~@?g z;Y!Sa2WsPzrWKNM6TczebapT$xF*YFApuuFck%;d3 zLF=s^TUZV8@Ws`N%@CP*>!hHY%mI<-twzx-83}+JUDOS;8GC?O6&;+55=t|>)IhYi zBra2Vg(A3R*SLy+DTQNGH30ZHe#qZI7BSUs(H}0IA+l-R-kVsatr=in{wbB<=w=6h zhXT&N530Tgea@ODLE2yX&pTh3RJ)9*@1{&APtVN0jXQmWWaXwGEV|$Lz?jiLBt}YT zDSC6bn-Zhj-+ZJxavLuH*51p50^yc5nXDQuC}Ttp>Z!2I_6Y8&QTtx|F1cWst+b>{ z?+X*n{g*_NO5Jsi5Ed1qWCsUWmqt?TD+t@rkvq$T6_t%Wg&#_Lqh3)$Z-#G*7!%Jk zs30)lC+D9)iqjwN-pwA==01)aJLTCMea=2R3}Ks@*k8Ke8;;@Uwf8I@2dTDJyn(83 zK`bzUB5hfvZ{OD-_EFzqvK@yG8*hR(4|175*~)*m9(tBKi{MPm+(=DP7f0y1n>~kE zS&K`OC#q#=E!4PVI+YDYpUDxX8NiAk*FI{f*7C@}MszpJ$OK9*IZ-$IXQtBH`z1`z zOhmkHt$ZK^&~3fh_yAHlpv;oGN%)ySKkxIivDC|%Ht17yee_|?dJ#*916S3io(+r| z>Dre>Jj^ZkT7-Ets$|y2icx~|#hTffxLbxLJCXNIrlv#DJ5mKLh0PRlFeI{rg(bfW z`I5HVtTxFi)WRsc>#Eqx(Khmo*qC=JDds?~#=WC$(Sv4TL_L^vSg7bJ(*iYQWY{>m zbjwLf47_=QG}(LNdq>2*4`6I={>yz5azz#-Z1tP9eD5f;A3#~DI=i?@Wnmw=P{K%` z{#vE4henX`Qpl^vs(dM1Q0lta9=X3d)CIt$-defLx1Gy{2kYH|!Z0CXWoZ4f#FCy~ zGVLx0{>u+Ik>Y+FP{IfcD!%g&EU0ksOiiok*C;om|A5hd$kdn_Z_}0@pcc#>1|=fh z%?})`K-Sh@e$`kXj+~4ZOn7;ugCZ(j^BLT&%xJ3gghjy2bliW?uo?S(aZG`Fk^tBt z_qO5YMC{h(y9Rsl*lGuHM1an%YB((7TXt8i`Pu9S23b&^SI7#rvT&l3_2xS}t-=O6 ziRaMWU{%WtSCPMuZM_M^NbacR1=o$Y@y-%8&ph|8gzBeo%{CYVZ>fq9Pd?Po*PUdNx&p>Z( zV%Fs6M=w>lhp;HGao`;VpWtF_wFwmR5=&+_HtgWCK7(L=z zctxbx0|sln1=ls6oBOlldT(2{?NaXK z2M|7Ai;>DNxUFI-`~&~e@?&vb&p8nu4T#QinH*3?xsi zHxF`3`o%Nc5Es1Nq$@Nv%^;@=T&^B+aF>UUav>+4H~Ih@UoS(vQ|1RU&-Uk>Mj?wO zKyap5VnS0IhKdhKOkkn@*RRR{g0|PLhv)GL?bVPmrYaFJUl)WD8tY;uO`ltxfQtIc z&k9#bZa60FP?4}TT;JcWt5f$wE92D@ z^wmfXLJLPL+j>b_Ge+P?~ z-GsbRz_lbbL$z=u=pzA##20e=&Wl8!gi%nnB`j*+T|;}S?ok{!p$6g4F_t*zHz zfs^iM5i>DlQ`RCy|KaOq)`#fN8mQ7O2fbZuB@v&ra=P`tnlTNvrO>@6fG?4CGL&da zT|Dv9js(VU2(;NdOBPQc7iZ+~I_;PA2;J!Wd^n#}E&C*em<=j|r*XeT&QXX{GOvmqcRlrD3JbrILKD{Dxx; zJI!vErp}-3&0Y1C@!tC4C1NJ)K$L<$2!=HtACUOE)Q`bAV>qk0s<+2`*v zg{S*JDWux^-8C*vw!nI4wSkQyumS=C9uv~1B|{EP58>#6Pw@U&$)Uf*A(b;%#UDRD zuI3u2wo>zH5}q1=dPuK2e0v3Sjew((Mh*^y*oqgw+9OSnboOCi@1a0_g+)oZ(z|~Qe?O-jZmpszcU_ZBB z@RL9CUC`2`bf@>`&L3tyJ>%32=~7NQ7P^RqvQ&_@$5i;;;#1FTaMHn319l=W4=I!~ zLpa(;2>lz9@2n$sy@Dwqb|h6e#YWp^rN_bYnB9ffU>FAdWG*oHAE=px!C=lMT|rPh z!zi`&Qu*x_MC*OWBU0{uvB8^1e_#wmWOj2!!#z_fZ36O?B&pjzc9!+#w&gncC#BtB zYO^-opb~hXq*DZJ@A72@0Ocf7@I9>t9jsalt|yzaUF_wDE7Iw&y`YI+x=oy!P5;0o!uPr=T1-`oRxUk5 zjI;!C6DJSlfPc);NkoC<_&hwVLiB;(VbhDeL=+z^nze0`4HN!%B8hEMtRXCc2gQ@u z+yQs}Tzqimxxu`NQS_gd?30vsM{c<~(f#MruwDWUxrulE->|zL1J?CDjr+du4opbq z^d51R89FkyC7VzTTcR)M=u3;qQ!^goYU@m5Y&qHokIr`*wi16(c&z*>?jxn-F8 zc8YW+D^uuatyi==;X>ZUd(xS;vW8#$FBWP&9jN5qVcqN^N&K#6BNM*7eq>2$JZ4H} zQ!@1g;n>fPHElS_{cs$}<)lyb(^Rv-*X7rks(_cBRiuNf{H?m(P(2B3HjeYPj&-o6Z{+JdQVO&fci z=5sT-k&#ro%Cf#0-7;eF6~k|)M9j`=uPUW$;e4)??k z{z3mK%31|oB*AbFe{jb-5n*1g@B0}Hw`l=!2L(d!{peb=@}jA38=5je@gPQS(K{`K z3hz}%S6cuavB*9Io(ro{Ck@>6Y~D5$qK08?P3?l;qPZ|&tFK-g zQg143(je8Gu+qAb82oVtfFZnG5(!i%{j=w-Qt{=1IxomKXJ%CjKX%t1P^YDx)r@d! z23gTA64)*mf3-o6=lUj(r1^vcYeU5>DmbfgGEl3P|M$s;M5{Y3E37Y|xmm(n(>-s& z1ShH{ek%+zRo?_Zi+u;-hwGhr&?87m#>2VZgiqJOizodOgQ~SIALp31A-NFPVo*@q zZn%9s=xxfe{UzeN&|qMv00#C`>zt-fk)MT+2rN)v2wd#9cm6|I6{uNvDH=PN$VcA?3X*IDi-=C))Yz_%nauM$d3I;bt;LG||>J1FsR;n4U7vJY^IY}acq zcBV``k}h>B<96a*w+h4zua)um~F|l2&AdFa8j@ z_0J#FsmismLVPm!&}*f1-Fb$Cs^nD(-3R%q;zpflIsuwGo3^@f(QaFlYN+ zurhu+$guN&%68L{bQOr_4pne8E+uTn_hrL)+cuNU~WLOE5i1E+(XS>KPB0oxS`FcRCg6B#(V)>ItqL?Ql61Z-G-s=P&*;AG8eO%e;N5K$?0~`BA^!#(hVDr70sAlp^-) z-F$@gx2o~wD4p-=n%*;8J@RDZY>3$@k0z^{58FtbhB>`@`1fju8&KMRvZe&cSN3It z4=M`B3TN_lEO69`j)t1LHxQS_wC{Pg-&- zM_wpABzE64@33uOFLF~3e_XXn?C25=Q74ReI$v`3dJVArR7C*7F^ zF5~QGWrukd>dtUSIi#GhGiVNpD4P6wbk*pb{tV9|;rE6Z7gX6MxuS*#iVvHa$F+_I zNT9k0E<-NsQ~vh9u7gu=*l06Bk#-3baNh0R0ez#*zlO5^i=SR5Ye=Y}fmo^2IW$WE zOLHN_0I_>R@g}}tv-Kl~q=d)t;5j4i!_S4=kRi?yGY6M+9&x$>Zi=}s8sQ%V>Q=17 zA2Tv&aKD~pXi2Bl)r?u;2WfF@ud1f;G#g90akE{+OWMI)9+`H;oi|?0qJMM{?R-i5-|OODjA1rz;s~gj>#QKFFqdnckR#X_VU{nB9uX z)W*Vqwa`9w_$gMr@7ZxNzttj*`s;ncTKP-KIZG|5oUIMoZY~ak(f|uvn;>GE?*w1~Ii)%9#(I?GvO(Wm5Kt7F<&!e!9xs ziYB^?NpfSK90#Mpb!76&)UlM%$t?aidh(=*P|$alQGkM5%>Wl7)hZfLB?^-C%qQp1 z64H%*L1*s6a~#ZxLu2}v`3*47Pay(GegYOtZ%Z+>yHh7TH)I( zex75&q(cY|=ljh)(jVm>i+blU(j+1!E&X1`LTcixMqNe^<9rjS`h04WehB1Oguy^Ck7N~z%A z?Cd=+hGb-dbhbpo3RBZ$kb9p<^KCq~JRooaPdV|&V;%EKf1~XzOd-yu%dY-TyRzhTnr69uY6fWqEDH8c zplZ*m+LDyt0!fMg%o?FgS5A}Dmc2mW?}(}gdvMNC)4_65vO~%pgOFY&PB9(M)(5+0 zp{2y5#dH;khun^6vt2qH-Nd*(f?L!NPy}p8oXgJOkH`79ygBaI{3AdkUq?5u!+{OO zTis@fkxfc#4C;U|6ofzdU98p|)ofh{k(k!w75KM&jg^@u6nT$BCcaRuLV{HbNoSa` z_rhUbi*2AsKCTQS{J+S>*Sh6;i#`a^ch+;KeI=(k5o>66H(j^nxV7awR+ zo8{BKs0y!e?+p`LI;WOJKY5#Qef7oL+oD3((*?tqO&UANPgl9}9>zrE)aOXp=`KVy zkZR)l&oobEY(7tmZJS+uJm7N>#!coO=$;9$oJ={ZIQsQgP+(wS%qqP&PK0o49a&jM zJnTbA745N-Qm{{eLR5IQgxu!S$Fd=6vWF81W!pZIL`M@D)ysdc_%hNG^-A~cOKau2 z*-BU-zgLg=?Ch*~E>b!CHM}zA6XVjm#j+PpLVfk=@DLjBB$=)CA1P zZ9LulrT&{R4)gUXJ*y|IJcyat}e=4PXS9yaQ|z4z~t!yJX+ zHn2cT&9(b3QDh3s+m*A>05-(TqcBrfo5q=$+2e`+50S&l@w3rRN1(PNPX%Yz?<3CH zriuK?evI%!#Lgs2>kgJ^r8_&O>>k#>Tc6Tz*Ws!v{EC;Cw|#rLMU#g|Ygo(NF0yEM zL)1Rt38=CC;vbDiQ|s+u;zTROB(S2MZ7X3jJ2iHQouC!U__=0E&G~B znk(JCIMt+2u-p3$;+Uad1nui8(7PNevAh>A*oaU7(%}jZ+qV&!wM@(zkvj79IoaCc zhlq=b&&p_BMn^~EXeDFilW7z^GVcJ3NAKs5T8rB6XZqI*^tL`1J;iQh z5%zh5Clvti_xBm?r;Kb$6?r3^vRW}}=$BXGaFV9SLr{flw0KrU0B*blUD7yY2ieF} zRn}<98q^O3C&${)%uo>a379Yj@t>RF^_{6Hl0A0h{PR_q=sPD}W z-tAZW_IAT2t)X2lssCOC*sVgDyAJ_C;VXVXTD`c-xPm=XQKu32BXD7rxf0jDBiw`P+Xi)7aRgd=f(zP`XR#k0%+ZGLxEL<8c|re`S5R{1t0k-D8WFR)MYc>S=Lc&5xzX`1aF7`ebf8F z?VeYZaQ>NFB!XYiL{2nbTUsrlCD+xpi{5DW0gqp-N#^0 zXt2II|EO+H%U_1bf?@A~MoAB_j$7}W&aV}PjOu~*%SE(I1%tM7AUHjN=wR&62zSrm z)Z=YiyPt?6XI#Xk^6;>hAS|3Kv4dK==(pL#x0N^TbUhv*9R&kLtbIHVZ}|4WKT}Wi zWpj~n6#X)G+py7ayXxzn+c8DaM~n(@+POlC)s)Q=N0OM~j|44DZ(4>ZJ{g}IrhF4w z;**j8=`c(L3sCyLHqCP}XzgG8oOy7xW;3)wf=cpooy7m5lReY0&e@E_E3`g?9oQL! z+w4#&*xjYy`~oiNyF!zXitQAG!XXjsi zeH}GFzRy8sTlF1738@|foV5j~r`jQoLwweyh3~R!f?#XhK^QE9mxVUpN2S@>$XQs? z@%E0etsR>5pVAT)?V9o|znuR7k|f%$5e|b1>fc_lw`!0bA>Ufolmt>m>rxYK;e5Yl zO4la-#yS4;gV9pAg{5+l&cX*e&;~DfgqM!7%!2+Q(e4@Bgty^?&U?BY#N969@FGk~ zI*nNnqfM3Ba5tY>fi%2jt5(PaN7V>^IHuAUXFiZ3VZdmn;&EnLBoYYiU7YS zk8am8U;Hd#nu`LZJ(F3dk=7|}tBSA;)XO4Rp=f(3raoOasL0#5`FN>!#+&*u0S&JI zpsJ#(Wx|!Tzp=PBdN##YQBFJ5Nz}w0t2qU1~ z*|BAg)l|fI4-)@5o1rmc{qaEu&Eb3eHO`_3SBx9G*_qBS0we60Q;jx*>tFk0v(>Sr zxl-0|G{~2?4P|h+1=bSyDzGUE40Tk8rt$N+E>g*18)cd@oaIGUKudS0ZbHRUr!&97 zii0l^cfu~AdCJM%E>;`g=&;B$g-@ zRn=BCq@!baPFpv&g!g{`ajoE3OK4g#2X0SQWqYMSRqMmVo$Z&yhiPwy2i_+pdyqm` zWa9rmx>!zQ(lhF8iL7+*kj~VoQ_XU|j?QxSCKO!zfZ=CEBJLs9Ue;b-UZ2;uXJ#%g zE_5#^M`L40kB=*k?7~XH;F*4oAChT`VIc4;E%KC_+ZZ0yJ2sgy%l9MCDEgZyK0i1)$TjRq%tBRq9;$DI6DW> zlLQ_W<>uz<1fF64@}Li1swdUDb`@UruwUyNWuz_YnX;zwJ!ew#OdaP zdt-yK8#*?P=gA|edWTN{xD0=1>gv5OpQ1>BJuXn@f@V7~=s3&q0 zx>W0P=EX{O>K0U14E=H!gbl^IQ+XN6qz}lFDc~KjEdW2I_uNGMvW>Tk%WF1Ip$TT0 zJRmAK>7y;5{{OjZn)VM~xbnYZJz5FHCb=3y+)+F|e;{ujUixdbH12y2>7@PqRG@Fd z==9HCSKEskXGcAq=r2l?pFdh{Jz~-Atx1;X$q_A~^0l}@>Gc>T0?|!YlAkWOpzBkO47yBbN_g>8pZX zcgj^T;<+Bvv~V>$u*F-#fxMy-oB4^pi0`;>X+&QH6j5nXRM_18{u}*asPpKPKv4^Sc0lToQ3}T(X*VuRKuZ?GYX#C@ zO4d;OIwC6fjf34!Bg7QHl)YtLylV;{3mWgUb!BOP7G!A{JT;z&_FS~Dk`WG3&xLjOC8>j$r(6aMCr0}V z0=mskK3uN>=5$j2sme3<=BtY~ZxY`V#(Ss#hPbo1mmy)4F7%4AhF#Td{16gKyuytk z&j%j!-a0m^-wMhsxizxBfaiah)NL=XdZk}flBUjLMCiJWRu?E4bA!6M=mA5Ik zE@>H&ga3>=aYk@XP~T-3gH>|{pLSgMz{m*@3`&M8t?y#6tdcW8zXTn{oe{DThA+c?-8wS zZO!>|0m&xaM=2%AM?nD;oeRX#)Pr~(!=&cfabNIT?KL+aweq|=Bt=p0S3E7mPQEJ> zD5-YXlk525J&>5A(qi;zK}nk;)?9r)}c- zh$(rBpN=J~~ZuF3rUoV5;>)#^L;*Q#Z(Uoco*&=EUz{pxz7u4doH;5@#%#s}6M z94&qJ{OQISXJa}3)e&nnfR4|DAGc%~%<5$XJ;I{>9BzQt&FF?q@D&75-TioyjAZTJ zBl;W(8E!LZP*23%zRwW*t+$b_eVc5K=_Wip5w8}_ z2ZL5=iu>c_0bhrIeZllimTK6Z+IuCq8R=tiZunUNto^*h=~JSz)|oiRMtvMm)gF^m z@WkuPX9~(5`*{6QJ(CY*MKlrudLHKjpQIWCR3207Fq_F0!`(>@zVCl)rp@uFF7I#9P53 zl=w)dzu(fD$wdd%-R(W6H7}8R22E3s^bS)=*Hed!YQabf_zt=_0LA z^%5szDOmrhJ$4v_m0Lm>_0SN@_z-1&c%gN1qG@jJHrJwzOtWn7bVOu00QO1A#|%`@ zHj&1U;Wm3-{fhl|`1d*8f3tU%0Unfojl&|5s`4*8paMI0;GOOJ->m3ssxtw8?Fc?I zw2Ibw7z(NAQEW?`=a(5_s{80dvF#Ge+buDYSbC zP4fP~$=}h!rA{td+PqadZ}Fa0ggxo7z(Eu*k>?j=7-BM=zp82V^nZd+Q2GUz#Ny1K zB}5|hbmY`G87qb*UB@Chn-{e1FEbw^6r{d*VYZf=SVZdZvibTsFzcP#h_%r#o`hMS z7ru79OofSG(hdns*D&>vnbB%0F*yzdvjnf`noh55JSZ4h3jbOGoou_D$;ruRuj zZ>ESde?MO4g(aT%#}e-6k4Jpix)!B|*zSb?=r~;6VD5^_F_M|K=6>{pWLoH;sK^HB zeBb{f97{p+;xiu^a-9pQy5L`SjVSl+2|%&59%20I`EWlC@%=aV&(6|c zV_Hx=6TR;Wx|XC0t{s;@Y3cO@1<%Q3-m41rp?UG0KRjJ{l4EA`i$<9SGjBR(^Epz@Hcs(~*+AZA( z;Uh*og6f#4^=V7o>GJ0b4@)yMopqHezWp6&dcc7N)j|6#-;l)|5N{IYrJaZG>hvDF?XJS$j#l~fHgR={ds)No8j87)g&sP^B!EdgCY zxdOy(V=Zg&Az8vVK^(wc~&<7q|Rl60u#0%t+Ntxp^5{5V{;{Iwi^+ULdq zKhq_dM$1h#TcG2qQQDfQ1@V4gJJt*}^D^Yx%g;GU)5nowg4|zn8JUZS#X>O>RU+o( z05$zyhv5qq&oEW#9Zwz7)yQtjO;`Qn&!?pV7U9DiM>=3rcRe?ZJ>bh zWn4tNT*aJ%U~S-CC@L{ETy+XHQs#*b^e)ZBDCFn%?yzRy!4l3{jY(ChKXe$K9{b35 zJKL1`d=E4E=Eu&UrsihJ(3WQAWn>ZJA!059cDQ><{CSmZ$=c{%)?itb4Cks%=K37R zVAO$mEN1_auW{zP`^9I6_Jr@Sfl`!*O(npCK`-w)Dg9}wacwWSbf9EF$rdK{DjwBL zgFs(By}I}w05Zrtced-tY}xwdGkbxQl_P#e8>4&8hluCU(Ode%rf3nVr-x0Fxpa`; zL@zin=-Q_Z?J*X`Q~Z3`&3Ex}w1K}9Mu2>u)c7sR{d)aXymN1_1kex*u#ZMXky&qp z+t5PCQOXN|G*ookR;TxWhw)#p+~}Aq!!{TRdE@UHCPWI`$g*hiRAYubNDRSiGY2L= zq%T8wV|jbi1!5leBy`Dfy4j=XzfjCRd_!YpdaqaD0qRV~OmsCw4BEh1h`D~Jo6Pr6 zKpm6?=h3b2U?K$09hiSlKQnhUl6M5LLhtew6{I`t05QpfZ-Ly=7)~vBxobqhgDtc- zMoRbgUfidDYjkT)9KG5FU|PcQpa6xe6#pkzug7qlzM`#MB@{SsE^na$C@swV(*rPV zfA*iw`N2{LE^n4agRoPt=({@qwRQH_;=H)L`~TXuU+s!x`W>Ny0zPD6-HRG6vxz!w zZi6{7>ioIC7o0i^Aw}NR8TTUzngmD$-K#7A#k>U1y~UFm6QA41Lr+JQG8hv{eeYWP z*`a{%zY1PHN9MQ=)KhX~UC#*AZqXnW*m}cscRhG(;wJ+0Qo-bI zHjSD{KuoS6`&RR}K>y<61IS;9-Ty84qD#Z%ZdS+`-gp;- zNy1LDTY@Yi9~q%f%lufEA#?`ItnphB4$=s2E@&9yh%xa@Ug>v`}{i1cn|tJ&Q%d+ zN0uC?H=5?|(_1g+`24GaGku>$35<2BT5ZFU$5hgc z=ns~Z>c@GVw5Y^tmqO&6@^SfGyyy_lInj>#{H`of+~-Vm>Kh^@)33($Q~Y!%%J5yL zPGj0>`)?T%nzVRK|+weE9+D5ZC*%#ugQ zc~o`OqH#;BX}VQWNiZ?2{xV(O`Uo=k6L(W`urtn6;XqQDyxfITDzi23^%oB2x+CNw zh-NS4_-07Nq?) zj6bElm=?1PK;Q=(_kSZTbxtGj6>&V!-tr-qJm;&r7-F#2$e7 zvDo`$=F;-e<9X(sHz?ut5UTWkQ&I}h|NCf#ZF~Z%eN3}Xd{U3nI1%he1-@hrRdGTq z;aF#7=ncqcic7xF+#nE+|37xQ_e;|yGEdvA^}lb0P6Qe5-HuMg{6r5X^f zz1yK6wi2Ix{}oP+o!vOkRj~i>Eq1-02rYpjpM4)rEb)~nfae<6NiI`IK%qbOB`Yqb zf_U28IJnodC@KS{v=ocTpy3%@ELRE*9FFYPZ;|A?ZZ^Oi3S__3XMe};BrYsV+0B@q zv4%ml$)Pme2FLeuev(7K)EPir8#3yOzAT+?tx>iF%P*!O7-%GvJBO>9ao?$uIh~6> z%L;Pt(KBxS)<_Xz?51{HxVtGaj1AIPSUvLpO1k!V zDAP7Pq(y~DZR5C$aco&Mv<@>h<2X*qVOC=L9KJA=U4u0T&DSC#QaQvj4q+WKIY$k4 z^HHJqWkRjP%GyzOMWJJ%7GWtLM`DdNv#KZcg#%wm>T` zyC~v7u}f16%x%4Gd41j|W(^*BklV~qwk?U4(HuUp5HT@du2Vp<9<8f-$vCFK(h3Nf zOD&8Z_-jL0Y5iUxp1BEOa#JS9`=ERmc0|tcgF$GAof+N9&z@TE7VZ&4ko056%rE_| zYZmkzhg3PCJ?$+l%F5_@)FVO_Cpfy(mHntCNtw|~C5oK6XQnM!9a|3Xr^1nz9tX^j z@pr_5&c%PvrffR7c58HsJ2$E@u8+4H34#RXIQJxKb7tujdoAKdG!yicBB=IfC(d9ok_{TQT5jEFY&FVbx_FN?l>J znx1`dtD#M@Ejv0QFj*N6her;+#tEa3G5WX(nLc9$q3O^ru6^ti{q`03kQ!I=r05D{ zj(4msyV0~;LQfUE4qO^?y_cMM1&*nV^m6CQvq;#TeA0y)=mGX@b7p;Hq1@2udh?nQ zzW1Q)CSB(3Q?KUu#q{2WSiMC9+pcfy;bQh!;_THNP_bu(06eC8VZJ z-pQJia?2??oa_2NLG@{KY~6SHwRbZR80h|x;`#d3pA4gJL@V%DFHoD%^{UjomkIVI z4O$rJzg_=IOiqX8>E+X7{(|Kc@U|9?c#uPa20hOx$4dVo2B1b zJc2dPm0W`hhH7Y(a^9#vYNZ>&E8?BP6OkMXrl&8+GOyT>$0o}4s-Ld?9klQ@EV;w7 zlii3F|7UA$B z^E!_-Dbk#VTPKUSC>&_rOjwY(knch4M>zpR7T#=%a-Sx@R;&J&2?dA=c59C!8)3Ek zQZ$&@eqRs}r8s`Lf8LIYnx(#KO^nFeLj-Cy!LDz%;G!ZIp!3vK`}JZJxm&Fcyhl|x zs!=--qrh*=nCtqXjP}IJ^u8Q$1wh-h9ez}eKA{~*A`9J@LVK%4NwcvncnR&zFOJ?;#sJICZNS4A$M@^WdfU2A z2v#stm&ng9>|(kr@0Xzm_Dd|pRlzDfTe>Qt`&IkrZTRx&&5*X~TL#%ua!wXws_pvB zFZ+Hi@&yw-y7jGgDpwe&^?c7n4eEz5o>(M9>c#)zqAo+f?3#Kyn^ZcJt9K9VXAu5_ z)p`dlZ~cF2a?@u(vdQL{!%i2+3whh~3~*!_qoD08RQY~6@GwX{le|gHHUG>!X7ki;Fc8HMGQO0uyA^VS0vF+Mut1njX4urDdU2c4;wljj|^_)~U zR{PAYS*p!Cj138qH*4L1vQERRak$`By*??`Xg0^NdcLFh++Gv^*}LRpsN7suhEJ5=ug?m7wlqRD&8A?ZhH w#jxghxk8}9+6ahYWXK$xw+{J7E2c{K6kN^Mx literal 0 HcmV?d00001 diff --git a/gsplus/lib/make_mac_icon b/gsplus/lib/make_mac_icon new file mode 100755 index 0000000..de47fd7 --- /dev/null +++ b/gsplus/lib/make_mac_icon @@ -0,0 +1,57 @@ +#!/usr/bin/perl -w + +# Based on https://gist.github.com/ansarizafar/6fa64f44aa933794c4d6638eec32b9aa +# and https://github.com/retifrav/generate-iconset +# We need to create a directory of the icon in several scaled sizes, +# (the Mac command "sips" can do this), then run iconutil to form the +# .icns file. +# kegsicon.png created by Alex Lee + +my $icondir; +my $img_file = ""; +my $ext = ".png"; +my $scale; +my $sz; +my $pixels; +my $scale_str; + +if($#ARGV == 0) { + $img_file = shift; + if($img_file =~ /^.*\.(^\.*)$/) { + $ext = $1; + print "Set ext to $ext\n"; + } +} else { + die "Usage: $0 image_file.jpg/.png" +} + +$icondir = "./icon.iconset"; # Must have .iconset extension +if(-d $icondir) { + `rm -rf $icondir`; +} + +`mkdir $icondir`; +for($scale = 1; $scale <= 2; $scale++) { + for($sz = 16; $sz <= 512; $sz = $sz * 2) { + if($sz == 64) { + next; + } + $pixels = $sz * $scale; + $scale_str = ""; + if($scale == 2) { + $scale_str = '@2x'; + } + @cmd = ("sips", "-z", $pixels, $pixels, $img_file, + "--matchTo", + "/System/Library/ColorSync/Profiles/sRGB\\ Profile.icc", + "--out", $icondir . "/" . "icon_" . $sz . "x" . $sz . + $scale_str . $ext); + print "cmd: @cmd\n"; + `@cmd`; + } +} + +print "Calling: iconutil -o kegs.icns -c icns $icondir"; +`iconutil -o kegs.icns -c icns $icondir`; +`rm -rf $icondir`; + diff --git a/gsplus/src/AppDelegate.swift b/gsplus/src/AppDelegate.swift new file mode 100644 index 0000000..6cecdf0 --- /dev/null +++ b/gsplus/src/AppDelegate.swift @@ -0,0 +1,365 @@ +// $KmKId: AppDelegate.swift,v 1.32 2024-09-15 13:55:35+00 kentd Exp $ + +// Copyright 2019-2024 by Kent Dickey +// This code is covered by the GNU GPL v3 +// See the file COPYING.txt or https://www.gnu.org/licenses/ +// +// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/ +// Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreAudio.framework/ +// Versions/A/Headers + +import Cocoa + +let Context_draw = false + // Default: use safe draw function. + // If true, use NSGraphicsContext.current.data to try to write + // directly to screen memory (in a different ARGB format) + +class Window_info { + var x_win : NSWindow? = nil + var mac_view : MainView? = nil + var kimage_ptr : UnsafeMutablePointer! = nil + var title : String = "" + var app_delegate : AppDelegate! = nil + var mac_a2_height : Int = 0 + +// init(_ new_is_main: Bool) { +// is_main = new_is_main +// } + + func set_kimage(_ kimage_ptr : UnsafeMutablePointer!, + title: String, delegate: AppDelegate!) { + self.kimage_ptr = kimage_ptr + self.title = title + self.app_delegate = delegate + } + + func create_window() { + let x_xpos = Int(video_get_x_xpos(kimage_ptr)) + let x_ypos = Int(video_get_x_ypos(kimage_ptr)) + let width = Int(video_get_x_width(kimage_ptr)) + let height = Int(video_get_x_height(kimage_ptr)) + let windowRect = NSRect(x: x_xpos, y: x_ypos, width: width, + height: height) + var window : NSWindow! + var view : MainView! + let style : NSWindow.StyleMask = [.titled, .closable, + .resizable] + + window = NSWindow(contentRect: windowRect, + styleMask: style, + backing: .buffered, defer: false) + + let viewRect = NSRect(x: 0, y: 0, width: windowRect.size.width, + height: windowRect.size.height) + print("About to init MainView"); + view = MainView(frame: viewRect) + print("About to set kimage_ptr"); + view.kimage_ptr = kimage_ptr; + print("About to call initialize"); + view.initialize() + view.closed = false + + window.delegate = app_delegate + window.contentView = view + window.makeKeyAndOrderFront(app_delegate) + window.acceptsMouseMovedEvents = true + window.title = title + window.showsToolbarButton = true + window.contentAspectRatio = NSSize(width: width, height: height) + + video_set_active(kimage_ptr, Int32(1)) + video_update_scale(kimage_ptr, Int32(width), Int32(height), + Int32(1)) + + x_win = window + mac_view = view + mac_a2_height = height; + window.makeKey() + } + + func update() { + // Decide if window should be opened/closed (if it's the + // debugger window), and call mac_update_display() to update + let new_height = Int(video_get_a2_height(kimage_ptr)) + let a2_active = video_get_active(kimage_ptr) + if let view = mac_view { + if(new_height != mac_a2_height) { + mac_resize_window() + } + if(a2_active == 0 && !view.closed) { + print("a2_active 0 on \(title), calling close") + x_win!.orderOut(x_win) + view.closed = true + } else if(a2_active != 0 && view.closed) { + print("Opening closed window \(title)") + view.closed = false + x_win!.orderFront(x_win) + x_win!.makeKey() // Move to front + } else if(a2_active != 0) { + view.mac_update_display() + } + if((a2_active != 0) && !view.closed) { + if(adb_get_copy_requested() != 0) { + view.do_copy_text(view); + } + } + } else { + if(a2_active != 0) { + print("Opening window \(title)") + create_window() + } + } + } + func mac_resize_window() { + let a2_height = Int(video_get_a2_height(kimage_ptr)) + let a2_width = Int(video_get_a2_width(kimage_ptr)) + let ratio = CGFloat(a2_height) / CGFloat(a2_width) + + let cur_width = x_win!.frame.size.width + let new_height = cur_width * ratio // CGFloat + var newframe = x_win!.frame // NSRect + mac_a2_height = a2_height + newframe.size.height = new_height + x_win!.contentAspectRatio = NSSize(width: a2_width, + height: a2_height) + x_win!.setFrame(newframe, display: true, animate: true) + mac_view!.initialize() + // Must call initialize for the case where the + // status lines were enabled, we need more lines + // print("Call video_update_scale from mac_resize_window\n"); + video_update_scale(kimage_ptr, Int32(x_win!.frame.width), + Int32(x_win!.frame.height), 0) + video_update_xpos_ypos(kimage_ptr, Int32(x_win!.frame.origin.x), + Int32(x_win!.frame.origin.y)) + //print("Did mac_resize window to \(a2_width), \(a2_height)" + + // " frame:\(x_win!.frame.width), " + + // "\(x_win!.frame.height)" + + // " ratio:\(ratio)\n") + } + + func update_window_size(width: Int, height: Int) { + // print("Call video_update_scale from update_window_size\n"); + video_update_scale(kimage_ptr, Int32(width), Int32(height), 0); + } +} + +@main +class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { + + static func main() { + let delegate = AppDelegate() + NSApplication.shared.delegate = delegate + _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) + } + + var mainwin_info = Window_info(); + var debugwin_info = Window_info(); + + func find_win_info(_ window: NSWindow) -> Window_info { + if(mainwin_info.x_win == window) { + return mainwin_info + } + return debugwin_info + } + @objc func do_about(_:AnyObject) { + print("About") + if let ver_str = Bundle.main.infoDictionary?[ + "CFBundleShortVersionString"] as? String { + NSApplication.shared.orderFrontStandardAboutPanel( + options: [.applicationVersion: ver_str, + .version: "", .applicationName: "GSplus"]) + } + } + func applicationDidFinishLaunching(_ aNotification: Notification) { + // This is your first real entry point into the app + print("start!") + set_menu_for_kegs() + NSApp.activate(ignoringOtherApps: true) // Bring window to front + main_init() + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + + func applicationShouldTerminateAfterLastWindowClosed( + _ theApplication: NSApplication) -> Bool { + // Application will close if main window is closed + return true + } + func windowDidBecomeKey(_ notification: Notification) { + if let w = notification.object as? NSWindow { + if(w == mainwin_info.x_win) { + adb_mainwin_focus(Int32(1)); + //print("Main window became KEY") + } + } + //print("DidbecomeKey") + // If window focus is changing, turn off key repeat + adb_kbd_repeat_off() + } + func windowDidResignKey(_ notification: Notification) { + //print("DidResignKey") + adb_kbd_repeat_off() + adb_mainwin_focus(Int32(0)) + CGDisplayShowCursor(CGMainDisplayID()) + } + func windowDidMove(_ notification: Notification) { + //print("DidMove") + if let w = notification.object as? NSWindow { + let win_info = find_win_info(w) + video_update_xpos_ypos(win_info.kimage_ptr, + Int32(w.frame.origin.x), + Int32(w.frame.origin.y)) + } + } + + func windowWillResize(_ window: NSWindow, to frameSize: NSSize) + -> NSSize { + // print("WILL RESIZE app \(frameSize)") + let width = Int(frameSize.width) + let height = Int(frameSize.height) + let win_info = find_win_info(window) + win_info.update_window_size(width: width, height: height) + return frameSize + } + func windowShouldClose(_ window: NSWindow) -> Bool { + print("windowShouldClose") + let win_info = find_win_info(window) + if(mainwin_info.x_win == window) { + // User has clicked the close box on the main emulator + // window. Just exit the app + NSApp.terminate(window) + return true // Let main window close + } else { + video_set_active(win_info.kimage_ptr, Int32(0)) + win_info.update() + return false + } + } + + func set_menu_for_kegs() { + let appname = "Kegs" + let menu = NSMenu(title: "MainMenu") + NSApp.mainMenu = menu + + print("Installing my menu now") + + // Add "Kegs" menu + let kegs = NSMenu(title: appname) + kegs.addItem(withTitle: "About \(appname)", + action: #selector(AppDelegate.do_about(_:)), + keyEquivalent: "") + kegs.addItem(NSMenuItem.separator()) + let quit_item = NSMenuItem(title: "Quit \(appname)", + action: #selector(NSApplication.terminate(_:)), + keyEquivalent: "q") + quit_item.keyEquivalentModifierMask = [.option, + .command ] + kegs.addItem(quit_item) + let kegs_item = NSMenuItem() + kegs_item.title = appname + kegs_item.submenu = kegs + menu.addItem(kegs_item) + + // Add "Edit" menu + let edit = NSMenu(title: "Edit") + edit.addItem(withTitle: "Copy Text Screen", + action: #selector(MainView.do_copy_text(_:)), + keyEquivalent: "") + edit.addItem(NSMenuItem.separator()) + edit.addItem(withTitle: "Paste", + action: #selector(MainView.do_paste(_:)), + keyEquivalent: "") + let edit_item = NSMenuItem() + edit_item.title = "Edit" + edit_item.submenu = edit + menu.addItem(edit_item) + + // Add "Config" menu + let config = NSMenu(title: "Config") + config.addItem(withTitle: "Configuration F4", + action: #selector(MainView.do_config(_:)), + keyEquivalent: "") + let config_item = NSMenuItem() + config_item.title = "Config" + config_item.submenu = config + menu.addItem(config_item) + + show_menu(menu, depth: 0) + } + + func show_menu(_ menu: NSMenu, depth: Int) { + if(depth >= -10) { + return // HACK: remove to see debug output! + } + print("menu at depth \(depth): \(menu.title)") + if(depth > 5) { // Prevent infinite recursion + return + } + for menuit in menu.items { + print("menuit: depth:\(depth) \(menuit.title)") + print(" keyeq:\(menuit.keyEquivalent)") + print(" modifiers:\(menuit.keyEquivalentModifierMask)") + print(" isSeparator:\(menuit.isSeparatorItem)") + if let sub = menuit.submenu { + show_menu(sub, depth: depth+1) + } + } + } + + var mainWindow : NSWindow! + var mainwin_view : MainView! + + func main_init() { + var kimage_ptr : UnsafeMutablePointer! + var rect = NSRect.zero + + let argc = CommandLine.argc + let argv = CommandLine.unsafeArgv + parse_argv(argc, argv, 3); + + rect.size.width = 2560 + rect.size.height = 1440 + if let screen = NSScreen.main { + rect = screen.frame + } + kegs_init(24, Int32(rect.size.width), Int32(rect.size.height), + Int32(1)) + if(Context_draw) { + video_set_blue_mask(UInt32(0x0000ff)) + video_set_green_mask(UInt32(0x00ff00)) + video_set_red_mask(UInt32(0xff0000)) + } else { + video_set_red_mask(UInt32(0x0000ff)) + video_set_green_mask(UInt32(0x00ff00)) + video_set_blue_mask(UInt32(0xff0000)) + } + video_set_palette() + kimage_ptr = video_get_kimage(Int32(0)) + mainwin_info.set_kimage(kimage_ptr, title: "GSplus", + delegate: self) + kimage_ptr = video_get_kimage(Int32(1)) + debugwin_info.set_kimage(kimage_ptr, title: "Debugger", + delegate: self) + + mainwin_info.create_window() + NSApp.activate(ignoringOtherApps: true) + main_run_loop() + } + + func main_run_loop() { + DispatchQueue.main.asyncAfter(deadline: .now() + + .milliseconds(1)) { + self.main_run_loop() + } + let ret = run_16ms() + if(ret != 0) { + exit(ret); + } + mainwin_info.update() + debugwin_info.update() + } +} + diff --git a/gsplus/src/Info.plist b/gsplus/src/Info.plist new file mode 100644 index 0000000..bc67b2b --- /dev/null +++ b/gsplus/src/Info.plist @@ -0,0 +1,52 @@ + + + + + BuildMachineOSBuild + 18G103 + CFBundleDevelopmentRegion + en + CFBundleExecutable + KEGSMAC + CFBundleIconFile + kegs.icns + CFBundleIdentifier + com.provalid.Kegs + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Kegs + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.38 + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11A1027 + DTPlatformVersion + GM + DTSDKBuild + 19A547 + DTSDKName + macosx10.15 + DTXcode + 1110 + DTXcodeBuild + 11A1027 + LSMinimumSystemVersion + 10.13 + NSHumanReadableCopyright + Copyright © 2025 Kent Dickey. All rights reserved. + NSHighResolutionCapable + + NSPrincipalClass + NSApplication + + diff --git a/gsplus/src/Kegs-Bridging-Header.h b/gsplus/src/Kegs-Bridging-Header.h new file mode 100644 index 0000000..7633497 --- /dev/null +++ b/gsplus/src/Kegs-Bridging-Header.h @@ -0,0 +1,5 @@ +// $KmKId: Kegs-Bridging-Header.h,v 1.1 2019-10-14 22:33:09+00 kentd Exp $ +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "defc.h" diff --git a/gsplus/src/MainView.swift b/gsplus/src/MainView.swift new file mode 100644 index 0000000..e134dec --- /dev/null +++ b/gsplus/src/MainView.swift @@ -0,0 +1,412 @@ +// $KmKId: MainView.swift,v 1.42 2024-09-15 13:55:35+00 kentd Exp $ + +// Copyright 2019-2024 by Kent Dickey +// This code is covered by the GNU GPL v3 +// See the file COPYING.txt or https://www.gnu.org/licenses/ +// + +import Cocoa +import CoreGraphics +import AudioToolbox + +class MainView: NSView { + + var bitmapContext : CGContext! + var bitmapData : UnsafeMutableRawPointer! + var rawData : UnsafeMutablePointer! + var current_flags : UInt = 0 + var mouse_moved : Bool = false + var mac_warp_pointer : Int32 = 0 + var mac_hide_pointer : Int32 = 0 + var kimage_ptr : UnsafeMutablePointer! + var closed : Bool = false + var pixels_per_line = 640 + var max_height = 600 + + let is_cmd = UInt(NSEvent.ModifierFlags.command.rawValue) + let is_control = UInt(NSEvent.ModifierFlags.control.rawValue) + let is_shift = NSEvent.ModifierFlags.shift.rawValue + let is_capslock = NSEvent.ModifierFlags.capsLock.rawValue + let is_option = NSEvent.ModifierFlags.option.rawValue + + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + func windowDidResize(_ notification: Notification) { + print("DID RESIZE") + } + override func performKeyEquivalent(with event: NSEvent) -> Bool { + let keycode = event.keyCode + let is_repeat = event.isARepeat + let unicode_key = get_unicode_key_from_event(event) + // print(".performKeyEquiv keycode: \(keycode), is_repeat: " + + // "\(is_repeat)") + if(((current_flags & is_cmd) == 0) || is_repeat) { + // If CMD isn't being held down, just ignore this + return false + } + // Otherwise, manually do down, then up, for this key + adb_physical_key_update(kimage_ptr, Int32(keycode), + UInt32(unicode_key), 0); + adb_physical_key_update(kimage_ptr, Int32(keycode), + UInt32(unicode_key), 1); + return true + } + + override var acceptsFirstResponder: Bool { + return true + } + + func get_unicode_key_from_event(_ event: NSEvent) -> UInt32 { + var unicode_key = UInt32(0); + if let str = event.charactersIgnoringModifiers { + //print(" keydown unmod str: \(str), " + + // "code:\(event.keyCode)") + let arr_chars = Array(str.unicodeScalars) + //print(" arr_chars: \(arr_chars)") + if(arr_chars.count == 1) { + unicode_key = UInt32(arr_chars[0]); + //print("key1:\(unicode_key)") + } + } + return unicode_key + } + override func keyDown(with event: NSEvent) { + let keycode = event.keyCode + let is_repeat = event.isARepeat; + // print(".keyDown code: \(keycode), repeat: \(is_repeat)") + //if let str = event.characters { + // print(" keydown str: \(str), code:\(keycode)") + //} + let unicode_key = get_unicode_key_from_event(event) + if(is_repeat) { + // If we do CMD-E, then we never get a down for the E, + // but we will get repeat events for that E. Let's + // ignore those + return + } + adb_physical_key_update(kimage_ptr, Int32(keycode), + UInt32(unicode_key), 0); + } + + override func keyUp(with event: NSEvent) { + let keycode = event.keyCode + // let is_repeat = event.isARepeat; + // print(".keyUp code: \(keycode), repeat: \(is_repeat)") + let unicode_key = get_unicode_key_from_event(event) + adb_physical_key_update(kimage_ptr, Int32(keycode), + UInt32(unicode_key), 1); + } + + override func flagsChanged(with event: NSEvent) { + let flags = event.modifierFlags.rawValue & + (is_cmd | is_control | is_shift | is_capslock | + is_option) + var c025_val = UInt32(0); + if((flags & is_shift) != 0) { + c025_val |= 1; + } + if((flags & is_control) != 0) { + c025_val |= 2; + } + if((flags & is_capslock) != 0) { + c025_val |= 4; + } + if((flags & is_option) != 0) { + c025_val |= 0x40; + } + if((flags & is_cmd) != 0) { + c025_val |= 0x80; + } + adb_update_c025_mask(kimage_ptr, c025_val, UInt32(0xc7)); + current_flags = flags + //print("flagsChanged: \(flags) and keycode: \(event.keyCode)") + } + override func acceptsFirstMouse(for event: NSEvent?) -> Bool { + // This is to let the first click when changing to this window + // through to the app, I probably don't want this. + return false + } + override func mouseMoved(with event: NSEvent) { + //let type = event.eventNumber + //print(" event type: \(type)") + mac_update_mouse(event: event, buttons_state:0, buttons_valid:0) + + } + override func mouseDragged(with event: NSEvent) { + mac_update_mouse(event: event, buttons_state: 0, + buttons_valid: 0) + } + override func otherMouseDown(with event: NSEvent) { + mac_update_mouse(event: event, buttons_state:2, buttons_valid:2) + } + override func otherMouseUp(with event: NSEvent) { + mac_update_mouse(event: event, buttons_state:0, buttons_valid:2) + } + + override func mouseEntered(with event: NSEvent) { + print("mouse entered") + } + override func rightMouseUp(with event: NSEvent) { + mac_update_mouse(event: event, buttons_state:0, buttons_valid:4) + } + override func rightMouseDown(with event: NSEvent) { + print("Right mouse down") + mac_update_mouse(event: event, buttons_state:4, buttons_valid:4) + } + override func mouseUp(with event: NSEvent) { + // print("Mouse up \(event.locationInWindow.x)," + + // "\(event.locationInWindow.y)") + mac_update_mouse(event: event, buttons_state:0, buttons_valid:1) + } + override func mouseDown(with event: NSEvent) { + //print("Mouse down \(event.locationInWindow.x)," + + // "\(event.locationInWindow.y)") + mac_update_mouse(event: event, buttons_state:1, buttons_valid:1) + } + + func mac_update_mouse(event: NSEvent, buttons_state: Int, + buttons_valid: Int) { + var warp = Int32(0) + var x_width = 0 + var y_height = 0 + var x = Int32(0) + var y = Int32(0) + var do_delta = Int(0) + let hide = adb_get_hide_warp_info(kimage_ptr, &warp) + if(warp != mac_warp_pointer) { + mouse_moved = true + } + mac_warp_pointer = warp + if(mac_hide_pointer != hide) { + mac_hide_pointer(hide: hide) + } + mac_hide_pointer = hide + let location = event.locationInWindow + if(!Context_draw) { + // We're letting the Mac scale the window for us, + // so video_scale_mouse*() doesn't know the scale + // factor, so pass it in + x_width = Int(bounds.size.width); + y_height = Int(bounds.size.height); + } + let raw_x = location.x + let raw_y = bounds.size.height - 1 - location.y + // raw_y is 0 at the top of the window now + x = video_scale_mouse_x(kimage_ptr, Int32(raw_x), + Int32(x_width)) + y = video_scale_mouse_y(kimage_ptr, Int32(raw_y), + Int32(y_height)) + do_delta = 0; + if(mac_warp_pointer != 0) { + do_delta |= 0x1000; // x,y are deltas + x = Int32(event.deltaX) + y = Int32(event.deltaY) + } + let ret = adb_update_mouse(kimage_ptr, x, y, + Int32(buttons_state), + Int32(buttons_valid | do_delta)) + if(ret != 0) { + mouse_moved = true + } + guard let win = window else { + return // No valid window + } + if(mouse_moved) { + var rect1 = NSRect.zero + + // If warp, warp cursor to middle of window. Moving + // the cursor requires absolute screen coordinates, + // where y=0 is the top of the screen. We must convert + // window coords (where y=0 is the bottom of the win). + mouse_moved = false + let warp_x = CGFloat(video_unscale_mouse_x(kimage_ptr, + Int32(A2_WINDOW_WIDTH/2), Int32(x_width))) + let warp_y = CGFloat(video_unscale_mouse_y(kimage_ptr, + Int32(A2_WINDOW_HEIGHT/2), Int32(y_height))) + let scr_height = CGDisplayPixelsHigh(CGMainDisplayID()); + rect1.origin.x = CGFloat(warp_x) + rect1.origin.y = bounds.size.height - 1 - + CGFloat(warp_y) + rect1.size.width = 1; + rect1.size.height = 0; + let screen_rect = win.convertToScreen(rect1) + let screen_rect_y = CGFloat(scr_height) - + screen_rect.origin.y + let cg_loc = CGPoint(x: screen_rect.origin.x, + y: CGFloat(screen_rect_y)) + //print("scr_rect:\(screen_rect)") + if(warp != 0) { + // Warp to middle of the window + CGDisplayMoveCursorToPoint(CGMainDisplayID(), + cg_loc); + } + } + } + + func mac_hide_pointer(hide: Int32) { + // print("Hide called: \(hide)") + if(hide != 0) { + CGDisplayHideCursor(CGMainDisplayID()) + } else { + CGDisplayShowCursor(CGMainDisplayID()) + } + } + func initialize() { + //let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) + print("Initialize view called") + // Get width,height from video.c to handle toggling status lines + let width = Int(video_get_a2_width(kimage_ptr)) + let height = Int(video_get_a2_height(kimage_ptr)) + //if let screen = NSScreen.main { + // let rect = screen.frame + // width = Int(rect.size.width) + // height = Int(rect.size.height) + //} + pixels_per_line = width + max_height = height + //print("pixels_per_line: \(pixels_per_line), " + + // "max_height: \(max_height)") + + let color_space = CGDisplayCopyColorSpace(CGMainDisplayID()) + //let colorSpace = CGColorSpaceCreateDeviceRGB() + bitmapContext = CGContext(data: nil, width: width, + height: height, bitsPerComponent: 8, + bytesPerRow: width * 4, + space: color_space, + bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) + + //CGImageAlphaInfo.noneSkipLast.rawValue + bitmapData = bitmapContext.data! + // Set the intial value of the data to black (0) + bitmapData.initializeMemory(as: UInt32.self, repeating: 0, + count: height * width) + rawData = bitmapData.bindMemory(to: UInt32.self, + capacity: height * width) + //print("Calling video_update_scale from MainViewinitialize()") + let x_width = Int(video_get_x_width(kimage_ptr)) + let x_height = Int(video_get_x_height(kimage_ptr)) + video_update_scale(kimage_ptr, Int32(x_width), Int32(x_height), + Int32(1)) + if(Context_draw) { + video_set_alpha_mask(UInt32(0xff000000)) + // Set video.c alpha mask, since 0 means transparent + } + } + + override func draw(_ dirtyRect: NSRect) { + var rect : Change_rect + //super.draw(dirtyRect) + // Documentation says super.draw not needed... + // Draw the current image buffer to the screen + let context = NSGraphicsContext.current!.cgContext + if(!Context_draw) { + // Safe, simple drawing + let image = bitmapContext.makeImage()! + context.draw(image, in: bounds) + // The above call does the actual copy of + // data to the screen, and can take a while + //print("Draw, bounds:\(bounds), dirtyr:\(dirtyRect)") + } else { + // Unsafe, more direct drawing by peeking into + // NSGraphicsContext.current.data + if let data = context.data { + rect = Change_rect(x:0, y:0, + width:Int32(context.width), + height:Int32(context.height)); + video_update_scale(kimage_ptr, + Int32(context.width), + Int32(context.height), Int32(0)) + video_out_data_scaled(data, kimage_ptr, + Int32(context.bytesPerRow/4), &rect); + video_out_done(kimage_ptr); + print("Did out_data_scaled, rect:\(rect)"); + } + } + } + + @objc func do_config(_ : AnyObject) { + // Create a "virtual" F4 press + //print("do_config") + // Create a keydown for the F4 key (keycode:0x76) + adb_physical_key_update(kimage_ptr, Int32(0x76), 0, 0); + + // and create a keyup for the F4 key (keycode:0x76) + adb_physical_key_update(kimage_ptr, Int32(0x76), 0, 1); + } + + @objc func do_copy_text(_ : AnyObject) { + // print("do_copy"); + //let text_buf_cstr = UnsafeMutablePointer.allocate( + // capacity: 2100); + if let cstr = cfg_text_screen_str() { + let str = String(cString: cstr); + NSPasteboard.general.clearContents(); + NSPasteboard.general.setString(str, + forType: NSPasteboard.PasteboardType.string); + } + } + @objc func do_paste(_ : AnyObject) { + // print("do_paste") + let general = NSPasteboard.general; + guard let str = general.string(forType: .string) else { + print("Cannot paste, nothing in clipboard"); + return + } + //print("str: \(str)") + for raw_c in str.utf8 { + let c = UInt32(raw_c) + let ret = adb_paste_add_buf(c) + if(ret != 0) { + print("Paste too large!") + return; + } + } + } + + func mac_update_display() { + var valid : Int32 + var rect : Change_rect + var dirty_rect = NSRect.zero + + if(Context_draw) { + // We just need to know if there are any changes, + // don't actually do the copies now + valid = video_out_query(kimage_ptr); + if(valid != 0) { + self.setNeedsDisplay(bounds) + print("Needs display"); + } + return; + } + // Otherwise, update rawData in our Bitmap now + rect = Change_rect(x:0, y:0, width:0, height:0) + for i in 0.. ../GSplus.app/Contents/PkgInfo + cp -f Info.plist ../GSplus.app/Contents/ + $(PROJROOT)/lib/make_mac_icon $(PROJROOT)/lib/kegsicon.png + cp -f kegs.icns ../GSplus.app/Contents/Resources/ + touch '../GSplus.app/Icon?' + #cp -f $(PROJROOT)/lib/2mg.icns ../GSplus.app/Contents/Resources/ + #cp -f $(PROJROOT)/lib/525.icns ../GSplus.app/Contents/Resources/ + + +# Linux for X builds: +gsplus-x: $(OBJECTS) $(OBJECTS1) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \ + $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \ + -lX11 -lXext + mv gsplus .. + +# Cygwin for X builds: +gsplus.exe: $(OBJECTS) $(OBJECTS1) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \ + $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \ + -lXext -lX11 -lm + mv gsplus.exe .. + +# Mingw32 (native windows) builds: (broken, doesn't work currently) +gspluswin.exe: $(OBJECTS) $(OBJECTS1) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \ + $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) \ + -lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32 + mv $(NAME)$(SUFFIX) .. + + +.s.o: + $(AS) -c $(OPTS) -I. $*.s + +.c.o: + $(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c + +.m.o: + $(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.m + +AppDelegate.o: AppDelegate.swift + sh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \ + MainView.swift -o $*.o + +MainView.o: MainView.swift + sh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \ + AppDelegate.swift -o $*.o + +win32.o: win32.rc + windres -o win32.o win32.rc + +.c.proto: + $(KMKROOT)/bin/kmkproto $(XOPTS) $*.c > $*.proto + echo >> $*.proto + +.m.proto: + $(KMKROOT)/bin/kmkproto $(XOPTS) $*.m > $*.proto + echo >> $*.proto + +$(PROTO_OUT): $(PROTO_FILE_LIST) ldvars proto_vars + $(KMKROOT)/bin/kmkproto_head $(PROTO_OUT) tmp_protos.h + cat /dev/null $(PROTO_FILE_LIST) >> tmp_protos.h + rm -f $(PROTO_OUT) + mv tmp_protos.h $(PROTO_OUT) + kmk_cp_if_diff $(PROTO_OUT) .. + rm -f *.proto + + +compile_time.o: $(OBJECTS) $(OBJECTS1) + +include dependency + diff --git a/gsplus/src/adb.c b/gsplus/src/adb.c new file mode 100644 index 0000000..9da98aa --- /dev/null +++ b/gsplus/src/adb.c @@ -0,0 +1,2265 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +/* adb_mode bit 3 and bit 2 (faster repeats for arrows and space/del) not done*/ + +#include "defc.h" + +extern int Verbose; +extern word32 g_vbl_count; +extern int g_num_lines_prev_superhires640; +extern int g_num_lines_prev_superhires; +extern int g_rom_version; +extern int g_fast_disk_emul_en; +extern int g_limit_speed; +extern int g_irq_pending; +extern int g_swap_paddles; +extern int g_invert_paddles; +extern int g_joystick_type; +extern int g_config_control_panel; +extern int g_status_enable; +extern dword64 g_cur_dfcyc; + +extern byte *g_slow_memory_ptr; +extern byte *g_memory_ptr; +extern word32 g_mem_size_total; +extern Kimage g_mainwin_kimage; +extern Kimage g_debugwin_kimage; + +enum { + ADB_IDLE = 0, + ADB_IN_CMD, + ADB_SENDING_DATA, +}; + +#define ADB_C027_MOUSE_DATA 0x80 +#define ADB_C027_MOUSE_INT 0x40 +#define ADB_C027_DATA_VALID 0x20 +#define ADB_C027_DATA_INT 0x10 +#define ADB_C027_KBD_VALID 0x08 +#define ADB_C027_KBD_INT 0x04 +#define ADB_C027_MOUSE_COORD 0x02 +#define ADB_C027_CMD_FULL 0x01 + +#define ADB_C027_NEG_MASK ( ~ ( \ + ADB_C027_MOUSE_DATA | ADB_C027_DATA_VALID | \ + ADB_C027_KBD_VALID | ADB_C027_MOUSE_COORD | \ + ADB_C027_CMD_FULL)) + + +int halt_on_all_c027 = 0; + +word32 g_adb_repeat_delay = 45; +word32 g_adb_repeat_rate = 3; +word32 g_adb_repeat_info = 0x23; +word32 g_adb_char_set = 0x0; +word32 g_adb_layout_lang = 0x0; + +word32 g_adb_interrupt_byte = 0; +int g_adb_state = ADB_IDLE; + +word32 g_adb_cmd = (word32)-1; +int g_adb_cmd_len = 0; +int g_adb_cmd_so_far = 0; +word32 g_adb_cmd_data[16]; + +#define MAX_ADB_DATA_PEND 16 + +word32 g_adb_data[MAX_ADB_DATA_PEND]; +int g_adb_data_pending = 0; + +word32 g_c027_val = 0; +word32 g_c025_val = 0; + +byte adb_memory[256]; + +word32 g_adb_mode = 0; /* mode set via set_modes, clear_modes */ + +int g_warp_pointer = 0; +int g_hide_pointer = 0; +int g_unhide_pointer = 0; +int g_adb_copy_requested = 0; + +int g_mouse_a2_x = 0; +int g_mouse_a2_y = 0; +int g_mouse_a2_button = 0; +int g_mouse_fifo_pos = 0; +int g_mouse_raw_x = 0; +int g_mouse_raw_y = 0; + +#define ADB_MOUSE_FIFO 8 + +STRUCT(Mouse_fifo) { + dword64 dfcyc; + int x; + int y; + int buttons; +}; + +Mouse_fifo g_mouse_fifo[ADB_MOUSE_FIFO] = { { 0, 0, 0, 0 } }; + +int g_adb_mouse_valid_data = 0; +int g_adb_mouse_coord = 0; + +#define MAX_KBD_BUF 8 +#define MAX_KBD_PASTE_BUF 32768 + +int g_adb_mainwin_has_focus = 1; +#if defined(__linux__) || defined(_WIN32) +int g_adb_swap_command_option = 1; // Default to swap on Linux/Win +#else +int g_adb_swap_command_option = 0; +#endif +int g_key_down = 0; +int g_hard_key_down = 0; +int g_a2code_down = 0; +int g_kbd_read_no_update = 0; +int g_kbd_chars_buffered = 0; +int g_kbd_buf[MAX_KBD_BUF]; +int g_kbd_paste_rd_pos = 0; +int g_kbd_paste_wr_pos = 0; +byte g_kbd_paste_buf[MAX_KBD_PASTE_BUF]; +word32 g_kbd_paste_last_key = 0; +word32 g_adb_repeat_vbl = 0; + +int g_kbd_dev_addr = 2; /* ADB physical kbd addr */ +int g_mouse_dev_addr = 3; /* ADB physical mouse addr */ + +int g_kbd_ctl_addr = 2; /* ADB microcontroller's kbd addr */ +int g_mouse_ctl_addr = 3; /* ADB ucontroller's mouse addr*/ + /* above are ucontroller's VIEW of where mouse/kbd */ + /* are...if they are moved, mouse/keyboard funcs */ + /* should stop (c025, c000, c024, etc). */ + +word32 g_virtual_key_up[4]; /* bitmask of all possible 128 a2codes */ + /* indicates which keys are up=1 by bit */ +int g_rawa2_to_a2code[128]; + +int g_keypad_key_is_down[10] = { 0 };/* List from 0-9 of which keypad */ + /* keys are currently pressed */ + + +#define SHIFT_DOWN ( (g_c025_val & 0x01) ) +#define CTRL_DOWN ( (g_c025_val & 0x02) ) +#define CAPS_LOCK_DOWN ( (g_c025_val & 0x04) ) +#define OPTION_DOWN ( (g_c025_val & 0x40) ) +#define CMD_DOWN ( (g_c025_val & 0x80) ) + + +#define MAX_ADB_KBD_REG3 16 + +int g_kbd_reg0_pos = 0; +int g_kbd_reg0_data[MAX_ADB_KBD_REG3]; +int g_kbd_reg3_16bit = 0x602; /* also set in adb_reset()! */ + +int g_adb_init = 0; + +/* Format: a2code, ascii if no shift, ascii if shift, ascii if ctl */ +const int g_a2_key_to_ascii[][4] = { + { 0x00, 'a', 'A', 0x01 }, + { 0x01, 's', 'S', 0x13 }, + { 0x02, 'd', 'D', 0x04 }, + { 0x03, 'f', 'F', 0x06 }, + { 0x04, 'h', 'H', 0x08 }, + { 0x05, 'g', 'G', 0x07 }, + { 0x06, 'z', 'Z', 0x1a }, + { 0x07, 'x', 'X', 0x18 }, + + { 0x08, 'c', 'C', 0x03 }, + { 0x09, 'v', 'V', 0x16 }, + { 0x0a, -1, -1, -1 }, + { 0x0b, 'b', 'B', 0x02 }, + { 0x0c, 'q', 'Q', 0x11 }, + { 0x0d, 'w', 'W', 0x17 }, + { 0x0e, 'e', 'E', 0x05 }, + { 0x0f, 'r', 'R', 0x12 }, + + { 0x10, 'y', 'Y', 0x19 }, + { 0x11, 't', 'T', 0x14 }, + { 0x12, '1', '!', -1 }, + { 0x13, '2', '@', 0x00 }, + { 0x14, '3', '#', -1 }, + { 0x15, '4', '$', -1 }, + { 0x16, '6', '^', 0x1e }, + { 0x17, '5', '%', -1 }, + + { 0x18, '=', '+', -1 }, + { 0x19, '9', '(', -1 }, + { 0x1a, '7', '&', -1 }, + { 0x1b, '-', '_', 0x1f }, + { 0x1c, '8', '*', -1 }, + { 0x1d, '0', ')', -1 }, + { 0x1e, ']', '}', 0x1d }, + { 0x1f, 'o', 'O', 0x0f }, + + { 0x20, 'u', 'U', 0x15 }, + { 0x21, '[', '{', 0x1b }, + { 0x22, 'i', 'I', 0x09 }, + { 0x23, 'p', 'P', 0x10 }, + { 0x24, 0x0d, 0x0d, -1 }, /* return */ + { 0x25, 'l', 'L', 0x0c }, + { 0x26, 'j', 'J', 0x0a }, + { 0x27, 0x27, '"', -1 }, /* single quote */ + + { 0x28, 'k', 'K', 0x0b }, + { 0x29, ';', ':', -1 }, + { 0x2a, 0x5c, '|', 0x1c }, /* \, | */ + { 0x2b, ',', '<', -1 }, + { 0x2c, '/', '?', 0x7f }, + { 0x2d, 'n', 'N', 0x0e }, + { 0x2e, 'm', 'M', 0x0d }, + { 0x2f, '.', '>', -1 }, + + { 0x30, 0x09, 0x09, -1 }, /* tab */ + { 0x31, ' ', ' ', -1 }, + { 0x32, '`', '~', -1 }, + { 0x33, 0x7f, 0x7f, -1 }, /* Delete */ + { 0x34, -1, -1, -1 }, + { 0x35, 0x1b, 0x1b, -1 }, /* Esc */ + { 0x36, 0x0200, 0x0200, -1 }, /* control */ + { 0x37, 0x8000, 0x8000, -1 }, /* Command */ + + { 0x38, 0x0100, 0x0100, -1 }, /* shift */ + { 0x39, 0x0400, 0x0400, -1 }, /* caps lock */ + { 0x3a, 0x4000, 0x4000, -1 }, /* Option */ + { 0x3b, 0x08, 0x08, -1 }, /* left */ + { 0x3c, 0x15, 0x15, -1 }, /* right */ + { 0x3d, 0x0a, 0x0a, -1 }, /* down */ + { 0x3e, 0x0b, 0x0b, -1 }, /* up arrow */ + { 0x3f, -1, -1, -1 }, + + { 0x40, -1, -1, -1 }, + { 0x41, 0x102e, 0x102c, -1 }, /* keypad . */ + { 0x42, -1, -1, -1 }, + { 0x43, 0x102a, 0x102a, -1 }, /* keypad * */ + { 0x44, -1, -1, -1 }, + { 0x45, 0x102b, 0x102b, -1 }, /* keypad + */ + { 0x46, -1, -1, -1 }, + { 0x47, 0x1018, 0x1018, -1 }, /* keypad Clear */ + + { 0x48, -1, -1, -1 }, + { 0x49, -1, -1, -1 }, + { 0x4a, -1, -1, -1 }, + { 0x4b, 0x102f, 0x102f, -1 }, /* keypad / */ + { 0x4c, 0x100d, 0x100d, -1 }, /* keypad enter */ + { 0x4d, -1, -1, -1 }, + { 0x4e, 0x102d, 0x102d, -1 }, /* keypad - */ + { 0x4f, -1, -1, -1 }, + + { 0x50, -1, -1, -1 }, + { 0x51, 0x103d, 0x103d, -1 }, /* keypad = */ + { 0x52, 0x1030, 0x1030, -1 }, /* keypad 0 */ + { 0x53, 0x1031, 0x1031, -1 }, /* keypad 1 */ + { 0x54, 0x1032, 0x1032, -1 }, /* keypad 2 */ + { 0x55, 0x1033, 0x1033, -1 }, /* keypad 3 */ + { 0x56, 0x1034, 0x1034, -1 }, /* keypad 4 */ + { 0x57, 0x1035, 0x1035, -1 }, /* keypad 5 */ + + { 0x58, 0x1036, 0x1036, -1 }, /* keypad 6 */ + { 0x59, 0x1037, 0x1037, -1 }, /* keypad 7 */ + { 0x5a, 'a', 'A', 0x01 }, /* probably not necessary */ + { 0x5b, 0x1038, 0x1038, -1 }, /* keypad 8 */ + { 0x5c, 0x1039, 0x1039, -1 }, /* keypad 9 */ + { 0x5d, -1, -1, -1 }, + { 0x5e, -1, -1, -1 }, + { 0x5f, -1, -1, -1 }, + + { 0x60, 0x8005, 0x1060, -1 }, /* F5 */ + { 0x61, 0x8006, 0x1061, -1 }, /* F6 */ + { 0x62, 0x8007, 0x1062, -1 }, /* F7 */ + { 0x63, 0x8003, 0x1063, -1 }, /* F3 */ + { 0x64, 0x8008, 0x1064, -1 }, /* F8 */ + { 0x65, 0x8009, 0x1065, -1 }, /* F9 */ + { 0x66, -1, -1, -1 }, + { 0x67, 0x800b, 0x1067, -1 }, /* F11 */ + + { 0x68, -1, -1, -1 }, + { 0x69, 0x800d, 0x1069, -1 }, /* F13 */ + { 0x6a, -1, -1, -1 }, + { 0x6b, 0x800e, 0x106b, -1 }, /* F14 */ + { 0x6c, -1, -1, -1 }, + { 0x6d, 0x800a, 0x106d, -1 }, /* F10 */ + { 0x6e, 0x4000, 0x4000, -1 }, /* windows key alias to option */ + { 0x6f, 0x800c, 0x106f, -1 }, /* F12 */ + + { 0x70, -1, -1, -1 }, + { 0x71, 0x800f, 0x1071, -1 }, /* F15 */ + { 0x72, 0x1072, 0x1072, -1 }, /* Help, insert */ + { 0x73, 0x1073, 0x1073, -1 }, /* Home */ + { 0x74, 0x1074, 0x1074, -1 }, /* Page up */ + { 0x75, 0x1075, 0x1075, -1 }, /* keypad delete */ + { 0x76, 0x8004, 0x1076, -1 }, /* F4 */ + { 0x77, 0x1077, 0x1077, -1 }, /* keypad end */ + + { 0x78, 0x8002, 0x1078, -1 }, /* F2 */ + { 0x79, 0x1079, 0x1079, -1 }, /* keypad page down */ + { 0x7a, 0x8001, 0x107a, -1 }, /* F1 */ + { 0x7b, 0x08, 0x08, -1 }, /* left */ /* remapped to 0x3b */ + { 0x7c, 0x15, 0x15, -1 }, /* right */ /* remapped to 0x3c */ + { 0x7d, 0x0a, 0x0a, -1 }, /* down */ /* remapped to 0x3d */ + { 0x7e, 0x0b, 0x0b, -1 }, /* up arrow */ /* remapped to 0x3e */ + { 0x7f, -1, -1, -1 }, /* Reset */ +}; + +int +adb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr) +{ + if((kimage_ptr == &g_mainwin_kimage) && g_adb_mainwin_has_focus) { + *warpptr = g_warp_pointer; + return g_hide_pointer; + } + *warpptr = 0; + return 0; +} + +int +adb_get_copy_requested() +{ + int ret; + + ret = g_adb_copy_requested; + g_adb_copy_requested = 0; + return ret; +} + +void +adb_nonmain_check() +{ + // Debug window active. Undo F8 pointer warping + g_warp_pointer = 0; + g_hide_pointer = 0; +} + +void +adb_init() +{ + int keycode; + int i; + + if(g_adb_init) { + halt_printf("g_adb_init = %d!\n", g_adb_init); + } + g_adb_init = 1; + + for(i = 0; i < 128; i++) { + keycode = g_a2_key_to_ascii[i][0]; + if(keycode != i) { + printf("ADB keycode lost/skipped: i=%x: keycode=%x\n", + i, keycode); + my_exit(1); + } + g_rawa2_to_a2code[i] = -1; + } + + g_c025_val = 0; + + for(i = 0; i < 4; i++) { + g_virtual_key_up[i] = -1; + } + + for(i = 0; i < 10; i++) { + g_keypad_key_is_down[i] = 0; + } +} + +void +adb_reset() +{ + g_c027_val = 0; + + g_key_down = 0; + g_kbd_paste_rd_pos = 0; + g_kbd_paste_wr_pos = 0; + g_kbd_chars_buffered = 0; + + g_kbd_dev_addr = 2; + g_mouse_dev_addr = 3; + + g_kbd_ctl_addr = 2; + g_mouse_ctl_addr = 3; + + adb_clear_data_int(); + adb_clear_mouse_int(); + adb_clear_kbd_srq(); + + g_adb_data_pending = 0; + g_adb_interrupt_byte = 0; + g_adb_state = ADB_IDLE; + g_adb_mouse_coord = 0; + g_adb_mouse_valid_data = 0; + + g_kbd_reg0_pos = 0; + g_kbd_reg3_16bit = 0x602; +} + +#define LEN_ADB_LOG 16 +STRUCT(Adb_log) { + word32 addr; + int val; + int state; +}; + +Adb_log g_adb_log[LEN_ADB_LOG]; +int g_adb_log_pos = 0; + +void +adb_log(word32 addr, int val) +{ + int pos; + + pos = g_adb_log_pos; + g_adb_log[pos].addr = addr; + g_adb_log[pos].val = val; + g_adb_log[pos].state = g_adb_state; + pos++; + if(pos >= LEN_ADB_LOG) { + pos = 0; + } + g_adb_log_pos = pos; +} + +void +show_adb_log(void) +{ + int pos; + int i; + + pos = g_adb_log_pos; + printf("ADB log pos: %d\n", pos); + for(i = 0; i < LEN_ADB_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_ADB_LOG - 1; + } + printf("%d:%d: addr:%04x = %02x, st:%d\n", i, pos, + g_adb_log[pos].addr, g_adb_log[pos].val, + g_adb_log[pos].state); + } + printf("kbd: dev: %x, ctl: %x; mouse: dev: %x, ctl: %x\n", + g_kbd_dev_addr, g_kbd_ctl_addr, + g_mouse_dev_addr, g_mouse_ctl_addr); + printf("g_adb_state: %d, g_adb_interrupt_byte: %02x\n", + g_adb_state, g_adb_interrupt_byte); +} + +void +adb_error(void) +{ + halt_printf("Adb Error\n"); + + show_adb_log(); +} + + + +void +adb_add_kbd_srq() +{ + if(g_kbd_reg3_16bit & 0x200) { + /* generate SRQ */ + g_adb_interrupt_byte |= 0x08; + add_irq(IRQ_PENDING_ADB_KBD_SRQ); + } else { + printf("Got keycode but no kbd SRQ!\n"); + } +} + +void +adb_clear_kbd_srq() +{ + remove_irq(IRQ_PENDING_ADB_KBD_SRQ); + + /* kbd SRQ's are the only ones to handle now, so just clean it out */ + g_adb_interrupt_byte &= (~(0x08)); +} + +void +adb_add_data_int() +{ + if(g_c027_val & ADB_C027_DATA_INT) { + add_irq(IRQ_PENDING_ADB_DATA); + } +} + +void +adb_add_mouse_int() +{ + if(g_c027_val & ADB_C027_MOUSE_INT) { + add_irq(IRQ_PENDING_ADB_MOUSE); + } +} + +void +adb_clear_data_int() +{ + remove_irq(IRQ_PENDING_ADB_DATA); +} + +void +adb_clear_mouse_int() +{ + remove_irq(IRQ_PENDING_ADB_MOUSE); +} + + +void +adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2) +{ + word32 val; + int shift_amount; + int i; + + if((num_bytes >= 12) || (num_bytes >= MAX_ADB_DATA_PEND)) { + halt_printf("adb_send_bytes: %d is too many!\n", num_bytes); + } + + g_adb_state = ADB_SENDING_DATA; + g_adb_data_pending = num_bytes; + adb_add_data_int(); + + for(i = 0; i < num_bytes; i++) { + if(i < 4) { + val = val0; + } else if(i < 8) { + val = val1; + } else { + val = val2; + } + + shift_amount = 8*(3 - i); + g_adb_data[i] = (val >> shift_amount) & 0xff; + adb_printf("adb_send_bytes[%d] = %02x\n", i, g_adb_data[i]); + } +} + + +void +adb_send_1byte(word32 val) +{ + + if(g_adb_data_pending != 0) { + halt_printf("g_adb_data_pending: %d\n", g_adb_data_pending); + } + + adb_send_bytes(1, val << 24, 0, 0); +} + +void +adb_response_packet(int num_bytes, word32 val) +{ + + if(g_adb_data_pending != 0) { + halt_printf("adb_response_packet, but pending: %d\n", + g_adb_data_pending); + } + + g_adb_state = ADB_IDLE; + g_adb_data_pending = num_bytes; + g_adb_data[0] = val & 0xff; + g_adb_data[1] = (val >> 8) & 0xff; + g_adb_data[2] = (val >> 16) & 0xff; + g_adb_data[3] = (val >> 24) & 0xff; + if(num_bytes) { + g_adb_interrupt_byte |= 0x80 + num_bytes - 1; + } else { + g_adb_interrupt_byte |= 0x80; + } + + adb_printf("adb_response packet: %d: %08x\n", + num_bytes, val); + + adb_add_data_int(); +} + +void +adb_kbd_reg0_data(int a2code, int is_up) +{ + if(g_kbd_reg0_pos >= MAX_ADB_KBD_REG3) { + /* too many keys, toss */ + halt_printf("Had to toss key: %02x, %d\n", a2code, is_up); + return; + } + + g_kbd_reg0_data[g_kbd_reg0_pos] = a2code + (is_up << 7); + + adb_printf("g_kbd_reg0_data[%d] = %02x\n", g_kbd_reg0_pos, + g_kbd_reg0_data[g_kbd_reg0_pos]); + + g_kbd_reg0_pos++; + + adb_add_kbd_srq(); +} + +void +adb_kbd_talk_reg0() +{ + word32 val0, val1, reg; + int num_bytes, num; + int i; + + num = 0; + val0 = g_kbd_reg0_data[0]; + val1 = g_kbd_reg0_data[1]; + + num_bytes = 0; + if(g_kbd_reg0_pos > 0) { + num_bytes = 2; + num = 1; + if((val0 & 0x7f) == 0x7f) { + /* reset */ + val1 = val0; + } else if(g_kbd_reg0_pos > 1) { + num = 2; + if((val1 & 0x7f) == 0x7f) { + /* If first byte some other key, don't */ + /* put RESET next! */ + num = 1; + val1 = 0xff; + } + } else { + val1 = 0xff; + } + } + + if(num) { + for(i = num; i < g_kbd_reg0_pos; i++) { + g_kbd_reg0_data[i-1] = g_kbd_reg0_data[i]; + } + g_kbd_reg0_pos -= num; + } + + reg = (val0 << 8) + val1; + + adb_printf("adb_kbd_talk0: %04x\n", reg); + + adb_response_packet(num_bytes, reg); + if(g_kbd_reg0_pos == 0) { + adb_clear_kbd_srq(); + } +} + +void +adb_set_config(word32 val0, word32 val1, word32 val2) +{ + int new_mouse; + int new_kbd; + int tmp1; + + new_mouse = val0 >> 4; + new_kbd = val0 & 0xf; + if(new_mouse != g_mouse_ctl_addr) { + printf("ADB config: mouse from %x to %x!\n", + g_mouse_ctl_addr, new_mouse); + adb_error(); + g_mouse_ctl_addr = new_mouse; + } + if(new_kbd != g_kbd_ctl_addr) { + printf("ADB config: kbd from %x to %x!\n", + g_kbd_ctl_addr, new_kbd); + adb_error(); + g_kbd_ctl_addr = new_kbd; + } + + if(val1) { + // Do nothing + } + + tmp1 = val2 >> 4; + if(tmp1 == 4) { + g_adb_repeat_delay = 0; + } else if(tmp1 < 4) { + g_adb_repeat_delay = (tmp1 + 1) * 15; + } else { + halt_printf("Bad ADB repeat delay: %02x\n", tmp1); + } + + tmp1 = val2 & 0xf; + if(g_rom_version >= 3) { + tmp1 = 9 - tmp1; + } + + switch(tmp1) { + case 0: + g_adb_repeat_rate = 1; + break; + case 1: + g_adb_repeat_rate = 2; + break; + case 2: + g_adb_repeat_rate = 3; + break; + case 3: + g_adb_repeat_rate = 3; + break; + case 4: + g_adb_repeat_rate = 4; + break; + case 5: + g_adb_repeat_rate = 5; + break; + case 6: + g_adb_repeat_rate = 7; + break; + case 7: + g_adb_repeat_rate = 15; + break; + case 8: + /* I don't know what this should be, ROM 03 uses it */ + g_adb_repeat_rate = 30; + break; + case 9: + /* I don't know what this should be, ROM 03 uses it */ + g_adb_repeat_rate = 60; + break; + default: + halt_printf("Bad repeat rate: %02x\n", tmp1); + } + +} + +void +adb_set_new_mode(word32 val) +{ + if(val & 0x03) { + printf("Disabling keyboard/mouse:%02x!\n", val); + } + + if(val & 0xa2) { + halt_printf("ADB set mode: %02x!\n", val); + adb_error(); + } + + g_adb_mode = val; +} + + +int +adb_read_c026() +{ + word32 ret; + int i; + + ret = 0; + switch(g_adb_state) { + case ADB_IDLE: + ret = g_adb_interrupt_byte; + g_adb_interrupt_byte = 0; + if(g_irq_pending & IRQ_PENDING_ADB_KBD_SRQ) { + g_adb_interrupt_byte |= 0x08; + } + if(g_adb_data_pending == 0) { + if(ret & 0x80) { + halt_printf("read_c026: ret:%02x, pend:%d\n", + ret, g_adb_data_pending); + } + adb_clear_data_int(); + } + if(g_adb_data_pending) { + if(g_adb_state != ADB_IN_CMD) { + g_adb_state = ADB_SENDING_DATA; + } + } + break; + case ADB_IN_CMD: + ret = 0; + break; + case ADB_SENDING_DATA: + ret = g_adb_data[0]; + for(i = 1; i < g_adb_data_pending; i++) { + g_adb_data[i-1] = g_adb_data[i]; + } + g_adb_data_pending--; + if(g_adb_data_pending <= 0) { + g_adb_data_pending = 0; + g_adb_state = ADB_IDLE; + adb_clear_data_int(); + } + break; + default: + halt_printf("Bad ADB state: %d!\n", g_adb_state); + adb_clear_data_int(); + break; + } + + adb_printf("Reading c026. Returning %02x, st: %02x, pend: %d\n", + ret, g_adb_state, g_adb_data_pending); + + adb_log(0xc026, ret); + return (ret & 0xff); +} + + +void +adb_write_c026(int val) +{ + word32 tmp; + int dev; + + adb_printf("Writing c026 with %02x\n", val); + adb_log(0x1c026, val); + + + switch(g_adb_state) { + case ADB_IDLE: + g_adb_cmd = val; + g_adb_cmd_so_far = 0; + g_adb_cmd_len = 0; + + dev = val & 0xf; + switch(val) { + case 0x01: /* Abort */ + adb_printf("Performing adb abort\n"); + /* adb_abort() */ + break; + case 0x03: /* Flush keyboard buffer */ + adb_printf("Flushing adb keyboard buffer\n"); + /* Do nothing */ + break; + case 0x04: /* Set modes */ + adb_printf("ADB set modes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x05: /* Clear modes */ + adb_printf("ADB clear modes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x06: /* Set config */ + adb_printf("ADB set config\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 3; + break; + case 0x07: /* Sync */ + adb_printf("Performing sync cmd!\n"); + g_adb_state = ADB_IN_CMD; + if(g_rom_version == 1) { + g_adb_cmd_len = 4; + } else { + g_adb_cmd_len = 8; + } + break; + case 0x08: /* Write mem */ + adb_printf("Starting write_mem cmd\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0x09: /* Read mem */ + adb_printf("Performing read_mem cmd!\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0x0a: /* Read modes byte */ + printf("Performing read_modes cmd!\n"); + /* set_halt(1); */ + adb_send_1byte(g_adb_mode); + break; + case 0x0b: /* Read config bytes */ + printf("Performing read_configs cmd!\n"); + tmp = (g_mouse_ctl_addr << 20) + + (g_kbd_ctl_addr << 16) + + (g_adb_char_set << 12) + + (g_adb_layout_lang << 8) + + (g_adb_repeat_info << 0); + tmp = (0x82U << 24) + tmp; + adb_send_bytes(4, tmp, 0, 0); + break; + case 0x0d: /* Get Version */ + adb_printf("Performing get_version cmd!\n"); + val = 0; + if(g_rom_version == 1) { + /* ROM 01 = revision 5 */ + val = 5; + } else { + /* ROM 03 checks for rev >= 6 */ + val = 6; + } + adb_send_1byte(val); + break; + case 0x0e: /* Read avail char sets */ + adb_printf("Performing read avail char sets cmd!\n"); + adb_send_bytes(2, /* just 2 bytes */ + 0x08000000, /* number of ch sets=0x8 */ + 0, 0); + /* set_halt(1); */ + break; + case 0x0f: /* Read avail kbd layouts */ + adb_printf("Performing read avail kbd layouts cmd!\n"); + adb_send_bytes(0x2, /* number of kbd layouts=0xa */ + 0x0a000000, 0, 0); + /* set_halt(1); */ + break; + case 0x10: /* Reset */ + printf("ADB reset, cmd 0x10\n"); + do_reset(); + break; + case 0x11: /* Send ADB keycodes */ + adb_printf("Sending ADB keycodes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x12: /* ADB cmd 12: ROM 03 only! */ + if(g_rom_version >= 3) { + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + } else { + printf("ADB cmd 12, but not ROM 3!\n"); + adb_error(); + } + break; + case 0x13: /* ADB cmd 13: ROM 03 only! */ + if(g_rom_version >= 3) { + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + } else { + printf("ADB cmd 13, but not ROM 3!\n"); + adb_error(); + } + break; + case 0x73: /* Disable SRQ device 3: mouse */ + adb_printf("Disabling Mouse SRQ's (device 3)\n"); + /* HACK HACK...should deal with SRQs on mouse */ + break; + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + /* Listen dev x reg 3 */ + adb_printf("Sending data to dev %x reg 3\n", dev); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + /* Talk dev x reg 0 */ + adb_printf("Performing talk dev %x reg 0\n", dev); + if(dev == g_kbd_dev_addr) { + adb_kbd_talk_reg0(); + } else { + printf("Unknown talk dev %x reg 0!\n", dev); + /* send no data, on SRQ, system polls devs */ + /* so we don't want to send anything */ + adb_error(); + } + break; + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + /* Talk dev x reg 3 */ + adb_printf("Performing talk dev %x reg 3\n", dev); + if(dev == g_kbd_dev_addr) { + adb_response_packet(2, g_kbd_reg3_16bit); + } else { + printf("Performing talk dev %x reg 3!!\n", dev); + adb_error(); + } + break; + default: + halt_printf("ADB ucontroller cmd %02x unknown!\n", val); + /* The Gog's says ACS Demo 2 has a bug and writes to */ + /* c026 */ + break; + } + break; + case ADB_IN_CMD: + adb_printf("Setting byte %d of cmd %02x to %02x\n", + g_adb_cmd_so_far, g_adb_cmd, val); + + g_adb_cmd_data[g_adb_cmd_so_far] = val; + g_adb_cmd_so_far++; + if(g_adb_cmd_so_far >= g_adb_cmd_len) { + adb_printf("Finished cmd %02x\n", g_adb_cmd); + do_adb_cmd(); + } + + break; + default: + printf("adb_state: %02x is unknown! Setting it to ADB_IDLE\n", + g_adb_state); + g_adb_state = ADB_IDLE; + adb_error(); + halt_on_all_c027 = 1; + break; + } + return; +} + +void +do_adb_cmd() +{ + word32 val; + int dev, new_kbd, addr; + + dev = g_adb_cmd & 0xf; + + g_adb_state = ADB_IDLE; + + switch(g_adb_cmd) { + case 0x04: /* Set modes */ + adb_printf("Performing ADB set mode: OR'ing in %02x\n", + g_adb_cmd_data[0]); + + val = g_adb_cmd_data[0] | g_adb_mode; + adb_set_new_mode(val); + + break; + case 0x05: /* clear modes */ + adb_printf("Performing ADB clear mode: AND'ing in ~%02x\n", + g_adb_cmd_data[0]); + + val = g_adb_cmd_data[0]; + val = g_adb_mode & (~val); + adb_set_new_mode(val); + break; + case 0x06: /* Set config */ + adb_printf("Set ADB config to %02x %02x %02x\n", + g_adb_cmd_data[0], g_adb_cmd_data[1],g_adb_cmd_data[2]); + + adb_set_config(g_adb_cmd_data[0], g_adb_cmd_data[1], + g_adb_cmd_data[2]); + + break; + case 0x07: /* SYNC */ + adb_printf("Performing ADB SYNC\n"); + adb_printf("data: %02x %02x %02x %02x\n", + g_adb_cmd_data[0], g_adb_cmd_data[1], g_adb_cmd_data[2], + g_adb_cmd_data[3]); + + adb_set_new_mode(g_adb_cmd_data[0]); + adb_set_config(g_adb_cmd_data[1], g_adb_cmd_data[2], + g_adb_cmd_data[3]); + + if(g_rom_version >= 3) { + adb_printf(" and cmd12:%02x %02x cmd13:%02x %02x\n", + g_adb_cmd_data[4], g_adb_cmd_data[5], + g_adb_cmd_data[6], g_adb_cmd_data[7]); + } + break; + case 0x08: /* Write mem */ + addr = g_adb_cmd_data[0]; + val = g_adb_cmd_data[1]; + write_adb_ram(addr, val); + break; + case 0x09: /* Read mem */ + addr = (g_adb_cmd_data[1] << 8) + g_adb_cmd_data[0]; + adb_printf("Performing mem read to addr %04x\n", addr); + adb_send_1byte(read_adb_ram(addr)); + break; + case 0x11: /* Send ADB keycodes */ + val = g_adb_cmd_data[0]; + adb_printf("Performing send ADB keycodes: %02x\n", val); + adb_virtual_key_update(val & 0x7f, val >> 7); + break; + case 0x12: /* ADB cmd12 */ + adb_printf("Performing ADB cmd 12\n"); + adb_printf("data: %02x %02x\n", g_adb_cmd_data[0], + g_adb_cmd_data[1]); + break; + case 0x13: /* ADB cmd13 */ + adb_printf("Performing ADB cmd 13\n"); + adb_printf("data: %02x %02x\n", g_adb_cmd_data[0], + g_adb_cmd_data[1]); + break; + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + /* Listen dev x reg 3 */ + if(dev == g_kbd_dev_addr) { + if(g_adb_cmd_data[1] == 0xfe) { + /* change keyboard addr? */ + new_kbd = g_adb_cmd_data[0] & 0xf; + if(new_kbd != dev) { + printf("Moving kbd to dev %x!\n", + new_kbd); + adb_error(); + } + g_kbd_dev_addr = new_kbd; + } else if(g_adb_cmd_data[1] != 1) { + /* see what new device handler id is */ + printf("KBD listen to dev %x reg 3: 1:%02x\n", + dev, g_adb_cmd_data[1]); + adb_error(); + } + if(g_adb_cmd_data[0] != (word32)g_kbd_dev_addr) { + /* see if app is trying to change addr */ + printf("KBD listen to dev %x reg 3: 0:%02x!\n", + dev, g_adb_cmd_data[0]); + adb_error(); + } + g_kbd_reg3_16bit = ((g_adb_cmd_data[0] & 0xf) << 12) + + (g_kbd_reg3_16bit & 0x0fff); + } else if(dev == g_mouse_dev_addr) { + if(g_adb_cmd_data[0] != (word32)dev) { + /* see if app is trying to change mouse addr */ + printf("MOUS listen to dev %x reg3: 0:%02x!\n", + dev, g_adb_cmd_data[0]); + adb_error(); + } + if(g_adb_cmd_data[1] != 1 && g_adb_cmd_data[1] != 2) { + /* see what new device handler id is */ + printf("MOUS listen to dev %x reg 3: 1:%02x\n", + dev, g_adb_cmd_data[1]); + adb_error(); + } + } else { + printf("Listen cmd to dev %x reg3????\n", dev); + printf("data0: %02x, data1: %02x ????\n", + g_adb_cmd_data[0], g_adb_cmd_data[1]); + adb_error(); + } + break; + default: + printf("Doing adb_cmd %02x: UNKNOWN!\n", g_adb_cmd); + break; + } +} + + +int +adb_read_c027() +{ + word32 ret; + + if(halt_on_all_c027) { + halt_printf("halting on all c027 reads!\n"); + } + + if(g_c027_val & (~ADB_C027_NEG_MASK)) { + halt_printf("read_c027: g_c027_val: %02x\n", g_c027_val); + } + + ret = (g_c027_val & ADB_C027_NEG_MASK); + + if(g_adb_mouse_valid_data) { + ret |= ADB_C027_MOUSE_DATA; + } + + if(g_adb_interrupt_byte != 0) { + ret |= ADB_C027_DATA_VALID; + } else if(g_adb_data_pending > 0) { + if((g_adb_state != ADB_IN_CMD)) { + ret |= ADB_C027_DATA_VALID; + } + } + + if(g_adb_mouse_coord) { + ret |= ADB_C027_MOUSE_COORD; + } + +#if 0 + adb_printf("Read c027: %02x, int_byte: %02x, d_pend: %d\n", + ret, g_adb_interrupt_byte, g_adb_data_pending); +#endif + +#if 0 + adb_log(0xc027, ret); +#endif + return ret; +} + +void +adb_write_c027(int val) +{ + word32 old_val; + word32 new_int; + word32 old_int; + + adb_printf("Writing c027 with %02x\n", val); + adb_log(0x1c027, val); + + + old_val = g_c027_val; + + g_c027_val = (val & ADB_C027_NEG_MASK); + new_int = g_c027_val & ADB_C027_MOUSE_INT; + old_int = old_val & ADB_C027_MOUSE_INT; + if(!new_int && old_int) { + adb_clear_mouse_int(); + } + + new_int = g_c027_val & ADB_C027_DATA_INT; + old_int = old_val & ADB_C027_DATA_INT; + if(!new_int && old_int) { + /* ints were on, now off */ + adb_clear_data_int(); + } + + if(g_c027_val & ADB_C027_KBD_INT) { + halt_printf("Can't support kbd interrupts!\n"); + } + + return; +} + +int +read_adb_ram(word32 addr) +{ + int val; + + adb_printf("Reading adb ram addr: %02x\n", addr); + + if(addr >= 0x100) { + if(addr >= 0x1000 && addr < 0x2000) { + /* ROM self-test checksum */ + if(addr == 0x1400) { + val = 0x72; + } else if(addr == 0x1401) { + val = 0xf7; + } else { + val = 0; + } + } else { + printf("adb ram addr out of range: %04x!\n", addr); + val = 0; + } + } else { + val = adb_memory[addr]; + if((addr == 0xb) && (g_rom_version == 1)) { + // read special key state byte for Out of This World + val = (g_c025_val >> 1) & 0x43; + val |= (g_c025_val << 2) & 0x4; + val |= (g_c025_val >> 2) & 0x10; + } + if((addr == 0xc) && (g_rom_version >= 3)) { + // read special key state byte for Out of This World + val = g_c025_val & 0xc7; + printf("val is %02x\n", val); + } + } + + adb_printf("adb_ram returning %02x\n", val); + return val; +} + +void +write_adb_ram(word32 addr, int val) +{ + + adb_printf("Writing adb_ram addr: %02x: %02x\n", addr, val); + + if(addr >= 0x100) { + printf("write adb_ram addr: %02x: %02x!\n", addr, val); + adb_error(); + } else { + adb_memory[addr] = val; + } +} + +int +adb_get_keypad_xy(int get_y) +{ + int x, y, key, num_keys; + int i, j; + + key = 1; + num_keys = 0; + x = 0; + y = 0; + for(i = 0; i < 3; i++) { + for(j = 0; j < 3; j++) { + if(g_keypad_key_is_down[key]) { + num_keys++; + x = x + (j - 1)*32768; + y = y + (1 - i)*32768; + } + key++; + } + } + if(num_keys == 0) { + num_keys = 1; + } + + adb_printf("get_xy=%d, num_keys: %d, x:%d, y:%d\n", get_y, + num_keys, x, y); + + if(get_y) { + return y / num_keys; + } else { + return x / num_keys; + } +} + +// g_mouse_raw_x/y: Current position (in A2 coordinates) of mouse on host screen +// g_mouse_fifo[0].x/y: Current position (in A2 coords) of where we "want" +// mouse on the A2 screen. +// g_mouse_a2_x/y: last x,y returned through $c024 to software. +// So, reading $c024 return g_mouse_fifo[].x - g_mouse_a2_x. +// And, in simple cases, host mouse movement just sets g_mouse_fifo[0].x=raw_x +int +adb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states, + int buttons_valid) +{ + dword64 dfcyc; + int button1_changed, mouse_moved, unhide, pos; + int i; + + if(kimage_ptr != &g_mainwin_kimage) { + adb_nonmain_check(); + } + dfcyc = g_cur_dfcyc; + + unhide = (g_adb_mainwin_has_focus == 0); + if((buttons_valid >= 0) && (buttons_valid & 0x1000)) { + // x, y are really deltas + buttons_valid &= 0xfff; + x = g_mouse_raw_x + x; + y = g_mouse_raw_y + y; + g_mouse_raw_x = x; + g_mouse_raw_y = y; + } else { + g_mouse_raw_x = x; + g_mouse_raw_y = y; + + // Clamp mouse to 0-639, 0-399 to make GSOS work nicely + if(x < 0) { + x = 0; + unhide = 1; + } + if(x >= 640) { + x = 639; + unhide = 1; + } + if(y < 0) { + y = 0; + unhide = 1; + } + if(y >= 400) { + y = 399; + unhide = 1; + } + } + + g_unhide_pointer = unhide && !g_warp_pointer; + + if(kimage_ptr != &g_mainwin_kimage) { + // In debugger window...just get out + return 0; + } + + if(!g_warp_pointer) { + if(g_hide_pointer && g_unhide_pointer) { + /* cursor has left a2 window, show it */ + g_hide_pointer = 0; + } + if((g_num_lines_prev_superhires == 200) && + (g_num_lines_prev_superhires640 == 0) && + ((g_slow_memory_ptr[0x19d00] & 0x80) == 0)) { + // In 320-mode superhires, cut mouse range in half + x = x >> 1; + } + y = y >> 1; + } + + mouse_compress_fifo(dfcyc); + +#if 0 + printf("Update Mouse called with buttons:%d x,y:%d,%d, fifo:%d,%d, " + " a2: %d,%d\n", buttons_valid, x, y, + g_mouse_fifo[0].x, g_mouse_fifo[0].y, + g_mouse_a2_x, g_mouse_a2_y); +#endif + + if((buttons_valid < 0) && g_warp_pointer) { + /* Warping the pointer causes it to jump here...this is not */ + /* real motion, just update info and get out */ + g_mouse_a2_x += (x - g_mouse_fifo[0].x); + g_mouse_a2_y += (y - g_mouse_fifo[0].y); + g_mouse_fifo[0].x = x; + g_mouse_fifo[0].y = y; + return 0; + } + +#if 0 + printf("...real move, new x: %d, %d, a2:%d,%d\n", g_mouse_fifo[0].x, + g_mouse_fifo[0].y, g_mouse_a2_x, g_mouse_a2_y); +#endif + + mouse_moved = (g_mouse_fifo[0].x != x) || (g_mouse_fifo[0].y != y); + + g_mouse_fifo[0].x = x; + g_mouse_fifo[0].y = y; + g_mouse_fifo[0].dfcyc = dfcyc; + + button1_changed = (buttons_valid & 1) && + ((button_states & 1) != (g_mouse_fifo[0].buttons & 1)); + + if((button_states & 4) && !(g_mouse_fifo[0].buttons & 4) && + (buttons_valid & 4)) { + /* right button pressed */ + adb_increment_speed(); + } + if((button_states & 2) && !(g_mouse_fifo[0].buttons & 2) && + (buttons_valid & 2)) { + /* middle button pressed */ + halt2_printf("Middle button pressed\n"); + } + + pos = g_mouse_fifo_pos; + if((pos < (ADB_MOUSE_FIFO - 2)) && button1_changed) { + /* copy delta to overflow, set overflow */ + /* overflow ensures the mouse button state is precise at */ + /* button up/down times. Using a mouse event list where */ + /* deltas accumulate until a button change would work, too */ + for(i = pos; i >= 0; i--) { + g_mouse_fifo[i + 1] = g_mouse_fifo[i]; /* copy struct*/ + } + g_mouse_fifo_pos = pos + 1; + } + + g_mouse_fifo[0].buttons = (button_states & buttons_valid) | + (g_mouse_fifo[0].buttons & ~buttons_valid); + + if(mouse_moved || button1_changed) { + if( (g_mouse_ctl_addr == g_mouse_dev_addr) && + ((g_adb_mode & 0x2) == 0)) { + g_adb_mouse_valid_data = 1; + adb_add_mouse_int(); + } + } + + return mouse_moved; +} + +int +mouse_read_c024(dword64 dfcyc) +{ + word32 ret, tool_start; + int em_active, target_x, target_y, delta_x, delta_y, a2_x, a2_y; + int mouse_button, clamped, pos; + + if(((g_adb_mode & 0x2) != 0) || (g_mouse_dev_addr != g_mouse_ctl_addr)){ + /* mouse is off, return 0, or mouse is not autopoll */ + g_adb_mouse_valid_data = 0; + adb_clear_mouse_int(); + return 0; + } + + mouse_compress_fifo(dfcyc); + + pos = g_mouse_fifo_pos; + target_x = g_mouse_fifo[pos].x; + target_y = g_mouse_fifo[pos].y; + mouse_button = (g_mouse_fifo[pos].buttons & 1); + delta_x = target_x - g_mouse_a2_x; + delta_y = target_y - g_mouse_a2_y; + + clamped = 0; + if(delta_x > 0x3f) { + delta_x = 0x3f; + clamped = 1; + } else if(delta_x < -0x3f) { + delta_x = -0x3f; + clamped = 1; + } + if(delta_y > 0x3f) { + delta_y = 0x3f; + clamped = 1; + } else if(delta_y < -0x3f) { + delta_y = -0x3f; + clamped = 1; + } + + if(pos > 0) { + /* peek into next entry's button info if we are not clamped */ + /* and we're returning the y-coord */ + if(!clamped && g_adb_mouse_coord) { + mouse_button = g_mouse_fifo[pos - 1].buttons & 1; + } + } + + if(g_adb_mouse_coord) { + /* y coord */ + delta_x = 0; /* clear unneeded x delta */ + } else { + delta_y = 0; /* clear unneeded y delta */ + } + + + adb_printf(" pre a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x100e9], g_slow_memory_ptr[0x100ea], + g_slow_memory_ptr[0x100eb], g_slow_memory_ptr[0x100ec]); + adb_printf(" pre a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192], + g_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]); + + /* Update event manager internal state */ + tool_start = (g_slow_memory_ptr[0x103ca] << 16) + + (g_slow_memory_ptr[0x103c9] << 8) + + g_slow_memory_ptr[0x103c8]; + + em_active = 0; + if((tool_start >= 0x20000) && (tool_start < (g_mem_size_total - 28)) ) { + /* seems to be valid ptr to addr of mem space for tools */ + /* see if event manager appears to be active */ + em_active = g_memory_ptr[tool_start + 6*4] + + (g_memory_ptr[tool_start + 6*4 + 1] << 8); + if(g_warp_pointer) { + em_active = 0; + } + } + + //em_active = 0; // HACK! + a2_x = g_mouse_a2_x; + a2_y = g_mouse_a2_y; + + if(em_active) { + if((!g_hide_pointer) && (g_num_lines_prev_superhires == 200) && + !g_unhide_pointer) { + /* if super-hires and forcing tracking, then hide */ + g_hide_pointer = 1; + } + if(g_adb_mouse_coord == 0) { + /* update x coord values */ + g_slow_memory_ptr[0x47c] = a2_x & 0xff; + g_slow_memory_ptr[0x57c] = a2_x >> 8; + g_memory_ptr[0x47c] = a2_x & 0xff; + g_memory_ptr[0x57c] = a2_x >> 8; + + g_slow_memory_ptr[0x10190] = a2_x & 0xff; + g_slow_memory_ptr[0x10192] = a2_x >> 8; + } else { + g_slow_memory_ptr[0x4fc] = a2_y & 0xff; + g_slow_memory_ptr[0x5fc] = a2_y >> 8; + g_memory_ptr[0x4fc] = a2_y & 0xff; + g_memory_ptr[0x5fc] = a2_y >> 8; + + g_slow_memory_ptr[0x10191] = a2_y & 0xff; + g_slow_memory_ptr[0x10193] = a2_y >> 8; + } + } else { + if(g_hide_pointer && !g_warp_pointer) { + g_hide_pointer = 0; + } + } + + ret = ((!mouse_button) << 7) + ((delta_x | delta_y) & 0x7f); + if(g_adb_mouse_coord) { + g_mouse_a2_button = mouse_button; /* y coord has button*/ + } else { + ret |= 0x80; /* mouse button not down on x coord rd */ + } + + a2_x += delta_x; + a2_y += delta_y; + g_mouse_a2_x = a2_x; + g_mouse_a2_y = a2_y; + if(g_mouse_fifo_pos) { + if((target_x == a2_x) && (target_y == a2_y) && + (g_mouse_a2_button == mouse_button)) { + g_mouse_fifo_pos--; + } + } + + + adb_printf("Rd c024, mouse is_y:%d, %02x, vbl:%08x, dfcyc:%016llx, em:" + "%d\n", g_adb_mouse_coord, ret, g_vbl_count, dfcyc, em_active); + adb_printf("...mouse targ_x:%d,%d delta_x,y:%d,%d fifo:%d, a2:%d,%d\n", + target_x, target_y, delta_x, delta_y, g_mouse_fifo_pos, + a2_x, a2_y); + adb_printf(" post a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192], + g_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]); + + if((g_mouse_fifo_pos == 0) && (g_mouse_fifo[0].x == a2_x) && + (g_mouse_fifo[0].y == a2_y) && + ((g_mouse_fifo[0].buttons & 1) == g_mouse_a2_button)) { + g_adb_mouse_valid_data = 0; + adb_clear_mouse_int(); + } + + g_adb_mouse_coord = !g_adb_mouse_coord; + return ret; +} + +void +mouse_compress_fifo(dword64 dfcyc) +{ + dword64 ddelta; + int pos; + + /* The mouse fifo exists so that fast button changes don't get lost */ + /* if the emulator lags behind the mouse events */ + /* But the FIFO means really old mouse events are saved if */ + /* the emulated code isn't looking at the mouse registers */ + /* This routine compresses all mouse events > 0.5 seconds old */ + + ddelta = (500LL*1000) << 16; + for(pos = g_mouse_fifo_pos; pos >= 1; pos--) { + if((g_mouse_fifo[pos].dfcyc + ddelta) < dfcyc) { + /* Remove this entry */ + adb_printf("Old mouse FIFO pos %d removed\n", pos); + g_mouse_fifo_pos = pos - 1; + continue; + } + /* Else, stop searching the FIFO */ + break; + } +} + +void +adb_paste_update_state() +{ + int rd_pos, wr_pos; + + rd_pos = g_kbd_paste_rd_pos; + wr_pos = g_kbd_paste_wr_pos; + if(rd_pos >= wr_pos) { + g_kbd_paste_rd_pos = 0; + g_kbd_paste_wr_pos = 0; + return; + } + if(g_kbd_chars_buffered == 0) { + g_kbd_buf[0] = g_kbd_paste_buf[rd_pos]; + g_kbd_paste_rd_pos = rd_pos + 1; + g_kbd_chars_buffered = 1; + } +} + +int +adb_paste_add_buf(word32 key) +{ + word32 last_key; + int pos; + + // Applesoft reads $C000 to check for ctrl-C after each statement. + // So if we dropped all chars into g_kbd_buf[], we could end up + // losing chars due to multiple reads of $C000 without writes to $C010 + // causing g_kbd_read_no_update to toss a paste char. + // Instead, have a separate buffer, and when g_kbd_chars_buffered==0, + // copy one paste char to g_kbd_buf[0]. This also solves a problem + // where Applesoft is doing: 10 GOTO 10 and it needs to see a Ctrl-C + // to stop--but a paste buffer is in the way. + // But, now pressing keys while a paste is pending causes those keys + // to take priority during the paste. + last_key = g_kbd_paste_last_key; + g_kbd_paste_last_key = key; + if(key == 10) { // \n, newline on Unix + key = 13; // \r, return + if(last_key == 13) { + key = 0; // CR, then LF--eat the LF + } + } + if((key == 0) || (key >= 0x80)) { + return 0; // Just skip these keys + } + pos = g_kbd_paste_wr_pos; + if(pos >= MAX_KBD_PASTE_BUF) { + return 1; + } + g_kbd_paste_buf[pos] = key | 0x80; + g_kbd_paste_wr_pos = pos + 1; + + adb_paste_update_state(); + return 0; +} + +void +adb_key_event(int a2code, int is_up) +{ + word32 special, vbl_count; + int key, hard_key, pos, tmp_ascii, ascii; + + if(is_up) { + adb_printf("adb_key_event, key:%02x, is up, g_key_down: %02x\n", + a2code, g_key_down); + } + + if(a2code < 0 || a2code > 0x7f) { + halt_printf("add_key_event: a2code: %04x!\n", a2code); + return; + } + + if(!is_up && a2code == 0x35) { + /* ESC pressed, see if ctrl & cmd key down */ + if(CTRL_DOWN && CMD_DOWN) { + /* Desk mgr int */ + printf("Desk mgr int!\n"); + + g_adb_interrupt_byte |= 0x20; + adb_add_data_int(); + } + } + + /* convert key to ascii, if possible */ + hard_key = 0; + if(g_a2_key_to_ascii[a2code][1] & 0xef00) { + /* special key */ + } else { + /* we have ascii */ + hard_key = 1; + } + + pos = 1; + ascii = g_a2_key_to_ascii[a2code][1]; + if(CAPS_LOCK_DOWN && (ascii >= 'a') && (ascii <= 'z')) { + pos = 2; + if(SHIFT_DOWN && (g_adb_mode & 0x40)) { + /* xor shift mode--capslock and shift == lowercase */ + pos = 1; + } + } else if(SHIFT_DOWN) { + pos = 2; + } + + ascii = g_a2_key_to_ascii[a2code][pos]; + if(CTRL_DOWN) { + tmp_ascii = g_a2_key_to_ascii[a2code][3]; + if(tmp_ascii >= 0) { + ascii = tmp_ascii; + } + } + key = (ascii & 0x7f) + 0x80; + + special = (ascii >> 8) & 0xff; + if(ascii < 0) { + printf("ascii1: %d, a2code: %02x, pos: %d\n", ascii,a2code,pos); + ascii = 0; + special = 0; + } + + if(!is_up) { + if(hard_key) { + g_kbd_buf[g_kbd_chars_buffered] = key; + g_kbd_chars_buffered++; + if(g_kbd_chars_buffered >= MAX_KBD_BUF) { + g_kbd_chars_buffered = MAX_KBD_BUF - 1; + } + g_key_down = 1; + g_a2code_down = a2code; + + /* first key down, set up autorepeat */ + vbl_count = g_vbl_count; + g_adb_repeat_vbl = vbl_count + g_adb_repeat_delay; + if(g_adb_repeat_delay == 0) { + g_key_down = 0; + } + g_hard_key_down = 1; + } + + g_c025_val = g_c025_val | special; + adb_printf("new c025_or: %02x\n", g_c025_val); + } else { + if(hard_key && (a2code == g_a2code_down)) { + g_hard_key_down = 0; + /* Turn off repeat */ + g_key_down = 0; + } + + g_c025_val = g_c025_val & (~ special); + adb_printf("new c025_and: %02x\n", g_c025_val); + } + + if(g_key_down) { + g_c025_val = g_c025_val & (~0x20); + } else { + /* If no hard key down, set update mod latch */ + g_c025_val = g_c025_val | 0x20; + } + +} + +word32 +adb_read_c000() +{ + word32 vbl_count; + + if( ((g_kbd_buf[0] & 0x80) == 0) && (g_key_down == 0)) { + /* nothing happening, just get out */ + return g_kbd_buf[0]; + } + if(g_kbd_buf[0] & 0x80) { + /* got one */ + if((g_kbd_read_no_update++ > 5) && (g_kbd_chars_buffered > 1)) { + /* read 5 times, keys pending, let's move it along */ + printf("Read %02x %d times, tossing\n", g_kbd_buf[0], + g_kbd_read_no_update); + adb_access_c010(); + } + } else { + vbl_count = g_vbl_count; + if(g_key_down && (vbl_count >= g_adb_repeat_vbl)) { + /* repeat the g_key_down */ + g_c025_val |= 0x8; + adb_key_event(g_a2code_down, 0); + g_adb_repeat_vbl = vbl_count + g_adb_repeat_rate; + } + } + + return g_kbd_buf[0]; +} + +word32 +adb_access_c010() +{ + int tmp; + int i; + + g_kbd_read_no_update = 0; + + tmp = g_kbd_buf[0] & 0x7f; + g_kbd_buf[0] = tmp; + + tmp = tmp | (g_hard_key_down << 7); + if(g_kbd_chars_buffered) { + for(i = 1; i < g_kbd_chars_buffered; i++) { + g_kbd_buf[i - 1] = g_kbd_buf[i]; + } + g_kbd_chars_buffered--; + if(g_kbd_chars_buffered == 0) { + adb_paste_update_state(); + } + } + + g_c025_val = g_c025_val & (~ (0x08)); + + return tmp; +} + +word32 +adb_read_c025() +{ + return g_c025_val; +} + +int +adb_is_cmd_key_down() +{ + return CMD_DOWN; +} + +int +adb_is_option_key_down() +{ + return OPTION_DOWN; +} + +void +adb_increment_speed() +{ + const char *str; + + g_limit_speed++; + if(g_limit_speed > 3) { + g_limit_speed = 0; + } + + str = ""; + switch(g_limit_speed) { + case 0: + str = "...as fast as possible!"; + break; + case 1: + str = "...1.024MHz!"; + break; + case 2: + str = "...2.8MHz!"; + break; + case 3: + str = "...8.0MHz!"; + break; + } + printf("Toggling g_limit_speed to %d%s\n", g_limit_speed, str); +} + +void +adb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask) +{ + // Called by *driver.c host drivers to handle focus changes and + // capslock state (so if capslock is on, we leave the window, release + // capslock, then reenter the window, we update things properly). + if(kimage_ptr == &g_mainwin_kimage) { + g_c025_val = (g_c025_val & (~mask)) | new_c025_val; + } else { + kimage_ptr->c025_val = (kimage_ptr->c025_val & (~mask)) | + new_c025_val; + } +} + +int +adb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr) +{ + int i; + + switch(unicode_c) { + case 0x00a3: // British pound + unicode_c = '#'; + break; + case 0x00e0: // a with left accent + unicode_c = '@'; + break; + case 0x00b0: // degrees (French) + case 0x00c4: // A with umlaut (German, Swedish) + case 0x00a1: // ! upside down (Spanish) + case 0x00c6: // AE (Danish) + unicode_c = '['; + break; + case 0x00e7: // c with tail (French/Italian) + case 0x00d1: // N with ~ (Spanish) + case 0x00d6: // O with umlaut (German, Swedish) + case 0x00d8: // O with slash (Danish) + unicode_c = '\\'; + break; + case 0x00a7: // ss like thing (French) + case 0x00dc: // U with umlaut + case 0x00bf: // ? upside down (Spanish) + case 0x00c5: // A with circle (Danish) + //case 0x00e9: // e with right accent (Italian) + unicode_c = ']'; + break; + //case 0x0000: // u with left accent (Italian) + // unicode_c = '`'; + // break; + case 0x00e4: // a with umlaut (german) + case 0x00e9: // e with accent (french) + case 0x0000: // ae (Danish) + unicode_c = '{'; + break; + case 0x00f6: // o with umlaut (German/Swedish) + case 0x00f9: // u with left accent (French) + case 0x00f8: // o with slash (Danish) + case 0x00f1: // n with ~ (Spanish) + case 0x00f2: // o with ` (Italian) + unicode_c = '|'; + break; + case 0x00e8: // e with ` (French, Italian) + case 0x00fc: // u with umlaut (German) + case 0x00e5: // a with circle (Danish/Swedish) + unicode_c = '}'; + break; + case 0x00a8: // two high dots (French) + case 0x00ec: // i with ` (Italian) + case 0x00df: // german B thing (German) + unicode_c = '~'; + break; + } + + if(unicode_c > 0x7f) { + return a2code; // Use a2code instead + } + if((g_a2_key_to_ascii[a2code][1] & 0xf000) == 0x1000) { // Keypad + // Don't remap keypad keys, we need them for Keypad Joystick + if((unicode_c >= '0') && (unicode_c <= '9')) { + return a2code; + } + } + + for(i = 0; i < 128; i++) { + if(g_a2_key_to_ascii[i][1] == unicode_c) { // Not-shifted + *shift_down_ptr = 0; + return g_a2_key_to_ascii[i][0]; + } + if(g_a2_key_to_ascii[i][2] == unicode_c) { // Shifted + *shift_down_ptr = 1; + return g_a2_key_to_ascii[i][0]; + } + } + + return a2code; // Not found, use default a2code +} + +void +adb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c, + int is_up) +{ + word32 restore_c025_val, restorek_c025_val; + int special, ascii_and_type, ascii, new_shift, a2code, other_a2code; + + /* this routine called by xdriver to pass raw codes--handle */ + /* ucontroller and ADB bus protocol issues here */ + /* if autopoll on, pass it on through to c025,c000 regs */ + /* else only put it in kbd reg 3, and pull SRQ if needed */ + + adb_printf("adb_phys_key_update: %02x, %d\n", raw_a2code, is_up); + + if((raw_a2code < 0) || (raw_a2code > 0x7f)) { + halt_printf("raw_a2code: %04x!\n", raw_a2code); + return; + } + a2code = raw_a2code; + restore_c025_val = 0; + restorek_c025_val = 0; + if(unicode_c > 0) { + // To enable international keyboards, ignore a2code, look up + // what U.S. keycode would be and return that + new_shift = g_c025_val & 1; + a2code = adb_ascii_to_a2code(unicode_c, a2code, &new_shift); + if(a2code && ((g_c025_val & 1) != new_shift)) { + restore_c025_val = g_c025_val | 0x100; + restorek_c025_val = kimage_ptr->c025_val; + g_c025_val = (g_c025_val & -2) | new_shift; + kimage_ptr->c025_val = (kimage_ptr->c025_val & -2) | + new_shift; + } + if(!is_up) { + g_rawa2_to_a2code[raw_a2code & 0x7f] = a2code; + } + } + + /* Remap 0x7b-0x7e to 0x3b-0x3e (arrow keys on new mac keyboards) */ + if((a2code >= 0x7b) && (a2code <= 0x7e)) { + a2code = a2code - 0x40; + } + if(g_adb_swap_command_option) { + if(a2code == 0x37) { // Command? + a2code = 0x3a; // -> Option + } else if(a2code == 0x3a) { // Option? + a2code = 0x37; // -> Command + } + } + + /* Now check for special keys (function keys, etc) */ + ascii_and_type = g_a2_key_to_ascii[a2code][1]; + special = 0; + if((ascii_and_type & 0xf000) == 0x8000) { + /* special function key */ + special = ascii_and_type & 0xff; + switch(special) { + case 0x01: /* F1 - remap to cmd */ + a2code = 0x37; + special = 0; + break; + case 0x02: /* F2 - remap to option */ + a2code = 0x3a; + special = 0; + break; + case 0x03: /* F3 - remap to escape for OS/2 */ + a2code = 0x35; + special = 0; + break; + case 0x0c: /* F12 - remap to reset */ + a2code = 0x7f; + special = 0; + break; + default: + break; + } + } + + /* Only process reset requests here */ + if((is_up == 0) && (a2code == 0x7f) && CTRL_DOWN) { + /* Reset pressed! */ + printf("Reset pressed since CTRL_DOWN: %d\n", CTRL_DOWN); + do_reset(); + return; + } + + if(special && !is_up) { + switch(special) { + case 0x04: /* F4 - Emulator config panel */ + cfg_toggle_config_panel(); + break; + case 0x05: /* F5 - Force Refresh */ + g_status_enable = !g_status_enable; + // video_update() will call video_update_status_enable() + break; + case 0x06: /* F6 - emulator speed */ + if(SHIFT_DOWN) { + halt2_printf("Shift-F6 pressed\n"); + } else { + adb_increment_speed(); + } + break; + case 0x07: /* F7 - toggle debugger window, SHIFT:fast disk */ + if(SHIFT_DOWN) { + g_fast_disk_emul_en = !g_fast_disk_emul_en; + iwm_update_fast_disk_emul(g_fast_disk_emul_en); + printf("g_fast_disk_emul_en is now %d\n", + g_fast_disk_emul_en); + } else { + video_set_active(&g_debugwin_kimage, + !g_debugwin_kimage.active); + printf("Toggled debugger window to:%d\n", + g_debugwin_kimage.active); + } + break; + case 0x08: /* F8 - warp pointer */ + g_warp_pointer = !g_warp_pointer; + g_hide_pointer = g_warp_pointer; + printf("New warp:%d, new hide:%d\n", g_warp_pointer, + g_hide_pointer); + break; + case 0x09: /* F9 - swap paddles */ + if(CTRL_DOWN) { + g_adb_copy_requested = 1; + } else if(SHIFT_DOWN) { + g_swap_paddles = !g_swap_paddles; + printf("Swap paddles is now: %d\n", + g_swap_paddles); + } else { + g_invert_paddles = !g_invert_paddles; + printf("Invert paddles is now: %d\n", + g_invert_paddles); + } + break; + case 0x0a: /* F10 - nothing */ + break; + case 0x0b: /* F11 - full screen */ + break; + } + + return; + } + + if(kimage_ptr == &g_debugwin_kimage) { + debugger_key_event(kimage_ptr, a2code, is_up); + if(restore_c025_val) { + g_c025_val = restore_c025_val & 0xff; // Restore shift + kimage_ptr->c025_val = restorek_c025_val; + } + return; + } + + /* Handle Keypad Joystick here partly...if keypad key pressed */ + /* while in Keypad Joystick mode, do not pass it on as a key press */ + if((ascii_and_type & 0xff00) == 0x1000) { + /* Keep track of keypad number keys being up or down even */ + /* if joystick mode isn't keypad. This avoid funny cases */ + /* if joystick mode is changed while a key is pressed */ + ascii = ascii_and_type & 0xff; + if(ascii > 0x30 && ascii <= 0x39) { + g_keypad_key_is_down[ascii - 0x30] = !is_up; + } + if(g_joystick_type == 0) { + /* If Joystick type is keypad, then do not let these */ + /* keypress pass on further, except for cmd/opt */ + if(ascii == 0x30) { + /* remap '0' to cmd */ + a2code = 0x37; + } else if(ascii == 0x2e || ascii == 0x2c) { + /* remap '.' and ',' to option */ + a2code = 0x3a; + } else { + /* Just ignore it in this mode */ + return; + } + } + } + + adb_maybe_virtual_key_update(a2code, is_up); + other_a2code = g_rawa2_to_a2code[raw_a2code & 0x7f]; + if((other_a2code >= 0) && is_up) { + adb_maybe_virtual_key_update(other_a2code, is_up); + g_rawa2_to_a2code[raw_a2code & 0x7f] = -1; + } + + if(restore_c025_val) { + g_c025_val = restore_c025_val & 0xff; // Restore shift + } +} + +void +adb_maybe_virtual_key_update(int a2code, int is_up) +{ + int autopoll; + + autopoll = 1; + if(g_adb_mode & 1) { + /* autopoll is explicitly off */ + autopoll = 0; + } + if(g_kbd_dev_addr != g_kbd_ctl_addr) { + /* autopoll is off because ucontroller doesn't know kbd moved */ + autopoll = 0; + } + if(g_config_control_panel) { + /* always do autopoll */ + autopoll = 1; + } + + if(is_up) { + if(!autopoll) { + /* no auto keys, generate SRQ! */ + adb_kbd_reg0_data(a2code, is_up); + } else { + adb_virtual_key_update(a2code, is_up); + } + } else { + if(!autopoll) { + /* no auto keys, generate SRQ! */ + adb_kbd_reg0_data(a2code, is_up); + } else { + /* was up, now down */ + adb_virtual_key_update(a2code, is_up); + } + } +} + +void +adb_virtual_key_update(int a2code, int is_up) +{ + word32 mask; + int bitpos; + int i; + + adb_printf("Virtual handle a2code: %02x, is_up: %d\n", a2code, is_up); + + if(a2code < 0 || a2code > 0x7f) { + halt_printf("a2code: %04x!\n", a2code); + return; + } + + i = (a2code >> 5) & 3; + bitpos = a2code & 0x1f; + mask = (1 << bitpos); + + if(is_up) { + if(g_virtual_key_up[i] & mask) { + /* already up, do nothing */ + } else { + g_virtual_key_up[i] |= mask; + adb_key_event(a2code, is_up); + } + } else { + if(g_virtual_key_up[i] & mask) { + g_virtual_key_up[i] &= (~mask); + adb_key_event(a2code, is_up); + } + } +} + +#if 0 +void +adb_all_keys_up() +{ + word32 mask; + int i, j; + + for(i = 0; i < 4; i++) { + for(j = 0; j < 32; j++) { + mask = 1 << j; + if((g_virtual_key_up[i] & mask) == 0) { + /* create key-up event */ + adb_physical_key_update(i*32 + j, 1); + } + } + } +} +#endif + +void +adb_kbd_repeat_off() +{ + g_key_down = 0; +} + +void +adb_mainwin_focus(int has_focus) +{ + g_adb_mainwin_has_focus = has_focus; + // printf("g_adb_mainwin_has_focus=%d\n", g_adb_mainwin_has_focus); + if(!has_focus) { + adb_nonmain_check(); + } +} diff --git a/gsplus/src/applesingle.c b/gsplus/src/applesingle.c new file mode 100644 index 0000000..f0cebc0 --- /dev/null +++ b/gsplus/src/applesingle.c @@ -0,0 +1,363 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// From Wikipedia AppleSingle_and_AppleDouble_formats): +// https://web.archive.org/web/20180311140826/http://kaiser-edv.de/ +// documents/AppleSingle_AppleDouble.pdf +// All fields in an Applesingle file are in big-endian format +// ProDOS forked files are described in Technote tn-pdos-025. + +#include "defc.h" + +word32 +applesingle_get_be32(const byte *bptr) +{ + return (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; +} + +word32 +applesingle_get_be16(const byte *bptr) +{ + return (bptr[0] << 8) | bptr[1]; +} + +void +applesingle_set_be32(byte *bptr, word32 val) +{ + bptr[3] = val; + bptr[2] = val >> 8; + bptr[1] = val >> 16; + bptr[0] = val >> 24; +} + +void +applesingle_set_be16(byte *bptr, word32 val) +{ + bptr[1] = val; + bptr[0] = val >> 8; +} + +word32 +applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data) +{ + byte *fptr, *bptr; + word32 data_size, resource_size, rounded_data_size, num_entries; + word32 rounded_resource_size, hdr_size, max_size, hdr_pos, data_pos; + word32 block, key_block, ret, good, has_finder_info, offset; + int level; + int i, j; + +#if 0 + printf("applesingle_map_from_prodos: %s do_file_data:%d\n", + fileptr->unix_path, do_file_data); +#endif + + // First, handle mini directory describing the forks + key_block = fileptr->key_block; + ret = dynapro_map_one_file_block(dsk, fileptr, key_block, 1U << 30, 0); + if(ret == 0) { + printf(" dynapro_map_one_file_block ret 0, applesingle done\n"); + return 0; + } + bptr = &(dsk->raw_data[key_block << 9]); + data_size = dynapro_get_word24(&bptr[5]); + resource_size = dynapro_get_word24(&bptr[0x100 + 5]); + has_finder_info = bptr[9] | bptr[27]; + + num_entries = 1; // ProDOS info always + if(has_finder_info) { + num_entries++; + } + rounded_data_size = data_size; + if(data_size) { + rounded_data_size = (data_size + 0x200) & -0x200; + num_entries++; + } + rounded_resource_size = resource_size; + if(resource_size) { + rounded_resource_size = (resource_size + 0x200) & -0x200; + num_entries++; + } + hdr_size = 256; + max_size = hdr_size + rounded_resource_size + rounded_data_size; + fileptr->buffer_ptr = 0; + fptr = 0; + if(do_file_data) { + fptr = calloc(1, max_size + 0x200); +#if 0 + printf(" fptr:%p, max_size:%08x, res:%08x, data:%08x\n", + fptr, max_size, rounded_resource_size, + rounded_data_size); +#endif + } + + // From now on, errors cannot return without free'ing fptr + good = 1; + if(resource_size) { + block = dynapro_get_word16(&bptr[0x100 + 1]); + level = bptr[0x100]; + if(fptr) { + fileptr->buffer_ptr = fptr + 256; + } + ret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0, + resource_size); + if(ret == 0) { + good = 0; + } + } + + if(data_size) { + block = dynapro_get_word16(&bptr[1]); + level = bptr[0]; + if(fptr) { + fileptr->buffer_ptr = fptr + 256 + + rounded_resource_size; + } + ret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0, + data_size); + if(ret == 0) { + good = 0; + } + } + + fileptr->buffer_ptr = 0; + + // Now prepare the header + if(fptr) { + applesingle_set_be32(&fptr[0], 0x00051600); // Magic + applesingle_set_be32(&fptr[4], 0x00020000); // Version + applesingle_set_be16(&fptr[24], num_entries); // Version + hdr_pos = 26; + data_pos = 192; + + // First do ProDOS entry + applesingle_set_be32(&fptr[hdr_pos + 0], 11); // ProDOS Info + applesingle_set_be32(&fptr[hdr_pos + 4], data_pos); + applesingle_set_be32(&fptr[hdr_pos + 8], 8); + + applesingle_set_be16(&fptr[data_pos + 0], 0xc3); + applesingle_set_be16(&fptr[data_pos + 2], fileptr->file_type); + applesingle_set_be32(&fptr[data_pos + 4], fileptr->aux_type); + hdr_pos += 12; + data_pos += 8; + + // Then do FinderInfo + if(has_finder_info) { + applesingle_set_be32(&fptr[hdr_pos + 0], 9); //Finder + applesingle_set_be32(&fptr[hdr_pos + 4], data_pos); + applesingle_set_be32(&fptr[hdr_pos + 8], 32); + + for(i = 0; i < 2; i++) { + offset = bptr[9 + 18*i]; + if(!offset) { + continue; // skip it + } + offset = ((offset - 1) & 1) * 8; + for(j = 0; j < 9; j++) { + fptr[data_pos + offset + j] = + bptr[10 + 18*i + j]; + } + } + hdr_pos += 12; + data_pos += 32; + } + + if(data_pos >= 256) { + printf("data_pos:%08x is too big\n", data_pos); + good = 0; + } + + // First, do data fork + if(data_size) { + applesingle_set_be32(&fptr[hdr_pos + 0], 1); // Data + applesingle_set_be32(&fptr[hdr_pos + 4], + 256 + rounded_resource_size); + applesingle_set_be32(&fptr[hdr_pos + 8], data_size); + hdr_pos += 12; + } + // Then do resource fork + if(resource_size) { + applesingle_set_be32(&fptr[hdr_pos + 0], 2); // Rsrc + applesingle_set_be32(&fptr[hdr_pos + 4], 256); + applesingle_set_be32(&fptr[hdr_pos + 8], resource_size); + hdr_pos += 12; + } + if(hdr_pos > 192) { + printf("hdr:%08x stomped on data\n", hdr_pos); + good = 0; + } + if(good) { + ret = dynapro_write_to_unix_file(fileptr->unix_path, + fptr, 256 + rounded_resource_size + data_size); + if(ret == 0) { + good = 0; + } + } + + free(fptr); + } + + // printf("applesingle_map_from_prodos done, good:%d\n", good); + return good; +} + +word32 +applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr, + dword64 dsize) +{ + byte *bptr, *tptr; + word32 key_block, blocks_used, entry_id, blocks_out, offset, length; + word32 magic, version, hdr_pos, did_fork; + int num_entries; + int i; + + // Return 0 if anything is wrong with the .applesingle file + // Otherwise, return (blocks_used << 16) | (key_block & 0xffff) + +#if 0 + printf("applesingle_from_unix %s, size:%08llx\n", fileptr->unix_path, + dsize); +#endif + + key_block = fileptr->key_block; + bptr = &(dsk->raw_data[key_block << 9]); + + if(dsize < 50) { + printf("Applesingle is too small\n"); + return 0; + } + magic = applesingle_get_be32(&fptr[0]); + version = applesingle_get_be32(&fptr[4]); + num_entries = applesingle_get_be16(&fptr[24]); + if((magic != 0x00051600) || (version < 0x00020000)) { + printf("Bad Applesingle magic number is: %08x, vers:%08x\n", + magic, version); + return 0; + } + hdr_pos = 26; + blocks_used = 1; + did_fork = 0; + // printf(" num_entries:%d\n", num_entries); + for(i = 0; i < num_entries; i++) { + if((hdr_pos + 24) > dsize) { + printf("Applesingle header exceeds file size i:%d of " + "%d, hdr_pos:%04x dsize:%08llx\n", i, + num_entries, hdr_pos, dsize); + return 0; + } + entry_id = applesingle_get_be32(&fptr[hdr_pos + 0]); + offset = applesingle_get_be32(&fptr[hdr_pos + 4]); + length = applesingle_get_be32(&fptr[hdr_pos + 8]); +#if 0 + printf(" header[%d] at +%04x: id:%d, offset:%08x, len:%08x\n", + i, hdr_pos, entry_id, offset, length); +#endif + if((offset + length) > dsize) { + printf("Applesingle entry_id:%d exceeds file size\n", + entry_id); + return 0; + } + switch(entry_id) { + case 1: // Data fork + case 2: // Resource fork + tptr = bptr; + if(entry_id == 2) { // Resource fork + tptr += 0x100; + } +#if 0 + printf(" for entry_id %d, offset:%08x, length:%08x, " + "fptr:%p\n", entry_id, offset, length, fptr); +#endif + if(did_fork & (1 << entry_id)) { + printf("fork %d repeated!\n", entry_id); + return 0; + } + did_fork |= (1 << entry_id); + blocks_out = applesingle_make_prodos_fork(dsk, + fptr + offset, tptr, length); + if(blocks_out == 0) { + return 0; + } + blocks_used += (blocks_out >> 16); + break; + case 9: // Finder Info + if(length < 32) { + printf("Invalid Finder info, len:%d\n", length); + } + bptr[8] = 0x12; + bptr[8 + 18] = 0x12; + bptr[9] = 1; + bptr[9 + 18] = 2; + for(i = 0; i < 16; i++) { + bptr[10 + i] = fptr[offset + i]; + bptr[10 + 18 + i] = fptr[offset + 16 + i]; + } + break; + case 11: // ProDOS File Info + fileptr->file_type = fptr[offset + 3]; + fileptr->aux_type = (fptr[offset + 6] << 8) | + fptr[offset + 7]; + break; + default: + break; // Ignore it + } + hdr_pos += 12; + } + + for(i = 1; i < 3; i++) { + if((did_fork >> i) & 1) { + continue; + } + // Create one block for this fork even though it's length==0 + // i==1: no data fork; i==2: no resource fork + printf(" Doing dummy fork, i:%d, fptr:%p\n", i, fptr); + blocks_out = applesingle_make_prodos_fork(dsk, fptr, + bptr + ((i & 2) * 0x80), 0); + if(blocks_out == 0) { + return blocks_out; + } + blocks_used += (blocks_out >> 16); + } + + fileptr->eof = 0x200; + return (blocks_used << 16) | key_block; +} + +word32 +applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length) +{ + word32 block, blocks_out, storage_type; + +#if 0 + printf("applesingle_make_prodos_fork: fptr:%p, tptr:%p, length:%08x\n", + fptr, tptr, length); +#endif + + // Handle creating either a resource or data fork + block = dynapro_find_free_block(dsk); + if(block == 0) { + return 0; + } + blocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type, block, + length); + + // printf(" dynapro_fork_from_unix ret: %08x, storage:%02x\n", + // blocks_out, storage_type); + tptr[0] = storage_type >> 4; + tptr[1] = blocks_out & 0xff; // key_block lo + tptr[2] = (blocks_out >> 8) & 0xff; // key_block hi + tptr[3] = (blocks_out >> 16) & 0xff; // blocks_used lo + tptr[4] = (blocks_out >> 24) & 0xff; // blocks_used hi + tptr[5] = length & 0xff; // eof lo + tptr[6] = (length >> 8) & 0xff; // eof mid + tptr[7] = (length >> 16) & 0xff; // eof hi + return blocks_out; +} diff --git a/gsplus/src/clock.c b/gsplus/src/clock.c new file mode 100644 index 0000000..1899bd8 --- /dev/null +++ b/gsplus/src/clock.c @@ -0,0 +1,397 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" +#include +#ifdef _WIN32 +# include +# include +#else +# include +#endif + +extern int Verbose; +extern word32 g_vbl_count; +extern int g_rom_version; +extern int g_config_kegs_update_needed; + +#define CLK_IDLE 1 +#define CLK_TIME 2 +#define CLK_INTERNAL 3 +#define CLK_BRAM1 4 +#define CLK_BRAM2 5 + +int g_clk_mode = CLK_IDLE; +int g_clk_read = 0; +int g_clk_reg1 = 0; + +extern word32 g_c033_data; +extern word32 g_c034_val; + +byte g_bram[2][256]; +byte *g_bram_ptr = &(g_bram[0][0]); + +word32 g_clk_cur_time = 0xa0000000; +int g_clk_next_vbl_update = 0; + +double +get_dtime() +{ + +#ifdef _WIN32 + FILETIME filetime; + dword64 dlow, dhigh; +#else + struct timeval tp1; + double dsec; + double dusec; +#endif + double dtime; + + /* Routine used to return actual system time as a double */ + /* No routine cares about the absolute value, only deltas--maybe */ + /* take advantage of that in future to increase usec accuracy */ + +#ifdef _WIN32 + //dtime = timeGetTime() / 1000.0; + GetSystemTimePreciseAsFileTime(&filetime); + dlow = filetime.dwLowDateTime; + dhigh = filetime.dwHighDateTime; + dlow = (dhigh << 32) | dlow; + dtime = (double)dlow; + dtime = dtime / (1000*1000*10.0); // FILETIME is in 100ns incs +#else + +# ifdef SOLARIS + gettimeofday(&tp1, (void *)0); +# else + gettimeofday(&tp1, (struct timezone *)0); +# endif + + dsec = (double)tp1.tv_sec; + dusec = (double)tp1.tv_usec; + + dtime = dsec + (dusec / (1000.0 * 1000.0)); +#endif + + return dtime; +} + +int +micro_sleep(double dtime) +{ +#ifndef _WIN32 + struct timeval Timer; + int ret; +#endif + + if(dtime <= 0.0) { + return 0; + } + if(dtime >= 1.0) { + halt_printf("micro_sleep called with %f!!\n", dtime); + return -1; + } + +#if 0 + printf("usleep: %f\n", dtime); +#endif + +#ifdef _WIN32 + Sleep((word32)(dtime * 1000)); +#else + Timer.tv_sec = 0; + Timer.tv_usec = (dtime * 1000000.0); + if( (ret = select(0, 0, 0, 0, &Timer)) < 0) { + fprintf(stderr, "micro_sleep (select) ret: %d, errno: %d\n", + ret, errno); + return -1; + } +#endif + return 0; +} + +void +clk_bram_zero() +{ + int i, j; + + /* zero out all bram */ + for(i = 0; i < 2; i++) { + for(j = 0; j < 256; j++) { + g_bram[i][j] = 0; + } + } + g_bram_ptr = &(g_bram[0][0]); +} + +void +clk_bram_set(int bram_num, int offset, int val) +{ + if((bram_num < 0) || (bram_num > 2)) { + printf("bram_num %d out of range\n", bram_num); + return; + } + if((offset < 0) || (offset > 0x100)) { + printf("bram offset %05x out of range\n", offset); + return; + } + g_bram[bram_num][offset] = val; +} + +void +clk_setup_bram_version() +{ + if(g_rom_version < 3) { + g_bram_ptr = (&g_bram[0][0]); // ROM 01 + } else { + g_bram_ptr = (&g_bram[1][0]); // ROM 03 + } +} + +void +clk_write_bram(FILE *fconf) +{ + int i, j, k; + + for(i = 0; i < 2; i++) { + fprintf(fconf, "\n"); + for(j = 0; j < 256; j += 16) { + fprintf(fconf, "bram%d[%02x] =", 2*i + 1, j); + for(k = 0; k < 16; k++) { + fprintf(fconf, " %02x", g_bram[i][j+k]); + } + fprintf(fconf, "\n"); + } + } +} + +void +update_cur_time() +{ + struct tm *tm_ptr; + time_t cur_time, secs, secs2; + + cur_time = time(0); + + /* Figure out the timezone (effectively) by diffing two times. */ + /* this is probably not right for a few hours around daylight savings*/ + /* time transition */ + secs2 = mktime(gmtime(&cur_time)); + tm_ptr = localtime(&cur_time); + secs = mktime(tm_ptr); + + secs2 = secs2 - secs; // this is the timezone offset +#ifdef MAC + /* Mac OS X's mktime function modifies the tm_ptr passed in for */ + /* the CDT timezone and breaks this algorithm. So on a Mac, we */ + /* will use the tm_ptr->gmtoff member to correct the time */ + secs = secs + tm_ptr->tm_gmtoff; +#else + secs = cur_time - secs2; + + if(tm_ptr->tm_isdst) { + /* adjust for daylight savings time */ + secs += 3600; + } +#endif + + /* add in secs to make date based on Apple Jan 1, 1904 instead of */ + /* Unix's Jan 1, 1970 */ + /* So add in 66 years and 17 leap year days (1904 is a leap year) */ + secs += ((66*365) + 17) * (24*3600); + + g_clk_cur_time = (word32)secs; + + clk_printf("Update g_clk_cur_time to %08x\n", g_clk_cur_time); + g_clk_next_vbl_update = g_vbl_count + 5; +} + +/* clock_update called by sim65816 every VBL */ +void +clock_update() +{ + /* Nothing to do */ +} + +void +clock_update_if_needed() +{ + int diff; + + diff = g_clk_next_vbl_update - g_vbl_count; + if(diff < 0 || diff > 60) { + /* Been a while, re-read the clock */ + update_cur_time(); + } +} + +void +clock_write_c034(word32 val) +{ + g_c034_val = val & 0x7f; + if((val & 0x80) != 0) { + if((val & 0x20) == 0) { + printf("c034 write not last = 1\n"); + /* set_halt(1); */ + } + do_clock_data(); + } +} + + +void +do_clock_data() +{ + word32 mask, read, op; + + clk_printf("In do_clock_data, g_clk_mode: %02x\n", g_clk_mode); + + read = g_c034_val & 0x40; + switch(g_clk_mode) { + case CLK_IDLE: + g_clk_read = (g_c033_data >> 7) & 1; + g_clk_reg1 = (g_c033_data >> 2) & 3; + op = (g_c033_data >> 4) & 7; + if(!read) { + /* write */ + switch(op) { + case 0x0: /* Read/write seconds register */ + g_clk_mode = CLK_TIME; + clock_update_if_needed(); + break; + case 0x3: /* internal registers */ + g_clk_mode = CLK_INTERNAL; + if(g_clk_reg1 & 0x2) { + /* extend BRAM read */ + g_clk_mode = CLK_BRAM2; + g_clk_reg1 = (g_c033_data & 7) << 5; + } + break; + case 0x2: /* read/write ram 0x10-0x13 */ + g_clk_mode = CLK_BRAM1; + g_clk_reg1 += 0x10; + break; + case 0x4: /* read/write ram 0x00-0x0f */ + case 0x5: case 0x6: case 0x7: + g_clk_mode = CLK_BRAM1; + g_clk_reg1 = (g_c033_data >> 2) & 0xf; + break; + default: + halt_printf("Bad c033_data in CLK_IDLE: %02x\n", + g_c033_data); + } + } else { + printf("clk read from IDLE mode!\n"); + /* set_halt(1); */ + g_clk_mode = CLK_IDLE; + } + break; + case CLK_BRAM2: + if(!read) { + /* get more bits of bram addr */ + if((g_c033_data & 0x83) == 0x00) { + /* more address bits */ + g_clk_reg1 |= ((g_c033_data >> 2) & 0x1f); + g_clk_mode = CLK_BRAM1; + } else { + halt_printf("CLK_BRAM2: c033_data: %02x!\n", + g_c033_data); + g_clk_mode = CLK_IDLE; + } + } else { + halt_printf("CLK_BRAM2: clock read!\n"); + g_clk_mode = CLK_IDLE; + } + break; + case CLK_BRAM1: + /* access battery ram addr g_clk_reg1 */ + if(read) { + if(g_clk_read) { + /* Yup, read */ + g_c033_data = g_bram_ptr[g_clk_reg1]; + clk_printf("Reading BRAM loc %02x: %02x\n", + g_clk_reg1, g_c033_data); + } else { + halt_printf("CLK_BRAM1: said wr, now read\n"); + } + } else { + if(g_clk_read) { + halt_printf("CLK_BRAM1: said rd, now write\n"); + } else { + /* Yup, write */ + clk_printf("Writing BRAM loc %02x with %02x\n", + g_clk_reg1, g_c033_data); + g_bram_ptr[g_clk_reg1] = g_c033_data; + g_config_kegs_update_needed = 1; + } + } + g_clk_mode = CLK_IDLE; + break; + case CLK_TIME: + if(read) { + if(g_clk_read == 0) { + halt_printf("Reading time, but in set mode!\n"); + } + g_c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) & + 0xff; + clk_printf("Returning time byte %d: %02x\n", + g_clk_reg1, g_c033_data); + } else { + /* Write */ + if(g_clk_read) { + halt_printf("Write time, but in read mode!\n"); + } + clk_printf("Writing TIME loc %d with %02x\n", + g_clk_reg1, g_c033_data); + mask = 0xff << (8 * g_clk_reg1); + + g_clk_cur_time = (g_clk_cur_time & (~mask)) | + ((g_c033_data & 0xff) << (8 * g_clk_reg1)); + } + g_clk_mode = CLK_IDLE; + break; + case CLK_INTERNAL: + if(read) { + printf("Attempting to read internal reg %02x!\n", + g_clk_reg1); + } else { + switch(g_clk_reg1) { + case 0x0: /* test register */ + if(g_c033_data & 0xc0) { + printf("Writing test reg: %02x!\n", + g_c033_data); + /* set_halt(1); */ + } + break; + case 0x1: /* write protect reg */ + clk_printf("Writing clk wr_protect: %02x\n", + g_c033_data); + if(g_c033_data & 0x80) { + printf("Stop, wr clk wr_prot: %02x\n", + g_c033_data); + /* set_halt(1); */ + } + break; + default: + halt_printf("Writing int reg: %02x with %02x\n", + g_clk_reg1, g_c033_data); + } + } + g_clk_mode = CLK_IDLE; + break; + default: + halt_printf("clk mode: %d unknown!\n", g_clk_mode); + g_clk_mode = CLK_IDLE; + break; + } +} + diff --git a/gsplus/src/comp_swift b/gsplus/src/comp_swift new file mode 100644 index 0000000..151b41b --- /dev/null +++ b/gsplus/src/comp_swift @@ -0,0 +1,13 @@ +#!/bin/bash +# $KmKId: comp_swift,v 1.2 2020-12-11 22:58:32+00 kentd Exp $ + +echo "args are: " "$@" +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c \ + -enable-objc-interop \ + -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \ + -swift-version 4 -Onone \ + -serialize-debugging-options \ + -import-objc-header Kegs-Bridging-Header.h \ + -module-name Kegs \ + "$@" + diff --git a/gsplus/src/compile_time.c b/gsplus/src/compile_time.c new file mode 100644 index 0000000..7b4a63d --- /dev/null +++ b/gsplus/src/compile_time.c @@ -0,0 +1,4 @@ + + +char g_compile_time[] = "Compiled: " __DATE__ " " __TIME__ ; + diff --git a/gsplus/src/config.c b/gsplus/src/config.c new file mode 100644 index 0000000..f5c894b --- /dev/null +++ b/gsplus/src/config.c @@ -0,0 +1,4614 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// g_cfg_slotdrive: 0: not doing file selection at all +// 1-0x7ff: doing file selection for given slot/drive +// 0xfff: doing file selection for ROM or charrom +#include "defc.h" +#include +#include "config.h" + +#ifdef _WIN32 +# include "win_dirent.h" +#else +# include +#endif + +extern int Verbose; +extern word32 g_vbl_count; + +extern int g_track_bytes_35[]; +extern int g_c031_disk35; + +extern int g_cur_a2_stat; +extern byte *g_slow_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern double g_cur_dcycs; +extern int g_rom_version; + +extern word32 g_adb_repeat_vbl; +extern int g_adb_swap_command_option; + +extern int g_limit_speed; +extern int g_zip_speed_mhz; +extern int g_force_depth; +int g_serial_cfg[2] = { 0, 1 }; // Slot 1=0=Real serial (printer?) + // Slot 2=1=Virt Modem +int g_serial_mask[2] = { 0, 0 }; +char *g_serial_remote_ip[2] = { "", "" }; // cfg_init_menus will malloc() +int g_serial_remote_port[2] = { 9100, 9100 }; +char *g_serial_device[2] = { "/dev/tty.USB.0", "/dev/tty.USB.1" }; + // cfg_init_menus() will malloc() the above +int g_serial_win_device[2] = { 0, 0 }; // Disabled +int g_serial_modem_response_code = 10; // 10 - 2400 +int g_serial_modem_allow_incoming = 0; // 1 for BBS'es +int g_serial_modem_init_telnet = 1; // 1 for BBS'es + +extern word32 g_mem_size_base; +extern word32 g_mem_size_exp; +extern int g_video_line_update_interval; +extern int g_user_halt_bad; +extern int g_joystick_type; +extern int g_joystick_scale_factor_x; +extern int g_joystick_scale_factor_y; +extern int g_joystick_trim_amount_x; +extern int g_joystick_trim_amount_y; +extern int g_swap_paddles; +extern int g_invert_paddles; +extern int g_voc_enable; +extern int g_status_enable; +extern int g_mainwin_width; +extern int g_mainwin_height; +extern int g_mainwin_xpos; +extern int g_mainwin_ypos; + +extern int g_screen_index[]; +extern word32 g_full_refresh_needed; +extern word32 g_a2_screen_buffer_changed; + +extern int g_key_down; +extern const char g_kegs_version_str[]; + +int g_config_control_panel = 0; +char g_config_kegs_name[1024] = { 0 }; +char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; + +int g_config_kegs_auto_update = 1; +int g_config_kegs_update_needed = 0; +int g_cfg_newdisk_select = 0; +int g_cfg_newdisk_blocks = 0; +int g_cfg_newdisk_blocks_default = 140*2; +int g_cfg_newdisk_type = 1; +int g_cfg_newdisk_type_default = 1; +word32 g_cfg_newdisk_slotdrive = 0; + +const char *g_config_kegs_name_list[] = { + "config.kegs", "kegs_conf", ".config.kegs", 0 +}; + +int g_highest_smartport_unit = -1; +int g_reparse_delay = 0; +int g_user_page2_shadow = 1; + +char g_cfg_printf_buf[CFG_PRINTF_BUFSIZE]; +char g_config_kegs_buf[CONF_BUF_LEN]; + +#define CFG_ERR_BUFSIZE 80 +#define CFG_ERR_MAX 5 + +int g_cfg_err_pos = 0; +char g_cfg_err_bufs[CFG_ERR_MAX][CFG_ERR_BUFSIZE]; + +int g_cfg_curs_x = 0; +int g_cfg_curs_y = 0; +int g_cfg_curs_inv = 0; +int g_cfg_curs_mousetext = 0; +int g_cfg_screen_changed = 0; +byte g_cfg_screen[24][80]; + +#if defined(MAC) || defined(_WIN32) +int g_cfg_ignorecase = 1; // Ignore case in filenames +#else +int g_cfg_ignorecase = 0; +#endif + +#define CFG_MAX_OPTS 34 +#define CFG_OPT_MAXSTR 100 + +int g_cfg_opts_vals[CFG_MAX_OPTS]; +char g_cfg_opts_str[CFG_PATH_MAX]; +char g_cfg_opt_buf[CFG_OPT_MAXSTR]; +char g_cfg_edit_buf[CFG_OPT_MAXSTR]; + +char *g_cfg_rom_path = "ROM"; // config_init_menus will malloc +char *g_cfg_charrom_path = "Undefined"; // config_init_menus will malloc +int g_cfg_charrom_pos = 0; +char *g_cfg_file_def_name = "Undefined"; +char **g_cfg_file_strptr = 0; +int g_cfg_file_min_size = 1024; +int g_cfg_file_max_size = 2047*1024*1024; +int g_cfg_edit_type = 0; +void *g_cfg_edit_ptr = 0; + +#define MAX_PARTITION_BLK_SIZE 65536 + +char *g_argv0_path = "."; + +const char *g_kegs_default_paths[] = { "", "./", "${HOME}/", + "${HOME}/Library/KEGS/", "${0}/../", "${0}/", + "${0}/Contents/Resources/", 0 }; + + +extern Cfg_menu g_cfg_main_menu[]; + +#define KNMP(a) &a, #a, 0 +#define KNM(a) &a, #a + +Cfg_menu g_cfg_disk_menu[] = { +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "s5d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x5000 }, +{ "s5d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x5010 }, +{ "", 0, 0, 0, 0 }, +{ "s6d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x6000 }, +{ "s6d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x6010 }, +{ "", 0, 0, 0, 0 }, +{ "s7d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x7000 }, +{ "s7d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x7010 }, +{ "s7d3 = ", 0, 0, 0, CFGTYPE_DISK + 0x7020 }, +{ "s7d4 = ", 0, 0, 0, CFGTYPE_DISK + 0x7030 }, +{ "s7d5 = ", 0, 0, 0, CFGTYPE_DISK + 0x7040 }, +{ "s7d6 = ", 0, 0, 0, CFGTYPE_DISK + 0x7050 }, +{ "s7d7 = ", 0, 0, 0, CFGTYPE_DISK + 0x7060 }, +{ "s7d8 = ", 0, 0, 0, CFGTYPE_DISK + 0x7070 }, +{ "s7d9 = ", 0, 0, 0, CFGTYPE_DISK + 0x7080 }, +{ "s7d10= ", 0, 0, 0, CFGTYPE_DISK + 0x7090 }, +{ "s7d11= ", 0, 0, 0, CFGTYPE_DISK + 0x70a0 }, +{ "s7d12= ", 0, 0, 0, CFGTYPE_DISK + 0x70b0 }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_newslot6_menu[] = { +{ "New 5.25\" disk image Configuration", g_cfg_newslot6_menu, 0, 0, + CFGTYPE_MENU }, +{ "size,280,140KB", KNM(g_cfg_newdisk_blocks), + &g_cfg_newdisk_blocks_default, CFGTYPE_INT }, +{ "Type,1,ProDOS/DOS 3.3,2,WOZ image,3,Dynamic ProDOS directory", + KNM(g_cfg_newdisk_type), + &g_cfg_newdisk_type_default, CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Create and name the image", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Cancel, go back to Disk Config", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_newslot5_menu[] = { +{ "New 3.5\" disk image Configuration", g_cfg_newslot5_menu, 0, 0,CFGTYPE_MENU}, +{ "size,1600,800KB", KNM(g_cfg_newdisk_blocks), + &g_cfg_newdisk_blocks_default, CFGTYPE_INT }, +{ "Type,1,ProDOS,2,WOZ image,3,Dynamic ProDOS directory", + KNM(g_cfg_newdisk_type), + &g_cfg_newdisk_type_default, CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Create and name the image", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Cancel, go back to Disk Config", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_newslot7_menu[] = { +{ "New Smartport disk image Configuration", g_cfg_newslot7_menu, 0, 0, + CFGTYPE_MENU}, +{ "size,1600,800KB,3200,1600KB,16384,8MB,32768,16MB,65535,32MB", + KNM(g_cfg_newdisk_blocks), &g_cfg_newdisk_blocks_default, + CFGTYPE_INT }, +{ "Type,1,ProDOS,3,Dynamic ProDOS directory", KNM(g_cfg_newdisk_type), + &g_cfg_newdisk_type_default, CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Create and name the image", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Cancel, go back to Disk Config", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_joystick_menu[] = { +{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, +{ "Joystick Emulation,0,Keypad Joystick,1,Mouse Joystick,2,Native Joystick 1," + "3,Native Joystick 2", KNMP(g_joystick_type), CFGTYPE_INT }, +{ "Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%," + "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", + KNMP(g_joystick_scale_factor_x), CFGTYPE_INT }, +{ "Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%," + "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", + KNMP(g_joystick_scale_factor_y), CFGTYPE_INT }, +{ "Joystick Trim X", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT }, +{ "Joystick Trim Y", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT }, +{ "Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped", + KNMP(g_swap_paddles), CFGTYPE_INT }, +{ "Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down", + KNMP(g_invert_paddles), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_rom_menu[] = { +{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, +{ "ROM File", KNMP(g_cfg_rom_path), CFGTYPE_FILE }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_charrom_menu[] = { +{ "Character ROM File Selection", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU }, +{ "Character ROM File", KNMP(g_cfg_charrom_path), CFGTYPE_FILE }, +{ "Character Set,0,0x00 US Enhanced,1,0x01 US Un-enhanced," + "2,0x02 Clinton Turner V1 Enhanced,3,0x03 ReActiveMicro Enhanced," + "4,0x04 Dan Paymar Enhanced,5,0x05 Blippo Black Enhanced," + "6,0x06 Byte Enhanced,7,0x07 Colossal Enhanced," + "8,0x08 Count Enhanced,9,0x09 Flow Enhanced," + "10,0x0a Gothic Enhanced,11,0x0b Outline Enhanced," + "12,0x0c Pigfont Enhanced,13,0x0d Pinocchio Enhanced," + "14,0x0e Slant Enhanced,15,0x0f Stop Enhanced," + "16,0x10 Euro Un-Enhanced,17,0x11 Euro Enhanced," + "18,0x12 Clinton Turner V2 Enhanced,19,0x13 Improved German Enhanced," + "20,0x14 Improved German Un-Enhanced,21,0x15 Franch Canadian Enhanced," + "22,0x16 French Canadian Un-Enhanced,23,0x17 Hebrew Enhanced," + "24,0x18 Hebrew Un-Enhanced,25,0x19 Apple II+ Enhanced," + "26,0x1a Apple II+ Un-Enhanced,27,0x1b Katakana Enhanced," + "28,0x1c Cyrillic Enhanced,29,0x1d Greek Enhanced," + "30,0x1e Esperanto Enhanced,31,0x1f Videx Enhanced", + KNMP(g_cfg_charrom_pos), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_serial_menu[] = { +{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +{ "Slot 1 (port 0) settings", 0, 0, 0, 0 }, +{ " Main setting ,0,Use Real Device below,1,Use a virtual modem," + "2,Use Remote IP below,3,Use incoming port 6501", + KNMP(g_serial_cfg[0]), CFGTYPE_INT }, +{ " Status ", (void *)cfg_get_serial0_status, 0, 0, CFGTYPE_FUNC }, +{ " Real Device ", KNMP(g_serial_device[0]), CFGTYPE_FILE }, +{ " Windows Device,0,Disabled,1,COM1,2,COM2,3,COM3,4,COM4", + KNMP(g_serial_win_device[0]), CFGTYPE_INT }, +{ " Remote IP ", KNMP(g_serial_remote_ip[0]), CFGTYPE_STR }, +{ " Remote Port ", KNMP(g_serial_remote_port[0]), CFGTYPE_INT }, +{ " Serial Mask ,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_serial_mask[0]), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Slot 2 (port 1) settings", 0, 0, 0, 0, }, +{ " Main setting ,0,Use Real Device below,1,Use a virtual modem," + "2,Use Remote IP below,3,Use incoming port 6502", + KNMP(g_serial_cfg[1]), CFGTYPE_INT }, +{ " Status ", (void *)cfg_get_serial1_status, 0, 0, CFGTYPE_FUNC }, +{ " Real Device ", KNMP(g_serial_device[1]), CFGTYPE_FILE }, +{ " Windows Device,1,COM1,2,COM2,3,COM3,4,COM4", + KNMP(g_serial_win_device[1]), CFGTYPE_INT }, +{ " Remote IP ", KNMP(g_serial_remote_ip[1]), CFGTYPE_STR }, +{ " Remote Port ", KNMP(g_serial_remote_port[1]), CFGTYPE_INT }, +{ " Serial Mask ,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_serial_mask[1]), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_modem_menu[] = { +{ "Virtual Modem Configuration", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU }, +{ "Modem Speed Response Code ,5,5 - CONNECT 1200,10,10 - CONNECT 2400," + "12,12 - CONNECT 9600 (HAYES/Warp6)," + "13,13 - CONNECT 9600 (USR/HST)," + "14,14 - CONNECT 19200 (HAYES/Warp6)," + "28,28 - CONNECT 38400 (HAYES/Warp6)", + KNMP(g_serial_modem_response_code), CFGTYPE_INT }, +{ "Allow Modem incoming on 6501/6502 ,0,Outgoing only," + "1,Incoming and outgoing (BBS)", + KNMP(g_serial_modem_allow_incoming), CFGTYPE_INT }, +{ "Send Telnet Escape codes ,0,Disable Telnet,1,Send Telnet codes", + KNMP(g_serial_modem_init_telnet), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + + + +Cfg_menu g_cfg_video_menu[] = { +{ "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, +{ "Enable VOC,0,Disabled,1,Enabled", KNMP(g_voc_enable), CFGTYPE_INT }, +{ "Default Main Window width", KNMP(g_mainwin_width), CFGTYPE_INT }, +{ "Default Main Window height", KNMP(g_mainwin_height), CFGTYPE_INT }, +{ "Main Window X position", KNMP(g_mainwin_xpos), CFGTYPE_INT }, +{ "Main Window Y position", KNMP(g_mainwin_ypos), CFGTYPE_INT }, +{ "3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line)," + "8,Off (Update video every 8 lines)", + KNMP(g_video_line_update_interval), CFGTYPE_INT }, +{ "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_main_menu[] = { +{ "KEGS Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, +{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, +{ "Character ROM Selection", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU }, +{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Modem Configuration", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU }, +{ "Video Settings", g_cfg_video_menu, 0, 0, CFGTYPE_MENU }, +{ "Auto-update config.kegs,0,Manual,1,Immediately", + KNMP(g_config_kegs_auto_update), CFGTYPE_INT }, +{ "Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)", + KNMP(g_limit_speed), CFGTYPE_INT }, +{ "ZipGS Speed,8,8MHz,16,16MHz,32,32MHz,64,64MHz,128,128MHz", + KNMP(g_zip_speed_mhz), CFGTYPE_INT }, +{ "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," + "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," + "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, +{ "Show Status lines,0,Disabled,1,Enabled", KNMP(g_status_enable), CFGTYPE_INT}, +{ "Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad " + "accesses", KNMP(g_user_halt_bad), CFGTYPE_INT }, +{ "Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware)," + "1,Enabled on ROM 01 and 03", + KNMP(g_user_page2_shadow), CFGTYPE_INT }, +{ "Swap Command/Option keys,0,Disabled,1,Swapped", + KNMP(g_adb_swap_command_option), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Save changes to config.kegs", (void *)config_write_config_kegs_file, 0, 0, + CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, +{ 0, 0, 0, 0, 0 }, +}; + + +#define CFG_MAX_DEFVALS 128 +Cfg_defval g_cfg_defvals[CFG_MAX_DEFVALS]; +int g_cfg_defval_index = 0; + +word32 g_cfg_slotdrive = 0; +int g_cfg_select_partition = -1; +char g_cfg_tmp_path[CFG_PATH_MAX]; +char g_cfg_file_path[CFG_PATH_MAX]; +char g_cfg_file_cachedpath[CFG_PATH_MAX]; +char g_cfg_file_cachedreal[CFG_PATH_MAX]; +char g_cfg_file_curpath[CFG_PATH_MAX]; +char g_cfg_file_shortened[CFG_PATH_MAX]; +char g_cfg_file_match[CFG_PATH_MAX]; + +char g_cfg_part_path[CFG_PATH_MAX]; +int g_cfg_partition_is_zip = 0; + +Cfg_listhdr g_cfg_dirlist = { 0 }; +Cfg_listhdr g_cfg_partitionlist = { 0 }; + +int g_cfg_file_pathfield = 0; + +const char *g_kegs_rom_names[] = { "ROM", "ROM", "ROM.01", "ROM.03", + "APPLE2GS.ROM", "APPLE2GS.ROM2", "xgs.rom", "XGS.ROM", "Rom03gd", + "342-0077-b", // MAME ROM.01 + 0 }; + /* First entry is special--it will be overwritten by g_cfg_rom_path */ + +const char *g_kegs_c1rom_names[] = { 0 }; +const char *g_kegs_c2rom_names[] = { 0 }; +const char *g_kegs_c3rom_names[] = { 0 }; +const char *g_kegs_c4rom_names[] = { 0 }; +const char *g_kegs_c5rom_names[] = { 0 }; +const char *g_kegs_c6rom_names[] = { "c600.rom", "controller.rom", "disk.rom", + "DISK.ROM", "diskII.prom", 0 }; +const char *g_kegs_c7rom_names[] = { 0 }; + +const char **g_kegs_rom_card_list[8] = { + 0, g_kegs_c1rom_names, + g_kegs_c2rom_names, g_kegs_c3rom_names, + g_kegs_c4rom_names, g_kegs_c5rom_names, + g_kegs_c6rom_names, g_kegs_c7rom_names }; + +byte g_rom_c600_rom01_diffs[256] = { + 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, + 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, + 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, + 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, + 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, + 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, + 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, + 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, + 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, + 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, + 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, + 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 +}; + +byte g_rom_c700[256] = { + //0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x3c, // For Apple //e + 0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x00, + //^^= LDX #$20; LDY #$00, LDX #$03 CMP #$3c + 0x80, 0x0c, 0x18, 0xb8, 0x70, 0x38, 0xb8, 0x42, + //^^= BRA $c716; CLC; CLV; BVS $c746 (SEC); CLV; WDM $c7,$00 + 0xc7, 0x00, 0x60, 0x00, 0x00, 0xea, 0xe2, 0x41, + //^^= ...; RTS..............; NOP; SEP #$41 + 0x70, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + //^^= BVS $c70f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // So does WDM $c7,$00 with psr.v=1 for $c700; v=0,c=0 for $c70a, + // and v=0,c=1 for $c70d + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xbf, 0x0a +}; + +Cfg_menu *g_menuptr = 0; +int g_menu_line = 1; +int g_menu_inc = 1; +int g_menu_max_line = 1; +int g_menu_redraw_needed = 1; + +#define MAX_CFG_ARGV_OVERRIDES 64 + +int g_cfg_argv_num_overrides = 0; +char *g_cfg_argv_overrides[MAX_CFG_ARGV_OVERRIDES]; + +int +config_add_argv_override(const char *str1, const char *str2) +{ + const char *equal_ptr; + char *str; + int ret, pos, len; + + // Handle things like "rom=rompath" and "rom", "rompath" + // Look through str1, see if there is '=', if so ignore str2 + equal_ptr = strchr(str1, '='); + ret = 1; + if(equal_ptr) { // str1 has '=' in it + ret = 0; // Don't eat up str2 argument + str = kegs_malloc_str(str1); + } else { + // We need to form a new string of str1, =, str2 + len = (int)(strlen(str1) + strlen(str2) + 2); + str = malloc(len); + cfg_strncpy(str, str1, len); + cfg_strlcat(str, "=", len); + cfg_strlcat(str, str2, len); + } + pos = g_cfg_argv_num_overrides++; + if(pos >= MAX_CFG_ARGV_OVERRIDES) { + g_cfg_argv_num_overrides = MAX_CFG_ARGV_OVERRIDES; + fatal_printf("MAX_CFG_ARGV_OVERRIDES overflow\n"); + my_exit(5); + return ret; + } + g_cfg_argv_overrides[pos] = str; + printf("Added config override %d, %s\n", pos, str); + + return ret; +} + +void +config_set_config_kegs_name(const char *str1) +{ + int maxlen; + + // Command line override "-cfg cfg_file" + g_config_kegs_name[0] = 0; + maxlen = (int)sizeof(g_config_kegs_name); + cfg_strncpy(&g_config_kegs_name[0], str1, maxlen); +} + +void +config_init_menus(Cfg_menu *menuptr) +{ + void *voidptr; + const char *name_str; + Cfg_defval *defptr; + char **str_ptr; + char *str; + int type, pos, val; + + if(menuptr[0].defptr != 0) { + return; + } + menuptr[0].defptr = (void *)1; + pos = 0; + while(pos < 100) { + type = menuptr->cfgtype; + voidptr = menuptr->ptr; + name_str = menuptr->name_str; + if(menuptr->str == 0) { + break; + } + if(name_str != 0) { + defptr = &(g_cfg_defvals[g_cfg_defval_index++]); + if(g_cfg_defval_index >= CFG_MAX_DEFVALS) { + fatal_printf("CFG_MAX_DEFVAL overflow\n"); + my_exit(5); + return; + } + defptr->menuptr = menuptr; + defptr->intval = 0; + defptr->strval = 0; + switch(type) { + case CFGTYPE_INT: + val = *((int *)voidptr); + defptr->intval = val; + menuptr->defptr = &(defptr->intval); + break; + case CFGTYPE_FILE: + case CFGTYPE_STR: + str_ptr = (char **)menuptr->ptr; + str = *str_ptr; + // We need to malloc this string since all + // string values must be dynamically alloced + defptr->strval = str; // this can have a copy + *str_ptr = kegs_malloc_str(str); + menuptr->defptr = &(defptr->strval); + break; + default: + fatal_printf("name_str is %p = %s, but type: " + "%d\n", name_str, name_str, type); + my_exit(5); + return; + } + } + if(type == CFGTYPE_MENU) { + config_init_menus((Cfg_menu *)voidptr); + } + pos++; + menuptr++; + } +} + +void +config_init() +{ + config_init_menus(g_cfg_main_menu); + + // Find the config.kegs file + if(g_config_kegs_name[0] == 0) { + cfg_find_config_kegs_file(); + } + + config_parse_config_kegs_file(); +} + +void +cfg_find_config_kegs_file() +{ + const char **path_ptr; + int maxlen, fd; + + g_config_kegs_name[0] = 0; + maxlen = sizeof(g_config_kegs_name); + fd = 0; + if(!config_setup_kegs_file(&g_config_kegs_name[0], maxlen, + &g_config_kegs_name_list[0])) { + // Try to create config.kegs + fd = -1; + path_ptr = &g_kegs_default_paths[0]; + while(*path_ptr) { + config_expand_path(&g_config_kegs_name[0], *path_ptr, + maxlen); + cfg_strlcat(&g_config_kegs_name[0], "config.kegs", + maxlen); + printf("Trying to create %s\n", &g_config_kegs_name[0]); + fd = open(&g_config_kegs_name[0], + O_CREAT | O_TRUNC | O_WRONLY, 0x1b6); + close(fd); + if(fd >= 0) { + break; + } + path_ptr++; + } + } + + if(fd < 0) { + fatal_printf("Could not create config.kegs!\n"); + my_exit(2); + } +} + +int +config_setup_kegs_file(char *outname, int maxlen, const char **name_ptr) +{ + struct stat stat_buf; + const char **path_ptr, **cur_name_ptr; + mode_t fmt; + int ret, len; + + outname[0] = 0; + + path_ptr = &g_kegs_default_paths[0]; // Array of strings + + while(*path_ptr) { + len = config_expand_path(outname, *path_ptr, maxlen); + if(len != (int)strlen(outname)) { + printf("config_expand_path ret %d, but strlen:%d!\n", + len, (int)strlen(outname)); + } + cur_name_ptr = name_ptr; + while(*cur_name_ptr && (len < maxlen)) { + outname[len] = 0; + cfg_strlcat(outname, *cur_name_ptr, maxlen); + // printf("Doing stat on %s\n", outname); + ret = cfg_stat(outname, &stat_buf, 0); + if(ret == 0) { + fmt = stat_buf.st_mode & S_IFMT; + if(fmt != S_IFDIR) { + /* got it! */ + return 1; + } + } + cur_name_ptr++; + } + path_ptr++; + } + + return 0; +} + +int +config_expand_path(char *out_ptr, const char *in_ptr, int maxlen) +{ + char name_buf[256]; + char *tmp_ptr; + int name_len, in_char, state, pos; + + out_ptr[0] = 0; + + pos = 0; + name_len = 0; + state = 0; + + /* See if in_ptr has ${} notation, replace with getenv or argv0 */ + while(pos < (maxlen - 1)) { + in_char = *in_ptr++; + out_ptr[pos++] = in_char; + out_ptr[pos] = 0; + if(in_char == 0) { + return pos - 1; + } + if(state == 0) { + /* No $ seen yet, look for it */ + if(in_char == '$') { + state = 1; + } + } else if(state == 1) { + /* See if next char is '{' (dummy }) */ + if(in_char == '{') { /* add dummy } */ + state = 2; + name_len = 0; + pos -= 2; + out_ptr[pos] = 0; + } else { + state = 0; + } + } else if(state == 2) { + /* fill name_buf ... dummy '{' */ + pos--; + out_ptr[pos] = 0; + if(in_char == '}') { + name_buf[name_len] = 0; + + /* got token, now look it up */ + tmp_ptr = ""; + if(!strncmp("0", name_buf, 128)) { + /* Replace ${0} with g_argv0_path */ + tmp_ptr = g_argv0_path; + } else { + tmp_ptr = getenv(name_buf); + if(tmp_ptr == 0) { + tmp_ptr = ""; + } + } + pos = cfg_strlcat(out_ptr, tmp_ptr, maxlen); + state = 0; + } else { + name_buf[name_len++] = in_char; + if(name_len >= 250) { + name_len--; + } + } + } + } + + return pos; +} + +char * +cfg_exit(int get_status) +{ + /* printf("In cfg exit\n"); */ + if(get_status) { + return 0; + } + cfg_toggle_config_panel(); + + return 0; +} + +void +cfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap) +{ + char *bufptr; + int pos, len, bufsize; + + pos = g_cfg_err_pos; + if(pos >= CFG_ERR_MAX) { + pos = CFG_ERR_MAX - 1; + } + bufptr = &g_cfg_err_bufs[pos][0]; + len = 0; + bufsize = CFG_ERR_BUFSIZE; + if(pre_str && *pre_str) { + cfg_strncpy(bufptr, pre_str, CFG_ERR_BUFSIZE); + cfg_strlcat(bufptr, " error: ", CFG_ERR_BUFSIZE); + len = (int)strlen(bufptr); + bufsize = CFG_ERR_BUFSIZE - len; + } + if(bufsize > 0) { + vsnprintf(&bufptr[len], bufsize, fmt, ap); + } + + fputs(bufptr, stderr); + g_cfg_err_pos = pos + 1; +} + +void +cfg_err_printf(const char *pre_str, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + cfg_err_vprintf(pre_str, fmt, ap); + va_end(ap); +} + +void +cfg_toggle_config_panel() +{ + int panel; + + panel = !g_config_control_panel; + if(g_rom_version < 0) { + panel = 1; /* Stay in config mode */ + } + if(panel != g_config_control_panel) { + cfg_set_config_panel(panel); + } +} + +void +cfg_set_config_panel(int panel) +{ + int i; + + g_config_control_panel = panel; + if(panel) { + // Entering configuration panel + video_force_reparse(); + + cfg_printf("Entering config_control_panel\n"); + + for(i = 0; i < 20; i++) { + // Toss any queued-up keypresses + if(adb_read_c000() & 0x80) { + (void)adb_access_c010(); + } + } + // HACK: Force adb keyboard (and probably mouse) to "normal"... + + cfg_home(); + + g_menu_line = 1; + g_menu_inc = 1; + g_menu_redraw_needed = 1; + g_cfg_slotdrive = 0; + g_cfg_newdisk_select = 0; + g_cfg_select_partition = -1; + } else { + // Leave config panel, go back to A2 emulation + + video_force_reparse(); + } + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + g_adb_repeat_vbl = g_vbl_count + 60; +} + +char * +cfg_text_screen_dump(int get_status) +{ + FILE *ofile; + char *bufptr; + char *filename; + + if(get_status) { + return 0; + } + filename = "kegs.screen.dump"; + printf("Writing text screen to the file %s\n", filename); + ofile = fopen(filename, "w"); + if(ofile == 0) { + fatal_printf("Could not write to file %s, (%d)\n", filename, + errno); + return 0; + } + bufptr = cfg_text_screen_str(); + fputs(bufptr, ofile); + fclose(ofile); + return 0; +} + +char g_text_screen_buf[2100] = { 0 }; + +char * +cfg_text_screen_str() +{ + char *bufptr; + int pos, start_pos, c, offset; + int i, j; + + // bufptr must be at least (81*24)+2 characters + bufptr = &g_text_screen_buf[0]; + pos = 0; + for(i = 0; i < 24; i++) { + start_pos = pos; + for(j = 0; j < 40; j++) { + offset = g_screen_index[i] + j; + if(g_cur_a2_stat & ALL_STAT_VID80) { + c = g_slow_memory_ptr[0x10400 + offset] & 0x7f; + if(c < 0x20) { + c += 0x40; + } + bufptr[pos++] = c; + } + c = g_slow_memory_ptr[0x0400 + offset] & 0x7f; + if(c < 0x20) { + c += 0x40; + } + if(c == 0x7f) { + c = ' '; + } + bufptr[pos++] = c; + } + while((pos > start_pos) && (bufptr[pos-1] == ' ')) { + /* try to strip out trailing spaces */ + pos--; + } + bufptr[pos++] = '\n'; + bufptr[pos] = 0; + } + + return bufptr; +} + +char * +cfg_get_serial0_status(int get_status) +{ + return scc_get_serial_status(get_status, 0); +} + +char * +cfg_get_serial1_status(int get_status) +{ + return scc_get_serial_status(get_status, 1); +} + +char * +cfg_get_current_copy_selection() +{ + return &g_text_screen_buf[0]; +} + +void +config_vbl_update(int doit_3_persec) +{ + if(doit_3_persec) { + if(g_config_kegs_auto_update && g_config_kegs_update_needed) { + (void)config_write_config_kegs_file(0); + } + } + return; +} + +void +cfg_file_update_rom(const char *str) +{ + cfg_file_update_ptr(&g_cfg_rom_path, str, 1); +} + +void +cfg_file_update_ptr(char **strptr, const char *str, int need_update) +{ + char *newstr; + int remote_changed, serial_changed; + int i; + + // Update whatever g_cfg_file_strptr points to. If changing + // ROM path or Charrom, then do the proper updates + + newstr = kegs_malloc_str(str); + if(!strptr) { + return; + } + if(*strptr) { + free(*strptr); + } + *strptr = newstr; + if(strptr == &(g_cfg_rom_path)) { + printf("Updated ROM file\n"); + load_roms_init_memory(); + do_reset(); + } + if(strptr == &(g_cfg_charrom_path)) { + printf("Updated Char ROM file\n"); + cfg_load_charrom(); + } + for(i = 0; i < 2; i++) { + remote_changed = 0; + serial_changed = 0; + if(strptr == &g_serial_remote_ip[i]) { + remote_changed = 1; + } + if(strptr == &g_serial_device[i]) { + serial_changed = 1; + } + if(remote_changed || serial_changed) { + scc_config_changed(i, 0, remote_changed, + serial_changed); + } + } + if(need_update) { + g_config_kegs_update_needed = 1; + printf("Set g_config_kegs_update_needed = 1\n"); + } +} + +void +cfg_int_update(int *iptr, int new_val) +{ + int old_val, cfg_changed, remote_changed, serial_changed; + int i; + + // Called to handle an integer being changed in the F4 config menus + // where it's value may need special handling + + old_val = *iptr; + *iptr = new_val; + if(old_val == new_val) { + return; + } + if(iptr == &g_cfg_charrom_pos) { + cfg_load_charrom(); + } + + for(i = 0; i < 2; i++) { + remote_changed = 0; + serial_changed = 0; + cfg_changed = 0; + if(iptr == &g_serial_cfg[i]) { + cfg_changed = 1; + } + if(iptr == &g_serial_remote_port[i]) { + remote_changed = 1; + } + if(iptr == &g_serial_win_device[i]) { + serial_changed = 1; + } + if(cfg_changed || remote_changed || serial_changed) { + scc_config_changed(i, cfg_changed, remote_changed, + serial_changed); + } + } +} + +void +cfg_load_charrom() +{ + byte buffer[4096]; + dword64 dsize, dret; + word32 upos; + int fd; + + printf("Loading character ROM from: %s\n", g_cfg_charrom_path); + fd = open(g_cfg_charrom_path, O_RDONLY | O_BINARY); + if(fd < 0) { + printf("Cannot open %s\n", g_cfg_charrom_path); + return; + } + dsize = cfg_get_fd_size(fd); + upos = g_cfg_charrom_pos * 0x1000U; + if(dsize < (upos + 0x1000)) { + g_cfg_charrom_pos = 0; + return; + } + dret = cfg_read_from_fd(fd, &buffer[0], upos, 4096); + if(dret != 0) { + prepare_a2_romx_font(&buffer[0]); + } +} + +void +config_load_roms() +{ + struct stat stat_buf; + const char **names_ptr; + int more_than_8mb, changed_rom, len, fd, ret; + int i; + + g_rom_version = -1; + + /* set first entry of g_kegs_rom_names[] to g_cfg_rom_path so that */ + /* it becomes the first place searched. */ + g_kegs_rom_names[0] = g_cfg_rom_path; + ret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, + &g_kegs_rom_names[0]); + if(ret == 0) { + // Just get out, let config interface select ROM + g_config_control_panel = 1; + printf("No ROM, set g_config_control_panel=1\n"); + return; + } + printf("Found ROM at path: %s\n", g_cfg_tmp_path); + fd = open(&g_cfg_tmp_path[0], O_RDONLY | O_BINARY); + if(fd < 0) { + fatal_printf("Open ROM file %s failed:%d, errno:%d\n", + &g_cfg_tmp_path[0], fd, errno); + g_config_control_panel = 1; + return; + } + + ret = fstat(fd, &stat_buf); + if(ret != 0) { + fatal_printf("fstat returned %d on fd %d, errno: %d\n", + ret, fd, errno); + g_config_control_panel = 1; + return; + } + + len = (int)stat_buf.st_size; + memset(&g_rom_fc_ff_ptr[0], 0, 4*65536); + /* Clear banks fc-ff to 0 */ + if(len == 32*1024) { // Apple //e + g_rom_version = 0; + g_mem_size_base = 128*1024; + ret = (int)read(fd, &g_rom_fc_ff_ptr[3*65536 + 32768], len); + } else if(len == 128*1024) { + g_rom_version = 1; + g_mem_size_base = 128*1024; + ret = (int)read(fd, &g_rom_fc_ff_ptr[2*65536], len); + } else if(len == 256*1024) { + g_rom_version = 3; + g_mem_size_base = 1024*1024; + ret = (int)read(fd, &g_rom_fc_ff_ptr[0], len); + } else { + fatal_printf("The ROM size should be 128K or 256K, this file " + "is %d bytes\n", len); + g_config_control_panel = 1; + return; + } + + printf("Read: %d bytes of ROM\n", ret); + if(ret != len) { + fatal_printf("errno: %d\n", errno); + g_config_control_panel = 1; + return; + } + close(fd); + + memset(&g_rom_cards_ptr[0], 0, 256*16); + + for(i = 0; i < 256; i++) { + // Place HD PROM in slot 7 + g_rom_cards_ptr[0x700 + i] = g_rom_c700[i]; + // g_rom_cards_ptr[0x500 + i] = g_rom_c700[i]; + } + /* initialize c600 rom to be diffs from the real ROM, to build-in */ + /* Apple II compatibility without distributing ROMs */ + if(g_rom_version == 0) { // Apple //e + for(i = 0; i < 256; i++) { + // Place Disk II PROM in slot 6 + g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x38600+i]; + } + } else { + for(i = 0; i < 256; i++) { + g_rom_cards_ptr[0x600 + i] = + g_rom_fc_ff_ptr[0x3c600 + i] ^ + g_rom_c600_rom01_diffs[i]; + } + } + if(g_rom_version >= 3) { + /* some patches */ + g_rom_cards_ptr[0x61b] ^= 0x40; + g_rom_cards_ptr[0x61c] ^= 0x33; + g_rom_cards_ptr[0x632] ^= 0xc0; + g_rom_cards_ptr[0x633] ^= 0x33; + } + + for(i = 1; i < 8; i++) { + names_ptr = g_kegs_rom_card_list[i]; + if(names_ptr == 0) { + continue; + } + if(*names_ptr == 0) { + continue; + } + + ret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, + names_ptr); + + if(ret != 0) { + fd = open(&(g_cfg_tmp_path[0]), O_RDONLY | O_BINARY); + if(fd < 0) { + fatal_printf("Open card ROM file %s failed: %d " + "err:%d\n", &g_cfg_tmp_path[0], fd, + errno); + continue; + } + + len = 256; + ret = (int)read(fd, &g_rom_cards_ptr[i*0x100], len); + + if(ret != len) { + fatal_printf("While reading card ROM %s, file " + "is too short. (%d) Expected %d bytes, " + "read %d bytes\n", errno, len, ret); + continue; + } + close(fd); + } + } + + more_than_8mb = (g_mem_size_exp > 0x800000); + /* Only do the patch if users wants more than 8MB of expansion mem */ + + changed_rom = 0; + if(g_rom_version == 1) { + /* make some patches to ROM 01 */ +#if 0 + /* 1: Patch ROM selftest to not do speed test */ + printf("Patching out speed test failures from ROM 01\n"); + g_rom_fc_ff_ptr[0x3785a] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* 2: Patch ROM selftests not to do tests 2,4 */ + /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ + g_rom_fc_ff_ptr[0x371e9] = 0xf5; + g_rom_fc_ff_ptr[0x371ea] = 0xff; + changed_rom = 1; +#endif + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30302] = 0xdf; + g_rom_fc_ff_ptr[0x30314] = 0xdf; + g_rom_fc_ff_ptr[0x3031c] = 0x00; + changed_rom = 1; + } + + /* Patch ROM selftest to not do ROM cksum if any changes*/ + if(changed_rom) { + g_rom_fc_ff_ptr[0x37a06] = 0x18; + g_rom_fc_ff_ptr[0x37a07] = 0x18; + } + } else if(g_rom_version == 3) { + /* patch ROM 03 */ + printf("Patching ROM 03 smartport bug\n"); + /* 1: Patch Smartport code to fix a stupid bug */ + /* that causes it to write the IWM status reg into c036, */ + /* which is the system speed reg...it's "safe" since */ + /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ + /* it might have turned on shadowing in all banks! */ + g_rom_fc_ff_ptr[0x357c9] = 0x00; + changed_rom = 1; + +#if 0 + /* patch ROM 03 to not to speed test */ + /* skip fast speed test */ + g_rom_fc_ff_ptr[0x36ad7] = 0x18; + g_rom_fc_ff_ptr[0x36ad8] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* skip slow speed test */ + g_rom_fc_ff_ptr[0x36ae7] = 0x18; + g_rom_fc_ff_ptr[0x36ae8] = 0x6b; + changed_rom = 1; +#endif + +#if 0 + /* 4: Patch ROM 03 selftests not to do tests 1-4 */ + g_rom_fc_ff_ptr[0x364a9] = 0xf0; + g_rom_fc_ff_ptr[0x364aa] = 0xff; + changed_rom = 1; +#endif + + /* ROM tests are in ff/6403-642x, where 6403 = addr of */ + /* test 1, etc. */ + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30b] = 0xdf; + g_rom_fc_ff_ptr[0x31d] = 0xdf; + g_rom_fc_ff_ptr[0x325] = 0x00; + changed_rom = 1; + } + + if(changed_rom) { + /* patch ROM 03 selftest to not do ROM cksum */ + g_rom_fc_ff_ptr[0x36cb0] = 0x18; + g_rom_fc_ff_ptr[0x36cb1] = 0x18; + } + + } +} + +void +config_parse_config_kegs_file() +{ + char *bufptr; + const char *str; + dword64 dsize; + int fd, pos, start, size, last_c, line, ret, c, maxlen; + int i; + + printf("Parsing config.kegs file: %s\n", g_config_kegs_name); + + clk_bram_zero(); + + g_highest_smartport_unit = -1; + + cfg_get_base_path(&g_cfg_cwd_str[0], g_config_kegs_name, 0); + if(g_cfg_cwd_str[0] != 0) { + ret = chdir(&g_cfg_cwd_str[0]); + if(ret != 0) { + printf("chdir to %s, errno:%d\n", g_cfg_cwd_str, errno); + } + // Do basename(g_config_kegs_name)--on it's own buffer + str = cfg_str_basename(g_config_kegs_name); + maxlen = sizeof(g_config_kegs_name); + cfg_strncpy(&g_config_kegs_name[0], str, maxlen); + } + + // In any case, copy the current directory path to g_cfg_cwd_str + (void)!getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX); + printf("CWD is now: %s\n", &g_cfg_cwd_str[0]); + + fd = open(g_config_kegs_name, O_RDONLY | O_BINARY); + dsize = 0; + if(fd >= 0) { + dsize = cfg_get_fd_size(fd); + } + if((fd < 0) || (dsize >= (1 << 30))) { + fatal_printf("cannot open config.kegs at %s, or it is too " + "large! Stopping!\n", g_config_kegs_name); + my_exit(3); + return; + } + size = (int)dsize; + bufptr = malloc(size + 2); + ret = (int)cfg_read_from_fd(fd, (byte *)bufptr, 0, size); + close(fd); + if(ret != size) { + free(bufptr); + fatal_printf("Could not read config.kegs at %s\n", + g_config_kegs_name); + my_exit(3); + return; + } + bufptr[size] = 0; // Ensure it's null terminated + + line = 0; + pos = 0; + last_c = 0; + while(pos < size) { + line++; + if((bufptr[pos] == '\n') && (last_c == '\r')) { + // CR,LF, just eat the LF + pos++; + } + start = pos; + while(pos < size) { + c = bufptr[pos]; + if((c == 0) || (c == '\n') || (c == '\r')) { + last_c = c; + bufptr[pos] = 0; + break; + } + pos++; + } + cfg_parse_one_line(&bufptr[start], line); + pos++; + } + + free(bufptr); + + // And now do command line argument overrides + for(i = 0; i < g_cfg_argv_num_overrides; i++) { + printf("Doing override %d, %s\n", i, g_cfg_argv_overrides[i]); + cfg_parse_one_line(g_cfg_argv_overrides[i], 1001 + i); + g_config_kegs_update_needed = 1; + } +} + +void +cfg_parse_one_line(char *buf, int line) +{ + Cfg_menu *menuptr; + Cfg_defval *defptr; + int *iptr; + const char *nameptr; + int pos, num_equals, type, val, c, len; + int i; + + // warning: modifies memory of bufptr (turns spaces to nulls) + if(line) { // Avoid unused parameter warning + } + + len = (int)strlen(buf); + if(len <= 1) { // Not a valid line, just get out + return; + } + + // printf("disk_conf[%d]: %s\n", line, buf); + if(buf[0] == '#') { + iwm_printf("Skipping comment\n"); + return; + } + + pos = 0; + + while((pos < len) && (buf[pos] == ' ' || buf[pos] == '\t') ) { + pos++; // Eat whitespace + } + if(pos >= len) { + return; // blank line + } + if((pos + 5) < len) { + c = buf[pos+1]; // Slot number + if((buf[pos] == 's') && (buf[pos+2] == 'd') && + (c >= '5' && c <= '7')) { + // looks like s5d1 through s7d15, parse that + cfg_parse_sxdx(buf, pos, len); + return; + } + } + +// parse buf from pos into option, "=" and then "rest" + + if(strncmp(&buf[pos], "bram", 4) == 0) { + cfg_parse_bram(buf, pos+4, len); + return; + } + + // find "name" as first contiguous string + //printf("...parse_option: line %d, %s (%s) len:%d\n", line, buf, + // &buf[pos], len); + + nameptr = &buf[pos]; + while(pos < len) { + c = buf[pos]; + if((c == 0) || (c == ' ') || (c == '\t') || (c == '\n') || + (c == '=')) { + break; + } + pos++; + } + buf[pos] = 0; + if(strcmp(nameptr, "rom") == 0) { + // Translate argument of "-rom" to "g_cfg_rom_path" + nameptr = "g_cfg_rom_path"; + } + pos++; + + // Eat up all whitespace and '=' + num_equals = 0; + while(pos < len) { + c = buf[pos]; + if((c == '=') && (num_equals == 0)) { + pos++; + num_equals++; + } else if(c == ' ' || c == '\t') { + pos++; + } else { + break; + } + } + + /* Look up nameptr to find type */ + type = -1; + defptr = 0; + menuptr = 0; + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + if(strcmp(menuptr->name_str, nameptr) == 0) { + type = menuptr->cfgtype; + break; + } + } + + switch(type) { + case CFGTYPE_INT: + /* use strtol */ + val = (int)strtol(&buf[pos], 0, 0); + iptr = (int *)menuptr->ptr; + cfg_int_update(iptr, val); + break; + case CFGTYPE_FILE: + case CFGTYPE_STR: + cfg_file_update_ptr(menuptr->ptr, &buf[pos], 0); + break; + default: + printf("Config file variable %s is unknown type: %d\n", + nameptr, type); + } +} + +void +cfg_parse_bram(char *buf, int pos, int len) +{ + word32 val; + int bram_num, offset; + + // Format: "bram1[00] = xx yy..." or "bram3[00] = xx yy ..." + // pos = position just after "bram" + if((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) { + fatal_printf("While reading config.kegs, found malformed bram " + "statement: %s\n", buf); + return; + } + bram_num = buf[pos] - '0'; + if((bram_num != 1) && (bram_num != 3)) { + fatal_printf("While reading config.kegs, found bad bram " + "num: %s\n", buf); + return; + } + + bram_num = bram_num >> 1; // turn 3->1 and 1->0 + + offset = (int)strtoul(&(buf[pos+2]), 0, 16); + pos += 5; + while(pos < len) { + if((buf[pos] == ' ') || (buf[pos] == '\t') || + (buf[pos] == 0x0a) || (buf[pos] == 0x0d) || + (buf[pos] == '=')) { + pos++; + continue; + } + val = (word32)strtoul(&buf[pos], 0, 16); // As hex + clk_bram_set(bram_num, offset, val); + offset++; + pos += 2; + } +} + +void +cfg_parse_sxdx(char *buf, int pos, int len) +{ + char *name_ptr, *partition_name; + word32 dynamic_blocks; + int part_num, ejected, slot, drive, c; + + slot = buf[pos+1] - '0'; + drive = buf[pos+3] - '0'; + + /* skip over slot, drive */ + pos += 4; + if((buf[pos] >= '0') && (buf[pos] <= '9')) { + // Second digit of drive is valid + drive = (drive * 10) + buf[pos] - '0'; + pos++; + } + + drive--; // make sxd1 mean index 0 + + while((pos < len) && ((buf[pos] == ' ') || (buf[pos] == '\t') || + (buf[pos] == '=')) ) { + pos++; + } + + ejected = 0; + if(buf[pos] == '#') { // disk is ejected, but read info anyway + ejected = 1; + pos++; + } + + partition_name = 0; + part_num = -1; + dynamic_blocks = 0; + if(buf[pos] == ':') { // yup, it's got a partition name! + pos++; + partition_name = &buf[pos]; + while((pos < len) && (buf[pos] != ':')) { + pos++; + } + buf[pos] = 0; /* null terminate partition name */ + pos++; + } + c = buf[pos]; + if((c == ';') || (c == '@')) { + pos++; + // ; - partition number; @ - Dynamic ProDOS dir size + part_num = 0; + while((pos < len) && (buf[pos] != ':')) { + part_num = (10*part_num) + buf[pos] - '0'; + pos++; + } + pos++; + if(c == '@') { // Dynamic ProDOS directory + dynamic_blocks = part_num * 2; + part_num = -1; + } + } + + /* Get filename */ + name_ptr = &(buf[pos]); + if((pos >= len) || (name_ptr[0] == 0)) { + return; + } + + insert_disk(slot, drive, name_ptr, ejected, partition_name, + part_num, dynamic_blocks); +} + +void +config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, + int with_extras) +{ + char *str; + + str = outstr; + + if(with_extras) { + if(dsk->fd < 0) { + snprintf(str, maxlen - (str - outstr), "#"); + str = &outstr[strlen(outstr)]; + } + if(dsk->dynapro_blocks) { + snprintf(str, maxlen - (str - outstr), "@%d:", + (dsk->dynapro_blocks + 1) / 2); + str = &outstr[strlen(outstr)]; + } else if(dsk->partition_name != 0) { + snprintf(str, maxlen - (str - outstr), ":%s:", + dsk->partition_name); + str = &outstr[strlen(outstr)]; + } else if(dsk->partition_num >= 0) { + snprintf(str, maxlen - (str - outstr), ";%d:", + dsk->partition_num); + str = &outstr[strlen(outstr)]; + } + } + snprintf(str, maxlen - (str - outstr), "%s", dsk->name_ptr); +} + +char * +config_write_config_kegs_file(int get_status) +{ + FILE *fconf; + Disk *dsk; + Cfg_defval *defptr; + Cfg_menu *menuptr; + char *curstr, *defstr; + int defval, curval, type, slot, drive; + int i; + + if(get_status) { + return 0; + } + printf("Writing config.kegs file to %s\n", g_config_kegs_name); + + fconf = fopen(g_config_kegs_name, "w+"); + if(fconf == 0) { + halt_printf("cannot open %s! Stopping!\n", g_config_kegs_name); + return 0; + } + + fprintf(fconf, "# KEGS configuration file version %s\n", + g_kegs_version_str); + + for(i = 0; i < MAX_C7_DISKS + 4; i++) { + slot = 7; + drive = i - 4; + if(i < 4) { + slot = (i >> 1) + 5; + drive = i & 1; + } + if(drive == 0) { + fprintf(fconf, "\n"); /* an extra blank line */ + } + + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + if(dsk->name_ptr == 0 && (i > 4)) { + /* No disk, not even ejected--just skip */ + continue; + } + fprintf(fconf, "s%dd%d = ", slot, drive + 1); + if(dsk->name_ptr == 0) { + fprintf(fconf, "\n"); + continue; + } + config_generate_config_kegs_name(&g_cfg_tmp_path[0], + CFG_PATH_MAX, dsk, 1); + fprintf(fconf, "%s\n", &g_cfg_tmp_path[0]); + } + + fprintf(fconf, "\n"); + + /* See if any variables are different than their default */ + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + defval = defptr->intval; + type = menuptr->cfgtype; + if(type == CFGTYPE_INT) { + curval = *((int *)menuptr->ptr); + if(curval != defval) { + fprintf(fconf, "%s = %d\n", menuptr->name_str, + curval); + } + } + if((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) { + curstr = *((char **)menuptr->ptr); + defstr = *((char **)menuptr->defptr); + if(strcmp(curstr, defstr) != 0) { + fprintf(fconf, "%s = %s\n", menuptr->name_str, + curstr); + } + } + } + + fprintf(fconf, "\n"); + + /* write bram state */ + clk_write_bram(fconf); + + fclose(fconf); + + g_config_kegs_update_needed = 0; + return 0; +} + +void +insert_disk(int slot, int drive, const char *name, int ejected, + const char *partition_name, int part_num, word32 dynamic_blocks) +{ + byte buf_2img[512]; + Disk *dsk; + char *name_ptr, *part_ptr; + dword64 dsize, dunix_pos, exp_dsize, dtmp; + word32 len_bits, save_ftrack; + int image_type, part_len, ret, locked, len, is_po, name_len; + int i; + + g_config_kegs_update_needed = 1; + + if((slot < 5) || (slot > 7)) { + fatal_printf("Invalid slot for insertiing disk: %d\n", slot); + return; + } + if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || + ((slot < 7) && (drive > 1))) { + fatal_printf("Invalid drive for inserting disk: %d\n", drive); + return; + } + + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + +#if 1 + printf("Inserting disk %s (%s or %d) in slot %d, drive: %d, " + "dyna_blocks:%d\n", name, partition_name, part_num, slot, drive, + dynamic_blocks); +#endif + + // DO NOT change dsk->just_ejected. If a disk was just ejected, then + // leave it alone. Otherwise, if we are a newly inserted disk, + // it should already be 0, so leave it alone + //dsk->just_ejected = 1; + + if(dsk->fd >= 0) { + iwm_eject_disk(dsk); + } + + /* Before opening, make sure no other mounted disk has this name */ + /* If so, unmount it */ + if(!ejected) { + for(i = 0; i < 2; i++) { + iwm_eject_named_disk(5, i, name, partition_name); + iwm_eject_named_disk(6, i, name, partition_name); + } + for(i = 0; i < MAX_C7_DISKS; i++) { + iwm_eject_named_disk(7, i, name, partition_name); + } + } + + /* free old name_ptr, partition_name */ + free(dsk->name_ptr); + free(dsk->partition_name); + dsk->name_ptr = 0; + dsk->partition_name = 0; + + name_ptr = kegs_malloc_str(name); + dsk->name_ptr = name_ptr; + name_len = (int)strlen(dsk->name_ptr); + + part_len = 0; + part_ptr = 0; + if(partition_name != 0) { + part_ptr = kegs_malloc_str(partition_name); + part_len = (int)strlen(part_ptr); + dsk->partition_name = part_ptr; + } + dsk->partition_num = part_num; + dsk->dynapro_blocks = dynamic_blocks; + + iwm_printf("Opening up disk image named: %s\n", name_ptr); + + if(ejected) { + /* just get out of here */ + dsk->fd = -1; + return; + } + + dsk->fd = -1; + dsk->raw_data = 0; + dsk->image_type = 0; + dsk->dimage_start = 0; + dsk->dimage_size = 0; + dsk->write_prot = 0; + dsk->write_through_to_unix = 1; + image_type = 0; + locked = 0; + + if(dynamic_blocks) { + ret = dynapro_mount(dsk, name_ptr, dynamic_blocks); + if(ret < 0) { + iwm_eject_disk(dsk); + return; + } + image_type = DSK_TYPE_DYNAPRO; + printf("After dynapro_mount, write_through:%d\n", + dsk->write_through_to_unix); + } + if((partition_name != 0) || (part_num >= 0)) { + ret = cfg_partition_find_by_name_or_num(dsk, partition_name, + part_num); + printf("partition %s (num %d) mounted, wr_prot: %d, ret:%d\n", + partition_name, part_num, dsk->write_prot, ret); + + if(ret < 0) { + iwm_eject_disk(dsk); + return; + } + locked = dsk->write_prot; + if(dsk->raw_data) { + // .zip file or something similar. Do name matching on + // partition name + name_len = part_len; + name_ptr = part_ptr; + locked = 1; + } + } + + if((name_len > 3) && !image_type && + !cfgcasecmp(".gz", &name_ptr[name_len - 3])) { + // it's gzip'ed, try to gunzip it to dsk->raw_data + undeflate_gzip(dsk, name_ptr); + + locked = 1; + dsk->dimage_start = 0; + dsk->dimage_size = dsk->raw_dsize; + name_len -= 3; // So .dsk, .po look for correct chars + } + + if((name_len > 4) && !image_type && + !cfgcasecmp(".bz2", &name_ptr[name_len - 4])) { + // it's bzip2'ed, try to bunzip2 it to dsk->raw_data + config_file_to_pipe(dsk, "bunzip2", name_ptr); + locked = 1; + + // Reduce name_len by 4 so that subsequent compares for .po + // look at the correct chars + name_len -= 4; + } + + if((name_len > 4) && !image_type && + !cfgcasecmp(&name_ptr[name_len - 4], ".sdk")) { + // it's a ShrinkIt archive with a disk image in it + unshk(dsk, name_ptr); + locked = 1; + image_type = DSK_TYPE_PRODOS; + printf("dsk->fd:%d dsk->raw_data:%p, raw_dsize:%lld\n", dsk->fd, + dsk->raw_data, dsk->raw_dsize); + dsk->dimage_start = 0; + dsk->dimage_size = dsk->raw_dsize; + } + + if((name_len > 4) && !image_type && dsk->disk_525 && + !cfgcasecmp(".nib", &name_ptr[name_len-4])) { + // Old, obsolete .nib 5.25" nibblized format. Support is + // read-only + image_type = DSK_TYPE_NIB; + locked = 1; + } + + if((dsk->fd < 0) && !locked && !dynamic_blocks) { + dsk->fd = open(name_ptr, O_RDWR | O_BINARY, 0x1b6); + } + + if((dsk->fd < 0) && !dynamic_blocks) { + printf("Trying to open %s read-only, errno: %d\n", name_ptr, + errno); + dsk->fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6); + locked = 2; + } + + iwm_printf("open returned: %d\n", dsk->fd); + + if((dsk->fd < 0) && !dynamic_blocks) { + fatal_printf("Disk image %s does not exist!\n", name_ptr); + free(dsk->raw_data); + return; + } + + //printf("Checking if it's woz, name_ptr:%s. %d vs %d\n", name_ptr, + // name_len, (int)strlen(name_ptr)); + if((name_len > 4) && !image_type && + !cfgcasecmp(&name_ptr[name_len - 4], ".woz")) { + // it's a WOZ applesauce disk image + image_type = DSK_TYPE_WOZ; + printf("It is woz!\n"); + } + + if(locked) { + dsk->write_prot = locked; + } + + save_ftrack = dsk->cur_frac_track; /* save arm position */ + + /* See if it is in 2IMG format */ + if(dsk->raw_data) { + // Just do a copy from raw_data + for(i = 0; i < 512; i++) { + buf_2img[i] = dsk->raw_data[i]; + } + dsize = dsk->raw_dsize; + if(!dsk->dynapro_info_ptr) { + dsk->write_through_to_unix = 0; + } + } else { + ret = (int)read(dsk->fd, (char *)&buf_2img[0], 512); + dsize = cfg_get_fd_size(dsk->fd); + } + +#if 0 + /* Try to guess that there is a Mac Binary header of 128 bytes */ + /* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */ + if(((dsize & 0xfff) == 0x080) && (dsize < (801*1024)) && !image_type) { + printf("Assuming Mac Binary header on %s\n", dsk->name_ptr); + dsk->dimage_start += 0x80; + dsize -= 0x80; + } +#endif + + dsk->dimage_size = dsize; + + if(!image_type && (buf_2img[0] == '2') && (buf_2img[1] == 'I') && + (buf_2img[2] == 'M') && (buf_2img[3] == 'G')) { + /* It's a 2IMG disk */ + printf("Image named %s is in 2IMG format\n", dsk->name_ptr); + image_type = DSK_TYPE_PRODOS; + + if(buf_2img[12] == 0) { + printf("2IMG is in DOS 3.3 sector order\n"); + image_type = DSK_TYPE_DOS33; + } + if(buf_2img[19] & 0x80) { + /* disk is locked */ + printf("2IMG is write protected\n"); + if(dsk->write_prot == 0) { + dsk->write_prot = 1; + } + } + if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { + dsk->vol_num = buf_2img[16]; + printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); + } + // Some 2IMG archives have the size byte reversed + dsize = (buf_2img[31] << 24) + (buf_2img[30] << 16) + + (buf_2img[29] << 8) + buf_2img[28]; + dunix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + + (buf_2img[25] << 8) + buf_2img[24]; + if(dsize == 0x800c00) { + // Byte reversed 0x0c8000 + dsize = 0x0c8000; + } + if(dsize == 0) { + /* Sweet-16 makes some images with size == 0 */ + /* Example: Prosel from */ + /* www.whatisthe2gs.apple2.org.za/the_ring/ */ + if(buf_2img[12] == 1) { + /* then get the size from 0x14 in blocks */ + dsize = (buf_2img[23] << 24) + + (buf_2img[22] << 16) + + (buf_2img[21] << 8) + buf_2img[20]; + dsize = dsize * 512; /* it was blocks */ + } + } + dsk->dimage_start = dunix_pos; + dsk->dimage_size = dsize; + } + exp_dsize = 800*1024; + dsk->fbit_mult = 256; + if(dsk->disk_525) { + exp_dsize = 140*1024; + dsk->fbit_mult = 128; + } + if(!image_type) { + /* See if it might be the Mac diskcopy format */ + dtmp = cfg_detect_dc42(&buf_2img[0], dsize, exp_dsize, slot); + if(dtmp != 0) { + // It's diskcopy 4.2 + printf("Image named %s is in Mac diskcopy format\n", + dsk->name_ptr); + dsk->dimage_start += 0x54; + dsk->dimage_size = dtmp; + image_type = DSK_TYPE_PRODOS; /* ProDOS */ + } + } + if(!image_type) { + /* Assume raw image */ + dsk->dimage_size = dsize; + image_type = DSK_TYPE_PRODOS; + is_po = (name_len > 3) && + !cfgcasecmp(".po", &name_ptr[name_len-3]); + if(dsk->disk_525) { + image_type = DSK_TYPE_DOS33; + if(is_po) { + image_type = DSK_TYPE_PRODOS; + } + } + } + + dsk->image_type = image_type; + dsk->disk_dirty = 0; + dsk->cur_fbit_pos = 0; + dsk->cur_track_bits = 0; + dsk->cur_trk_ptr = 0; + + if(image_type == DSK_TYPE_WOZ) { + // Special handling + ret = woz_open(dsk, 0); + if(!ret) { + iwm_eject_disk(dsk); + return; + } + } else if(dsk->smartport) { + g_highest_smartport_unit = MY_MAX(dsk->drive, + g_highest_smartport_unit); + + iwm_printf("adding smartport device[%d], img_sz:%08llx\n", + dsk->drive, dsk->dimage_size); + } else if(dsk->disk_525) { + dunix_pos = dsk->dimage_start; + dsize = dsk->dimage_size; + disk_set_num_tracks(dsk, 4*35); + len = 0x1000; + if(dsk->image_type == DSK_TYPE_NIB) { + // Weird .nib format, has no sync bits + len = (int)(dsk->dimage_size / 35); + for(i = 0; i < 35; i++) { + disk_unix_to_nib(dsk, 4*i, dunix_pos, len, + len * 8, 0); + dunix_pos += len; + } + } else { + for(i = 0; i < 35; i++) { + len_bits = iwm_get_default_track_bits(dsk, 4*i); + disk_unix_to_nib(dsk, 4*i, dunix_pos, len, + len_bits, 0); + dunix_pos += len; + } + } + if(dsize != (dword64)35*len) { + fatal_printf("Disk 5.25 error: size is %lld, not %d. " + "Will try to mount anyway\n", dsize, 35*len); + } + } else { + /* disk_35 */ + dunix_pos = dsk->dimage_start; + dsize = dsk->dimage_size; + if(dsize != 800*1024) { + printf("Disk 3.5 Drive %d (Image File: %s), Error: " + "size is %lld, not 800K. Will try to mount " + "anyway\n", drive+1, name, dsize); + } + disk_set_num_tracks(dsk, 2*80); + for(i = 0; i < 2*80; i++) { + len = g_track_bytes_35[i >> 5]; + len_bits = iwm_get_default_track_bits(dsk, i); + iwm_printf("Trk: %d.%d = unix: %08llx, %04x, %04x\n", + i>>1, i & 1, dunix_pos, len, len_bits); + disk_unix_to_nib(dsk, i, dunix_pos, len, len_bits, 0); + dunix_pos += len; + + iwm_printf(" trk_bits:%05x\n", dsk->trks[i].track_bits); + } + } + + iwm_move_to_ftrack(dsk, save_ftrack, 0, 0); +} + +dword64 +cfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot) +{ + dword64 ddata_size, dtag_size; + int i; + + // Detect Mac DiskCopy4.2 disk image (often .dmg or .dc or .dc42) + // bptr points to just the first 512 bytes of the file + + if((bptr[0x52] != 1) || (bptr[0x53] != 0)) { + return 0; // No "magic number 0x01, 0x00 for DiskCopy4.2 + } + if(bptr[0] > 0x3f) { + return 0; // not a valid image name length (1-0x3f) + } + ddata_size = 0; + dtag_size = 0; + for(i = 0; i < 4; i++) { + ddata_size = (ddata_size << 8) | bptr[0x40 + i]; + dtag_size = (dtag_size << 8) | bptr[0x44 + i]; + } + if((dtag_size != 0) && (dtag_size != ((ddata_size >> 9) * 12))) { + return 0; // Tags are either 0, or 12 bytes per block + } + if(slot == 7) { + if(ddata_size < 140*1024) { + return 0; // Allow any size >=140K slot 7 + } + } else if(ddata_size != exp_dsize) { + return 0; // Must be 140K or 800K + } + + if(ddata_size > (dsize - 0x54)) { + return 0; // data_size doesn't make sense + } + if((ddata_size & 0x1ff) || ((ddata_size >> 31) > 1)) { + return 0; // data_size not a multiple of 512 bytes + } + if((ddata_size + dtag_size + 0x54) != dsize) { + return 0; // File doesn't appear to be well-formed + } + + return ddata_size; +} + +dword64 +cfg_get_fd_size(int fd) +{ + struct stat stat_buf; + int ret; + + ret = fstat(fd, &stat_buf); + if(ret != 0) { + fprintf(stderr,"fstat returned %d on fd %d, errno: %d\n", + ret, fd, errno); + stat_buf.st_size = 0; + } + + return stat_buf.st_size; +} + +dword64 +cfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize) +{ + dword64 dret, doff; + word32 this_len; + + dret = kegs_lseek(fd, dpos, SEEK_SET); + if(dret != dpos) { + printf("lseek failed: %lld\n", dret); + return 0; + } + doff = 0; + while(1) { + if(doff >= dsize) { + break; + } + this_len = 1UL << 30; + if((dsize - doff) < this_len) { + this_len = (word32)(dsize - doff); + } + dret = read(fd, bufptr + doff, this_len); + if((dret + 1) == 0) { // dret==-1 + printf("read failed\n"); + return 0; + } + if(dret == 0) { + printf("Unexpected end of file, tried to read from " + "dpos:%lld dsize:%lld\n", dpos, dsize); + return 0; + } + doff += dret; + } + return doff; +} + +dword64 +cfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize) +{ + dword64 dret; + + dret = kegs_lseek(fd, dpos, SEEK_SET); + if(dret != dpos) { + printf("lseek failed: %lld\n", dret); + return 0; + } + return must_write(fd, bufptr, dsize); +} + +int +cfg_partition_maybe_add_dotdot() +{ + int part_len; + + part_len = (int)strlen(&(g_cfg_part_path[0])); + if(part_len > 0) { + // Add .. entry here + cfg_file_add_dirent(&g_cfg_partitionlist, "..", 1, 0, 0, 0, 0); + } + return part_len; +} + +int +cfg_partition_name_check(const byte *name_ptr, int name_len) +{ + int part_len; + int i; + + // Return 0 if name_ptr is not at the right path depth, 1 if OK + + part_len = (int)strlen(&g_cfg_part_path[0]); + for(i = 0; i < part_len; i++) { + if(i >= name_len) { + return 0; + } + if(name_ptr[i] != g_cfg_part_path[i]) { + return 0; + } + } + return 1; +} + +int +cfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size) +{ + if(!cfg_read_from_fd(fd, buf, blk * blk_size, blk_size)) { + // Read failed + return 0; + } + return blk_size; +} + +int +cfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr, + int part_num) +{ + Cfg_dirent *direntptr; + const char *partnamestr; + int match, num_parts, ret, fd, len, c; + int i; + +#if 0 + printf("cfg_partition_find_by_name_or_num: %s, part_num:%d\n", + partnamestr, part_num); +#endif + + // We need to copy partnamestr up to the last / to g_cfg_part_path[], + // and use just the end as the partition name. + cfg_strncpy(&g_cfg_part_path[0], in_partnamestr, CFG_PATH_MAX); + len = (int)strlen(in_partnamestr); + partnamestr = in_partnamestr; + for(i = len - 1; i >= 0; i--) { + c = g_cfg_part_path[i]; + if(c == '/') { + partnamestr = &(in_partnamestr[i+1]); + break; + } + g_cfg_part_path[i] = 0; + } + + fd = open(dsk->name_ptr, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + fatal_printf("Cannot open disk image: %s\n", dsk->name_ptr); + return -1; + } + + num_parts = cfg_partition_make_list(fd); + + if(num_parts <= 0) { + printf("num_parts: %d\n", num_parts); + close(fd); + return -1; + } + + for(i = 0; i < g_cfg_partitionlist.last; i++) { + direntptr = &(g_cfg_partitionlist.direntptr[i]); + match = 0; + if((strcmp(partnamestr, direntptr->name) == 0) && + (part_num < 0)) { + //printf("partition, match1, name:%s %s, part_num:%d\n", + // partnamestr, direntptr->name, part_num); + + match = 1; + } + if((partnamestr == 0) && (direntptr->part_num == part_num)) { + //printf("partition, match2, n:%s, part_num:%d == %d\n", + // direntptr->name, direntptr->part_num, part_num); + match = 1; + } + if(match) { + //printf("match with dimage_start:%08llx, dimage_size:" + // "%08llx\n", dsk->dimage_start, + // dsk->dimage_size); + + printf("match with dimage_start:%08llx, dimage_size:" + "%08llx\n", direntptr->dimage_start, + direntptr->dsize); + ret = i; + if(g_cfg_partition_is_zip) { + ret = undeflate_zipfile(dsk, fd, + direntptr->dimage_start, + direntptr->dsize, + direntptr->compr_dsize); + close(fd); + } else { + dsk->fd = fd; + dsk->dimage_start = direntptr->dimage_start; + dsk->dimage_size = direntptr->dsize; + } + + return ret; + } + } + + close(fd); + // printf("No matches, ret -1\n"); + return -1; +} + +int +cfg_partition_make_list_from_name(const char *namestr) +{ + int fd, ret; + + fd = open(namestr, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + fatal_printf("Cannot open part image: %s\n", namestr); + return 0; + } + ret = cfg_partition_make_list(fd); + + close(fd); + return ret; +} + +int +cfg_partition_make_list(int fd) +{ + Driver_desc *driver_desc_ptr; + Part_map *part_map_ptr; + word32 *blk_bufptr; + dword64 dimage_start, dimage_size, dsize; + word32 start, len, data_off, data_len, sig, map_blk_cnt, cur_blk; + word32 map_blks, block_size; + int is_dir, ret; + + block_size = 512; + g_cfg_partition_is_zip = 0; + + cfg_free_alldirents(&g_cfg_partitionlist); + + blk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE); + + cfg_partition_read_block(fd, blk_bufptr, 0, block_size); + + driver_desc_ptr = (Driver_desc *)blk_bufptr; + sig = cfg_get_be_word16(&(driver_desc_ptr->sig)); + block_size = cfg_get_be_word16(&(driver_desc_ptr->blk_size)); + if(block_size == 0) { + block_size = 512; + } + if((sig != 0x4552) || (block_size < 0x200) || + (block_size > MAX_PARTITION_BLK_SIZE)) { + printf("Partition error: No driver descriptor map found\n"); + free(blk_bufptr); + ret = undeflate_zipfile_make_list(fd); + if(ret > 0) { + g_cfg_partition_is_zip = 1; + } + return ret; + } + + map_blks = 1; + cur_blk = 0; + dsize = cfg_get_fd_size(fd); + cfg_file_add_dirent(&g_cfg_partitionlist, "None - Whole image", + is_dir=0, dsize, 0, 0, -1); + + while(cur_blk < map_blks) { + cur_blk++; + cfg_partition_read_block(fd, blk_bufptr, cur_blk, block_size); + part_map_ptr = (Part_map *)blk_bufptr; + sig = cfg_get_be_word16(&(part_map_ptr->sig)); + map_blk_cnt = cfg_get_be_word32(&(part_map_ptr->map_blk_cnt)); + if(cur_blk <= 1) { + map_blks = MY_MIN(20, map_blk_cnt); + } + if(sig != 0x504d) { + printf("Partition entry %d bad signature:%04x\n", + cur_blk, sig); + free(blk_bufptr); + return g_cfg_partitionlist.last; + } + + /* found it, check for consistency */ + start = cfg_get_be_word32(&(part_map_ptr->phys_part_start)); + len = cfg_get_be_word32(&(part_map_ptr->part_blk_cnt)); + data_off = cfg_get_be_word32(&(part_map_ptr->data_start)); + data_len = cfg_get_be_word32(&(part_map_ptr->data_cnt)); + if(data_off + data_len > len) { + printf("Poorly formed entry\n"); + continue; + } + + if(data_len < 10 || start < 1) { + printf("Poorly formed entry %d, datalen:%d, " + "start:%08x\n", cur_blk, data_len, start); + continue; + } + + dimage_size = (dword64)data_len * block_size; + dimage_start = ((dword64)start + data_off) * block_size; + is_dir = 2*(dimage_size < 800*1024); +#if 0 + printf(" partition add entry %d = %s %d %08llx %08llx\n", + cur_blk, part_map_ptr->part_name, is_dir, + dimage_size, dimage_start); +#endif + + cfg_file_add_dirent(&g_cfg_partitionlist, + part_map_ptr->part_name, is_dir, dimage_size, + dimage_start, 0, cur_blk); + } + + free(blk_bufptr); + return g_cfg_partitionlist.last; +} + +int +cfg_maybe_insert_disk(int slot, int drive, const char *namestr) +{ + int num_parts; + + g_cfg_part_path[0] = 0; + num_parts = cfg_partition_make_list_from_name(namestr); + + if(num_parts > 0) { + printf("Choose a partition\n"); + g_cfg_select_partition = 1; + g_cfg_file_pathfield = 0; + } else { + insert_disk(slot, drive, namestr, 0, 0, -1, 0); + return 1; + } + return 0; +} + +void +cfg_insert_disk_dynapro(int slot, int drive, const char *name) +{ + int dynapro_blocks; + + dynapro_blocks = 280; + if(slot == 5) { + dynapro_blocks = 1600; + } else if(slot == 7) { + dynapro_blocks = 65535; + } + if(g_cfg_newdisk_select && (g_cfg_newdisk_type == 3) && + g_cfg_newdisk_blocks) { + dynapro_blocks = g_cfg_newdisk_blocks; + } + insert_disk(slot, drive, name, 0, 0, -1, dynapro_blocks); +} + +int +cfg_stat(char *path, struct stat *sb, int do_lstat) +{ + int ret, len, removed_slash; + + removed_slash = 0; + len = 0; + + /* Windows doesn't like to stat paths ending in a /, so remove it */ + len = (int)strlen(path); +#ifdef _WIN32 + if((len > 1) && (path[len - 1] == '/') ) { + path[len - 1] = 0; /* remove the slash */ + removed_slash = 1; + } +#endif + + if(do_lstat) { + ret = lstat(path, sb); + } else { + ret = stat(path, sb); + } + + /* put the slash back */ + if(removed_slash) { + path[len - 1] = '/'; + } + + return ret; +} + +word32 +cfg_get_le16(byte *bptr) +{ + return bptr[0] | (bptr[1] << 8); +} + +word32 +cfg_get_le32(byte *bptr) +{ + return bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) | (bptr[3] << 24); +} + +dword64 +cfg_get_le64(byte *bptr) +{ + dword64 dval; + int i; + + dval = 0; + for(i = 7; i >= 0; i--) { + dval = (dval << 8) | bptr[i]; + } + return dval; +} + +word32 +cfg_get_be_word16(word16 *ptr) +{ + byte *bptr; + + bptr = (byte *)ptr; + return (bptr[0] << 8) | bptr[1]; +} + +word32 +cfg_get_be_word32(word32 *ptr) +{ + byte *bptr; + + bptr = (byte *)ptr; + return (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; +} + +void +cfg_set_le32(byte *bptr, word32 val) +{ + *bptr++ = val; + *bptr++ = val >> 8; + *bptr++ = val >> 16; + *bptr++ = val >> 24; +} + +void +config_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr) +{ +#ifdef _WIN32 + printf("Cannot do pipe from cmd %s to %s\n", cmd_ptr, name_ptr); + return; +#else + int output_pipe[2]; + byte *bptr2; + char *bufptr; + pid_t pid; + int stat_loc, ret, bufsize, pos, fd; + int i; + + // Create a pipe to cmd_ptr, and send the contents of name_ptr to it + // Collect the output in a 32MB buffer. Once complete, allocate + // a buffer of the correct size, copy to it, and free the giant buffer + // Sample usage: "gunzip", "{filename}.gz" will run gunzip and collect + // uncompressed data + ret = pipe(&output_pipe[0]); + if(ret < 0) { + return; + } + printf("output_pipe[0]=%d, [1]=%d\n", output_pipe[0], output_pipe[1]); + bufsize = 32*1024*1024; + bufptr = malloc(bufsize); + if(bufptr == 0) { + return; + } + pos = 0; + pid = fork(); + if(pid == 0) { + close(output_pipe[0]); + ret = dup2(output_pipe[1], 1); + if(ret < 0) { + exit(1); + } + // The child. Open 0 as the file, and then do system + close(0); + fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6); + if(fd != 0) { + exit(1); + } + // Now just run the command. Input is from name_ptr, output is + // to a pipe + (void)!system(cmd_ptr); + exit(0); + } else if(pid > 0) { + // Parent. Collect output from output_pipe[0], and write it + // to bufptr. + close(output_pipe[1]); + while(1) { + if(pos >= bufsize) { + break; + } + ret = read(output_pipe[0], bufptr + pos, bufsize - pos); + if(ret <= 0) { + break; + } + pos += ret; + } + close(output_pipe[0]); + waitpid(pid, &stat_loc, 0); + } else { + // Error case + close(output_pipe[1]); + close(output_pipe[0]); + } + + // See what we got + bptr2 = 0; + printf("Read %d bytes from %s\n", pos, name_ptr); + if(pos >= 140*1024) { + // Looks like it could be an image + bptr2 = malloc(pos); + for(i = 0; i < pos; i++) { + bptr2[i] = bufptr[i]; + } + dsk->raw_data = bptr2; + dsk->fd = 0; // Indicates raw_data is valid + dsk->raw_dsize = pos; + } + free(bufptr); +#endif +} + +void +cfg_htab_vtab(int x, int y) +{ + if(x > 79) { + x = 0; + } + if(y > 23) { + y = 0; + } + g_cfg_curs_x = x; + g_cfg_curs_y = y; + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; +} + +void +cfg_home() +{ + int i; + + cfg_htab_vtab(0, 0); + for(i = 0; i < 24; i++) { + cfg_cleol(); + } +} + +void +cfg_cleol() +{ + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; + cfg_putchar(' '); + while(g_cfg_curs_x != 0) { + cfg_putchar(' '); + } +} + +void +cfg_putchar(int c) +{ + int x, y; + + if(c == '\n') { + cfg_cleol(); + return; + } + if(c == '\b') { + g_cfg_curs_inv = !g_cfg_curs_inv; + return; + } + if(c == '\t') { + g_cfg_curs_mousetext = !g_cfg_curs_mousetext; + return; + } + y = g_cfg_curs_y; + x = g_cfg_curs_x; + + // Normal: 0xa0-0xff for space through lowercase + // Inverse: 0x00-0x3f for upper case and numbers, 0x60-0x7f for lcase + // Mousetext: 0x40-0x5f + if(g_cfg_curs_inv) { + if(c >= 0x40 && c < 0x60) { + c = c & 0x1f; + } + } else { + c = c | 0x80; + } + if(g_cfg_curs_mousetext) { + c = (c & 0x1f) | 0x40; + } + g_cfg_screen[y][x] = c; + x++; + if(x >= 80) { + x = 0; + y++; + if(y >= 24) { + y = 0; + } + } + g_cfg_curs_y = y; + g_cfg_curs_x = x; + g_cfg_screen_changed = 1; +} + +void +cfg_printf(const char *fmt, ...) +{ + va_list ap; + int c; + int i; + + va_start(ap, fmt); + (void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap); + va_end(ap); + + for(i = 0; i < CFG_PRINTF_BUFSIZE; i++) { + c = g_cfg_printf_buf[i]; + if(c == 0) { + return; + } + cfg_putchar(c); + } +} + +void +cfg_print_dnum(dword64 dnum, int max_len) +{ + char buf[64]; + char buf2[64]; + int len, cnt, c; + int i, j; + + /* Prints right-adjusted "num" in field "max_len" wide */ + snprintf(&buf[0], 64, "%lld", dnum); + len = (int)strlen(buf); + for(i = 0; i < 64; i++) { + buf2[i] = ' '; + } + j = max_len + 1; + buf2[j] = 0; + j--; + cnt = 0; + for(i = len - 1; (i >= 0) && (j >= 1); i--) { + c = buf[i]; + if(c >= '0' && c <= '9') { + if(cnt >= 3) { + buf2[j--] = ','; + cnt = 0; + } + cnt++; + } + buf2[j--] = c; + } + cfg_printf(&buf2[1]); +} + +int +cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras) +{ + Disk *dsk; + int slot, drive; + + slot = type_ext >> 8; + drive = type_ext & 0xff; + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + + outstr[0] = 0; + if(dsk->name_ptr == 0) { + return 0; + } + + config_generate_config_kegs_name(outstr, maxlen, dsk, with_extras); + return dsk->dynapro_blocks; +} + +int +cfg_get_disk_locked(int type_ext) +{ + Disk *dsk; + int slot, drive; + + slot = type_ext >> 8; + drive = type_ext & 0xff; + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + if(dsk->fd < 0) { + return 0; + } + + if(dsk->write_prot) { + return 1; + } else if(!dsk->write_through_to_unix) { + return 2; + } + + return 0; +} + +void +cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) +{ + char valbuf[CFG_OPT_MAXSTR]; + char *(*fn_ptr)(int); + int *iptr; + char **str_ptr; + const char *menustr; + char *curstr, *defstr, *str, *outstr; + void *edit_ptr; + int val, num_opts, opt_num, bufpos, outpos, curval, defval, type; + int type_ext, opt_get_str, separator, len, c, locked; + int i; + + // For this menu_pos line, create output in g_cfg_opt_buf[] string + // Highlight it if menu_pos==highlight_pos + // Allow arrows to modify the currently selected item of a list using + // change: -1 moves to a previous item, +1 moves to the next + + g_cfg_opt_buf[0] = 0; + + num_opts = 0; + opt_get_str = 0; + separator = ','; + + menuptr += menu_pos; /* move forward to entry menu_pos */ + + menustr = menuptr->str; + type = menuptr->cfgtype; + type_ext = (type >> 4); + type = type & 0xf; + len = (int)strlen(menustr) + 1; + + bufpos = 0; + outstr = &(g_cfg_opt_buf[0]); + + outstr[bufpos++] = ' '; // 0 + outstr[bufpos++] = ' '; // 1 + outstr[bufpos++] = '\t'; // 2 + outstr[bufpos++] = '\t'; // 3 + outstr[bufpos++] = ' '; // 4 + outstr[bufpos++] = ' '; // 5 + + // Figure out if we should get a checkmark + curval = -1; + defval = -1; + curstr = 0; + if(type == CFGTYPE_INT) { + iptr = menuptr->ptr; + curval = *iptr; + iptr = menuptr->defptr; + if(!iptr) { + printf("BAD MENU, defptr is 0!\n"); + } else { + defval = *iptr; + } + if(curval == defval) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + + if((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) { + str_ptr = (char **)menuptr->ptr; + curstr = *str_ptr; + str_ptr = (char **)menuptr->defptr; + if(!str_ptr) { + printf("BAD MENU, defptr str is 0!\n"); + defstr = ""; + } else { + defstr = *str_ptr; + } + if(strcmp(curstr, defstr) == 0) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + + // If it's a menu, give it a special menu indicator + if(type == CFGTYPE_MENU) { + g_cfg_opt_buf[1] = '\t'; + g_cfg_opt_buf[2] = 'M'; /* return-like symbol */ + g_cfg_opt_buf[3] = '\t'; + g_cfg_opt_buf[4] = ' '; + } + + if(type == CFGTYPE_DISK) { + locked = cfg_get_disk_locked(type_ext); + if(locked) { + g_cfg_opt_buf[4] = '*'; + if(locked == 2) { // inverse + g_cfg_opt_buf[2] = '\b'; + g_cfg_opt_buf[3] = '*'; + g_cfg_opt_buf[4] = '\b'; + } + } + } + + if(menu_pos == highlight_pos) { + outstr[bufpos++] = '\b'; + } + + opt_get_str = 2; + i = -1; + outpos = bufpos; +#if 0 + printf("cfg menu_pos: %d str len: %d\n", menu_pos, len); +#endif + while(++i < len) { + c = menustr[i]; + if(c == separator) { // ','? + if(i == 0) { + continue; + } + c = 0; + } + outstr[outpos++] = c; + outstr[outpos] = 0; + if(outpos >= CFG_OPT_MAXSTR) { + fprintf(stderr, "CFG_OPT_MAXSTR exceeded\n"); + my_exit(1); + return; + } + if(c == 0) { + if(opt_get_str == 2) { + outstr = &(valbuf[0]); + bufpos = outpos - 1; + opt_get_str = 0; + } else if(opt_get_str) { +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt %d = %s=%d\n", + menu_pos, num_opts, + g_cfg_opts_str[0], + g_cfg_opts_vals[num_opts]); + } +#endif + num_opts++; + outstr = &(valbuf[0]); + opt_get_str = 0; + if(num_opts >= CFG_MAX_OPTS) { + fprintf(stderr, "CFG_MAX_OPTS oflow\n"); + my_exit(1); + return; + } + } else { + val = (word32)strtoul(valbuf, 0, 0); + g_cfg_opts_vals[num_opts] = val; + outstr = &(g_cfg_opts_str[0]); + if(val != curval) { + outstr = valbuf; + } + opt_get_str = 1; + } + outpos = 0; + outstr[0] = 0; + } + } + + if(menu_pos == highlight_pos) { + g_cfg_opt_buf[bufpos++] = '\b'; + } + + g_cfg_opt_buf[bufpos] = 0; + + // Decide what to display on the "right" side + str = 0; + opt_num = -1; + if((type == CFGTYPE_INT) || (type == CFGTYPE_FILE) || + (type == CFGTYPE_STR)) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = '='; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + for(i = 0; i < num_opts; i++) { + if(curval == g_cfg_opts_vals[i]) { + opt_num = i; + break; + } + } + } + + if(change != 0) { + if(type == CFGTYPE_INT) { + if(num_opts > 0) { + opt_num += change; + if(opt_num >= num_opts) { + opt_num = 0; + } + if(opt_num < 0) { + opt_num = num_opts - 1; + } + curval = g_cfg_opts_vals[opt_num]; + } else { + curval += change; + /* HACK: min_val, max_val testing here */ + } + iptr = (int *)menuptr->ptr; + cfg_int_update(iptr, curval); + } + g_config_kegs_update_needed = 1; + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt_num %d\n", menu_pos, opt_num); + } +#endif + + edit_ptr = g_cfg_edit_ptr; + if((edit_ptr == menuptr->ptr) && edit_ptr) { + // Just show the current edit string + str = cfg_shorten_filename(&(g_cfg_edit_buf[0]), 68 - 1); + cfg_strlcat(str, "\b \b", CFG_PATH_MAX); + } else if(opt_num >= 0) { + str = &(g_cfg_opts_str[0]); + } else { + if(type == CFGTYPE_INT) { + str = &(g_cfg_opts_str[0]); + snprintf(str, CFG_OPT_MAXSTR, "%d", curval); + } else if (type == CFGTYPE_DISK) { + str = &(g_cfg_opts_str[0]); + (void)cfg_get_disk_name(str, CFG_PATH_MAX, type_ext, 1); + str = cfg_shorten_filename(str, 68); + } else if ((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) { + str = cfg_shorten_filename(curstr, 68); + } else if(type == CFGTYPE_FUNC) { + fn_ptr = (char *(*)(int))menuptr->ptr; + str = ""; + curstr = (*fn_ptr)(1); + if(curstr) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = ':'; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + str = cfg_shorten_filename(curstr, 68); + free(curstr); + } + } else { + str = ""; + } + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d buf_pos %d, str is %s, %02x, %02x, " + "%02x %02x\n", + menu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1], + g_cfg_opt_buf[bufpos-2], + g_cfg_opt_buf[bufpos-3], + g_cfg_opt_buf[bufpos-4]); + } +#endif + + g_cfg_opt_buf[bufpos] = 0; + cfg_strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1); + g_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0; +} + +void +cfg_get_base_path(char *pathptr, const char *inptr, int go_up) +{ + const char *tmpptr; + char *slashptr; + char *outptr; + int add_dotdot, is_dotdot; + int len; + int c; + + // Take full filename, copy it to pathptr, and truncate at last slash + // inptr and pathptr can be the same. + // If go_up is set, then replace a blank dir with ".." + // but first, see if path is currently just ../ over and over + // if so, just tack .. onto the end and return + + //printf("cfg_get_base start with %s\n", inptr); + + g_cfg_file_match[0] = 0; + tmpptr = inptr; + is_dotdot = 1; + while(1) { + if(tmpptr[0] == 0) { + break; + } + if((tmpptr[0] == '.') && (tmpptr[1] == '.') && + (tmpptr[2] == '/')) { + tmpptr += 3; + } else { + is_dotdot = 0; + break; + } + } + slashptr = 0; + outptr = pathptr; + c = -1; + while(c != 0) { + c = *inptr++; + if(c == '/') { + if(*inptr != 0) { /* if not a trailing slash... */ + slashptr = outptr; + } + } + *outptr++ = c; + } + if(!go_up) { + /* if not go_up, copy chopped part to g_cfg_file_match*/ + /* copy from slashptr+1 to end */ + tmpptr = slashptr+1; + if(slashptr == 0) { + tmpptr = pathptr; + } + cfg_strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX); + /* remove trailing / from g_cfg_file_match */ + len = (int)strlen(&g_cfg_file_match[0]); + if((len > 1) && (len < (CFG_PATH_MAX - 1)) && + g_cfg_file_match[len - 1] == '/') { + g_cfg_file_match[len - 1] = 0; + } + //printf("set g_cfg_file_match to %s\n", &g_cfg_file_match[0]); + } + if(!is_dotdot && (slashptr != 0)) { + slashptr[0] = '/'; + slashptr[1] = 0; + outptr = slashptr + 2; + } + add_dotdot = 0; + if(pathptr[0] == 0 || is_dotdot) { + /* path was blank, or consisted of just ../ pattern */ + if(go_up) { + add_dotdot = 1; + } + } else if(slashptr == 0) { + /* no slashes found, but path was not blank--make it blank */ + if(pathptr[0] == '/') { + pathptr[1] = 0; + } else { + pathptr[0] = 0; + } + } + + if(add_dotdot) { + --outptr; + outptr[0] = '.'; + outptr[1] = '.'; + outptr[2] = '/'; + outptr[3] = 0; + } + + //printf("cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\n", + // pathptr, is_dotdot, add_dotdot); +} + +char * +cfg_name_new_image(int get_status) +{ + // Called from menu to create a new disk image, this will pop up the + // file selection dialog. Once name is selected, + // cfg_create_new_image() is called. + if(get_status) { + return 0; + } + printf("cfg_name_new_image called!\n"); + g_cfg_slotdrive = g_cfg_newdisk_slotdrive; + g_cfg_newdisk_select = 1; + cfg_file_init(); + return 0; +} + +void +cfg_dup_existing_image(word32 slotdrive) +{ + // Set g_cfg_newdisk_* to copy the slotdrive image + g_cfg_slotdrive = slotdrive; + g_cfg_newdisk_select = 2; // Do DUP + cfg_file_init(); +} + +void +cfg_dup_image_selected() +{ + Disk *dsk; + Woz_info *wozinfo_ptr; + byte *bufptr; + char *str; + dword64 dsize, dret; + word32 slotdrive; + int fd; + + // printf("cfg_dup_image_selected\n"); + + slotdrive = g_cfg_slotdrive; + g_cfg_slotdrive = 0; + g_menuptr = &g_cfg_disk_menu[0]; + g_menu_redraw_needed = 1; + g_cfg_newdisk_select = 0; + g_cfg_newdisk_slotdrive = 0; + printf("slotdrive:%04x\n", slotdrive); + if(slotdrive < 0x500) { + return; // Invalid slot,drive: Do nothing + } + dsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7, + slotdrive & 0xff); + if(dsk->fd < 0) { + return; // No disk + } + dsize = dsk->dimage_size; + + str = &g_cfg_file_path[0], + printf("Create dup image %s, dsize:%lld\n", str, dsize); + if((word32)dsize != dsize) { + return; + } + fd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6); + if(fd < 0) { + printf("Open %s failed, errno:%d\n", str, errno); + return; + } + wozinfo_ptr = dsk->wozinfo_ptr; + if(wozinfo_ptr && !dsk->write_through_to_unix) { + // Just write out the WOZ image, and then fully enable this + // image + printf("Writing out .WOZ image to %s, size:%d\n", str, + wozinfo_ptr->woz_size); + if((dsk->raw_data == 0) && (dsk->fd >= 0)) { + close(dsk->fd); + } + dsk->fd = fd; + woz_rewrite_crc(dsk, wozinfo_ptr->woz_size); + // Above will recalc CRC and write out woz_size bytes + dsk->raw_data = 0; + dsk->write_through_to_unix = 1; + free(dsk->name_ptr); + dsk->name_ptr = kegs_malloc_str(str); + free(dsk->partition_name); + dsk->partition_name = 0; + dsk->image_type = DSK_TYPE_WOZ; + dsk->dimage_size = wozinfo_ptr->woz_size; + dsk->dimage_start = 0; + g_config_kegs_update_needed = 1; + woz_check_file(dsk); + } else if(dsk->raw_data) { + cfg_write_to_fd(fd, dsk->raw_data, 0, dsize); + close(fd); + } else { + bufptr = malloc((size_t)dsize); + if((bufptr != 0) && ((size_t)dsize == dsize)) { + dret = cfg_read_from_fd(dsk->fd, bufptr, 0, dsize); + if(dret == dsize) { + cfg_write_to_fd(fd, bufptr, 0, dsize); + } + } + free(bufptr); + close(fd); + } +} + +void +cfg_validate_image(word32 slotdrive) +{ + Disk *dsk; + + dsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7, + slotdrive & 0xff); + dynapro_validate_any_image(dsk); +} + +void +cfg_toggle_lock_disk(word32 slotdrive) +{ + Disk *dsk; + + dsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7, + slotdrive & 0xff); + iwm_toggle_lock(dsk); +} + +int +cfg_create_new_image_act(const char *str, int type, int size_blocks) +{ + byte buf[512]; + dword64 dret; + int fd, ret; + int i; + + // Called after file dialog selects a new image name, this creates it + + if(size_blocks == 65536) { + size_blocks--; // Shrink to 65535 total blocks + } + printf("Create new image type:%d, size:%dKB\n", type, size_blocks/2); + fd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6); + if(fd < 0) { + printf("Open %s failed, errno:%d\n", str, errno); + return fd; + } + for(i = 0; i < 512; i++) { + buf[i] = 0; + } + + ret = 0; + if(type == 2) { // WOZ + (void)woz_new(fd, str, size_blocks/2); + } else { + for(i = 0; i < size_blocks; i++) { + dret = cfg_write_to_fd(fd, &(buf[0]), i * 512U, 512); + if(dret != 512) { + ret = -1; + break; + } + } + } + close(fd); + + return ret; // 0=success, -1 is a failure +} + +void +cfg_create_new_image() +{ + word32 dynamic_blocks; + int ret; + + // Type is in g_cfg_file_path. Create this file and prepare it + printf("Creating new image: %s\n", &g_cfg_file_path[0]); + + if(g_cfg_newdisk_select == 2) { + cfg_dup_image_selected(); + return; + } + ret = 0; + dynamic_blocks = 0; + if(g_cfg_newdisk_type == 3) { + dynamic_blocks = g_cfg_newdisk_blocks; + } else { + ret = cfg_create_new_image_act(&g_cfg_file_path[0], + g_cfg_newdisk_type, g_cfg_newdisk_blocks); + } + if(ret < 0) { + // Maybe open a dialog? Oh well...do nothing + } else { + insert_disk((g_cfg_slotdrive >> 8) & 0xf, + g_cfg_slotdrive & 0xff, &(g_cfg_file_path[0]), + 0, 0, -2, dynamic_blocks); + } + g_cfg_slotdrive = 0; + g_menuptr = &g_cfg_disk_menu[0]; + g_menu_redraw_needed = 1; + g_cfg_newdisk_select = 0; + g_cfg_newdisk_slotdrive = 0; +} + +void +cfg_file_init() +{ + int slot, drive, is_dynapro; + int i; + + is_dynapro = 0; + if((g_cfg_slotdrive & 0xfff) == 0xfff) { // File selection + // Just use g_cfg_file_def_name + cfg_strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name, + CFG_PATH_MAX); + } else { + is_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, + g_cfg_slotdrive, 0); + + slot = (g_cfg_slotdrive >> 8) & 7; + drive = g_cfg_slotdrive & 1; + for(i = 0; i < 6; i++) { + if(g_cfg_tmp_path[0] != 0) { + break; + } + /* try to get a starting path from some mounted drive */ + drive = !drive; + if(i & 1) { + slot++; + if(slot >= 8) { + slot = 5; + } + } + is_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0], + CFG_PATH_MAX, (slot << 8) + drive, 0); + } + } + + cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0); + if(is_dynapro) { + // Use the full path to the dir (don't strip off last part) + cfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], + CFG_PATH_MAX); + } + g_cfg_dirlist.invalid = 1; +} + +void +cfg_free_alldirents(Cfg_listhdr *listhdrptr) +{ + int i; + + if(listhdrptr->max > 0) { + // Free the old directory listing + for(i = 0; i < listhdrptr->last; i++) { + free(listhdrptr->direntptr[i].name); + } + free(listhdrptr->direntptr); + } + + listhdrptr->direntptr = 0; + listhdrptr->last = 0; + listhdrptr->max = 0; + listhdrptr->invalid = 0; + + listhdrptr->topent = 0; + listhdrptr->curent = 0; +} + +void +cfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr, + int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, + int part_num) +{ + Cfg_dirent *direntptr; + int num, namelen, this_len; + int i; + + // Loop through all entries, make sure name is unique + num = listhdrptr->last; + namelen = (int)strlen(nameptr); + for(i = 0; i < num; i++) { + direntptr = &(listhdrptr->direntptr[i]); + this_len = (int)strlen(direntptr->name); + if(cfg_str_match(direntptr->name, nameptr, namelen) == 0) { + // It's a match...check len + if(namelen == this_len) { + return; + } + } + } + cfg_file_add_dirent(listhdrptr, nameptr, is_dir, dsize, dimage_start, + compr_dsize, part_num); +} + +void +cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, + dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num) +{ + Cfg_dirent *direntptr; + char *ptr; + int inc_amt, namelen; + + namelen = (int)strlen(nameptr); + if(listhdrptr->last >= listhdrptr->max) { + // realloc + inc_amt = MY_MAX(64, listhdrptr->max); + inc_amt = MY_MIN(inc_amt, 1024); + listhdrptr->max += inc_amt; + listhdrptr->direntptr = realloc(listhdrptr->direntptr, + listhdrptr->max * sizeof(Cfg_dirent)); + } + ptr = malloc(namelen+1+is_dir); + cfg_strncpy(ptr, nameptr, namelen+1); + if(is_dir && (namelen >= 1) && (ptr[namelen - 1] != '/')) { + // Add a trailing '/' to directories, unless already there + cfg_strlcat(ptr, "/", namelen + 1 + is_dir); + } +#if 0 + printf("...file entry %d is %s\n", listhdrptr->last, ptr); +#endif + direntptr = &(listhdrptr->direntptr[listhdrptr->last]); + direntptr->name = ptr; + direntptr->is_dir = is_dir; + direntptr->dsize = dsize; + direntptr->dimage_start = dimage_start; + direntptr->compr_dsize = compr_dsize; + direntptr->part_num = part_num; + listhdrptr->last++; +} + +int +cfg_dirent_sortfn(const void *obj1, const void *obj2) +{ + const Cfg_dirent *direntptr1, *direntptr2; + int ret; + + /* Called by qsort to sort directory listings */ + direntptr1 = (const Cfg_dirent *)obj1; + direntptr2 = (const Cfg_dirent *)obj2; + ret = cfg_str_match(direntptr1->name, direntptr2->name, CFG_PATH_MAX); + return ret; +} + +int +cfg_str_match(const char *str1, const char *str2, int len) +{ + return cfg_str_match_maybecase(str1, str2, len, g_cfg_ignorecase); +} + +int +cfg_str_match_maybecase(const char *str1, const char *str2, int len, + int ignorecase) +{ + const byte *bptr1, *bptr2; + int c, c2; + int i; + + /* basically, work like strcmp or strcasecmp */ + + bptr1 = (const byte *)str1; + bptr2 = (const byte *)str2; + for(i = 0; i < len; i++) { + c = *bptr1++; + c2 = *bptr2++; + if(ignorecase) { + c = tolower(c); + c2 = tolower(c2); + } + if((c == 0) || (c2 == 0) || (c != c2)) { + return c - c2; + } + } + + return 0; +} + +int +cfgcasecmp(const char *str1, const char *str2) +{ + return cfg_str_match_maybecase(str1, str2, 32767, 1); +} + +int +cfg_strlcat(char *dstptr, const char *srcptr, int dstsize) +{ + char *ptr; + int destlen, srclen, ret, c; + + // Concat srcptr to the end of dstptr, ensuring a null within dstsize + // Return the total buffer size that would be needed, even if dstsize + // is too small. Compat with strlcat() + destlen = (int)strlen(dstptr); + srclen = (int)strlen(srcptr); + ret = destlen + srclen; + dstsize--; + if(destlen >= dstsize) { + return ret; // Do nothing, buf too small + } + ptr = dstptr + destlen; + while(destlen < dstsize) { + c = *srcptr++; + *ptr++ = c; + if(c == 0) { + return ret; + } + destlen++; + } + dstptr[dstsize] = 0; + return ret; +} + +char * +cfg_strncpy(char *dstptr, const char *srcptr, int dstsize) +{ + char *ptr; + int c; + + // Copy srcptr to dstptr, ensuring there is room for a null + // Compatible with strncpy()--except dstptr is ALWAYS null terminated + ptr = dstptr; + while(--dstsize > 0) { + c = *srcptr++; + *ptr++ = c; + if(c == 0) { + return dstptr; + } + } + *ptr = 0; + return dstptr; +} + +const char * +cfg_str_basename(const char *str) +{ + int len; + int i; + + // If str is /aa/bb/cc, this routine returns cc + len = (int)strlen(str); + while(len && (str[len - 1] == '/')) { + len--; // Ignore trailing '/' if there are any + } + for(i = len - 1; i > 0; i--) { + if(str[i] == '/') { + return str + i + 1; + } + } + + return str; +} + +char * +cfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize) +{ + char *ptr; + int c; + + // If srcptr is /aa/bb/cc, this routine returns /aa/bb/ + // Copy srcptr to dstptr, ensuring there is room for a null + // Compatible with strncpy()--except dstptr is ALWAYS null terminated + ptr = dstptr; + while(--dstsize > 0) { + c = *srcptr++; + *ptr++ = c; + if(c == 0) { + // Remove any trailing /'s + ptr--; + while((ptr > dstptr) && (ptr[0] == '/')) { + ptr[0] = 0; + ptr--; + } + while(ptr > dstptr) { + if(ptr[0] == '/') { + ptr[1] = 0; + break; + } + ptr--; + } + return dstptr; + } + } + *ptr = 0; + return dstptr; +} + +void +cfg_file_readdir(const char *pathptr) +{ + struct dirent *direntptr; + struct stat stat_buf; + DIR *dirptr; + mode_t fmt; + char *str; + const char *tmppathptr; + int size, ret, is_dir, is_gz, len; + int i; + + if(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) && + (g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){ + return; + } + // No match, must read new directory + + // Free all dirents that were cached previously + cfg_free_alldirents(&g_cfg_dirlist); + + cfg_strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX); + cfg_strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX); + + str = &g_cfg_file_cachedreal[0]; + + for(i = 0; i < 200; i++) { + len = (int)strlen(str); + if(len <= 0) { + break; + } else if(len < CFG_PATH_MAX-2) { + if(str[len-1] != '/') { + // append / to make various routines work + str[len] = '/'; + str[len+1] = 0; + } + } + ret = cfg_stat(str, &stat_buf, 0); + is_dir = 0; + if(ret == 0) { + fmt = stat_buf.st_mode & S_IFMT; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } + } + if(is_dir) { + break; + } else { + // user is entering more path, use base for display + cfg_get_base_path(str, str, 0); + } + } + + tmppathptr = str; + if(str[0] == 0) { + tmppathptr = "."; + } + cfg_file_add_dirent(&g_cfg_dirlist, "..", 1, 0, 0, 0, -1); + + dirptr = opendir(tmppathptr); + if(dirptr == 0) { + printf("Could not open %s as a directory\n", tmppathptr); + return; + } + while(1) { + direntptr = readdir(dirptr); + if(direntptr == 0) { + break; + } + if(!strcmp(".", direntptr->d_name)) { + continue; + } + if(!strcmp("..", direntptr->d_name)) { + continue; + } + /* Else, see if it is a directory or a file */ + cfg_strncpy(&(g_cfg_tmp_path[0]), &(g_cfg_file_cachedreal[0]), + CFG_PATH_MAX); + cfg_strlcat(&(g_cfg_tmp_path[0]), direntptr->d_name, + CFG_PATH_MAX); + ret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf, 0); + len = (int)strlen(g_cfg_tmp_path); + is_dir = 0; + is_gz = 0; + if((len > 3) && !cfgcasecmp(&g_cfg_tmp_path[len - 3], ".gz")) { + is_gz = 1; + } + if((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], ".bz2")) { + is_gz = 1; + } + if((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], ".zip")) { + is_gz = 1; + } + if((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], ".woz")) { + is_gz = 1; + } + if((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], ".sdk")) { + is_gz = 1; + } + if(ret != 0) { + printf("stat %s ret %d, errno:%d\n", &g_cfg_tmp_path[0], + ret, errno); + stat_buf.st_size = 0; + continue; /* skip it */ + } else { + fmt = stat_buf.st_mode & S_IFMT; + size = (int)stat_buf.st_size; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } else if((fmt == S_IFREG) && (is_gz == 0)) { + if((g_cfg_slotdrive & 0xfff) == 0xfff) { + /* see if there are size limits */ + if((size < g_cfg_file_min_size) || + (size > g_cfg_file_max_size)) { + continue; /* skip it */ + } + } else { + if(size < 140*1024) { + continue; /* skip it */ + } + } + } + } + cfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir, + (dword64)stat_buf.st_size, 0, 0, -1); + } + + closedir(dirptr); + + /* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/ + qsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last, + sizeof(Cfg_dirent), cfg_dirent_sortfn); + + g_cfg_dirlist.curent = g_cfg_dirlist.last - 1; + for(i = g_cfg_dirlist.last - 1; i >= 0; i--) { + ret = cfg_str_match(&(g_cfg_file_match[0]), + g_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX); + if(ret <= 0) { + /* set cur ent to closest filename to the match name */ + g_cfg_dirlist.curent = i; + } + } +} + +char * +cfg_shorten_filename(const char *in_ptr, int maxlen) +{ + char *out_ptr; + int len, c; + int i; + + /* Warning: uses a static string, not reentrant! */ + + out_ptr = &(g_cfg_file_shortened[0]); + len = (int)strlen(in_ptr); + maxlen = MY_MIN(len, maxlen); + for(i = 0; i < maxlen; i++) { + c = in_ptr[i] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[i] = c; + } + out_ptr[maxlen] = 0; + if(len > maxlen) { + for(i = 0; i < (maxlen/2); i++) { + c = in_ptr[len-i-1] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[maxlen-i-1] = c; + } + out_ptr[(maxlen/2) - 1] = '.'; + out_ptr[maxlen/2] = '.'; + out_ptr[(maxlen/2) + 1] = '.'; + } + + return out_ptr; +} + +void +cfg_fix_topent(Cfg_listhdr *listhdrptr) +{ + int num_to_show; + + num_to_show = listhdrptr->num_to_show; + + /* Force curent and topent to make sense */ + if(listhdrptr->curent >= listhdrptr->last) { + listhdrptr->curent = listhdrptr->last - 1; + } + if(listhdrptr->curent < 0) { + listhdrptr->curent = 0; + } + if(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent > listhdrptr->curent) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent < 0) { + listhdrptr->topent = 0; + } +} + +void +cfg_file_draw() +{ + Cfg_listhdr *listhdrptr; + Cfg_dirent *direntptr; + const char *tmp_str; + char *str, *fmt; + int num_to_show; + int yoffset; + int x, y; + int i; + + //printf("cfg_file_draw called\n"); + + cfg_file_readdir(&g_cfg_file_curpath[0]); + + for(y = 0; y < 21; y++) { + cfg_htab_vtab(0, y); + cfg_printf("\tZ\t"); + for(x = 1; x < 79; x++) { + cfg_htab_vtab(x, y); + cfg_putchar(' '); + } + cfg_htab_vtab(79, y); + cfg_printf("\t_\t"); + } + + cfg_htab_vtab(1, 0); + cfg_putchar('\b'); + for(x = 1; x < 79; x++) { + cfg_putchar(' '); + } + if((g_cfg_slotdrive & 0xfff) == 0xfff) { + cfg_htab_vtab(5, 0); + cfg_printf("\bSelect file to use as %-40s\b", + cfg_shorten_filename(g_cfg_file_def_name, 40)); + } else { + cfg_htab_vtab(30, 0); + tmp_str = "Select"; + if(g_cfg_newdisk_select == 2) { + tmp_str = "Create duplicate"; + } else if(g_cfg_newdisk_select) { + tmp_str = "Create new"; + } + cfg_printf("\b%s image for s%dd%d\b", tmp_str, + (g_cfg_slotdrive >> 8) & 0xf, + (g_cfg_slotdrive & 0xff) + 1); + } + + cfg_htab_vtab(2, 1); + cfg_printf("config.kegs path: %-56s", + cfg_shorten_filename(&g_config_kegs_name[0], 56)); + + cfg_htab_vtab(2, 2); + cfg_printf("Current KEGS directory: %-50s", + cfg_shorten_filename(&g_cfg_cwd_str[0], 50)); + + cfg_htab_vtab(2, 3); + + str = ""; + if(g_cfg_file_pathfield) { + str = "\b \b"; + } + cfg_printf("Path: %s%s", + cfg_shorten_filename(&g_cfg_file_curpath[0], 68), str); + + cfg_htab_vtab(0, 4); + cfg_printf(" \t"); + for(x = 1; x < 79; x++) { + cfg_putchar('\\'); + } + cfg_printf("\t "); + + /* Force curent and topent to make sense */ + listhdrptr = &g_cfg_dirlist; + num_to_show = CFG_NUM_SHOWENTS; + yoffset = 5; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + num_to_show -= 2; + cfg_htab_vtab(2, yoffset); + cfg_printf("Select partition of %-50s", + cfg_shorten_filename(&g_cfg_file_path[0], 50), ""); + cfg_htab_vtab(2, yoffset + 1); + cfg_printf("Current partition: %-50s", + cfg_shorten_filename(&g_cfg_part_path[0], 50), ""); + yoffset += 2; + } + + listhdrptr->num_to_show = num_to_show; + cfg_fix_topent(listhdrptr); + for(i = 0; i < num_to_show; i++) { + y = i + yoffset; + if(listhdrptr->last > (i + listhdrptr->topent)) { + direntptr = &(listhdrptr-> + direntptr[i + listhdrptr->topent]); + cfg_htab_vtab(2, y); + if(direntptr->is_dir) { + cfg_printf("\tXY\t "); + } else { + cfg_printf(" "); + } + if(direntptr->part_num >= 0) { + cfg_printf("%3d: ", direntptr->part_num); + } + str = cfg_shorten_filename(direntptr->name, 50); + fmt = "%-50s"; + if((i + listhdrptr->topent) == listhdrptr->curent) { + if(g_cfg_file_pathfield == 0) { + fmt = "\b%-50s\b"; + } else { + fmt = "%-49s\b \b"; + } + //printf("file highlight l %d top:%d cur:%d\n", + // i, listhdrptr->topent, + // listhdrptr->curent); + } + cfg_printf(fmt, str); + if(!direntptr->is_dir) { + cfg_print_dnum(direntptr->dsize, 18); + } + //printf(" :%s:%lld:\n", str, direntptr->dsize); + } + } + + cfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS); + cfg_putchar('\t'); + for(x = 1; x < 79; x++) { + cfg_putchar('L'); + } + cfg_putchar('\t'); + + //printf("cfg_file_draw done\n"); +} + +void +cfg_partition_select_all() +{ + word32 slot_drive; + int part_path_len, curent; + + slot_drive = g_cfg_slotdrive; + part_path_len = (int)strlen(&g_cfg_part_path[0]); + curent = g_cfg_partitionlist.curent; + while(1) { + g_cfg_slotdrive = slot_drive; + g_cfg_partitionlist.curent = curent; + cfg_partition_selected(); + if(g_cfg_slotdrive != 0) { + // Something went wrong, get out + return; + } + slot_drive++; + curent++; + g_cfg_part_path[part_path_len] = 0; + if(curent >= g_cfg_partitionlist.last) { + return; + } + if((slot_drive >> 8) == 7) { + if((slot_drive & 0xff) >= MAX_C7_DISKS) { + return; + } + if((slot_drive & 0xff) >= 12) { + return; + } + } else if((slot_drive & 0xff) >= 2) { + return; + } + } +} + +void +cfg_partition_selected() +{ + char *str; + const char *part_str; + char *part_str2; + int pos; + int part_num; + + pos = g_cfg_partitionlist.curent; + str = g_cfg_partitionlist.direntptr[pos].name; + if(g_cfg_partitionlist.direntptr[pos].is_dir) { + // Add this path to the partition path, and try again + if(!strcmp(str, "../")) { + /* go up one directory */ + cfg_get_base_path(&g_cfg_part_path[0], + &g_cfg_part_path[0], 1); + } else { + cfg_strlcat(&(g_cfg_part_path[0]), str, CFG_PATH_MAX); + } + cfg_partition_make_list_from_name(&g_cfg_file_path[0]); + return; + } + + part_num = -2; + part_str = 0; + if(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) { + part_num = g_cfg_partitionlist.direntptr[pos].part_num; + } else { + part_str = str; + } + part_str2 = 0; + if(part_str != 0) { + cfg_strlcat(&g_cfg_part_path[0], part_str, CFG_PATH_MAX); + part_str2 = kegs_malloc_str(&g_cfg_part_path[0]); + g_cfg_part_path[0] = 0; + } + printf("cfg_partition_selected, pos:%d, g_cfg_file_path[0]:%s, " + "part:%s\n", pos, g_cfg_file_path, part_str2); + + insert_disk((g_cfg_slotdrive >> 8) & 0xf, g_cfg_slotdrive & 0xff, + &(g_cfg_file_path[0]), 0, part_str2, part_num, 0); + free(part_str2); + g_cfg_slotdrive = 0; + g_cfg_newdisk_select = 0; + g_cfg_select_partition = -1; +} + +void +cfg_file_selected() +{ + struct stat stat_buf; + char *str; + int fmt, stat_errno, is_cmd_key_down; + int ret; + + is_cmd_key_down = adb_is_cmd_key_down() && + ((g_cfg_slotdrive & 0xfff) != 0xfff); + // Cmd-Return means create DynaPro image when using slot/drive + + if(g_cfg_select_partition > 0) { + cfg_partition_selected(); + return; + } + + if(!is_cmd_key_down && (g_cfg_file_pathfield == 0)) { + // in file section area of window + str = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name; + if(!strcmp(str, "../")) { + /* go up one directory */ + cfg_get_base_path(&g_cfg_file_curpath[0], + &g_cfg_file_curpath[0], 1); + return; + } + + cfg_strncpy(&(g_cfg_file_path[0]), &(g_cfg_file_cachedreal[0]), + CFG_PATH_MAX); + cfg_strlcat(&(g_cfg_file_path[0]), str, CFG_PATH_MAX); + } else { + // just use cfg_file_curpath directly + cfg_strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0], + CFG_PATH_MAX); + } + + ret = cfg_stat(&g_cfg_file_path[0], &stat_buf, 0); + stat_errno = errno; + fmt = stat_buf.st_mode & S_IFMT; + cfg_printf("Stat'ing %s, st_mode is: %08x\n", &g_cfg_file_path[0], + (int)stat_buf.st_mode); + + if((ret == 0) && (fmt == S_IFDIR) && is_cmd_key_down && + (g_cfg_newdisk_select != 2)) { + // Make a new DynaPro disk + cfg_insert_disk_dynapro((g_cfg_slotdrive >> 8) & 0xf, + g_cfg_slotdrive & 0xff, &g_cfg_file_path[0]); + g_cfg_slotdrive = 0; // End file selection + g_cfg_newdisk_select = 0; + g_menuptr = &g_cfg_disk_menu[0]; + } else if((g_cfg_newdisk_select == 1) && (g_cfg_newdisk_type == 3) && + g_cfg_file_pathfield && (fmt == S_IFDIR)) { + // Special handling for Dynamic ProDOS directories. User hit + // return in the Path field on a directory, use this directory + cfg_create_new_image(); + } if(ret != 0) { + if(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) { + // This looks good, a new file name was entered + if(stat_errno == ENOENT) { + cfg_create_new_image(); + } else { + printf("Unknown errno:%d while checking %s\n", + stat_errno, &g_cfg_file_path[0]); + } + } else { + printf("stat %s returned %d, errno: %d\n", + &g_cfg_file_path[0], ret, stat_errno); + } + } else if(fmt == S_IFDIR) { + /* it's a directory */ + cfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0], + CFG_PATH_MAX); + } else if(g_cfg_newdisk_select) { + // Do not allow selecting files, just ignore it + } else if((g_cfg_slotdrive & 0xfff) < 0xfff) { + /* select it */ + ret = cfg_maybe_insert_disk((g_cfg_slotdrive >> 8) & 0xf, + g_cfg_slotdrive & 0xff, &g_cfg_file_path[0]); + if(ret > 0) { + g_cfg_slotdrive = 0; + g_cfg_newdisk_select = 0; + } + } else { + cfg_file_update_ptr(g_cfg_file_strptr, &g_cfg_file_path[0], 1); + g_cfg_slotdrive = 0; + g_cfg_newdisk_select = 0; + } +} + +void +cfg_file_handle_key(int key) +{ + Cfg_listhdr *listhdrptr; + int len, lowkey, got_match_key, is_cmd_key_down; + + // Modes: g_cfg_slotdrive: 1 to 0xfff: File selection dialog + // otherwise: normal menu being shown + // g_cfg_file_pathfield: File selection with cursor in Path: field + // otherwise: in scrolling file selection field + // g_cfg_select_partition: file selection for partition name + if(g_cfg_file_pathfield) { + if(key >= 0x20 && key < 0x7f) { + len = (int)strlen(&g_cfg_file_curpath[0]); + if(len < CFG_PATH_MAX-4) { + g_cfg_file_curpath[len] = key; + g_cfg_file_curpath[len+1] = 0; + } + return; + } + } + + listhdrptr = &g_cfg_dirlist; + is_cmd_key_down = 0; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + is_cmd_key_down = adb_is_cmd_key_down() && + ((g_cfg_slotdrive & 0xfff) != 0xfff); + } + lowkey = tolower(key); + got_match_key = 0; + if((g_cfg_file_pathfield == 0) && (lowkey >= 'a') && (lowkey <= 'z') && + !is_cmd_key_down) { + /* jump to file starting with this letter */ + g_cfg_file_match[0] = key; + g_cfg_file_match[1] = 0; + g_cfg_dirlist.invalid = 1; /* re-read directory */ + got_match_key = 1; + } + + switch(key) { + case 0x1b: // ESC + if(((g_cfg_slotdrive & 0xfff) < 0xfff) && + !g_cfg_newdisk_select) { + iwm_eject_disk_by_num((g_cfg_slotdrive >> 8) & 0xf, + g_cfg_slotdrive & 0xff); + } + g_cfg_slotdrive = 0; + g_cfg_select_partition = -1; + g_cfg_dirlist.invalid = 1; + g_cfg_newdisk_select = 0; + break; + case 0x0a: /* down arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent++; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0b: /* up arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent--; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0d: /* return */ + //printf("handling return press\n"); + cfg_file_selected(); + break; + case 0x61: /* 'a' */ + if(is_cmd_key_down && (g_cfg_select_partition > 0)) { + cfg_partition_select_all(); + } + break; + case 0x09: /* tab */ + g_cfg_file_pathfield = !g_cfg_file_pathfield; + if(g_cfg_select_partition > 0) { + // If selecting file inside zip or partition, don't + // allow editing of the Path info + g_cfg_file_pathfield = 0; + } + break; + case 0x08: /* left arrow */ + case 0x7f: /* delete key */ + if(g_cfg_file_pathfield) { + // printf("left arrow/delete\n"); + len = (int)strlen(&g_cfg_file_curpath[0]) - 1; + if(len >= 0) { + g_cfg_file_curpath[len] = 0; + } + } + break; + default: + if(!got_match_key) { + printf("key: %02x\n", key); + } + } +#if 0 + printf("curent: %d, topent: %d, last: %d\n", + g_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last); +#endif +} + +void +cfg_draw_menu() +{ + const char *str; + Cfg_menu *menuptr; + int print_eject_help, line, type, match_found, menu_line, max_line; + + g_menu_redraw_needed = 0; + + menuptr = g_menuptr; + if(menuptr == 0) { + menuptr = g_cfg_main_menu; + } + if(g_rom_version < 0) { + /* Must select ROM file */ + menuptr = g_cfg_rom_menu; + } + g_menuptr = menuptr; + + cfg_home(); + line = 1; + max_line = 1; + match_found = 0; + print_eject_help = 0; + menu_line = g_menu_line; + cfg_printf("%s\n\n", menuptr[0].str); + while(line < 24) { + str = menuptr[line].str; + type = menuptr[line].cfgtype; + if(str == 0) { + break; + } + if((type & 0xf) == CFGTYPE_DISK) { + print_eject_help = 1; + } +#if 0 + printf("Calling parse_menu line:%d, menu_line:%d, %p\n", line, + menu_line, menuptr); +#endif + cfg_parse_menu(menuptr, line, menu_line, 0); + if(line == g_menu_line) { + if(type != 0) { + match_found = 1; + } else if(g_menu_inc) { + menu_line++; + } else { + menu_line--; + } + } + if(line > max_line) { + max_line = line; + } + + cfg_printf("%s\n", g_cfg_opt_buf); + line++; + } + if((menu_line < 1) && !match_found) { + menu_line = 1; + } + if((menu_line >= max_line) && !match_found) { + menu_line = max_line; + } + + g_menu_line = menu_line; + g_menu_max_line = max_line; + if(!match_found) { + g_menu_redraw_needed = 1; + } + if(g_rom_version < 0) { + cfg_htab_vtab(0, 21); + cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); + } + + cfg_htab_vtab(0, 23); + cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); + if(print_eject_help) { + cfg_printf(" Eject: "); + if((g_cfg_slotdrive & 0xfff) > 0) { + cfg_printf("\bESC\b"); + } else { + cfg_printf("E"); + cfg_printf(" New image: N Dup image: D Verify: V"); + } + } + if((g_cfg_slotdrive & 0xfff) > 0) { + cfg_printf(" Edit Path: \bTAB\b"); + if(g_cfg_select_partition > 0) { + cfg_printf(" (\bCmd\b-\bA\b to mount all)"); + } else if((g_cfg_newdisk_type == 3) || !g_cfg_newdisk_select) { + // Dynamic ProDOS, select a directory + cfg_printf(" (\bCmd\b-\bEnter\b for DynaPro)"); + } + if(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) { + cfg_printf(" (Enter new name on Path)"); + } + } +#if 0 + cfg_htab_vtab(0, 22); + cfg_printf("menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\n", + menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, + g_key_down); +#endif + + if((g_cfg_slotdrive & 0xfff) > 0) { + cfg_file_draw(); + } +} + +void +cfg_newdisk_pick_menu(word32 slotdrive) +{ + slotdrive = slotdrive & 0xfff; + g_cfg_newdisk_slotdrive = slotdrive; // 0x601: s6d2, 0x500: s5d1 + g_menu_line = 1; + //printf("N key, g_menuptr=%p\n", g_menuptr); + g_cfg_newdisk_type_default = 1; + g_cfg_newdisk_type = 1; + g_cfg_newdisk_blocks_default = 140*2; + g_cfg_newdisk_blocks = 280; + if((slotdrive >> 8) == 6) { + g_menuptr = g_cfg_newslot6_menu; + } else if((slotdrive >> 8) == 5) { + g_menuptr = g_cfg_newslot5_menu; + g_cfg_newdisk_blocks_default = 1600; + g_cfg_newdisk_blocks = 1600; + } else { + g_menuptr = g_cfg_newslot7_menu; + g_cfg_newdisk_blocks_default = 65535; + g_cfg_newdisk_blocks = 65535; + } +} + +int +cfg_control_panel_update() +{ + int ret; + int i; + + ret = cfg_control_panel_update1(); + if(g_cfg_screen_changed) { + for(i = 0; i < 24; i++) { + video_draw_a2_string(i, &g_cfg_screen[i][0]); + } + } + g_cfg_screen_changed = 0; + + return ret; +} + +void +cfg_edit_mode_key(int key) +{ + char *new_str; + int *iptr; + int len, ival; + + len = (int)strlen(&g_cfg_edit_buf[0]); + if(key == 0x0d) { // Return + // Try to accept the change + new_str = kegs_malloc_str(&g_cfg_edit_buf[0]); + if(g_cfg_edit_type == CFGTYPE_STR) { + cfg_file_update_ptr(g_cfg_edit_ptr, new_str, 1); + } else if(g_cfg_edit_type == CFGTYPE_INT) { + ival = strtol(&g_cfg_edit_buf[0], 0, 0); + iptr = (int *)g_cfg_edit_ptr; + cfg_int_update(iptr, ival); + } + g_cfg_edit_ptr = 0; + g_config_kegs_update_needed = 1; + } else if(key == 0x1b) { // ESC + g_cfg_edit_ptr = 0; // Abort out of edit mode, no changes + } else if((key == 0x08) || (key == 0x7f)) { // Left arrow or Delete + len--; + if(len >= 0) { + g_cfg_edit_buf[len] = 0; + } + } else if((key >= 0x20) && (key < 0x7f)) { + if(len < (CFG_OPT_MAXSTR - 3)) { + g_cfg_edit_buf[len] = key; + g_cfg_edit_buf[len+1] = 0; + } + } +} + +int +cfg_control_panel_update1() +{ + char *(*fn_ptr)(int); + void *ptr; + char **str_ptr; + int *iptr; + int type, key; + + while(g_config_control_panel) { + if(g_menu_redraw_needed) { + cfg_draw_menu(); + } + if(g_menu_redraw_needed) { + cfg_draw_menu(); + } + key = adb_read_c000(); + if(key & 0x80) { + key = key & 0x7f; + (void)adb_access_c010(); + } else { + return 0; // No keys + } + g_menu_redraw_needed = 1; + // If we get here, we got a key, figure out what to do with it + if(g_cfg_slotdrive & 0xfff) { + cfg_file_handle_key(key); + continue; + } + if(g_cfg_edit_ptr) { + cfg_edit_mode_key(key); + continue; + } + + // Normal menu system + switch(key) { + case 0x0a: /* down arrow */ + g_menu_line++; + g_menu_inc = 1; + break; + case 0x0b: /* up arrow */ + g_menu_line--; + g_menu_inc = 0; + if(g_menu_line < 1) { + g_menu_line = 1; + } + break; + case 0x15: /* right arrow */ + cfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, 1); + break; + case 0x08: /* left arrow */ + cfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, -1); + break; + case 0x0d: + type = g_menuptr[g_menu_line].cfgtype; + ptr = g_menuptr[g_menu_line].ptr; + switch(type & 0xf) { + case CFGTYPE_MENU: + g_menuptr = (Cfg_menu *)ptr; + g_menu_line = 1; + break; + case CFGTYPE_DISK: + g_cfg_slotdrive = (type >> 4) & 0xfff; + cfg_file_init(); + break; + case CFGTYPE_FUNC: + fn_ptr = (char * (*)(int))ptr; + (void)(*fn_ptr)(0); + break; + case CFGTYPE_FILE: + g_cfg_slotdrive = 0xfff; + g_cfg_file_def_name = *((char **)ptr); + g_cfg_file_strptr = (char **)ptr; + cfg_file_init(); + break; + case CFGTYPE_STR: + str_ptr = (char **)ptr; + if(str_ptr) { + g_cfg_edit_type = type & 0xf; + g_cfg_edit_ptr = str_ptr; + cfg_strncpy(&g_cfg_edit_buf[0], + *str_ptr, CFG_OPT_MAXSTR); + } + break; + case CFGTYPE_INT: + // If there are no ',' in the menu str, then + // allow user to enter a manual number + if(!strchr(g_menuptr[g_menu_line].str, ',')) { + g_cfg_edit_type = type & 0xf; + g_cfg_edit_ptr = ptr; + iptr = (int *)ptr; + snprintf(&g_cfg_edit_buf[0], + CFG_OPT_MAXSTR, "%d", *iptr); + } + } + break; + case 0x1b: + // Jump to last menu entry + g_menu_line = g_menu_max_line; + break; + case 'd': + case 'D': // Duplicate an image + type = g_menuptr[g_menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + cfg_dup_existing_image(type >> 4); + } + break; + case 'e': + case 'E': + type = g_menuptr[g_menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + iwm_eject_disk_by_num(type >> 12, + (type >> 4) & 0xff); + } + break; + case 'l': + case 'L': + type = g_menuptr[g_menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + cfg_toggle_lock_disk(type >> 4); + } + break; + case 'n': + case 'N': + type = g_menuptr[g_menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + cfg_newdisk_pick_menu(type >> 4); + } + break; + case 'v': + case 'V': + type = g_menuptr[g_menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + cfg_validate_image(type >> 4); + } + break; + default: + printf("key: %02x\n", key); + } + } + + return 0; +} + diff --git a/gsplus/src/config.h b/gsplus/src/config.h new file mode 100644 index 0000000..1522f5b --- /dev/null +++ b/gsplus/src/config.h @@ -0,0 +1,36 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2019 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#define CONF_BUF_LEN 1024 +#define COPY_BUF_SIZE 4096 +#define CFG_PRINTF_BUFSIZE 2048 + +#define CFG_PATH_MAX 1024 + +#define CFG_NUM_SHOWENTS 16 + +#define CFGTYPE_MENU 1 +#define CFGTYPE_INT 2 +#define CFGTYPE_DISK 3 +#define CFGTYPE_FUNC 4 +#define CFGTYPE_FILE 5 +#define CFGTYPE_STR 6 +/* CFGTYPE limited to just 4 bits: 0-15 */ + +/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */ + +STRUCT(Cfg_defval) { + Cfg_menu *menuptr; + int intval; + char *strval; +}; diff --git a/gsplus/src/cp_gsplus_libs b/gsplus/src/cp_gsplus_libs new file mode 100644 index 0000000..10d9b9a --- /dev/null +++ b/gsplus/src/cp_gsplus_libs @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w +# $KmKId: cp_kegs_libs,v 1.2 2021-02-09 00:35:48+00 kentd Exp $ + +use strict; +use English; + +if($#ARGV < 2) { + die "Usage: executable srclib_dir destlib_dir"; +} + +# Runs objdump on the executable, finds all unresolved dependencies, and +# then copies each of those libraries from srclib_dir to destlib_dir +# destlib_dir should be APPLICATION/Contents/Frameworks/ + +my $exe = shift; +my $srcdir = shift; +my $destdir = shift; +if(! -f $exe || ! -d $srcdir || ! -d $destdir) { + die "$exe is not a file, or $srcdir or $destdir are not a dir"; +} + +my $do_swiftos = 0; +open(RPATHS, "objdump -macho -dylibs-used $exe|") or die "Open failed: $!"; +my $line; +my $lib; +foreach $line () { + chomp($line); + if($line =~ m:\@rpath/([^ ]*) :) { + $lib = $1; + print "lib: $lib\n"; + `cp $srcdir/$lib $destdir/`; + } + $do_swiftos = 1; +} + +# And copy libswiftos.dylib if we copied any files +if($do_swiftos) { + `cp $srcdir/libswiftos.dylib $destdir/`; +} diff --git a/gsplus/src/debugger.c b/gsplus/src/debugger.c new file mode 100644 index 0000000..5d7c703 --- /dev/null +++ b/gsplus/src/debugger.c @@ -0,0 +1,2173 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +#include +#include +#include "defc.h" + +#include "disas.h" + +#define LINE_SIZE 160 /* Input buffer size */ +#define PRINTF_BUF_SIZE 239 +#define DEBUG_ENTRY_MAX_CHARS 80 + +STRUCT(Debug_entry) { + byte str_buf[DEBUG_ENTRY_MAX_CHARS]; +}; + +char g_debug_printf_buf[PRINTF_BUF_SIZE]; +char g_debug_stage_buf[PRINTF_BUF_SIZE]; +int g_debug_stage_pos = 0; + +Debug_entry *g_debug_lines_ptr = 0; +int g_debug_lines_total = 0; +int g_debug_lines_pos = 0; +int g_debug_lines_alloc = 0; +int g_debug_lines_max = 1024*1024; +int g_debug_lines_view = -1; +int g_debug_lines_viewable_lines = 20; +int g_debugwin_changed = 1; +int g_debug_to_stdout = 1; + +extern byte *g_memory_ptr; +extern byte *g_slow_memory_ptr; +extern int g_halt_sim; +extern word32 g_c068_statereg; +extern word32 stop_run_at; +extern int Verbose; +extern int Halt_on; +extern int g_a2_key_to_ascii[][4]; +extern Kimage g_debugwin_kimage; + +extern int g_config_control_panel; +extern word32 g_mem_size_total; + +extern char *g_sound_file_str; +extern word32 g_sound_file_bytes; + +int g_num_breakpoints = 0; +Break_point g_break_pts[MAX_BREAK_POINTS]; + +extern int g_irq_pending; + +extern dword64 g_last_vbl_dcyc; +extern int g_ret1; +extern Engine_reg engine; +extern dword64 g_dcycles_end; + +int g_stepping = 0; + +word32 g_list_kpc; +int g_hex_line_len = 0x10; +word32 g_a1 = 0; +word32 g_a2 = 0; +word32 g_a3 = 0; +word32 g_a4 = 0; +word32 g_a1bank = 0; +word32 g_a2bank = 0; +word32 g_a3bank = 0; +word32 g_a4bank = 0; + +#define MAX_CMD_BUFFER 229 + +#define PC_LOG_LEN (2*1024*1024) + +Pc_log g_pc_log_array[PC_LOG_LEN + 2]; +Data_log g_data_log_array[PC_LOG_LEN + 2]; + +word32 g_log_pc_enable = 0; +Pc_log *g_log_pc_ptr = &(g_pc_log_array[0]); +Pc_log *g_log_pc_start_ptr = &(g_pc_log_array[0]); +Pc_log *g_log_pc_end_ptr = &(g_pc_log_array[PC_LOG_LEN]); + +Data_log *g_log_data_ptr = &(g_data_log_array[0]); +Data_log *g_log_data_start_ptr = &(g_data_log_array[0]); +Data_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]); + +char g_cmd_buffer[MAX_CMD_BUFFER + 2] = { 0 }; +int g_cmd_buffer_len = 2; + +#define MAX_DISAS_BUF 150 +char g_disas_buffer[MAX_DISAS_BUF]; + +void +debugger_init() +{ + debugger_help(); + g_list_kpc = engine.kpc; +#if 0 + if(g_num_breakpoints == 0) { + set_bp(0xff5a0e, 0xff5a0e, 4); + set_bp(0x00c50a, 0x00c50a, 4); + set_bp(0x00c50d, 0x00c50d, 4); + } +#endif +} + +int g_dbg_new_halt = 0; + +void +check_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack, + word32 type) +{ + Break_point *bp_ptr; + int count; + int i; + + count = g_num_breakpoints; + for(i = 0; i < count; i++) { + bp_ptr = &(g_break_pts[i]); + if((type & bp_ptr->acc_type) == 0) { + continue; + } + if((addr >= (bp_ptr->start_addr & 0xffffff)) && + (addr <= (bp_ptr->end_addr & 0xffffff))) { + debug_hit_bp(addr, dfcyc, maybe_stack, type, i); + } + } + + if((type == 4) && ((addr == 0xe10000) || (addr == 0xe10004))) { + FINISH(RET_TOOLTRACE, 0); + } +} + +void +debug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type, + int pos) +{ + word32 trk_side, side, trk, cmd, unit, buf, blk, param_cnt, list_ptr; + word32 status_code, cmd_list, stack, rts; + + if((addr == 0xff5a0e) && (type == 4)) { + trk_side = get_memory_c(0xe10f32); + side = (trk_side >> 5) & 1; + trk = get_memory_c(0xe10f34) + ((trk_side & 0x1f) << 6); + buf = get_memory_c(0x42) | (get_memory_c(0x43) << 8) | + (get_memory_c(0x44) << 16); + printf("ff5a0e: 3.5 read of track %03x side:%d sector:%03x to " + "%06x at %016llx\n", trk, side, get_memory_c(0xe10f33), + buf, dfcyc); + return; + } + if((addr == 0x00c50a) && (type == 4)) { + cmd = get_memory_c(0x42); + unit = get_memory_c(0x43); + buf = get_memory_c(0x44) | (get_memory_c(0x45) << 8); + blk = get_memory_c(0x46) | (get_memory_c(0x47) << 8); + printf("00c50a: cmd %02x u:%02x buf:%04x blk:%04x at %016llx\n", + cmd, unit, buf, blk, dfcyc); + return; + } + if((addr == 0x00c50d) && (type == 4)) { + stack = maybe_stack & 0xffff; + rts = get_memory_c(stack + 1) | (get_memory_c(stack + 2) << 8); + cmd = get_memory_c(rts + 1); + cmd_list = get_memory_c(rts + 2) | (get_memory_c(rts+3) << 8); + param_cnt = get_memory_c(cmd_list); + unit = get_memory_c(cmd_list + 1); + list_ptr = get_memory_c(cmd_list + 2) | + (get_memory_c(cmd_list + 3) << 8); + status_code = get_memory_c(cmd_list + 4); + printf("00c50d: stack:%04x rts:%04x cmd:%02x cmd_list:%04x " + "param_cnt:%02x unit:%02x listptr:%04x " + "status:%02x at %016llx\n", stack, rts, cmd, cmd_list, + param_cnt, unit, list_ptr, status_code, dfcyc); + printf(" list_ptr: %04x: %02x %02x %02x %02x %02x %02x %02x\n", + list_ptr, get_memory_c(list_ptr), + get_memory_c(list_ptr + 1), get_memory_c(list_ptr + 2), + get_memory_c(list_ptr + 3), get_memory_c(list_ptr + 4), + get_memory_c(list_ptr + 5), get_memory_c(list_ptr + 6)); + return; + } + + dbg_log_info(dfcyc, addr, pos, 0x6270); + halt2_printf("Hit breakpoint at %06x\n", addr); +} + +int +debugger_run_16ms() +{ + // Called when g_halt_sim is set + if(g_dbg_new_halt) { + g_list_kpc = engine.kpc; + show_regs(); + } + g_dbg_new_halt = 0; + adb_nonmain_check(); + // printf("debugger_run_16ms: g_halt_sim:%d\n", g_halt_sim); + return 0; +} + +void +dbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type) +{ + if(dfcyc == 0) { + return; // Ignore some IWM t:00e7 events and others + } + g_log_data_ptr->dfcyc = dfcyc; + g_log_data_ptr->stat = 0; + g_log_data_ptr->addr = info1; + g_log_data_ptr->val = info2; + g_log_data_ptr->size = type; // type must be > 4 + g_log_data_ptr++; + if(g_log_data_ptr >= g_log_data_end_ptr) { + g_log_data_ptr = g_log_data_start_ptr; + } +} + +void +debugger_update_list_kpc() +{ + g_dbg_new_halt = 1; +} + +void +debugger_key_event(Kimage *kimage_ptr, int a2code, int is_up) +{ + word32 c025_val, special; + int key, pos, changed; + + pos = 1; + + c025_val = kimage_ptr->c025_val; + + if(c025_val & 1) { // Shift is down + pos = 2; + } else if(c025_val & 4) { // Capslock is down + key = g_a2_key_to_ascii[a2code][1]; + if((key >= 'a') && (key <= 'z')) { + pos = 2; // CAPS LOCK on + } + } + if(c025_val & 2) { // Ctrl is down + pos = 3; + } + key = g_a2_key_to_ascii[a2code][pos]; + if(key < 0) { + return; + } + special = (key >> 8) & 0xff; // c025 changes + if(is_up) { + c025_val = c025_val & (~special); + } else { + c025_val = c025_val | special; + } + kimage_ptr->c025_val = c025_val; + if(is_up) { + return; // Nothing else to do + } + if(key >= 0x80) { + // printf("key: %04x\n", key); + if(key == 0x8007) { // F7 - close debugger + video_set_active(kimage_ptr, !kimage_ptr->active); + printf("Toggled debugger window to:%d\n", + kimage_ptr->active); + } + if((key & 0xff) == 0x74) { // Page up keycode + debugger_page_updown(1); + } + if((key & 0xff) == 0x79) { // Page down keycode + debugger_page_updown(-1); + } + return; + } + pos = g_cmd_buffer_len; + changed = 0; + if((key >= 0x20) && (key < 0x7f)) { + // printable character, add it + if(pos < MAX_CMD_BUFFER) { + // printf("cmd[%d]=%c\n", pos, key); + g_cmd_buffer[pos++] = key; + changed = 1; + } + } else if((key == 0x08) || (key == 0x7f)) { + // Left arrow or backspace + if(pos > 2) { + pos--; + changed = 1; + } + } else if((key == 0x0d) || (key == 0x0a)) { + //dbg_printf("Did return, pos:%d, str:%s\n", pos, g_cmd_buffer); + do_debug_cmd(&g_cmd_buffer[2]); + pos = 2; + changed = 1; + } else { + // printf("ctrl key:%04x\n", key); + } + g_cmd_buffer[pos] = 0; + g_cmd_buffer_len = pos; + g_debug_lines_view = -1; + g_debugwin_changed |= changed; + // printf("g_cmd_buffer: %s\n", g_cmd_buffer); +} + +void +debugger_page_updown(int isup) +{ + int view, max; + + view = g_debug_lines_view; + if(view < 0) { + view = 0; + } + view = view + (isup*g_debug_lines_viewable_lines); + if(view < 0) { + view = -1; + } + max = g_debug_lines_pos; + if(g_debug_lines_alloc >= g_debug_lines_max) { + max = g_debug_lines_alloc - 4; + } + view = MY_MIN(view, max - g_debug_lines_viewable_lines); + + // printf("new view:%d, was:%d\n", view, g_debug_lines_view); + if(view != g_debug_lines_view) { + g_debug_lines_view = view; + g_debugwin_changed++; + } +} + +void +debugger_redraw_screen(Kimage *kimage_ptr) +{ + int line, vid_line, back, border_top, save_pos, num, lines_done; + int save_view, save_to_stdout; + int i; + + if((g_debugwin_changed == 0) || (kimage_ptr->active == 0)) { + return; // Nothing to do + } + + save_pos = g_debug_lines_pos; + save_view = g_debug_lines_view; + // printf("DEBUGGER drawing SCREEN!\n"); + g_cmd_buffer[0] = '>'; + g_cmd_buffer[1] = ' '; + g_cmd_buffer[g_cmd_buffer_len] = 0xa0; // Cursor: inverse space + g_cmd_buffer[g_cmd_buffer_len+1] = 0; + save_to_stdout = g_debug_to_stdout; + g_debug_to_stdout = 0; + dbg_printf("%s\n", &g_cmd_buffer[0]); + g_cmd_buffer[g_cmd_buffer_len] = 0; + dbg_printf("g_halt_sim:%02x\n", g_halt_sim); + border_top = 8; + g_debug_to_stdout = save_to_stdout; + + vid_line = (((kimage_ptr->a2_height - 2*border_top) / 16) * 8) - 1; + num = g_debug_lines_pos - save_pos; + if(num < 0) { + num = num + g_debug_lines_alloc; + } + if(num > 4) { + // printf("num is > 4!\n"); + num = 4; + } + for(i = 0; i < num; i++) { + line = debug_get_view_line(i); + debug_draw_debug_line(kimage_ptr, line, vid_line); + vid_line -= 8; + } + g_debug_lines_pos = save_pos; + g_debug_lines_view = save_view; + back = save_view; + if(back < 0) { // -1 means always show most recent + back = 0; + } + lines_done = 0; + while(vid_line >= border_top) { + line = debug_get_view_line(back); + debug_draw_debug_line(kimage_ptr, line, vid_line); + back++; + vid_line -= 8; + lines_done++; +#if 0 + printf(" did a line, line is now: %d after str:%s\n", line, + str); +#endif + } + g_debug_lines_viewable_lines = lines_done; + g_debugwin_changed = 0; + kimage_ptr->x_refresh_needed = 1; + // printf("x_refresh_needed = 1, viewable_lines:%d\n", lines_done); +} + +void +debug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line) +{ + word32 line_bytes; + int i; + + // printf("draw debug line:%d at vid_line:%d\n", line, vid_line); + for(i = 7; i >= 0; i--) { + line_bytes = (vid_line << 16) | (40 << 8) | 0; + redraw_changed_string(&(g_debug_lines_ptr[line].str_buf[0]), + line_bytes, -1L, kimage_ptr->wptr + 8, 0, 0x00ffffff, + kimage_ptr->a2_width_full, 1); + vid_line--; + } +} + +Dbg_longcmd g_debug_bp_clear[] = { + { "all", debug_bp_clear_all, 0, + "clear all breakpoints" }, + { 0, 0, 0, 0 } +}; +Dbg_longcmd g_debug_bp[] = { + { "set", debug_bp_set, 0, + "Set breakpoint: ADDR or ADDR0-ADDR1" }, + { "clear", debug_bp_clear, &g_debug_bp_clear[0], + "Clear breakpoint: ADDR OR ADDR0-ADDR1"}, + { 0, 0, 0, 0 } +}; + +Dbg_longcmd g_debug_logpc[] = { + { "on", debug_logpc_on, 0, "Turn on logging of pc and data" }, + { "off", debug_logpc_off,0, "Turn off logging of pc and data" }, + { "save", debug_logpc_save,0, "logpc save FILE: save to file" }, + { 0, 0, 0, 0 } +}; + +Dbg_longcmd g_debug_iwm[] = { + { "check", debug_iwm_check, 0, "Denibblize current track" }, + { 0, 0, 0, 0 } +}; + +// Main table of commands +Dbg_longcmd g_debug_longcmds[] = { + { "help", debug_help, 0, "Help" }, + { "bp", debug_bp, &g_debug_bp[0], + "bp ADDR: sets breakpoint on addr" }, + { "logpc", debug_logpc, &g_debug_logpc[0], "Log PC" }, + { "iwm", debug_iwm, &g_debug_iwm[0], "IWM" }, + { "soundfile", debug_soundfile, 0, "Save sound to a WAV file" }, + { 0, 0, 0, 0 } +}; + +void +debugger_help() +{ + dbg_printf("KEGS Debugger help (courtesy Fredric Devernay\n"); + dbg_printf("General command syntax: [bank]/[address][command]\n"); + dbg_printf("e.g. 'e1/0010B' to set a breakpoint at the interrupt jump " + "pt\n"); + dbg_printf("Enter all addresses using lower-case\n"); + dbg_printf("As with the IIgs monitor, you can omit the bank number " + "after\n"); + dbg_printf("having set it: 'e1/0010B' followed by '14B' will set\n"); + dbg_printf("breakpoints at e1/0010 and e1/0014\n"); + dbg_printf("\n"); + dbg_printf("g Go\n"); + dbg_printf("[bank]/[addr]g Go from [bank]/[address]\n"); + dbg_printf("s Step one instruction\n"); + dbg_printf("[bank]/[addr]s Step one instr at [bank]/[addr]\n"); + dbg_printf("[bank]/[addr]B Set breakpoint at [bank]/[addr]\n"); + dbg_printf("B Show all breakpoints\n"); + dbg_printf("[bank]/[addr]D Delete breakpoint at [bank]/" + "[addr]\n"); + dbg_printf("[bank]/[addr1].[addr2] View memory\n"); + dbg_printf("[bank]/[addr]L Disassemble memory\n"); + + dbg_printf("Z Dump SCC state\n"); + dbg_printf("I Dump IWM state\n"); + dbg_printf("[drive].[track]I Dump IWM state\n"); + dbg_printf("E Dump Ensoniq state\n"); + dbg_printf("[osc]E Dump oscillator [osc] state\n"); + dbg_printf("R Dump dtime array and events\n"); + dbg_printf("T Show toolbox log\n"); + dbg_printf("[bank]/[addr]T Dump tools using ptr [bank]/" + "[addr]\n"); + dbg_printf(" as 'tool_set_info'\n"); + dbg_printf("[mode]V XOR verbose with 1=DISK, 2=IRQ,\n"); + dbg_printf(" 4=CLK,8=SHADOW,10=IWM,20=DOC,\n"); + dbg_printf(" 40=ABD,80=SCC, 100=TEST, 200=" + "VIDEO\n"); + dbg_printf("[mode]H XOR halt_on with 1=SCAN_INT,\n"); + dbg_printf(" 2=IRQ, 4=SHADOW_REG, 8=" + "C70D_WRITES\n"); + dbg_printf("r Reset\n"); + dbg_printf("[0/1]=m Changes m bit for l listings\n"); + dbg_printf("[0/1]=x Changes x bit for l listings\n"); + dbg_printf("S show_bankptr_bank0 & smartport " + "errs\n"); + dbg_printf("P show_pmhz\n"); + dbg_printf("A show_a2_line_stuff show_adb_log\n"); + dbg_printf("Ctrl-e Dump registers\n"); + dbg_printf("[bank]/[addr1].[addr2]us[file] Save mem area to [file]\n"); + dbg_printf("[bank]/[addr1].[addr2]ul[file] Load mem area from " + "[file]\n"); + dbg_printf("v Show video information\n"); + dbg_printf("q Exit Debugger (and KEGS)\n"); +} + +void +dbg_help_show_strs(int help_depth, const char *str, const char *help_str) +{ + const char *blank_str, *pre_str, *post_str; + int column, len, blank_len, pre_len, post_len; + + // Indent by 3*help_depth chars, then output str, then hit + // column 14, then output help_str. This can be done in just 2-3 + // lines, but I made it longer and clearer to avoid any "overflow" + // cases + if(help_str == 0) { + return; + } + blank_str = " " " " " "; + blank_len = (int)strlen(blank_str); // should be >=17 + column = 17; + len = (int)strlen(str); + if(help_depth < 0) { + help_depth = 0; + } + pre_str = blank_str; + pre_len = 3 * help_depth; + if(pre_len < blank_len) { + pre_str = blank_str + blank_len - pre_len; + } + post_str = ""; + post_len = column - pre_len - len; + if((post_len >= 1) && (post_len < blank_len)) { + post_str = blank_str + blank_len - post_len; + } + dbg_printf("%s%s%s: %s\n", pre_str, str, post_str, help_str); +} + +const char * +debug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr, + int help_depth) +{ + Dbg_fn *fnptr; + Dbg_longcmd *subptr; + const char *str, *newstr; + int len, c; + int i; + + // See if the command is from the longcmd list + while(*line_ptr == ' ') { + line_ptr++; // eat spaces + } + // Output " str :" where : is at column 14 always + // printf("dfcit: %s, help_depth:%d\n", line_ptr, help_depth); + for(i = 0; i < 1000; i++) { + // Provide a limit to avoid hang if table not terminated right + str = longptr[i].str; + fnptr = longptr[i].fnptr; + if(!str) { // End of table + break; // No match found + } + if(help_depth < 0) { + // Print the help string for all entries in this table + dbg_help_show_strs(-1 - help_depth, str, + longptr[i].help_str); + continue; + } + len = (int)strlen(str); + if(strncmp(line_ptr, str, len) != 0) { + continue; // Not a match + } + // Ensure next char is either a space, or 0 + // Let's us avoid commands which are prefixes, or + // which are old Apple II monitor hex+commands + c = line_ptr[len]; + if((c != 0) && (c != ' ')) { + continue; // Not valid + } + if(help_depth) { + dbg_help_show_strs(help_depth, str, + longptr[i].help_str); + } + subptr = longptr[i].subptr; + // Try a subcmd first + newstr = line_ptr + len; + if(subptr != 0) { + if(help_depth) { + help_depth++; + } + newstr = debug_find_cmd_in_table(newstr, subptr, + help_depth); + // If a subcmd was found, newstr is now 0 + } + if((newstr == 0) || help_depth) { + return 0; + } + if((newstr != 0) && (fnptr != 0)) { + (*fnptr)(line_ptr + len); + return 0; // Success + } + } + if(help_depth >= 1) { + // No subcommands found, print out all entries in this table + debug_find_cmd_in_table(line_ptr, longptr, -1 - help_depth); + return 0; + } + return line_ptr; +} + +void +do_debug_cmd(const char *in_str) +{ + const char *line_ptr; + const char *newstr; + int slot_drive, track, ret_val, mode, old_mode, got_num; + int save_to_stdout; + + mode = 0; + old_mode = 0; + + save_to_stdout = g_debug_to_stdout; + g_debug_to_stdout = 1; + dbg_printf("*%s\n", in_str); + line_ptr = in_str; + + // See if the command is from the longcmd list + newstr = debug_find_cmd_in_table(in_str, &(g_debug_longcmds[0]), 0); + if(newstr == 0) { + g_debug_to_stdout = save_to_stdout; + return; // Command found get out + } + + // If we get here, parse an Apple II monitor like command: + // {address}{cmd} repeat. + while(1) { + ret_val = 0; + g_a2 = 0; + got_num = 0; + while(1) { + if((mode == 0) && (got_num != 0)) { + g_a3 = g_a2; + g_a3bank = g_a2bank; + g_a1 = g_a2; + g_a1bank = g_a2bank; + } + ret_val = *line_ptr++ & 0x7f; + if((ret_val >= '0') && (ret_val <= '9')) { + g_a2 = (g_a2 << 4) + ret_val - '0'; + got_num = 1; + continue; + } + if((ret_val >= 'a') && (ret_val <= 'f')) { + g_a2 = (g_a2 << 4) + ret_val - 'a' + 10; + got_num = 1; + continue; + } + if(ret_val == '/') { + g_a2bank = g_a2; + g_a2 = 0; + continue; + } + break; + } + old_mode = mode; + mode = 0; + switch(ret_val) { + case 'h': + debugger_help(); + break; + case 'R': + show_dtime_array(); + show_all_events(); + break; + case 'I': + slot_drive = -1; + track = -1; + if(got_num) { + if(old_mode == '.') { + slot_drive = g_a1; + } + track = g_a2; + } + iwm_show_track(slot_drive, track, 0); + iwm_show_stats(slot_drive); + break; + case 'E': + doc_show_ensoniq_state(); + break; + case 'T': + if(got_num) { + show_toolset_tables(g_a2bank, g_a2); + } else { + show_toolbox_log(); + } + break; + case 'v': + if(got_num) { + dis_do_compare(); + } else { + video_show_debug_info(); + } + break; + case 'V': + dbg_printf("g_irq_pending: %05x\n", g_irq_pending); + dbg_printf("Setting Verbose ^= %04x\n", g_a1); + Verbose ^= g_a1; + dbg_printf("Verbose is now: %04x\n", Verbose); + break; + case 'H': + dbg_printf("Setting Halt_on ^= %04x\n", g_a1); + Halt_on ^= g_a1; + dbg_printf("Halt_on is now: %04x\n", Halt_on); + break; + case 'r': + do_reset(); + g_list_kpc = engine.kpc; + break; + case 'm': + if(old_mode == '=') { + if(!g_a1) { + engine.psr &= ~0x20; + } else { + engine.psr |= 0x20; + } + if(engine.psr & 0x100) { + engine.psr |= 0x30; + } + } else { + dis_do_memmove(); + } + break; + case 'p': + dis_do_pattern_search(); + break; + case 'x': + if(old_mode == '=') { + if(!g_a1) { + engine.psr &= ~0x10; + } else { + engine.psr |= 0x10; + } + if(engine.psr & 0x100) { + engine.psr |= 0x30; + } + } + break; + case 'z': + if(old_mode == '=') { + stop_run_at = g_a1; + dbg_printf("Calling add_event for t:%08x\n", + g_a1); + add_event_stop(((dword64)g_a1) << 16); + dbg_printf("set stop_run_at = %x\n", g_a1); + } + break; + case 'l': case 'L': + if(got_num) { + g_list_kpc = (g_a2bank << 16) + (g_a2 & 0xffff); + } + do_debug_list(); + break; + case 'Z': + show_scc_state(); + break; + case 'S': + show_bankptrs_bank0rdwr(); + smartport_error(); + break; + case 'M': + show_pmhz(); + mockingboard_show(got_num, g_a1); + break; + case 'A': + show_a2_line_stuff(); + show_adb_log(); + break; + case 's': + g_stepping = 1; + if(got_num) { + engine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff); + } + mode = 's'; + g_list_kpc = engine.kpc; + break; + case 'B': + if(got_num) { + dbg_printf("got_num:%d, a2bank:%x, g_a2:%x\n", + got_num, g_a2bank, g_a2); + set_bp((g_a2bank << 16) + g_a2, + (g_a2bank << 16) + g_a2, 4); + } else { + show_bp(); + } + break; + case 'D': + if(got_num) { + dbg_printf("got_num: %d, a2bank: %x, a2: %x\n", + got_num, g_a2bank, g_a2); + delete_bp((g_a2bank << 16) + g_a2, + (g_a2bank << 16) + g_a2); + } + break; + case 'g': + case 'G': + dbg_printf("Going..\n"); + g_stepping = 0; + if(got_num) { + engine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff); + } + do_go(); + g_list_kpc = engine.kpc; + break; + case 'u': + dbg_printf("Unix commands\n"); + line_ptr = do_debug_unix(line_ptr, old_mode); + break; + case ':': case '.': + case '+': case '-': + case '=': case ',': + mode = ret_val; + dbg_printf("Setting mode = %x\n", mode); + break; + case ' ': case '\t': + if(!got_num) { + mode = old_mode; + break; + } + mode = do_blank(mode, old_mode); + break; + case '<': + g_a4 = g_a2; + g_a4bank = g_a2bank; + break; + case 0x05: /* ctrl-e */ + case 'Q': + case 'q': + show_regs(); + break; + case 0: // The final null char + if(old_mode == 's') { + mode = do_blank(mode, old_mode); + g_debug_to_stdout = save_to_stdout; + return; + } + if(line_ptr == &in_str[1]) { + g_a2 = g_a1 | (g_hex_line_len - 1); + show_hex_mem(g_a1bank, g_a1, g_a2, -1); + g_a1 = g_a2 + 1; + } else { + if((got_num == 1) || (mode == 's')) { + mode = do_blank(mode, old_mode); + } + } + g_debug_to_stdout = save_to_stdout; + return; // Get out, all done + break; + default: + dbg_printf("\nUnrecognized command: %s\n", in_str); + g_debug_to_stdout = save_to_stdout; + return; + } + } +} + +word32 +dis_get_memory_ptr(word32 addr) +{ + word32 tmp1, tmp2, tmp3; + + tmp1 = get_memory_c(addr); + tmp2 = get_memory_c(addr + 1); + tmp3 = get_memory_c(addr + 2); + + return (tmp3 << 16) + (tmp2 << 8) + tmp1; +} + +void +show_one_toolset(FILE *toolfile, int toolnum, word32 addr) +{ + word32 rout_addr; + int num_routs; + int i; + + num_routs = dis_get_memory_ptr(addr); + fprintf(toolfile, "Tool 0x%02x, table: 0x%06x, num_routs:%03x\n", + toolnum, addr, num_routs); + if((addr < 0x10000) || (num_routs > 0x100)) { + fprintf(toolfile, "addr in page 0, or num_routs too large\n"); + return; + } + + for(i = 1; i < num_routs; i++) { + rout_addr = dis_get_memory_ptr(addr + 4*i); + fprintf(toolfile, "%06x = %02x%02x\n", rout_addr, i, toolnum); + } +} + +void +show_toolset_tables(word32 a2bank, word32 addr) +{ + FILE *toolfile; + word32 tool_addr; + int num_tools; + int i; + + addr = (a2bank << 16) + (addr & 0xffff); + + toolfile = fopen("tool_set_info", "w"); + if(toolfile == 0) { + fprintf(stderr, "fopen of tool_set_info failed: %d\n", errno); + exit(2); + } + + num_tools = dis_get_memory_ptr(addr); + fprintf(toolfile, "There are 0x%02x tools using ptr at %06x\n", + num_tools, addr); + + if(num_tools > 40) { + fprintf(toolfile, "Too many tools, aborting\n"); + num_tools = 0; + } + for(i = 1; i < num_tools; i++) { + tool_addr = dis_get_memory_ptr(addr + 4*i); + show_one_toolset(toolfile, i, tool_addr); + } + + fclose(toolfile); +} + +word32 +debug_getnum(const char **str_ptr) +{ + const char *str; + word32 val; + int c, got_num; + + str = *str_ptr; + while(*str == ' ') { + str++; + } + got_num = 0; + val = 0; + while(1) { + c = tolower(*str); + //printf("got c:%02x %c val was %08x got_num:%d\n", c, c, val, + // got_num); + if((c >= '0') && (c <= '9')) { + val = (val << 4) + (c - '0'); + got_num = 1; + } else if((c >= 'a') && (c <= 'f')) { + val = (val << 4) + 10 + (c - 'a'); + got_num = 1; + } else { + break; + } + str++; + } + *str_ptr = str; + if(got_num) { + return val; + } + return (word32)-1L; +} + +char * +debug_get_filename(const char **str_ptr) +{ + const char *str, *start_str; + char *new_str; + int c, len; + + // Go to first whitespace (or end of str), then kegs_malloc_str() + // the string and copy to it + str = *str_ptr; + start_str = 0; + //printf("get_filename, str now :%s:\n", str); + while(1) { + c = *str++; + if(c == 0) { + break; + } + if((c == ' ') || (c == '\t') || (c == '\n')) { + //printf("c:%02x at str :%s: , start_str:%p\n", c, str, + // start_str); + if(start_str) { + break; + } + continue; + } + // Else it's a valid char, set start_str if needed + if(!start_str) { + start_str = str - 1; + //printf("Got c:%02x, start_str :%s:\n", c, start_str); + } + } + new_str = 0; + if(start_str) { + len = (int)(str - start_str); + if(len > 1) { + new_str = malloc(len); + memcpy(new_str, start_str, len); + new_str[len - 1] = 0; + } + } + *str_ptr = str; + return new_str; +} + +void +debug_help(const char *str) +{ + dbg_printf("Help:\n"); + (void)debug_find_cmd_in_table(str, &(g_debug_longcmds[0]), 1); +} + +void +debug_bp(const char *str) +{ + // bp without a following set/clear command. Set a breakpoint if + // an address range follows, otherwise just print current breakpoints + debug_bp_setclr(str, 0); +} + +void +debug_bp_set(const char *str) +{ + debug_bp_setclr(str, 1); +} + +void +debug_bp_clear(const char *str) +{ + debug_bp_setclr(str, 2); +} + +void +debug_bp_clear_all(const char *str) +{ + if(str) { + // Use str to avoid warning + } + if(g_num_breakpoints) { + g_num_breakpoints = 0; + setup_pageinfo(); + dbg_printf("Deleted all breakpoints\n"); + } +} + +void +debug_bp_setclr(const char *str, int is_set_clear) +{ + word32 addr, end_addr, acc_type; + + printf("In debug_bp: %s\n", str); + + addr = debug_getnum(&str); + // printf("getnum ret:%08x\n", addr); + if(addr == (word32)-1L) { // No argument + show_bp(); + return; + } + end_addr = addr; + if(*str == '-') { // Range + str++; + end_addr = debug_getnum(&str); + // printf("end_addr is %08x\n", end_addr); + if(end_addr == (word32)-1L) { + end_addr = addr; + } + } + acc_type = 4; + acc_type = debug_getnum(&str); + if(acc_type == (word32)-1L) { + acc_type = 4; // Code breakpoint + } + if(is_set_clear == 2) { // clear + delete_bp(addr, end_addr); + } else { // set, or nothing + set_bp(addr, end_addr, acc_type); + } +} + +void +debug_soundfile(const char *cmd_str) +{ + char *str; + + // See if there's an argument + str = debug_get_filename(&cmd_str); // str=0 if no argument + sound_file_start(str); // str==0 means close file +} + +void +debug_logpc(const char *str) +{ + if(str) { + // Dummy use of argument + } + dbg_printf("logpc enable:%d, cur offset:%08lx\n", g_log_pc_enable, + (long)(g_log_pc_ptr - g_log_pc_start_ptr)); +} + +void +debug_logpc_on(const char *str) +{ + if(str) { + // Dummy use of argument + } + g_log_pc_enable = 1; + g_dcycles_end = 0; + dbg_printf("Enabled logging of PC and data accesses\n"); +} + +void +debug_logpc_off(const char *str) +{ + if(str) { + // Dummy use of argument + } + g_log_pc_enable = 0; + g_dcycles_end = 0; + dbg_printf("Disabled logging of PC and data accesses\n"); +} + +void +debug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc) +{ + char *str, *shadow_str; + dword64 lstat, offset64, offset64slow, addr64; + word32 wstat, addr, size, val; + + addr = log_data_ptr->addr; + lstat = (dword64)(log_data_ptr->stat); + wstat = lstat & 0xff; + addr64 = lstat - wstat + (addr & 0xff); + offset64 = addr64 - (dword64)&(g_memory_ptr[0]); + str = "IO"; + shadow_str = ""; + if((wstat & BANK_SHADOW) || (wstat & BANK_SHADOW2)) { + shadow_str = "SHADOWED"; + } + size = log_data_ptr->size; + if(size > 32) { + fprintf(pcfile, "INFO %08x %08x %04x t:%04x %lld.%02lld\n", + log_data_ptr->addr, log_data_ptr->val, size >> 16, + size & 0xffff, (log_data_ptr->dfcyc - start_dcyc)>>16, + ((log_data_ptr->dfcyc & 0xffff) * 100) >> 16); + } else { + offset64slow = addr64 - (dword64)&(g_slow_memory_ptr[0]); + if(offset64 < g_mem_size_total) { + str = "mem"; + } else if(offset64slow < 0x20000) { + str = "slow_mem"; + offset64 = offset64slow; + } else { + str = "IO"; + offset64 = offset64 & 0xff; + } + val = log_data_ptr->val; + fprintf(pcfile, "DATA set %06x = ", addr); + if(size == 8) { + fprintf(pcfile, "%02x (8) ", val & 0xff); + } else if(size == 16) { + fprintf(pcfile, "%04x (16) ", val & 0xffff); + } else { + fprintf(pcfile, "%06x (%d) ", val, size); + } + fprintf(pcfile, "%lld.%02lld, %s[%06llx] %s\n", + (log_data_ptr->dfcyc - start_dcyc) >> 16, + ((log_data_ptr->dfcyc & 0xffff) * 100) >> 16, + str, offset64 & 0xffffffULL, shadow_str); + } +} + +Data_log * +debug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc, + dword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr, + int *count_ptr) +{ + + while((*data_wrap_ptr < 2) && (log_data_ptr->dfcyc <= dfcyc) && + (log_data_ptr->dfcyc >= start_dcyc)) { + if(*count_ptr >= PC_LOG_LEN) { + break; + } + debug_logpc_out_data(pcfile, log_data_ptr, base_dcyc); + if(log_data_ptr->dfcyc == 0) { + break; + } + log_data_ptr++; + (*count_ptr)++; + if(log_data_ptr >= g_log_data_end_ptr) { + log_data_ptr = g_log_data_start_ptr; + (*data_wrap_ptr)++; + } + } + return log_data_ptr; +} + +void +debug_logpc_save(const char *cmd_str) +{ + FILE *pcfile; + Pc_log *log_pc_ptr; + Data_log *log_data_ptr; + char *str; + dword64 dfcyc, start_dcyc, base_dcyc, max_dcyc; + word32 instr, psr, acc, xreg, yreg, stack, direct, dbank, kpc, num; + int data_wrap, accsize, xsize, abs_time, data_count; + int i; + + // See if there's an argument + num = debug_getnum(&cmd_str); + abs_time = 1; + if(num != (word32)-1L) { + dbg_printf("Doing relative time\n"); + abs_time = 0; + } + + pcfile = fopen("logpc_out", "w"); + if(pcfile == 0) { + fprintf(stderr,"fopen failed...errno: %d\n", errno); + exit(2); + } + + log_pc_ptr = g_log_pc_ptr; + log_data_ptr = g_log_data_ptr; +#if 0 + printf("debug_logpc_save called, log_pc_ptr:%p, %p,%p log_data_ptr:%p, " + "%p,%p\n", log_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr, + log_data_ptr, g_log_data_start_ptr, g_log_data_end_ptr); +#endif +#if 0 + fprintf(pcfile, "current pc_log_ptr: %p, start: %p, end: %p\n", + log_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr); +#endif + + // See if we haven't filled buffer yet + if(log_pc_ptr->dfcyc == 0) { + log_pc_ptr = g_log_pc_start_ptr; + } + if(log_data_ptr->dfcyc == 0) { + log_data_ptr = g_log_data_start_ptr; + data_wrap = 1; + } + + start_dcyc = log_pc_ptr->dfcyc; + // Round to an exact usec + start_dcyc = (start_dcyc >> 16) << 16; + base_dcyc = start_dcyc; + if(abs_time) { + base_dcyc = 0; // Show absolute time + } + dfcyc = start_dcyc; + + data_wrap = 0; + data_count = 0; + /* find first data entry */ + while((data_wrap < 2) && (log_data_ptr->dfcyc < dfcyc)) { + log_data_ptr++; + if(log_data_ptr >= g_log_data_end_ptr) { + log_data_ptr = g_log_data_start_ptr; + data_wrap++; + } + } + fprintf(pcfile, "start_dcyc: %016llx, first entry:%016llx\n", + start_dcyc, log_pc_ptr->dfcyc); + + dfcyc = start_dcyc; + max_dcyc = dfcyc; + for(i = 0; i < PC_LOG_LEN; i++) { + dfcyc = log_pc_ptr->dfcyc; + log_data_ptr = debug_show_data_info(pcfile, log_data_ptr, + base_dcyc, dfcyc, start_dcyc, + &data_wrap, &data_count); + dbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff; + kpc = log_pc_ptr->dbank_kpc & 0xffffff; + instr = log_pc_ptr->instr; + psr = (log_pc_ptr->psr_acc >> 16) & 0xffff; + acc = log_pc_ptr->psr_acc & 0xffff; + xreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff; + yreg = log_pc_ptr->xreg_yreg & 0xffff; + stack = (log_pc_ptr->stack_direct >> 16) & 0xffff; + direct = log_pc_ptr->stack_direct & 0xffff; + + accsize = 2; + xsize = 2; + if(psr & 0x20) { + accsize = 1; + } + if(psr & 0x10) { + xsize = 1; + } + + str = do_dis(kpc, accsize, xsize, 1, instr, 0); + fprintf(pcfile, "%06x] A:%04x X:%04x Y:%04x P:%03x " + "S:%04x D:%04x B:%02x %lld.%02lld %s\n", i, + acc, xreg, yreg, psr, stack, direct, dbank, + (dfcyc - base_dcyc) >> 16, + ((dfcyc & 0xffff) * 100) >> 16, str); + + if((dfcyc == 0) && (i != 0)) { + break; + } + max_dcyc = dfcyc; + log_pc_ptr++; + if(log_pc_ptr >= g_log_pc_end_ptr) { + log_pc_ptr = g_log_pc_start_ptr; + } + } + + // Print any more DATA or INFO after last PC entry + log_data_ptr = debug_show_data_info(pcfile, log_data_ptr, + base_dcyc, max_dcyc + 10 * 65536, start_dcyc, + &data_wrap, &data_count); + + fclose(pcfile); +} + +void +set_bp(word32 addr, word32 end_addr, word32 acc_type) +{ + int count; + + dbg_printf("About to set BP at %06x - %06x, type:%02x\n", addr, + end_addr, acc_type); + count = g_num_breakpoints; + if(count >= MAX_BREAK_POINTS) { + dbg_printf("Too many (0x%02x) breakpoints set!\n", count); + return; + } + + g_break_pts[count].start_addr = addr; + g_break_pts[count].end_addr = end_addr; + g_break_pts[count].acc_type = acc_type; + g_num_breakpoints = count + 1; + fixup_brks(); +} + +void +show_bp() +{ + char acc_str[4]; + word32 addr, end_addr, acc_type; + int i; + + dbg_printf("Showing breakpoints set\n"); + for(i = 0; i < g_num_breakpoints; i++) { + addr = g_break_pts[i].start_addr; + end_addr = g_break_pts[i].end_addr; + acc_type = g_break_pts[i].acc_type; + acc_str[0] = ' '; + acc_str[1] = ' '; + acc_str[2] = ' '; + acc_str[3] = 0; + if(acc_type & 4) { + acc_str[2] = 'X'; + } + if(acc_type & 2) { + acc_str[1] = 'W'; + } + if(acc_type & 1) { + acc_str[0] = 'R'; + } + if(end_addr != addr) { + dbg_printf("bp:%02x: %06x-%06x, t:%02x %s\n", i, addr, + end_addr, acc_type, acc_str); + } else { + dbg_printf("bp:%02x: %06x, t:%02x %s\n", i, addr, + acc_type, acc_str); + } + } +} + +void +delete_bp(word32 addr, word32 end_addr) +{ + int count, hit; + int i, j; + + dbg_printf("About to delete BP at %06x\n", addr); + count = g_num_breakpoints; + + hit = -1; + for(i = count - 1; i >= 0; i--) { + if((g_break_pts[i].start_addr > end_addr) || + (g_break_pts[i].end_addr < addr)) { + continue; // Not this entry + } + hit = i; + dbg_printf("Deleting brkpoint #0x%02x\n", hit); + for(j = i+1; j < count; j++) { + g_break_pts[j-1] = g_break_pts[j]; + } + count--; + } + g_num_breakpoints = count; + if(hit < 0) { + dbg_printf("Breakpoint not found!\n"); + } else { + setup_pageinfo(); + } + + show_bp(); +} + +void +debug_iwm(const char *str) +{ + if(str) { + // Dummy use of argument + } + iwm_show_track(-1, -1, 0); +} + +void +debug_iwm_check(const char *str) +{ + if(str) { + // Dummy use of argument + } + iwm_check_nibblization(0); +} + +int +do_blank(int mode, int old_mode) +{ + int tmp; + + switch(old_mode) { + case 's': + tmp = g_a2; + if(tmp == 0) { + tmp = 1; + } +#if 0 + for(i = 0; i < tmp; i++) { + g_stepping = 1; + do_step(); + if(g_halt_sim != 0) { + break; + } + } +#endif + g_list_kpc = engine.kpc; + /* video_update_through_line(262); */ + break; + case ':': + set_memory_c(((g_a3bank << 16) + g_a3), g_a2, 0); + g_a3++; + mode = old_mode; + break; + case '.': + case 0: + xam_mem(-1); + break; + case ',': + xam_mem(16); + break; + case '+': + dbg_printf("%x\n", g_a1 + g_a2); + break; + case '-': + dbg_printf("%x\n", g_a1 - g_a2); + break; + default: + dbg_printf("Unknown mode at space: %d\n", old_mode); + break; + } + return mode; +} + +void +do_go() +{ + /* also called by do_step */ + + g_config_control_panel = 0; + clear_halt(); +} + +void +do_step() +{ + int size_mem_imm, size_x_imm; + + return; // This is not correct + + do_go(); + + size_mem_imm = 2; + if(engine.psr & 0x20) { + size_mem_imm = 1; + } + size_x_imm = 2; + if(engine.psr & 0x10) { + size_x_imm = 1; + } + dbg_printf("%s\n", + do_dis(engine.kpc, size_mem_imm, size_x_imm, 0, 0, 0)); +} + +void +xam_mem(int count) +{ + show_hex_mem(g_a1bank, g_a1, g_a2, count); + g_a1 = g_a2 + 1; +} + +void +show_hex_mem(word32 startbank, word32 start, word32 end, int count) +{ + char ascii[MAXNUM_HEX_PER_LINE]; + word32 i; + int val, offset; + + if(count < 0) { + count = 16 - (start & 0xf); + } + + offset = 0; + ascii[0] = 0; + dbg_printf("Showing hex mem: bank: %x, start: %x, end: %x\n", + startbank, start, end); + for(i = start; i <= end; i++) { + if( (i==start) || (count == 16) ) { + dbg_printf("%04x:",i); + } + dbg_printf(" %02x", get_memory_c((startbank <<16) + i)); + val = get_memory_c((startbank << 16) + i) & 0x7f; + if((val < 32) || (val >= 0x7f)) { + val = '.'; + } + ascii[offset++] = val; + ascii[offset] = 0; + count--; + if(count <= 0) { + dbg_printf(" %s\n", ascii); + offset = 0; + ascii[0] = 0; + count = 16; + } + } + if(offset > 0) { + dbg_printf(" %s\n", ascii); + } +} + +void +do_debug_list() +{ + char *str; + int size, size_mem_imm, size_x_imm; + int i; + + dbg_printf("%d=m %d=x %d=LCBANK\n", (engine.psr >> 5)&1, + (engine.psr >> 4) & 1, (g_c068_statereg & 0x4) >> 2); + + size_mem_imm = 2; + if(engine.psr & 0x20) { + size_mem_imm = 1; + } + size_x_imm = 2; + if(engine.psr & 0x10) { + size_x_imm = 1; + } + for(i = 0; i < 20; i++) { + str = do_dis(g_list_kpc, size_mem_imm, size_x_imm, 0, 0, &size); + g_list_kpc += size; + dbg_printf("%s\n", str); + } +} + +void +dis_do_memmove() +{ + word32 val; + + dbg_printf("Memory move from %02x/%04x.%04x to %02x/%04x\n", g_a1bank, + g_a1, g_a2, g_a4bank, g_a4); + while(g_a1 <= (g_a2 & 0xffff)) { + val = get_memory_c((g_a1bank << 16) + g_a1); + set_memory_c((g_a4bank << 16) + g_a4, val, 0); + g_a1++; + g_a4++; + } + g_a1 = g_a1 & 0xffff; + g_a4 = g_a4 & 0xffff; +} + +void +dis_do_pattern_search() +{ +#if 0 + word32 match_val, val; + int match_shift, count; + + dbg_printf("Memory pattern search for %04x in %02x/%04x to %02x/%04x\n", + g_a4, g_a1bank, g_a1, g_a2bank, g_a2); + match_shift = 0; + count = 0; + match_val = g_a4; + while(1) { + if(g_a1bank > g_a2bank) { + break; + } + if(g_a1 > g_a2) { + break; + } + val = get_memory_c((g_a1bank << 16) + g_a1); + if(val == ((match_val >> match_shift) & 0xff)) { + match_shift += 8; + if(match_shift >= 16) { + dbg_printf("Found %04x at %02x/%04x\n", + match_val, g_a1bank, g_a1); + count++; + } + } else { + match_shift = 0; + } + g_a1++; + if(g_a1 >= 0x10000) { + g_a1 = 0; + g_a1bank++; + } + } +#endif +} + +void +dis_do_compare() +{ + word32 val1, val2; + + dbg_printf("Memory Compare from %02x/%04x.%04x with %02x/%04x\n", + g_a1bank, g_a1, g_a2, g_a4bank, g_a4); + while(g_a1 <= (g_a2 & 0xffff)) { + val1 = get_memory_c((g_a1bank << 16) + g_a1); + val2 = get_memory_c((g_a4bank << 16) + g_a4); + if(val1 != val2) { + dbg_printf("%02x/%04x: %02x vs %02x\n", g_a1bank, g_a1, + val1, val2); + } + g_a1++; + g_a4++; + } + g_a1 = g_a1 & 0xffff; + g_a4 = g_a4 & 0xffff; +} + +const char * +do_debug_unix(const char *str, int old_mode) +{ + char localbuf[LINE_SIZE+2]; + byte *bptr; + word32 offset, len, a1_val; + long ret; + int fd, load; + int i; + + load = 0; + switch(*str++) { + case 'l': case 'L': + dbg_printf("Loading.."); + load = 1; + break; + case 's': case 'S': + dbg_printf("Saving..."); + break; + default: + dbg_printf("Unknown unix command: %c\n", *(str - 1)); + if(str[-1] == 0) { + return str - 1; + } + return str; + } + while((*str == ' ') || (*str == '\t')) { + str++; + } + i = 0; + while(i < LINE_SIZE) { + localbuf[i++] = *str++; + if((*str==' ') || (*str == '\t') || (*str == '\n') || + (*str == 0)) { + break; + } + } + localbuf[i] = 0; + + dbg_printf("About to open: %s,len: %d\n", localbuf, + (int)strlen(localbuf)); + if(load) { + fd = open(localbuf, O_RDONLY | O_BINARY); + } else { + fd = open(localbuf, O_WRONLY | O_CREAT | O_BINARY, 0x1b6); + } + if(fd < 0) { + dbg_printf("Open %s failed: %d. errno:%d\n", localbuf, fd, + errno); + return str; + } + if(load) { + offset = g_a1 & 0xffff; + len = 0x20000 - offset; + } else { + if(old_mode == '.') { + len = g_a2 - g_a1 + 1; + } else { + len = 0x100; + } + } + a1_val = (g_a1bank << 16) | g_a1; + bptr = &g_memory_ptr[a1_val]; + if((g_a1bank >= 0xe0) && (g_a1bank < 0xe2)) { + bptr = &g_slow_memory_ptr[a1_val & 0x1ffff]; + } + if(load) { + ret = read(fd, bptr, len); + } else { + ret = write(fd, bptr, len); + } + dbg_printf("Read/write: addr %06x for %04x bytes, ret: %lx bytes\n", + a1_val, len, ret); + if(ret < 0) { + dbg_printf("errno: %d\n", errno); + } + g_a1 = g_a1 + (int)ret; + return str; +} + +void +do_debug_load() +{ + dbg_printf("Sorry, can't load now\n"); +} + +char * +do_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr, + int *size_ptr) +{ + char buffer[MAX_DISAS_BUF]; + char buffer2[MAX_DISAS_BUF]; + const char *str; + word32 val, oldkpc, dtype; + int args, type, opcode, signed_val; + int i; + + oldkpc = kpc; + if(op_provided) { + opcode = (instr >> 24) & 0xff; + } else { + opcode = (int)get_memory_c(kpc) & 0xff; + } + + kpc++; + + dtype = disas_types[opcode]; + str = disas_opcodes[opcode]; + type = dtype & 0xff; + args = dtype >> 8; + + if(args > 3) { + if(args == 4) { + args = accsize; + } else if(args == 5) { + args = xsize; + } + } + + val = -1; + switch(args) { + case 0: + val = 0; + break; + case 1: + if(op_provided) { + val = instr & 0xff; + } else { + val = get_memory_c(kpc); + } + break; + case 2: + if(op_provided) { + val = instr & 0xffff; + } else { + val = get_memory16_c(kpc); + } + break; + case 3: + if(op_provided) { + val = instr & 0xffffff; + } else { + val = get_memory24_c(kpc); + } + break; + default: + fprintf(stderr, "args out of rang: %d, opcode: %08x\n", + args, opcode); + break; + } + kpc += args; + + if(!op_provided) { + instr = (opcode << 24) | (val & 0xffffff); + } + + switch(type) { + case ABS: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%04x", str, val); + break; + case ABSX: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%04x,X", str, val); + break; + case ABSY: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%04x,Y", str, val); + break; + case ABSLONG: + if(args != 3) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%06x", str, val); + break; + case ABSIND: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%04x)", str, val); + break; + case ABSXIND: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%04x,X)", str, val); + break; + case IMPLY: + case ACCUM: + if(args != 0) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s", str); + break; + case IMMED: + if(args == 1) { + snprintf(&buffer[0], MAX_DISAS_BUF, "%s #$%02x", str, + val); + } else if(args == 2) { + snprintf(&buffer[0], MAX_DISAS_BUF, "%s #$%04x", str, + val); + } else { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + break; + case JUST8: + case DLOC: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%02x", str, val); + break; + case DLOCX: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%02x,X", str, val); + break; + case DLOCY: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%02x,Y", str, val); + break; + case LONG: + if(args != 3) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%06x", str, val); + break; + case LONGX: + if(args != 3) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%06x,X", str, val); + break; + case DLOCIND: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%02x)", str, val); + break; + case DLOCINDY: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%02x),Y", str, val); + break; + case DLOCXIND: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%02x,X)", str, val); + break; + case DLOCBRAK: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s [$%02x]", str, val); + break; + case DLOCBRAKY: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s [$%02x],y", str, val); + break; + case DISP8: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + signed_val = (signed char)val; + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%04x", str, + (kpc + signed_val) & 0xffff); + break; + case DISP8S: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%02x,S", str, + val & 0xff); + break; + case DISP8SINDY: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s ($%02x,S),Y", str, + val & 0xff); + break; + case DISP16: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%04x", str, + (word32)(kpc+(signed)(word16)(val)) & 0xffff); + break; + case MVPMVN: + if(args != 2) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s $%02x,$%02x", str, + val & 0xff, val >> 8); + break; + case SEPVAL: + case REPVAL: + if(args != 1) { + dbg_printf("arg # mismatch for opcode %x\n", opcode); + } + snprintf(&buffer[0], MAX_DISAS_BUF, "%s #$%02x", str, val); + break; + default: + dbg_printf("argument type: %d unexpected\n", type); + break; + } + + g_disas_buffer[0] = 0; + snprintf(&g_disas_buffer[0], MAX_DISAS_BUF, "%02x/%04x: %02x ", + oldkpc >> 16, oldkpc & 0xffff, opcode); + for(i = 1; i <= args; i++) { + snprintf(&buffer2[0], MAX_DISAS_BUF, "%02x ", instr & 0xff); + cfg_strlcat(&g_disas_buffer[0], &buffer2[0], MAX_DISAS_BUF); + instr = instr >> 8; + } + for(; i < 4; i++) { + cfg_strlcat(&g_disas_buffer[0], " ", MAX_DISAS_BUF); + } + cfg_strlcat(&g_disas_buffer[0], " ", MAX_DISAS_BUF); + cfg_strlcat(&g_disas_buffer[0], &buffer[0], MAX_DISAS_BUF); + + if(size_ptr) { + *size_ptr = args + 1; + } + return (&g_disas_buffer[0]); +} + +int +debug_get_view_line(int back) +{ + int pos; + + // where back==0 means return pos - 1. + pos = g_debug_lines_pos - 1; + pos = pos - back; + if(pos < 0) { + if(g_debug_lines_alloc >= g_debug_lines_max) { + pos += g_debug_lines_alloc; + } else { + return 0; // HACK: return -1 + } + } + return pos; +} + +int +debug_add_output_line(char *in_str) +{ + Debug_entry *line_ptr; + byte *out_bptr; + int pos, alloc, view, used_len, c; + int i; + + // printf("debug_add_output_line %s len:%d\n", in_str, len); + pos = g_debug_lines_pos; + line_ptr = g_debug_lines_ptr; + alloc = g_debug_lines_alloc; + if(pos >= alloc) { + if(alloc < g_debug_lines_max) { + alloc = MY_MAX(2048, alloc*3); + alloc = MY_MAX(alloc, pos*3); + alloc = MY_MIN(alloc, g_debug_lines_max); + line_ptr = realloc(line_ptr, + alloc * sizeof(Debug_entry)); + printf("realloc. now %p, alloc:%d\n", line_ptr, alloc); + g_debug_lines_ptr = line_ptr; + g_debug_lines_alloc = alloc; + printf("Alloced debug lines to %d\n", alloc); + } else { + pos = 0; + } + } + // Convert to A2 format chars: set high bit of each byte, 80 chars + // per line + out_bptr = &(line_ptr[pos].str_buf[0]); + used_len = 0; + for(i = 0; i < DEBUG_ENTRY_MAX_CHARS; i++) { + c = ' '; + if(*in_str) { + c = *in_str++; + used_len++; + } + c = c ^ 0x80; // Set highbit if not already set + out_bptr[i] = c; + } + pos++; + g_debug_lines_pos = pos; + g_debug_lines_total++; // For updating the window + g_debugwin_changed++; + view = g_debug_lines_view; + if(view >= 0) { + view++; // view is back from pos, so to stay the same, + // it must be incremented when pos incs + if((view - 50) >= g_debug_lines_max) { + // We were viewing the oldest page, and by wrapping + // around we're about to wipe out this old data + // Jump to most recent data + view = -1; + } + g_debug_lines_view = view; + } + + return used_len; +} + +void +debug_add_output_string(char *in_str, int len) +{ + int ret, tries; + + tries = 0; + ret = 0; + if(g_debug_to_stdout) { + puts(in_str); // Send output to stdout, too + } + while((len > 0) || (tries == 0)) { + // printf("DEBUG: adding str: %s, len:%d, ret:%d\n", in_str, + // len, ret); + ret = debug_add_output_line(in_str); + len -= ret; + in_str += ret; + tries++; + } +} + +void +debug_add_output_chars(char *str) +{ + int pos, c, tab_spaces; + + pos = g_debug_stage_pos; + tab_spaces = 0; + while(1) { + if(tab_spaces > 0) { + c = ' '; + tab_spaces--; + } else { + c = *str++; + if(c == '\t') { + tab_spaces = 7 - (pos & 7); + c = ' '; + } + } + pos = MY_MIN(pos, (PRINTF_BUF_SIZE - 1)); + if((c == '\n') || (pos >= (PRINTF_BUF_SIZE - 1))) { + g_debug_stage_buf[pos] = 0; + debug_add_output_string(&g_debug_stage_buf[0], pos); + pos = 0; + g_debug_stage_pos = 0; + continue; + } + if(c == 0) { + g_debug_stage_pos = pos; + return; + } + g_debug_stage_buf[pos++] = c; + } +} + +int +dbg_printf(const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = dbg_vprintf(fmt, args); + va_end(args); + return ret; +} + +int +dbg_vprintf(const char *fmt, va_list args) +{ + int ret; + + ret = vsnprintf(&g_debug_printf_buf[0], PRINTF_BUF_SIZE, fmt, args); + debug_add_output_chars(&g_debug_printf_buf[0]); + return ret; +} + +void +halt_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dbg_vprintf(fmt, args); + va_end(args); + + set_halt(1); +} + +void +halt2_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dbg_vprintf(fmt, args); + va_end(args); + + set_halt(2); +} + diff --git a/gsplus/src/defc.h b/gsplus/src/defc.h new file mode 100644 index 0000000..a85882c --- /dev/null +++ b/gsplus/src/defc.h @@ -0,0 +1,364 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defcomm.h" + +#define STRUCT(a) typedef struct a ## _st a; struct a ## _st + +typedef unsigned char byte; +typedef unsigned short word16; +typedef unsigned int word32; +#if _MSC_VER +typedef unsigned __int64 dword64; +#else +typedef unsigned long long dword64; +#endif + +/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */ +#define CYCS_28_MHZ (28636360) +#define DCYCS_28_MHZ (1.0*CYCS_28_MHZ) +#define CYCS_3_5_MHZ (CYCS_28_MHZ/8) +#define DCYCS_1_MHZ ((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0))) + +// DCYCS_1_MHZ is 1020484.32016 + +#define CYCLES_IN_16MS_RAW (262 * 65) +/* Use precisely 17030 instead of forcing 60 Hz since this is the number of */ +/* 1MHz cycles per screen */ + +#define DCYCS_IN_16MS ((double)(CYCLES_IN_16MS_RAW)) +#define DRECIP_DCYCS_IN_16MS (1.0 / (DCYCS_IN_16MS)) +#define VBL_RATE (DCYCS_1_MHZ / DCYCS_IN_16MS) +// VBL rate is about 59.9227 frames/sec + +#define MAXNUM_HEX_PER_LINE 32 +#define MAX_SCALE_SIZE 5100 + +#ifdef __NeXT__ +# include +#endif + +#ifndef _WIN32 +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HPUX +# include /* for GET_ITIMER */ +#endif + +#ifdef SOLARIS +# include +#endif + +#ifdef _WIN32 +# include +# include +# pragma warning(disable : 4996) /* open() is deprecated...sigh */ +int ftruncate(int fd, word32 length); +int lstat(const char *path, struct stat *bufptr); +#endif + +#ifndef O_BINARY +/* work around some Windows junk */ +# define O_BINARY 0 +#endif + +#define MAX_CHANGE_RECTS 20 + +#ifdef __GNUC__ +int dbg_printf(const char *fmt, ...) __attribute__ (( + __format__(printf, 1, 2))); +#endif + +STRUCT(Pc_log) { + dword64 dfcyc; + word32 dbank_kpc; + word32 instr; + word32 psr_acc; + word32 xreg_yreg; + word32 stack_direct; + word32 pad; +}; + +STRUCT(Data_log) { + dword64 dfcyc; + byte *stat; + word32 addr; + word32 val; + word32 size; +}; + +STRUCT(Event) { + dword64 dfcyc; + int type; + Event *next; +}; + +STRUCT(Fplus) { + dword64 dplus_1; + dword64 dplus_x_minus_1; +}; + +STRUCT(Engine_reg) { + dword64 dfcyc; + word32 kpc; + word32 acc; + + word32 xreg; + word32 yreg; + + word32 stack; + word32 dbank; + + word32 direct; + word32 psr; + Fplus *fplus_ptr; +}; + +STRUCT(Break_point) { + word32 start_addr; + word32 end_addr; + word32 acc_type; +}; + +STRUCT(Change_rect) { + int x; + int y; + int width; + int height; +}; + + +STRUCT(Kimage) { + word32 *wptr; + int a2_width_full; + int a2_height_full; + int a2_width; + int a2_height; + int x_width; + int x_height; + int x_refresh_needed; + int x_max_width; + int x_max_height; + int x_xpos; + int x_ypos; + int active; + word32 vbl_of_last_resize; + word32 c025_val; + word32 scale_width_to_a2; + word32 scale_width_a2_to_x; + word32 scale_height_to_a2; + word32 scale_height_a2_to_x; + int num_change_rects; + Change_rect change_rect[MAX_CHANGE_RECTS]; + word32 scale_width[MAX_SCALE_SIZE + 1]; + word32 scale_height[MAX_SCALE_SIZE + 1]; +}; + +typedef byte *Pg_info; +STRUCT(Page_info) { + Pg_info rd_wr; +}; + +STRUCT(Cfg_menu) { + const char *str; + void *ptr; + const char *name_str; + void *defptr; + int cfgtype; +}; + +STRUCT(Cfg_dirent) { + char *name; + int is_dir; + int part_num; + dword64 dsize; + dword64 dimage_start; + dword64 compr_dsize; +}; + +STRUCT(Cfg_listhdr) { + Cfg_dirent *direntptr; + int max; + int last; + int invalid; + + int curent; + int topent; + + int num_to_show; +}; + +typedef void (Dbg_fn)(const char *str); + +STRUCT(Dbg_longcmd) { + const char *str; + Dbg_fn *fnptr; + Dbg_longcmd *subptr; + const char *help_str; +}; + +STRUCT(Emustate_intlist) { + const char *str; + word32 *iptr; +}; + +STRUCT(Emustate_dword64list) { + const char *str; + dword64 *dptr; +}; + +STRUCT(Emustate_word32list) { + const char *str; + word32 *wptr; +}; + +STRUCT(Lzw_state) { + word32 table[4096 + 2]; + word32 entry; + int bits; +}; + +#ifdef __LP64__ +# define PTR2WORD(a) ((word32)(unsigned long long)(a)) +#else +# define PTR2WORD(a) ((word32)(unsigned long long)(a)) +#endif + + +#define ALTZP (g_c068_statereg & 0x80) +/* #define PAGE2 (g_c068_statereg & 0x40) */ +#define RAMRD (g_c068_statereg & 0x20) +#define RAMWRT (g_c068_statereg & 0x10) +#define RDROM (g_c068_statereg & 0x08) +#define LCBANK2 (g_c068_statereg & 0x04) +#define ROMB (g_c068_statereg & 0x02) +// #define INTCX (g_c068_statereg & 0x01) + +#define C041_EN_25SEC_INTS 0x10 +#define C041_EN_VBL_INTS 0x08 +#define C041_EN_SWITCH_INTS 0x04 +#define C041_EN_MOVE_INTS 0x02 +#define C041_EN_MOUSE 0x01 + +/* WARNING: SCC1 and SCC0 interrupts must be in this order for scc.c */ +/* This order matches the SCC hardware, and SCC1_ZEROCNT must be 0x0001 */ +#define IRQ_PENDING_SCC1_ZEROCNT 0x00001 +#define IRQ_PENDING_SCC1_TX 0x00002 +#define IRQ_PENDING_SCC1_RX 0x00004 +#define IRQ_PENDING_SCC0_ZEROCNT 0x00008 +#define IRQ_PENDING_SCC0_TX 0x00010 +#define IRQ_PENDING_SCC0_RX 0x00020 +#define IRQ_PENDING_C023_SCAN 0x00100 +#define IRQ_PENDING_C023_1SEC 0x00200 +#define IRQ_PENDING_C046_25SEC 0x00400 +#define IRQ_PENDING_C046_VBL 0x00800 +#define IRQ_PENDING_ADB_KBD_SRQ 0x01000 +#define IRQ_PENDING_ADB_DATA 0x02000 +#define IRQ_PENDING_ADB_MOUSE 0x04000 +#define IRQ_PENDING_DOC 0x08000 +#define IRQ_PENDING_MOCKINGBOARDA 0x10000 +#define IRQ_PENDING_MOCKINGBOARDB 0x20000 /* must be BOARDA*2 */ + + +#define EXTRU(val, pos, len) \ + ( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) : \ + (((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) ) + +#define DEP1(val, pos, old_val) \ + (((old_val) & ~(1 << (31 - (pos))) ) | \ + ( ((val) & 1) << (31 - (pos))) ) + +#define set_halt(val) \ + if(val) { set_halt_act(val); } + +#define clear_halt() \ + clr_halt_act() + +#define GET_PAGE_INFO_RD(page) \ + (page_info_rd_wr[page].rd_wr) + +#define GET_PAGE_INFO_WR(page) \ + (page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr) + +#define SET_PAGE_INFO_RD(page,val) \ + ;page_info_rd_wr[page].rd_wr = (Pg_info)val; + +#define SET_PAGE_INFO_WR(page,val) \ + ;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \ + (Pg_info)val; + +#define VERBOSE_DISK 0x001 +#define VERBOSE_IRQ 0x002 +#define VERBOSE_CLK 0x004 +#define VERBOSE_SHADOW 0x008 +#define VERBOSE_IWM 0x010 +#define VERBOSE_DOC 0x020 +#define VERBOSE_ADB 0x040 +#define VERBOSE_SCC 0x080 +#define VERBOSE_TEST 0x100 +#define VERBOSE_VIDEO 0x200 +#define VERBOSE_MAC 0x400 +#define VERBOSE_DYNA 0x800 + +#ifdef NO_VERB +# define DO_VERBOSE 0 +#else +# define DO_VERBOSE 1 +#endif + +#define disk_printf if(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf +#define irq_printf if(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf +#define clk_printf if(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf +#define shadow_printf if(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf +#define iwm_printf if(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf +#define doc_printf if(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf +#define adb_printf if(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf +#define scc_printf if(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf +#define test_printf if(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf +#define vid_printf if(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf +#define mac_printf if(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf +#define dyna_printf if(DO_VERBOSE && (Verbose & VERBOSE_DYNA)) printf + + +#define HALT_ON_SCAN_INT 0x001 +#define HALT_ON_IRQ 0x002 +#define HALT_ON_SHADOW_REG 0x004 +#define HALT_ON_C70D_WRITES 0x008 + +#define HALT_ON(a, msg) \ + if(Halt_on & a) { \ + halt_printf(msg); \ + } + + +#define MY_MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MY_MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define GET_ITIMER(dest) dest = get_itimer(); + +#define FINISH(arg1, arg2) g_ret1 = arg1 | ((arg2) << 8); g_dcycles_end=0; + +#include "iwm.h" +#include "protos.h" diff --git a/gsplus/src/defcomm.h b/gsplus/src/defcomm.h new file mode 100644 index 0000000..d14707c --- /dev/null +++ b/gsplus/src/defcomm.h @@ -0,0 +1,129 @@ +#ifdef INCLUDE_RCSID_C +const char rcsdif_defcomm_h[] = "@(#)$KmKId: defcomm.h,v 1.109 2023-11-12 15:29:41+00 kentd Exp $"; +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#define SHIFT_PER_CHANGE 3 +#define CHANGE_SHIFT (5 + SHIFT_PER_CHANGE) + +#define SLOW_MEM_CH_SIZE (0x20000 >> CHANGE_SHIFT) + +#define MAXNUM_HEX_PER_LINE 32 + +/* Different Joystick defines */ +#define JOYSTICK_MOUSE 1 +#define JOYSTICK_LINUX 2 +#define JOYSTICK_KEYPAD 3 +#define JOYSTICK_WIN32_1 4 +#define JOYSTICK_WIN32_2 5 + +#define MAX_BREAK_POINTS 0x20 + +#define MAX_BP_INDEX 0x100 +#define MAX_BP_PER_INDEX 3 /* 4 word32s total = 16 bytes */ +#define SIZE_BREAKPT_ENTRY_BITS 4 /* 16 bytes = 4 bits */ + +/* Warning--next defines used by asm! */ +#define PAGE_INFO_PAD_SIZE 0x800 +#define PAGE_INFO_WR_OFFSET 0x10000+PAGE_INFO_PAD_SIZE + +#define BANK_IO_BIT 31 +#define BANK_SHADOW_BIT 30 +#define BANK_SHADOW2_BIT 29 +#define BANK_IO2_BIT 28 +#define BANK_BREAK_BIT 27 +#define BANK_BREAK (1 << (31 - BANK_BREAK_BIT)) +#define BANK_IO2_TMP (1 << (31 - BANK_IO2_BIT)) +#define BANK_IO_TMP (1 << (31 - BANK_IO_BIT)) +#define BANK_SHADOW (1 << (31 - BANK_SHADOW_BIT)) +#define BANK_SHADOW2 (1 << (31 - BANK_SHADOW2_BIT)) +#define SET_BANK_IO \ + (&g_dummy_memory1_ptr[BANK_IO_TMP | BANK_IO2_TMP]) + +#define BANK_BAD_MEM (&g_dummy_memory1_ptr[0xff]) + + +#define RET_BREAK 0x1 +#define RET_COP 0x2 +#define RET_WDM 0x3 +#define RET_WAI 0x4 +#define RET_STP 0x5 +#define RET_PSR 0x6 +#define RET_IRQ 0x7 +#define RET_TOOLTRACE 0x8 + + +#define BIT_ALL_STAT_TEXT 0 +#define BIT_ALL_STAT_VID80 1 +#define BIT_ALL_STAT_ST80 2 +#define BIT_ALL_STAT_COLOR_C021 3 +#define BIT_ALL_STAT_MIX_T_GR 4 +#define BIT_ALL_STAT_DIS_COLOR_DHIRES 5 /* special val, c029 */ +#define BIT_ALL_STAT_PAGE2 6 /* special val, statereg */ +#define BIT_ALL_STAT_SUPER_HIRES 7 /* special, c029 */ +#define BIT_ALL_STAT_HIRES 8 +#define BIT_ALL_STAT_ANNUNC3 9 +#define BIT_ALL_STAT_ALTCHARSET 10 +#define BIT_ALL_STAT_FLASH_STATE 11 +#define BIT_ALL_STAT_BG_COLOR 12 /* 4 bits */ +#define BIT_ALL_STAT_TEXT_COLOR 16 /* 4 bits */ + /* Text must be just above */ + /* bg to match c022 reg */ +#define BIT_ALL_STAT_VOC_INTERLACE 20 +#define BIT_ALL_STAT_VOC_MAIN 21 +#define BIT_ALL_STAT_BORDER 22 + +#define ALL_STAT_SUPER_HIRES (1 << (BIT_ALL_STAT_SUPER_HIRES)) +#define ALL_STAT_TEXT (1 << (BIT_ALL_STAT_TEXT)) +#define ALL_STAT_VID80 (1 << (BIT_ALL_STAT_VID80)) +#define ALL_STAT_PAGE2 (1 << (BIT_ALL_STAT_PAGE2)) +#define ALL_STAT_ST80 (1 << (BIT_ALL_STAT_ST80)) +#define ALL_STAT_COLOR_C021 (1 << (BIT_ALL_STAT_COLOR_C021)) +#define ALL_STAT_DIS_COLOR_DHIRES (1 << (BIT_ALL_STAT_DIS_COLOR_DHIRES)) +#define ALL_STAT_MIX_T_GR (1 << (BIT_ALL_STAT_MIX_T_GR)) +#define ALL_STAT_HIRES (1 << (BIT_ALL_STAT_HIRES)) +#define ALL_STAT_ANNUNC3 (1 << (BIT_ALL_STAT_ANNUNC3)) +#define ALL_STAT_TEXT_COLOR (0xf << (BIT_ALL_STAT_TEXT_COLOR)) +#define ALL_STAT_BG_COLOR (0xf << (BIT_ALL_STAT_BG_COLOR)) +#define ALL_STAT_ALTCHARSET (1 << (BIT_ALL_STAT_ALTCHARSET)) +#define ALL_STAT_FLASH_STATE (1 << (BIT_ALL_STAT_FLASH_STATE)) +#define ALL_STAT_VOC_INTERLACE (1 << (BIT_ALL_STAT_VOC_INTERLACE)) +#define ALL_STAT_VOC_MAIN (1 << (BIT_ALL_STAT_VOC_MAIN)) +#define ALL_STAT_BORDER (1 << (BIT_ALL_STAT_BORDER)) + +#define BORDER_WIDTH 32 + +#define EFF_BORDER_WIDTH (BORDER_WIDTH + (640-560)) + +/* BASE_MARGIN_BOTTOM+MARGIN_TOP must equal 62. There are 262 scan lines */ +/* at 60Hz (15.7KHz line rate) and so we just make 62 border lines */ +#define BASE_MARGIN_TOP 32 +#define BASE_MARGIN_BOTTOM 30 +#define BASE_MARGIN_LEFT BORDER_WIDTH +#define BASE_MARGIN_RIGHT BORDER_WIDTH + +#define A2_WINDOW_WIDTH 640 +#define A2_WINDOW_HEIGHT 400 + +#define X_A2_WINDOW_WIDTH (A2_WINDOW_WIDTH + BASE_MARGIN_LEFT + \ + BASE_MARGIN_RIGHT) +#define X_A2_WINDOW_HEIGHT (A2_WINDOW_HEIGHT + BASE_MARGIN_TOP + \ + BASE_MARGIN_BOTTOM) + +#define MAX_STATUS_LINES 4 +#define STATUS_LINE_LENGTH 88 + +#define BASE_WINDOW_WIDTH (X_A2_WINDOW_WIDTH) + + +#define A2_BORDER_COLOR_NUM 0xfe + diff --git a/gsplus/src/defs_instr.h b/gsplus/src/defs_instr.h new file mode 100644 index 0000000..07e1bf6 --- /dev/null +++ b/gsplus/src/defs_instr.h @@ -0,0 +1,759 @@ +// $KmKId: defs_instr.h,v 1.70 2023-11-05 16:22:26+00 kentd Exp $ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#undef GET_DLOC_X_IND_RD + +#ifdef ACC8 +# define GET_DLOC_X_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR(); \ + GET_MEMORY8(arg, arg); +#else +# define GET_DLOC_X_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR(); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_DISP8_S_RD + +#ifdef ACC8 +# define GET_DISP8_S_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR(); \ + GET_MEMORY8(arg, arg); +#else +# define GET_DISP8_S_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR(); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_DLOC_RD + +#ifdef ACC8 +# define GET_DLOC_RD() \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY8((direct + arg) & 0xffff, arg); +#else +# define GET_DLOC_RD() \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY16((direct + arg) & 0xffff, arg, 1); +#endif + +#undef GET_DLOC_RD_RMW +#undef GET_MEM_RMW + +#define GET_MEM_RMW() \ + if(!IS_ACC16) { \ + if(psr & 0x100) { \ + /* emulation re-writes the address */ \ + SET_MEMORY8(addr_latch, arg); \ + } else { \ + /* otherwise, just read addr again */ \ + GET_MEMORY8(addr_latch, dummy1); \ + } \ + } else { \ + /* 16-bit re-reads addr+1 again */ \ + dummy1 = addr_latch + 1; \ + GET_MEMORY8(dummy1, dummy1); \ + addr_latch--; \ + } + +#define GET_DLOC_RD_RMW() \ + GET_DLOC_RD(); \ + GET_MEM_RMW(); + + +#undef GET_DLOC_L_IND_RD + +#ifdef ACC8 +# define GET_DLOC_L_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR(); \ + GET_MEMORY8(arg, arg); +#else +# define GET_DLOC_L_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR(); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_IMM_MEM + +#ifdef ACC8 +# define GET_IMM_MEM() \ + GET_1BYTE_ARG; \ + INC_KPC_2; +#else +# define GET_IMM_MEM() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_3; +#endif + +#undef GET_ABS_RD + +#ifdef ACC8 +# define GET_ABS_RD() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + GET_MEMORY8((dbank << 16) + arg, arg); \ + INC_KPC_3; +#else +# define GET_ABS_RD() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + GET_MEMORY16((dbank << 16) + arg, arg, 0); \ + INC_KPC_3; +#endif + +#undef GET_ABS_RD_RMW + +#define GET_ABS_RD_RMW() \ + GET_ABS_RD(); \ + GET_MEM_RMW(); + +#undef GET_LONG_RD + +#ifdef ACC8 +# define GET_LONG_RD() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + GET_MEMORY8(arg, arg); \ + INC_KPC_4; +#else +# define GET_LONG_RD() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + GET_MEMORY16(arg, arg, 0); \ + INC_KPC_4; +#endif + +#undef GET_DLOC_IND_Y_RD +#undef GET_DLOC_IND_Y_ADDR + +#ifdef ACC8 +# define GET_DLOC_IND_Y_RD() \ + GET_DLOC_IND_Y_ADDR(0) \ + GET_MEMORY8(arg, arg); +#else +# define GET_DLOC_IND_Y_RD() \ + GET_DLOC_IND_Y_ADDR(0) \ + GET_MEMORY16(arg, arg, 0); +#endif + +#define GET_DLOC_IND_Y_ADDR(is_write) \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1, 0); \ + tmp1 += (dbank << 16); \ + arg = (tmp1 + yreg) & 0xffffff; \ + tmp2 = (tmp1 & 0xffff00) | (arg & 0xff); \ + if((psr & 0x10) && ((arg != tmp2) | is_write)) { \ + GET_MEMORY8(tmp2, tmp1); \ + } else if(((psr & 0x10) == 0) | (arg != tmp2) | is_write) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; + +#undef GET_DLOC_IND_RD + +#ifdef ACC8 +# define GET_DLOC_IND_RD() \ + GET_1BYTE_ARG; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \ + GET_MEMORY8((dbank << 16) + arg, arg); +#else +# define GET_DLOC_IND_RD() \ + GET_1BYTE_ARG; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \ + GET_MEMORY16((dbank << 16) + arg, arg, 0); +#endif + +#undef GET_DLOC_X_RD + +#ifdef ACC8 +# define GET_DLOC_X_RD() \ + GET_1BYTE_ARG; \ + CYCLES_PLUS_1; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + arg = (arg + xreg + direct) & 0xffff; \ + if(psr & 0x100) { \ + if((direct & 0xff) == 0) { \ + arg = (direct & 0xff00) | (arg & 0xff); \ + } \ + } \ + GET_MEMORY8(arg, arg); +#else +# define GET_DLOC_X_RD() \ + GET_1BYTE_ARG; \ + CYCLES_PLUS_1; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + arg = (arg + xreg + direct) & 0xffff; \ + if(!IS_ACC16 && (psr & 0x100)) { \ + if((direct & 0xff) == 0) { \ + arg = (direct & 0xff00) | (arg & 0xff); \ + } \ + } \ + GET_MEMORY16(arg, arg, 1); +#endif + +#undef GET_DLOC_X_RD_RMW + +#define GET_DLOC_X_RD_RMW() \ + GET_DLOC_X_RD(); \ + GET_MEM_RMW(); + + +#undef GET_DISP8_S_IND_Y_RD + +#ifdef ACC8 +# define GET_DISP8_S_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR(); \ + GET_MEMORY8(arg, arg); +#else +# define GET_DISP8_S_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR(); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_DLOC_L_IND_Y_RD + +#ifdef ACC8 +# define GET_DLOC_L_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR(); \ + GET_MEMORY8(arg, arg); +#else +# define GET_DLOC_L_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR(); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_ABS_INDEX_ADDR + +#define GET_ABS_INDEX_ADDR(index_reg, is_write) \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_3; \ + tmp1 = (dbank << 16) + arg; \ + arg = tmp1 + index_reg; \ + tmp1 = (tmp1 & 0xffff00) + (arg & 0xff); \ + if((psr & 0x10) && ((tmp1 != arg) | is_write)) { \ + GET_MEMORY8(tmp1, tmp2); \ + } else if(((psr & 0x10) == 0) | (tmp1 != arg) | is_write) { \ + CYCLES_PLUS_1; \ + } + +#undef GET_ABS_Y_RD + +#ifdef ACC8 +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR(yreg, 0); \ + GET_MEMORY8(arg, arg); +#else +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR(yreg, 0); \ + GET_MEMORY16(arg, arg, 0); +#endif + +#undef GET_ABS_X_RD +#undef GET_ABS_X_RD_RMW + +#ifdef ACC8 +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR(xreg, 0); \ + GET_MEMORY8(arg, arg); +#define GET_ABS_X_RD_RMW() \ + GET_ABS_INDEX_ADDR(xreg, 1); \ + GET_MEMORY8(arg, arg); \ + GET_MEM_RMW(); +#else +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR(xreg, 0); \ + GET_MEMORY16(arg, arg, 0); +#define GET_ABS_X_RD_RMW() \ + GET_ABS_INDEX_ADDR(xreg, 1); \ + GET_MEMORY16(arg, arg, 0); \ + GET_MEM_RMW(); +#endif + +#undef GET_LONG_X_RD + +#ifdef ACC8 +# define GET_LONG_X_RD() \ + GET_3BYTE_ARG; \ + arg = (arg + xreg) & 0xffffff; \ + INC_KPC_4; \ + CYCLES_PLUS_2; \ + GET_MEMORY8(arg, arg); +#else +# define GET_LONG_X_RD() \ + GET_3BYTE_ARG; \ + arg = (arg + xreg) & 0xffffff; \ + INC_KPC_4; \ + CYCLES_PLUS_2; \ + GET_MEMORY16(arg, arg, 0); +#endif + +#define SET_NEG_ZERO16(val) \ + zero = val; \ + neg7 = (val) >> 8; + +#define SET_NEG_ZERO8(val) \ + zero = val; \ + neg7 = val; + +#define SET_CARRY8(val) \ + psr = (psr & ~1) + (((val) >> 8) & 1); + +#define SET_CARRY16(val) \ + psr = (psr & ~1) + (((val) >> 16) & 1); + +#define SET_INDEX_REG(src, dest) \ + if(psr & 0x10) { \ + dest = (src) & 0xff; \ + SET_NEG_ZERO8(dest); \ + } else { \ + dest = (src) & 0xffff; \ + SET_NEG_ZERO16(dest); \ + } + +#define LD_INDEX_INST(index_reg, in_bank) \ + if(psr & 0x10) { \ + GET_MEMORY8(arg, arg); \ + } else { \ + GET_MEMORY16(arg, arg, in_bank);\ + } \ + SET_INDEX_REG(arg, index_reg); + +#define LDX_INST(in_bank) LD_INDEX_INST(xreg, in_bank) +#define LDY_INST(in_bank) LD_INDEX_INST(yreg, in_bank) + +#undef ORA_INST + +#ifdef ACC8 +# define ORA_INST() \ + tmp1 = (acc | arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +#else +# define ORA_INST() \ + acc = (acc | arg); \ + SET_NEG_ZERO16(acc); +#endif + +#undef AND_INST + +#ifdef ACC8 +# define AND_INST() \ + tmp1 = (acc & arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +#else +# define AND_INST() \ + acc = (acc & arg); \ + SET_NEG_ZERO16(acc); +#endif + +#undef EOR_INST + +#ifdef ACC8 +# define EOR_INST() \ + tmp1 = (acc ^ arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +#else +# define EOR_INST() \ + acc = (acc ^ arg); \ + SET_NEG_ZERO16(acc); +#endif + +#undef LDA_INST + +#ifdef ACC8 +# define LDA_INST() \ + acc = (acc & 0xff00) + (arg & 0xff); \ + SET_NEG_ZERO8(arg & 0xff); +#else +# define LDA_INST() \ + acc = (arg & 0xffff); \ + SET_NEG_ZERO16(acc); +#endif + +#undef ADC_INST + +#ifdef ACC8 +# define ADC_INST() \ + tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 0); \ + acc = (acc & 0xff00) + (tmp1 & 0xff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg7 = psr; +#else +# define ADC_INST() \ + tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 0); \ + acc = (tmp1 & 0xffff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg7 = psr; +#endif + +#undef SBC_INST + +#ifdef ACC8 +# define SBC_INST() \ + tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 1); \ + acc = (acc & 0xff00) + (tmp1 & 0xff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg7 = psr; +#else +# define SBC_INST() \ + tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 1); \ + acc = (tmp1 & 0xffff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg7 = psr; +#endif + + +#undef CMP_INST + +#ifdef ACC8 +# define CMP_INST() \ + arg = (acc & 0xff) + (0x100 - arg); \ + SET_CARRY8(arg); \ + arg = arg & 0xff; \ + SET_NEG_ZERO8(arg & 0xff); +#else +# define CMP_INST() \ + arg = (acc & 0xffff) + (0x10000 - arg); \ + SET_CARRY16(arg); \ + arg = arg & 0xffff; \ + SET_NEG_ZERO16(arg & 0xffff); +#endif + +#undef BIT_INST + +#ifdef ACC8 +# define BIT_INST() \ + neg7 = arg; \ + zero = (acc & arg & 0xff); \ + psr = (psr & (~0x40)) | (arg & 0x40); +#else +# define BIT_INST() \ + neg7 = arg >> 8; \ + zero = (acc & arg & 0xffff); \ + psr = (psr & (~0x40)) | ((arg >> 8) & 0x40); +#endif + +#undef STA_INST + +#ifdef ACC8 +# define STA_INST(in_bank) \ + SET_MEMORY8(arg, acc); +#else +# define STA_INST(in_bank) \ + SET_MEMORY16(arg, acc, in_bank); +#endif + +#undef TSB_INST + +#ifdef ACC8 +# define TSB_INST(in_bank) \ + arg = arg & 0xff; \ + tmp1 = arg | acc; \ + zero = arg & acc; \ + SET_MEMORY8(addr_latch, tmp1); +#else +# define TSB_INST(in_bank) \ + tmp1 = arg | acc; \ + zero = arg & acc; \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +#endif + +#undef ASL_INST + +#ifdef ACC8 +# define ASL_INST(in_bank) \ + psr = (psr & 0x1fe) + ((arg >> 7) & 1); \ + tmp1 = (arg << 1) & 0xff; \ + SET_NEG_ZERO8(tmp1); \ + SET_MEMORY8(addr_latch, tmp1); +#else +# define ASL_INST(in_bank) \ + psr = (psr & 0x1fe) + ((arg >> 15) & 1);\ + tmp1 = (arg << 1) & 0xffff; \ + SET_NEG_ZERO16(tmp1); \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +#endif + +#undef ROL_INST + +#ifdef ACC8 +# define ROL_INST(in_bank) \ + arg = arg & 0xff; \ + arg = (arg << 1) | (psr & 1); \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg & 0xff); \ + SET_CARRY8(arg); +#else +# define ROL_INST(in_bank) \ + arg = (arg << 1) | (psr & 1); \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg & 0xffff); \ + SET_CARRY16(arg); +#endif + +#undef LSR_INST + +#ifdef ACC8 +# define LSR_INST(in_bank) \ + SET_CARRY8(arg << 8); \ + arg = (arg >> 1) & 0x7f; \ + SET_NEG_ZERO8(arg); \ + SET_MEMORY8(addr_latch, arg); +#else +# define LSR_INST(in_bank) \ + SET_CARRY16(arg << 16); \ + arg = (arg >> 1) & 0x7fff; \ + SET_NEG_ZERO16(arg); \ + SET_MEMORY16(addr_latch, arg, in_bank); +#endif + +#undef ROR_INST + +#ifdef ACC8 +# define ROR_INST(in_bank) \ + tmp1 = psr & 1; \ + SET_CARRY8(arg << 8); \ + arg = ((arg >> 1) & 0x7f) | (tmp1 << 7); \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg); +#else +# define ROR_INST(in_bank) \ + tmp1 = psr & 1; \ + SET_CARRY16(arg << 16); \ + arg = ((arg >> 1) & 0x7fff) | (tmp1 << 15); \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg); +#endif + +#undef TRB_INST + +#ifdef ACC8 +# define TRB_INST(in_bank) \ + arg = arg & 0xff; \ + tmp1 = arg & ~acc; \ + zero = arg & acc; \ + SET_MEMORY8(addr_latch, tmp1); +#else +# define TRB_INST(in_bank) \ + tmp1 = arg & ~acc; \ + zero = arg & acc; \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +#endif + +#undef DEC_INST + +#ifdef ACC8 +# define DEC_INST(in_bank) \ + arg = (arg - 1) & 0xff; \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg); +#else +# define DEC_INST(in_bank) \ + arg = (arg - 1) & 0xffff; \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg); +#endif + +#undef INC_INST + +#ifdef ACC8 +# define INC_INST(in_bank) \ + arg = (arg + 1) & 0xff; \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg); +#else +# define INC_INST(in_bank) \ + arg = (arg + 1) & 0xffff; \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg); +#endif + +#undef STZ_INST + +#ifdef ACC8 +# define STZ_INST(in_bank) \ + SET_MEMORY8(arg, 0); +#else +# define STZ_INST(in_bank) \ + SET_MEMORY16(arg, 0, in_bank); +#endif + +#undef BRANCH_DISP8 + +#define BRANCH_DISP8(cond) \ + GET_1BYTE_ARG; \ + tmp2 = kpc & 0xff0000; \ + kpc += 2; \ + tmp1 = kpc; \ + if(cond) { \ + kpc = kpc + arg - ((arg & 0x80) << 1); \ + CYCLES_PLUS_1; \ + if((tmp1 ^ kpc) & psr & 0x100) { \ + CYCLES_PLUS_1; \ + } \ + } \ + kpc = tmp2 + (kpc & 0xffff); + +#undef STY_INST +#undef STX_INST + +#define STY_INST(in_bank) \ + if(psr & 0x10) { \ + SET_MEMORY8(arg, yreg); \ + } else { \ + SET_MEMORY16(arg, yreg, in_bank);\ + } +#define STX_INST(in_bank) \ + if(psr & 0x10) { \ + SET_MEMORY8(arg, xreg); \ + } else { \ + SET_MEMORY16(arg, xreg, in_bank);\ + } + +#define C_LDX_ABS_Y() \ + GET_ABS_INDEX_ADDR(yreg, 0); \ + LDX_INST(0); + +#define C_LDY_ABS_X() \ + GET_ABS_INDEX_ADDR(xreg, 0); \ + LDY_INST(0); + +#define C_LDX_ABS() \ + GET_ABS_ADDR(); \ + LDX_INST(0); + +#define C_LDY_ABS() \ + GET_ABS_ADDR(); \ + LDY_INST(0); + +#define C_LDX_DLOC() \ + GET_DLOC_ADDR(); \ + LDX_INST(1); + +#define C_LDY_DLOC() \ + GET_DLOC_ADDR(); \ + LDY_INST(1); + +#define C_LDY_DLOC_X() \ + GET_DLOC_X_ADDR(); \ + LDY_INST(1); + +#define C_LDX_DLOC_Y() \ + GET_DLOC_Y_ADDR(); \ + LDX_INST(1); + +#define CP_INDEX_VAL(index_reg) \ + arg = 0x100 - arg + index_reg; \ + if((psr & 0x10) == 0) { \ + arg += 0xff00; \ + SET_NEG_ZERO16(arg & 0xffff); \ + SET_CARRY16(arg); \ + } else { \ + SET_NEG_ZERO8(arg & 0xff);\ + SET_CARRY8(arg); \ + } + +#define CP_INDEX_LOAD(index_reg, in_bank) \ + if((psr & 0x10) != 0) { \ + GET_MEMORY8(arg, arg); \ + } else { \ + GET_MEMORY16(arg, arg, in_bank);\ + } \ + CP_INDEX_VAL(index_reg) + +#define CPX_INST(in_bank) \ + CP_INDEX_LOAD(xreg, in_bank); + +#define CPY_INST(in_bank) \ + CP_INDEX_LOAD(yreg, in_bank); + +#define C_CPX_IMM() \ + INC_KPC_2; \ + if((psr & 0x10) == 0) { \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_1; \ + } else { \ + GET_1BYTE_ARG; \ + } \ + CP_INDEX_VAL(xreg); + +#define C_CPY_IMM() \ + INC_KPC_2; \ + if((psr & 0x10) == 0) { \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_1; \ + } else { \ + GET_1BYTE_ARG; \ + } \ + CP_INDEX_VAL(yreg); + +#define C_CPX_DLOC() \ + GET_DLOC_ADDR(); \ + CPX_INST(1); + +#define C_CPY_DLOC() \ + GET_DLOC_ADDR(); \ + CPY_INST(1); + +#define C_CPX_ABS() \ + GET_ABS_ADDR(); \ + CPX_INST(0); + +#define C_CPY_ABS() \ + GET_ABS_ADDR(); \ + CPY_INST(0); + diff --git a/gsplus/src/dependency b/gsplus/src/dependency new file mode 100644 index 0000000..f0c21f6 --- /dev/null +++ b/gsplus/src/dependency @@ -0,0 +1,31 @@ +adb.o: adb.c defc.h defcomm.h iwm.h protos.h protos_base.h +engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_base.h size_c.h op_routs.h engine.h defs_instr.h instable.h +clock.o: clock.c defc.h defcomm.h iwm.h protos.h protos_base.h +compile_time.o: compile_time.c +config.o: config.c defc.h defcomm.h iwm.h protos.h protos_base.h config.h win_dirent.h +debugger.o: debugger.c defc.h defcomm.h iwm.h protos.h protos_base.h disas.h +scc.o: scc.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h +scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h +scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h +scc_unixdriver.o: scc_unixdriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h +iwm.o: iwm.c defc.h defcomm.h iwm.h protos.h protos_base.h +joystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h +moremem.o: moremem.c defc.h defcomm.h iwm.h protos.h protos_base.h +paddles.o: paddles.c defc.h defcomm.h iwm.h protos.h protos_base.h +mockingboard.o: mockingboard.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h +sim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h protos_base.h +smartport.o: smartport.c defc.h defcomm.h iwm.h protos.h protos_base.h +doc.o: doc.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h +sound.o: sound.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h +sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h +woz.o: woz.c defc.h defcomm.h iwm.h protos.h protos_base.h +unshk.o: unshk.c defc.h defcomm.h iwm.h protos.h protos_base.h +undeflate.o: undeflate.c defc.h defcomm.h iwm.h protos.h protos_base.h +dynapro.o: dynapro.c defc.h defcomm.h iwm.h protos.h protos_base.h win_dirent.h +dyna_type.o: dyna_type.c defc.h defcomm.h iwm.h protos.h protos_base.h +dyna_filt.o: dyna_filt.c defc.h defcomm.h iwm.h protos.h protos_base.h +dyna_validate.o: dyna_validate.c defc.h defcomm.h iwm.h protos.h protos_base.h +applesingle.o: applesingle.c defc.h defcomm.h iwm.h protos.h protos_base.h +video.o: video.c defc.h defcomm.h iwm.h protos.h protos_base.h kegsfont.h +voc.o: voc.c defc.h defcomm.h iwm.h protos.h protos_base.h +macsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h diff --git a/gsplus/src/disas.h b/gsplus/src/disas.h new file mode 100644 index 0000000..727aa89 --- /dev/null +++ b/gsplus/src/disas.h @@ -0,0 +1,209 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2020 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +enum { + ABS = 1, + ABSX, + ABSY, + ABSLONG, + ABSIND, + ABSXIND, + IMPLY, + ACCUM, + IMMED, + JUST8, + DLOC, + DLOCX, + DLOCY, + LONG, + LONGX, + DLOCIND, + DLOCINDY, + DLOCXIND, + DLOCBRAK, + DLOCBRAKY, + DISP8, + DISP8S, + DISP8SINDY, + DISP16, + MVPMVN, + REPVAL, + SEPVAL +}; + + +const char * const disas_opcodes[256] = { + "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", /* 00-07 */ + "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", /* 08-0f */ + "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", /* 10-17 */ + "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", /* 18-1f */ + "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", /* 20-27 */ + "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", /* 28-2f */ + "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", /* 30-37 */ + "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", /* 38-3f */ + "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", /* 40-47 */ + "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", /* 48-4f */ + "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", /* 50-57 */ + "CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR", /* 58-5f */ + "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", /* 60-67 */ + "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", /* 68-6f */ + "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", /* 70-77 */ + "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", /* 78-7f */ + "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", /* 80-87 */ + "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", /* 88-8f */ + "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", /* 90-97 */ + "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", /* 98-9f */ + "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", /* a0-a7 */ + "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", /* a8-af */ + "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", /* b0-b7 */ + "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", /* b8-bf */ + "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", /* c0-c7 */ + "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", /* c8-cf */ + "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", /* d0-d7 */ + "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", /* d8-df */ + "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", /* e0-e7 */ + "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", /* e8-ef */ + "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", /* f0-f7 */ + "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC", /* f8-ff */ +}; + + +const word32 disas_types[256] = { + JUST8+0x100, DLOCXIND+0x100, /* 00-01 */ + JUST8+0x100, DISP8S+0x100, /* 02-03 */ + DLOC+0x100, DLOC+0x100, /* 04-05 */ + DLOC+0x100, DLOCBRAK+0x100, /* 06-07 */ + IMPLY+0x000, IMMED+0x400, /* 08-9 */ + ACCUM+0x000, IMPLY+0x000, /* 0a-b */ + ABS+0x200, ABS+0x200, /* c-d */ + ABS+0x200, LONG+0x300, /* e-f */ + DISP8+0x100, DLOCINDY+0x100, /* 10-11 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 12-13 */ + DLOC+0x100, DLOCX+0x100, /* 14-15 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 16-17 */ + IMPLY+0x000, ABSY+0x200, /* 18-19 */ + ACCUM+0x000, IMPLY+0x000, /* 1a-1b */ + ABS+0x200, ABSX+0x200, /* 1c-1d */ + ABSX+0x200, LONGX+0x300, /* 1e-1f */ + ABS+0x200, DLOCXIND+0x100, /* 20-21 */ + ABSLONG+0x300, DISP8S+0x100, /* 22-23 */ + DLOC+0x100, DLOC+0x100, /* 24-25 */ + DLOC+0x100, DLOCBRAK+0x100, /* 26-27 */ + IMPLY+0x000, IMMED+0x400, /* 28-29 */ + ACCUM+0x000, IMPLY+0x000, /* 2a-2b */ + ABS+0x200, ABS+0x200, /* 2c-2d */ + ABS+0x200, LONG+0x300, /* 2e-2f */ + DISP8+0x100, DLOCINDY+0x100, /* 30-31 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 32-33 */ + DLOCX+0x100, DLOCX+0x100, /* 34-35 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 36-37 */ + IMPLY+0x000, ABSY+0x200, /* 38-39 */ + ACCUM+0x000, IMPLY+0x000, /* 3a-3b */ + ABSX+0x200, ABSX+0x200, /* 3c-3d */ + ABSX+0x200, LONGX+0x300, /* 3e-3f */ + IMPLY+0x000, DLOCXIND+0x100, /* 40-41 */ + JUST8+0x100, DISP8S+0x100, /* 42-43 */ + MVPMVN+0x200, DLOC+0x100, /* 44-45 */ + DLOC+0x100, DLOCBRAK+0x100, /* 46-47 */ + IMPLY+0x000, IMMED+0x400, /* 48-49 */ + ACCUM+0x000, IMPLY+0x000, /* 4a-4b */ + ABS+0x200, ABS+0x200, /* 4c-4d */ + ABS+0x200, LONG+0x300, /* 4e-4f */ + DISP8+0x100, DLOCINDY+0x100, /* 50-51 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 52-53 */ + MVPMVN+0x200, DLOCX+0x100, /* 54-55 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 56-57 */ + IMPLY+0x000, ABSY+0x200, /* 58-59 */ + IMPLY+0x000, IMPLY+0x000, /* 5a-5b */ + LONG+0x300, ABSX+0x200, /* 5c-5d */ + ABSX+0x200, LONGX+0x300, /* 5e-5f */ + IMPLY+0x000, DLOCXIND+0x100, /* 60-61 */ + DISP16+0x200, DISP8S+0x100, /* 62-63 */ + DLOC+0x100, DLOC+0x100, /* 64-65 */ + DLOC+0x100, DLOCBRAK+0x100, /* 66-67 */ + IMPLY+0x000, IMMED+0x400, /* 68-69 */ + ACCUM+0x000, IMPLY+0x000, /* 6a-6b */ + ABSIND+0x200, ABS+0x200, /* 6c-6d */ + ABS+0x200, LONG+0x300, /* 6e-6f */ + DISP8+0x100, DLOCINDY+0x100, /* 70-71 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 72-73 */ + DLOCX+0x100, DLOCX+0x100, /* 74-75 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 76-77 */ + IMPLY+0x000, ABSY+0x200, /* 78-79 */ + IMPLY+0x000, IMPLY+0x000, /* 7a-7b */ + ABSXIND+0x200, ABSX+0x200, /* 7c-7d */ + ABSX+0x200, LONGX+0x300, /* 7e-7f */ + DISP8+0x100, DLOCXIND+0x100, /* 80-81 */ + DISP16+0x200, DISP8S+0x100, /* 82-83 */ + DLOC+0x100, DLOC+0x100, /* 84-85 */ + DLOC+0x100, DLOCBRAK+0x100, /* 86-87 */ + IMPLY+0x000, IMMED+0x400, /* 88-89 */ + IMPLY+0x000, IMPLY+0x000, /* 8a-8b */ + ABS+0x200, ABS+0x200, /* 8c-8d */ + ABS+0x200, LONG+0x300, /* 8e-8f */ + DISP8+0x100, DLOCINDY+0x100, /* 90-91 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 92-93 */ + DLOCX+0x100, DLOCX+0x100, /* 94-95 */ + DLOCY+0x100, DLOCBRAKY+0x100, /* 96-97 */ + IMPLY+0x000, ABSY+0x200, /* 98-99 */ + IMPLY+0x000, IMPLY+0x000, /* 9a-9b */ + ABS+0x200, ABSX+0x200, /* 9c-9d */ + ABSX+0x200, LONGX+0x300, /* 9e-9f */ + IMMED+0x500, DLOCXIND+0x100, /* a0-a1 */ + IMMED+0x500, DISP8S+0x100, /* a2-a3 */ + DLOC+0x100, DLOC+0x100, /* a4-a5 */ + DLOC+0x100, DLOCBRAK+0x100, /* a6-a7 */ + IMPLY+0x000, IMMED+0x400, /* a8-a9 */ + IMPLY+0x000, IMPLY+0x000, /* aa-ab */ + ABS+0x200, ABS+0x200, /* ac-ad */ + ABS+0x200, LONG+0x300, /* ae-af */ + DISP8+0x100, DLOCINDY+0x100, /* b0-b1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* b2-b3 */ + DLOCX+0x100, DLOCX+0x100, /* b4-b5 */ + DLOCY+0x100, DLOCBRAKY+0x100, /* b6-b7 */ + IMPLY+0x000, ABSY+0x200, /* b8-b9 */ + IMPLY+0x000, IMPLY+0x000, /* ba-bb */ + ABSX+0x200, ABSX+0x200, /* bc-bd */ + ABSY+0x200, LONGX+0x300, /* be-bf */ + IMMED+0x500, DLOCXIND+0x100, /* c0-c1 */ + REPVAL+0x100, DISP8S+0x100, /* c2-c3 */ + DLOC+0x100, DLOC+0x100, /* c4-c5 */ + DLOC+0x100, DLOCBRAK+0x100, /* c6-c7 */ + IMPLY+0x000, IMMED+0x400, /* c8-c9 */ + IMPLY+0x000, IMPLY+0x000, /* ca-cb */ + ABS+0x200, ABS+0x200, /* cc-cd */ + ABS+0x200, LONG+0x300, /* ce-cf */ + DISP8+0x100, DLOCINDY+0x100, /* d0-d1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* d2-d3 */ + DLOC+0x100, DLOCX+0x100, /* d4-d5 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* d6-d7 */ + IMPLY+0x000, ABSY+0x200, /* d8-d9 */ + IMPLY+0x000, IMPLY+0x000, /* da-db */ + ABSIND+0x200, ABSX+0x200, /* dc-dd */ + ABSX+0x200, LONGX+0x300, /* de-df */ + IMMED+0x500, DLOCXIND+0x100, /* e0-e1 */ + SEPVAL+0x100, DISP8S+0x100, /* e2-e3 */ + DLOC+0x100, DLOC+0x100, /* e4-e5 */ + DLOC+0x100, DLOCBRAK+0x100, /* e6-e7 */ + IMPLY+0x000, IMMED+0x400, /* e8-e9 */ + IMPLY+0x000, IMPLY+0x000, /* ea-eb */ + ABS+0x200, ABS+0x200, /* ec-ed */ + ABS+0x200, LONG+0x300, /* ee-ef */ + DISP8+0x100, DLOCINDY+0x100, /* f0-f1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* f2-f3 */ + IMMED+0x200, DLOCX+0x100, /* f4-f5 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* f6-f7 */ + IMPLY+0x000, ABSY+0x200, /* f8-f9 */ + IMPLY+0x000, IMPLY+0x000, /* fa-fb */ + ABSXIND+0x200, ABSX+0x200, /* fc-fd */ + ABSX+0x200, LONGX+0x300, /* fe-ff */ +}; + diff --git a/gsplus/src/doc.c b/gsplus/src/doc.c new file mode 100644 index 0000000..7109642 --- /dev/null +++ b/gsplus/src/doc.c @@ -0,0 +1,1091 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Ensoniq 5503 DOC routines +// The Ensoniq DOC is controlled through the Sound GLU at $C03C-$C03F, +// where $C03E-$C03F are a 16-bit pointer to "what" to access, $C03D is +// the data register, and $C03C is a control register with volume. +// The DOC operates at 894.886KHZ (7M clock divided by 8). It visits each +// oscillator (up to 32) once per clock, plus 2 extra clocks for DOC RAM +// refresh. +// KEGS cheats and pretends the DOC runs at 48KHz, and visits all oscillators +// each "sample". KEGS adjusts the internal accumulators so the right +// frequency is achieved. This allows the sample calculations to be +// greatly simplified, and achieves higher fidelity when all 32 osc are +// enabled (which is generally how most Apple IIgs code works). + +#include "defc.h" +#include "sound.h" + +#define DOC_LOG(a,b,c,d) + +extern int Verbose; +extern int g_use_shmem; +extern word32 g_vbl_count; +extern int g_preferred_rate; + +extern word32 g_c03ef_doc_ptr; + +extern dword64 g_last_vbl_dfcyc; + +byte doc_ram[0x10000 + 16]; + +word32 g_doc_sound_ctl = 0; +word32 g_doc_saved_val = 0; +int g_doc_num_osc_en = 1; +double g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); + +int g_doc_vol = 8; +int g_doc_saved_ctl = 0; +int g_num_osc_interrupting = 0; +Doc_reg g_doc_regs[32]; + +word32 doc_reg_e0 = 0xff; + +extern double g_drecip_audio_rate; +extern double g_dsamps_per_dfcyc; +extern double g_fcyc_per_samp; + +/* local function prototypes */ +void doc_write_ctl_reg(dword64 dfcyc, int osc, int val); + +#define DOC_SCAN_RATE (DCYCS_28_MHZ/32.0) + +#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en) \ + g_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2); + +#define SND_PTR_SHIFT 14 +#define SND_PTR_SHIFT_DBL ((double)(1 << SND_PTR_SHIFT)) + +void +doc_init() +{ + Doc_reg *rptr; + int i; + + for(i = 0; i < 32; i++) { + rptr = &(g_doc_regs[i]); + rptr->dsamp_ev = 0.0; + rptr->dsamp_ev2 = 0.0; + rptr->complete_dsamp = 0.0; + rptr->samps_left = 0; + rptr->cur_acc = 0; + rptr->cur_inc = 0; + rptr->cur_start = 0; + rptr->cur_end = 0; + rptr->cur_mask = 0; + rptr->size_bytes = 0; + rptr->event = 0; + rptr->running = 0; + rptr->has_irq_pending = 0; + rptr->freq = 0; + rptr->vol = 0; + rptr->waveptr = 0; + rptr->ctl = 1; + rptr->wavesize = 0; + rptr->last_samp_val = 0; + } +} +void +doc_reset(dword64 dfcyc) +{ + int i; + + for(i = 0; i < 32; i++) { + doc_write_ctl_reg(dfcyc, i, g_doc_regs[i].ctl | 1); + doc_reg_e0 = 0xff; + if(g_doc_regs[i].has_irq_pending) { + halt_printf("reset: has_irq[%02x] = %d\n", i, + g_doc_regs[i].has_irq_pending); + } + g_doc_regs[i].has_irq_pending = 0; + } + if(g_num_osc_interrupting) { + halt_printf("reset: num_osc_int:%d\n", g_num_osc_interrupting); + } + g_num_osc_interrupting = 0; + + g_doc_num_osc_en = 1; + UPDATE_G_DCYCS_PER_DOC_UPDATE(1); +} + +int +doc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps, + int snd_buf_init, int *outptr_start) +{ + Doc_reg *rptr; + int *outptr; + double complete_dsamp, cur_dsamp; + word32 cur_acc, cur_pos, cur_mask, cur_inc, cur_end; + int val, val2, imul, off, ctl, num_osc_en; + int samps_left, samps_to_do, samp_offset, pos, osc, done; + int i, j; + + num_osc_en = g_doc_num_osc_en; + + dbg_log_info(dfcyc, num_samps, 0, 0xd0c0); + + done = 0; + // Do Ensoniq oscillators + while(!done) { + done = 1; + for(j = 0; j < num_osc_en; j++) { + osc = j; + rptr = &(g_doc_regs[osc]); + complete_dsamp = rptr->complete_dsamp; + samps_left = rptr->samps_left; + cur_acc = rptr->cur_acc; + cur_mask = rptr->cur_mask; + cur_inc = rptr->cur_inc; + cur_end = rptr->cur_end; + if(!rptr->running || cur_inc == 0 || + (complete_dsamp >= dsamp_now)) { + continue; + } + + done = 0; + ctl = rptr->ctl; + + samp_offset = 0; + if(complete_dsamp > last_dsamp) { + samp_offset = (int)(complete_dsamp- last_dsamp); + if(samp_offset > num_samps) { + rptr->complete_dsamp = dsamp_now; + continue; + } + } + outptr = outptr_start + 2 * samp_offset; + if(ctl & 0x10) { + /* other channel */ + outptr += 1; + } + + imul = (rptr->vol * g_doc_vol); + off = imul * 128; + + samps_to_do = MY_MIN(samps_left, + num_samps - samp_offset); + if(imul == 0 || samps_to_do == 0) { + /* produce no sound */ + samps_left = samps_left - samps_to_do; + cur_acc += cur_inc * samps_to_do; + rptr->samps_left = samps_left; + rptr->cur_acc = cur_acc; + cur_dsamp = last_dsamp + + (double)(samps_to_do + samp_offset); + DOC_LOG("nosnd", osc, cur_dsamp, samps_to_do); + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start+(cur_acc & cur_mask); + if(samps_left <= 0) { + doc_sound_end(osc, 1, cur_dsamp, + dsamp_now); + val = 0; + j--; + } else { + val = doc_ram[cur_pos >> SND_PTR_SHIFT]; + } + rptr->last_samp_val = val; + continue; + } + if(snd_buf_init == 0) { + memset(outptr_start, 0, + 2*sizeof(outptr_start[0])*num_samps); + snd_buf_init++; + } + val = 0; + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start + (cur_acc & cur_mask); + pos = 0; + for(i = 0; i < samps_to_do; i++) { + pos = cur_pos >> SND_PTR_SHIFT; + cur_pos += cur_inc; + cur_acc += cur_inc; + val = doc_ram[pos]; + val2 = (val * imul - off) >> 4; + if((val == 0) || (cur_pos >= cur_end)) { + cur_dsamp = last_dsamp + + (double)(samp_offset + i + 1); + rptr->cur_acc = cur_acc; + rptr->samps_left = 0; + DOC_LOG("end or 0", osc, cur_dsamp, + ((pos & 0xffffU) << 16) | + ((i &0xff) << 8) | val); + doc_sound_end(osc, val, cur_dsamp, + dsamp_now); + val = 0; + break; + } + val2 = outptr[0] + val2; + samps_left--; + *outptr = val2; + outptr += 2; + } + rptr->last_samp_val = val; + + if(val != 0) { + rptr->cur_acc = cur_acc; + rptr->samps_left = samps_left; + rptr->complete_dsamp = dsamp_now; + } + DOC_LOG("splayed", osc, dsamp_now, + (samps_to_do << 16) + (pos & 0xffff)); + } + } + + return snd_buf_init; +} +void +doc_handle_event(int osc, dword64 dfcyc) +{ + /* handle osc stopping and maybe interrupting */ + + //DOC_LOG("doc_ev", osc, dsamps, 0); + + g_doc_regs[osc].event = 0; + + sound_play(dfcyc); +} + +void +doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps) +{ + Doc_reg *rptr, *orptr; + int mode, omode; + int other_osc; + int one_shot_stop; + int ctl; + + /* handle osc stopping and maybe interrupting */ + + if(osc < 0 || osc > 31) { + printf("doc_handle_event: osc: %d!\n", osc); + return; + } + + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + rptr->cur_acc = 0; /* reset internal accumulator*/ + + /* check to make sure osc is running */ + if(ctl & 0x01) { + /* Oscillator already stopped. */ + halt_printf("Osc %d interrupt, but it was already stop!\n",osc); +#ifdef HPUX + U_STACK_TRACE(); +#endif + return; + } + + if(ctl & 0x08) { + if(rptr->has_irq_pending == 0) { + doc_add_sound_irq(osc); + } + } + + if(!rptr->running) { + halt_printf("Doc event for osc %d, but ! running\n", osc); + } + + rptr->running = 0; + + mode = (ctl >> 1) & 3; + other_osc = osc ^ 1; + orptr = &(g_doc_regs[other_osc]); + omode = (orptr->ctl >> 1) & 3; + + /* If either this osc or it's partner is in swap mode, treat the */ + /* pair as being in swap mode. This Ensoniq feature pointed out */ + /* by Ian Schmidt */ + if(mode == 0 && can_repeat) { + /* free-running mode with no 0 byte! */ + /* start doing it again */ + + doc_start_sound(osc, eff_dsamps, dsamps); + + return; + } else if((mode == 3) || (omode == 3)) { + /* swap mode (even if we're one_shot and partner is swap)! */ + /* unless we're one-shot and we hit a 0-byte--then */ + /* Olivier Goguel says just stop */ + rptr->ctl |= 1; + one_shot_stop = (mode == 1) && (!can_repeat); + if(!one_shot_stop && !orptr->running && + (orptr->ctl & 0x1)) { + orptr->ctl = orptr->ctl & (~1); + doc_start_sound(other_osc, eff_dsamps, dsamps); + } + return; + } else { + /* stop the oscillator */ + rptr->ctl |= 1; + } + + return; +} + +void +doc_add_sound_irq(int osc) +{ + int num_osc_interrupting; + + if(g_doc_regs[osc].has_irq_pending) { + halt_printf("Adding sound_irq for %02x, but irq_p: %d\n", osc, + g_doc_regs[osc].has_irq_pending); + } + + num_osc_interrupting = g_num_osc_interrupting + 1; + g_doc_regs[osc].has_irq_pending = num_osc_interrupting; + g_num_osc_interrupting = num_osc_interrupting; + + add_irq(IRQ_PENDING_DOC); + if(num_osc_interrupting == 1) { + doc_reg_e0 = 0x00 + (osc << 1); + } + + DOC_LOG("add_irq", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0); +} + +void +doc_remove_sound_irq(int osc, int must) +{ + Doc_reg *rptr; + int num_osc_interrupt; + int has_irq_pending; + int first; + int i; + + doc_printf("remove irq for osc: %d, has_irq: %d\n", + osc, g_doc_regs[osc].has_irq_pending); + + num_osc_interrupt = g_doc_regs[osc].has_irq_pending; + first = 0; + if(num_osc_interrupt) { + g_num_osc_interrupting--; + g_doc_regs[osc].has_irq_pending = 0; + DOC_LOG("rem_irq", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0); + if(g_num_osc_interrupting == 0) { + remove_irq(IRQ_PENDING_DOC); + } + + first = 0x40 | (doc_reg_e0 >> 1); + /* if none found, then def = no ints */ + for(i = 0; i < g_doc_num_osc_en; i++) { + rptr = &(g_doc_regs[i]); + has_irq_pending = rptr->has_irq_pending; + if(has_irq_pending > num_osc_interrupt) { + has_irq_pending--; + rptr->has_irq_pending = has_irq_pending; + } + if(has_irq_pending == 1) { + first = i; + } + } + if(num_osc_interrupt == 1) { + doc_reg_e0 = (first << 1); + } else { +#if 0 + halt_printf("remove_sound_irq[%02x]=%d, first:%d\n", + osc, num_osc_interrupt, first); +#endif + } + } else { +#if 0 + /* make sure no int pending */ + if(doc_reg_e0 != 0xff) { + halt_printf("remove_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } +#endif + if(must) { + halt_printf("REMOVE_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } + } + + if(doc_reg_e0 & 0x80) { + for(i = 0; i < 0x20; i++) { + has_irq_pending = g_doc_regs[i].has_irq_pending; + if(has_irq_pending) { + halt_printf("remove_sound_irq[%02x], but " + "[%02x]=%d!\n", osc,i,has_irq_pending); + printf("num_osc_int: %d, first: %02x\n", + num_osc_interrupt, first); + } + } + } +} + +void +doc_start_sound2(int osc, dword64 dfcyc) +{ + double dsamps; + + dsamps = dfcyc * g_dsamps_per_dfcyc; + doc_start_sound(osc, dsamps, dsamps); +} + +void +doc_start_sound(int osc, double eff_dsamps, double dsamps) +{ + Doc_reg *rptr; + int ctl; + int mode; + word32 sz; + word32 size; + word32 wave_size; + + if(osc < 0 || osc > 31) { + halt_printf("start_sound: osc: %02x!\n", osc); + } + + rptr = &(g_doc_regs[osc]); + + if(osc >= g_doc_num_osc_en) { + rptr->ctl |= 1; + return; + } + + ctl = rptr->ctl; + mode = (ctl >> 1) & 3; + wave_size = rptr->wavesize; + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + + if(size < 0x100) { + halt_printf("size: %08x is too small, sz: %08x!\n", size, sz); + } + + if(rptr->running) { + halt_printf("start_sound osc: %d, already running!\n", osc); + } + + rptr->running = 1; + + rptr->complete_dsamp = eff_dsamps; + + doc_printf("Starting osc %02x, dsamp: %f\n", osc, dsamps); + doc_printf("size: %04x\n", size); + + if((mode == 2) && ((osc & 1) == 0)) { + printf("Sync mode osc %d starting!\n", osc); + /* set_halt(1); */ + + /* see if we should start our odd partner */ + if((rptr[1].ctl & 7) == 5) { + /* odd partner stopped in sync mode--start him */ + rptr[1].ctl &= (~1); + doc_start_sound(osc + 1, eff_dsamps, dsamps); + } else { + printf("Osc %d starting sync, but osc %d ctl: %02x\n", + osc, osc+1, rptr[1].ctl); + } + } + + doc_wave_end_estimate(osc, eff_dsamps, dsamps); + + DOC_LOG("st playing", osc, eff_dsamps, size); +#if 0 + if(rptr->cur_acc != 0) { + halt_printf("Start osc %02x, acc: %08x\n", osc, rptr->cur_acc); + } +#endif +} + +void +doc_wave_end_estimate2(int osc, dword64 dfcyc) +{ + double dsamps; + + dsamps = dfcyc * g_dsamps_per_dfcyc; + doc_wave_end_estimate(osc, dsamps, dsamps); +} + +void +doc_wave_end_estimate(int osc, double eff_dsamps, double dsamps) +{ + Doc_reg *rptr; + byte *ptr1; + dword64 event_dfcyc; + double event_dsamp, dfcycs_per_samp, dsamps_per_byte, num_dsamps; + double dcur_inc; + word32 tmp1, cur_inc, save_val; + int save_size, pos, size, estimate; + + dfcycs_per_samp = g_fcyc_per_samp; + + rptr = &(g_doc_regs[osc]); + + cur_inc = rptr->cur_inc; + dcur_inc = (double)cur_inc; + dsamps_per_byte = 0.0; + if(cur_inc) { + dsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc; + } + + /* see if there's a zero byte */ + tmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask); + pos = tmp1 >> SND_PTR_SHIFT; + size = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos; + + ptr1 = &doc_ram[pos]; + + estimate = 0; + if(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) { + estimate = 1; + } + +#if 0 + estimate = 1; +#endif + if(estimate) { + save_size = size; + save_val = ptr1[size]; + ptr1[size] = 0; + size = (int)strlen((char *)ptr1); + ptr1[save_size] = save_val; + } + + /* calc samples to play */ + num_dsamps = (dsamps_per_byte * (double)size) + 1.0; + + rptr->samps_left = (int)num_dsamps; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + + event_dsamp = eff_dsamps + num_dsamps; + if(estimate) { + rptr->event = 1; + rptr->dsamp_ev = event_dsamp; + rptr->dsamp_ev2 = dsamps; + event_dfcyc = (dword64)(event_dsamp * dfcycs_per_samp) + + 65536LL; + add_event_doc(event_dfcyc, osc); + } +} + +void +doc_remove_sound_event(int osc) +{ + if(g_doc_regs[osc].event) { + g_doc_regs[osc].event = 0; + remove_event_doc(osc); + } +} + + +void +doc_write_ctl_reg(dword64 dfcyc, int osc, int val) +{ + Doc_reg *rptr; + word32 old_halt, new_halt; + int old_val; + + if(osc < 0 || osc >= 0x20) { + halt_printf("doc_write_ctl_reg: osc: %02x, val: %02x\n", + osc, val); + return; + } + + rptr = &(g_doc_regs[osc]); + old_val = rptr->ctl; + g_doc_saved_ctl = old_val; + + if(old_val == val) { + return; + } + + //DOC_LOG("ctl_reg", osc, dsamps, (old_val << 16) + val); + + old_halt = (old_val & 1); + new_halt = (val & 1); + + /* bits are: 28: old int bit */ + /* 29: old halt bit */ + /* 30: new int bit */ + /* 31: new halt bit */ + +#if 0 + if(osc == 0x10) { + printf("osc %d new_ctl: %02x, old: %02x\n", osc, val, old_val); + } +#endif + + /* no matter what, remove any pending IRQs on this osc */ + doc_remove_sound_irq(osc, 0); + +#if 0 + if(old_halt) { + printf("doc_write_ctl to osc %d, val: %02x, old: %02x\n", + osc, val, old_val); + } +#endif + + if(new_halt != 0) { + /* make sure sound is stopped */ + doc_remove_sound_event(osc); + if(old_halt == 0) { + /* it was playing, finish it up */ +#if 0 + halt_printf("Aborted osc %d at eff_dsamps: %f, ctl: " + "%02x, oldctl: %02x\n", osc, eff_dsamps, + val, old_val); +#endif + sound_play(dfcyc); + } + if(((old_val >> 1) & 3) > 0) { + /* don't clear acc if free-running */ + g_doc_regs[osc].cur_acc = 0; + } + + g_doc_regs[osc].ctl = val; + g_doc_regs[osc].running = 0; + } else { + /* new halt == 0 = make sure sound is running */ + if(old_halt != 0) { + /* start sound */ + //DOC_LOG("ctl_sound_play", osc, eff_dsamps, val); + sound_play(dfcyc); + g_doc_regs[osc].ctl = val; + + doc_start_sound2(osc, dfcyc); + } else { + /* was running, and something changed */ + doc_printf("osc %d old ctl:%02x new:%02x!\n", + osc, old_val, val); + sound_play(dfcyc); + g_doc_regs[osc].ctl = val; + if((old_val ^ val) & val & 0x8) { + /* now has ints on */ + doc_wave_end_estimate2(osc, dfcyc); + } + } + } +} + +void +doc_recalc_sound_parms(dword64 dfcyc, int osc) +{ + Doc_reg *rptr; + double dfreq, dtmp1, dacc, dacc_recip; + word32 res, sz, size, wave_size, cur_start, shifted_size; + + rptr = &(g_doc_regs[osc]); + + wave_size = rptr->wavesize; + + dfreq = (double)rptr->freq; + + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + rptr->size_bytes = size; + res = wave_size & 7; + + shifted_size = size << SND_PTR_SHIFT; + cur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (0-shifted_size); + + dtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate); + dacc = (double)(1 << (20 - (17 - sz + res))); + dacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20)); + dtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip; + + rptr->cur_inc = (int)(dtmp1); + rptr->cur_start = cur_start; + rptr->cur_end = cur_start + shifted_size; + rptr->cur_mask = (shifted_size - 1); + + dbg_log_info(dfcyc, (rptr->waveptr << 16) + wave_size, osc, 0xd0cf); +} + +int +doc_read_c03c() +{ + return g_doc_sound_ctl; +} + +int +doc_read_c03d(dword64 dfcyc) +{ + Doc_reg *rptr; + int osc, type, ret; + + ret = g_doc_saved_val; + + if(g_doc_sound_ctl & 0x40) { + /* Read RAM */ + g_doc_saved_val = doc_ram[g_c03ef_doc_ptr]; + } else { + /* Read DOC */ + g_doc_saved_val = 0; + + osc = g_c03ef_doc_ptr & 0x1f; + type = (g_c03ef_doc_ptr >> 5) & 0x7; + rptr = &(g_doc_regs[osc]); + + switch(type) { + case 0x0: /* freq lo */ + g_doc_saved_val = rptr->freq & 0xff; + break; + case 0x1: /* freq hi */ + g_doc_saved_val = rptr->freq >> 8; + break; + case 0x2: /* vol */ + g_doc_saved_val = rptr->vol; + break; + case 0x3: /* data register */ + sound_play(dfcyc); // Fix for Paperboy GS + g_doc_saved_val = rptr->last_samp_val; + break; + case 0x4: /* wave ptr register */ + g_doc_saved_val = rptr->waveptr; + break; + case 0x5: /* control register */ + g_doc_saved_val = rptr->ctl; + break; + case 0x6: /* control register */ + g_doc_saved_val = rptr->wavesize; + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + g_doc_saved_val = doc_reg_e0; + doc_printf("Reading doc 0xe0, ret: %02x\n", + g_doc_saved_val); + + /* Clear IRQ on read of e0, if any irq pend */ + if((doc_reg_e0 & 0x80) == 0) { + doc_remove_sound_irq(doc_reg_e0 >> 1, + 1); + } + break; + case 0x01: /* 0xe1 */ + g_doc_saved_val = (g_doc_num_osc_en - 1) << 1; + break; + case 0x02: /* 0xe2 */ + g_doc_saved_val = 0x80; +#if 0 + halt_printf("Reading doc 0xe2, ret: %02x\n", + g_doc_saved_val); +#endif + break; + default: + g_doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + g_c03ef_doc_ptr, g_doc_saved_val); + } + break; + default: + g_doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + g_c03ef_doc_ptr, g_doc_saved_val); + } + } + + doc_printf("read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\n", + g_c03ef_doc_ptr, ret, g_doc_saved_val); + + //DOC_LOG("read c03d", -1, dsamps, (g_c03ef_doc_ptr << 16) + + // (g_doc_saved_val << 8) + ret); + + if(g_doc_sound_ctl & 0x20) { + g_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff; + } + + + return ret; +} + +void +doc_write_c03c(dword64 dfcyc, word32 val) +{ + int vol; + + vol = val & 0xf; + dbg_log_info(dfcyc, val, g_doc_vol, 0xc03c); + if(g_doc_vol != vol) { + sound_play(dfcyc); + + g_doc_vol = vol; + doc_printf("Setting doc vol to 0x%x at %016llx\n", vol, dfcyc); + } + + g_doc_sound_ctl = val; +} + +void +doc_write_c03d(dword64 dfcyc, word32 val) +{ + Doc_reg *rptr; + int osc, type, ctl, tmp; + int i; + + val = val & 0xff; + + doc_printf("write c03d, doc_ptr: %04x, val: %02x\n", + g_c03ef_doc_ptr, val); + + dbg_log_info(dfcyc, g_c03ef_doc_ptr, val, 0xc03d); + + if(g_doc_sound_ctl & 0x40) { + /* RAM */ + doc_ram[g_c03ef_doc_ptr] = val; + } else { + /* DOC */ + osc = g_c03ef_doc_ptr & 0x1f; + type = (g_c03ef_doc_ptr >> 5) & 0x7; + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; +#if 0 + if((ctl & 1) == 0) { + if(type < 2 || type == 4 || type == 6) { + halt_printf("Osc %d is running, old ctl: %02x, " + "but write reg %02x=%02x\n", + osc, ctl, g_c03ef_doc_ptr & 0xff, val); + } + } +#endif + + switch(type) { + case 0x0: /* freq lo */ + if((rptr->freq & 0xff) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + //DOC_LOG("flo_sound_play", osc, dsamps, val); + sound_play(dfcyc); + } + rptr->freq = (rptr->freq & 0xff00) + val; + doc_recalc_sound_parms(dfcyc, osc); + break; + case 0x1: /* freq hi */ + if((rptr->freq >> 8) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + //DOC_LOG("fhi_sound_play", osc, dsamps, val); + sound_play(dfcyc); + } + rptr->freq = (rptr->freq & 0xff) + (val << 8); + doc_recalc_sound_parms(dfcyc, osc); + break; + case 0x2: /* vol */ + if(rptr->vol == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + //DOC_LOG("vol_sound_play", osc, dsamps, val); + sound_play(dfcyc); + } + rptr->vol = val; + break; + case 0x3: /* data register */ +#if 0 + printf("Writing %02x into doc_data_reg[%02x]!\n", + val, osc); +#endif + break; + case 0x4: /* wave ptr register */ + if(rptr->waveptr == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + //DOC_LOG("wptr_sound_play", osc, dsamps, val); + sound_play(dfcyc); + } + rptr->waveptr = val; + doc_recalc_sound_parms(dfcyc, osc); + break; + case 0x5: /* control register */ +#if 0 + printf("doc_write ctl osc %d, val: %02x\n", osc, val); +#endif + if(rptr->ctl == (word32)val) { + break; + } + doc_write_ctl_reg(dfcyc, osc, val); + break; + case 0x6: /* wavesize register */ + if(rptr->wavesize == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + //DOC_LOG("wsz_sound_play", osc, dsamps, val); + sound_play(dfcyc); + } + rptr->wavesize = val; + doc_recalc_sound_parms(dfcyc, osc); + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + doc_printf("writing doc 0xe0 with %02x, " + "was:%02x\n", val, doc_reg_e0); +#if 0 + if(val != doc_reg_e0) { + halt_printf("writing doc 0xe0 with " + "%02x, was:%02x\n", val, + doc_reg_e0); + } +#endif + break; + case 0x01: /* 0xe1 */ + doc_printf("Writing doc 0xe1 with %02x\n", val); + tmp = val & 0x3e; + tmp = (tmp >> 1) + 1; + if(tmp < 1) { + tmp = 1; + } + if(tmp > 32) { + halt_printf("doc 0xe1: %02x!\n", val); + tmp = 32; + } + g_doc_num_osc_en = tmp; + UPDATE_G_DCYCS_PER_DOC_UPDATE(tmp); + + /* Stop any oscs that were running but now */ + /* are disabled */ + for(i = g_doc_num_osc_en; i < 0x20; i++) { + doc_write_ctl_reg(dfcyc, i, + g_doc_regs[i].ctl | 1); + } + + break; + default: + /* this should be illegal, but Turkey Shoot */ + /* and apparently TaskForce, OOTW, etc */ + /* writes to e2-ff, for no apparent reason */ + doc_printf("Writing doc 0x%x with %02x\n", + g_c03ef_doc_ptr, val); + break; + } + break; + default: + halt_printf("Writing %02x into bad doc_reg[%04x]\n", + val, g_c03ef_doc_ptr); + } + } + + if(g_doc_sound_ctl & 0x20) { + g_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff; + } + + g_doc_saved_val = val; +} + +void +doc_show_ensoniq_state() +{ + Doc_reg *rptr; + int i; + + printf("Ensoniq state\n"); + printf("c03c doc_sound_ctl: %02x, doc_saved_val: %02x\n", + g_doc_sound_ctl, g_doc_saved_val); + printf("doc_ptr: %04x, num_osc_en: %02x, e0: %02x\n", + g_c03ef_doc_ptr, g_doc_num_osc_en, doc_reg_e0); + + for(i = 0; i < 32; i += 8) { + printf("irqp: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\n", + i, + g_doc_regs[i].has_irq_pending, + g_doc_regs[i + 1].has_irq_pending, + g_doc_regs[i + 2].has_irq_pending, + g_doc_regs[i + 3].has_irq_pending, + g_doc_regs[i + 4].has_irq_pending, + g_doc_regs[i + 5].has_irq_pending, + g_doc_regs[i + 6].has_irq_pending, + g_doc_regs[i + 7].has_irq_pending); + } + + for(i = 0; i < 32; i += 8) { + printf("freq: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\n", + i, + g_doc_regs[i].freq, g_doc_regs[i + 1].freq, + g_doc_regs[i + 2].freq, g_doc_regs[i + 3].freq, + g_doc_regs[i + 4].freq, g_doc_regs[i + 5].freq, + g_doc_regs[i + 6].freq, g_doc_regs[i + 7].freq); + } + + for(i = 0; i < 32; i += 8) { + printf("vol: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].vol, g_doc_regs[i + 1].vol, + g_doc_regs[i + 2].vol, g_doc_regs[i + 3].vol, + g_doc_regs[i + 4].vol, g_doc_regs[i + 5].vol, + g_doc_regs[i + 6].vol, g_doc_regs[i + 6].vol); + } + + for(i = 0; i < 32; i += 8) { + printf("wptr: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].waveptr, g_doc_regs[i + 1].waveptr, + g_doc_regs[i + 2].waveptr, g_doc_regs[i + 3].waveptr, + g_doc_regs[i + 4].waveptr, g_doc_regs[i + 5].waveptr, + g_doc_regs[i + 6].waveptr, g_doc_regs[i + 7].waveptr); + } + + for(i = 0; i < 32; i += 8) { + printf("ctl: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].ctl, g_doc_regs[i + 1].ctl, + g_doc_regs[i + 2].ctl, g_doc_regs[i + 3].ctl, + g_doc_regs[i + 4].ctl, g_doc_regs[i + 5].ctl, + g_doc_regs[i + 6].ctl, g_doc_regs[i + 7].ctl); + } + + for(i = 0; i < 32; i += 8) { + printf("wsize: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].wavesize, g_doc_regs[i + 1].wavesize, + g_doc_regs[i + 2].wavesize, g_doc_regs[i + 3].wavesize, + g_doc_regs[i + 4].wavesize, g_doc_regs[i + 5].wavesize, + g_doc_regs[i + 6].wavesize, g_doc_regs[i + 7].wavesize); + } + + for(i = 0; i < 32; i++) { + rptr = &(g_doc_regs[i]); + printf("%2d: ctl:%02x wp:%02x ws:%02x freq:%04x vol:%02x " + "ev:%d run:%d irq:%d sz:%04x\n", i, + rptr->ctl, rptr->waveptr, rptr->wavesize, rptr->freq, + rptr->vol, rptr->event, rptr->running, + rptr->has_irq_pending, rptr->size_bytes); + printf(" acc:%08x inc:%08x st:%08x end:%08x m:%08x\n", + rptr->cur_acc, rptr->cur_inc, rptr->cur_start, + rptr->cur_end, rptr->cur_mask); + printf(" compl_ds:%f samps_left:%d ev:%f ev2:%f\n", + rptr->complete_dsamp, rptr->samps_left, + rptr->dsamp_ev, rptr->dsamp_ev2); + } + +#if 0 + for(osc = 0; osc < 32; osc++) { + fmax = 0.0; + printf("osc %d has %d samps\n", osc, g_fsamp_num[osc]); + for(i = 0; i < g_fsamp_num[osc]; i++) { + printf("%4d: %f\n", i, g_fsamps[osc][i]); + fmax = MY_MAX(fmax, g_fsamps[osc][i]); + } + printf("osc %d, fmax: %f\n", osc, fmax); + } +#endif +} diff --git a/gsplus/src/dyna_filt.c b/gsplus/src/dyna_filt.c new file mode 100644 index 0000000..b8acfe5 --- /dev/null +++ b/gsplus/src/dyna_filt.c @@ -0,0 +1,17 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +// Provide filters for Dynapro to use for copying files from the host to +// a ProDOS volume, and for writing changes to the ProDOS volume back to +// host files. + diff --git a/gsplus/src/dyna_type.c b/gsplus/src/dyna_type.c new file mode 100644 index 0000000..517fd96 --- /dev/null +++ b/gsplus/src/dyna_type.c @@ -0,0 +1,330 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2021-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +// Provide routines for Dynapro to use for detecting the file type on the +// host system. Host files can be "basic1.bas", "basic2,tbas,a$801" + +STRUCT(Dynatype_extensions) { + char str[16]; + word16 file_type; + word16 aux_type; +}; + +Dynatype_extensions g_dynatype_extensions[] = { +{ "applesingle", 0xfff, 0xffff }, +{ "txt", 0x04, 0 }, +{ "c", 0x04, 0 }, // ,ttxt +{ "s", 0x04, 0 }, // ,ttxt +{ "h", 0x04, 0 }, // ,ttxt +{ "bin", 0x06, 0x2000 }, // ,tbin +{ "bas", 0xfc, 0x0801 }, // ,tbas +{ "system", 0xff, 0x2000 }, // ,tsys +//{ "shr", 0xc0, 0x0002 }, // ,t$c0 +{ "shk", 0xe0, 0x8002 }, // ,t$e0 +{ "sdk", 0xe0, 0x8002 }, // ,t$e0 +{ "", 0, 0 } +}; + +STRUCT(Dynatype_types) { + char str[16]; + word16 file_type; + word16 aux_type; +}; + +Dynatype_types g_dynatype_types[] = { +{ "non", 0x00, 0 }, +{ "bad", 0x01, 0 }, +{ "txt", 0x04, 0 }, +{ "bin", 0x06, 0x2000 }, +{ "pnt", 0xc0, 0x0002 }, +{ "fnd", 0xc9, 0 }, +{ "icn", 0xca, 0 }, +{ "cmd", 0xf0, 0 }, +{ "bas", 0xfc, 0x0801 }, +{ "sys", 0xff, 0x2000 }, +{ "", 0, 0 } +}; + +word32 +dynatype_scan_extensions(const char *str) +{ + int len; + int i; + + len = (int)(sizeof(g_dynatype_extensions) / + sizeof(g_dynatype_extensions[0])); + for(i = 0; i < len; i++) { + if(cfgcasecmp(str, g_dynatype_extensions[i].str) == 0) { + return (g_dynatype_extensions[i].file_type << 16) | + g_dynatype_extensions[i].aux_type | + 0x1000000; + } + } + + return 0; +} + +word32 +dynatype_find_prodos_type(const char *str) +{ + word32 file_type; + int len; + int i; + + len = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0])); + for(i = 0; i < len; i++) { + if(cfgcasecmp(str, g_dynatype_types[i].str) == 0) { + file_type = g_dynatype_types[i].file_type; + return (file_type << 16) | g_dynatype_types[i].aux_type; + } + } + + return 0; +} + +const char * +dynatype_find_file_type(word32 file_type) +{ + int len; + int i; + + len = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0])); + for(i = 0; i < len; i++) { + if(g_dynatype_types[i].file_type == file_type) { + return g_dynatype_types[i].str; + } + } + + return 0; +} + +word32 +dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr, + word32 storage_type) +{ + char ext_buf[32]; + const char *str; + char *endstr; + word32 file_type, aux_type, type_or_aux; + int len, this_len, c, pos; + + // Look for ,tbas and ,a$2000 to get filetype and aux_type info + + str = cfg_str_basename(path_ptr); + len = (int)strlen(str); + + // Look for .ext and ,tbas, etc. + pos = 0; + ext_buf[0] = 0; + file_type = 0x06; // Default to BIN + aux_type = 0; + while(pos < len) { + c = str[pos++]; + if(c == '.') { + this_len = dynatype_get_extension(&str[pos], + &ext_buf[0], 30); + pos += this_len; + continue; + } else if(c == ',') { + this_len = dynatype_comma_arg(&str[pos], &type_or_aux); + if(type_or_aux & 0x1000000) { + file_type = type_or_aux; + } else if(type_or_aux & 0x2000000) { + aux_type = type_or_aux; + } else { + printf("Unknown , extension, %s ignored\n", + &str[pos]); + } + pos += this_len; + continue; + } else if(c == '#') { + // Cadius style encoding: #ff2000 is type=$ff, aux=$2000 + type_or_aux = strtol(&str[pos], &endstr, 16); + file_type = (type_or_aux & 0xffffff) | 0x1000000; + aux_type = 0; + pos += (int)(endstr - str); + continue; + } + } + + // Handle extensions and type. First do extension mapping + if(ext_buf[0]) { + type_or_aux = dynatype_scan_extensions(&ext_buf[0]); + if((type_or_aux) >= 0x0f000000UL) { + // AppleSingle + storage_type = 0x50; // Forked file + } + if(file_type < 0x1000000) { + file_type = type_or_aux; + } + if(aux_type < 0x1000000) { + aux_type = type_or_aux; + } + } +#if 0 + printf("After parsing ext, file_type:%08x, aux_type:%08x\n", + file_type, aux_type); +#endif + + fileptr->file_type = (file_type >> 16) & 0xff; + if(aux_type == 0) { + aux_type = file_type & 0xffff; + } + fileptr->aux_type = aux_type & 0xffff; + + return storage_type; +} + +int +dynatype_get_extension(const char *str, char *out_ptr, int buf_len) +{ + int c, len; + + // Will write up to buf_len chars to out_ptr + if(buf_len < 1) { + return 0; + } + buf_len--; + len = 0; + while(1) { + c = *str++; + *out_ptr = c; + if((c == 0) || (c == '.') || (c == ',') || (c == '#') || + (len >= buf_len)) { + *out_ptr = 0; + return len; + } + out_ptr++; + len++; + } +} + +int +dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr) +{ + char type_buf[8]; + char *endstr; + word32 val, type_or_aux; + int c, len, base, this_len; + int i; + + // Read next char + *type_or_aux_ptr = 0; + + c = *str++; + if(c == 0) { + return 0; + } + len = 1; + c = tolower(c); + type_or_aux = c; + // See if next char is $ for hex + c = *str; + base = 0; + if(c == '$') { + base = 16; + str++; + len++; + } + val = strtol(str, &endstr, base); + this_len = (int)(endstr - str); + if((val == 0) && (this_len < 2) && (base == 0) && + (type_or_aux == 't')) { + // Not a valid number + for(i = 0; i < 3; i++) { + c = *str++; + if(c == 0) { + return len; + } + type_buf[i] = c; + len++; + } + type_buf[3] = 0; + val = dynatype_find_prodos_type(&type_buf[0]); + *type_or_aux_ptr = 0x1000000 | val; + } else { + len += this_len; + } + if(type_or_aux == 't') { + if(val < 0x100) { + *type_or_aux_ptr = 0x1000000 | ((val << 16) & 0xffffff); + } + } else if(type_or_aux == 'a') { + *type_or_aux_ptr = 0x2000000 | (val & 0xffff); + } + + return len; +} + +void +dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max) +{ + char buf[16]; + Dynapro_file tmpfile; + const char *str; + word32 aux_type; + +#if 0 + printf("Looking at %s ftype:%02x aux:%04x\n", outbuf_ptr, + fileptr->file_type, fileptr->aux_type); +#endif + + if(fileptr->prodos_name[0] >= 0xd0) { + return; // Directory, or Dir/Volume Header + } + if((fileptr->prodos_name[0] & 0xf0) == 0x50) { + // Forked file, add .applesingle + cfg_strlcat(outbuf_ptr, ".applesingle", path_max); + return; + } + + memset(&tmpfile, 0, sizeof(Dynapro_file)); + + // See what this file defaults to as to type/aux + (void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10); + + // Otherwise, add ,ttype and ,a$aux as needed + if(tmpfile.file_type != fileptr->file_type) { + str = dynatype_find_file_type(fileptr->file_type); + if(str) { + aux_type = dynatype_find_prodos_type(str); + } else { + str = &buf[0]; + buf[15] = 0; + snprintf(&buf[0], 15, "$%02x", fileptr->file_type); + } + cfg_strlcat(outbuf_ptr, ",t", path_max); + cfg_strlcat(outbuf_ptr, str, path_max); + } + + (void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10); + aux_type = fileptr->aux_type; + + if(aux_type != tmpfile.aux_type) { + buf[15] = 0; + snprintf(&buf[0], 15, ",a$%04x", aux_type & 0xffff); + cfg_strlcat(outbuf_ptr, &buf[0], path_max); + } + + // printf("dynatype_new_unix_name: %s\n", outbuf_ptr); + + // Check that it succeeded + (void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10); + if((tmpfile.file_type != fileptr->file_type) || + (tmpfile.aux_type != fileptr->aux_type)) { + halt_printf("File %s want ftype:%02x aux:%04x, got:%02x %04x\n", + outbuf_ptr, fileptr->file_type, fileptr->aux_type, + tmpfile.file_type, tmpfile.aux_type); + exit(1); + } +} diff --git a/gsplus/src/dyna_validate.c b/gsplus/src/dyna_validate.c new file mode 100644 index 0000000..2cbf303 --- /dev/null +++ b/gsplus/src/dyna_validate.c @@ -0,0 +1,460 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2021-2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Main information is from Beneath Apple ProDOS which has disk layout +// descriptions. Forked files are described in Technote tn-pdos-025. + +#include "defc.h" + +word32 +dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte, + word32 parent_dir_byte) +{ + word32 storage_type, exp_type, val, parent_block, exp_val; + + storage_type = fileptr->prodos_name[0] & 0xf0; + exp_type = 0xe0; + if(dir_byte == 0x0404) { + exp_type = 0xf0; // Volume header + } + if(storage_type != exp_type) { + printf("Volume/Dir header is %02x at %07x\n", + storage_type, dir_byte); + return 0; + } + + if(fileptr->aux_type != 0x0d27) { + printf("entry_length, entries_per_block:%04x at %07x\n", + fileptr->aux_type, dir_byte); + return 0; + } + + if(exp_type == 0xf0) { // Volume header + val = fileptr->lastmod_time >> 16; + if(val != 6) { + printf("bit_map_ptr:%04x, should be 6\n", val); + return 0; + } + val = fileptr->header_pointer; + if(val != (dsk->dimage_size >> 9)) { + printf("Num blocks at %07x is wrong: %04x\n", dir_byte, + val); + return 0; + } + } else { // Directory header + val = fileptr->lastmod_time >> 16; // parent_pointer + parent_block = parent_dir_byte >> 9; + if(val != parent_block) { + printf("Dir at %07x parent:%04x should be %04x\n", + dir_byte, val, parent_block); + return 0; + } + val = fileptr->header_pointer; + exp_val = ((parent_dir_byte & 0x1ff) - 4) / 0x27; + exp_val = (exp_val + 1) | 0x2700; + if(val != exp_val) { + printf("Parent entry at %07x is:%04x, should be:%04x\n", + dir_byte, val, exp_val); + return 0; + } + } + + return 1; +} + +void +dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks) +{ + word32 num_map_blocks, mask; + int pos; + word32 ui; + + for(ui = 0; ui < (num_blocks + 7)/8; ui++) { + freeblks_ptr[ui] = 0xff; + } + freeblks_ptr[0] &= 0x3f; + if(num_blocks & 7) { + freeblks_ptr[num_blocks / 8] = 0xff00 >> (num_blocks & 7); + } + + num_map_blocks = (num_blocks + 4095) >> 12; // 4096 bits per block + for(ui = 0; ui < num_map_blocks; ui++) { + // Mark blocks used in the bitmap as in use + pos = (ui + 6) >> 3; + mask = 0x80 >> ((ui + 6) & 7); + freeblks_ptr[pos] &= (~mask); + } +} + +word32 +dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block) +{ + word32 mask, ret; + int pos; + + // Return != 0 if block is free (which is success), returns == 0 + // if it is in use (which is an error). Marks block as in use + pos = block >> 3; + if(block >= (dsk->dimage_size >> 9)) { + return 0x100; // Out of range + } + mask = 0x80 >> (block & 7); + ret = freeblks_ptr[pos] & mask; + freeblks_ptr[pos] &= (~mask); + + if(!ret) { + printf("Block %04x was already in use\n", block); + } + return ret; +} + +word32 +dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, + word32 eof, int level_first) +{ + byte *bptr; + word32 num_blocks, tmp, ret, exp_blocks, extra_blocks; + int level, first; + int i; + + level = level_first & 0xf; + first = level_first & 0x10; + + if(!dynapro_validate_freeblk(dsk, freeblks_ptr, block_num)) { + return 0; + } + if(level_first == 0x15) { + return dynapro_validate_forked_file(dsk, freeblks_ptr, + block_num, eof); + } + if((level < 1) || (level >= 4)) { + printf("level %d out of range, %08x\n", level, level_first); + return 0; + } + if(level == 1) { + return 1; + } + num_blocks = 1; + bptr = &(dsk->raw_data[block_num * 0x200]); + for(i = 0; i < 256; i++) { + tmp = bptr[i] + (bptr[256 + i] << 8); + if(tmp == 0) { + if(first) { + printf("First block is spare, illegal!\n"); + return 0; + } + continue; + } + ret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof, + first | (level - 1)); + if(ret == 0) { + return 0; + } + num_blocks += ret; + first = 0; + } + + if(level_first & 0x10) { + // Try to estimate exp_blocks based on eof + exp_blocks = (eof + 0x1ff) >> 9; + if(exp_blocks == 0) { + exp_blocks = 1; + } else if(exp_blocks > 1) { + // Add in sapling blocks + extra_blocks = ((exp_blocks + 255) >> 8); + if(exp_blocks > 256) { + extra_blocks++; + } + exp_blocks += extra_blocks; + } + if(num_blocks > exp_blocks) { + printf("blocks_used:%04x, eof:%07x, exp:%04x\n", + num_blocks, eof, exp_blocks); + return 0; + } + } + return num_blocks; +} + +word32 +dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, + word32 eof) +{ + byte *bptr; + word32 num_blocks, tmp, ret, size, type, exp_blocks; + int level; + int i; + + bptr = &(dsk->raw_data[block_num * 0x200]); + + if(eof != 0x200) { + printf("In forked file block %04x, eof in dir:%08x, exp 0200\n", + block_num, eof); + return 0; + } + // Check that most of the block is 0 + for(i = 44; i < 512; i++) { + if((i >= 0x100) && (i < 0x108)) { + continue; + } + if(bptr[i] != 0) { + printf("In forked file block:%04x, byte %03x is %02x\n", + block_num, i, bptr[i]); + return 0; + } + } + + // Check for basic Finder Info format + for(i = 0; i < 2; i++) { + size = bptr[8 + 18*i]; + type = bptr[9 + 18*i]; + if(((size != 0) && (size != 18)) || (type > 2)) { + printf("Finder Info size %04x+%03x=%02x, type:%02x\n", + block_num, 8 + 18*i, size, type); + return 0; + } + } + + num_blocks = 1; + for(i = 0; i < 2; i++) { + tmp = bptr[1 + 0x100*i] + (bptr[2 + 0x100*i] << 8); + if(tmp == 0) { + printf("First fork %d block is spare, illegal!\n", i); + return 0; + } + eof = bptr[5 + 0x100*i] + (bptr[6 + 0x100*i] << 8) + + (bptr[7 + 0x100*i] << 16); + level = bptr[0 + 0x100*i]; + ret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof, + 0x10 | level); + if(ret == 0) { + printf("Fork %d failed, eof:%08x, block:%04x " + "fork:%04x, level:%d\n", i, eof, block_num, + tmp, level); + return 0; + } + exp_blocks = bptr[3 + 0x100*i] + (bptr[4 + 0x100*i] << 8); + if(ret != exp_blocks) { + printf("Fork %d at %04x, blocks:%04x, exp:%04x\n", + i, block_num, ret, exp_blocks); + } + num_blocks += ret; + } + + return num_blocks; +} + +word32 +dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte, + word32 parent_dir_byte, word32 exp_blocks_used) +{ + char buf32[32]; + Dynapro_file localfile; + byte *bptr; + word32 start_dir_block, last_block, max_block, tmp_byte, sub_blocks; + word32 ret, act_entries, exp_entries, blocks_used, prev, next; + int cnt, is_header; + + // Read directory, make sure each entry is consistent + // Return 0 if there is damage, != 0 if OK. + bptr = dsk->raw_data; + start_dir_block = dir_byte >> 9; + last_block = 0; + max_block = (word32)(dsk->dimage_size >> 9); + cnt = 0; + is_header = 1; + exp_entries = 0xdeadbeef; + act_entries = 0; + blocks_used = 0; + while(dir_byte) { + if((dir_byte & 0x1ff) == 4) { + // First entry in this block, check prev/next + tmp_byte = dir_byte & -0x200; // Block align + prev = dynapro_get_word16(&bptr[tmp_byte + 0]); + next = dynapro_get_word16(&bptr[tmp_byte + 2]); + if((prev != last_block) || (next >= max_block)) { + printf("dir at %07x is damaged in links\n", + dir_byte); + return 0; + } + last_block = dir_byte >> 9; + ret = dynapro_validate_freeblk(dsk, freeblks_ptr, + dir_byte >> 9); + if(!ret) { + return 0; + } + blocks_used++; + } + if(cnt++ >= 65536) { + printf("Loop detected, dir_byte:%07x\n", dir_byte); + return 0; + } + ret = dynapro_fill_fileptr_from_prodos(dsk, &localfile, + &buf32[0], dir_byte); + if(ret == 0) { + return 0; + } + if(ret != 1) { + act_entries = act_entries + 1 - is_header; + } + if(is_header) { + if(ret == 1) { + printf("Volume/Dir header is erased\n"); + return 0; + } + ret = dynapro_validate_header(dsk, &localfile, dir_byte, + parent_dir_byte); + if(ret == 0) { + return 0; + } + exp_entries = localfile.lastmod_time & 0xffff; + } else if(ret != 1) { + if(localfile.header_pointer != start_dir_block) { + printf("At %07x, header_ptr:%04x != %04x\n", + dir_byte, localfile.header_pointer, + start_dir_block); + return 0; + } + if(localfile.prodos_name[0] >= 0xd0) { + sub_blocks = localfile.blocks_used; + if(localfile.eof != (sub_blocks * 0x200UL)) { + printf("At %07x, eof:%08x != %08x\n", + dir_byte, localfile.eof, + sub_blocks * 0x200U); + return 0; + } + ret = dynapro_validate_dir(dsk, freeblks_ptr, + (localfile.key_block * 0x200) + 4, + dir_byte, sub_blocks); + if(ret == 0) { + return 0; + } + } else { + ret = dynapro_validate_file(dsk, freeblks_ptr, + localfile.key_block, localfile.eof, + 0x10 | (localfile.prodos_name[0] >> 4)); + if(ret == 0) { + printf("At %07x, bad file\n", dir_byte); + return 0; + } + if(localfile.blocks_used != ret) { + printf("At %07x, blocks_used prodos " + "%04x != %04x calc\n", dir_byte, + localfile.blocks_used, ret); + return 0; + } + } + } + + is_header = 0; + dir_byte = dir_byte + 0x27; + tmp_byte = (dir_byte & 0x1ff) + 0x27; + if(tmp_byte < 0x200) { + continue; + } + + tmp_byte = (dir_byte - 0x27) & (0 - 0x200UL); + dir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL; + if(dir_byte == 0) { + if(act_entries != exp_entries) { + printf("act_entries:%04x != exp:%04x, " + "dir_block:%04x\n", act_entries, + exp_entries, start_dir_block); + return 0; + } + if(blocks_used != exp_blocks_used) { + printf("At dir %07x, blocks_used:%04x!=%04x " + "exp\n", tmp_byte, blocks_used, + exp_blocks_used); + return 0; + } + return 1; + } + dir_byte += 4; + if(dir_byte >= (max_block * 0x200L)) { + printf(" invalid link pointer %07x\n", dir_byte); + return 0; + } + } + + return 0; +} + +int +dynapro_validate_disk(Disk *dsk) +{ + byte freeblks[65536/8]; // 8KB + byte *bptr; + word32 num_blocks, ret; + word32 ui; + + num_blocks = (word32)(dsk->dimage_size >> 9); + printf("******************************\n"); + printf("Validate disk: %s, blocks:%05x\n", dsk->name_ptr, num_blocks); + dynapro_validate_init_freeblks(&freeblks[0], num_blocks); + + // Validate starting at directory in block 2 + ret = dynapro_validate_dir(dsk, &freeblks[0], 0x0404, 0, 4); + if(!ret) { + printf("Disk does not validate!\n"); + exit(1); + return ret; + } + + // Check freeblks + bptr = &(dsk->raw_data[6*0x200]); + for(ui = 0; ui < (num_blocks + 7)/8; ui++) { + if(freeblks[ui] != bptr[ui]) { + printf("Expected free mask for blocks %04x-%04x:%02x, " + "but it is %02x\n", ui*8, ui*8 + 7, + freeblks[ui], bptr[ui]); + exit(1); + return 0; + } + } + return 1; +} + +void +dynapro_validate_any_image(Disk *dsk) +{ + byte *bufptr; + dword64 dsize; + int ret; + + // If dsk->raw_data already set, just use it. Otherwise, we need to + // temporarily read in entire image, set it, do validate, and then + // free it + + if(dsk->fd < 0) { + return; // No disk + } + if(dsk->wozinfo_ptr) { + return; + } + dsize = dsk->dimage_size; + bufptr = 0; + if((dsize >> 31) != 0) { + printf("Disk is too large, not valid\n"); + ret = 0; + } else if(dsk->raw_data == 0) { + bufptr = malloc((size_t)dsize); + dsk->raw_data = bufptr; + cfg_read_from_fd(dsk->fd, bufptr, 0, dsize); + ret = dynapro_validate_disk(dsk); + dsk->raw_data = 0; + free(bufptr); + } else { + ret = dynapro_validate_disk(dsk); + } + printf("validate_disk returned is_good: %d (0 is bad)\n", ret); +} + diff --git a/gsplus/src/dynapro.c b/gsplus/src/dynapro.c new file mode 100644 index 0000000..fe24a78 --- /dev/null +++ b/gsplus/src/dynapro.c @@ -0,0 +1,2303 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2021-2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Main information is from Beneath Apple ProDOS which has disk layout +// descriptions. Upper/lowercase is from Technote tn-gsos-008, and +// forked files are storage_type $5 from Technote tn-pdos-025. + +#include "defc.h" +#ifdef _WIN32 +# include "win_dirent.h" +#else +# include +#endif +#include + +extern int Verbose; + +#define DYNAPRO_PATH_MAX 2048 +char g_dynapro_path_buf[DYNAPRO_PATH_MAX]; + +extern word32 g_vbl_count, g_iwm_dynapro_last_vbl_count; + +byte g_prodos_block0[512] = { + // From Beagle Bros Pro-Byter disk + // This is a ProDOS boot block, able to load PRODOS and jump to it + 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x32, 0xa1, 0x86, // 0x000 + 0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70, 0x4a, + 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0, // 0x010 + 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0, + 0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08, // 0x020 + 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, 0x48, + 0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x63, 0xb1, // 0x030 + 0x48, 0x99, 0x94, 0x09, 0xc8, 0xc0, 0xeb, 0xd0, + 0xf6, 0xa2, 0x06, 0xbc, 0x1d, 0x09, 0xbd, 0x24, // 0x040 + 0x09, 0x99, 0xf2, 0x09, 0xbd, 0x2b, 0x09, 0x9d, + 0x7f, 0x0a, 0xca, 0x10, 0xee, 0xa9, 0x09, 0x85, // 0x050 + 0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9, 0xb0, + 0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84, // 0x060 + 0x4c, 0x84, 0x4e, 0x84, 0x47, 0xc8, 0x84, 0x42, + 0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, 0x85, // 0x070 + 0x4b, 0x20, 0x12, 0x09, 0xb0, 0x68, 0xe6, 0x61, + 0xe6, 0x61, 0xe6, 0x46, 0xa5, 0x46, 0xc9, 0x06, // 0x080 + 0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01, 0x0c, + 0xd0, 0x6d, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a, // 0x090 + 0x18, 0x6d, 0x23, 0x0c, 0xa8, 0x90, 0x0d, 0xe6, + 0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, 0x0a, // 0x0a0 + 0xf0, 0x55, 0xa0, 0x04, 0x84, 0x4a, 0xad, 0x02, + 0x09, 0x29, 0x0f, 0xa8, 0xb1, 0x4a, 0xd9, 0x02, // 0x0b0 + 0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0x29, 0xf0, + 0xc9, 0x20, 0xd0, 0x3b, 0xa0, 0x10, 0xb1, 0x4a, // 0x0c0 + 0xc9, 0xff, 0xd0, 0x33, 0xc8, 0xb1, 0x4a, 0x85, + 0x46, 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00, // 0x0d0 + 0x85, 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61, + 0xc8, 0x84, 0x4d, 0x20, 0x12, 0x09, 0xb0, 0x17, // 0x0e0 + 0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e, + 0xb1, 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47, // 0x0f0 + 0x11, 0x4a, 0xd0, 0xe7, 0x4c, 0x00, 0x20, 0x4c, + + 0x3f, 0x09, 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f, // 0x100 + 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa5, 0x60, 0x85, 0x44, 0xa5, 0x61, // 0x110 + 0x85, 0x45, 0x6c, 0x48, 0x00, 0x08, 0x1e, 0x24, + 0x3f, 0x45, 0x47, 0x76, 0xf4, 0xd7, 0xd1, 0xb6, // 0x120 + 0x4b, 0xb4, 0xac, 0xa6, 0x2b, 0x18, 0x60, 0x4c, + 0xbc, 0x09, 0xa9, 0x9f, 0x48, 0xa9, 0xff, 0x48, // 0x130 + 0xa9, 0x01, 0xa2, 0x00, 0x4c, 0x79, 0xf4, 0x20, + 0x58, 0xfc, 0xa0, 0x1c, 0xb9, 0x50, 0x09, 0x99, // 0x140 + 0xae, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x4d, 0x09, + 0xaa, 0xaa, 0xaa, 0xa0, 0xd5, 0xce, 0xc1, 0xc2, // 0x150 + 0xcc, 0xc5, 0xa0, 0xd4, 0xcf, 0xa0, 0xcc, 0xcf, + 0xc1, 0xc4, 0xa0, 0xd0, 0xd2, 0xcf, 0xc4, 0xcf, // 0x160 + 0xd3, 0xa0, 0xaa, 0xaa, 0xaa, 0xa5, 0x53, 0x29, + 0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, 0x80, 0xc0, // 0x170 + 0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9, + 0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46, // 0x180 + 0x29, 0x07, 0xc9, 0x04, 0x29, 0x03, 0x08, 0x0a, + 0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5, // 0x190 + 0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85, + 0x51, 0xa5, 0x45, 0x85, 0x27, 0xa6, 0x2b, 0xbd, // 0x1a0 + 0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6, + 0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09, // 0x1b0 + 0xbc, 0x88, 0xc0, 0x60, 0xa5, 0x40, 0x0a, 0x85, + 0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85, // 0x1c0 + 0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04, + 0xe6, 0x53, 0x90, 0x02, 0xc6, 0x53, 0x38, 0x20, // 0x1d0 + 0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09, + 0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28, // 0x1e0 + 0x38, 0xc6, 0x52, 0xf0, 0xce, 0x18, 0x08, 0x88, + 0xf0, 0xf5, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x00, // 0x1f0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +word32 +dynapro_get_word32(byte *bptr) +{ + return (bptr[3] << 24) | (bptr[2] << 16) | (bptr[1] << 8) | bptr[0]; +} + +word32 +dynapro_get_word24(byte *bptr) +{ + return (bptr[2] << 16) | (bptr[1] << 8) | bptr[0]; +} + +word32 +dynapro_get_word16(byte *bptr) +{ + return (bptr[1] << 8) | bptr[0]; +} + +void +dynapro_set_word24(byte *bptr, word32 val) +{ + // Write 3 bytes in little-endian form + *bptr++ = val; + *bptr++ = (val >> 8); + *bptr++ = (val >> 16); +} + +void +dynapro_set_word32(byte *bptr, word32 val) +{ + // Write 4 bytes in little-endian form + *bptr++ = val; + *bptr++ = (val >> 8); + *bptr++ = (val >> 16); + *bptr++ = (val >> 24); +} + +void +dynapro_set_word16(byte *bptr, word32 val) +{ + // Write 2 bytes in little-endian form + *bptr++ = val; + *bptr++ = (val >> 8); +} + +void +dynapro_error(Disk *dsk, const char *fmt, ...) +{ + Dynapro_info *info_ptr; + va_list args; + + va_start(args, fmt); + cfg_err_vprintf("Dynapro", fmt, args); + va_end(args); + info_ptr = dsk->dynapro_info_ptr; + if(info_ptr) { + cfg_err_printf("", "Path: %s\n", info_ptr->root_path); + } +} + +Dynapro_file * +dynapro_alloc_file() +{ + Dynapro_file *fileptr; + + fileptr = calloc(1, sizeof(Dynapro_file)); + return fileptr; +} + +void +dynapro_free_file(Dynapro_file *fileptr, int check_map) +{ + if(!fileptr) { + return; + } + if(fileptr->subdir_ptr) { + dynapro_free_recursive_file(fileptr->subdir_ptr, check_map); + } + fileptr->subdir_ptr = 0; + free(fileptr->unix_path); + fileptr->unix_path = 0; + free(fileptr->buffer_ptr); + fileptr->buffer_ptr = 0; + fileptr->next_ptr = 0; + // printf("FREE %p\n", fileptr); + if(check_map && (fileptr->map_first_block != 0)) { + printf(" ERROR: map_first_block is %08x\n", + fileptr->map_first_block); + exit(1); + } + free(fileptr); +} + +void +dynapro_free_recursive_file(Dynapro_file *fileptr, int check_map) +{ + Dynapro_file *nextptr; + + if(!fileptr) { + return; + } + // printf("free_recursive %s\n", fileptr->unix_path); + while(fileptr) { + nextptr = fileptr->next_ptr; + dynapro_free_file(fileptr, check_map); + fileptr = nextptr; + }; +} + +void +dynapro_free_dynapro_info(Disk *dsk) +{ + Dynapro_info *info_ptr; + + info_ptr = dsk->dynapro_info_ptr; + if(info_ptr) { + free(info_ptr->root_path); + + dynapro_free_recursive_file(info_ptr->volume_ptr, 0); + info_ptr->volume_ptr = 0; + } + free(info_ptr); + dsk->dynapro_info_ptr = 0; +} + +word32 +dynapro_find_free_block_internal(Disk *dsk) +{ + byte *bptr; + word32 num_blocks, bitmap_size_bytes, val, mask; + word32 ui; + int j; + + num_blocks = (word32)(dsk->raw_dsize >> 9); + bitmap_size_bytes = (num_blocks + 7) >> 3; + bptr = &(dsk->raw_data[6 * 512]); // Block 6 + for(ui = 0; ui < bitmap_size_bytes; ui++) { + val = bptr[ui]; + if(val == 0) { + continue; + } + mask = 0x80; + for(j = 0; j < 8; j++) { + if(val & mask) { + bptr[ui] = val & (~mask); + return 8*ui + j; + } + mask = mask >> 1; + } + return 0; + } + return 0; +} + +word32 +dynapro_find_free_block(Disk *dsk) +{ + byte *bptr; + word32 block; + int i; + + // Find first free block, and zero it out + + block = dynapro_find_free_block_internal(dsk); + if(block == 0) { + return 0; + } + bptr = &(dsk->raw_data[block * 512]); + for(i = 0; i < 512; i++) { + bptr[i] = 0; + } + + return block; +} + +byte * +dynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size) +{ + byte *bptr; + dword64 dsize, dpos; + int fd; + int i; + + *dsize_ptr = 0; + fd = open(path_ptr, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + return 0; + } + dsize = cfg_get_fd_size(fd); + + if((size_t)(dsize + extra_size) != (dsize + extra_size)) { + return 0; + } + bptr = malloc((size_t)(dsize + extra_size)); + if(bptr == 0) { + return bptr; + } + // printf("dynapro_malloc_file %p, size:%08lld\n", bptr, dsize); + for(i = 0; i < extra_size; i++) { + bptr[dsize + i] = 0; + } + dpos = cfg_read_from_fd(fd, bptr, 0, dsize); + close(fd); + if(dpos != dsize) { + free(bptr); + return 0; + } + *dsize_ptr = dsize; + return bptr; +} + +void +dynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str, + int path_max) +{ + int len; + + // Create "unix_path" + "/" + "str" in outstr (which has size path_max) + cfg_strncpy(outstr, unix_path, path_max); + len = (int)strlen(outstr); + if((len > 0) && (outstr[len - 1] != '/')) { + cfg_strlcat(outstr, "/", path_max); + } + cfg_strlcat(outstr, str, path_max); +} + + +word32 +dynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr, + char *buf32_ptr, word32 dir_byte) +{ + byte *bptr; + word32 upper_lower; + int len, c; + int i; + + buf32_ptr[0] = 0; + if((dir_byte < 0x400) || (dir_byte >= dsk->dimage_size)) { + return 0; // Directory is damaged + } + if(!fileptr) { + return 0; + } + bptr = &(dsk->raw_data[dir_byte]); + memset(fileptr, 0, sizeof(Dynapro_file)); + + fileptr->dir_byte = dir_byte; + fileptr->file_type = bptr[0x10]; + fileptr->key_block = dynapro_get_word16(&bptr[0x11]); + fileptr->blocks_used = dynapro_get_word16(&bptr[0x13]); + fileptr->eof = dynapro_get_word24(&bptr[0x15]); + //printf("Filling from entry %07x, eof:%06x\n", dir_byte, fileptr->eof); + fileptr->creation_time = dynapro_get_word32(&bptr[0x18]); + fileptr->upper_lower = dynapro_get_word16(&bptr[0x1c]); + fileptr->aux_type = dynapro_get_word16(&bptr[0x1f]); + fileptr->lastmod_time = dynapro_get_word32(&bptr[0x21]); + fileptr->header_pointer = dynapro_get_word16(&bptr[0x25]); + if(dir_byte == 0x404) { // Volume header + fileptr->upper_lower = dynapro_get_word32(&bptr[0x1a]); + fileptr->creation_time &= 0xffff; + } + + len = (bptr[0] & 0xf) + 1; + upper_lower = fileptr->upper_lower; + if((upper_lower & 0x8000) == 0) { // Not valid + upper_lower = 0; + } + for(i = 0; i < 16; i++) { + c = bptr[i]; + if(i > len) { + c = 0; + } + fileptr->prodos_name[i] = c; + if(i > 0) { + if(upper_lower & 0x4000) { + if((c >= 'A') && (c <= 'Z')) { + c = c - 'A' + 'a'; // Make lower + } + } + upper_lower = upper_lower << 1; + buf32_ptr[i - 1] = c; + buf32_ptr[i] = 0; + } + } + if(((bptr[0] & 0xf0) == 0) || ((bptr[0] & 0xf) == 0)) { + fileptr->prodos_name[0] = 0; + return 1; // Invalid entry + } + if(fileptr->prodos_name[0] >= 0xe0) { // Dir/Volume header + fileptr->key_block = dir_byte >> 9; + if((dir_byte & 0x1ff) != 4) { + printf("Header at dir_byte:%07x != 4\n", dir_byte); + return 0; // Not in first pos + } + if(bptr[-4] || bptr[-3]) { + printf("prev_link %02x,%02x should be 0\n", + bptr[-4], bptr[-3]); + return 0; // Not first dir block + } + if(fileptr->prodos_name[0] >= 0xf0) { + if(dir_byte != 0x0404) { + printf("Volume head dir_byte:%07x\n", dir_byte); + return 0; + } + } else if(dir_byte == 0x0404) { + printf("Directory head dir_byte 0x0404\n"); + return 0; // 0xe0 in block 2->bad + } + } else { + // Normal entry. Make sure it's not the first entry in a dir + if((bptr[-4] == 0) && (bptr[-3] == 0) && + ((dir_byte & 0x1ff) == 4)) { + printf("dir_byte:%07x, normal, prev:0\n", dir_byte); + return 0; // This is a dir/volume header! + } + } +#if 0 + printf("Fill resulted in buf32:%s, upper_lower:%04x\n", buf32_ptr, + fileptr->upper_lower); +#endif + + return 2; // OK +} + +word32 +dynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr) +{ + word32 ret, new_storage, old_storage; + int i; + + // Return 0 if the directory is damaged + // Return 1 if the entry is invalid (and not case 3!) + // Return 3 if the entry was valid and is now deleted + // Return 4 if no changes are needed + // Return 5 if oldfileptr needs to be rewritten + // Return 7 if oldfileptr needs to be erased and replaced with newfile + + old_storage = oldfileptr->prodos_name[0]; + new_storage = newfileptr->prodos_name[0]; + if(new_storage == 0) { // Erased + if(old_storage >= 0xe0) { // Vol/Dir header + return 0; + } + if(oldfileptr->dir_byte == newfileptr->dir_byte) { + return 3; // Entry just deleted + } + return 1; // Just an invalid entry + } + if(oldfileptr->dir_byte != newfileptr->dir_byte) { + return 0; + } + ret = 4; // No changes needed + + // Handle file expanding from seedling to tree + if((new_storage >= 0x10) && (new_storage < 0x40) && + (old_storage >= 0x10) && (old_storage < 0x40)) { + // Copy upper 4 bits from new_storage to old_storage + old_storage = (old_storage & 0x0f) | (new_storage & 0xf0); + if(oldfileptr->prodos_name[0] != old_storage) { + // Storage type changed, rewrite the file + oldfileptr->prodos_name[0] = old_storage; + ret |= 5; + } + } + for(i = 0; i < 16; i++) { + if(oldfileptr->prodos_name[i] != newfileptr->prodos_name[i]) { + ret |= 7; // Name changed + } + oldfileptr->prodos_name[i] = newfileptr->prodos_name[i]; + } + + if(oldfileptr->file_type != newfileptr->file_type) { + ret |= 7; // Filetype changed + oldfileptr->file_type = newfileptr->file_type; + } + if(newfileptr->prodos_name[0] < 0xe0) { + // Not a directory or volume header + if(oldfileptr->key_block != newfileptr->key_block) { + ret |= 5; // Key block has changed + oldfileptr->key_block = newfileptr->key_block; + } + if(oldfileptr->blocks_used != newfileptr->blocks_used) { + // ret stays 1, we don't care about this field + oldfileptr->blocks_used = newfileptr->blocks_used; + } + if(oldfileptr->eof != newfileptr->eof) { + ret |= 5; // eof has changed + oldfileptr->eof = newfileptr->eof; + } + } else { + // Directory or volume header + // Ignore key_block (used internally by dynapro.c, but not in + // the ProDOS disk image), blocks_used, eof. + // We ignore file_count at +0x21,0x22. But bitmap_ptr matters + // and if it moves, we are damaged + if((oldfileptr->lastmod_time >> 16) != + (newfileptr->lastmod_time >> 16)) { + return 0; // Bitmap_ptr moved, we are damaged + } + } + if(oldfileptr->upper_lower != newfileptr->upper_lower) { + ret |= 7; // lowercase flags have changed + oldfileptr->upper_lower = newfileptr->upper_lower; + } + if(oldfileptr->aux_type != newfileptr->aux_type) { + ret |= 5; // aux_type has changed + oldfileptr->aux_type = newfileptr->aux_type; + } + if(oldfileptr->header_pointer != newfileptr->header_pointer) { + return 0; // We are damaged + } + if(newfileptr->prodos_name[0] >= 0xe0) { + if(ret > 5) { + ret = 5; // No renaming volume or dir headers + } + } + return ret; +} + +word32 +dynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr, + Dynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte) +{ + word32 ret, diffs; + + ret = dynapro_fill_fileptr_from_prodos(dsk, localfile_ptr, + buf32_ptr, dir_byte); + if((ret == 0) || ((ret == 1) && !fileptr)) { + return ret; // Damaged or not valid + } + if(!fileptr) { + return 2; // must allocate new + } + + // Now, head_ptr must be non-null + diffs = dynapro_diff_fileptrs(fileptr, localfile_ptr); + return diffs; +} + +void +dynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr) +{ + if(fileptr->prodos_name[0] >= 0xe0) { + // This is a volume/directory header. Re-parse entire dir + dynapro_handle_write_dir(dsk, fileptr->parent_ptr, fileptr, + (fileptr->key_block * 0x200UL) + 4); + } else if(fileptr->prodos_name[0] >= 0xd0) { + // This is a directory entry. + dynapro_handle_write_dir(dsk, fileptr, fileptr->subdir_ptr, + (fileptr->key_block * 0x200UL) + 4); + } else { + dynapro_handle_write_file(dsk, fileptr); + } +} + +void +dynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr) +{ + // Walk entire tree (recursing to dynapro_try_fix_damage) + if(!fileptr) { + return; + } + while(fileptr) { + if(fileptr->damaged) { + dyna_printf("try_fix_damage %p %s\n", fileptr, + fileptr->unix_path); + dynapro_fix_damaged_entry(dsk, fileptr); + } + dynapro_try_fix_damage(dsk, fileptr->subdir_ptr); + fileptr = fileptr->next_ptr; + } +} + +void +dynapro_try_fix_damaged_disk(Disk *dsk) +{ + Dynapro_info *info_ptr; + + info_ptr = dsk->dynapro_info_ptr; + if(!info_ptr) { // This is impossible + return; + } + if(info_ptr->damaged == 0) { + return; + } + + dyna_printf("************************************\n"); + dyna_printf("try_fix_damaged_dsk called, damaged:%d\n", + info_ptr->damaged); + dyna_printf(" vbl_count:%d, g_iwm_dynapro_last_vbl_count:%d\n", + g_vbl_count, g_iwm_dynapro_last_vbl_count); + + info_ptr->damaged = 0; + dynapro_try_fix_damage(dsk, info_ptr->volume_ptr); + + dyna_printf("try_fix_damaged_dsk, damaged:%d\n", info_ptr->damaged); +} + + +void +dynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str, + const char *name_str) +{ + if(fileptr->unix_path) { + free(fileptr->unix_path); + } + dynapro_join_path_and_file(&g_dynapro_path_buf[0], path_str, name_str, + DYNAPRO_PATH_MAX); + dynatype_fix_unix_name(fileptr, &g_dynapro_path_buf[0], + DYNAPRO_PATH_MAX); + fileptr->unix_path = kegs_malloc_str(&g_dynapro_path_buf[0]); +} + +Dynapro_file * +dynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr, + Dynapro_file **head_ptr_ptr, word32 dir_byte) +{ + char buf32[32]; + Dynapro_file localfile; + Dynapro_file *fileptr, *prev_ptr, *head_ptr; + byte *bptr; + const char *str; + word32 tmp_byte, prev, next, ret, last_block, parent_dir_byte; + int cnt, error, iret, is_header; + + head_ptr = *head_ptr_ptr; // head_ptr_ptr must be valid + // but head_ptr can be 0 + // We can be called with parent_ptr=0, head_ptr != 0: this is for the + // volume header. Otherwise, parent_ptr should be valid. + // If head_ptr==0, it means we need to allocate directory header and + // all other dir entries + // head_ptr is a pointer to a directory or volume header. + // For all entries, see if anything changed. We need to also + // possibly update head_ptr->parent_ptr + // Return 0 if the directory is damaged. If directory only contains + // damaged files, try to fix them, and always return success + + // First, unmap the directory blocks (this is done even if nothing + // changed, we'll map them back at the end). + if(head_ptr) { + dynapro_unmap_file(dsk, head_ptr); + } + + parent_dir_byte = 0; + if(parent_ptr) { + str = parent_ptr->unix_path; + parent_dir_byte = parent_ptr->dir_byte; + if(head_ptr == 0) { + // Do mkdir to make sure it exists +#ifdef _WIN32 + iret = _mkdir(str); +#else + iret = mkdir(str, 0x1ff); +#endif + error = errno; + dyna_printf("Did mkdir %s, iret:%d\n", str, iret); + if(iret < 0) { + if((error == EEXIST) || (error == EISDIR)) { + error = 0; // These are OK errors + } + if(error) { + printf("mkdir(%s) failed, error=%d\n", + str, error); + } + } + } + } else { + str = head_ptr->unix_path; // volume header + } +#if 0 + printf("process_write_dir str:%s %p parent:%p\n", str, head_ptr, + parent_ptr); +#endif + + // The directory blocks have already been unmapped + + // Then, walk the directory, noting if anything changed. If new + // files appear in the directory, add then to the chain. We may need + // to erase existing entries which no longer exist (or their directory + // entry was changed to a different file) + bptr = dsk->raw_data; + prev_ptr = 0; + fileptr = head_ptr; + last_block = 0; + cnt = 0; + is_header = 1; + while(dir_byte) { + //printf("process_write_dir, dir_byte:%07x, prev_ptr:%p\n", + // dir_byte, prev_ptr); + if((dir_byte & 0x1ff) == 4) { + // First entry in this block: check prev/next + tmp_byte = dir_byte & -0x200; // Block align + prev = dynapro_get_word16(&bptr[tmp_byte + 0]); + next = dynapro_get_word16(&bptr[tmp_byte + 2]); + if((prev != last_block) || + (next >= (dsk->raw_dsize >> 9))) { + // This is a damaged directory + printf("dir %s is damaged in the link fields\n", + str); + return 0; + } + last_block = dir_byte >> 9; + } + if(cnt++ >= 65536) { + printf("dir %s has a loop in block pointers\n", + head_ptr->unix_path); + return 0; + } + + ret = dynapro_do_one_dir_entry(dsk, fileptr, &localfile, + &buf32[0], dir_byte); +#if 0 + printf(" do_one_dir_entry ret:%08x fileptr:%p, &localfile:%p\n", + ret, fileptr, &localfile); +#endif + if((ret == 7) && !is_header) { // Entry dramatically changed + // Erase this file + dynapro_mark_damaged(dsk, fileptr); + } + if(ret == 0) { + return 0; + } else if((ret == 1) || (ret == 3)) { + if((ret == 3) && fileptr) { + // This entry was valid and is now deleted. + // Erase it right now and fix links + dyna_printf("fileptr %p deleted\n", fileptr); + prev_ptr->next_ptr = fileptr->next_ptr; + dynapro_erase_free_entry(dsk, fileptr); + } + if(head_ptr == 0) { + printf("return, head_ptr==0, deleted file at " + "%07x\n", dir_byte); + return 0; // Directory damaged + } + fileptr = prev_ptr; + } + if(ret == 2) { + // prev_ptr->next_ptr is 0, this is a new entry we + // need to put on the list + if(fileptr) { + halt_printf("file %s was ignored!\n", + fileptr->unix_path); + exit(1); + } + fileptr = dynapro_alloc_file(); + if(!fileptr) { + return 0; + } + *fileptr = localfile; // STRUCT copy! + dyna_printf("Allocated new fileptr:%p\n", fileptr); + fileptr->parent_ptr = parent_ptr; + if(head_ptr) { + fileptr->parent_ptr = head_ptr; + } + } + if((ret == 2) || (ret == 7)) { + // New entry, or dramatically changed, update path + if(!head_ptr) { + if(!parent_ptr) { + printf("parent_ptr is 0!\n"); + return 0; + } + parent_ptr->subdir_ptr = fileptr; + printf("2/7 set %p %s subdir=%p\n", parent_ptr, + parent_ptr->unix_path, fileptr); + fileptr->unix_path = kegs_malloc_str(str); + head_ptr = fileptr; + *head_ptr_ptr = head_ptr; + } else { + dyna_printf("Forming new path: %s buf32:%s\n", + str, buf32); + dynapro_new_unix_path(fileptr, str, buf32); + } + // If we are a directory entry (fileptr->subdir_ptr!=0) + // then now fileptr->unix_path != subdirptr->unix_path + // The subdir will be erased in + // dynapro_handle_changed_entry() + if(prev_ptr) { + prev_ptr->next_ptr = fileptr; + } + } + if((ret == 5) || (ret == 2) || (ret == 7)) { + // Changed, or new entry + if(is_header) { + ret = dynapro_validate_header(dsk, fileptr, + dir_byte, parent_dir_byte); + if(ret == 0) { + return 0; + } + } else { + dynapro_handle_changed_entry(dsk, fileptr); + } + } + prev_ptr = fileptr; + fileptr = prev_ptr->next_ptr; + dir_byte = dir_byte + 0x27; + tmp_byte = (dir_byte & 0x1ff) + 0x27; + is_header = 0; + if(tmp_byte < 0x200) { + continue; + } + tmp_byte = (dir_byte - 0x27) & (0 - 0x200UL); + dir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL; + dyna_printf(" dir link at %07x = %04x\n", tmp_byte + 2, + dir_byte); + if(dir_byte == 0) { + if(fileptr) { + printf("At dir end, fileptr: %p\n", fileptr); + prev_ptr->next_ptr = 0; + dynapro_erase_free_dir(dsk, fileptr); + return 0; + } + ret = dynapro_map_dir_blocks(dsk, head_ptr); + // printf("process_write_dir %s done, remap ret:%d\n", + // head_ptr->unix_path, ret); + if(ret == 0) { + return 0; + } + return head_ptr; // Success + } + dir_byte += 4; + if(dir_byte >= dsk->dimage_size) { + // printf(" invalid link pointer, dir_byte:%08x\n", + // dir_byte); + return 0; // Bad link, get out + } + } + dyna_printf("At end of process_write_dir, returning 0\n"); + return 0; +} + +void +dynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr, + Dynapro_file *head_ptr, word32 dir_byte) +{ + Dynapro_file *fileptr; + + //save_headptr = head_ptr; + dyna_printf("handle_write_dir parent_ptr:%p, head_ptr:%p, dir_b:%07x\n", + parent_ptr, head_ptr, dir_byte); + if(parent_ptr && head_ptr) { + dyna_printf(" parent:%s head:%s\n", parent_ptr->unix_path, + head_ptr->unix_path); + if(parent_ptr->subdir_ptr != head_ptr) { + printf("parent subdir:%p does not match %p\n", + parent_ptr->subdir_ptr, head_ptr); + exit(1); + } + } + if(parent_ptr == 0) { + if(head_ptr != dsk->dynapro_info_ptr->volume_ptr) { + printf("handle_write_dir %p %p %07x\n", parent_ptr, + head_ptr, dir_byte); + exit(1); + } + } + fileptr = dynapro_process_write_dir(dsk, parent_ptr, &head_ptr, + dir_byte); + if(fileptr == 0) { + // Directory is damaged. Free and erase it + dynapro_erase_free_dir(dsk, head_ptr); + head_ptr = 0; + } + //printf("handle_write_dir, process returned %p (was %p), parent:%p, " + // "save:%p\n", fileptr, head_ptr, parent_ptr, save_headptr); + if(parent_ptr) { + parent_ptr->subdir_ptr = head_ptr; + if(!fileptr) { + parent_ptr->damaged = 1; + dsk->dynapro_info_ptr->damaged = 1; + } + dyna_printf("hwd set parent %p %s subdir=%p\n", parent_ptr, + parent_ptr->unix_path, head_ptr); + } +} + +word32 +dynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr) +{ + word32 ret; + + //printf("dynapro_process_write_file %p %s\n", fileptr, + // fileptr->unix_path); + + if(fileptr->subdir_ptr) { + printf("dynapro_handle_write_file, has subdir: %s\n", + fileptr->unix_path); + halt_printf("dynapro_handle_write_file, has subdir: %s\n", + fileptr->unix_path); + return 0; + } + + // First, unmap the file (the sapling/tree blocks may have changed). + dynapro_unmap_file(dsk, fileptr); + + // Then remap the blocks. This will copy the new data to buffer_ptr + + dynapro_debug_map(dsk, "handle_write_file, right before re-map"); + + ret = dynapro_map_file(dsk, fileptr, 1); + + // printf("dynapro_handle_write_file ending, ret:%d\n", ret); + + return ret; +} + +void +dynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr) +{ + word32 ret; + + ret = dynapro_process_write_file(dsk, fileptr); + if(ret == 0) { + dynapro_mark_damaged(dsk, fileptr); + } +} + +void +dynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr) +{ + // printf("handle_changed_entry with fileptr:%p\n", fileptr); + fileptr->damaged = 0; + if(fileptr->prodos_name[0] >= 0xe0) { + // Directory header, not valid to be called here + fileptr->damaged = 1; + dsk->dynapro_info_ptr->damaged = 1; + } else if(fileptr->prodos_name[0] >= 0xd0) { + // Directory entry + if(fileptr->subdir_ptr) { + dynapro_erase_free_dir(dsk, fileptr->subdir_ptr); + fileptr->subdir_ptr = 0; + } + dynapro_handle_write_dir(dsk, fileptr, 0, + (fileptr->key_block * 0x200UL) + 4); + } else { + dynapro_handle_write_file(dsk, fileptr); +#if 0 + printf("handle_changed_entry called handle_write_file for %p, " + "%s\n", fileptr, fileptr->unix_path); +#endif + } +} + +word32 +dynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size) +{ + dword64 dret; + int fd; + + fd = open(unix_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6); + if(fd < 0) { + printf("Open %s for writing failed\n", unix_path); + exit(1); + return 0; + } + dret = cfg_write_to_fd(fd, data_ptr, 0, size); + close(fd); + dyna_printf("dynapro_write_to_unix: %s size:%d, dret:%lld\n", + unix_path, size, dret); + + + if(size == 0) { + return 1; + } + return (word32)dret; +} + +void +dynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr) +{ + Dynapro_file *this_fileptr; + Dynapro_map *map_ptr; + //const char *str; + word32 map_block, next_map_block, max_blocks; + int i; + + //printf("File %p: %s is unmapped\n", fileptr, fileptr->unix_path); + dynapro_debug_map(dsk, "start unmap file"); + + // Unmap all blocks to this file/dir + map_ptr = dsk->dynapro_info_ptr->block_map_ptr; + max_blocks = (word32)(dsk->dimage_size >> 9); + map_block = fileptr->map_first_block; + //printf(" map_block:%04x, fileptr:%p %s\n", map_block, fileptr, + // fileptr->unix_path); + fileptr->map_first_block = 0; + //printf(" unmap starting, map_block:%08x, max_blocks:%07x\n", + // map_block, max_blocks); + for(i = 0; i < 65536; i++) { + if((map_block == 0) || (map_block >= max_blocks)) { + break; + } + next_map_block = map_ptr[map_block].next_map_block; + this_fileptr = map_ptr[map_block].file_ptr; + if(this_fileptr != fileptr) { + //str = "??"; + if(this_fileptr) { + //str = this_fileptr->unix_path; + this_fileptr->damaged = 1; + } +#if 0 + printf("Found map[%04x]=%s while walking %s\n", + map_block, str, fileptr->unix_path); +#endif + } + map_ptr[map_block].file_ptr = 0; + map_ptr[map_block].next_map_block = 0; + map_ptr[map_block].modified = 0; + //printf(" just unmapped block %05x\n", map_block); + map_block = next_map_block; + } + + // printf(" unmap ending\n"); +} + +void +dynapro_unlink_file(Dynapro_file *fileptr) +{ + const char *str; + int ret, err; + + // Try to unlink unix_path + dyna_printf("Unlink %s (%p)\n", fileptr->unix_path, fileptr); + if(fileptr->unix_path == 0) { + printf("unix_path of %p is null!\n", fileptr); + exit(1); + } + if(fileptr->subdir_ptr != 0) { + printf("unlink_file %s, but subdirptr is valid!\n", + fileptr->unix_path); + exit(1); + } + + ret = unlink(fileptr->unix_path); + if(ret != 0) { + // Maybe it's a directory, rmdir + ret = rmdir(fileptr->unix_path); + } + if(ret != 0) { + // Cannot erase, try to rename + cfg_strncpy_dirname(&g_dynapro_path_buf[0], fileptr->unix_path, + DYNAPRO_PATH_MAX); + cfg_strlcat(&g_dynapro_path_buf[0], ".kegsrm_", + DYNAPRO_PATH_MAX); + str = cfg_str_basename(fileptr->unix_path); + cfg_strlcat(&g_dynapro_path_buf[0], str, DYNAPRO_PATH_MAX); + printf("Could not erase %s, renaming to: %s\n", + fileptr->unix_path, &g_dynapro_path_buf[0]); + ret = rename(fileptr->unix_path, &g_dynapro_path_buf[0]); + err = errno; + if(ret != 0) { + printf("Rename of %s failed, err:%d\n", + fileptr->unix_path, err); + } + } +} + +void +dynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr) +{ + if(!fileptr) { + return; + } + dynapro_mark_damaged(dsk, fileptr); + + fileptr->next_ptr = 0; + if(fileptr != dsk->dynapro_info_ptr->volume_ptr) { + // Free everything--except the volume header + dyna_printf("erase_free_entry erasing %p since it != %p\n", + fileptr, dsk->dynapro_info_ptr->volume_ptr); + dynapro_free_file(fileptr, 1); + } +} + +void +dynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr) +{ + Dynapro_file *nextptr, *parent_ptr, *save_fileptr; + + dyna_printf("dynapro_erase_free_dir of %p\n", fileptr); + if(fileptr == 0) { + return; + } + dyna_printf(" dynapro_erase_free_dir of %s\n", fileptr->unix_path); + dsk->dynapro_info_ptr->damaged = 1; + parent_ptr = fileptr->parent_ptr; + if(parent_ptr) { + if(parent_ptr->subdir_ptr) { + parent_ptr->damaged = 1; + } + } + save_fileptr = fileptr; + nextptr = fileptr->next_ptr; + fileptr->next_ptr = 0; + fileptr = nextptr; + while(fileptr) { + nextptr = fileptr->next_ptr; + dynapro_erase_free_entry(dsk, fileptr); + fileptr = nextptr; + } + dynapro_erase_free_entry(dsk, save_fileptr); +} + +void +dynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr) +{ + if(fileptr == 0) { + return; + } + dyna_printf("dynapro_mark_damaged: %s damaged\n", fileptr->unix_path); + fileptr->damaged = 1; + dsk->dynapro_info_ptr->damaged = 1; + dynapro_unmap_file(dsk, fileptr); + if(fileptr->subdir_ptr) { + dynapro_erase_free_dir(dsk, fileptr->subdir_ptr); + fileptr->subdir_ptr = 0; + } + + if((fileptr->prodos_name[0] >= 0xe0) && fileptr->parent_ptr) { + // We are a directory header, mark the directory entry of our + // parent as damaged (but don't actually damage it) + fileptr->parent_ptr->damaged = 1; + } else if(fileptr != dsk->dynapro_info_ptr->volume_ptr) { + dynapro_unlink_file(fileptr); + } +} + +int +dynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size) +{ + Dynapro_info *info_ptr; + Dynapro_map *map_ptr; + Dynapro_file *fileptr; + byte *bptr; + word32 ui, block; + int num; + int i; + + // Return 1 if write was done. Return < 0 if an error occurs + + dyna_printf("\n"); + dyna_printf("------------------------------------------------\n"); + dyna_printf("dynapro_write to %08llx, size:%08x\n", doffset, size); + dynapro_debug_update(dsk); + + bptr = dsk->raw_data; + if((doffset + size) > dsk->dimage_size) { + printf("Write past end of disk, ignored\n"); + return -1; + } + for(ui = 0; ui < size; ui++) { +#if 0 + if((bptr[doffset + ui] != bufptr[ui]) && (diffs < 500)) { + printf("%07llx:%02x (was %02x)\n", doffset+ui, + bufptr[ui], bptr[doffset + ui]); + diffs++; + } +#endif + bptr[doffset + ui] = bufptr[ui]; + } + + info_ptr = dsk->dynapro_info_ptr; + if(info_ptr == 0) { + printf("dynapro_info_ptr==0\n"); + return -1; + } + + num = (size + 511) >> 9; + block = (word32)(doffset >> 9); + dyna_printf("Marking blocks %05x-%05x modified\n", block, + block + num - 1); + for(i = 0; i < num; i++) { + map_ptr = &(info_ptr->block_map_ptr[block + i]); + map_ptr->modified = 1; + } + for(i = 0; i < num; i++) { + map_ptr = &(info_ptr->block_map_ptr[block + i]); + if(!map_ptr->modified) { + continue; // Already cleared + } + fileptr = map_ptr->file_ptr; + if(fileptr == 0) { + continue; + } + if(fileptr->prodos_name[0] >= 0xe0) { + dynapro_handle_write_dir(dsk, fileptr->parent_ptr, + fileptr, fileptr->dir_byte); + } else { + dynapro_handle_write_file(dsk, fileptr); + } + } + + return 1; +} + + +void +dynapro_debug_update(Disk *dsk) +{ + dyna_printf("Writing out DYNAPRO_IMAGE, %p\n", dsk); +#if 0 + // This causes the file DYNAPRO_IMAGE to be written out as the raw + // image after any write to the Dynapro volume. This is for manual + // debugging. + dynapro_write_to_unix_file("DYNAPRO_IMAGE", dsk->raw_data, + (word32)dsk->dimage_size); +#endif +} + +void +dynapro_debug_map(Disk *dsk, const char *str) +{ + Dynapro_map *map_ptr; + Dynapro_file *lastfileptr, *fileptr; + const char *newstr; + int num_blocks; + int i; + + return; // HACK! + + num_blocks = (word32)((dsk->dimage_size + 511) >> 9); + map_ptr = dsk->dynapro_info_ptr->block_map_ptr; + lastfileptr = 0; + printf(" Showing map for %s, %05x blocks, %s\n", + dsk->dynapro_info_ptr->root_path, num_blocks, str); + for(i = 0; i < num_blocks; i++) { + fileptr = map_ptr[i].file_ptr; + if(fileptr != lastfileptr) { + newstr = ""; + if(fileptr) { + newstr = fileptr->unix_path; + } + printf(" %04x (%07x): %p %s\n", i, i << 9, fileptr, + newstr); + } + lastfileptr = fileptr; + } + printf("Recursive file map:\n"); + dynapro_debug_recursive_file_map(dsk->dynapro_info_ptr->volume_ptr, 1); +} + +void +dynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start) +{ + if(!fileptr) { + return; + } + while(fileptr) { + printf(" file %p %s map_first_block:%05x, storage:%02x key:" + "%04x\n", fileptr, fileptr->unix_path, + fileptr->map_first_block, fileptr->prodos_name[0], + fileptr->key_block); + printf(" n:%p, sub:%p, eof:%06x, parent:%p dam:%d\n", + fileptr->next_ptr, fileptr->subdir_ptr, + fileptr->eof, fileptr->parent_ptr, fileptr->damaged); + if(fileptr->unix_path == 0) { + printf("Filename is invalid, exiting\n"); + exit(1); + } + if(!fileptr->parent_ptr && !start) { + printf("parent_ptr is 0, exiting\n"); + exit(1); + } + dynapro_debug_recursive_file_map(fileptr->subdir_ptr, 0); + start = 0; + fileptr = fileptr->next_ptr; + } +} + +word32 +dynapro_unix_to_prodos_time(const time_t *time_ptr) +{ + struct tm *tm_ptr; + word32 ymd, hours_mins, date_time; + int year; + + tm_ptr = localtime(time_ptr); + hours_mins = (tm_ptr->tm_hour << 8) | tm_ptr->tm_min; + year = tm_ptr->tm_year; // years since 1900 + if(year < 80) { + year = 80; + } else if(year >= 100) { + year -= 100; + if(year >= 80) { + year = 79; + } + } + ymd = (year << 9) | ((tm_ptr->tm_mon + 1) << 5) | (tm_ptr->tm_mday); + date_time = (ymd & 0xffff) | (hours_mins << 16); + // printf("Unix time:%s results in:%08x\n", asctime(tm_ptr), date_time); + + return date_time; +} + +int +dynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr, + word32 storage_type) +{ + Dynapro_file *thisptr; + char *str; + word32 upper_lower; + int len, outpos, inpos, max_inpos, c, done, dot_pos, inc_pos; + int i; + +#if 0 + printf("dynapro_create_prodos_name to %s, match:%p, st:%03x\n", + newfileptr->unix_path, matchptr, storage_type); +#endif + + for(i = 0; i < 17; i++) { + newfileptr->prodos_name[i] = 0; + } + str = newfileptr->unix_path; + if(!str) { + return 0; + } + inpos = (int)strlen(str); + max_inpos = inpos + 1; + while(inpos >= 1) { + inpos--; + if(str[inpos] == '/') { + inpos++; + break; + } + } + if(storage_type == 0x50) { + // printf("max_inpos:%d, inpos:%d\n", max_inpos, inpos); + } + if((storage_type == 0x50) && ((max_inpos - inpos) > 12)) { + // Remove .applesingle extension + max_inpos -= 13; + } + //printf(" inpos:%d max_inpos:%d str:%s\n", inpos, max_inpos, + // &(str[inpos])); + outpos = 0; + while(outpos < (DYNAPRO_PATH_MAX - 1)) { + c = 0; + if(inpos < max_inpos) { + c = str[inpos++]; + } + g_dynapro_path_buf[outpos] = c; + if(c == 0) { + break; + } + outpos++; + if((c >= 'A') && (c <= 'Z')) { + continue; // This is legal + } + if((c >= 'a') && (c <= 'z')) { + continue; // Also legal + } + if((outpos > 1) && (c >= '0') && (c <= '9')) { + continue; // Also legal + } + if((outpos > 1) && (c == '.')) { + continue; // Also legal + } + // If this is the first character, make it "A" and continue + if(outpos == 1) { + g_dynapro_path_buf[0] = 'A'; + continue; + } + if((c == ',') || (c == '#')) { // ,ttxt,a$2000, ignore + outpos--; + break; // All done + } + + // This is not legal. Make it a '.' + if((c >= 0x20) && (c <= 0x7e)) { + g_dynapro_path_buf[outpos - 1] = '@'; // do '.' later + } else { + outpos--; // Ignore it + } + } + + g_dynapro_path_buf[outpos] = 0; + // printf(" initial path_buf:%s, %d\n", &g_dynapro_path_buf[0], outpos); + while((outpos >= 0) && (g_dynapro_path_buf[outpos-1] == '@')) { + // Remove trailing '@' since they are not useful + outpos--; + g_dynapro_path_buf[outpos] = 0; + } + for(i = 1; i < outpos; i++) { + // Convert '@' to '.' to make name legal + if(g_dynapro_path_buf[i] == '@') { + g_dynapro_path_buf[i] = '.'; + } + } + if(outpos == 0) { + // Not a valid file, just skip it + return 0; + } + // Now, it's valid. Squeeze it to 15 character but saving extension + len = (int)strlen(&g_dynapro_path_buf[0]); + if(len > 15) { + // Copy last 8 characters to be in positions 7..14 + for(i = 7; i < 16; i++) { + g_dynapro_path_buf[i] = g_dynapro_path_buf[len-15 + i]; + } + } + + len = (int)strlen(&g_dynapro_path_buf[0]); + if((len > 15) || (len == 0)) { + printf("Bad filename handling: %s\n", &g_dynapro_path_buf[0]); + return 0; + } + + // See if it conflicts with matchptr + thisptr = matchptr; + for(i = 0; i < 10000; i++) { + if(!thisptr || (thisptr == newfileptr)) { + thisptr = 0; + break; + } + //printf("Comparing %s to %s\n", &g_dynapro_path_buf[0], + // (char *)&(thisptr->prodos_name[1])); + len = (int)strlen(&g_dynapro_path_buf[0]); + if((len == (thisptr->prodos_name[0] & 0xf)) && + (cfgcasecmp(&g_dynapro_path_buf[0], + (char *)&(thisptr->prodos_name[1])) == 0)) { + dyna_printf(" that was a match\n"); + dot_pos = 0; + inc_pos = len - 1; + for(i = len - 2; i >= 1; i--) { + if(g_dynapro_path_buf[i] == '.') { + dot_pos = i; + } + } + if(len < 15) { + // Append "1" to the end + len++; + inc_pos = len - 1; + g_dynapro_path_buf[len] = 0; + g_dynapro_path_buf[len - 1] = '1'; + if(dot_pos > 1) { + for(i = len - 1; i >= dot_pos; i--) { + g_dynapro_path_buf[i] = + g_dynapro_path_buf[i-1]; + } + inc_pos = dot_pos; + } + g_dynapro_path_buf[inc_pos] = '1'; + } else if(dot_pos > 3) { + inc_pos = dot_pos - 1; + } + done = 0; + for(i = inc_pos; i >= 1; i--) { + c = g_dynapro_path_buf[i]; + c++; + if(c == ('9' + 1)) { + c = '0'; + } else if(c == ('z' + 1)) { + c = 'a'; + } else if(c == ('Z' + 1)) { + c = 'A'; + } else { + done = 1; + } + g_dynapro_path_buf[i] = c; + if(done) { + break; + } + } + thisptr = matchptr; + } else { + thisptr = thisptr->next_ptr; + } + } + if(thisptr) { + // File could not be made unique + printf("Could not make a unique ProDOS filename: %s\n", + newfileptr->unix_path); + return 0; + } + + upper_lower = 0; + for(i = 0; i < len; i++) { + c = g_dynapro_path_buf[i]; + if((c >= 'a') && (c <= 'z')) { + c = c - 'a' + 'A'; + upper_lower |= 0x8000 | (0x4000 >> i); + } + newfileptr->prodos_name[1 + i] = c; + } + newfileptr->prodos_name[0] = len | storage_type; + newfileptr->upper_lower = upper_lower; + + return len; +} + +Dynapro_file * +dynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr, + Dynapro_file *match_ptr, word32 storage_type) +{ + Dynapro_file *fileptr; + int len; + int i; + +#if 0 + printf("dynapro_new_unix_file for %s, parent:%p, m:%p, st:%03x\n", + path, parent_ptr, match_ptr, storage_type); +#endif + fileptr = dynapro_alloc_file(); + if(!fileptr) { + return 0; + } + + fileptr->next_ptr = 0; + fileptr->parent_ptr = parent_ptr; + fileptr->subdir_ptr = 0; + fileptr->buffer_ptr = 0; + fileptr->unix_path = kegs_malloc_str(path); + for(i = 0; i < 17; i++) { + fileptr->prodos_name[i] = 0; + } + fileptr->dir_byte = 0; + fileptr->eof = 0; + fileptr->blocks_used = 0; + fileptr->creation_time = 0; + fileptr->lastmod_time = 0; + fileptr->upper_lower = 0; + fileptr->key_block = 0; + fileptr->aux_type = 0; + fileptr->header_pointer = 0; + fileptr->map_first_block = 0; + fileptr->file_type = 0x0f; // Default to "DIR" + fileptr->modified_flag = 0; + fileptr->damaged = 0; + + len = (int)strlen(fileptr->unix_path); + for(i = len - 1; i >= 0; i--) { + if(fileptr->unix_path[i] == '/') { + fileptr->unix_path[i] = 0; // Strip trailing / + } else { + break; + } + } + + if(storage_type < 0xd0) { + storage_type = dynatype_detect_file_type(fileptr, + fileptr->unix_path, storage_type); + } + + len = dynapro_create_prodos_name(fileptr, match_ptr, storage_type); + if(len == 0) { + printf("Could not create prodos name for: %s\n", path); + free(fileptr); + return 0; + } + +#if 0 + printf("dynapro_create_new_unix_file: %s prodos:%s, st:%02x, ft:%02x, " + "aux:%04x\n", fileptr->unix_path, &(fileptr->prodos_name[1]), + fileptr->prodos_name[0], fileptr->file_type, fileptr->aux_type); +#endif + return fileptr; +} + +int +dynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr, + word32 dir_byte) +{ + struct stat stat_buf; + struct dirent *direntptr; + DIR *opendirptr; + Dynapro_file *fileptr, *head_ptr, *prev_ptr; + mode_t fmt; + word32 storage_type, val; + int ret; + + // Create a directory entry at dir_byte first +#if 0 + printf("\n"); + printf("dynapro_add_files to %s, %p dir_byte:%08x\n", unix_path, + parent_ptr, dir_byte); +#endif + + storage_type = 0xe0; // Directory header + if(dir_byte < 0x600) { // Block 2: volume header + storage_type = 0xf0; + } + head_ptr = dynapro_new_unix_file(unix_path, parent_ptr, 0, + storage_type); + if(parent_ptr) { + parent_ptr->subdir_ptr = head_ptr; +#if 0 + printf("set parent %s subdir_ptr=%p\n", parent_ptr->unix_path, + head_ptr); +#endif + } + if(dsk->dynapro_info_ptr->volume_ptr == 0) { + dsk->dynapro_info_ptr->volume_ptr = head_ptr; + } + if(head_ptr == 0) { + printf("new_file returned 0, skipping %s\n", unix_path); + return dir_byte; + } + head_ptr->key_block = dir_byte >> 9; + head_ptr->aux_type = 0x0d27; // 0x27,0x0d + head_ptr->file_type = 0x75; // Directory header type + if(storage_type >= 0xf0) { + head_ptr->file_type = 0x00; + } + ret = cfg_stat(unix_path, &stat_buf, 0); + if(ret != 0) { + printf("stat %s ret %d, errno:%d\n", unix_path, ret, errno); + return 0; + } + head_ptr->creation_time = dynapro_unix_to_prodos_time( + &stat_buf.st_ctime); + + dir_byte = dynapro_add_file_entry(dsk, head_ptr, 0, dir_byte, 0); + + opendirptr = opendir(unix_path); + if(opendirptr == 0) { + printf("Could not open %s as a dir\n", unix_path); + return 0; + } + prev_ptr = head_ptr; + while(1) { + direntptr = readdir(opendirptr); + if(direntptr == 0) { + break; + } + if(direntptr->d_name[0] == '.') { + continue; // Ignore all '.' files + } + dynapro_join_path_and_file(&(g_dynapro_path_buf[0]), unix_path, + direntptr->d_name, DYNAPRO_PATH_MAX); + ret = cfg_stat(&(g_dynapro_path_buf[0]), &stat_buf, 0); + if(ret != 0) { + printf("stat %s ret %d, errno:%d\n", + &g_dynapro_path_buf[0], ret, errno); + continue; // skip it + } + + fmt = stat_buf.st_mode & S_IFMT; + storage_type = 0; + if(fmt == S_IFDIR) { + // Ignore symlinks to directories (since they may point + // outside the base directory, and so dynamically + // removing files could be a security issue). + ret = cfg_stat(&g_dynapro_path_buf[0], &stat_buf, 1); + if(ret != 0) { + printf("lstat %s ret %d, errno:%d\n", + &g_dynapro_path_buf[0], ret, errno); + continue; + } + storage_type = 0xd0; // Directory + } else if(fmt != S_IFREG) { + continue; // Skip this + } +#if 0 + printf("GOT file: %s, is_dir:%d (%s), parent:%p\n", + &(g_dynapro_path_buf[0]), is_dir, direntptr->d_name, + parent_ptr); +#endif + fileptr = dynapro_new_unix_file(&(g_dynapro_path_buf[0]), + head_ptr, head_ptr->next_ptr, storage_type); + if(fileptr == 0) { + closedir(opendirptr); + return 0; + } + prev_ptr->next_ptr = fileptr; + fileptr->key_block = dynapro_find_free_block(dsk); + fileptr->creation_time = dynapro_unix_to_prodos_time( + &stat_buf.st_ctime); + fileptr->lastmod_time = dynapro_unix_to_prodos_time( + &stat_buf.st_mtime); + if(fileptr->key_block == 0) { + printf("Allocating directory block failed\n"); + closedir(opendirptr); + return 0; + } + fileptr->blocks_used = 1; + fileptr->eof = 1*0x200; + dir_byte = dynapro_add_file_entry(dsk, fileptr, head_ptr, + dir_byte, 0x27); + if(dir_byte == 0) { + closedir(opendirptr); + return 0; + } + if(fmt == S_IFDIR) { + val = dynapro_create_dir(dsk, fileptr->unix_path, + fileptr, (fileptr->key_block << 9) + 4); + if(val == 0) { + closedir(opendirptr); + return 0; + } + } else { + val = dynapro_file_from_unix(dsk, fileptr); + if(val == 0) { + closedir(opendirptr); + return 0; + } + } + prev_ptr = fileptr; + } + + closedir(opendirptr); + return dir_byte; +} + +word32 +dynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr, + word32 dir_byte, word32 inc) +{ + Dynapro_file *parent_ptr; + byte *bptr, *pkeyptr; + word32 storage_type, val, ent, new_dir_blk, new_dir_byte; + word32 header_pointer; + int i; + +#if 0 + printf("dynapro_add_file_entry: %p %p %s head:%p dir_byte:%08x " + "inc:%03x\n", dsk, fileptr, fileptr->unix_path, head_ptr, + dir_byte, inc); +#endif + bptr = dsk->raw_data; + if(((dir_byte & 0x1ff) + inc + inc) >= 0x200) { + // This entry will not fit in this directory block. + // Try to step to next block, otherwise allocate a new one + new_dir_byte = dir_byte & -0x200L; + new_dir_blk = dynapro_get_word16(&bptr[new_dir_byte + 2]); + dyna_printf(" Entry does not fit, new_dir_blk:%04x\n", + new_dir_blk); + if(new_dir_blk != 0) { + // Follow to the next block + dir_byte = (new_dir_blk * 0x200) + 4; + } else if(dir_byte < (6 * 0x200)) { + // Otherwise, allocate a new block (not for volume dir) + // This is a volume header, always 4 blocks, don't + // allocate any more, this is now full + printf("Too many file in volume directory\n"); + return 0; // Out of space + } else { + new_dir_blk = dynapro_find_free_block(dsk); + if(new_dir_blk == 0) { + return 0; + } + new_dir_byte = new_dir_blk * 512; + dynapro_set_word16(&bptr[new_dir_byte], dir_byte >> 9); + dynapro_set_word16(&bptr[new_dir_byte + 2], 0); + dir_byte = (dir_byte >> 9) << 9; + dynapro_set_word16(&bptr[dir_byte + 2], new_dir_blk); + dir_byte = new_dir_byte + 4; + if(!head_ptr) { + dyna_printf("No head:%s\n", fileptr->unix_path); + } + parent_ptr = head_ptr->parent_ptr; + if(!parent_ptr) { + printf("No parent: %s\n", fileptr->unix_path); + return 0; + } + parent_ptr->blocks_used++; + parent_ptr->eof += 0x200; + new_dir_byte = parent_ptr->dir_byte; + if(new_dir_byte == 0) { + printf("Invalid dir_byte for %s\n", + parent_ptr->unix_path); + return 0; + } + dynapro_set_word16(&bptr[new_dir_byte + 0x13], + parent_ptr->blocks_used); + dynapro_set_word24(&bptr[new_dir_byte + 0x15], + parent_ptr->eof); + } + } else { + dir_byte += inc; + } + bptr = &(dsk->raw_data[dir_byte]); + + fileptr->dir_byte = dir_byte; + for(i = 0; i < 0x27; i++) { + bptr[i] = 0; + } + for(i = 0; i < 16; i++) { + bptr[i] = fileptr->prodos_name[i]; // [0] = len,storage_t + } + bptr[0x10] = fileptr->file_type; + dynapro_set_word16(&bptr[0x11], fileptr->key_block); + dynapro_set_word16(&bptr[0x13], fileptr->blocks_used); + dynapro_set_word24(&bptr[0x15], fileptr->eof); + dynapro_set_word32(&bptr[0x18], fileptr->creation_time); + // creation date&time + bptr[0x1c] = fileptr->upper_lower & 0xff; // Version + bptr[0x1d] = fileptr->upper_lower >> 8; // Min_Version + bptr[0x1e] = 0xe3; // Access + dynapro_set_word16(&bptr[0x1f], fileptr->aux_type); + storage_type = bptr[0]; + if(storage_type >= 0xf0) { // Volume header + dynapro_set_word16(&bptr[0x11], 0); + fileptr->lastmod_time = 0x00060000; + // low 16 bits: file_count, upper 16 bits: bitmap_block + fileptr->header_pointer = (word32)(dsk->raw_dsize >> 9); + // Total blocks + dynapro_set_word16(&bptr[0x1c], 0x0005); + dynapro_set_word16(&bptr[0x16], fileptr->upper_lower); + } else if(storage_type >= 0xe0) { // Directory header + dynapro_set_word16(&bptr[0x11], 0); + dynapro_set_word16(&bptr[0x1c], 0x0005); + parent_ptr = fileptr->parent_ptr; // subdir entry + if(parent_ptr == 0) { + printf("parent_ptr of %s is 0\n", fileptr->unix_path); + return 0; + } + val = parent_ptr->dir_byte >> 9; + fileptr->lastmod_time = (val << 16); // Parent block + val = parent_ptr->dir_byte & 0x1ff; + ent = (val - 4) / 0x27; + fileptr->header_pointer = 0x2700 | (ent + 1); + } else { + // Directory entry, or normal file + if(head_ptr == 0) { + printf("head_ptr of %s is 0\n", fileptr->unix_path); + return 0; + } + header_pointer = head_ptr->key_block; + fileptr->header_pointer = header_pointer; + dynapro_set_word16(&bptr[0x25], header_pointer); + pkeyptr = &(dsk->raw_data[header_pointer << 9]); + val = head_ptr->lastmod_time + 1; + head_ptr->lastmod_time = val; + dynapro_set_word16(&pkeyptr[4 + 0x21], val); // File count + } + dynapro_set_word32(&bptr[0x21], fileptr->lastmod_time); + // Last Modified date&time (or header info) + dynapro_set_word16(&bptr[0x25], fileptr->header_pointer); +#if 0 + printf("Set dir_byte %07x=%04x\n", dir_byte + 0x25, + fileptr->header_pointer); +#endif + + return dir_byte; +} + +// When creating sparse files, always ensure first block is not sparse. GS/OS +// does not treat the first block as sparse, it will actually read block 0 +// This handles normal files, and one fork of a forked file +word32 +dynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr, + word32 key_block, dword64 dsize) +{ + byte *bptr; + word32 sap_block, tree_block, sap_byte, tree_byte, sparse, block_num; + word32 num_blocks, blocks_used, block_off, storage_type; + int num_bytes; + int i; + + bptr = &(dsk->raw_data[0]); + *storage_type_ptr = 0; + sap_block = 0; + tree_block = 0; + num_blocks = (word32)((dsize + 511) >> 9); + if(num_blocks == 0) { // 0-length file + num_blocks = 1; + } else if(num_blocks > 0x8000) { // >= 16MB (32K*512) + printf("File is too large, failing\n"); + return 0; + } + block_off = 0; + blocks_used = 1; + while(block_off < num_blocks) { + sparse = (block_off > 0); // sparse=0 for first block + for(i = 0; i < 0x200; i++) { + if(fptr[(block_off << 9) + i] != 0) { + sparse = 0; + break; + } + } + if(sparse) { + block_off++; + continue; + } + + if((tree_block == 0) && (num_blocks > 256)) { + tree_block = dynapro_find_free_block(dsk); + if(tree_block == 0) { + return 0; + } + blocks_used++; + } + tree_byte = (tree_block << 9) + ((block_off >> 8) & 0xff); + if(tree_block) { + sap_block = bptr[tree_byte + 0] | + (bptr[tree_byte + 256] << 8); + } + if((sap_block == 0) && (num_blocks > 1)) { + sap_block = dynapro_find_free_block(dsk); + if(sap_block == 0) { + return 0; + } + blocks_used++; + if(tree_block) { + bptr[tree_byte + 0] = sap_block; + bptr[tree_byte + 256] = sap_block >> 8; + } + } + if(block_off == 0) { + block_num = key_block; + } else { + block_num = dynapro_find_free_block(dsk); + if(block_num == 0) { + return 0; + } + blocks_used++; + } + sap_byte = (sap_block << 9) | (block_off & 0xff); + if(sap_block) { + bptr[sap_byte + 0] = block_num; + bptr[sap_byte + 256] = block_num >> 8; + } + + num_bytes = 0x200; + if(block_off == (dsize >> 9)) { // Last block + num_bytes = dsize & 0x1ff; + } + for(i = 0; i < num_bytes; i++) { + bptr[(block_num << 9) + i] = fptr[(block_off << 9) + i]; + } + + block_off++; + } + + storage_type = 0x10; + if(tree_block) { + storage_type = 0x30; + key_block = tree_block; + } else if(sap_block) { + storage_type = 0x20; + key_block = sap_block; + } + *storage_type_ptr = storage_type; + return (blocks_used << 16) | key_block; +} + +word32 +dynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr) +{ + byte *bptr, *fptr; + dword64 dsize; + word32 storage_type, blocks_out, dir_byte; + + fptr = dynapro_malloc_file(fileptr->unix_path, &dsize, 0x200); + fileptr->eof = (word32)dsize; +#if 0 + printf("file_from_unix %s, size:%08llx, file_type:%02x, dir_byte:" + "%07x, storage:%02x\n", fileptr->unix_path, dsize, + fileptr->file_type, fileptr->dir_byte, fileptr->prodos_name[0]); +#endif + storage_type = 0; + if((fileptr->prodos_name[0] & 0xf0) == 0x50) { + // .applesingle file with data and/or resource forks + blocks_out = applesingle_from_unix(dsk, fileptr, fptr, dsize); + } else { + // Normal file + fileptr->prodos_name[0] = (fileptr->prodos_name[0] & 0xf); + + blocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type, + fileptr->key_block, dsize); + } + free(fptr); + fileptr->prodos_name[0] |= storage_type; + fileptr->key_block = blocks_out & 0xffff; + fileptr->blocks_used = (blocks_out >> 16) & 0xffff; + + // Update dir_byte information for this file + dir_byte = fileptr->dir_byte; + if(dir_byte == 0) { + dyna_printf("dir_byte is 0 for %s\n", fileptr->unix_path); + } + bptr = &(dsk->raw_data[dir_byte]); + bptr[0] = fileptr->prodos_name[0]; + bptr[0x10] = fileptr->file_type; + dynapro_set_word16(&bptr[0x11], fileptr->key_block); + dynapro_set_word16(&bptr[0x13], fileptr->blocks_used); + dynapro_set_word24(&bptr[0x15], fileptr->eof); + dynapro_set_word16(&bptr[0x1f], fileptr->aux_type); +#if 0 + printf("Set %s dir_byte:%07x+0x10=%02x (file_type)\n", + fileptr->unix_path, dir_byte, fileptr->file_type); +#endif + + return blocks_out; +} + +word32 +dynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks) +{ + Dynapro_info *infoptr; + byte *bptr; + word32 bitmap_size_bytes, bitmap_size_blocks; + int pos; + word32 ui; + int i; + + dsk->raw_data = calloc(num_blocks, 512); + if(dsk->raw_data == 0) { + dynapro_error(dsk, "Could not allocate %d bytes\n", + num_blocks * 512); + return 0; + } + dsk->dimage_size = num_blocks * 512LL; + dsk->dimage_start = 0; + dsk->raw_dsize = num_blocks * 512LL; + + bptr = &(dsk->raw_data[0]); + for(i = 0; i < 512; i++) { + bptr[i] = g_prodos_block0[i]; + } + + // Directory is from blocks 2 through 5. Set up prev and next ptrs + bptr = &(dsk->raw_data[2 * 0x200]); + for(i = 0; i < 3; i++) { // Blocks 2,3,4 (or 3,4,5) + dynapro_set_word16(&bptr[(i + 1)*0x200], i + 2); // Prev_blk + dynapro_set_word16(&bptr[i*0x200 + 2], i + 3); // Next_blk + } + + // Calculate bitmap to go in blocks 6... + bitmap_size_bytes = (num_blocks + 7) >> 3; + bitmap_size_blocks = (bitmap_size_bytes + 512 - 1) >> 9; + bptr = &(dsk->raw_data[6 * 512]); // Block 6 + bptr[0] = 0; + for(ui = (6 + bitmap_size_blocks); ui < num_blocks; ui++) { + pos = (ui >> 3); + bptr[pos] |= (0x80U >> (ui & 7)); + } + + infoptr = calloc(sizeof(Dynapro_info), 1); + if(!infoptr) { + return 0; + } + infoptr->root_path = kegs_malloc_str(dir_path); + infoptr->volume_ptr = 0; + infoptr->block_map_ptr = calloc(num_blocks * sizeof(Dynapro_map), 1); + infoptr->damaged = 0; + if((infoptr->root_path == 0) || (infoptr->block_map_ptr == 0)) { + dynapro_error(dsk, "Could not allocate memory!\n"); + return 0; + } + dsk->dynapro_info_ptr = infoptr; + return 1; +} + +word32 +dynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num, + word32 file_offset, word32 eof) +{ + Dynapro_info *info_ptr; + Dynapro_map *map_ptr; + byte *buffer_ptr; + word32 size, size_to_end; + + info_ptr = dsk->dynapro_info_ptr; + if(!info_ptr || (block_num >= (dsk->dimage_size >> 9))) { + printf(" mapping file %s, block %04x is invalid\n", + fileptr->unix_path, block_num); + return 0; + } + if(info_ptr->block_map_ptr == 0) { + return 0; + } + if(block_num == 0) { + return 1; + } + map_ptr = &(info_ptr->block_map_ptr[block_num]); + if((map_ptr->file_ptr != 0) || (map_ptr->next_map_block != 0)) { + dyna_printf("Mapping %s to block %04x, already has file_ptr:" + "%p, next_map:%04x, mod:%d\n", fileptr->unix_path, + block_num, map_ptr->file_ptr, map_ptr->next_map_block, + map_ptr->modified); + if(map_ptr->file_ptr) { + dyna_printf(" Existing file: %s\n", + map_ptr->file_ptr->unix_path); + } + return 0; + } + //printf(" map file %s block %05x off:%08x\n", fileptr->unix_path, + // block_num, file_offset); + + map_ptr->next_map_block = fileptr->map_first_block; + fileptr->map_first_block = block_num; + map_ptr->modified = 0; + map_ptr->file_ptr = fileptr; + + if(file_offset >= eof) { + return 1; // This block was an "overhead" block + } + + buffer_ptr = fileptr->buffer_ptr; + if(buffer_ptr) { + // Copy this block in at file_offset + size = 0x200; + size_to_end = eof - file_offset; + if(size_to_end < size) { + size = size_to_end; + } +#if 0 + printf("mofb: Write to %p + %07x from block %04x, size:%04x\n", + buffer_ptr, file_offset, block_num, size); +#endif + memcpy(buffer_ptr + file_offset, + &(dsk->raw_data[block_num * 0x200]), size); + } + return 1; +} + +word32 +dynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num, + int level, word32 file_offset, word32 eof) +{ + byte *bptr; + word32 entry_inc, tmp, ret; + int i; + +#if 0 + printf("dynapro_map_file_blocks %s block_num %05x level:%d off:%08x\n", + fileptr->unix_path, block_num, level, file_offset); +#endif + if(level == 0) { + return 0; // Bad value, should not happen + } + if(level == 1) { + return dynapro_map_one_file_block(dsk, fileptr, block_num, + file_offset, eof); + } + ret = dynapro_map_one_file_block(dsk, fileptr, block_num, 1U << 30, 0); + if(ret == 0) { + return ret; + } + entry_inc = 512; + if(level == 3) { // Tree + entry_inc = 256*512; + } + bptr = &(dsk->raw_data[block_num * 0x200]); + for(i = 0; i < 256; i++) { + tmp = bptr[i] + (bptr[256 + i] << 8); + if(tmp == 0) { + continue; + } + ret = dynapro_map_file_blocks(dsk, fileptr, tmp, level - 1, + file_offset + i*entry_inc, eof); + if(ret == 0) { + return ret; + } + } + dynapro_debug_map(dsk, "post map_file_blocks"); + + return 1; +} + +word32 +dynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data) +{ + word32 block_num, ret; + int level; + + level = (fileptr->prodos_name[0] >> 4) & 0xf; + block_num = fileptr->key_block; + if(level == 5) { // Forked file + return applesingle_map_from_prodos(dsk, fileptr, + do_file_data); + } else if((level < 0) || (level >= 4)) { + printf("Storage_type: %02x for %s is bad\n", level, + fileptr->unix_path); + return 0; + } + + fileptr->buffer_ptr = 0; + if(do_file_data) { + // Create a place for data. We will free before returning + fileptr->buffer_ptr = calloc(1, fileptr->eof + 0x200); + if(fileptr->buffer_ptr == 0) { + printf("malloc failed!\n"); + return 0; + } + } + // Must not return now before free'ing fileptr->buffer_ptr! + + ret = dynapro_map_file_blocks(dsk, fileptr, block_num, level, 0, + fileptr->eof); + + // printf(" dynapro_map_file, map_file_blocks ret:%04x\n", ret); + if((ret != 0) && (do_file_data)) { + // Then, write buffer_ptr to the unix file + ret = dynapro_write_to_unix_file(fileptr->unix_path, + fileptr->buffer_ptr, fileptr->eof); + // printf(" map_file, write_to_unix_file ret:%04x\n", ret); + } + + // And free the buffer_ptr + free(fileptr->buffer_ptr); + fileptr->buffer_ptr = 0; + + return ret; +} + +word32 +dynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr) +{ + byte *bptr; + word32 block_num, ret; + int cnt; + + // Loop over all directory blocks marking the map + block_num = fileptr->key_block; + if(block_num == 0) { + printf("dynapro_map_dir_blocks, block_num is 0\n"); + return 0; + } + bptr = &(dsk->raw_data[0]); + cnt = 0; + fileptr->map_first_block = 0; + while(block_num != 0) { + ret = dynapro_map_one_file_block(dsk, fileptr, block_num, + 1U << 30, 0); + if(ret == 0) { + printf("dynapro_map_dir_on_block, ret 0, block:%04x\n", + block_num); + return 0; + } + block_num = dynapro_get_word16(&bptr[(block_num * 0x200) + 2]); + cnt++; + if(cnt > 1000) { + printf("Directory had loop in it, error\n"); + return 0; + } + } + + dynapro_debug_map(dsk, "post map_dir_blocks"); + return 1; +} + +word32 +dynapro_build_map(Disk *dsk, Dynapro_file *fileptr) +{ + word32 ret; + + if(fileptr == 0) { + return 0; + } + + // printf("### dynapro_build_map for dir:%s\n", fileptr->unix_path); + + // fileptr points to a directory header (volume or subdir). Walk + // all siblings and build a map + ret = 1; + while(fileptr && ret) { + if(fileptr->prodos_name[0] >= 0xe0) { + // Directory/Volume header + ret = dynapro_map_dir_blocks(dsk, fileptr); + } else if(fileptr->subdir_ptr) { + // Recurse to handle subdirectory + ret = dynapro_build_map(dsk, fileptr->subdir_ptr); + } else { + ret = dynapro_map_file(dsk, fileptr, 0); + } + fileptr = fileptr->next_ptr; + } + dynapro_debug_map(dsk, "post build_map"); + return ret; +} + +int +dynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks) +{ + word32 ret; + +#if 0 + printf("dynapro_mount: %p, %s %08x\n", dsk, dir_path, num_blocks); +#endif + if(num_blocks >= 65536) { + num_blocks = 65535; + } + ret = dynapro_prep_image(dsk, dir_path, num_blocks); + if(ret == 0) { + return -1; + } + + ret = dynapro_create_dir(dsk, dir_path, 0, 0x404); // Block 2, +4 + // printf("dynapro_mount will end with ret:%05x\n", ret); + if(ret != 0) { + ret = dynapro_build_map(dsk, dsk->dynapro_info_ptr->volume_ptr); + } + // dynapro_debug_update(dsk); + if(ret == 0) { + dynapro_error(dsk, "Folder too large. dynapro_build_map " + "ret:0\n"); + } else { + ret = dynapro_validate_disk(dsk); + } + if(ret == 0) { + dynapro_error(dsk, "dynapro_validate_disk ret:0\n"); + dynapro_free_dynapro_info(dsk); + return -1; + } +#ifndef _WIN32 + setvbuf(stdout, 0, _IOLBF, 0); +#endif + dsk->fd = 0; + return 0; +} + diff --git a/gsplus/src/engine.h b/gsplus/src/engine.h new file mode 100644 index 0000000..58c5145 --- /dev/null +++ b/gsplus/src/engine.h @@ -0,0 +1,84 @@ +// "@(#)$KmKId: engine.h,v 1.9 2023-09-11 12:55:16+00 kentd Exp $" + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +int +ENGINE_TYPE (Engine_reg *engine_ptr) +{ + register byte *ptr; + byte *arg_ptr; + Pc_log *tmp_pc_ptr; + Fplus *fplus_ptr; + byte *stat; + dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1; + register word32 kpc, acc, xreg, yreg, direct, psr, zero, neg7, addr; + word32 wstat, arg, stack, dbank, opcode, addr_latch, tmp1, tmp2; + word32 getmem_tmp, save_addr, pull_tmp, tmp_bytes, dummy1; + + tmp_pc_ptr = 0; + dummy1 = 0; + if(tmp_pc_ptr || dummy1) { // "use" tmp_pc_ptr to avoid warning + } + + kpc = engine_ptr->kpc; + acc = engine_ptr->acc; + xreg = engine_ptr->xreg; + yreg = engine_ptr->yreg; + stack = engine_ptr->stack; + dbank = engine_ptr->dbank; + direct = engine_ptr->direct; + psr = engine_ptr->psr; + fplus_ptr = engine_ptr->fplus_ptr; + zero = !(psr & 2); + neg7 = psr; + + dplus_1 = fplus_ptr->dplus_1; + dplus_x_m1 = fplus_ptr->dplus_x_minus_1; + dfcyc = engine_ptr->dfcyc; + + g_ret1 = 0; + + while(dfcyc <= g_dcycles_end) { + + FETCH_OPCODE; + + LOG_PC_MACRO(); + + switch(opcode) { + default: + halt_printf("acc8 unk op: %02x\n", opcode); + arg = 9 +#include "defs_instr.h" + * 2; + break; +#include "instable.h" + break; + } + LOG_PC_MACRO2(); + } + + engine_ptr->kpc = kpc; + engine_ptr->acc = acc; + engine_ptr->xreg = xreg; + engine_ptr->yreg = yreg; + engine_ptr->stack = stack; + engine_ptr->dbank = dbank; + engine_ptr->direct = direct; + engine_ptr->dfcyc = dfcyc; + + psr = psr & (~0x82); + psr |= (neg7 & 0x80); + psr |= ((!zero) << 1); + + engine_ptr->psr = psr; + + return g_ret1; +} diff --git a/gsplus/src/engine_c.c b/gsplus/src/engine_c.c new file mode 100644 index 0000000..02224fd --- /dev/null +++ b/gsplus/src/engine_c.c @@ -0,0 +1,959 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +// PSR[8:0] is E_NVMX_DIZC + +extern int g_limit_speed; +extern int g_halt_sim; +extern int g_engine_recalc_event; +extern int g_code_red; +extern int g_ignore_halts; +extern int g_user_halt_bad; +extern dword64 g_dcycles_end; +extern dword64 g_last_vbl_dfcyc; +extern dword64 g_cur_dfcyc; +extern int g_wait_pending; +extern int g_irq_pending; +extern int g_num_brk; +extern int g_num_cop; +extern int g_emul_6502_ind_page_cross_bug; +extern byte *g_slow_memory_ptr; +extern byte *g_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern byte *g_dummy_memory1_ptr; + +extern int g_num_breakpoints; +extern Break_point g_break_pts[]; + +extern Kimage g_debugwin_kimage; + +extern word32 g_log_pc_enable; +extern Pc_log *g_log_pc_ptr; +extern Pc_log *g_log_pc_start_ptr; +extern Pc_log *g_log_pc_end_ptr; + +extern Data_log *g_log_data_ptr; +extern Data_log *g_log_data_start_ptr; +extern Data_log *g_log_data_end_ptr; + +int g_ret1 = 0; + +int size_tab[] = { +#include "size_c.h" +}; + +int bogus[] = { + 0, +#include "op_routs.h" +}; + +#define INC_KPC_1 kpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff); +#define INC_KPC_2 kpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff); +#define INC_KPC_3 kpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff); +#define INC_KPC_4 kpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff); + +#define CYCLES_PLUS_1 dfcyc += dplus_1; +#define CYCLES_PLUS_2 dfcyc += dplus_1 * 2; +#define CYCLES_PLUS_3 dfcyc += dplus_1 * 3; +#define CYCLES_PLUS_4 dfcyc += dplus_1 * 4; +#define CYCLES_PLUS_5 dfcyc += dplus_1 * 5; +#define CYCLES_MINUS_1 dfcyc -= dplus_1; +#define CYCLES_MINUS_2 dfcyc -= dplus_1 * 2; + +#define FCYCLES_ROUND dfcyc = dfcyc + dplus_x_m1; \ + dfcyc = (dfcyc >> 16) << 16; + + +#define GET_1BYTE_ARG arg = arg_ptr[1]; +#define GET_2BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8); +#define GET_3BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16); + +#define LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat) \ + g_log_data_ptr->dfcyc = dfcyc; \ + g_log_data_ptr->stat = in_stat; \ + g_log_data_ptr->addr = in_addr; \ + g_log_data_ptr->val = in_val; \ + g_log_data_ptr->size = in_size; \ + g_log_data_ptr++; \ + if(g_log_data_ptr >= g_log_data_end_ptr) { \ + g_log_data_ptr = g_log_data_start_ptr; \ + } + +/* HACK HACK HACK */ +#define UPDATE_PSR(dummy, old_psr) \ + if(psr & 0x100) { \ + psr |= 0x30; \ + stack = 0x100 + (stack & 0xff); \ + } \ + if((~old_psr & psr) & 0x10) { \ + xreg = xreg & 0xff; \ + yreg = yreg & 0xff; \ + } \ + if(((psr & 4) == 0) && g_irq_pending) { \ + FINISH(RET_IRQ, 0); \ + } \ + if((old_psr ^ psr) & 0x20) { \ + FINISH(RET_PSR, 0); \ + } + +extern Page_info page_info_rd_wr[]; +extern word32 g_slow_mem_changed[]; + +#define GET_MEMORY8(addr,dest) \ + addr_latch = (addr); \ + CYCLES_PLUS_1; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if(wstat & (1 << (31 - BANK_IO_BIT))) { \ + dcycles_tmp1 = dfcyc; \ + dest = get_memory8_io_stub((addr), stat, \ + &dcycles_tmp1, dplus_x_m1); \ + dfcyc = dcycles_tmp1; \ + } else { \ + dest = *ptr; \ + } + +#define GET_MEMORY(addr,dest) GET_MEMORY8(addr, dest) + +#define GET_MEMORY16(addr, dest, in_bank) \ + save_addr = addr; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \ + dcycles_tmp1 = dfcyc; \ + dest = get_memory16_pieces_stub((addr), stat, \ + &dcycles_tmp1, fplus_ptr, in_bank); \ + dfcyc = dcycles_tmp1; \ + } else { \ + CYCLES_PLUS_2; \ + dest = ptr[0] + (ptr[1] << 8); \ + } \ + addr_latch = save_addr; + +#define GET_MEMORY24(addr, dest, in_bank) \ + save_addr = addr; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \ + dcycles_tmp1 = dfcyc; \ + dest = get_memory24_pieces_stub((addr), stat, \ + &dcycles_tmp1, fplus_ptr, in_bank); \ + dfcyc = dcycles_tmp1; \ + } else { \ + CYCLES_PLUS_3; \ + dest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16); \ + } \ + addr_latch = save_addr; + +#define GET_MEMORY_DIRECT_PAGE16(addr, dest, dloc_x_wrap) \ + save_addr = addr; \ + if(psr & 0x100) { \ + if((direct & 0xff) == 0) { \ + save_addr = (save_addr & 0xff) + direct; \ + } \ + } \ + if((psr & 0x100) && (((addr) & 0xff) == 0xff)) { \ + GET_MEMORY8(save_addr, getmem_tmp); \ + if(dloc_x_wrap) { \ + save_addr = (save_addr & 0xff00) | \ + ((save_addr + 1) & 0xff); \ + } else { \ + save_addr = (save_addr + 1) & 0xffff; \ + } \ + if((direct & 0xff) == 0) { \ + save_addr = (save_addr & 0xff) + direct; \ + } \ + GET_MEMORY8(save_addr, dest); \ + dest = (dest << 8) + getmem_tmp; \ + } else { \ + GET_MEMORY16(save_addr, dest, 1); \ + } + + +#define PUSH8(arg) \ + SET_MEMORY8(stack, arg); \ + stack = (stack - 1) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PUSH16(arg) \ + if((stack & 0xfe) == 0) { \ + /* stack will cross page! */ \ + PUSH8((arg) >> 8); \ + PUSH8(arg); \ + } else { \ + stack = (stack - 2) & 0xffff; \ + SET_MEMORY16(stack + 1, arg, 1); \ + } + +#define PUSH16_UNSAFE(arg) \ + save_addr = (stack - 1) & 0xffff; \ + stack = (stack - 2) & 0xffff; \ + SET_MEMORY16(save_addr, arg, 1); \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PUSH24_UNSAFE(arg) \ + save_addr = (stack - 2) & 0xffff; \ + stack = (stack - 3) & 0xffff; \ + SET_MEMORY24(save_addr, arg, 1); \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PULL8(dest) \ + stack++; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + stack = stack & 0xffff; \ + GET_MEMORY8(stack, dest); + +#define PULL8_UNSAFE(dest) \ + stack = (stack + 1) & 0xffff; \ + GET_MEMORY8(stack, dest); \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PULL16(dest) \ + if((stack & 0xfe) == 0xfe) { /* page cross */ \ + PULL8(dest); \ + PULL8(pull_tmp); \ + dest = (pull_tmp << 8) + dest; \ + } else { \ + GET_MEMORY16(stack + 1, dest, 1); \ + stack = (stack + 2) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + } + +#define PULL16_UNSAFE(dest) \ + stack = (stack + 1) & 0xffff; \ + GET_MEMORY16(stack, dest, 1); \ + stack = (stack + 1) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PULL24(dest) \ + if((stack & 0xfc) == 0xfc) { /* page cross */ \ + PULL8(dest); \ + PULL8(pull_tmp); \ + pull_tmp = (pull_tmp << 8) + dest; \ + PULL8(dest); \ + dest = (dest << 16) + pull_tmp; \ + } else { \ + GET_MEMORY24(stack + 1, dest, 1); \ + stack = (stack + 3) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + } + +#define PULL24_UNSAFE(dest) \ + stack = (stack + 1) & 0xffff; \ + GET_MEMORY24(stack, dest, 1); \ + stack = (stack + 2) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define SET_MEMORY8(addr, val) \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + LOG_DATA_MACRO(addr, val, 8, stat); \ + CYCLES_PLUS_1; \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if(wstat) { \ + dcycles_tmp1 = dfcyc; \ + set_memory8_io_stub((addr), val, stat, &dcycles_tmp1, \ + dplus_x_m1); \ + dfcyc = dcycles_tmp1; \ + } else { \ + *ptr = val; \ + } + + +#define SET_MEMORY16(addr, val, in_bank) \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + LOG_DATA_MACRO(addr, val, 16, stat); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat) || (((addr) & 0xff) == 0xff)) { \ + dcycles_tmp1 = dfcyc; \ + set_memory16_pieces_stub((addr), (val), \ + &dcycles_tmp1, dplus_1, dplus_x_m1, in_bank); \ + dfcyc = dcycles_tmp1; \ + } else { \ + CYCLES_PLUS_2; \ + ptr[0] = (val); \ + ptr[1] = (val) >> 8; \ + } + +#define SET_MEMORY24(addr, val, in_bank) \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + LOG_DATA_MACRO(addr, val, 24, stat); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat) || (((addr) & 0xfe) == 0xfe)) { \ + dcycles_tmp1 = dfcyc; \ + set_memory24_pieces_stub((addr), (val), \ + &dcycles_tmp1, fplus_ptr, in_bank); \ + dfcyc = dcycles_tmp1; \ + } else { \ + CYCLES_PLUS_3; \ + ptr[0] = (val); \ + ptr[1] = (val) >> 8; \ + ptr[2] = (val) >> 16; \ + } + + +word32 +get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, + dword64 dplus_x_m1) +{ + dword64 dfcyc; + word32 wstat; + byte *ptr; + + wstat = PTR2WORD(stat) & 0xff; + dfcyc = *dcycs_ptr; + if(wstat & BANK_BREAK) { + check_breakpoints(addr, dfcyc, 0, 1); + } + if(wstat & BANK_IO2_TMP) { + FCYCLES_ROUND; + *dcycs_ptr = dfcyc; + return get_memory_io((addr), dcycs_ptr); + } else { + ptr = stat - wstat + (addr & 0xff); + return *ptr; + } +} + +word32 +get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1; + word32 addrp1, wstat, ret, tmp1, addr_latch; + + addr_latch = 0; + if(addr_latch != 0) { // "Use" addr_latch to avoid warning + } + dfcyc = *dcycs_ptr; + dplus_1 = fplus_ptr->dplus_1; + dplus_x_m1 = fplus_ptr->dplus_x_minus_1; + GET_MEMORY8(addr, tmp1); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + GET_MEMORY8(addrp1, ret); + *dcycs_ptr = dfcyc; + return (ret << 8) + (tmp1); +} + +word32 +get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1; + word32 addrp1, addrp2, wstat, addr_latch, ret, tmp1, tmp2; + + addr_latch = 0; + if(addr_latch != 0) { // "Use" addr_latch to avoid warning + } + dfcyc = *dcycs_ptr; + dplus_1 = fplus_ptr->dplus_1; + dplus_x_m1 = fplus_ptr->dplus_x_minus_1; + GET_MEMORY8(addr, tmp1); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + GET_MEMORY8(addrp1, tmp2); + addrp2 = addr + 2; + if(in_bank) { + addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff); + } + GET_MEMORY8(addrp2, ret); + *dcycs_ptr = dfcyc; + return (ret << 16) + (tmp2 << 8) + tmp1; +} + +void +set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr, + dword64 dplus_x_m1) +{ + byte *ptr; + dword64 dfcyc; + word32 setmem_tmp1, tmp1, tmp2, wstat; + + wstat = PTR2WORD(stat) & 0xff; + dfcyc = *dcycs_ptr; + if(wstat & (1 << (31 - BANK_BREAK_BIT))) { + check_breakpoints(addr, dfcyc, 0, 2); + } + ptr = stat - wstat + ((addr) & 0xff); + if(wstat & (1 << (31 - BANK_IO2_BIT))) { + FCYCLES_ROUND; + *dcycs_ptr = dfcyc; + set_memory_io((addr), val, dcycs_ptr); + } else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) { + if(g_limit_speed) { + FCYCLES_ROUND; + *dcycs_ptr = dfcyc; + } + tmp1 = (addr & 0xffff); + setmem_tmp1 = g_slow_memory_ptr[tmp1]; + *ptr = val; + g_slow_memory_ptr[tmp1] = val; + if(setmem_tmp1 != ((val) & 0xff)) { + g_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |= + (1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31)); + } + } else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) { + if(g_limit_speed) { + FCYCLES_ROUND; + *dcycs_ptr = dfcyc; + } + tmp2 = (addr & 0xffff); + tmp1 = 0x10000 + tmp2; + setmem_tmp1 = g_slow_memory_ptr[tmp1]; + *ptr = val; + g_slow_memory_ptr[tmp1] = val; + if(setmem_tmp1 != ((val) & 0xff)) { + g_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |= + (1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31)); + if((tmp1 & 0xff00) == 0x9d00) { + scb_changed(dfcyc, tmp1, val, setmem_tmp1); + } + } + } else { + /* breakpoint only */ + *ptr = val; + } +} + +#define LOG_PC_MACRO() +#define LOG_PC_MACRO2() +#define LOG_DATA_MACRO(addr, val, size, in_stat) + +void +set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, + dword64 dplus_1, dword64 dplus_x_m1, int in_bank) +{ + byte *ptr; + byte *stat; + dword64 dfcyc, dcycles_tmp1; + word32 addrp1, wstat; + + dfcyc = *dcycs_ptr; + SET_MEMORY8(addr, val); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + SET_MEMORY8(addrp1, val >> 8); + + *dcycs_ptr = dfcyc; +} + +void +set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + byte *stat; + dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1; + word32 addrp1, addrp2; + word32 wstat; + + dfcyc = *dcycs_ptr; + dplus_1 = fplus_ptr->dplus_1; + dplus_x_m1 = fplus_ptr->dplus_x_minus_1; + SET_MEMORY8(addr, val); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + SET_MEMORY8(addrp1, val >> 8); + addrp2 = addr + 2; + if(in_bank) { + addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff); + } + SET_MEMORY8(addrp2, val >> 16); + + *dcycs_ptr = dfcyc; +} + +word32 +get_memory_c(word32 addr) +{ + byte *stat, *ptr; + dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1; + word32 addr_latch, wstat, ret; + + dfcyc = 0; + dplus_1 = 0; + dplus_x_m1 = 0; + addr_latch = 0; + if(addr_latch != 0) { // "Use" addr_latch to avoid warning + } + GET_MEMORY8(addr, ret); + return ret; +} + +word32 +get_memory16_c(word32 addr) +{ + return get_memory_c(addr) + + (get_memory_c(addr+1) << 8); +} + +word32 +get_memory24_c(word32 addr) +{ + return get_memory_c(addr) + + (get_memory_c(addr+1) << 8) + + (get_memory_c(addr+2) << 16); +} + +void +set_memory_c(word32 addr, word32 val, int do_log) +{ + byte *stat, *ptr; + dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1; + word32 wstat; + + dfcyc = g_cur_dfcyc; + dplus_1 = 0; + dplus_x_m1 = 0; + SET_MEMORY8(addr, val); + if(g_log_pc_enable && do_log) { + LOG_DATA_MACRO_ACT(addr, val, 8, stat) + } +} + +void +set_memory16_c(word32 addr, word32 val, int do_log) +{ + byte *stat, *ptr; + dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1; + word32 wstat; + + dfcyc = g_cur_dfcyc; + dplus_1 = 0; + dplus_x_m1 = 0; + SET_MEMORY16(addr, val, 0); + if(g_log_pc_enable && do_log) { + LOG_DATA_MACRO_ACT(addr, val, 16, stat) + } +} + +void +set_memory24_c(word32 addr, word32 val) +{ + set_memory_c(addr, val, 1); + set_memory_c(addr + 1, val >> 8, 1); + set_memory_c(addr + 2, val >> 16, 1); +} + +word32 +do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub) +{ + word32 sum, carry, overflow; + word32 zero; + int decimal; + + overflow = 0; + decimal = psr & 8; + if(sub) { + in2 = (in2 ^ 0xff); + } + if(!decimal) { + sum = (in1 & 0xff) + in2 + (psr & 1); + overflow = ((sum ^ in2) >> 1) & 0x40; + } else { + /* decimal */ + sum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1); + if(sub) { + if(sum < 0x10) { + sum = (sum - 0x6) & 0xf; + } + } else { + if(sum >= 0xa) { + sum = (sum - 0xa) | 0x10; + } + } + + sum = (in1 & 0xf0) + (in2 & 0xf0) + sum; + overflow = ((sum >> 2) ^ (sum >> 1)) & 0x40; + if(sub) { + if(sum < 0x100) { + sum = (sum + 0xa0) & 0xff; + } + } else { + if(sum >= 0xa0) { + sum += 0x60; + } + } + } + + zero = ((sum & 0xff) == 0); + carry = (sum >= 0x100); + if((in1 ^ in2) & 0x80) { + overflow = 0; + } + + psr = psr & (~0xc3); + psr = psr + (sum & 0x80) + overflow + (zero << 1) + carry; + + return (psr << 16) + (sum & 0xff); +} + +word32 +do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub) +{ + word32 sum, carry, overflow; + word32 tmp1, tmp2; + word32 zero; + int decimal; + + overflow = 0; + decimal = psr & 8; + if(!decimal) { + if(sub) { + in2 = (in2 ^ 0xffff); + } + sum = in1 + in2 + (psr & 1); + overflow = ((sum ^ in2) >> 9) & 0x40; + } else { + /* decimal */ + if(sub) { + tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub); + psr = (tmp1 >> 16); + tmp2 = do_adc_sbc8((in1 >> 8) & 0xff, + (in2 >> 8) & 0xff, psr, sub); + in2 = (in2 ^ 0xfffff); + } else { + tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub); + psr = (tmp1 >> 16); + tmp2 = do_adc_sbc8((in1 >> 8) & 0xff, + (in2 >> 8) &0xff, psr, sub); + } + sum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) + + (((tmp2 >> 16) & 1) << 16); + overflow = (tmp2 >> 16) & 0x40; + } + + zero = ((sum & 0xffff) == 0); + carry = (sum >= 0x10000); + if((in1 ^ in2) & 0x8000) { + overflow = 0; + } + + psr = psr & (~0xc3); + psr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry; + + return (psr << 16) + (sum & 0xffff); +} + +void +fixed_memory_ptrs_init() +{ + /* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */ + /* and rom_cards_ptr */ + + g_slow_memory_ptr = memalloc_align(128*1024, 0, 0); + g_dummy_memory1_ptr = memalloc_align(256, 1024, 0); + g_rom_fc_ff_ptr = memalloc_align(256*1024, 512, 0); + g_rom_cards_ptr = memalloc_align(16*256, 256, 0); + +#if 0 + printf("g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\n", + (word32)g_memory_ptr, (word32)g_dummy_memory1_ptr, + (word32)g_slow_memory_ptr); + printf("g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\n", + (word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr); + printf("page_info_rd = %08x, page_info_wr end = %08x\n", + (word32)&(page_info_rd_wr[0]), + (word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr)); +#endif +} + +word32 +get_itimer() +{ +#if defined(__i386) && defined(__GNUC__) + /* Here's my bad ia32 asm code to do rdtsc */ + /* Linux source uses: */ + /* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */ + /* asm volatile("rdtsc" : "=%eax"(ret) : : "%edx"); */ + + /* GCC bug report 2001-03/msg00786.html used: */ + /*register dword64 dtmp; */ + /*asm volatile ("rdtsc" : "=A" (dtmp)); */ + /*return (word32)dtmp; */ + + register word32 ret; + + asm volatile ("rdtsc;movl %%eax,%0" : "=r"(ret) : : "%eax","%edx"); + + return ret; +#else +# if defined(__POWERPC__) && defined(__GNUC__) + register word32 ret; + + asm volatile ("mftb %0" : "=r"(ret)); + return ret; +# else +# if defined(__x86_64__) + register word32 ret, hi; + //ret = __rdtsc(); + asm volatile ("rdtsc" : "=a" (ret), "=d" (hi)); + return ret; +# else +# if defined(__aarch64__) /* 64-bit ARM architecture */ + register dword64 ret; + asm volatile("mrs %0,CNTVCT_EL0" : "=r"(ret)); + return ret; +# else + return 0; +# endif +# endif +# endif +#endif +} + +void +engine_recalc_events() +{ + g_dcycles_end = 0; // End inner loop + g_engine_recalc_event++; +} + +void +set_halt_act(int val) +{ + if((val == 1) && g_ignore_halts && !g_user_halt_bad) { + g_code_red++; + } else { + if(g_halt_sim == 0) { + debugger_update_list_kpc(); + } + g_halt_sim |= val; + if(g_halt_sim) { + video_set_active(&g_debugwin_kimage, 1); + } + g_dcycles_end = 0; + } +} + +void +clr_halt_act() +{ + g_halt_sim = 0; +} + +word32 +get_remaining_operands(word32 addr, word32 opcode, word32 psr, + dword64 *dcyc_ptr, Fplus *fplus_ptr) +{ + byte *stat, *ptr; + dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1; + word32 addr_latch, wstat, save_addr, arg, addrp1; + int size; + + dfcyc = *dcyc_ptr; + dplus_1 = fplus_ptr->dplus_1; + dplus_x_m1 = fplus_ptr->dplus_x_minus_1; + addr_latch = 0; + if(addr_latch != 0) { // "Use" addr_latch to avoid warning + } + + size = size_tab[opcode]; + + addrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff); + switch(size) { + case 0: + // Always read pc+1 for single-byte opcodes + GET_MEMORY8(addrp1, arg); + arg = 0; /* no args */ + break; + case 1: + GET_MEMORY8(addrp1, arg); + break; /* 1 arg, already done */ + case 2: + GET_MEMORY16(addrp1, arg, 1); + dfcyc -= dplus_1; + break; + case 3: + GET_MEMORY24(addrp1, arg, 1); + dfcyc = dfcyc - dplus_1 - dplus_1; + break; + case 4: + if(psr & 0x20) { + GET_MEMORY8(addrp1, arg); + } else { + GET_MEMORY16(addrp1, arg, 1); + dfcyc -= dplus_1; + } + break; + case 5: + if(psr & 0x10) { + GET_MEMORY8(addrp1, arg); + } else { + GET_MEMORY16(addrp1, arg, 1); + dfcyc -= dplus_1; + } + break; + default: + printf("Unknown size: %d\n", size); + arg = 0; + exit(-2); + } + *dcyc_ptr = dfcyc; + + return arg; +} + +#define FETCH_OPCODE \ + addr = kpc; \ + CYCLES_PLUS_2; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + arg_ptr = ptr; \ + opcode = *ptr; \ + if((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\ + CYCLES_MINUS_1; \ + if(wstat & BANK_BREAK) { \ + check_breakpoints(addr, dfcyc, stack, 4); \ + } \ + if(wstat & (1 << (31 - BANK_IO2_BIT))) { \ + FCYCLES_ROUND; \ + dcycles_tmp1 = dfcyc; \ + opcode = get_memory_io((addr), &dcycles_tmp1); \ + dfcyc = dcycles_tmp1; \ + } else { \ + opcode = *ptr; \ + } \ + dcycles_tmp1 = dfcyc; \ + arg = get_remaining_operands(addr, opcode, psr, \ + &dcycles_tmp1, fplus_ptr); \ + dfcyc = dcycles_tmp1; \ + arg_ptr = (byte *)&tmp_bytes; \ + arg_ptr[1] = arg; \ + arg_ptr[2] = arg >> 8; \ + arg_ptr[3] = arg >> 16; \ + } + + +#define ACC8 +#define IS_ACC16 0 +#define ENGINE_TYPE enter_engine_acc8 +#include "engine.h" +// The above creates enter_engine_acc8 + +#undef ACC8 +#undef IS_ACC16 +#undef ENGINE_TYPE +#define IS_ACC16 1 +#define ENGINE_TYPE enter_engine_acc16 +#include "engine.h" +// The above creates enter_engine_acc16 + +#undef LOG_PC_MACRO +#undef LOG_PC_MACRO2 +#undef LOG_DATA_MACRO + +#define LOG_PC_MACRO() \ + tmp_pc_ptr = g_log_pc_ptr; \ + tmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc; \ + tmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] + \ + (arg_ptr[2] << 8) + (arg_ptr[3] << 16); \ + tmp_pc_ptr->dfcyc = dfcyc - dplus_1 * 2; + +#define LOG_PC_MACRO2() \ + tmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) | acc | \ + ((neg7 & 0x80) << 16) | ((!zero) << 17); \ + tmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg; \ + tmp_pc_ptr->stack_direct = (stack << 16) + direct; \ + tmp_pc_ptr++; \ + if(tmp_pc_ptr >= g_log_pc_end_ptr) { \ + tmp_pc_ptr = g_log_pc_start_ptr; \ + } \ + g_log_pc_ptr = tmp_pc_ptr; + +#define LOG_DATA_MACRO(in_addr, in_val, in_size, in_stat) \ + LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat) + +#undef ACC8 +#undef IS_ACC16 +#undef ENGINE_TYPE +#define ACC8 +#define IS_ACC16 0 +#define ENGINE_TYPE enter_engine_acc8_log +#include "engine.h" +// The above creates enter_engine_acc8_log + +#undef ACC8 +#undef IS_ACC16 +#undef ENGINE_TYPE +#define IS_ACC16 1 +#define ENGINE_TYPE enter_engine_acc16_log +#include "engine.h" +// The above creates enter_engine_acc16_log + + +int +enter_engine(Engine_reg *engine_ptr) +{ + dword64 dcycles_end_save; + int ret; + + dcycles_end_save = g_dcycles_end; + while(1) { + if(g_log_pc_enable) { + if(engine_ptr->psr & 0x20) { // 8-bit accumulator + ret = enter_engine_acc8_log(engine_ptr); + } else { + ret = enter_engine_acc16_log(engine_ptr); + } + } else { + if(engine_ptr->psr & 0x20) { // 8-bit accumulator + ret = enter_engine_acc8(engine_ptr); + } else { + ret = enter_engine_acc16(engine_ptr); + } + } + if((ret == RET_PSR) && !g_halt_sim) { + g_dcycles_end = dcycles_end_save; + continue; + } + return ret; + } +} + diff --git a/gsplus/src/instable.h b/gsplus/src/instable.h new file mode 100644 index 0000000..4795631 --- /dev/null +++ b/gsplus/src/instable.h @@ -0,0 +1,1555 @@ +// "@(#)$KmKId: instable.h,v 1.121 2023-11-12 15:31:14+00 kentd Exp $" + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +case 0x00: /* brk */ + GET_1BYTE_ARG; + g_num_brk++; + INC_KPC_2; + psr = (psr & (~0x82)) | (neg7 & 0x80) | ((!zero) << 1); + if(psr & 0x100) { + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + tmp1 = 0xfffffe; + dbank = 0; + } else { + PUSH8(kpc >> 16); + PUSH16(kpc); + PUSH8(psr & 0xff); + tmp1 = 0xffffe6; + halt_printf("Halting for native break!\n"); + } + tmp1 = moremem_fix_vector_pull(tmp1); + GET_MEMORY16(tmp1, kpc, 0); + kpc = kpc & 0xffff; + psr |= 0x4; + psr &= ~(0x8); + break; + +case 0x01: /* ORA (Dloc,X) */ + GET_DLOC_X_IND_RD(); + ORA_INST(); + break; + +case 0x02: /* COP */ + g_num_cop++; + INC_KPC_2; + psr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1); + if(psr & 0x100) { + halt_printf("Halting for emul COP at %04x\n", kpc); + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + tmp1 = 0xfffff4; + dbank = 0; + } else { + PUSH8(kpc >> 16); + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + tmp1 = 0xffffe4; + } + tmp1 = moremem_fix_vector_pull(tmp1); + GET_MEMORY16(tmp1, kpc, 0); + kpc = kpc & 0xffff; + psr |= 4; + psr &= ~(0x8); + break; + +case 0x03: /* ORA Disp8,S */ + GET_DISP8_S_RD(); + ORA_INST(); + break; + +case 0x04: /* TSB Dloc */ + GET_DLOC_RD_RMW(); + TSB_INST(1); + break; + +case 0x05: /* ORA Dloc */ + GET_DLOC_RD(); + ORA_INST(); + break; + +case 0x06: /* ASL Dloc */ + GET_DLOC_RD_RMW(); + ASL_INST(1); + break; + +case 0x07: /* ORA [Dloc] */ + GET_DLOC_L_IND_RD(); + ORA_INST(); + break; + +case 0x08: /* PHP */ + INC_KPC_1; + psr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1); + PUSH8(psr); + break; + +case 0x09: /* ORA #imm */ + GET_IMM_MEM(); + ORA_INST(); + break; + +case 0x0a: /* ASL a */ + INC_KPC_1; + tmp1 = acc + acc; +#ifdef ACC8 + SET_CARRY8(tmp1); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(acc & 0xff); +#else + SET_CARRY16(tmp1); + acc = tmp1 & 0xffff; + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x0b: /* PHD */ + INC_KPC_1; + PUSH16_UNSAFE(direct); + break; + +case 0x0c: /* TSB abs */ + GET_ABS_RD_RMW(); + TSB_INST(0); + break; + +case 0x0d: /* ORA abs */ + GET_ABS_RD(); + ORA_INST(); + break; + +case 0x0e: /* ASL abs */ + GET_ABS_RD_RMW(); + ASL_INST(0); + break; + +case 0x0f: /* ORA long */ + GET_LONG_RD(); + ORA_INST(); + break; + +case 0x10: /* BPL disp8 */ + BRANCH_DISP8((neg7 & 0x80) == 0); + break; + +case 0x11: /* ORA (Dloc),y */ + GET_DLOC_IND_Y_RD(); + ORA_INST(); + break; + +case 0x12: /* ORA (Dloc) */ + GET_DLOC_IND_RD(); + ORA_INST(); + break; + +case 0x13: /* ORA (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + ORA_INST(); + break; + +case 0x14: /* TRB Dloc */ + GET_DLOC_RD_RMW(); + TRB_INST(1); + break; + +case 0x15: /* ORA Dloc,x */ + GET_DLOC_X_RD(); + ORA_INST(); + break; + +case 0x16: /* ASL Dloc,X */ + GET_DLOC_X_RD_RMW(); + ASL_INST(1); + break; + +case 0x17: /* ORA [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + ORA_INST(); + break; + +case 0x18: /* CLC */ + psr = psr & (~1); + INC_KPC_1; + break; + +case 0x19: /* ORA abs,y */ + GET_ABS_Y_RD(); + ORA_INST(); + break; + +case 0x1a: /* INC a */ + INC_KPC_1; +#ifdef ACC8 + acc = (acc & 0xff00) | ((acc + 1) & 0xff); + SET_NEG_ZERO8(acc & 0xff); +#else + acc = (acc + 1) & 0xffff; + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x1b: /* TCS */ + stack = acc; + INC_KPC_1; + if(psr & 0x100) { + stack = (stack & 0xff) + 0x100; + } + break; + +case 0x1c: /* TRB Abs */ + GET_ABS_RD_RMW(); + TRB_INST(0); + break; + +case 0x1d: /* ORA Abs,X */ + GET_ABS_X_RD(); + ORA_INST(); + break; + +case 0x1e: /* ASL Abs,X */ + GET_ABS_X_RD_RMW(); + ASL_INST(0); + break; + +case 0x1f: /* ORA Long,X */ + GET_LONG_X_RD(); + ORA_INST(); + break; + +case 0x20: /* JSR abs */ + GET_2BYTE_ARG; + INC_KPC_2; + PUSH16(kpc); + kpc = (kpc & 0xff0000) + arg; + CYCLES_PLUS_2; + break; + +case 0x21: /* AND (Dloc,X) */ + GET_DLOC_X_IND_RD(); + AND_INST(); + break; + +case 0x22: /* JSL Long */ + GET_3BYTE_ARG; + tmp1 = arg; + CYCLES_PLUS_3; + INC_KPC_3; + PUSH24_UNSAFE(kpc); + kpc = tmp1 & 0xffffff; + break; + +case 0x23: /* AND Disp8,S */ + GET_DISP8_S_RD(); + AND_INST(); + break; + +case 0x24: /* BIT Dloc */ + GET_DLOC_RD(); + BIT_INST(); + break; + +case 0x25: /* AND Dloc */ + GET_DLOC_RD(); + AND_INST(); + break; + +case 0x26: /* ROL Dloc */ + GET_DLOC_RD_RMW(); + ROL_INST(1); + break; + +case 0x27: /* AND [Dloc] */ + GET_DLOC_L_IND_RD(); + AND_INST(); + break; + +case 0x28: /* PLP */ + PULL8(tmp1); + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_1; + psr = (psr & ~0xff) | (tmp1 & 0xff); + zero = !(psr & 2); + neg7 = psr; + UPDATE_PSR(psr, tmp2); + break; + +case 0x29: /* AND #imm */ + GET_IMM_MEM(); + AND_INST(); + break; + +case 0x2a: /* ROL a */ + INC_KPC_1; +#ifdef ACC8 + tmp1 = ((acc & 0xff) << 1) + (psr & 1); + SET_CARRY8(tmp1); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +#else + tmp1 = (acc << 1) + (psr & 1); + SET_CARRY16(tmp1); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x2b: /* PLD */ + INC_KPC_1; + PULL16_UNSAFE(direct); + CYCLES_PLUS_1; + SET_NEG_ZERO16(direct); + break; + +case 0x2c: /* BIT abs */ + GET_ABS_RD(); + BIT_INST(); + break; + +case 0x2d: /* AND abs */ + GET_ABS_RD(); + AND_INST(); + break; + +case 0x2e: /* ROL abs */ + GET_ABS_RD_RMW(); + ROL_INST(0); + break; + +case 0x2f: /* AND long */ + GET_LONG_RD(); + AND_INST(); + break; + +case 0x30: /* BMI disp8 */ + BRANCH_DISP8(neg7 & 0x80); + break; + +case 0x31: /* AND (Dloc),y */ + GET_DLOC_IND_Y_RD(); + AND_INST(); + break; + +case 0x32: /* AND (Dloc) */ + GET_DLOC_IND_RD(); + AND_INST(); + break; + +case 0x33: /* AND (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + AND_INST(); + break; + +case 0x34: /* BIT Dloc,x */ + GET_DLOC_X_RD(); + BIT_INST(); + break; + +case 0x35: /* AND Dloc,x */ + GET_DLOC_X_RD(); + AND_INST(); + break; + +case 0x36: /* ROL Dloc,X */ + GET_DLOC_X_RD_RMW(); + ROL_INST(1); + break; + +case 0x37: /* AND [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + AND_INST(); + break; + +case 0x38: /* SEC */ + psr = psr | 1; + INC_KPC_1; + break; + +case 0x39: /* AND abs,y */ + GET_ABS_Y_RD(); + AND_INST(); + break; + +case 0x3a: /* DEC a */ + INC_KPC_1; +#ifdef ACC8 + acc = (acc & 0xff00) | ((acc - 1) & 0xff); + SET_NEG_ZERO8(acc & 0xff); +#else + acc = (acc - 1) & 0xffff; + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x3b: /* TSC */ +/* set N,Z according to 16 bit acc */ + INC_KPC_1; + acc = stack; + SET_NEG_ZERO16(acc); + break; + +case 0x3c: /* BIT Abs,x */ + GET_ABS_X_RD(); + BIT_INST(); + break; + +case 0x3d: /* AND Abs,X */ + GET_ABS_X_RD(); + AND_INST(); + break; + +case 0x3e: /* ROL Abs,X */ + GET_ABS_X_RD_RMW(); + ROL_INST(0); + break; + +case 0x3f: /* AND Long,X */ + GET_LONG_X_RD(); + AND_INST(); + break; + +case 0x40: /* RTI */ + CYCLES_PLUS_1 + if(psr & 0x100) { + PULL24(tmp1); + kpc = (kpc & 0xff0000) + ((tmp1 >> 8) & 0xffff); + tmp2 = psr; + psr = (psr & ~0xff) + (tmp1 & 0xff); + neg7 = psr; + zero = !(psr & 2); + UPDATE_PSR(psr, tmp2); + } else { + PULL8(tmp1); + tmp2 = psr; + psr = (tmp1 & 0xff); + neg7 = psr; + zero = !(psr & 2); + PULL24(kpc); + UPDATE_PSR(psr, tmp2); + } + break; + +case 0x41: /* EOR (Dloc,X) */ + GET_DLOC_X_IND_RD(); + EOR_INST(); + break; + +case 0x42: /* WDM */ + GET_2BYTE_ARG; + INC_KPC_2; + if(arg < 0x100) { // Next byte is 00 + INC_KPC_1; // Skip over the BRK + } + FINISH(RET_WDM, arg); + break; + +case 0x43: /* EOR Disp8,S */ + GET_DISP8_S_RD(); + EOR_INST(); + break; + +case 0x44: /* MVP */ + GET_2BYTE_ARG; + /* arg & 0xff = dest bank, arg & 0xff00 = src bank */ + if(psr & 0x100) { + halt_printf("MVP in emulation!\n"); + break; + } + dbank = arg & 0xff; + tmp1 = (arg >> 8) & 0xff; + CYCLES_PLUS_1; + GET_MEMORY8((tmp1 << 16) + xreg, arg); + SET_MEMORY8((dbank << 16) + yreg, arg); + CYCLES_PLUS_2; + xreg = (xreg - 1) & 0xffff; + yreg = (yreg - 1) & 0xffff; + if(psr & 0x10) { // 8-bit index registers + xreg = xreg & 0xff; + yreg = yreg & 0xff; + } + acc = (acc - 1) & 0xffff; + if(acc == 0xffff) { + INC_KPC_3; + } + break; + +case 0x45: /* EOR Dloc */ + GET_DLOC_RD(); + EOR_INST(); + break; + +case 0x46: /* LSR Dloc */ + GET_DLOC_RD_RMW(); + LSR_INST(1); + break; + +case 0x47: /* EOR [Dloc] */ + GET_DLOC_L_IND_RD(); + EOR_INST(); + break; + +case 0x48: /* PHA */ + INC_KPC_1; +#ifdef ACC8 + PUSH8(acc); +#else + PUSH16(acc); +#endif + break; + +case 0x49: /* EOR #imm */ + GET_IMM_MEM(); + EOR_INST(); + break; + +case 0x4a: /* LSR a */ + INC_KPC_1; +#ifdef ACC8 + tmp1 = ((acc & 0xff) >> 1); + SET_CARRY8(acc << 8); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +#else + tmp1 = (acc >> 1); + SET_CARRY8((acc << 8)); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x4b: /* PHK */ + PUSH8(kpc >> 16); + INC_KPC_1; + break; + +case 0x4c: /* JMP abs */ + GET_2BYTE_ARG; + CYCLES_PLUS_1; + kpc = (kpc & 0xff0000) + arg; + break; + +case 0x4d: /* EOR abs */ + GET_ABS_RD(); + EOR_INST(); + break; + +case 0x4e: /* LSR abs */ + GET_ABS_RD_RMW(); + LSR_INST(0); + break; + +case 0x4f: /* EOR long */ + GET_LONG_RD(); + EOR_INST(); + break; + +case 0x50: /* BVC disp8 */ + BRANCH_DISP8((psr & 0x40) == 0); + break; + +case 0x51: /* EOR (Dloc),y */ + GET_DLOC_IND_Y_RD(); + EOR_INST(); + break; + +case 0x52: /* EOR (Dloc) */ + GET_DLOC_IND_RD(); + EOR_INST(); + break; + +case 0x53: /* EOR (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + EOR_INST(); + break; + +case 0x54: /* MVN */ + GET_2BYTE_ARG; + /* arg & 0xff = dest bank, arg & 0xff00 = src bank */ + if(psr & 0x100) { + halt_printf("MVN in emulation!\n"); + break; + } + dbank = arg & 0xff; + tmp1 = (arg >> 8) & 0xff; + CYCLES_PLUS_1; + GET_MEMORY8((tmp1 << 16) + xreg, arg); + SET_MEMORY8((dbank << 16) + yreg, arg); + CYCLES_PLUS_2; + xreg = (xreg + 1) & 0xffff; + yreg = (yreg + 1) & 0xffff; + if(psr & 0x10) { // 8-bit index registers + xreg = xreg & 0xff; + yreg = yreg & 0xff; + } + acc = (acc - 1) & 0xffff; + if(acc == 0xffff) { + INC_KPC_3; + } + break; + +case 0x55: /* EOR Dloc,x */ + GET_DLOC_X_RD(); + EOR_INST(); + break; + +case 0x56: /* LSR Dloc,X */ + GET_DLOC_X_RD_RMW(); + LSR_INST(1); + break; + +case 0x57: /* EOR [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + EOR_INST(); + break; + +case 0x58: /* CLI */ + psr = psr & (~4); + INC_KPC_1; + if(((psr & 0x4) == 0) && g_irq_pending) { + FINISH(RET_IRQ, 0); + } + break; + +case 0x59: /* EOR abs,y */ + GET_ABS_Y_RD(); + EOR_INST(); + break; + +case 0x5a: /* PHY */ + INC_KPC_1; + if(psr & 0x10) { + PUSH8(yreg); + } else { + PUSH16(yreg); + } + break; + +case 0x5b: /* TCD */ + INC_KPC_1; + direct = acc; + SET_NEG_ZERO16(acc); + break; + +case 0x5c: /* JMP Long */ + GET_3BYTE_ARG; + CYCLES_PLUS_2; + kpc = arg; + break; + +case 0x5d: /* EOR Abs,X */ + GET_ABS_X_RD(); + EOR_INST(); + break; + +case 0x5e: /* LSR Abs,X */ + GET_ABS_X_RD_RMW(); + LSR_INST(0); + break; + +case 0x5f: /* EOR Long,X */ + GET_LONG_X_RD(); + EOR_INST(); + break; + +case 0x60: /* RTS */ + CYCLES_PLUS_2 + PULL16(tmp1); + kpc = (kpc & 0xff0000) + ((tmp1 + 1) & 0xffff); + break; + +case 0x61: /* ADC (Dloc,X) */ + GET_DLOC_X_IND_RD(); + ADC_INST(); + break; + +case 0x62: /* PER */ + GET_2BYTE_ARG; + CYCLES_PLUS_2; + INC_KPC_3; + PUSH16_UNSAFE(kpc + arg); + break; + +case 0x63: /* ADC Disp8,S */ + GET_DISP8_S_RD(); + ADC_INST(); + break; + +case 0x64: /* STZ Dloc */ + GET_DLOC_ADDR(); + STZ_INST(1); + break; + +case 0x65: /* ADC Dloc */ + GET_DLOC_RD(); + ADC_INST(); + break; + +case 0x66: /* ROR Dloc */ + GET_DLOC_RD_RMW(); + ROR_INST(1); + break; + +case 0x67: /* ADC [Dloc] */ + GET_DLOC_L_IND_RD(); + ADC_INST(); + break; + +case 0x68: /* PLA */ + INC_KPC_1; + CYCLES_PLUS_1; +#ifdef ACC8 + PULL8(tmp1); + acc = (acc & 0xff00) + tmp1; + SET_NEG_ZERO8(tmp1); +#else + PULL16(tmp1); + acc = tmp1; + SET_NEG_ZERO16(tmp1); +#endif + break; + +case 0x69: /* ADC #imm */ + GET_IMM_MEM(); + ADC_INST(); + break; + +case 0x6a: /* ROR a */ + INC_KPC_1; +#ifdef ACC8 + tmp1 = ((acc & 0xff) >> 1) + ((psr & 1) << 7); + SET_CARRY8((acc << 8)); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +#else + tmp1 = (acc >> 1) + ((psr & 1) << 15); + SET_CARRY16((acc << 16)); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +#endif + break; + +case 0x6b: /* RTL */ + CYCLES_PLUS_1; + PULL24_UNSAFE(tmp1); + kpc = (tmp1 & 0xff0000) + ((tmp1 + 1) & 0xffff); + break; + +case 0x6c: /* JMP (abs) */ + GET_2BYTE_ARG; + CYCLES_PLUS_1; + GET_MEMORY16(arg, tmp1, 1); + if((psr & 0x100) && g_emul_6502_ind_page_cross_bug && + ((arg & 0xff) == 0xff)) { + GET_MEMORY8(arg - 0xff, tmp2); + tmp1 = (tmp1 & 0xff) + (tmp2 << 8); + halt_printf("Halting for emul ind jmp\n"); + } + kpc = (kpc & 0xff0000) + tmp1; + break; + +case 0x6d: /* ADC abs */ + GET_ABS_RD(); + ADC_INST(); + break; + +case 0x6e: /* ROR abs */ + GET_ABS_RD_RMW(); + ROR_INST(0); + break; + +case 0x6f: /* ADC long */ + GET_LONG_RD(); + ADC_INST(); + break; + +case 0x70: /* BVS disp8 */ + BRANCH_DISP8((psr & 0x40)); + break; + +case 0x71: /* ADC (Dloc),y */ + GET_DLOC_IND_Y_RD(); + ADC_INST(); + break; + +case 0x72: /* ADC (Dloc) */ + GET_DLOC_IND_RD(); + ADC_INST(); + break; + +case 0x73: /* ADC (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + ADC_INST(); + break; + +case 0x74: /* STZ Dloc,x */ + GET_1BYTE_ARG; + GET_DLOC_X_WR(); + STZ_INST(1); + break; + +case 0x75: /* ADC Dloc,x */ + GET_DLOC_X_RD(); + ADC_INST(); + break; + +case 0x76: /* ROR Dloc,X */ + GET_DLOC_X_RD_RMW(); + ROR_INST(1); + break; + +case 0x77: /* ADC [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + ADC_INST(); + break; + +case 0x78: /* SEI */ + psr = psr | 4; + INC_KPC_1; + break; + +case 0x79: /* ADC abs,y */ + GET_ABS_Y_RD(); + ADC_INST(); + break; + +case 0x7a: /* PLY */ + INC_KPC_1; + CYCLES_PLUS_1 + if(psr & 0x10) { + PULL8(yreg); + SET_NEG_ZERO8(yreg); + } else { + PULL16(yreg); + SET_NEG_ZERO16(yreg); + } + break; + +case 0x7b: /* TDC */ + INC_KPC_1; + acc = direct; + SET_NEG_ZERO16(direct); + break; + +case 0x7c: /* JMP (Abs,x) */ +/* always access kbank, xreg cannot wrap into next bank */ + GET_2BYTE_ARG; + arg = (kpc & 0xff0000) + ((xreg + arg) & 0xffff); + CYCLES_PLUS_2; + GET_MEMORY16(arg, tmp1, 1); + kpc = (kpc & 0xff0000) + tmp1; + break; + +case 0x7d: /* ADC Abs,X */ + GET_ABS_X_RD(); + ADC_INST(); + break; + +case 0x7e: /* ROR Abs,X */ + GET_ABS_X_RD_RMW(); + ROR_INST(0); + break; + +case 0x7f: /* ADC Long,X */ + GET_LONG_X_RD(); + ADC_INST(); + break; + +case 0x80: /* BRA */ + BRANCH_DISP8(1); + break; + +case 0x81: /* STA (Dloc,X) */ + GET_DLOC_X_IND_ADDR(); + STA_INST(0); + break; + +case 0x82: /* BRL disp16 */ + GET_2BYTE_ARG; + CYCLES_PLUS_1; + kpc = (kpc & 0xff0000) + ((kpc + 3 + arg) & 0xffff); + break; + +case 0x83: /* STA Disp8,S */ + GET_DISP8_S_ADDR(); + STA_INST(1); + break; + +case 0x84: /* STY Dloc */ + GET_DLOC_ADDR(); + STY_INST(1); + break; + +case 0x85: /* STA Dloc */ + GET_DLOC_ADDR(); + STA_INST(1); + break; + +case 0x86: /* STX Dloc */ + GET_DLOC_ADDR(); + STX_INST(1); + break; + +case 0x87: /* STA [Dloc] */ + GET_DLOC_L_IND_ADDR(); + STA_INST(0); + break; + +case 0x88: /* DEY */ + INC_KPC_1; + SET_INDEX_REG(yreg - 1, yreg); + break; + +case 0x89: /* BIT #imm */ + GET_IMM_MEM(); +#ifdef ACC8 + zero = (acc & arg) & 0xff; +#else + zero = (acc & arg) & 0xffff; +#endif + break; + +case 0x8a: /* TXA */ + INC_KPC_1; + arg = xreg; + LDA_INST(); + break; + +case 0x8b: /* PHB */ + INC_KPC_1; + PUSH8(dbank); + break; + +case 0x8c: /* STY abs */ + GET_ABS_ADDR(); + STY_INST(0); + break; + +case 0x8d: /* STA abs */ + GET_ABS_ADDR(); + STA_INST(0); + break; + +case 0x8e: /* STX abs */ + GET_ABS_ADDR(); + STX_INST(0); + break; + +case 0x8f: /* STA long */ + GET_LONG_ADDR(); + STA_INST(0); + break; + +case 0x90: /* BCC disp8 */ + BRANCH_DISP8((psr & 0x01) == 0); + break; + +case 0x91: /* STA (Dloc),y */ + GET_DLOC_IND_Y_ADDR(1); + STA_INST(0); + break; + +case 0x92: /* STA (Dloc) */ + GET_DLOC_IND_ADDR(); + STA_INST(0); + break; + +case 0x93: /* STA (Disp8,s),y */ + GET_DISP8_S_IND_Y_ADDR(); + STA_INST(0); + break; + +case 0x94: /* STY Dloc,x */ + GET_DLOC_X_ADDR(); + STY_INST(1); + break; + +case 0x95: /* STA Dloc,x */ + GET_DLOC_X_ADDR(); + STA_INST(1); + break; + +case 0x96: /* STX Dloc,Y */ + GET_DLOC_Y_ADDR(); + STX_INST(1); + break; + +case 0x97: /* STA [Dloc],Y */ + GET_DLOC_L_IND_Y_ADDR(); + STA_INST(0); + break; + +case 0x98: /* TYA */ + INC_KPC_1; + arg = yreg; + LDA_INST(); + break; + +case 0x99: /* STA abs,y */ + GET_ABS_INDEX_ADDR(yreg, 1) + STA_INST(0); + break; + +case 0x9a: /* TXS */ + stack = xreg; + if(psr & 0x100) { + stack = 0x100 | (stack & 0xff); + } + INC_KPC_1; + break; + +case 0x9b: /* TXY */ + SET_INDEX_REG(xreg, yreg); + INC_KPC_1; + break; + +case 0x9c: /* STZ Abs */ + GET_ABS_ADDR(); + STZ_INST(0); + break; + +case 0x9d: /* STA Abs,X */ + GET_ABS_INDEX_ADDR(xreg, 1); + STA_INST(0); + break; + +case 0x9e: /* STZ Abs,X */ + GET_ABS_INDEX_ADDR(xreg, 1); + STZ_INST(0); + break; + +case 0x9f: /* STA Long,X */ + GET_LONG_X_ADDR_FOR_WR(); + STA_INST(0); + break; + +case 0xa0: /* LDY #imm */ + INC_KPC_2; + if((psr & 0x10) == 0) { + GET_2BYTE_ARG; + CYCLES_PLUS_1 + INC_KPC_1; + } else { + GET_1BYTE_ARG; + } + SET_INDEX_REG(arg, yreg); + break; + +case 0xa1: /* LDA (Dloc,X) */ + GET_DLOC_X_IND_RD(); + LDA_INST(); + break; + +case 0xa2: /* LDX #imm */ + INC_KPC_2; + if((psr & 0x10) == 0) { + GET_2BYTE_ARG; + CYCLES_PLUS_1 + INC_KPC_1; + } else { + GET_1BYTE_ARG; + } + SET_INDEX_REG(arg, xreg); + break; + +case 0xa3: /* LDA Disp8,S */ + GET_DISP8_S_RD(); + LDA_INST(); + break; + +case 0xa4: /* LDY Dloc */ + C_LDY_DLOC(); + break; + +case 0xa5: /* LDA Dloc */ + GET_DLOC_RD(); + LDA_INST(); + break; + +case 0xa6: /* LDX Dloc */ + C_LDX_DLOC(); + break; + +case 0xa7: /* LDA [Dloc] */ + GET_DLOC_L_IND_RD(); + LDA_INST(); + break; + +case 0xa8: /* TAY */ + INC_KPC_1; + SET_INDEX_REG(acc, yreg); + break; + +case 0xa9: /* LDA #imm */ + GET_IMM_MEM(); + LDA_INST(); + break; + +case 0xaa: /* TAX */ + INC_KPC_1; + SET_INDEX_REG(acc, xreg); + break; + +case 0xab: /* PLB */ + INC_KPC_1; + CYCLES_PLUS_1 + PULL8_UNSAFE(dbank); + SET_NEG_ZERO8(dbank); + break; + +case 0xac: /* LDY abs */ + C_LDY_ABS(); + break; + +case 0xad: /* LDA abs */ + GET_ABS_RD(); + LDA_INST(); + break; + +case 0xae: /* LDX abs */ + C_LDX_ABS(); + break; + +case 0xaf: /* LDA long */ + GET_LONG_RD(); + LDA_INST(); + break; + +case 0xb0: /* BCS disp8 */ + BRANCH_DISP8((psr & 0x01)); + break; + +case 0xb1: /* LDA (Dloc),y */ + GET_DLOC_IND_Y_RD(); + LDA_INST(); + break; + +case 0xb2: /* LDA (Dloc) */ + GET_DLOC_IND_RD(); + LDA_INST(); + break; + +case 0xb3: /* LDA (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + LDA_INST(); + break; + +case 0xb4: /* LDY Dloc,x */ + C_LDY_DLOC_X(); + break; + +case 0xb5: /* LDA Dloc,x */ + GET_DLOC_X_RD(); + LDA_INST(); + break; + +case 0xb6: /* LDX Dloc,y */ + C_LDX_DLOC_Y(); + break; + +case 0xb7: /* LDA [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + LDA_INST(); + break; + +case 0xb8: /* CLV */ + psr = psr & ~0x40; + INC_KPC_1; + break; + +case 0xb9: /* LDA abs,y */ + GET_ABS_Y_RD(); + LDA_INST(); + break; + +case 0xba: /* TSX */ + INC_KPC_1; + SET_INDEX_REG(stack, xreg); + break; + +case 0xbb: /* TYX */ + INC_KPC_1; + SET_INDEX_REG(yreg, xreg); + break; + +case 0xbc: /* LDY Abs,X */ + C_LDY_ABS_X(); + break; + +case 0xbd: /* LDA Abs,X */ + GET_ABS_X_RD(); + LDA_INST(); + break; + +case 0xbe: /* LDX Abs,y */ + C_LDX_ABS_Y(); + break; + +case 0xbf: /* LDA Long,X */ + GET_LONG_X_RD(); + LDA_INST(); + break; + +case 0xc0: /* CPY #imm */ + C_CPY_IMM(); + break; + +case 0xc1: /* CMP (Dloc,X) */ + GET_DLOC_X_IND_RD(); + CMP_INST(); + break; + +case 0xc2: /* REP #imm */ + GET_1BYTE_ARG; + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_2; + psr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1); + psr = psr & ~(arg & 0xff); + zero = !(psr & 2); + neg7 = psr; + UPDATE_PSR(psr, tmp2); + break; + +case 0xc3: /* CMP Disp8,S */ + GET_DISP8_S_RD(); + CMP_INST(); + break; + +case 0xc4: /* CPY Dloc */ + C_CPY_DLOC(); + break; + +case 0xc5: /* CMP Dloc */ + GET_DLOC_RD(); + CMP_INST(); + break; + +case 0xc6: /* DEC Dloc */ + GET_DLOC_RD_RMW(); + DEC_INST(1); + break; + +case 0xc7: /* CMP [Dloc] */ + GET_DLOC_L_IND_RD(); + CMP_INST(); + break; + +case 0xc8: /* INY */ + INC_KPC_1; + SET_INDEX_REG(yreg + 1, yreg); + break; + +case 0xc9: /* CMP #imm */ + GET_IMM_MEM(); + CMP_INST(); + break; + +case 0xca: /* DEX */ + INC_KPC_1; + SET_INDEX_REG(xreg - 1, xreg); + break; + +case 0xcb: /* WAI */ + if(g_irq_pending) { + g_wait_pending = 0; + INC_KPC_1; + } else { + g_wait_pending = 1; + } + break; + +case 0xcc: /* CPY abs */ + C_CPY_ABS(); + break; + +case 0xcd: /* CMP abs */ + GET_ABS_RD(); + CMP_INST(); + break; + +case 0xce: /* DEC abs */ + GET_ABS_RD_RMW(); + DEC_INST(0); + break; + +case 0xcf: /* CMP long */ + GET_LONG_RD(); + CMP_INST(); + break; + +case 0xd0: /* BNE disp8 */ + BRANCH_DISP8(zero != 0); + break; + +case 0xd1: /* CMP (Dloc),y */ + GET_DLOC_IND_Y_RD(); + CMP_INST(); + break; + +case 0xd2: /* CMP (Dloc) */ + GET_DLOC_IND_RD(); + CMP_INST(); + break; + +case 0xd3: /* CMP (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + CMP_INST(); + break; + +case 0xd4: /* PEI Dloc */ + GET_DLOC_ADDR() + GET_MEMORY16(arg, arg, 1); + CYCLES_PLUS_1; + PUSH16_UNSAFE(arg); + break; + +case 0xd5: /* CMP Dloc,x */ + GET_DLOC_X_RD(); + CMP_INST(); + break; + +case 0xd6: /* DEC Dloc,x */ + GET_DLOC_X_RD_RMW(); + DEC_INST(1); + break; + +case 0xd7: /* CMP [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + CMP_INST(); + break; + +case 0xd8: /* CLD */ + psr = psr & (~0x8); + INC_KPC_1; + break; + +case 0xd9: /* CMP abs,y */ + GET_ABS_Y_RD(); + CMP_INST(); + break; + +case 0xda: /* PHX */ + INC_KPC_1; + if(psr & 0x10) { + PUSH8(xreg); + } else { + PUSH16(xreg); + } + break; + +case 0xdb: /* STP */ + FINISH(RET_STP, 0); + break; + +case 0xdc: /* JML (Abs) */ + GET_2BYTE_ARG; + CYCLES_PLUS_1; + GET_MEMORY24(arg, kpc, 1); + break; + +case 0xdd: /* CMP Abs,X */ + GET_ABS_X_RD(); + CMP_INST(); + break; + +case 0xde: /* DEC Abs,X */ + GET_ABS_X_RD_RMW(); + DEC_INST(0); + break; + +case 0xdf: /* CMP Long,X */ + GET_LONG_X_RD(); + CMP_INST(); + break; + +case 0xe0: /* CPX #imm */ + C_CPX_IMM(); + break; + +case 0xe1: /* SBC (Dloc,X) */ + GET_DLOC_X_IND_RD(); + SBC_INST(); + break; + +case 0xe2: /* SEP #imm */ + GET_1BYTE_ARG; + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_2; + psr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1); + psr = psr | (arg & 0xff); + zero = !(psr & 2); + neg7 = psr; + UPDATE_PSR(psr, tmp2); + break; + +case 0xe3: /* SBC Disp8,S */ + GET_DISP8_S_RD(); + SBC_INST(); + break; + +case 0xe4: /* CPX Dloc */ + C_CPX_DLOC(); + break; + +case 0xe5: /* SBC Dloc */ + GET_DLOC_RD(); + SBC_INST(); + break; + +case 0xe6: /* INC Dloc */ + GET_DLOC_RD_RMW(); + INC_INST(1); + break; + +case 0xe7: /* SBC [Dloc] */ + GET_DLOC_L_IND_RD(); + SBC_INST(); + break; + +case 0xe8: /* INX */ + INC_KPC_1; + SET_INDEX_REG(xreg + 1, xreg); + break; + +case 0xe9: /* SBC #imm */ + GET_IMM_MEM(); + SBC_INST(); + break; + +case 0xea: /* NOP */ + INC_KPC_1; + break; + +case 0xeb: /* XBA */ + tmp1 = acc & 0xff; + CYCLES_PLUS_1 + acc = (tmp1 << 8) + (acc >> 8); + INC_KPC_1; + SET_NEG_ZERO8(acc & 0xff); + break; + +case 0xec: /* CPX abs */ + C_CPX_ABS(); + break; + +case 0xed: /* SBC abs */ + GET_ABS_RD(); + SBC_INST(); + break; + +case 0xee: /* INC abs */ + GET_ABS_RD_RMW(); + INC_INST(0); + break; + +case 0xef: /* SBC long */ + GET_LONG_RD(); + SBC_INST(); + break; + +case 0xf0: /* BEQ disp8 */ + BRANCH_DISP8(zero == 0); + break; + +case 0xf1: /* SBC (Dloc),y */ + GET_DLOC_IND_Y_RD(); + SBC_INST(); + break; + +case 0xf2: /* SBC (Dloc) */ + GET_DLOC_IND_RD(); + SBC_INST(); + break; + +case 0xf3: /* SBC (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + SBC_INST(); + break; + +case 0xf4: /* PEA Abs */ + GET_2BYTE_ARG; + CYCLES_PLUS_1; + INC_KPC_3; + PUSH16_UNSAFE(arg); + break; + +case 0xf5: /* SBC Dloc,x */ + GET_DLOC_X_RD(); + SBC_INST(); + break; + +case 0xf6: /* INC Dloc,x */ + GET_DLOC_X_RD_RMW(); + INC_INST(1); + break; + +case 0xf7: /* SBC [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + SBC_INST(); + break; + +case 0xf8: /* SED */ + INC_KPC_1; + psr |= 0x8; + break; + +case 0xf9: /* SBC abs,y */ + GET_ABS_Y_RD(); + SBC_INST(); + break; + +case 0xfa: /* PLX */ + INC_KPC_1; + CYCLES_PLUS_1; + if(psr & 0x10) { + PULL8(xreg); + SET_NEG_ZERO8(xreg); + } else { + PULL16(xreg); + SET_NEG_ZERO16(xreg); + } + break; + +case 0xfb: /* XCE */ + tmp2 = psr; + INC_KPC_1; + psr = (tmp2 & 0xfe) | ((tmp2 & 1) << 8) | ((tmp2 >> 8) & 1); + UPDATE_PSR(psr, tmp2); + break; + +case 0xfc: /* JSR (Abs,X) */ + GET_2BYTE_ARG; + INC_KPC_2; + tmp1 = kpc; + arg = (kpc & 0xff0000) + ((arg + xreg) & 0xffff); + GET_MEMORY16(arg, tmp2, 1); + kpc = (kpc & 0xff0000) + tmp2; + CYCLES_PLUS_2 + PUSH16_UNSAFE(tmp1); + break; + +case 0xfd: /* SBC Abs,X */ + GET_ABS_X_RD(); + SBC_INST(); + break; + +case 0xfe: /* INC Abs,X */ + GET_ABS_X_RD_RMW(); + INC_INST(0); + break; + +case 0xff: /* SBC Long,X */ + GET_LONG_X_RD(); + SBC_INST(); + break; + diff --git a/gsplus/src/iwm.c b/gsplus/src/iwm.c new file mode 100644 index 0000000..8bd1232 --- /dev/null +++ b/gsplus/src/iwm.c @@ -0,0 +1,3753 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Information from Beneath Apple DOS, Apple IIgs HW reference, Apple IIgs +// Firmware reference, and Inside Macintosh Vol 3 (for status35 info). +// Neil Parker wrote about the Apple 3.5 drive using IWM at +// http://nparker.llx.com/a2/disk and it is very useful. +// http://nparker.llx.com/a2/sethook lists hooks for IWM routines, from +// $e1/0c00 - 0fab. +// ff/5fa4 is STAT35. ff/5fae is CONT35 +// When testing DOS3.3, set bp at $b942, which is bad sector detected +// IWM mode register: 7:reserved; 6:5: 0; 4: 1=8MHz,0=7MHz (should always be 0); +// 3: bit-cells are 2usec, 2: 1sec timer off; 1: async handshake (writes) +// 0: latch mode enabled for reads (holds data for 8 bit times) +// dsk->fd >= 0 indicates a valid disk. dsk->raw_data != 0 indicates this is +// a compressed disk mounted read-only, and ignore dsk->fd (which will always +// be 0, to indicate a valid disk). +// fbit_pos encodes head position on track in increments of 1/64 usecs for 5.25" +// { byte_offset, bit[2:0], sub_bit[8:0] }, so fbit_pos >> 12 gives byte offset +// fbit_mult turns dfcyc into fbit_pos, where (512/fbit_mult) = the bit cell +// 5.25" bit cell of 4usec has fbit_mult=128; cell of 3.75usec has mult=136. +// For 3.5", fbit_mult=256 indicates a 2usec bit cell. +// https://support.apple.com/kb/TA39910?locale=en_US&viewlocale=en_US gives +// the RPMs of 800KB disks + +#include "defc.h" + +int g_halt_arm_move = 0; + +extern int Verbose; +extern word32 g_vbl_count; +extern word32 g_c036_val_speed; +extern int g_config_kegs_update_needed; +extern Engine_reg engine; + +const byte phys_to_dos_sec[] = { + 0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, + 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f +}; + +const byte phys_to_prodos_sec[] = { + 0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b, + 0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f +}; + + +const byte to_disk_byte[] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, +/* 0x10 */ + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, +/* 0x20 */ + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, +/* 0x30 */ + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +int g_track_bytes_35[] = { + 0x200*12, + 0x200*11, + 0x200*10, + 0x200*9, + 0x200*8 +}; + +int g_track_bits_35[] = { + // Do 1.001 * (1020484 * (60/rpm)) / 2 usec = bits per track + 77779, // Trks 0-15: 394 rpm + 71433, // Trks 16-31: 429 rpm + 64926, // Trks 32-47: 472 rpm + 58371, // Trks 48-63: 525 rpm + 51940 // Trks 64-79: 590 rpm +}; + +int g_fast_disk_emul_en = 1; +int g_fast_disk_emul = 0; +int g_slow_525_emul_wr = 0; +dword64 g_dfcyc_end_emul_wr = 0; +int g_fast_disk_unnib = 0; +int g_iwm_fake_fast = 0; + +word32 g_from_disk_byte[256]; +int g_from_disk_byte_valid = 0; + +Iwm g_iwm; + +int g_iwm_motor_on = 0; +// g_iwm_motor_on is set when drive turned on, 0 when $c0e8 is touched. +// Use this to throttle speed to 1MHz. +// g_iwm.motor_on=1 means drive is spinning. g_iwm.motor_off=1 means we're +// in the 1-second countdown of g_iwm.motor_off_vbl_count. + +int g_check_nibblization = 1; + +void +iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525) +{ + int num_tracks; + int i; + + dsk->dfcyc_last_read = 0; + dsk->raw_data = 0; + dsk->wozinfo_ptr = 0; + dsk->dynapro_info_ptr = 0; + dsk->name_ptr = 0; + dsk->partition_name = 0; + dsk->partition_num = -1; + dsk->fd = -1; + dsk->dynapro_blocks = 0; + dsk->raw_dsize = 0; + dsk->dimage_start = 0; + dsk->dimage_size = 0; + dsk->smartport = smartport; + dsk->disk_525 = disk_525; + dsk->drive = drive; + dsk->cur_frac_track = 0; + dsk->image_type = 0; + dsk->vol_num = 254; + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + dsk->disk_dirty = 0; + dsk->just_ejected = 0; + dsk->last_phases = 0; + dsk->cur_fbit_pos = 0; + dsk->fbit_mult = 128; // 128 for 5.25", 256 for 3.5" + dsk->cur_track_bits = 0; + dsk->raw_bptr_malloc = 0; + dsk->cur_trk_ptr = 0; + dsk->num_tracks = 0; + dsk->trks = 0; + if(!smartport) { + // 3.5" and 5.25" drives. This is only called at init time, + // so one-time malloc can be done now + num_tracks = 2*80; // 3.5" needs 160 + dsk->trks = (Trk *)malloc(num_tracks * sizeof(Trk)); + + for(i = 0; i < num_tracks; i++) { + dsk->trks[i].raw_bptr = 0; + dsk->trks[i].sync_ptr = 0; + dsk->trks[i].dunix_pos = 0; + dsk->trks[i].unix_len = 0; + dsk->trks[i].dirty = 0; + dsk->trks[i].track_bits = 0; + } + // num_tracks != 0 indicates a valid disk, do not set it here + } +} + +void +iwm_init() +{ + word32 val; + int i; + + memset(&g_iwm, 0, sizeof(g_iwm)); + + for(i = 0; i < 2; i++) { + iwm_init_drive(&(g_iwm.drive525[i]), 0, i, 1); + iwm_init_drive(&(g_iwm.drive35[i]), 0, i, 0); + } + + for(i = 0; i < MAX_C7_DISKS; i++) { + iwm_init_drive(&(g_iwm.smartport[i]), 1, i, 0); + } + + if(g_from_disk_byte_valid == 0) { + for(i = 0; i < 256; i++) { + g_from_disk_byte[i] = 0x100 + i; + } + for(i = 0; i < 64; i++) { + val = to_disk_byte[i]; + g_from_disk_byte[val] = i; + } + g_from_disk_byte_valid = 1; + } else { + halt_printf("iwm_init called twice!\n"); + } +} + +void +iwm_reset() +{ + int i; + + g_iwm.state = 0; + g_iwm.motor_off_vbl_count = 0; + g_iwm.forced_sync_bit = 32*12345; + g_iwm.last_rd_bit = 32*12345; + g_iwm.write_val = 0; + for(i = 0; i < 5; i++) { + g_iwm.wr_last_bit[i] = 0; + g_iwm.wr_qtr_track[i] = 0; + g_iwm.wr_num_bits[i] = 0; + g_iwm.wr_prior_num_bits[i] = 0; + g_iwm.wr_delta[i] = 0; + } + g_iwm.num_active_writes = 0; + + g_iwm_motor_on = 0; +} + +void +disk_set_num_tracks(Disk *dsk, int num_tracks) +{ + if(num_tracks <= 2*80) { + dsk->num_tracks = num_tracks; + } else { + halt_printf("num_tracks out of range: %d\n", num_tracks); + } +} + +word32 +iwm_get_default_track_bits(Disk *dsk, word32 qtr_trk) +{ + word32 track_bits, extra; + + extra = (qtr_trk + (qtr_trk >> 5)) & 0xf; + // up to 15 extra bits + if(dsk->disk_525) { + track_bits = NIB_LEN_525 * 8; // 0x18f2 = 51088 + } else { + track_bits = g_track_bits_35[qtr_trk >> 5]; + } + return track_bits + extra; +} + +void +draw_iwm_status(int line, char *buf) +{ + char *flag[2][2]; + int apple35_sel, drive_select; + + flag[0][0] = " "; + flag[0][1] = " "; + flag[1][0] = " "; + flag[1][1] = " "; + + apple35_sel = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1; + drive_select = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1; + if(g_iwm.state & IWM_STATE_MOTOR_ON) { + flag[apple35_sel][drive_select] = "*"; + } + + sprintf(buf, "s6d1:%2d%s s6d2:%2d%s s5d1:%2d/%d%s " + "s5d2:%2d/%d%s fast_disk_emul:%d,%d c036:%02x", + g_iwm.drive525[0].cur_frac_track >> 18, flag[0][0], + g_iwm.drive525[1].cur_frac_track >> 18, flag[0][1], + g_iwm.drive35[0].cur_frac_track >> 17, + (g_iwm.drive35[0].cur_frac_track >> 16) & 1, flag[1][0], + g_iwm.drive35[1].cur_frac_track >> 17, + (g_iwm.drive35[1].cur_frac_track >> 16) & 1, flag[1][1], + g_fast_disk_emul, g_slow_525_emul_wr, g_c036_val_speed); + + video_update_status_line(line, buf); +} + +void +iwm_flush_cur_disk() +{ + Disk *dsk; + + dsk = iwm_get_dsk(g_iwm.state); + iwm_flush_disk_to_unix(dsk); +} + +void +iwm_flush_disk_to_unix(Disk *dsk) +{ + byte buffer[0x4000]; + int ret, did_write; + int i; + + if((dsk->disk_dirty == 0) || (dsk->write_through_to_unix == 0)) { + return; + } + if((dsk->raw_data != 0) && !dsk->dynapro_info_ptr) { + return; + } + + printf("Writing disk %s to Unix\n", dsk->name_ptr); + + for(i = 0; i < 160; i++) { + ret = iwm_track_to_unix(dsk, i, &(buffer[0])); + + if((ret != 1) && (ret != 0)) { + printf("iwm_flush_disk_to_unix ret: %d, cannot write " + "image to unix, qtrk:%04x\n", ret, i); + halt_printf("Converting to WOZ format!\n"); + woz_reparse_woz(dsk); + return; // Don't clear dsk->disk_dirty + } + if(ret == 1) { + did_write = 1; + } + } + if(dsk->wozinfo_ptr && did_write) { + woz_check_file(dsk); + } + + dsk->disk_dirty = 0; +} + +/* Check for dirty disk 3 times a second */ + +word32 g_iwm_dynapro_last_vbl_count = 0; + +void +iwm_vbl_update() +{ + word32 state; + int i; + + state = g_iwm.state; + if((state & IWM_STATE_MOTOR_ON) && (state & IWM_STATE_MOTOR_OFF)) { + if(g_iwm.motor_off_vbl_count <= g_vbl_count) { + printf("Disk timer expired, drive off: %08x\n", + g_vbl_count); + iwm_flush_cur_disk(); + g_iwm.state = state & + (~(IWM_STATE_MOTOR_ON | IWM_STATE_MOTOR_OFF)); + g_iwm.drive525[0].dfcyc_last_phases = 0; + g_iwm.drive525[1].dfcyc_last_phases = 0; + } + } + if((g_vbl_count - g_iwm_dynapro_last_vbl_count) >= 60) { + for(i = 0; i < 2; i++) { + dynapro_try_fix_damaged_disk(&(g_iwm.drive525[i])); + dynapro_try_fix_damaged_disk(&(g_iwm.drive35[i])); + } + for(i = 0; i < MAX_C7_DISKS; i++) { + dynapro_try_fix_damaged_disk(&(g_iwm.smartport[i])); + } + g_iwm_dynapro_last_vbl_count = g_vbl_count; + } +} + +void +iwm_update_fast_disk_emul(int fast_disk_emul_en) +{ + if(g_iwm_motor_on == 0) { + g_fast_disk_emul = fast_disk_emul_en; + } +} + +void +iwm_show_stats(int slot_drive) +{ + Trk *trkptr; + Disk *dsk; + int slot, drive; + int i; + + dbg_printf("IWM state: %07x (slot_drive:%d)\n", g_iwm.state, + slot_drive); + dbg_printf("g_iwm.drive525[0].fd: %d, [1].fd: %d\n", + g_iwm.drive525[0].fd, g_iwm.drive525[1].fd); + dbg_printf("g_iwm.drive525[0].last_phases: %d, [1].last_phases: %d\n", + g_iwm.drive525[0].last_phases, g_iwm.drive525[1].last_phases); + dbg_printf("g_iwm.write_val:%02x, wr_num_bits[0]:%d, last_rd_bit:" + "%06x, forced_sync_bit:%06x\n", g_iwm.write_val, + g_iwm.wr_num_bits[0], g_iwm.last_rd_bit, + g_iwm.forced_sync_bit); + if(slot_drive < 0) { + return; + } + drive = slot_drive & 1; + slot = 5 + ((slot_drive >> 1) & 1); + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + if(dsk->trks == 0) { + return; + } + for(i = 0; i < 160; i++) { + trkptr = &(dsk->trks[i]); + printf("Qtrk:%02x: bits:%05x dunix_pos:%08llx unix_len:%06x, " + "d:%d %p,%p\n", i, trkptr->track_bits, + trkptr->dunix_pos, trkptr->unix_len, trkptr->dirty, + trkptr->raw_bptr, trkptr->sync_ptr); + } +} + +Disk * +iwm_get_dsk(word32 state) +{ + Disk *dsk; + int drive; + + drive = (state >> IWM_BIT_DRIVE_SEL) & 1; + if(state & IWM_STATE_C031_APPLE35SEL) { + dsk = &(g_iwm.drive35[drive]); + } else { + dsk = &(g_iwm.drive525[drive]); + } + + return dsk; +} + +Disk * +iwm_touch_switches(int loc, dword64 dfcyc) +{ + Disk *dsk; + dword64 dadd, dcycs_passed; + word32 track_bits, fbit_pos, forced_sync_bit, mask, mask_on, state; + int phase, on, motor_on; + + if(g_iwm.state & IWM_STATE_RESET) { + iwm_printf("IWM under reset: %06x\n", g_iwm.state); + } + + dsk = iwm_get_dsk(g_iwm.state); + + track_bits = dsk->cur_track_bits; + fbit_pos = track_bits * 4096; // Set to an illegal value + motor_on = g_iwm.state & IWM_STATE_MOTOR_ON; + if(motor_on) { + dcycs_passed = (dfcyc - dsk->dfcyc_last_read) >> 16; + dsk->dfcyc_last_read = dfcyc; + + // track_bits can be 0, indicating no valid data + dadd = dcycs_passed * dsk->fbit_mult; + if(track_bits) { + if(dadd >= (track_bits * 512)) { + dadd = dadd % (track_bits * 512); + } + } + fbit_pos = dsk->cur_fbit_pos + (word32)dadd; + if(fbit_pos >= (track_bits * 512)) { + fbit_pos -= (track_bits * 512); + } + if(g_slow_525_emul_wr || (g_iwm.state & IWM_STATE_Q7) || + !g_fast_disk_emul || !track_bits) { + dsk->cur_fbit_pos = fbit_pos; + } else { + fbit_pos = track_bits << 12; // out of range value + } +#if 0 + printf("dsk->cur_fbit:%08x, fbit_pos:%08x\n", + dsk->cur_fbit_pos, fbit_pos); +#endif + } + + dbg_log_info(dfcyc, dsk->cur_fbit_pos, + ((loc & 0xff) << 24) | g_iwm.state, + ((dsk->cur_frac_track + 0x8000) & 0x1ff0000) | 0xe0); + + if(loc < 0) { + // Not a real access, just a way to update fbit_pos + return dsk; + } + + if(dsk->dfcyc_last_phases) { + iwm525_update_head(dsk, dfcyc); + } + + on = loc & 1; + phase = loc >> 1; + state = g_iwm.state; + if(loc < 8) { + /* phase adjustments. See if motor is on */ + + mask = (1 << (phase & 3)) << IWM_BIT_PHASES; + mask_on = (on << (phase & 3)) << IWM_BIT_PHASES; + state = (state & (~mask)) | mask_on; + g_iwm.state = state; + iwm_printf("Iwm phase %d=%d, all phases: %06x (%016llx)\n", + phase, on, state, dfcyc); + + if(state & IWM_STATE_MOTOR_ON) { + if(state & IWM_STATE_C031_APPLE35SEL) { + if((phase == 3) && on) { + iwm_do_action35(dfcyc); + } + } else { + // Move apple525 head + iwm525_update_phases(dsk, dfcyc); + } + } + state = g_iwm.state; + /* See if enable or reset is asserted */ + if(((state >> IWM_BIT_PHASES) & 5) == 5) { // Ph 0, 2 set + state |= IWM_STATE_RESET; + iwm_printf("IWM reset active\n"); + } else { + state &= (~IWM_STATE_RESET); + } + if(((state >> IWM_BIT_PHASES) & 0xa) == 0xa) { // Ph 1, 3 set + state |= IWM_STATE_ENABLE2; + iwm_printf("IWM ENABLE2 active\n"); + } else { + state &= (~IWM_STATE_ENABLE2); + } + g_iwm.state = state; + } else { + /* loc >= 8 */ + switch(loc) { + case 0x8: + iwm_printf("Turning IWM motor off!\n"); + if(g_iwm.state & 0x04) { // iwm MODE + /* Turn off immediately */ + state &= (~(IWM_STATE_MOTOR_ON | + IWM_STATE_MOTOR_OFF)); + } else { + /* 1 second delay */ + if((state & IWM_STATE_MOTOR_ON) && + !(state & IWM_STATE_MOTOR_OFF)){ + state |= IWM_STATE_MOTOR_OFF; + g_iwm.motor_off_vbl_count = g_vbl_count + + 60; + } + } + g_iwm.state = state; + +#if 0 + printf("IWM motor off, g_iwm_motor_on:%d, slow:%d\n", + g_iwm_motor_on, g_slow_525_emul_wr); +#endif + if(g_iwm_motor_on || g_slow_525_emul_wr) { + /* recalc current speed */ + g_fast_disk_emul = g_fast_disk_emul_en; + engine_recalc_events(); + iwm_flush_cur_disk(); + } + + g_iwm_motor_on = 0; + g_slow_525_emul_wr = 0; + break; + case 0x9: + iwm_printf("Turning IWM motor on!\n"); + state |= IWM_STATE_MOTOR_ON; + state &= (~IWM_STATE_MOTOR_OFF); + state &= (~IWM_STATE_LAST_SEL35); + state |= ((state >> 6) & 1) << IWM_BIT_LAST_SEL35; + g_iwm.state = state; + dsk->dfcyc_last_read = dfcyc; + + if(g_iwm_motor_on == 0) { + /* recalc current speed */ + if(dsk->wozinfo_ptr) { + g_fast_disk_emul = 0; + } + engine_recalc_events(); + } + g_iwm_motor_on = 1; + + break; + case 0xa: + case 0xb: + if(((state >> IWM_BIT_DRIVE_SEL) & 1) != on) { + iwm_flush_cur_disk(); + } + state &= (~IWM_STATE_DRIVE_SEL); + state |= (on << IWM_BIT_DRIVE_SEL); + g_iwm.state = state; + dsk = iwm_get_dsk(state); + if(dsk->wozinfo_ptr && g_iwm_motor_on) { + g_fast_disk_emul = 0; + } else { + g_fast_disk_emul = g_fast_disk_emul_en; + } + break; + case 0xc: + case 0xd: + mask = IWM_STATE_Q6 | IWM_STATE_Q7 | + IWM_STATE_MOTOR_ON | IWM_STATE_ENABLE2; + mask_on = IWM_STATE_Q6 | IWM_STATE_MOTOR_ON; + if(((state & mask) == mask_on) && !on) { + // q6 on, q7 off, !on, motor_on, !enable2 + // Switched from $c08d to $c08c, resync now + // If latch mode, back up one more bit + // (for The School Speller - Tools.woz) + forced_sync_bit = iwm_calc_bit_sum( + fbit_pos >> 9, 0 - (state & 1), + track_bits); + g_iwm.forced_sync_bit = forced_sync_bit; + g_iwm.last_rd_bit = forced_sync_bit; + dbg_log_info(dfcyc, forced_sync_bit*2, 0, + 0x500ec); + } + state &= (~IWM_STATE_Q6); + state |= (on << IWM_BIT_Q6); + g_iwm.state = state; + break; + case 0xe: + case 0xf: + mask = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON | + IWM_STATE_ENABLE2; + mask_on = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON; + if(((state & mask) == mask_on) && !on) { + // q7, !on, motor_on, !enable2 +#if 0 + printf("state:%04x and new q7:%d, write_end\n", + state, on); +#endif + dbg_log_info(dfcyc, state, mask, 0xed); + iwm_write_end(dsk, 1, dfcyc); + // printf("write end complete, the track:\n"); + // iwm_check_nibblization(dfcyc); + } + state &= (~IWM_STATE_Q7); + state |= (on << IWM_BIT_Q7); + g_iwm.state = state; + break; + default: + printf("iwm_touch_switches: loc: %02x unknown!\n", loc); + exit(2); + } + } + + if(!(state & IWM_STATE_Q7)) { + g_iwm.num_active_writes = 0; + if(g_slow_525_emul_wr) { + g_slow_525_emul_wr = 0; + engine_recalc_events(); + } + } + + return dsk; +} + +void +iwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc) +{ + Trk *trkptr; + word32 track_bits, cur_frac_track, wr_last_bit, write_val; + int disk_525, drive, new_track, cur_track, max_track; + int num_active_writes; + + if(dsk->smartport) { + return; + } + cur_frac_track = dsk->cur_frac_track; + if(delta != 0) { + // 3.5" move, clear out lower fractional track bits + new_frac_track = new_frac_track & (-0x10000); + if((delta < 0) && (new_frac_track < (word32)(-delta))) { + // Moving down past track 0...stop, but preserve side + new_frac_track = cur_frac_track & 0x10000; + delta = 0; + } + } + new_frac_track = new_frac_track + delta; + + disk_525 = dsk->disk_525; +#if 0 + printf("iwm_move_to_track: %07x, num_tracks:%03x (cur:%07x) %016llx\n", + new_frac_track, dsk->num_tracks, dsk->cur_frac_track, dfcyc); +#endif + + max_track = 36*4 - 1; // Go to track 35.75 on 5.25" + if(dsk->num_tracks >= (max_track + 1)) { + max_track = dsk->num_tracks - 1; + } + if(max_track >= 159) { + max_track = 159; // Limit to 159 always + } + if(new_frac_track > (word32)(max_track << 16)) { + new_frac_track = max_track << 16; + } + new_track = (new_frac_track + 0x8000) >> 16; + + cur_track = (cur_frac_track + 0x8000) >> 16; + num_active_writes = g_iwm.num_active_writes; + wr_last_bit = g_iwm.wr_last_bit[0]; + write_val = g_iwm.write_val; + if(num_active_writes) { + iwm_write_end(dsk, 0, dfcyc); + printf("moving arm to new_track:%d, write active:%d, bit:%08x, " + "write_val:%02x\n", new_track, num_active_writes, + wr_last_bit, write_val); + } + if((cur_track != new_track) || (dsk->cur_trk_ptr == 0)) { + drive = dsk->drive + 1; + if(1) { + // Don't do this printf + } else if(disk_525) { + printf("s6d%d Track: %d.%02d\n", drive, + new_track >> 2, 25* (new_track & 3)); + } else { + printf("s5d%d Track: %d Side: %d\n", drive, + new_track >> 1, new_track & 1); + } + + trkptr = &(dsk->trks[new_track]); + track_bits = trkptr->track_bits; + if((track_bits == 0) && disk_525) { + // Use an adjacent .25 track if it is valid + if((new_track > 0) && (trkptr[-1].track_bits != 0)) { + new_track--; + // printf("Moved dn to qtrk:%04x\n", new_track); + } else if((new_track < max_track) && + (trkptr[1].track_bits != 0)) { + new_track++; + // printf("Moved up to qtrk:%04x\n", new_track); + } + } + iwm_flush_disk_to_unix(dsk); + iwm_move_to_qtr_track(dsk, new_track); + if(dfcyc != 0) { + dbg_log_info(dfcyc, cur_frac_track, new_frac_track, + (g_iwm.state << 16) | 0x00f000e1); +#if 0 + printf("Just moved from track %08x to %08x %016llx\n", + cur_frac_track, new_frac_track, dfcyc); +#endif + } + } + dsk->cur_frac_track = new_frac_track; + if(num_active_writes) { + iwm_start_write(dsk, wr_last_bit, write_val, 1); + } +} + +void +iwm_move_to_qtr_track(Disk *dsk, word32 qtr_track) +{ + Trk *trkptr; + word32 track_bits, fbit_pos; + + trkptr = &(dsk->trks[qtr_track]); + track_bits = trkptr->track_bits; + + dsk->cur_trk_ptr = trkptr; + dsk->cur_track_bits = track_bits; + fbit_pos = dsk->cur_fbit_pos; + if(track_bits) { + // Moving to a valid track. Ensure fbit_pos in range + fbit_pos = fbit_pos % (track_bits * 512); + } + dsk->cur_fbit_pos = fbit_pos; +} + +dword64 g_iwm_last_phase_dfcyc = 0; + +void +iwm525_update_phases(Disk *dsk, dword64 dfcyc) +{ + word32 ign_mask; + int last_phases, new_phases, eff_last_phases, eff_new_phases; + int my_phase; + + // Decide if dsk->last_phases needs to change. + last_phases = dsk->last_phases; + new_phases = (g_iwm.state >> IWM_BIT_PHASES) & 0xf; + if(last_phases != new_phases) { + iwm_printf("Phases changing %02x -> %02x, ftrack:%08x at %lld, " + "diff:%.2fmsec\n", last_phases, new_phases, + dsk->cur_frac_track, dfcyc >> 16, + ((dfcyc - g_iwm_last_phase_dfcyc) >> 16) / 1000.0); + g_iwm_last_phase_dfcyc = dfcyc; + } + my_phase = (dsk->cur_frac_track >> 17) & 3; + ign_mask = 1 << ((2 + my_phase) & 3); + eff_last_phases = last_phases & (~ign_mask); + eff_new_phases = new_phases & (~ign_mask); + if(eff_last_phases == eff_new_phases) { + dsk->last_phases = new_phases; + return; // Nothing to do + } + + // Update last_phases + iwm525_update_head(dsk, dfcyc); + + dsk->last_phases = new_phases; + dsk->dfcyc_last_phases = dfcyc; + dbg_log_info(dfcyc, new_phases, dsk->cur_frac_track, 0x100e1); +} + +void +iwm525_update_head(Disk *dsk, dword64 dfcyc) +{ + double dinc; + dword64 diff_dusec, dfcyc_last_phases; + word32 cur_frac_track, frac_track, sum, num, phases, diff; + int new_qtrk, cur_qtrk, my_phase, prev_phase, grind, inc; + int i; + + cur_frac_track = dsk->cur_frac_track; + my_phase = (dsk->cur_frac_track >> 17) & 3; + + // If my_phase is 1, then phase 0 being on should decrement the trk + // number, and phase 2 being on should increment the trk number + phases = dsk->last_phases & 0xf; // one bit for each phase + phases = (phases << 4) | phases; + prev_phase = (my_phase + 4 - 1) & 3; + phases = (phases >> prev_phase) & 0xf; + + // Now, phases[0] means decrement trk, phases[1] is where we are, + // phases[2] means increment trk, and phases[3] MIGHT mean increment + sum = 0; + num = 0; + grind = 0; + for(i = 0; i < 4; i++) { + if(((phases >> i) & 1) == 0) { + continue; + } + frac_track = cur_frac_track & -0x20000; + switch(i) { + case 0: // Previous phase is on, decrement trk + if(frac_track >= 0x20000) { + frac_track -= 0x20000; + } else { + frac_track = 0; + } + sum += frac_track; + num++; + if(cur_frac_track == 0) { + grind++; + } + break; + case 1: // "our" phase, pull to aligned track + sum += frac_track; + num++; + break; + case 2: // Next phase, increment track + sum += frac_track + 0x20000; + num++; + break; + case 3: // Next next phase: might pull + // Phase is nominally 2 away...but if cur_frac_track + // is just a little less than a new halftrack, + // (0xxx1ffff), then it may be within a half track + // and we'll let it pull us + frac_track += 0x20000; + if((frac_track - cur_frac_track) < 0x30000) { + sum += frac_track; + num++; + } + } + } + + frac_track = cur_frac_track; + dfcyc_last_phases = dsk->dfcyc_last_phases; + dsk->dfcyc_last_phases = 0; + if(num) { + frac_track = sum / num; // New desired track + // Now see if enough time has elapsed that we got to frac_track + diff_dusec = (dfcyc - dfcyc_last_phases) >> 16; + dinc = 0x20000 / 2800.0; // 2.8msec to move one phase + inc = (int)dinc; + if(cur_frac_track >= frac_track) { + diff = cur_frac_track - frac_track; + inc = -inc; + } else { + diff = frac_track - cur_frac_track; + } + if(g_fast_disk_emul) { + diff = 0; // Always moved enough + } + // Add inc*diff_dusec to cur_frac_track, unless we already reach + if(diff > (dinc * diff_dusec)) { + // Enough time has NOT elapsed, so calc where head is + // Update frac_track to be cur_frac_track + inc * dusec + frac_track = cur_frac_track + + (word32)(inc * diff_dusec); + dsk->dfcyc_last_phases = dfcyc; + } + } + + new_qtrk = (frac_track + 0x8000) >> 16; + cur_qtrk = (cur_frac_track + 0x8000) >> 16; + if(new_qtrk != cur_qtrk) { + if(g_halt_arm_move) { + halt_printf("Halt on arm move\n"); + g_halt_arm_move = 0; + } + if(grind) { + printf("GRIND GRIND GRIND\n"); + } + + dbg_log_info(dfcyc, frac_track, dsk->cur_frac_track, + (phases << 24) | 0x00e1); + iwm_move_to_ftrack(dsk, frac_track, 0, dfcyc); + + if(new_qtrk > 2) { +#if 0 + printf("Moving to qtr track: %04x (trk:%d.%02d), %d, " + "%02x, %08x dfcyc:%lld\n", new_qtrk, + new_qtrk >> 2, 25*(new_qtrk & 3), my_phase, + phases, g_iwm.state, dfcyc >> 16); +#endif + } + } else { + // On the same qtr_track, but update the fraction + dsk->cur_frac_track = frac_track; + } + +#if 0 + /* sanity check stepping algorithm */ + if((qtr_track & 7) == 0) { + /* check for just access phase 0 */ + if(last_phase != 0) { + halt_printf("last_phase: %d!\n", last_phase); + } + } +#endif +} + +int +iwm_read_status35(dword64 dfcyc) +{ + Disk *dsk; + word32 state; + int drive, stat35, tmp; + + // This is usually done by STAT35 at ff/5fa4 + // This code is called on the rising edge of Q6 asserted + state = g_iwm.state; + drive = (state >> IWM_BIT_DRIVE_SEL) & 1; + dsk = &(g_iwm.drive35[drive]); + + if(state & IWM_STATE_MOTOR_ON) { + /* Read status: ph[1], ph[0], disk35, ph[2] */ + stat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) | + ((state >> 6) & 2) | + (((state >> IWM_BIT_PHASES) >> 2) & 1); + + iwm_printf("Iwm status read state: %02x\n", state); + dbg_log_info(dfcyc, state, stat35, 0xe7); + + switch(stat35) { + case 0x00: /* step direction */ + return (state >> IWM_BIT_STEP_DIRECTION35) & 1; + break; + case 0x01: /* lower head activate */ + /* also return instantaneous data from head */ + iwm_move_to_ftrack(dsk, + (dsk->cur_frac_track & (-0x20000)), 0, dfcyc); + return (dsk->cur_fbit_pos >> 15) & 1; + break; + case 0x02: /* disk in place */ + /* 1 = no disk, 0 = disk */ + iwm_printf("read disk in place, num_tracks: %d\n", + dsk->num_tracks); + tmp = (dsk->num_tracks <= 0); + dbg_log_info(dfcyc, 0, dsk->num_tracks, 0x100e7); + return tmp; + break; + case 0x03: /* upper head activate */ + /* also return instantaneous data from head */ + iwm_move_to_ftrack(dsk, dsk->cur_frac_track | 0x10000, + 0, dfcyc); + return (dsk->cur_fbit_pos >> 15) & 1; + break; + case 0x04: /* disk is stepping? */ + /* 1 = not stepping, 0 = stepping */ + return 1; + break; + case 0x05: /* Unknown function of ROM 03? */ + /* 1 = or $20 into 0xe1/f24+drive, 0 = don't */ + return 1; + break; + case 0x06: /* disk is locked */ + /* 0 = locked, 1 = unlocked */ + return (!dsk->write_prot); + break; + case 0x08: /* motor on */ + /* 0 = on, 1 = off */ + return !(state & IWM_STATE_MOTOR_ON35); + break; + case 0x09: /* number of sides */ + /* 1 = 2 sides, 0 = 1 side */ + return 1; + break; + case 0x0a: /* at track 0 */ + /* 1 = not at track 0, 0 = there */ + tmp = (dsk->cur_frac_track != 0); + iwm_printf("Read at track0_35: %d\n", tmp); + return tmp; + break; + case 0x0b: /* disk ready??? */ + /* 0 = ready, 1 = not ready? */ + tmp = !(state & IWM_STATE_MOTOR_ON35); + iwm_printf("Read disk ready, ret: %d\n", tmp); + return tmp; + break; + case 0x0c: /* disk switched?? */ + /* 0 = not switched, 1 = switched? */ + tmp = (dsk->just_ejected != 0); + iwm_printf("Read disk switched: %d\n", tmp); + return tmp; + break; + case 0x0d: /* false read when ejecting disk */ + return 1; + case 0x0e: /* tachometer */ + halt_printf("Reading tachometer!\n"); + return (dsk->cur_fbit_pos >> 11) & 1; + break; + case 0x0f: /* drive installed? */ + /* 0 = drive exists, 1 = no drive */ + if(drive) { + /* pretend no drive 1 */ + return 1; + } + return 0; + break; + default: + halt_printf("Read 3.5 status, stat35: %02x\n", stat35); + return 1; + } + } else { + iwm_printf("Read 3.5 status with drive off!\n"); + return 1; + } +} + +void +iwm_do_action35(dword64 dfcyc) +{ + Disk *dsk; + word32 state; + int drive, stat35; + + // Actions done by CONT35 routine at ff/5fae + // This code is called on the rising edge of phase[3] asserted + state = g_iwm.state; + drive = (state >> IWM_BIT_DRIVE_SEL) & 1; + dsk = &(g_iwm.drive35[drive]); + + if(state & IWM_STATE_MOTOR_ON) { + /* Perform action */ + /* stat35: ph[1], ph[0], disk35_ctrl, ph[2] */ + stat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) | + ((state >> 6) & 2) | + (((state >> IWM_BIT_PHASES) >> 2) & 1); + dbg_log_info(dfcyc, state, stat35, 0xf00e7); + + switch(stat35) { + case 0x00: /* Set step direction inward (higher tracks) */ + /* towards higher tracks, clear STEP_DIRECTION35 */ + state &= (~IWM_STATE_STEP_DIRECTION35); + iwm_printf("Iwm set step dir35 = 0\n"); + break; + case 0x01: /* Set step direction outward (lower tracks) */ + /* towards lower tracks */ + state |= IWM_STATE_STEP_DIRECTION35; + dbg_log_info(dfcyc, state, stat35, 0x300e7); + iwm_printf("Iwm set step dir35 = 1\n"); + break; + case 0x03: /* reset disk-switched flag? */ + iwm_printf("Iwm reset disk switch\n"); + dsk->just_ejected = 0; + break; + case 0x04: /* step disk */ + if(state & IWM_STATE_STEP_DIRECTION35) { + iwm_move_to_ftrack(dsk, dsk->cur_frac_track, + -0x20000, dfcyc); + } else { + iwm_move_to_ftrack(dsk, dsk->cur_frac_track, + 0x20000, dfcyc); + } + break; + case 0x08: /* turn motor on */ + iwm_printf("Iwm set motor_on35 = 1\n"); + state |= IWM_STATE_MOTOR_ON35; + break; + case 0x09: /* turn motor off */ + iwm_printf("Iwm set motor_on35 = 0\n"); + state &= (~IWM_STATE_MOTOR_ON35); + break; + case 0x0d: /* eject disk */ + printf("Action 0x0d, will eject disk\n"); + iwm_eject_disk(dsk); + break; + case 0x02: + case 0x07: + case 0x0b: /* hacks to allow AE 1.6MB driver to not crash me */ + break; + default: + halt_printf("Do 3.5 action, state: %02x\n", state); + return; + } + } else { + halt_printf("Set 3.5 status with drive off!\n"); + return; + } + g_iwm.state = state; + dbg_log_info(dfcyc, state, stat35, 0x400e7); +} + +int +read_iwm(int loc, dword64 dfcyc) +{ + Disk *dsk; + word32 status, bit_diff, bit_pos, state; + int on, q7_q6, val; + + loc = loc & 0xf; + on = loc & 1; + + dsk = iwm_touch_switches(loc, dfcyc); + + state = g_iwm.state; + q7_q6 = (state >> IWM_BIT_Q6) & 3; + + if(on) { + /* odd address, return 0 */ + return 0; + } else { + /* even address */ + switch(q7_q6) { + case 0x00: /* q7 = 0, q6 = 0 */ + if(state & IWM_STATE_ENABLE2) { + return iwm_read_enable2(dfcyc); + } else { + if(state & IWM_STATE_MOTOR_ON) { + return iwm_read_data(dsk, dfcyc); + } else { + iwm_printf("read iwm st 0, m off!\n"); +/* HACK!!!! */ + return 0xff; + //return (((int)dfcyc) & 0x7f) + 0x80; + } + } + break; + case 0x01: /* q7 = 0, q6 = 1 */ + /* read IWM status reg */ + if(state & IWM_STATE_ENABLE2) { + iwm_printf("Read status under enable2: 1\n"); + status = 1; + } else { + if(state & IWM_STATE_C031_APPLE35SEL) { + status = iwm_read_status35(dfcyc); + } else { + status = dsk->write_prot; + } + } + + val = ((status & 1) << 7) | (state & 0x3f); + // bit 5 is motor_on, bits[4:0] are iwm_mode + iwm_printf("Read status: %02x\n", val); + + return val; + break; + case 0x02: /* q7 = 1, q6 = 0 */ + /* read handshake register */ + if(state & IWM_STATE_ENABLE2) { + return iwm_read_enable2_handshake(dfcyc); + } else { + status = 0xc0; + bit_pos = dsk->cur_fbit_pos >> 9; + bit_diff = iwm_calc_bit_diff(bit_pos, + g_iwm.wr_last_bit[0], + dsk->cur_track_bits); + if(bit_diff > 8) { + iwm_printf("Write underrun!\n"); + status = status & 0xbf; + } + return status; + } + break; + case 0x03: /* q7 = 1, q6 = 1 */ + iwm_printf("read iwm q7_q6=3!\n"); + return 0; + break; + } + } + halt_printf("Got to end of read_iwm, loc: %02x!\n", loc); + + return 0; +} + +void +write_iwm(int loc, int val, dword64 dfcyc) +{ + Disk *dsk; + word32 state; + int on, q7_q6, drive, fast_writes; + + loc = loc & 0xf; + on = loc & 1; + + dsk = iwm_touch_switches(loc, dfcyc); + + state = g_iwm.state; + q7_q6 = (state >> IWM_BIT_Q6) & 3; + drive = (state >> IWM_BIT_DRIVE_SEL) & 1; + fast_writes = g_fast_disk_emul; + if(state & IWM_STATE_C031_APPLE35SEL) { + dsk = &(g_iwm.drive35[drive]); + } else { + dsk = &(g_iwm.drive525[drive]); + fast_writes = !g_slow_525_emul_wr && fast_writes; + } + + if(on) { + /* odd address, write something */ + if(q7_q6 == 3) { + /* q7, q6 = 1,1 */ + if(state & IWM_STATE_MOTOR_ON) { + if(state & IWM_STATE_ENABLE2) { + iwm_write_enable2(val); + } else { + iwm_write_data(dsk, val, dfcyc); + } + } else { + /* write mode register */ + // bit 0: latch mode (should set if async hand) + // bit 1: async handshake + // bit 2: immediate motor off (no 1 sec delay) + // bit 3: 2us bit timing + // bit 4: Divide input clock by 8 (instead of 7) + val = val & 0x1f; + state = (state & (~0x1f)) | val; + g_iwm.state = state; + if(val & 0x10) { + iwm_printf("set iwm_mode:%02x!\n",val); + } + } + } else { + if(state & IWM_STATE_ENABLE2) { + iwm_write_enable2(val); + } else { +#if 0 +// Flobynoid writes to 0xc0e9 causing these messages... + printf("Write iwm1, st: %02x, loc: %x: %02x\n", + q7_q6, loc, val); +#endif + } + } + return; + } else { + /* even address */ + if(state & IWM_STATE_ENABLE2) { + iwm_write_enable2(val); + } else { + iwm_printf("Write iwm2, st: %02x, loc: %x: %02x\n", + q7_q6, loc, val); + } + return; + } + + return; +} + + +int +iwm_read_enable2(dword64 dfcyc) +{ + iwm_printf("Read under enable2 %016llx!\n", dfcyc); + return 0xff; +} + +int g_cnt_enable2_handshake = 0; + +int +iwm_read_enable2_handshake(dword64 dfcyc) +{ + int val; + + iwm_printf("Read handshake under enable2, %016llx!\n", dfcyc); + + val = 0xc0; + g_cnt_enable2_handshake++; + if(g_cnt_enable2_handshake > 3) { + g_cnt_enable2_handshake = 0; + val = 0x80; + } + + return val; +} + +void +iwm_write_enable2(int val) +{ + // Smartport is selected (PH3=1, PH1=1, Sel35=0), just ignore this data + iwm_printf("Write under enable2: %02x!\n", val); + + return; +} + +word32 +iwm_fastemul_start_write(Disk *dsk, dword64 dfcyc) +{ + double new_fast_cycs; + dword64 dfcyc_passed; + word32 fbit_pos, fbit_diff, track_bits; + + // Nox Archaist doesn't finish reading sector header's checksum, but + // instead waits for 7 bytes to pass and then writes. This code + // tries to allow fast_disk_emul mode to not overwrite the checksum. + // Move the fbit_pos forward to try to account for a delay from the + // last read to the current write. But accesses to I/O locations + // would still take a lot of time. Let's assume there were + // 2 slow cycles, and clamp the skip to a min of 1 byte, max 3 bytes. + dfcyc_passed = dfcyc - g_iwm.dfcyc_last_fastemul_read; + new_fast_cycs = (((double)dfcyc_passed) - 0x20000) / + ((double)engine.fplus_ptr->dplus_1); + // new_fast_cycs approximates the number of fast cycles that have + // passed, so 4.0 means 4 fast cycles have passed + + iwm_printf("start write, dfcyc:%016llx, new_f_cycs:%f, plus_1:%08llx\n", + dfcyc_passed, new_fast_cycs, engine.fplus_ptr->dplus_1); + + fbit_diff = (word32)(new_fast_cycs * dsk->fbit_mult); + if(new_fast_cycs < 0.0) { + fbit_diff = 8*512; // 8 bits + } else { + if(fbit_diff < 8*512) { // 8 bits + fbit_diff = 8*512; + } else if(fbit_diff > (32*512)) { + fbit_diff = 32*512; + } + } + track_bits = dsk->cur_track_bits; + fbit_pos = dsk->cur_fbit_pos; + + if(track_bits == 0) { + return fbit_pos; + } + + fbit_pos = fbit_pos + fbit_diff; + if(fbit_pos >= (track_bits * 512)) { + fbit_pos = fbit_pos - (track_bits * 512); + } +#if 0 + printf(" adjusted fbit_pos from %07x to %07x\n", + dsk->cur_fbit_pos, fbit_pos); +#endif + dbg_log_info(dfcyc, fbit_pos, dsk->cur_fbit_pos, 0xee); + dsk->cur_fbit_pos = fbit_pos; + + return fbit_pos; +} + +word32 +iwm_read_data_fast(Disk *dsk, dword64 dfcyc) +{ + dword64 dval, dsync_val_tmp, dsync_val; + word32 bit_pos, new_bit_pos, track_bits, val; + int msb, dec; + + if(!g_fast_disk_unnib) { + g_iwm.dfcyc_last_fastemul_read = dfcyc; + } + track_bits = dsk->cur_track_bits; + bit_pos = dsk->cur_fbit_pos >> 9; + + // fbit_pos points just after the LSB of the last nibble. Read +8 + // past to get the next nibble. Get more to ensure a valid nibble + new_bit_pos = iwm_calc_bit_sum(bit_pos, 15, track_bits); + dval = iwm_get_raw_bits(dsk, new_bit_pos, 16, &dsync_val_tmp); + dsync_val = dsync_val_tmp; +#if 0 + printf("fast %010llx syncs:%010llx bit:%07x\n", dval, dsync_val, + bit_pos * 2); +#endif + + if(!g_fast_disk_unnib) { + //dbg_log_info(dfcyc, dval, dsync_val, 0xeb); + } + // Find the sync val closest to 15 without going over + msb = (dsync_val >> 8) & 0xff; + if((msb > 15) || (msb == 0)) { + msb = dsync_val & 0xff; + } + if(msb > 15) { + msb = 8; // Just return something + } + if(msb < 7) { + // This can happen when the arm is moved from a long track to a + // shorter track, fbit_pos at an arbitrary position at the + // track start, placing us at dsync_val=0x1006. Just ignore + msb = 7; + } + val = (word32)(dval >> (msb - 7)); + + // We've moved new_fbit_pos forward 15 bits, move back 7 bits for msb=15 + dec = 15 - msb - 7; + if(!g_iwm_motor_on && (msb == 15) && !g_fast_disk_unnib) { + // Return this byte properly...but not the next one for the + // DOS3.3 RWTS motor on detect code, which reads the drive + // without enabling the motor, to see if the motor is on + dec--; + } + if((msb != 15) && !g_fast_disk_unnib) { + // Pull a trick to make the disk motor-on test pass ($bd34 in + // DOS 3.3 RWTS): if this is a sync byte, don't return whole + // byte, but return whole byte next time + val = val & 0x7f; + dec = dec - 8; + } + dsk->cur_fbit_pos = iwm_calc_bit_sum(new_bit_pos, dec, track_bits) << 9; +#if 0 + printf(" val:%02x fbit:%07x new_fbit:%07x dec:%d, msb:%d\n", + val & 0xff, dsk->cur_fbit_pos, new_fbit_pos, dec, msb); +#endif + if(!g_fast_disk_unnib) { + dbg_log_info(dfcyc, dsk->cur_fbit_pos, + (dec << 24) | (bit_pos * 2), (val << 16) | 0xeb); + } + return val & 0xff; +} + +dword64 +iwm_return_rand_data(Disk *dsk, dword64 dfcyc) +{ + dword64 dval, dval2; + + if(dsk) { + // Use dsk + } + dval = dfcyc >> 16; + dval2 = dval ^ (dval >> 16) ^ (dval << 10) ^ (dval << 26) ^ + (dval << 41); + dval = dval2 & ((dval >> 6) ^ (dval >> 17)); + return dval; +} + +dword64 +iwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr) +{ + byte *bptr, *sync_ptr; + dword64 dval, dtmp_val, sync_dval; + word32 track_bits; + int pos, bits, total_bits, sync_pos, sync; + + track_bits = dsk->cur_track_bits; + if(track_bits == 0) { + halt_printf("iwm_get_raw_bits track_bits 0, %08x\n", + dsk->cur_frac_track); + return 0; + } + total_bits = 0; + bits = 1 + (bit_pos & 7); // 1..8 + pos = bit_pos >> 3; + dval = 0; + sync_dval = 0; + bptr = &(dsk->cur_trk_ptr->raw_bptr[0]); + sync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]); + sync_pos = 0; + if((bptr == 0) || (sync_ptr == 0)) { + halt_printf("bptr:%p, sync:%p, bit_pos:%08x, track_bits:%08x, " + "cur_trk_ptr eff:%05lx\n", bptr, sync_ptr, bit_pos, + track_bits, dsk->cur_trk_ptr - &(dsk->trks[0])); + *dsyncs_ptr = 0xffffffffffffffULL; + return 0; + } + while(total_bits < num_bits) { + dtmp_val = bptr[pos]; + dtmp_val = dtmp_val >> (8 - bits); + dval = dval | (dtmp_val << total_bits); + sync = sync_ptr[pos]; + if((sync < 8) && (sync >= (8 - bits))) { // MSB is here + sync = sync - (8 - bits); + sync = sync + total_bits; + if((sync_pos < 64) && (sync != (sync_dval & 0xff))){ + sync_dval |= ((dword64)sync & 0xff) << sync_pos; + sync_pos += 8; + } + } + total_bits += bits; + bits = 8; + pos--; + if(pos < 0) { + pos = (track_bits - 1) >> 3; + bits = 1 + ((track_bits - 1) & 7); + } + } + + if(sync_pos == 0) { + sync_dval = 0xff; + } + *dsyncs_ptr = sync_dval; + return dval; +} + +word32 +iwm_calc_bit_diff(word32 first, word32 last, word32 track_bits) +{ + word32 val; + + val = first - last; + if(val >= track_bits) { + val = val + track_bits; + } + return val; +} + +word32 +iwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits) +{ + word32 val; + + val = bit_pos + add_ival; + if(val >= track_bits) { + if(add_ival < 0) { + val = val + track_bits; + } else { + val = val - track_bits; + } + } + return val; +} + +dword64 +iwm_calc_forced_sync(dword64 dval, int forced_bit) +{ + dword64 sync_dval; + + // Something like the "E7" protection scheme has toggled + // $c0ed in the middle of reading a nibble, causing a new sync. This + // is used to "move" sync bits inside disk nibbles, to defeat + // nibble copiers (since a 36-cycle sync nibble cannot be + // differentiated from a 40-cycle sync nibble in one read pass) + // Return these misaligned nibbles until we re-sync (then clear + // g_iwm.forced_sync_bit. Toggling $c0ed can set the data latch + // to $ff is the disk is write-protected, but this gets cleared + // to $00 within 4 CPU cycles always (or less). A phantom 1 will + // also shift in + + dval |= (1ULL << forced_bit); + sync_dval = 30; // A default for the previous nibble + while(forced_bit >= 0) { + // Scan until this bit is set + if((dval >> forced_bit) & 1) { + // this bit is the MSB of a nibble, note it + sync_dval = (sync_dval << 8) | forced_bit; + forced_bit -= 7; + } + forced_bit--; + } + return sync_dval; +} + +int +iwm_calc_forced_sync_0s(dword64 sync_dval, int bits) +{ + int sync, forced_bits, done; + int i; + + // Return number between 7 and bits as to the oldest valid sync bit + // in the sync_dval array. 0xff in the first position means no syncs + if(bits <= 8) { + return bits; + } + forced_bits = 8; + done = 0; + if((sync_dval & 0xff) <= 7) { + sync_dval = sync_dval >> 8; + } + for(i = 0; i < 8; i++) { + sync = sync_dval & 0xff; + if(sync == 0xff) { + return bits; + } + sync_dval = sync_dval >> 8; + while(sync > bits) { + sync -= 8; // Bring it back into range + done = 1; + } + if(sync < forced_bits) { + break; + } + if(sync < bits) { + forced_bits = sync; + } + if(done) { + break; + } + } + return forced_bits; +} + +word32 +iwm_read_data(Disk *dsk, dword64 dfcyc) +{ + dword64 dval, sync_dval, dsync_val_tmp, dval0, dval2; + word32 track_bits, fbit_pos, bit_pos, val, forced_sync_bit; + int msb_bit, bits, forced_bits, diff; + + track_bits = dsk->cur_track_bits; + + fbit_pos = dsk->cur_fbit_pos; + bit_pos = fbit_pos >> 9; + if((track_bits == 0) || (dsk->cur_trk_ptr == 0)) { + val = ((fbit_pos * 25) >> 11) & 0xff; + iwm_printf("Reading c0ec, track_len 0, returning %02x\n", val); + return val; + } + + if(g_fast_disk_emul) { + return iwm_read_data_fast(dsk, dfcyc); + } + + // First, get the last few bytes of data + bits = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit, track_bits) + 10; + if(bits < 25) { + bits = 25; + } else if(bits > 60) { + bits = 60; + } + forced_sync_bit = g_iwm.forced_sync_bit; + forced_bits = -1; + if(forced_sync_bit < track_bits) { + forced_bits = iwm_calc_bit_diff(bit_pos, forced_sync_bit, + track_bits); + if(forced_bits >= 64) { + forced_bits = 63; + } + if(forced_bits > bits) { + bits = forced_bits; + } + } + dval = iwm_get_raw_bits(dsk, bit_pos, bits, &dsync_val_tmp); + sync_dval = dsync_val_tmp; + + // See if there are runs of more than three 0 bits...introduce noise + dval0 = (1ULL << bits) | dval; + dval0 = dval0 | (dval0 >> 1) | (dval0 >> 2) | (dval0 >> 3); + dval0 = (~dval0) & ((1ULL << bits) - 1); + + if(dval0) { + // Each set bit of dval0 indicates the previous 3 bits are 0 + dval2 = dval0 | (dval0 << 1); + dval2 = dval0 & iwm_return_rand_data(dsk, dfcyc); +#if 0 + printf("dval0 is %016llx, dval2:%016llx, bits:%d\n", dval0, + dval2, bits); +#endif + dval = dval ^ dval2; + if(forced_bits < 0) { + forced_bits = iwm_calc_forced_sync_0s(sync_dval, bits); + g_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos, + 0 - forced_bits, track_bits); + dbg_log_info(dfcyc, g_iwm.forced_sync_bit * 2, + (forced_bits << 24) | (bit_pos * 2), 0x400ec); +#if 0 + printf("Forced bits are %d at %06x, %016llx " + "%016llx\n", forced_bits, bit_pos * 2, dval, + sync_dval); +#endif + } + } + if(forced_bits >= 0) { + sync_dval = iwm_calc_forced_sync(dval, forced_bits); + dval = dval & ((2ULL << forced_bits) - 1LL); + dbg_log_info(dfcyc, (word32)dval, forced_sync_bit * 32, + (forced_bits << 16) | 0xea); + if(((dsync_val_tmp & 0xff) == (sync_dval & 0xff)) && (!dval0)) { + // Only clear it if there are no 0's in the dval, + // otherwise we'll reenter and could calc a diff sync + g_iwm.forced_sync_bit = 234567*4; + //printf("cleared forced_sync\n"); + } else { + g_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos, + 0 - (sync_dval & 0xf), track_bits); + } + +#if 0 + printf("Forced_bits were %d, sync_was %016llx\n", + forced_bits, dsync_val_tmp); +#endif + } +#if 0 + printf(" Raw bits: %016llx, dsync:%016llx, fbit:%08x\n", dval, + sync_dval, fbit_pos); +#endif + + dbg_log_info(dfcyc, (word32)dval, + (bits << 24) | ((word32)sync_dval & 0x00ffffffUL), 0x300ec); + msb_bit = sync_dval & 0xff; + if(g_iwm.state & 1) { // Latch mode (3.5" disk) + // last_rd_bit points just past the last bit read (it is + // fbit_pos normally, which is one bit past the read bit). + // Use latched data if that bit is before the latched LSB + diff = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit, + track_bits); + diff = diff + 8; // Calc to the MSB + msb_bit = (sync_dval >> 8) & 0xff; + if((msb_bit >= 8) && (diff > msb_bit)) { + // We've not seen the LSB of this data: ret latched data + dbg_log_info(dfcyc, bit_pos * 2, + (msb_bit << 16) | (diff & 0xffff), + 0x2200ec); + bit_pos = iwm_calc_bit_sum(bit_pos, -msb_bit + 8, + track_bits); + dbg_log_info(dfcyc, bit_pos * 2, g_iwm.last_rd_bit*2, + 0x200ec); + } else { + msb_bit = sync_dval & 0xff; + if(diff <= msb_bit) { + // Handle case of 10-bit nibble which we've + // already returned...don't return it again! + msb_bit = 1; + } + } +#if 0 + printf("Latch mode diff:%d, msb_bit:%d, new_msb:%d\n", + diff, msb_bit, (int)(sync_dval & 0xff)); +#endif + dbg_log_info(dfcyc, ((word32)diff << 12) | (msb_bit & 0xff), + (word32)sync_dval, 0x100ec); + } else { + if((sync_dval & 0xff) <= 1) { + // The LSbit or the next one is a sync bit--but we need + // to ignore it. The LSbit is not valid yet, and + // the next bit being the MSB of the next nibble will + // be ignored to make the 7usec hold time of the + // previous nibble work + sync_dval = sync_dval >> 8; + msb_bit = sync_dval & 0xff; + } + } + val = (word32)dval; + if(msb_bit < 8) { + // We only have a partial byte + val = val & ((2ULL << msb_bit) - 1); + } else if(msb_bit < 63) { + val = (word32)(dval >> (msb_bit - 8)); + } + + // val is now valid from bit 8 down to bit 1 + // We've allowed in to dval an extra bit which we must toss + val = val >> 1; + + g_iwm.last_rd_bit = bit_pos; + dbg_log_info(dfcyc, (msb_bit << 24) | (fbit_pos >> 8), + (word32)(dval0 << 8) | (val & 0xff), 0xec); +#if 0 + if(forced_bits >= 0) { + printf("Forced bits, msb:%d, val:%02x, dval:%016llx b_p:%06x\n", + msb_bit, val, dval, bit_pos * 2); + } +#endif + return val & 0xff; +} + +void +iwm_write_data(Disk *dsk, word32 val, dword64 dfcyc) +{ + Trk *trk; + word32 track_bits, bit_pos, fbit_pos, bit_last, tmp_val; + int bits; + + trk = dsk->cur_trk_ptr; + if((trk == 0) || dsk->write_prot) { + return; + } + track_bits = dsk->cur_track_bits; + + bit_pos = (dsk->cur_fbit_pos + 511) >> 9; + if(track_bits && (bit_pos >= track_bits)) { + bit_pos = bit_pos - track_bits; + if(bit_pos >= track_bits) { + bit_pos = bit_pos % track_bits; + } + } +#if 0 + printf("iwm_write_data: %02x %016llx, bit*2:%06x\n", val, dfcyc, + bit_pos *2); +#endif + if(dsk->disk_525) { + if(!g_slow_525_emul_wr) { + g_slow_525_emul_wr = 1; + engine_recalc_events(); + if(track_bits && g_fast_disk_emul) { + fbit_pos = iwm_fastemul_start_write(dsk, dfcyc); + bit_pos = fbit_pos >> 9; + } + } + } + dsk->cur_fbit_pos = bit_pos * 512; + if(g_iwm.num_active_writes == 0) { + // No write was pending, enter write mode now + // printf("Starting write of data to the track, track now:\n"); + // iwm_show_track(-1, -1, dfcyc); + // printf("Write data to track at bit*2:%06x\n", bit_pos); + iwm_start_write(dsk, bit_pos, val, 0); + return; + } + if(track_bits == 0) { + halt_printf("Impossible: track_bits: 0\n"); + return; + } + if(g_iwm.state & 2) { // async handshake mode, 3.5" + iwm_write_data35(dsk, val, dfcyc); + return; + } + + // From here on, it's 5.25" disks only + bit_last = g_iwm.wr_last_bit[0]; + bits = iwm_calc_bit_diff(bit_pos, bit_last, track_bits); + if(bits >= 500) { + halt_printf("bits are %d. bit*2:%06x, bit_last*2:%06x\n", + bits, bit_pos * 2, bit_last * 2); + bits = 40; + } + iwm_write_one_nib(dsk, bits, dfcyc); + + tmp_val = g_iwm.write_val; + dbg_log_info(dfcyc, (g_iwm.wr_last_bit[0] << 25) | (bit_last * 2), + (bits << 16) | ((val & 0xff) << 8) | (tmp_val & 0xff), 0xed); + g_iwm.write_val = val; +} + +void +iwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior) +{ + int num; + + g_iwm.write_val = val; + num = 0; + num = iwm_start_write_act(dsk, bit_pos, num, no_prior, 0); + num = iwm_start_write_act(dsk, bit_pos, num, no_prior, -1); // -0.25 + num = iwm_start_write_act(dsk, bit_pos, num, no_prior, +1); // +0.25 + num = iwm_start_write_act(dsk, bit_pos, num, no_prior, -2); // -0.50 + num = iwm_start_write_act(dsk, bit_pos, num, no_prior, +2); // +0.50 + g_iwm.num_active_writes = num; + + woz_maybe_reparse(dsk); +} + +int +iwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta) +{ + Trk *trkptr; + word32 qtr_track, track_bits, bit_diff, prior_sum, allow_diff; + + qtr_track = (dsk->cur_frac_track + 0x8000) >> 16; + qtr_track += delta; + if(qtr_track >= 160) { // Could be unsigned wrap around + if(delta == 0) { + halt_printf("iwm_start_write_act0, qtr_track:%04x\n", + qtr_track); + g_iwm.num_active_writes = 0; + } + return num; + } + if(!dsk->disk_525 && delta) { + return num; // 3.5" does not affect adjacent trks + } + + trkptr = &(dsk->trks[qtr_track]); + track_bits = trkptr->track_bits; + if(track_bits == 0) { + if((delta == -2) || (delta == 2)) { + return num; // Nothing to do + } + if((delta == -1) && (qtr_track > 0)) { + if(trkptr[-1].track_bits == 0) { + return num; // Nothing to do + } + } + if((delta == 1) && (qtr_track < 159)) { + if(trkptr[1].track_bits == 0) { + return num; // Nothing to do + } + } + // Otherwise, we need to create this track and write to it + track_bits = woz_add_a_track(dsk, qtr_track); + } + if(track_bits) { + bit_pos = bit_pos % track_bits; + } + bit_diff = iwm_calc_bit_diff(bit_pos, g_iwm.wr_last_bit[num], + track_bits); + prior_sum = 0; + allow_diff = 16 + (16 * g_fast_disk_emul); + if((g_iwm.wr_qtr_track[num] == qtr_track) && (bit_diff < allow_diff)) { + // consider this write a continuation of the previous write + prior_sum = g_iwm.wr_prior_num_bits[num] + + g_iwm.wr_num_bits[num] + bit_diff; +#if 0 + printf("prior_sum is %d, qtr_track:%04x, bit_diff:%d\n", + prior_sum, qtr_track, bit_diff); +#endif + } else { +#if 0 + printf("No prior_sum, qtr_track:%04x vs %04x, bit_diff:%08x, " + "bit_pos:%05x wr_last_bit:%05x\n", + g_iwm.wr_qtr_track[num], qtr_track, bit_diff, + bit_pos * 2, g_iwm.wr_last_bit[num]*2); +#endif + } + if(no_prior) { + prior_sum = 0; + } + if(delta == 0) { + dsk->cur_fbit_pos = bit_pos << 9; + iwm_move_to_qtr_track(dsk, qtr_track); + } + g_iwm.wr_last_bit[num] = bit_pos; + g_iwm.wr_qtr_track[num] = qtr_track; + g_iwm.wr_num_bits[num] = 0; + g_iwm.wr_prior_num_bits[num] = prior_sum; + g_iwm.wr_delta[num] = abs(delta); // 0, 1 or 2 + return num + 1; +} + +void +iwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc) +{ + // Just always write 8 bits to the track + iwm_write_one_nib(dsk, 8, dfcyc); + g_iwm.write_val = val; + dsk->cur_fbit_pos = g_iwm.wr_last_bit[0] * 512; +} + +void +iwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc) +{ + Trk *trkptr; + word32 last_bit, qtr_track, num_bits, bit_start, delta, track_bits; + word32 prior_sum; + int num_active_writes; + int i; + + // Flush out previous write, then turn writing off + num_active_writes = g_iwm.num_active_writes; +#if 0 + printf("In iwm_write_end at %016llx, num:%d, %d\n", dfcyc, + num_active_writes, dsk->disk_dirty); +#endif + if(num_active_writes == 0) { + return; // Invalid, not in a write + } + if(write_through_now) { + iwm_write_data(dsk, 0, dfcyc); + } + + for(i = 0; i < num_active_writes; i++) { + last_bit = g_iwm.wr_last_bit[i]; + qtr_track = g_iwm.wr_qtr_track[i]; + num_bits = g_iwm.wr_num_bits[i]; + delta = g_iwm.wr_delta[i]; +#if 0 + printf(" end %d, last_bit:%05x, qtrk:%04x, num_b:%d, " + "delta:%d\n", i, last_bit * 2, qtr_track, num_bits, + delta); +#endif + if((num_bits == 0) || (qtr_track >= 160)) { + continue; + } + trkptr = &(dsk->trks[qtr_track]); + track_bits = trkptr->track_bits; + prior_sum = g_iwm.wr_prior_num_bits[i]; + if((num_bits + prior_sum) >= track_bits) { + // Full track write. If delta != 0, erase this track + printf("Full track write at qtrk:%04x %016llx\n", + qtr_track, dfcyc); +#if 0 + if(qtr_track == 4) { + halt_printf("Full track at qtr_trk:4\n"); + } +#endif + if(delta != 0) { + woz_remove_a_track(dsk, qtr_track); + printf("TRACK %04x REMOVED\n", qtr_track); + continue; + } + g_iwm.wr_prior_num_bits[i] = 0; + } + + // Otherwise, recalc sync for this track + if(num_bits >= track_bits) { + num_bits = num_bits % track_bits; + } + bit_start = iwm_calc_bit_sum(last_bit, -(int)num_bits - 24, + track_bits); + iwm_recalc_sync_from(dsk, qtr_track, bit_start, dfcyc); +#if 0 + printf("Wrote %d bits to qtrk:%04x at %05x %016llx, i:%d,%d, " + "%d\n", num_bits, qtr_track, bit_start*2, dfcyc, i, + num_active_writes, dsk->disk_dirty); +#endif + } + g_iwm.num_active_writes = 0; + + woz_maybe_reparse(dsk); +} + +void +iwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc) +{ + word32 qtr_track, bit_pos, delta, val, track_bits; + int num; + int i; + + num = g_iwm.num_active_writes; + val = g_iwm.write_val; + for(i = 0; i < num; i++) { + qtr_track = g_iwm.wr_qtr_track[i]; + bit_pos = g_iwm.wr_last_bit[i]; + delta = g_iwm.wr_delta[i]; + dbg_log_info(dfcyc, val, bit_pos * 2, 0x200ed); + if(delta == 2) { // Trk +0.50 and -0.50: corrupt + val = (val & 0x7f) ^ i ^ 0x0c; + } + bit_pos = disk_nib_out_raw(dsk, qtr_track, val, bits, bit_pos, + dfcyc); + track_bits = dsk->trks[qtr_track].track_bits; + if(bit_pos >= track_bits) { + bit_pos = bit_pos - track_bits; + } + g_iwm.wr_last_bit[i] = bit_pos; + g_iwm.wr_num_bits[i] += bits; + dbg_log_info(dfcyc, (bits << 24) | (bit_pos * 2), + 2*iwm_calc_bit_sum(bit_pos, 0-g_iwm.wr_num_bits[i], + track_bits), 0x300ed); + } +} + +void +iwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc) +{ + Trk *trkptr; + byte *bptr, *sync_ptr; + word32 track_bits, val, this_sync, sync0; + int pos, next_pos, wrap, bit, last_bit, last_byte, match, this_bits; + + // We are called with a bit_pos 3 bytes before the desired byte. + // Look at pos, pos-1, pos+1 in a safe way, and find a valid sync_num + if(dfcyc) { + //printf("new sync from %d, %016llx %p\n", bit_pos, dfcyc, dsk); + } + if(qtr_track >= 160) { + halt_printf("iwm_recalc_sync_from bad qtr:%04x bit_pos:%06x\n", + qtr_track, bit_pos); + return; + } + trkptr = &(dsk->trks[qtr_track]); + + track_bits = trkptr->track_bits; + if(track_bits == 0) { + halt_printf("iwm_recalc_sync_from track_bits 0 for %04x\n", + qtr_track); + return; + } + last_byte = (track_bits - 1) >> 3; + last_bit = ((track_bits - 1) & 7) + 1; // 1...8 + + sync_ptr = &(trkptr->sync_ptr[0]); + bptr = &(trkptr->raw_bptr[0]); + pos = bit_pos >> 3; + bit = bit_pos & 7; // 0...7 + sync0 = sync_ptr[pos]; + if(sync0 >= 8) { + if(pos > 0) { + pos--; + } else { + pos++; // pos is now 1 + } + sync0 = sync_ptr[pos]; + } + bit = (7 - sync0) & 7; + match = 0; + wrap = 0; + next_pos = pos + 1; + // cnt = 0; + // printf("recalc_sync_from pos:%04x, bit:%d\n", pos, bit); + while(1) { + val = bptr[pos]; + this_bits = 8; + this_sync = sync_ptr[pos]; + sync_ptr[pos] = 0x20; + next_pos = pos + 1; + if(pos >= last_byte) { +#if 0 + printf("At last_byte, val:%02x, pos:%04x, last_bit:%d, " + "bit:%d\n", val, pos, last_bit, bit); +#endif + this_bits = last_bit; + val = val & (0xff00 >> last_bit); + next_pos = 0; + wrap++; + if(wrap >= 3) { + halt_printf("no stable sync found\n"); + return; + } + if(bit >= this_bits) { + // Skip over this partial byte to byte 0 + bit -= this_bits; + pos = 0; + continue; + } + } +#if 0 + if(wrap || (pos >= 0x2630)) { + printf("pos:%04x, bit:%d, val:%04x, this_bits:%d\n", + pos, bit, val, this_bits); + } +#endif +#if 0 + if((cnt++ < 10) && (dsk->cur_frac_track == 0)) { + printf("sync[%04x]=%02x, val:%02x bit:%d, this_b:%d\n", + pos, this_sync, val, bit, this_bits); + } +#endif + // bit is within this byte. Find next set bit + while(bit < this_bits) { + if(((val << bit) & 0x80) == 0) { + // Slide to next bit + bit++; + continue; + } + sync_ptr[pos] = 7 - bit; + if(this_sync == (word32)(7 - bit)) { + match++; + if(match >= 7) { +#if 0 + printf("match %d at pos:%04x wrap:%d\n", + match, pos, wrap); +#endif + return; + } + } else { + match = 0; + } + break; + } + bit = (bit + 8 - this_bits) & 7; + pos = next_pos; + } +} + +/* c600 */ +void +sector_to_partial_nib(byte *in, byte *nib_ptr) +{ + byte *aux_buf, *nib_out; + word32 val, val2; + int x; + int i; + + /* Convert 256(+1) data bytes to 342+1 disk nibbles */ + + aux_buf = nib_ptr; + nib_out = nib_ptr + 0x56; + + for(i = 0; i < 0x56; i++) { + aux_buf[i] = 0; + } + + x = 0x55; + for(i = 0x101; i >= 0; i--) { + val = in[i]; + if(i >= 0x100) { + val = 0; + } + val2 = (aux_buf[x] << 1) + (val & 1); + val = val >> 1; + val2 = (val2 << 1) + (val & 1); + val = val >> 1; + nib_out[i] = val; + aux_buf[x] = val2; + x--; + if(x < 0) { + x = 0x55; + } + } +} + + +int +disk_unnib_4x4(Disk *dsk) +{ + int val1; + int val2; + + val1 = iwm_read_data_fast(dsk, 0); + val2 = iwm_read_data_fast(dsk, 0); + + return ((val1 << 1) + 1) & val2; +} + +int +iwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf) +{ + byte aux_buf[0x80]; + int sector_done[16]; + byte *buf; + word32 val, val2, prev_val, save_frac_track; + int track_len, vol, track, phys_sec, log_sec, cksum, x, my_nib_cnt; + int save_fbit_pos, tmp_fbit_pos, status, ret, num_sectors_done; + int i; + + //printf("iwm_denib_track525\n"); + + save_fbit_pos = dsk->cur_fbit_pos; + save_frac_track = dsk->cur_frac_track; + iwm_move_to_qtr_track(dsk, qtr_track); + + dsk->cur_fbit_pos = 0; + g_fast_disk_unnib = 1; + + track_len = (dsk->cur_track_bits + 7) >> 3; + + for(i = 0; i < 16; i++) { + sector_done[i] = 0; + } + + num_sectors_done = 0; + + val = 0; + status = -1; + my_nib_cnt = 0; + while(my_nib_cnt++ < 2*track_len) { + /* look for start of a sector */ + if(val != 0xd5) { + val = iwm_read_data_fast(dsk, 0); + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0x96) { + continue; + } + + /* It's a sector start */ + vol = disk_unnib_4x4(dsk); + track = disk_unnib_4x4(dsk); + phys_sec = disk_unnib_4x4(dsk); + if(phys_sec < 0 || phys_sec > 15) { + printf("Track %02x, read sec as %02x\n", + qtr_track >> 2, phys_sec); + break; + } + if(dsk->image_type == DSK_TYPE_DOS33) { + log_sec = phys_to_dos_sec[phys_sec]; + } else { + log_sec = phys_to_prodos_sec[phys_sec]; + } + cksum = disk_unnib_4x4(dsk); + if((vol ^ track ^ phys_sec ^ cksum) != 0) { + /* not correct format */ + printf("Track %02x not DOS 3.3 since hdr cksum, %02x " + "%02x %02x %02x\n", qtr_track >> 2, + vol, track, phys_sec, cksum); + break; + } + + /* see what sector it is */ + if(track != (qtr_track >> 2) || (phys_sec < 0) || + (phys_sec > 15)) { + printf("Track %02x bad since track: %02x, sec: %02x\n", + qtr_track>>2, track, phys_sec); + break; + } + + if(sector_done[phys_sec]) { + printf("Already done sector %02x on track %02x!\n", + phys_sec, qtr_track>>2); + break; + } + + /* So far so good, let's do it! */ + val = 0; + i = 0; + while(i < NIBS_FROM_ADDR_TO_DATA) { + i++; + if(val != 0xd5) { + val = iwm_read_data_fast(dsk, 0); + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xad) { + continue; + } + + /* got it, just break */ + break; + } + + if(i >= NIBS_FROM_ADDR_TO_DATA) { + printf("No data header, track %02x, sec %02x at %07x\n", + qtr_track>>2, phys_sec, dsk->cur_fbit_pos); + break; + } + + buf = outbuf + 0x100*log_sec; + + /* Data start! */ + prev_val = 0; + for(i = 0x55; i >= 0; i--) { + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + if(val2 >= 0x100) { + printf("Bad data area1, val:%02x,val2:%03x\n", + val, val2); + printf(" i:%03x, fbit_pos:%08x\n", i, + dsk->cur_fbit_pos); + break; + } + prev_val = val2 ^ prev_val; + aux_buf[i] = prev_val; + } + + /* rest of data area */ + for(i = 0; i < 0x100; i++) { + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + if(val2 >= 0x100) { + printf("Bad data area2, read: %02x\n", val); + printf(" fbit_pos: %07x\n", dsk->cur_fbit_pos); + break; + } + prev_val = val2 ^ prev_val; + buf[i] = prev_val; + } + + /* checksum */ + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + if(val2 >= 0x100) { + printf("Bad data area3, read: %02x\n", val); + printf(" fbit_pos: %07x\n", dsk->cur_fbit_pos); + break; + } + if(val2 != prev_val) { + printf("Bad data cksum, got %02x, wanted: %02x\n", + val2, prev_val); + printf(" fbit_pos: %07x\n", dsk->cur_fbit_pos); + break; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xde) { + printf("No 0xde at end of sector data:%02x\n", val); + printf(" fbit_pos: %07x\n", dsk->cur_fbit_pos); + break; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + printf("No 0xde,0xaa at end of sector:%02x\n", val); + printf(" fbit_pos: %07x\n", dsk->cur_fbit_pos); + break; + } + + /* Got this far, data is good, merge aux_buf into buf */ + x = 0x55; + for(i = 0; i < 0x100; i++) { + val = aux_buf[x]; + val2 = (buf[i] << 1) + (val & 1); + val = val >> 1; + val2 = (val2 << 1) + (val & 1); + buf[i] = val2; + val = val >> 1; + aux_buf[x] = val; + x--; + if(x < 0) { + x = 0x55; + } + } + sector_done[phys_sec] = 1; + num_sectors_done++; + if(num_sectors_done >= 16) { + status = 0; + break; + } + } + + tmp_fbit_pos = dsk->cur_fbit_pos; + g_fast_disk_unnib = 0; + + ret = 0; + if(status != 0) { + printf("Nibblization not done, %02x sectors found qtrk %04x, " + "drive:%d, slot:%d\n", num_sectors_done, qtr_track, + dsk->drive, dsk->disk_525 + 5); + printf("my_nib_cnt: %05x, fbit_pos:%07x, trk_len:%05x\n", + my_nib_cnt, tmp_fbit_pos, track_len); + ret = 16; + for(i = 0; i < 16; i++) { + printf("sector_done[%d] = %d\n", i, sector_done[i]); + if(sector_done[i]) { + ret--; + } + } + iwm_show_a_track(dsk, dsk->cur_trk_ptr, 0); + if(!ret) { + ret = -1; + } + } else { + //printf("iwm_denib_track525 succeeded\n"); + } + + dsk->cur_fbit_pos = save_fbit_pos; + iwm_move_to_ftrack(dsk, save_frac_track, 0, 0); + + return ret; +} + +int +iwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf) +{ + word32 buf_c00[0x100]; + word32 buf_d00[0x100]; + word32 buf_e00[0x100]; + int sector_done[16]; + byte *buf; + word32 tmp_5c, tmp_5d, tmp_5e, tmp_66, tmp_67, val, val2; + word32 save_frac_track; + int num_sectors_done, track_len, phys_track, phys_sec, phys_side; + int phys_capacity, cksum, tmp, track, side, num_sectors, x, y; + int carry, my_nib_cnt, save_fbit_pos, status, ret; + int i; + + save_fbit_pos = dsk->cur_fbit_pos; + save_frac_track = dsk->cur_frac_track; + iwm_move_to_qtr_track(dsk, qtr_track); + + if(dsk->cur_trk_ptr == 0) { + return 0; + } + + dsk->cur_fbit_pos = 0; + g_fast_disk_unnib = 1; + + track_len = dsk->cur_track_bits >> 3; + + num_sectors = g_track_bytes_35[qtr_track >> 5] >> 9; + + for(i = 0; i < num_sectors; i++) { + sector_done[i] = 0; + } + + num_sectors_done = 0; + + val = 0; + status = -1; + my_nib_cnt = 0; + + track = qtr_track >> 1; + side = qtr_track & 1; + + while(my_nib_cnt++ < 2*track_len) { + /* look for start of a sector */ + if(val != 0xd5) { + val = iwm_read_data_fast(dsk, 0); + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0x96) { + continue; + } + + /* It's a sector start */ + val = iwm_read_data_fast(dsk, 0); + phys_track = g_from_disk_byte[val]; + if(phys_track != (track & 0x3f)) { + printf("Track %02x.%d, read track %02x, %02x\n", + track, side, phys_track, val); + break; + } + + phys_sec = g_from_disk_byte[iwm_read_data_fast(dsk, 0)]; + if((phys_sec < 0) || (phys_sec >= num_sectors)) { + printf("Track %02x.%d, read sector %02x??\n", + track, side, phys_sec); + break; + } + phys_side = g_from_disk_byte[iwm_read_data_fast(dsk, 0)]; + + if(phys_side != ((side << 5) + (track >> 6))) { + printf("Track %02x.%d, read side %02x??\n", + track, side, phys_side); + break; + } + phys_capacity = g_from_disk_byte[iwm_read_data_fast(dsk, 0)]; + if(phys_capacity != 0x24 && phys_capacity != 0x22) { + printf("Track %02x.%x capacity: %02x != 0x24/22\n", + track, side, phys_capacity); + } + cksum = g_from_disk_byte[iwm_read_data_fast(dsk, 0)]; + + tmp = phys_track ^ phys_sec ^ phys_side ^ phys_capacity; + if(cksum != tmp) { + printf("Track %02x.%d, sector %02x, cksum: %02x.%02x\n", + track, side, phys_sec, cksum, tmp); + break; + } + + + if(sector_done[phys_sec]) { + printf("Already done sector %02x on track %02x.%x!\n", + phys_sec, track, side); + break; + } + + /* So far so good, let's do it! */ + val = 0; + for(i = 0; i < 38; i++) { + val = iwm_read_data_fast(dsk, 0); + if(val == 0xd5) { + break; + } + } + if(val != 0xd5) { + printf("No data header, track %02x.%x, sec %02x\n", + track, side, phys_sec); + break; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + printf("Bad data hdr1,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + printf("fbit_pos: %08x\n", dsk->cur_fbit_pos); + break; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xad) { + printf("Bad data hdr2,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + printf("dsk->cur_fbit_pos:%07x\n", dsk->cur_fbit_pos); + break; + } + + buf = outbuf + (phys_sec << 9); + + /* check sector again */ + tmp = g_from_disk_byte[iwm_read_data_fast(dsk, 0)]; + if(tmp != phys_sec) { + printf("Bad data hdr3,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Data start! */ + tmp_5c = 0; + tmp_5d = 0; + tmp_5e = 0; + y = 0xaf; + carry = 0; + + while(y > 0) { +/* 626f */ + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + if(val2 >= 0x100) { + printf("Bad data area1b, read: %02x\n", val); + printf(" i:%03x, fbit_pos:%07x\n", i, + dsk->cur_fbit_pos); + break; + } + tmp_66 = val2; + + tmp_5c = tmp_5c << 1; + carry = (tmp_5c >> 8); + tmp_5c = (tmp_5c + carry) & 0xff; + + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + if(val2 >= 0x100) { + printf("Bad data area2, read: %02x\n", val); + break; + } + + val2 = val2 + ((tmp_66 << 2) & 0xc0); + + val2 = val2 ^ tmp_5c; + buf_c00[y] = val2; + + tmp_5e = val2 + tmp_5e + carry; + carry = (tmp_5e >> 8); + tmp_5e = tmp_5e & 0xff; +/* 62b8 */ + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + val2 = val2 + ((tmp_66 << 4) & 0xc0); + val2 = val2 ^ tmp_5e; + buf_d00[y] = val2; + tmp_5d = val2 + tmp_5d + carry; + + carry = (tmp_5d >> 8); + tmp_5d = tmp_5d & 0xff; + + y--; + if(y <= 0) { + break; + } + +/* 6274 */ + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + val2 = val2 + ((tmp_66 << 6) & 0xc0); + val2 = val2 ^ tmp_5d; + buf_e00[y+1] = val2; + + tmp_5c = val2 + tmp_5c + carry; + carry = (tmp_5c >> 8); + tmp_5c = tmp_5c & 0xff; + } + +/* 62d0 */ + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val]; + + tmp_66 = (val2 << 6) & 0xc0; + tmp_67 = (val2 << 4) & 0xc0; + val2 = (val2 << 2) & 0xc0; + + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val] + val2; + if(tmp_5e != (word32)val2) { + printf("Checksum 5e bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val] + tmp_67; + if(tmp_5d != (word32)val2) { + printf("Checksum 5d bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + val = iwm_read_data_fast(dsk, 0); + val2 = g_from_disk_byte[val] + tmp_66; + if(tmp_5c != (word32)val2) { + printf("Checksum 5c bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Whew, got it!...check for DE AA */ + val = iwm_read_data_fast(dsk, 0); + if(val != 0xde) { + printf("Bad data epi1,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + printf("fbit_pos: %08x\n", dsk->cur_fbit_pos); + break; + } + + val = iwm_read_data_fast(dsk, 0); + if(val != 0xaa) { + printf("Bad data epi2,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Now, convert buf_c/d/e to output */ +/* 6459 */ + y = 0; + for(x = 0xab; x >= 0; x--) { + *buf++ = buf_c00[x]; + y++; + if(y >= 0x200) { + break; + } + + *buf++ = buf_d00[x]; + y++; + if(y >= 0x200) { + break; + } + + *buf++ = buf_e00[x]; + y++; + if(y >= 0x200) { + break; + } + } + + sector_done[phys_sec] = 1; + num_sectors_done++; + if(num_sectors_done >= num_sectors) { + status = 0; + break; + } + val = 0; + } + + g_fast_disk_unnib = 0; + + ret = 0; + if(status != 0) { + printf("dsk->fbit_pos: %07x, status: %d\n", dsk->cur_fbit_pos, + status); + for(i = 0; i < num_sectors; i++) { + printf("sector done[%d] = %d\n", i, sector_done[i]); + } + printf("Nibblization not done, %02x blocks found qtrk %04x\n", + num_sectors_done, qtr_track); + + ret = -1; + } + + dsk->cur_fbit_pos = save_fbit_pos; + iwm_move_to_ftrack(dsk, save_frac_track, 0, 0); + + return ret; + +} + +/* ret = 1 -> dirty data written out */ +/* ret = 0 -> not dirty, no error */ +/* ret < 0 -> error */ +int +iwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf) +{ + Trk *trk; + dword64 dunix_pos, dret, unix_len; + int ret; + + trk = &(dsk->trks[qtr_track]); + if((trk->track_bits == 0) || (trk->dirty == 0)) { + return 0; + } + + printf("iwm_track_to_unix dirty qtr:%04x, dirty:%d\n", qtr_track, + trk->dirty); +#if 0 + if((qtr_track & 3) && disk_525) { + halt_printf("You wrote to phase %02x! Can't wr bk to unix!\n", + qtr_track); + dsk->write_through_to_unix = 0; + return -1; + } +#endif + + if(dsk->wozinfo_ptr) { // WOZ disk + outbuf = trk->raw_bptr; + ret = 0; + } else { + if(dsk->disk_525) { + if(qtr_track & 3) { + // Not a valid track + ret = -1; + } else { + ret = iwm_denib_track525(dsk, qtr_track, + outbuf); + } + } else { + ret = iwm_denib_track35(dsk, qtr_track, outbuf); + } + } + + if(ret != 0) { + return -1; + } + + /* Write it out */ + dunix_pos = trk->dunix_pos; + unix_len = trk->unix_len; + if(unix_len < 0x1000) { + halt_printf("Disk:%s trk:%06x, dunix_pos:%08llx, len:%08llx\n", + dsk->name_ptr, dsk->cur_frac_track, dunix_pos, + unix_len); + return -1; + } + + trk->dirty = 0; + if(dsk->dynapro_info_ptr) { + return dynapro_write(dsk, outbuf, dunix_pos, (word32)unix_len); + } + + dret = cfg_write_to_fd(dsk->fd, outbuf, dunix_pos, unix_len); +#if 0 + printf("Write: qtr_trk:%04x, dunix_pos:%08llx, %s\n", qtr_track, + dunix_pos, dsk->name_ptr); +#endif + if(dret != unix_len) { + printf("write: %08llx, errno:%d, trk: %06x, disk: %s\n", + dret, errno, dsk->cur_frac_track, dsk->name_ptr); + return -1; + } + if(dsk->wozinfo_ptr) { // WOZ disk + printf("Wrote track %07x to fd:%d off:%08llx, len:%07llx\n", + dsk->cur_frac_track, dsk->fd, dunix_pos, unix_len); + woz_rewrite_crc(dsk, 0); + } + + return 1; +} + +void +show_hex_data(byte *buf, int count) +{ + int i; + + for(i = 0; i < count; i += 16) { + printf("%04x: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, + buf[i+0], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7], + buf[i+8], buf[i+9], buf[i+10], buf[i+11], + buf[i+12], buf[i+13], buf[i+14], buf[i+15]); + } +} + +void +iwm_check_nibblization(dword64 dfcyc) +{ + Disk *dsk; + int slot, drive, sel35; + + drive = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1; + slot = 6; + if(g_iwm.state & IWM_STATE_MOTOR_ON) { + sel35 = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1; + } else { + sel35 = (g_iwm.state >> IWM_BIT_LAST_SEL35) & 1; + } + if(sel35) { + dsk = &(g_iwm.drive35[drive]); + slot = 5; + } else { + dsk = &(g_iwm.drive525[drive]); + } + printf("iwm_check_nibblization, s%d d%d\n", slot, drive); + disk_check_nibblization(dsk, 0, 4096, dfcyc); +} + +void +disk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc) +{ + byte buffer[0x3000]; + word32 qtr_track; + int ret, ret2; + int i; + + if(size > 0x3000) { + printf("size %08x is > 0x3000, disk_check_nibblization\n",size); + exit(3); + } + + for(i = 0; i < size; i++) { + buffer[i] = 0; + } + + //printf("About to call iwm_denib_track*, here's the track:\n"); + //iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc); + + qtr_track = (word32)(dsk->cur_trk_ptr - &(dsk->trks[0])); + if(qtr_track >= 160) { + halt_printf("cur_trk_ptr points to bad qtr_track:%08x\n", + qtr_track); + return; + } + if(dsk->disk_525) { + ret = iwm_denib_track525(dsk, qtr_track, &(buffer[0])); + } else { + ret = iwm_denib_track35(dsk, qtr_track, &(buffer[0])); + } + + ret2 = -1; + if(in_buf) { + for(i = 0; i < size; i++) { + if(buffer[i] != in_buf[i]) { + printf("buffer[%04x]: %02x != %02x\n", i, + buffer[i], in_buf[i]); + ret2 = i; + break; + } + } + } + + if((ret != 0) || (ret2 >= 0)) { + printf("disk_check_nib ret:%d, ret2:%d for track %06x\n", + ret, ret2, dsk->cur_frac_track); + if(in_buf) { + show_hex_data(in_buf, 0x1000); + } + show_hex_data(buffer, size); + iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc); + if(ret == 16) { + printf("No sectors found, ignore it\n"); + return; + } + + halt_printf("Stop\n"); + exit(2); + } +} + +#define TRACK_BUF_LEN 0x2000 + +void +disk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len, + int len_bits, dword64 dfcyc) +{ + byte track_buf[TRACK_BUF_LEN]; + Trk *trk; + byte *bptr; + dword64 dret, dlen; + word32 num_bytes, must_clear_track; + int i; + + /* Read track from dsk int track_buf */ +#if 0 + printf("disk_unix_to_nib: qtr:%04x, unix_pos:%08llx, unix_len:%08x, " + "len_bits:%06x\n", qtr_track, dunix_pos, unix_len, len_bits); +#endif + + must_clear_track = 0; + + if(unix_len > TRACK_BUF_LEN) { + printf("diks_unix_to_nib: requested len of image %s = %05x\n", + dsk->name_ptr, unix_len); + } + + bptr = dsk->raw_data; + if(bptr != 0) { + // raw_data is valid, so use it + if((dunix_pos + unix_len) > dsk->raw_dsize) { + must_clear_track = 1; + } else { + bptr += dunix_pos; + for(i = 0; i < (int)unix_len; i++) { + track_buf[i] = bptr[i]; + } + } + } else if(unix_len > 0) { + dret = kegs_lseek(dsk->fd, dunix_pos, SEEK_SET); + if(dret != dunix_pos) { + printf("lseek of disk %s len 0x%llx ret: %lld, errno:" + "%d\n", dsk->name_ptr, dunix_pos, dret, errno); + must_clear_track = 1; + } + + dlen = read(dsk->fd, track_buf, unix_len); + if(dlen != unix_len) { + printf("read of disk %s q_trk %d ret: %lld, errno:%d\n", + dsk->name_ptr, qtr_track, dlen, errno); + must_clear_track = 1; + } + } + + if(must_clear_track) { + for(i = 0; i < TRACK_BUF_LEN; i++) { + track_buf[i] = 0; + } + } + +#if 0 + printf("Q_track %02x dumped out\n", qtr_track); + + for(i = 0; i < 4096; i += 32) { + printf("%04x: %02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x\n", i, + track_buf[i+0], track_buf[i+1], track_buf[i+2], + track_buf[i+3], track_buf[i+4], track_buf[i+5], + track_buf[i+6], track_buf[i+7], track_buf[i+8], + track_buf[i+9], track_buf[i+10], track_buf[i+11], + track_buf[i+12], track_buf[i+13], track_buf[i+14], + track_buf[i+15], track_buf[i+16], track_buf[i+17], + track_buf[i+18], track_buf[i+19], track_buf[i+20], + track_buf[i+21], track_buf[i+22], track_buf[i+23], + track_buf[i+24], track_buf[i+25], track_buf[i+26], + track_buf[i+27], track_buf[i+28], track_buf[i+29], + track_buf[i+30], track_buf[i+31]); + } +#endif + + dsk->cur_fbit_pos = 0; /* for consistency */ + dsk->raw_bptr_malloc = 1; + + trk = &(dsk->trks[qtr_track]); + num_bytes = 2 + ((len_bits + 7) >> 3) + 4; + trk->track_bits = len_bits; + trk->dunix_pos = dunix_pos; + trk->unix_len = unix_len; + trk->dirty = 0; + trk->raw_bptr = (byte *)malloc(num_bytes); + trk->sync_ptr = (byte *)malloc(num_bytes); + + iwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc); + + /* create nibblized image */ + + if(dsk->disk_525 && (dsk->image_type == DSK_TYPE_NIB)) { + iwm_nibblize_track_nib525(dsk, track_buf, qtr_track, unix_len); + } else if(dsk->disk_525) { + iwm_nibblize_track_525(dsk, track_buf, qtr_track, dfcyc); + } else { + iwm_nibblize_track_35(dsk, track_buf, qtr_track, unix_len, + dfcyc); + } + + //printf("For qtr_track:%04x, trk->dirty:%d\n", qtr_track, trk->dirty); + trk->dirty = 0; +} + +void +iwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track, + word32 unix_len) +{ + byte *bptr, *sync_ptr; + int len; + int i; + + // This is the old, dumb .nib format. It consists of 0x1a00 bytes + // per track, but there's no sync information. Just mark each byte + // as being sync=7 + len = unix_len; + bptr = &(dsk->cur_trk_ptr->raw_bptr[0]); + sync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]); + for(i = 0; i < len; i++) { + bptr[i] = track_buf[i]; + } + for(i = 0; i < len; i++) { + sync_ptr[i] = 7; + } + if(dsk->cur_track_bits != (unix_len * 8)) { + fatal_printf("Track %d.%02d of nib image should be bits:%06x " + "but it is: %06x\n", qtr_track >> 2, (qtr_track & 3)*25, + unix_len * 8, dsk->cur_track_bits); + } + + iwm_printf("Nibblized q_track %02x\n", qtr_track); +} + +void +iwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc) +{ + byte partial_nib_buf[0x300]; + word32 val, last_val; + int phys_sec, log_sec, num_sync; + int i; + +#if 0 + printf("nibblize track 525, qtr_track:%04x, trk:%p, trk->raw_bptr:%p, " + "sync_ptr:%p\n", qtr_track, trk, trk->raw_bptr, trk->sync_ptr); +#endif + + for(phys_sec = 0; phys_sec < 16; phys_sec++) { + if(dsk->image_type == DSK_TYPE_DOS33) { + log_sec = phys_to_dos_sec[phys_sec]; + } else { + log_sec = phys_to_prodos_sec[phys_sec]; + } + + /* Create sync headers */ + if(phys_sec == 0) { + num_sync = 70; + } else { + num_sync = 22; + } + + for(i = 0; i < num_sync; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 8); // prolog: d5,aa,96 + disk_nib_out(dsk, 0xaa, 8); + disk_nib_out(dsk, 0x96, 8); + disk_4x4_nib_out(dsk, dsk->vol_num); + disk_4x4_nib_out(dsk, qtr_track >> 2); + disk_4x4_nib_out(dsk, phys_sec); + disk_4x4_nib_out(dsk, dsk->vol_num ^ (qtr_track>>2) ^ phys_sec); + disk_nib_out(dsk, 0xde, 8); // epilog: de,aa,eb + disk_nib_out(dsk, 0xaa, 8); + disk_nib_out(dsk, 0xeb, 8); + + /* Inter sync */ + disk_nib_out(dsk, 0xff, 10); + for(i = 0; i < 6; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 8); // data prolog: d5,aa,ad + disk_nib_out(dsk, 0xaa, 8); + disk_nib_out(dsk, 0xad, 8); + + sector_to_partial_nib( &(track_buf[log_sec*256]), + &(partial_nib_buf[0])); + + last_val = 0; + for(i = 0; i < 0x156; i++) { + val = partial_nib_buf[i]; + disk_nib_out(dsk, to_disk_byte[last_val ^ val], 8); + last_val = val; + } + disk_nib_out(dsk, to_disk_byte[last_val], 8); + + /* data epilog */ + disk_nib_out(dsk, 0xde, 8); // data epilog: de,aa,eb + disk_nib_out(dsk, 0xaa, 8); + disk_nib_out(dsk, 0xeb, 8); + } + + /* finish nibblization */ + disk_nib_end_track(dsk, dfcyc); + + iwm_printf("Nibblized q_track %02x\n", qtr_track); + + if(g_check_nibblization) { + disk_check_nibblization(dsk, &(track_buf[0]), 0x1000, dfcyc); + } + + //printf("Showing track after nibblization:\n"); + //iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc); +} + +void +iwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track, + word32 unix_len, dword64 dfcyc) +{ + int phys_to_log_sec[16]; + word32 buf_c00[0x100]; + word32 buf_d00[0x100]; + word32 buf_e00[0x100]; + byte *buf; + word32 val, phys_track, phys_side, capacity, cksum, acc_hi; + word32 tmp_5c, tmp_5d, tmp_5e, tmp_5f, tmp_63, tmp_64, tmp_65; + int num_sectors, log_sec, track, side, num_sync, carry; + int interleave, x, y; + int i, phys_sec; + + if(dsk->cur_fbit_pos & 511) { + halt_printf("fbit_pos:%07x is not bit-aligned!\n", + dsk->cur_fbit_pos); + } + + num_sectors = (unix_len >> 9); + + for(i = 0; i < num_sectors; i++) { + phys_to_log_sec[i] = -1; + } + + phys_sec = 0; + interleave = 2; + for(log_sec = 0; log_sec < num_sectors; log_sec++) { + while(phys_to_log_sec[phys_sec] >= 0) { + phys_sec++; + if(phys_sec >= num_sectors) { + phys_sec = 0; + } + } + phys_to_log_sec[phys_sec] = log_sec; + phys_sec += interleave; + if(phys_sec >= num_sectors) { + phys_sec -= num_sectors; + } + } + + track = qtr_track >> 1; + side = qtr_track & 1; + for(phys_sec = 0; phys_sec < num_sectors; phys_sec++) { + + log_sec = phys_to_log_sec[phys_sec]; + if(log_sec < 0) { + printf("Track: %02x.%x phys_sec: %02x = %d!\n", + track, side, phys_sec, log_sec); + exit(2); + } + + /* Create sync headers */ + if(phys_sec == 0) { + num_sync = 400; + } else { + num_sync = 54; + } + + for(i = 0; i < num_sync; i++) { + disk_nib_out(dsk, 0xff, 10); + } + + disk_nib_out(dsk, 0xd5, 8); /* prolog */ + disk_nib_out(dsk, 0xaa, 8); /* prolog */ + disk_nib_out(dsk, 0x96, 8); /* prolog */ + + phys_track = track & 0x3f; + phys_side = (side << 5) + (track >> 6); + capacity = 0x22; + disk_nib_out(dsk, to_disk_byte[phys_track], 8); /* trk */ + disk_nib_out(dsk, to_disk_byte[log_sec], 8); /* sec */ + disk_nib_out(dsk, to_disk_byte[phys_side], 8); /* sides+trk */ + disk_nib_out(dsk, to_disk_byte[capacity], 8); /* capacity*/ + + cksum = (phys_track ^ log_sec ^ phys_side ^ capacity) & 0x3f; + disk_nib_out(dsk, to_disk_byte[cksum], 8); /* cksum*/ + + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + + /* Inter sync */ + for(i = 0; i < 5; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 8); /* data prolog */ + disk_nib_out(dsk, 0xaa, 8); /* data prolog */ + disk_nib_out(dsk, 0xad, 8); /* data prolog */ + disk_nib_out(dsk, to_disk_byte[log_sec], 8); /* sec again */ + + /* do nibblizing! */ + buf = track_buf + (log_sec << 9); + +/* 6320 */ + tmp_5e = 0; + tmp_5d = 0; + tmp_5c = 0; + y = 0; + x = 0xaf; + buf_c00[0] = 0; + buf_d00[0] = 0; + buf_e00[0] = 0; + buf_e00[1] = 0; + for(y = 0x4; y > 0; y--) { + buf_c00[x] = 0; + buf_d00[x] = 0; + buf_e00[x] = 0; + x--; + } + + while(x >= 0) { +/* 6338 */ + tmp_5c = tmp_5c << 1; + carry = (tmp_5c >> 8); + tmp_5c = (tmp_5c + carry) & 0xff; + + val = buf[y]; + tmp_5e = val + tmp_5e + carry; + carry = (tmp_5e >> 8); + tmp_5e = tmp_5e & 0xff; + + val = val ^ tmp_5c; + buf_c00[x] = val; + y++; +/* 634c */ + val = buf[y]; + tmp_5d = tmp_5d + val + carry; + carry = (tmp_5d >> 8); + tmp_5d = tmp_5d & 0xff; + val = val ^ tmp_5e; + buf_d00[x] = val; + y++; + x--; + if(x <= 0) { + break; + } + +/* 632a */ + val = buf[y]; + tmp_5c = tmp_5c + val + carry; + carry = (tmp_5c >> 8); + tmp_5c = tmp_5c & 0xff; + + val = val ^ tmp_5d; + buf_e00[x+1] = val; + y++; + } + +/* 635f */ + val = ((tmp_5c >> 2) ^ tmp_5d) & 0x3f; +/* 6367 */ + val = (val ^ tmp_5d) >> 2; +/* 636b */ + val = (val ^ tmp_5e) & 0x3f; +/* 636f */ + val = (val ^ tmp_5e) >> 2; +/* 6373 */ + tmp_5f = val; +/* 6375 */ + tmp_63 = 0; + tmp_64 = 0; + tmp_65 = 0; + acc_hi = 0; + + + y = 0xae; + while(y >= 0) { +/* 63e4 */ + /* write out acc_hi */ + val = to_disk_byte[acc_hi & 0x3f]; + disk_nib_out(dsk, val, 8); + +/* 63f2 */ + val = to_disk_byte[tmp_63 & 0x3f]; + tmp_63 = buf_c00[y]; + acc_hi = tmp_63 >> 6; + disk_nib_out(dsk, val, 8); +/* 640b */ + val = to_disk_byte[tmp_64 & 0x3f]; + tmp_64 = buf_d00[y]; + acc_hi = (acc_hi << 2) + (tmp_64 >> 6); + disk_nib_out(dsk, val, 8); + y--; + if(y < 0) { + break; + } + +/* 63cb */ + val = to_disk_byte[tmp_65 & 0x3f]; + tmp_65 = buf_e00[y+1]; + acc_hi = (acc_hi << 2) + (tmp_65 >> 6); + disk_nib_out(dsk, val, 8); + } +/* 6429 */ + val = to_disk_byte[tmp_5f & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5e & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5d & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5c & 0x3f]; + disk_nib_out(dsk, val, 8); + +/* 6440 */ + /* data epilog */ + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + disk_nib_out(dsk, 0xff, 8); + } + + disk_nib_end_track(dsk, dfcyc); + + if(g_check_nibblization) { + disk_check_nibblization(dsk, &(track_buf[0]), unix_len, dfcyc); + } +} + +void +disk_4x4_nib_out(Disk *dsk, word32 val) +{ + disk_nib_out(dsk, 0xaa | (val >> 1), 8); + disk_nib_out(dsk, 0xaa | val, 8); +} + +void +disk_nib_out(Disk *dsk, word32 val, int size) +{ + word32 bit_pos; + + bit_pos = dsk->cur_fbit_pos >> 9; + dsk->cur_fbit_pos = disk_nib_out_raw(dsk, + (dsk->cur_frac_track + 0x8000) >> 16, val, size, bit_pos, 0) * + 512; +} + +void +disk_nib_end_track(Disk *dsk, dword64 dfcyc) +{ + // printf("disk_nib_end_track %p\n", dsk); + + dsk->cur_fbit_pos = 0; + dsk->disk_dirty = 0; + iwm_recalc_sync_from(dsk, (word32)(dsk->cur_trk_ptr - &(dsk->trks[0])), + 0, dfcyc); +} + +word32 +disk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits, + word32 bit_pos, dword64 dfcyc) +{ + Trk *trkptr; + byte *bptr, *sync_ptr; + word32 track_bits, tmp, mask; + int pos, next_pos, bit, to_do, shift_left, shift_right, last_byte; + int this_bits; + int do_print; + + // write bits from val[7:x]. If bits=3 and val=0xaf, write bits 101. + // If bits=10 and val=0xaf, write 0xaf then bits 00. + + if(qtr_track >= 160) { + return bit_pos; + } + trkptr = &(dsk->trks[qtr_track]); + track_bits = trkptr->track_bits; + if(track_bits == 0) { + halt_printf("disk_nib_out_raw track_bits=0, %04x\n", qtr_track); + return bit_pos; + } + + last_byte = (track_bits - 1) >> 3; + bit = bit_pos & 7; + pos = bit_pos >> 3; + bptr = &(trkptr->raw_bptr[0]); + sync_ptr = &(trkptr->sync_ptr[0]); + if(dfcyc != 0) { + dbg_log_info(dfcyc, (bits << 24) | (bit_pos << 1), + (track_bits << 16) | (val & 0xffff), 0x100ed); + } + dsk->disk_dirty = 1; + trkptr->dirty = 1; + do_print = ((pos <= 0x10) || (pos >= 0x18e0)) && + (dsk->cur_frac_track == 0xb0000); + do_print = 0; + if(do_print) { + printf("disk_nib_out %02x, %d, %06x\n", val, bits, bit_pos*2); + } + while(1) { + this_bits = 8; + next_pos = pos + 1; + if(pos >= last_byte) { + this_bits = ((track_bits - 1) & 7) + 1; // 1..8 + next_pos = 0; + } + this_bits = (this_bits - bit); // 1..8 + to_do = bits; // 1...inf + if(to_do > this_bits) { + to_do = this_bits; // 1..8 + } + shift_left = (8 - bit - to_do) & 7; + shift_right = 8 - to_do; + mask = (1U << to_do) - 1; + tmp = (val >> shift_right) & mask; + mask = mask << shift_left; + if(do_print) { + printf(" pos:%04x bit:%d tmp:%02x mask:%02x bits:%d " + "bptr[]=%02x new:%02x todo:%d, r:%d l:%d\n", + pos, bit, tmp, mask, bits, bptr[pos], + (bptr[pos] & (~mask)) | + ((tmp << shift_left) & mask), + to_do, shift_right, shift_left); + } + bptr[pos] = (bptr[pos] & (~mask)) | + ((tmp << shift_left) & mask); + sync_ptr[pos] = 0xff; + bits -= to_do; + if(bits <= 0) { + pos = (pos * 8) + bit + to_do; + if((bit + to_do) >= 8) { + pos = next_pos * 8; + } + if((word32)pos >= track_bits) { + pos -= track_bits; + } + if(do_print) { + printf(" returning %05x, bits:%d orig:%05x " + "%05x\n", + pos*2, bits, bit_pos*2, last_byte); + } + return pos; + } + val = (val << to_do) & 0xff; + bit = 0; + pos = next_pos; + } +} + +Disk * +iwm_get_dsk_from_slot_drive(int slot, int drive) +{ + Disk *dsk; + int max_drive; + + // pass in slot=5,6,7 drive=0,1 (or more for slot 7) + max_drive = 2; + switch(slot) { + case 5: + dsk = &(g_iwm.drive35[drive]); + break; + case 6: + dsk = &(g_iwm.drive525[drive]); + break; + default: // slot 7 + max_drive = MAX_C7_DISKS; + dsk = &(g_iwm.smartport[drive]); + } + if(drive >= max_drive) { + dsk -= drive; // Move back to drive 0 effectively + } + + return dsk; +} + +void +iwm_toggle_lock(Disk *dsk) +{ + printf("iwm_toggle_lock: write_prot:%d, write_through:%d\n", + dsk->write_prot, dsk->write_through_to_unix); + if(dsk->write_prot == 2) { + // nothing to do + return; + } + + if(dsk->write_prot) { + dsk->write_prot = 0; + } else { + dsk->write_prot = 1; + } + printf("New dsk->write_prot: %d\n", dsk->write_prot); + if(dsk->wozinfo_ptr && dsk->write_through_to_unix) { + woz_rewrite_lock(dsk); + printf("Called woz_rewrite_lock()\n"); + return; + } +} + +void +iwm_eject_named_disk(int slot, int drive, const char *name, + const char *partition_name) +{ + Disk *dsk; + + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + if(dsk->fd < 0) { + return; + } + + /* If name matches, eject the disk! */ + if(!strcmp(dsk->name_ptr, name)) { + /* It matches, eject it */ + if((partition_name != 0) && (dsk->partition_name != 0)) { + /* If both have partitions, and they differ, then */ + /* don't eject. Otherwise, eject */ + if(strcmp(dsk->partition_name, partition_name) != 0) { + /* Don't eject */ + return; + } + } + iwm_eject_disk(dsk); + } +} + +void +iwm_eject_disk_by_num(int slot, int drive) +{ + Disk *dsk; + + dsk = iwm_get_dsk_from_slot_drive(slot, drive); + + iwm_eject_disk(dsk); +} + +void +iwm_eject_disk(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + word32 state; + int motor_on; + int i; + + printf("Ejecting dsk: %s, fd:%d\n", dsk->name_ptr, dsk->fd); + + if(dsk->fd < 0) { + return; + } + + g_config_kegs_update_needed = 1; + + state = g_iwm.state; + motor_on = state & IWM_STATE_MOTOR_ON; + if(state & IWM_STATE_C031_APPLE35SEL) { + motor_on = state & IWM_STATE_MOTOR_ON35; + } + if(motor_on) { + halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); + } + + dynapro_try_fix_damaged_disk(dsk); + iwm_flush_disk_to_unix(dsk); + + printf("Ejecting disk: %s\n", dsk->name_ptr); + + /* Free all memory, close file */ + + /* free the tracks first */ + if(dsk->trks != 0) { + for(i = 0; i < MAX_TRACKS; i++) { + if(dsk->raw_bptr_malloc) { + free(dsk->trks[i].raw_bptr); + } + free(dsk->trks[i].sync_ptr); + dsk->trks[i].raw_bptr = 0; + dsk->trks[i].sync_ptr = 0; + dsk->trks[i].track_bits = 0; + } + } + dsk->num_tracks = 0; + dsk->raw_bptr_malloc = 0; + + wozinfo_ptr = dsk->wozinfo_ptr; + if(wozinfo_ptr) { + if(dsk->raw_data == 0) { + free(wozinfo_ptr->wozptr); + } + wozinfo_ptr->wozptr = 0; + free(wozinfo_ptr); + } + dsk->wozinfo_ptr = 0; + + dynapro_free_dynapro_info(dsk); + + /* close file, clean up dsk struct */ + if(dsk->raw_data) { + free(dsk->raw_data); + } else { + close(dsk->fd); + } + + dsk->fd = -1; + dsk->raw_dsize = 0; + dsk->raw_data = 0; + dsk->dimage_start = 0; + dsk->dimage_size = 0; + dsk->cur_fbit_pos = 0; + dsk->cur_track_bits = 0; + dsk->disk_dirty = 0; + dsk->write_through_to_unix = 0; + dsk->write_prot = 1; + dsk->just_ejected = 1; + + /* Leave name_ptr valid */ +} + +void +iwm_show_track(int slot_drive, int track, dword64 dfcyc) +{ + Disk *dsk; + Trk *trk; + word32 state; + int drive, sel35, qtr_track; + + state = g_iwm.state; + if(slot_drive < 0) { + drive = (state >> IWM_BIT_DRIVE_SEL) & 1; + if(state & IWM_STATE_MOTOR_ON) { + sel35 = (state >> IWM_BIT_C031_APPLE35SEL) & 1; + } else { + sel35 = (state >> IWM_BIT_LAST_SEL35) & 1; + } + } else { + drive = slot_drive & 1; + sel35 = !((slot_drive >> 1) & 1); + } + if(sel35) { + dsk = &(g_iwm.drive35[drive]); + } else { + dsk = &(g_iwm.drive525[drive]); + } + + if(track < 0) { + qtr_track = dsk->cur_frac_track >> 16; + } else { + qtr_track = track; + } + if((dsk->trks == 0) || (qtr_track >= 160)) { + return; + } + trk = &(dsk->trks[qtr_track]); + + if(trk->track_bits == 0) { + dbg_printf("Track_bits: %d\n", trk->track_bits); + dbg_printf("No track for type: %d, drive: %d, qtrk:0x%02x\n", + sel35, drive, qtr_track); + return; + } + + dbg_printf("Current s%dd%d, q_track:0x%02x\n", 6 - sel35, + drive + 1, qtr_track); + + iwm_show_a_track(dsk, trk, dfcyc); +} + +void +iwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc) +{ + byte *bptr; + byte *sync_ptr; + word32 val, track_bits, len, shift, line_len, sync; + int i, j; + + track_bits = trk->track_bits; + dbg_printf(" Showtrack:track_bits*2: %06x, fbit_pos: %07x, " + "trk:%06x, dfcyc:%016llx\n", track_bits*2, dsk->cur_fbit_pos, + dsk->cur_frac_track, dfcyc); + dbg_printf(" disk_525:%d, drive:%d name:%s fd:%d, dimage_start:" + "%08llx, dimage_size:%08llx\n", dsk->disk_525, dsk->drive, + dsk->name_ptr, dsk->fd, dsk->dimage_start, dsk->dimage_size); + dbg_printf(" image_type:%d, vol_num:%02x, write_prot:%d, " + "write_through:%d, disk_dirty:%d\n", dsk->image_type, + dsk->vol_num, dsk->write_prot, dsk->write_through_to_unix, + dsk->disk_dirty); + dbg_printf(" just_ejected:%d, last_phases:%d, num_tracks:%d\n", + dsk->just_ejected, dsk->last_phases, dsk->num_tracks); + + len = (track_bits + 7) >> 3; + if(len >= 0x3000) { + len = 0x3000; + dbg_printf("len too big, using %04x\n", len); + } + + bptr = trk->raw_bptr; + sync_ptr = trk->sync_ptr; + + len = len + 2; // Show an extra 2 bytes + for(i = 0; i < (int)len; i += 16) { + line_len = 16; + if((i + line_len) > len) { + line_len = len - i; + } + // First, print raw bptr bytes + dbg_printf("%04x: ", i); + for(j = 0; j < (int)line_len; j++) { + dbg_printf(" %02x", bptr[i + j]); + if(((i + j) * 8U) >= track_bits) { + dbg_printf("*"); + } + } + dbg_printf("\n"); + dbg_printf(" sync:"); + for(j = 0; j < (int)line_len; j++) { + dbg_printf(" %2d", sync_ptr[i + j]); + } + dbg_printf("\n"); + dbg_printf(" nibs:"); + for(j = 0; j < (int)line_len; j++) { + sync = sync_ptr[i+j]; + if(sync >= 8) { + dbg_printf(" XX"); + } else { + shift = (7 - sync) & 7; + val = (bptr[i + j] << 8) | bptr[i + j + 1]; + val = ((val << shift) >> 8) & 0xff; + dbg_printf(" %02x", val); + } + } + dbg_printf("\n"); + } +} + + +void +dummy1(word32 psr) +{ + printf("dummy1 psr: %05x\n", psr); +} + +void +dummy2(word32 psr) +{ + printf("dummy2 psr: %05x\n", psr); +} diff --git a/gsplus/src/iwm.h b/gsplus/src/iwm.h new file mode 100644 index 0000000..06df1fd --- /dev/null +++ b/gsplus/src/iwm.h @@ -0,0 +1,206 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#define MAX_TRACKS (2*80) +#define MAX_C7_DISKS 16 + +#define NIB_LEN_525 0x18f2 /* 51088 bits per track */ +// Expected bits per track: (1020484/5)/4 = 51024. A little extra seems good + +#define NIBS_FROM_ADDR_TO_DATA 28 +// Copy II+ Manual Sector Copy fails if this is 20, so make it 28 + +// image_type settings. 0 means unknown type +#define DSK_TYPE_PRODOS 1 +#define DSK_TYPE_DOS33 2 +#define DSK_TYPE_DYNAPRO 3 +#define DSK_TYPE_NIB 4 +#define DSK_TYPE_WOZ 5 + +// Note: C031_APPLE35SEL must be 6, C031_CTRL must be 7, MOTOR_ON must be 5! +// Q7 needs to be adjacent and higher than Q6 +// Bits 4:0 are IWM mode register: 0: latch mode; 1: async handshake; +// 2: immediate motor off (no 1 sec delay); 3: 2us bit timing; +// 4: Divide input clock by 8 (instead of 7) +#define IWM_BIT_MOTOR_ON 5 +#define IWM_BIT_C031_APPLE35SEL 6 +#define IWM_BIT_C031_CTRL 7 +#define IWM_BIT_STEP_DIRECTION35 8 +#define IWM_BIT_MOTOR_ON35 9 +#define IWM_BIT_MOTOR_OFF 10 +#define IWM_BIT_DRIVE_SEL 11 +#define IWM_BIT_Q6 12 +#define IWM_BIT_Q7 13 +#define IWM_BIT_ENABLE2 14 +#define IWM_BIT_LAST_SEL35 15 +#define IWM_BIT_PHASES 16 +#define IWM_BIT_RESET 20 + +#define IWM_STATE_MOTOR_ON (1 << IWM_BIT_MOTOR_ON) +#define IWM_STATE_C031_APPLE35SEL (1 << IWM_BIT_C031_APPLE35SEL) +#define IWM_STATE_C031_CTRL (1 << IWM_BIT_C031_CTRL) +#define IWM_STATE_STEP_DIRECTION35 (1 << IWM_BIT_STEP_DIRECTION35) +#define IWM_STATE_MOTOR_ON35 (1 << IWM_BIT_MOTOR_ON35) +#define IWM_STATE_MOTOR_OFF (1 << IWM_BIT_MOTOR_OFF) +#define IWM_STATE_DRIVE_SEL (1 << IWM_BIT_DRIVE_SEL) +#define IWM_STATE_Q6 (1 << IWM_BIT_Q6) +#define IWM_STATE_Q7 (1 << IWM_BIT_Q7) +#define IWM_STATE_ENABLE2 (1 << IWM_BIT_ENABLE2) +#define IWM_STATE_LAST_SEL35 (1 << IWM_BIT_LAST_SEL35) +#define IWM_STATE_PHASES (1 << IWM_BIT_PHASES) +#define IWM_STATE_RESET (1 << IWM_BIT_RESET) + +STRUCT(Trk) { + byte *raw_bptr; + byte *sync_ptr; + dword64 dunix_pos; + word16 unix_len; + word16 dirty; + word32 track_bits; +}; + +STRUCT(Woz_info) { + byte *wozptr; + word32 woz_size; + int version; + int reparse_needed; + word32 max_trk_blocks; + int meta_size; + int trks_size; + int tmap_offset; + int trks_offset; + int info_offset; + int meta_offset; +}; + +typedef struct Dynapro_map_st Dynapro_map; + +STRUCT(Dynapro_file) { + Dynapro_file *next_ptr; + Dynapro_file *parent_ptr; + Dynapro_file *subdir_ptr; + char *unix_path; + byte *buffer_ptr; + byte prodos_name[17]; // +0x00-0x0f: [0] is len, nul at end + word32 dir_byte; // Byte address of this file's dir ent + word32 eof; // +0x15-0x17 + word32 blocks_used; // +0x13-0x14 + word32 creation_time; // +0x18-0x1b + word32 lastmod_time; // +0x21-0x24 + word16 upper_lower; // +0x1c-0x1d: Versions: lowercase flags + word16 key_block; // +0x11-0x12 + word16 aux_type; // +0x1f-0x20 + word16 header_pointer; // +0x25-0x26 + word16 map_first_block; + byte file_type; // +0x10 + byte modified_flag; + byte damaged; +}; + +struct Dynapro_map_st { + Dynapro_file *file_ptr; + word16 next_map_block; + word16 modified; +}; + +STRUCT(Dynapro_info) { + char *root_path; + Dynapro_file *volume_ptr; + Dynapro_map *block_map_ptr; + int damaged; +}; + +STRUCT(Disk) { + dword64 dfcyc_last_read; + byte *raw_data; + Woz_info *wozinfo_ptr; + Dynapro_info *dynapro_info_ptr; + char *name_ptr; + char *partition_name; + int partition_num; + int fd; + word32 dynapro_blocks; + dword64 raw_dsize; + dword64 dimage_start; + dword64 dimage_size; + int smartport; + int disk_525; + int drive; + word32 cur_frac_track; + int image_type; + int vol_num; + int write_prot; + int write_through_to_unix; + int disk_dirty; + int just_ejected; + int last_phases; + dword64 dfcyc_last_phases; + word32 cur_fbit_pos; + word32 fbit_mult; + word32 cur_track_bits; + int raw_bptr_malloc; + Trk *cur_trk_ptr; + int num_tracks; + Trk *trks; +}; + +STRUCT(Iwm) { + Disk drive525[2]; + Disk drive35[2]; + Disk smartport[MAX_C7_DISKS]; + dword64 dfcyc_last_fastemul_read; + word32 state; + word32 motor_off_vbl_count; + word32 forced_sync_bit; + word32 last_rd_bit; + word32 write_val; + word32 wr_last_bit[5]; + word32 wr_qtr_track[5]; + word32 wr_num_bits[5]; + word32 wr_prior_num_bits[5]; + word32 wr_delta[5]; + int num_active_writes; +}; + +STRUCT(Driver_desc) { + word16 sig; + word16 blk_size; + word32 blk_count; + word16 dev_type; + word16 dev_id; + word32 data; + word16 drvr_count; +}; + +STRUCT(Part_map) { + word16 sig; + word16 sigpad; + word32 map_blk_cnt; + word32 phys_part_start; + word32 part_blk_cnt; + char part_name[32]; + char part_type[32]; + word32 data_start; + word32 data_cnt; + word32 part_status; + word32 log_boot_start; + word32 boot_size; + word32 boot_load; + word32 boot_load2; + word32 boot_entry; + word32 boot_entry2; + word32 boot_cksum; + char processor[16]; + char junk[128]; +}; + diff --git a/gsplus/src/joystick_driver.c b/gsplus/src/joystick_driver.c new file mode 100644 index 0000000..e5206dc --- /dev/null +++ b/gsplus/src/joystick_driver.c @@ -0,0 +1,469 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +#ifdef __linux__ +# include +#endif + +#ifdef _WIN32 +# include +# include +#endif + +extern int g_joystick_native_type1; /* in paddles.c */ +extern int g_joystick_native_type2; /* in paddles.c */ +extern int g_joystick_native_type; /* in paddles.c */ +extern int g_paddle_buttons; +extern int g_paddle_val[]; + + +const char *g_joystick_dev = "/dev/js0"; /* default joystick dev file */ +#define MAX_JOY_NAME 128 + +int g_joystick_native_fd = -1; +int g_joystick_num_axes = 0; +int g_joystick_num_buttons = 0; + +int g_joystick_callback_buttons = 0; +int g_joystick_callback_x = 32767; +int g_joystick_callback_y = 32767; + +#ifdef __linux__ +# define JOYSTICK_DEFINED +void +joystick_init() +{ + char joy_name[MAX_JOY_NAME]; + int version, fd; + int i; + + fd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK); + if(fd < 0) { + printf("Unable to open joystick dev file: %s, errno: %d\n", + g_joystick_dev, errno); + printf("Defaulting to mouse joystick\n"); + return; + } + + strcpy(&joy_name[0], "Unknown Joystick"); + version = 0x800; + + ioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]); + ioctl(fd, JSIOCGAXES, &g_joystick_num_axes); + ioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons); + ioctl(fd, JSIOCGVERSION, &version); + + printf("Detected joystick: %s [%d axes, %d buttons vers: %08x]\n", + joy_name, g_joystick_num_axes, g_joystick_num_buttons, + version); + + g_joystick_native_type1 = 1; + g_joystick_native_type2 = -1; + g_joystick_native_fd = fd; + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 32767; + } + g_paddle_buttons = 0xc; +} + +/* joystick_update_linux() called from paddles.c. Update g_paddle_val[] */ +/* and g_paddle_buttons with current information */ +void +joystick_update(dword64 dfcyc) +{ + struct js_event js; /* the linux joystick event record */ + int mask, val, num, type, ret, len; + int i; + + /* suck up to 20 events, then give up */ + len = sizeof(struct js_event); + for(i = 0; i < 20; i++) { + ret = read(g_joystick_native_fd, &js, len); + if(ret != len) { + /* just get out */ + break; + } + type = js.type & ~JS_EVENT_INIT; + val = js.value; + num = js.number & 3; /* clamp to 0-3 */ + switch(type) { + case JS_EVENT_BUTTON: + mask = 1 << num; + if(val) { + val = mask; + } + g_paddle_buttons = (g_paddle_buttons & ~mask) | val; + break; + case JS_EVENT_AXIS: + /* val is -32767 to +32767 */ + g_paddle_val[num] = val; + break; + } + } + + if(i > 0) { + paddle_update_trigger_dcycs(dfcyc); + } +} + +void +joystick_update_buttons() +{ +} +#endif /* LINUX */ + +#ifdef _WIN32 +# define JOYSTICK_DEFINED +#undef JOYSTICK_DEFINED + // HACK: remove +#if 0 +void +joystick_init() +{ + JOYINFO info; + JOYCAPS joycap; + MMRESULT ret1, ret2; + int i; + + // Check that there is a joystick device + if(joyGetNumDevs() <= 0) { + printf("No joystick hardware detected\n"); + g_joystick_native_type1 = -1; + g_joystick_native_type2 = -1; + return; + } + + g_joystick_native_type1 = -1; + g_joystick_native_type2 = -1; + + // Check that at least joystick 1 or joystick 2 is available + ret1 = joyGetPos(JOYSTICKID1, &info); + ret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap)); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_joystick_native_type1 = JOYSTICKID1; + printf("Joystick #1 = %s\n", joycap.szPname); + g_joystick_native_type = JOYSTICKID1; + } + ret1 = joyGetPos(JOYSTICKID2, &info); + ret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap)); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_joystick_native_type2 = JOYSTICKID2; + printf("Joystick #2 = %s\n", joycap.szPname); + if(g_joystick_native_type < 0) { + g_joystick_native_type = JOYSTICKID2; + } + } + + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 32767; + } + g_paddle_buttons = 0xc; +} + +void +joystick_update(dword64 dfcyc) +{ + JOYCAPS joycap; + JOYINFO info; + UINT id; + MMRESULT ret1, ret2; + + id = g_joystick_native_type; + + ret1 = joyGetDevCaps(id, &joycap, sizeof(joycap)); + ret2 = joyGetPos(id, &info); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_paddle_val[0] = (info.wXpos - joycap.wXmin) * 32768 / + (joycap.wXmax - joycap.wXmin); + g_paddle_val[1] = (info.wYpos - joycap.wYmin) * 32768 / + (joycap.wYmax - joycap.wYmin); + if(info.wButtons & JOY_BUTTON1) { + g_paddle_buttons = g_paddle_buttons | 1; + } else { + g_paddle_buttons = g_paddle_buttons & (~1); + } + if(info.wButtons & JOY_BUTTON2) { + g_paddle_buttons = g_paddle_buttons | 2; + } else { + g_paddle_buttons = g_paddle_buttons & (~2); + } + paddle_update_trigger_dcycs(dfcyc); + } +} + +void +joystick_update_buttons() +{ + JOYINFOEX info; + UINT id; + + id = g_joystick_native_type; + + info.dwSize = sizeof(JOYINFOEX); + info.dwFlags = JOY_RETURNBUTTONS; + if(joyGetPosEx(id, &info) == JOYERR_NOERROR) { + if(info.dwButtons & JOY_BUTTON1) { + g_paddle_buttons = g_paddle_buttons | 1; + } else { + g_paddle_buttons = g_paddle_buttons & (~1); + } + if(info.dwButtons & JOY_BUTTON2) { + g_paddle_buttons = g_paddle_buttons | 2; + } else { + g_paddle_buttons = g_paddle_buttons & (~2); + } + } +} +#endif +#endif + +#ifdef MAC +# define JOYSTICK_DEFINED + +#include +#include +#include +#include +#include +// Headers are at: /Applications/Xcode.app/Contents/Developer/Platforms/ +// MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/ +// Frameworks/IOKit.framework/Headers +// Thanks to VirtualC64 and hidapi library for coding example + +CFIndex g_joystick_min = 0; +CFIndex g_joystick_range = 256; +int g_joystick_minmax_valid = 0; +int g_joystick_dummy = 0; + +void +hid_device_callback(void *ptr, IOReturn result, void *sender, + IOHIDValueRef value) +{ + IOHIDElementRef element; + word32 mask; + int usage_page, usage, ival, button; + + // This is a callback routine, and it's unclear to me what the state is + // For safety, do no printfs() (other than for debug). + if((ptr || result || sender || 1) == 0) { + printf("Bad\n"); // Avoid unused var warning + } + + element = IOHIDValueGetElement(value); + usage_page = IOHIDElementGetUsagePage(element); + usage = IOHIDElementGetUsage(element); + ival = IOHIDValueGetIntegerValue(value); +#if 0 + printf(" usage_page:%d, usage:%d, value:%d\n", usage_page, usage, ival); +#endif + if((usage_page == kHIDPage_GenericDesktop) && + ((usage >= kHIDUsage_GD_X) && + (usage <= kHIDUsage_GD_Y)) && + !g_joystick_minmax_valid) { + g_joystick_min = IOHIDElementGetLogicalMin(element); + g_joystick_range = IOHIDElementGetLogicalMax(element) + 1 - + g_joystick_min; + // printf("min:%lld range:%lld\n", (dword64)g_joystick_min, + // (dword64)g_joystick_range); + if(g_joystick_range == 0) { + g_joystick_range = 1; + } + g_joystick_minmax_valid = 1; + } + if((usage_page == kHIDPage_GenericDesktop) && + (usage == kHIDUsage_GD_X)) { + g_joystick_callback_x = ((ival * 65536) / g_joystick_range) - + 32768; + //printf("g_joystick_callback_x = %d\n", g_joystick_callback_x); + } + if((usage_page == kHIDPage_GenericDesktop) && + (usage == kHIDUsage_GD_Y)) { + g_joystick_callback_y = ((ival * 65536) / g_joystick_range) - + 32768; + //printf("g_joystick_callback_y = %d\n", g_joystick_callback_y); + } + if((usage_page == kHIDPage_Button) && (usage >= 1) && (usage <= 10)) { + // Buttons: usage=1 is button 0, usage=2 is button 1, etc. + button = (~usage) & 1; + mask = 1 << button; + //printf("Button %d (%d) pressed:%d\n", button, usage, ival); + if(ival == 0) { // Button released + g_joystick_callback_buttons &= (~mask); + } else { // Button pressed + g_joystick_callback_buttons |= mask; + } + } +} + +int +hid_get_int_property(IOHIDDeviceRef device, CFStringRef key_cfstr) +{ + CFTypeRef ref; + Boolean bret; + int int_val; + + ref = IOHIDDeviceGetProperty(device, key_cfstr); + if(ref) { + bret = CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, + &int_val); + if(bret) { + return int_val; + } + } + return 0; +} + +void +joystick_init() +{ + IOHIDManagerRef hid_mgr; + CFSetRef devices_set; + CFIndex num_devices; + IOHIDDeviceRef *devices_array, device; + int vendor, usage_page, usage; + int i; + + g_joystick_native_type1 = -1; + g_joystick_native_type2 = -1; + + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, + kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(hid_mgr, 0); + IOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone); + + devices_set = IOHIDManagerCopyDevices(hid_mgr); + num_devices = CFSetGetCount(devices_set); + + // Sets are hashtables, so we cannot directly access it. The only way + // to iterate over all values is to use CFSetGetValues to get a simple + // array of the values, and iterate over that + devices_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(devices_set, (const void **)devices_array); + + for(i = 0; i < num_devices; i++) { + device = devices_array[i]; + vendor = hid_get_int_property(device, CFSTR(kIOHIDVendorIDKey)); + // printf(" vendor: %d\n", vendor); + usage_page = hid_get_int_property(device, + CFSTR(kIOHIDDeviceUsagePageKey)); + usage = hid_get_int_property(device, + CFSTR(kIOHIDDeviceUsageKey)); + // printf(" usage_page:%d, usage:%d\n", usage_page, usage); + usage_page = hid_get_int_property(device, + CFSTR(kIOHIDPrimaryUsagePageKey)); + usage = hid_get_int_property(device, + CFSTR(kIOHIDPrimaryUsageKey)); + // printf(" primary_usage_page:%d, usage:%d\n", usage_page, + // usage); + if(usage_page != kHIDPage_GenericDesktop) { + continue; + } + if((usage != kHIDUsage_GD_Joystick) && + (usage != kHIDUsage_GD_GamePad) && + (usage != kHIDUsage_GD_MultiAxisController)) { + continue; + } + printf(" JOYSTICK FOUND, vendor:%08x!\n", vendor); + IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone); + IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), + kCFRunLoopCommonModes); + IOHIDDeviceRegisterInputValueCallback(device, + hid_device_callback, 0); + g_joystick_native_type1 = 1; + return; + // Now, hid_device_callback will be called whenever a joystick + // value changes. Only set global variables for joystick. + } +} + +void +joystick_update(dword64 dfcyc) +{ + int i; + + if(dfcyc) { + // Avoid unused parameter warnings + } + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 32767; + } + g_paddle_buttons = 0xc; + if(g_joystick_native_type1 >= 0) { + g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3); + g_paddle_val[0] = g_joystick_callback_x; + g_paddle_val[1] = g_joystick_callback_y; + paddle_update_trigger_dcycs(dfcyc); + } +} + +void +joystick_update_buttons() +{ + if(g_joystick_native_type1 >= 0) { + g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3); + } +} +#endif + +#ifndef JOYSTICK_DEFINED +/* stubs for the routines */ +void +joystick_init() +{ + g_joystick_native_type1 = -1; + g_joystick_native_type2 = -1; + g_joystick_native_type = -1; +} + +void +joystick_update(dword64 dfcyc) +{ + int i; + + if(dfcyc) { + // Avoid unused parameter warnings + } + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 32767; + } + g_paddle_buttons = 0xc; + if(g_joystick_native_type1 >= 0) { + g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3); + g_paddle_val[0] = g_joystick_callback_x; + g_paddle_val[1] = g_joystick_callback_y; + paddle_update_trigger_dcycs(dfcyc); + } +} + +void +joystick_update_buttons() +{ + if(g_joystick_native_type1 >= 0) { + g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3); + } +} +#endif + +void +joystick_callback_init(int native_type) +{ + g_joystick_native_type1 = native_type; +} + +void +joystick_callback_update(word32 buttons, int paddle_x, int paddle_y) +{ + g_joystick_callback_buttons = (g_paddle_buttons & (~3)) | (buttons & 3); + g_joystick_callback_x = paddle_x; + g_joystick_callback_y = paddle_y; +} diff --git a/gsplus/src/kegs.icns b/gsplus/src/kegs.icns new file mode 100644 index 0000000000000000000000000000000000000000..8cf7224cc28ac63cf1d37e8527b439e7124ec1cc GIT binary patch literal 858711 zcmb@NQENB1#04FISqV$ss{Ffkse~zRu#}Pjnkduw@qldygg005E)1OWNTf&ROe2mHV93gm(O zpYned#T13=etN(uDI%z10am?X8LhYM0dcFjz25Vk>M( zr1c0=o=7kGR<2Y*K>?#erSz6Bq03ivMUGlN3|yFq2u)ZXXaoX3GU7f=y3=~c#hmlG z{l@*=bwA^71{*YZciMB3<27^7^KH&O+rIiYL@57q<7)K#_E7p4y^rDGuL|pm=?QY# z(zb~Os7jd7?UTuWA>JP`(qsv>Wn~nxs1ptCHe^X27uBt7_B%W*EDu-qe(%?vH+)`C zmo(KbYNDoTntFPAih7EQnl%?}sgv+rIQL@}*>ox$fKy40BHH=F_&;N@u84 zivqL@Gy@d$$q)cE)4#6_?Oilmu~FP&9RskmnwfO+rBuq9cstIcxNbJv9WIk8w6$IT zCWzyB8ayA*b?DU9)mJwbgkBhQh3gc?{{CB+admcfX0Tk0U>zy%?0g^orLWWBY-wvt z%SmJA4@rCw;jL!cpX4PGQwn(9qCO+G@VpVr$=Z7y31b z!0)|j+iJMIv!bW24}l<6BRnYV)kjrfScNYhxf)I| zgXKh3nZ_5`ZuPs1k#kJX=B?ekroH`jUL0TdyYH=fv*qiUy?6b-KZpKvK6NThHEnr* zzJK)JV1I--Go}yxf?x4>26|p^?)S$D4l4Lt-;#t$bx>K4HBNL&gb#8j9COlHv8to3 zhN_|t_B&F4n>yxpJ7+S}Wo&<+j`PNvd-f8O}%|B+*7VTl(c5n$r=&&kP2BA3_l zxZY|YJnD{tk16}{3AuKNno-@ z!G)#xwFbp9s)b`t0YXw^W5pDRKAuXC<9iEN-*P{Ui7#5T zBpEpk(?5^GB;(>OIKm4tHF0*Zi1+mL@-n?ZAqV~neS71?#l1h7w$ELZ18d@*CsR+z z?jzNpa5sR&ZvS4ZH(uep?E~q38Kz%Lfyv{60~G$owB-AKosIfDO~4;yDgX+9RxhEl zwzkU5$lyA5MnuA!8Ob5~Ekk@38{00ON|(*;MrT<-7sN$2cGa@bL#mP}?XCjJ$i~9a z>huna`%NoC-xAU0aDS!uKz>e# zMTIUC8xOOEHs|Wt)O;PnQ8UB?tTKc&D9=W+7-vua1&$1|0|WP}b3q&iw$pO4gwyHR z`-FSfCB!DVrM`$q4^sTvSz&wW8Jg33X{l3;gnO@r`KH0|QuL`9II(Yk>*w zHoq|pce*@^_UA<+(C6mo5>szmW+xd)^5+ec|v^CDr zg4H8qn7rK%A~>efu2k!~9mY&@A{+?@Nu~V@9C9eWiTsqtT4rc~b9QmH-D-2>3C~yg za8L-KKGDFe%7Ki5tEoAj`!tT@6*E*M;4x9<4%Hbiea+EvygaR+7@3IAhJKYGS)kNC z+&E=G&3AEimGa!c?)5x;i8#DitrcgG%Yy`0X(gf~RX}uOB2r~~d6APz5XwAt-96<= zh|z$KS(%`wG2`|W%D5JU=F6P~Drs-l(W%kurrHVRfHkA0B61~KUqAz^E-n_0P1RF@ zlewGJ|4J+$W?vp+FmU~ilGhZbl%-|71^kZm3|M3)yBi)|*6Ut>kc#j3=W`U$#IpLg0||PRR>>R@KzLxR z+m@n?pAsVofCe2p1VpiD4-6GOwpl?Kii;O{EjOCMX&_2j3hwXdq|+P$Nc5VwWl;%W zX>gx65?$UO6!(2<%!Pu|ewmBi6Kb-_e}(3zq8{;2rU|5#kO+ngPqyOpJ9CB}mSF?o zBG#(ClOVw$v>Hf|9Zj4|Jlqu7H(Q_&m6B;dwlFQ$mB3wIULH5sbKeJxx6#uH|0YPg zg#tu&Y&Bc|f)aw7o1b5s^9K|sD$x>%Q8Qs;Gz{k3VFMYvYYhD=FIw!9YsASh2rQZw zIgM}%1PkmI5{aOu4>hWp1yGh?G}2OUuHYmrqRC(6JW?VZe*q?A-9U;{4QpLM|p zv@bU!bcUh9_jcTNeH!5AT4Xvi*Mt%@YKmY({EU^AosmwGXBn_Z}qE(s8iDtJzQ`vWBWRPfgh5$k5y0W#U*lI8WtEYwvN%CXz~r`+jYC9Dzcq&kI75GdS~@_C4?V?JA=my4&}>9zR|6 zIYnhc5h)mBz_mk~_CAfg8(kmwO85aHJ;zOpX?g)0|AE;ok@SQ?!{HOx3{ z$RH>#+IbiwD{#ON8P89W3>%~auQGhDlcjVZ87r1fU3l2x^)e^m`y z(|-N9pWQsFN|xM*sgO{@VAz+?SB@$0s2P#0LP~vJ>;Q)X=K`de!6!j8v7E_(q-hH+ zEU!!A2Ie&vt9CFe8{O_OIr5iYDPT!(U>U|2h@lOINdJ<{<{1V?#sw4+C9TFj?|9`F zY9dA}t?P~vMOvc{kmV&c0VKl6bRq&7KOI4!9VVHwlzbgROr|qfRRAQM0z{0Kc|Lc; zwBYL1TBaUbjj$V~S&Ff^{3~m760nq;M+}7P)xrRGKMq8iXbzqcke;~(t0D^hw3r$W zuiJiz!p%D2db4olM4LS%(?ymLsOc5W>I0>9$d*L}BAvg-nf6ah@3Ni+_J7hTTDuM0 z7iHOrtfi6gV#(w_O^~%vTYnEraZY676Ptms_N>$8^7-0I5|8yV z;p@x7gbIN65#oQ|ax%D` z@922j*uc^QYJZ&PCTC(hMdztdg-2F2*HmBqyuf$&`0D9N_%i4I_MDDf*4EqjeLD9Y zVUvai(1P;UK>(icpYLYgjzpu@nXd2V%JZx9dj0}|D6%15vRX9xFwgM_byrNBD9!cd zddfTE`CTm!3mEH}p~h2$Qh}{VN6B=bb6k!t+}Tzw~XWkgyJWh8w#K*UbF1*CS*3zn`tNrV;J7{p` z-)qC_J%pKkrv`7dsY_AU}F{*ww5NALE&fE29vu)I0W7!*%Vbrxw3U- zOEM}+d+)7~OTm%`vT6>sD9fBH2Ev3t@DvfF;N!#$6_LOpLL7+H)JIh4RoB#2m^4QN z5*`&c7G6(#nSa`aMobP0?6;_7bCn|uVhQWV%o)Uai z{B8&F%T9nhi)6v$DM)qp3^kp)iVKl1C9xN%XCm}TiARW15Tn+;Uk5aazdxFTXq(gB zT|q1s#B5Mt>KAGI+bo^?_UB+AQH6Y)(!OMInvlQZO|3Hr>$P~NW7X&P?AP;qMaPLk9lBt_B;%n-=kC`9^j~+re{OTTT_;n&Hu&`S z+)R?F&mK{8afkLc-`N0{isY^BfA~3`h+mxWujig$-gH~E(`E(6;cyD60T_TKjpZ0@ zw42V1Ba{4jnw(Z=AXaPjTkWzzS?Ax0ALBp}%?YH;6S3$YMpkFy3UCK^hQ70hx}@!D z#(kdU<(r$$elPj`0RoNz7N5?sKl@U2o%dU6nr%K?XmwQbY#*=Fkl?Gq0=U-XGL{!-Ud-N zq`ojH+gX2>-{`i#=bjE8pFj=L>2@SbO2%<;hwBEELaI`EypL!1AwrSG>lHHx=+$#8 z101<9W(Z>Y+1W$i9Jf0<5qzDSEN6S|%DC7ZepB65ibI>8U4Pfe&SGmlqeg`T#m)%J zVBulbb0#HT1MLazXA3C>0|U(6>opbY9rgz*DMLHuAqZ*^7$ycfOTna5=<(LqKjys* z3D%(wf~|FjRjz7sVO6Gim)<0v$BFHGKiLqFbY0zVDotz)f{?OGYiDU0fOP?o2>#Pq zQm>u>5vq_#XVd4Iw`%?=Q}L3wPZc>s5*zoBx7IJa$$a(a2bNZrr8&C%aq%q;UCqdrKtR9h}?Zd{%sd&;4bAymg>64>0(*Wlt171imQ+@jdaiBjrGGBhca64_0c zhsz4AkCpR;?W?#H^RQwj{e9tTH597dv}Vq3b|DqI@rFbr#SeNAW@FYd=m85&YXo`Gl;RD2iy>fl4b z(gO1R=shi;Y$?p<&J2FLj#{?&UCw;Qc+$Vr_jA`vD!&7Im~l&PzGK>q9_wW8NW9!c z2PU*yBq`y&k>TP5Ygdlz&dvd)o88)XdR4`>`pPLqYR+MZew6vbt$v*0f?^6Vw>J}j znT!@29Jo#LW9jS*EbsJUI>KGdwUg;5NYiITl*Dl1%konCp)e|e4o-NfT!$5Cr%a2H z^V*8%@=IyA(a%CCfEnVg$3Lw1W5qd%-!v<4sjreDWSm zb!WAhPw{)H7BoXeH1$*$%zx_%{%^AE(FO)I+rBU@f-av^J`pRP5NRayC5H?PTWeKi zIc*$1a&3L|15|IeV+h?kp z?q8u|MeCPDA4Nqp>pu&`**awG*3(%PDd(30BbhG|ab_ECB4xE1KBHSYWZrnWO_AV- z;5J@44;JTF4}b%nSP>vsaneX3(bA?KOOM5am`Q~1)pu3zy~Wlk2iN^V>f>F{L6Yx? zalGBQ)i;sQLwi!P*qI`~SDo+HoNZsTK1Cn0#nF?p?oBJIuTT4HEA2TJKF{v%3kd&1 zYIE_Pe;eg8WcXjwL$H}{%xHfz@zcLg*me9cnG%-qK>_*SoQ$`R-@KdKxs;ibQq#xU z*2>>c2BU}Bi#W1mk*xP$_|Sk-;qJoSUElrU!OwkTBxRXq@ap$Un(u!Hq2?-zsx^Zy@NS4QV4>*wVEi>&Maf62Q4wE&Qa1$F!B0o8x9 zt~=23HdMUcstftk&U}oIKBT{hF|lA|l!WBguxOJQc5woWtBIymyHo`7GDo2l)nNsN z; z05Ks1PT1}~yFSl3Uf17QUf1oDUfVo8JX_B7`v~8+2;T?vUq(M7>d%gC_ENKvn~Q5> zV>3}B5f=-~s%RG@&PMp8!kq&Du!bRACFVCuthl>aqbCbt`|`XTIuJ`@)A8PfED~lTo6MzrU z=W$7RQ&VJYxt)cBL7AU{L7rdUv&_v|&W|XZ&8Ij}Z#hIebB?0FQeuG1mRVsx`}jfUHoeYSq)LA};`s5xc&gh4V6$1f{ZXk7Yq)PN z_1b^GHJ#xq|2)GDhGgVv>*bbUQj}Q;MUNWUgib+DIzd1kUU?G*)qrHL+OUH~EssfF zXV>G3_J0aguJ^xj_8oWOgMkp3o3_K)INyiDO10{OHsn9LT^4s+9!{HDn_5~xyP)5v zAhLtgvLr}FM5s>?;Dq|VZ|`27UU`21jwCzi^IY$MUp9L`H^uRN9)6;UTt?%OrE=wb z`OjH0|07BQUavCVX_~*#P7Z%UVVNa>)UpL0LXpK1u_(;TsRBIJ?&?rO%tZ53IODDF zNWh4{6Nvvu2jRUNLg{q9D(Go6n||Aa;D5P`^YeZ@Uq;;ObfZtIG@nXm7C$CP>u z?X$;MAXBC)K$R0p3$rwjHj7xDy30*QCaCI+ZH5{Z77&C_J^E={q9;q35f=q!LpJji zN7j+cPB&RyZ8YP*&4tx;oKsoB)NOaVp+E^YIiD?D)O5y{g$Jmna~dBfQ!0y{=3#CnN3*yreuqUW z1nOje>Nc$6^L}Bt({Y++MSkw7Z*8nhqWdR^hz|w2y_7sC^APm)g|X{3`|vQt;h@Fg z@jVE7jVq z_PaaV+dfy#s|}ZS<}$hcCgs$Za_(cobERMAZ);J^rGrp#AD?wXN_1j?G2XA2Lo8<7 za~2r^eh7aNLQeEq371TH?0XiovV%irP|DEEGde6TNiHg@QwH|H@Q)2ol2nRAC7i7qG9fk(f+pFpy?NAcX*5zh3<%eTs_NxBK7$ zP|0=-dW#?H=)XCz&Y&N7A!mV;)U)q3bmeZNTGAL91xa175t}%_BSFhEU^3(>^xzNZ z5+wvEBugEB)XO?3gBZzplqMPlo9))nd~!h2+ng{0CenCeZSVJv*Y!TZ)o$+)_04QM3BY@f;02P?| zarQvO(*r?@M0s#(_X&OPMr5&R^G`Jm2GGxm{Nb0R5RoDSTft4h`eZ;OB&39p&~W*@ zCHN`jU7pnb)G{mkpv=NTQopKqghUY9f6*F{m6;!j_|zWA29X#gYlvx{8bQsGCH zg{8x}d|`S1`-fOu?mRIt75n1@MTV8FEMm~(a4PpN4B^Z6xAqHGRR+N^*tksm7+Kca#dVi&D$$K`ot>IJ3-n0}&FRR@yt-H7)3j8CSs7^nc`SF?Zp zihC7w%FdEBIGO1s^6>8SNRPPh%h*o0{VC5Z-+R>zq3{y#!lf7lwh}jdZ)@?GI_SBvABGy99$ySG$L{e!@>p+Q!RwwMhr`v>2)1KLL~bhv9EKb zcH7(AleKzw0~p{|6jkX|1?w)krI9Af#&*n3d{$G4l`cR&4*_X1sbD=s0fua>?lc$Q zT1^&rL@DC@0MxCox7zOw1_y)h0JvJ)_^1^*SJ*taEGl&U^mjh*-3{y2f-+#S!UX<1 zn2;W;wXzXy3_5Df0N;pH*bLkC=E7>@Hf9keYs(sk>@!}+k6;(Q1_O}6nEE;E!2ELHTrdk)muMyT zw(qa^-X9P5{vkXV>W@Y z`#~{r#^6c%}Gx(SDyGqn)ldEh>4s z3#Ksit}-W;tD$)*Bg^*c|DX?*oqL;#r$nTM0`YbG4A3g=)dZBmM?jzAIhpVfKNgCm zUxi)>L+>H|OHL`JBIka=MNQYfo%>Y?XD=)E!q_)Uvk}-^#oSdkQK9_FVpvGaS&Hh2 z8}WV6m4%bGQ+0L?o@H~3#hlbhw6KrEjDD~@Vs^pW_I&-TuIF{ds&K{(jv&8(pjBB@ zhh~F1#oTVA6Dm=maF-(N6W#Cg+3nLk27N|ZC)zT*R`8$GFosEC4UKxB13VXjfuE!_ z0qohG`I%2+g2w|jzjf*~-v46Vkam_KyUh?rc;NhZq%8)#wJA+o69gKq5=N}NEZ;XK zJXpzCr$H`ZObI4E-&M6O?+7#0gK&jKVn9So81=Bv)x~8dgEMKc!SbHW!GgxZ8Hs0) z3i?>#m#4{~k zzhL-Hy#&Bq9+-utiUWx;%FF?!Wrf&I+(O{s!rQwAa2K$%n2V*)4^MVmZHo39%~pSk zoBmFwSX+9I0J&j&5|9t&baEN-#9}F7tVa@_=ms082+8af!X&Harj&;s`YZQu~nyo}2VJXP1iul0ggNq1DiDmzr>*~DjxcJ$k z^Xy+hF9Z#*QBh?-3`t&uI3v=HP}@|_-(k-LeT#TP#h>)jb7!Q}r5tXg@DxknTT;ZiUx9|th z7YgLn`oS=wCWCQg?^K(n|IkC+D;TU-E86zi?nK~NL7{Z_KR|;}EznTY*|hB@j*0(6AG3JLLZ!Lxo!GL>|;K0*mH05E$uEjgr%}tJzr_C1G7S<{4;~0Y^8kJV=|-I_gB*MjS40i}_8?4=tfl+DZmY@407X zOg&VWg|ToR3N56gplQIc09ko5Sbmw}BmbObq^E%mEGFd-fIuxeFWw?M*MHwwrIC~2 z0WZ@Adc}Z?#~g+I6&>29rHcP0Um^U(H5sk7>D+y_+3NRD_eiB+4B$!@k8&AEtpC}8EvlEzWrWZq61TOoic+BVVOo&l+a)v=>PAaap z)58}o$|FuiwYWc~#Yy`iH&FDp;xC=7kP-vWnUDvgP( z1C^oMR7f%$u7PwcpKTio4(MsQ(wm`OhSfK|wy}rvzKZf&b~>!sP_NQR_*V*CA>K7X zaG+MUz)Z*cNR53q;bP1_pO8<*r=-4erd6v$Txs(QOA##S{_DASxBTYsWNphke1n{>~8LX zwhPh{(sFR%AM}_#hJ`-w^9n2*$`I+LF@A1^nAO18HXBs|(&4!@BE{kw3CDwUU{LXM zY+T_wMnaeZHnMKU+4Td>$cB4OgdQJ<)oWdF(QXCpG0zC&xT$zr=;$%ffX+ zp1@MTTZPLDA#IX~@HQxLysEi*XT)|9yDw~JajPiqn8}YD zwNG?9Eeq3#Pew9f9Tbr)*{?BS1kLOwB?6>Y#fr9Jj$p#LvP+F2|H~8=v1Ujq+UX+h z0Dy^UW}=bo^JH5sCeXGBE&eVz<-bL>do1DrP5j64VW!?9nB}%aKzY3iqZE%4C};CJ ziQIZdel93BB=oJ2potLKY_`V59IdQPY&*7oxHe_q5)uM7$q&xJE*-*4&8DD+^aL1F znHWqvu@ceRI||6M>NoRe2n9)hpohR;&m9VfMV`KWY8p6YuPBToWcaPU%F)kT zVHyou%f&`Nw(#SAzQ2Y|oKSAR-}L4VqGLPpcCVpB23?W+^F$xAE!Gnj0W8Ga zpN!jX)1J@P5HEld6mpsVr3B93%`Pl3nuu*zZ2GKdc_|6s&gu&yF6j?UbKcM8kfbK>t@820v zas|}4x_@RIx6}uOFPY9UsR1U^9_K_(VOIQp-*IZm;$GaoZ`=5+$~{o@-c?!jb%g4g zjrZt=t{5JQLX{I^6mxLY{Kbz2u@FHf%G8*g)jBJ~n$57j4B9JRQn9bh{HL5~j6Oov z)YMF8Go6lgMHY4A``o#+-DWn-iZd4!22Y5UomVB$Ce(}+fb}M5v6Vt{64uPh5f5~y z^t}KvnD+~crQrf8_jkiYI7kS!!ap{_R1IF);nXA&CdNt`g^F`(F4f4fmm!(*@Kp)b zIW}}U$5-Maz`$362N@cse_nKxArygfEXdueag3qCp4aZXUJw7Ec5~nWY4{-SaIwsz z{8o~V(FK<)4f88-_ZOaPUvgzIgAlQdwVZ%57;?BVtRDHql%{;UnUu?NUWJMnPb66k zOirwHCoI%h#a$qzAg53Ob|n&;mUbFAg(jP3gF6%i!or-h)zHCo4Ev6Sp)g;zfI2#r zX=DNE3!FJY9Fw4qys|@5tr)cq$K1DZOs(41t6*&*`ecB!O#9%G2gp@Z0{J;L|Himy zopIXAZ>6E8jcqDYnec`onb`}l0GFMq@d)cb%Cs@9s-E>+*98zJMtS=9@XTP4%Aute z;h02R?RGEfevj%au{8VXoc7xtr1P^MnyKf(1Zp@jiogWCLP5UuANyaEBz8i5K;d8! z4Oo#16Ku7xS$VbXrZ*xoooyq>yJ7P9XA!GL@{Suv2|x*H+fzBlC&W*d+#8NcLy8xw z;}>Y114c5sme!FMzZ&IcL2Cacac)Bkl$ATJ6P}z+-{kh1&)P-!eVzEp;o`gB3R2&` zKXn%`Yt9wYWFoCM@ z$e1LMD@k&_9XDBOrdv~ZecXB&rKisND@vn^6tpeN3&a1~%xC+e=rr$A6)DDwAcICr zN{Ti(#NnJ3b1hPF=$p8}S%_Sx{8AWc)RiO&Jv<<8Y85+aae(rabqKy?_dR6id#aC1 z560ugI`K=c)}0LGi3g+NAOf5w0hvPVPW++n1{!e2jIV}t1TCcglf(%mzEPNOxEQVR z_Y}V_+oaEY$wB8B!BI=ZM`29s&p?1A>!F1hu_6dZo|;v6sl)A@pj}TSNkgfcfazdF zPI#bah!enb9ly9YH4HD1!xBB9gsKR@Lf%frO9DR*>FH;#o@z>j4eR$Y>i4l+>$}|Y zs}Ph|J}5C>DMkY(%Nkf20$a~3W<{EiX9hW&051wi%v)Kolge$!H_Yc` zt9yaj^>QuW2q>_wF*IYP*9fcafDOzVd(t#&jiJeAec}4r&bwa0>S$p-tY3zugjtVQ zr^{sg!{c7!7uZLep5Bn7>bwj~RL8F@Cs4XU0k$Fl25$pTtVFfjYWojbKUz^eewh0) zp6h)VrQdXumgCdkWr%2J{XQnnr-TB);mk|fo+h*4J)Ei&(+w)&%laxxR zIJn|&;r>lKn^LV!s)gqGfg}?sGHfbW(&$ms-+!iYovQz%{v}24wCpt1F?l&Jt4gcs zIK_M!sd#kaSZ>^&gM&a_9Hr%g;G);fnQKrl(W&5#FTW_wAvmFYwp4Mx=|<;1gs-r= zsq{0b-6aufVA*kb-^RlW!i@L*9{y290D71TkVL2(^3wN#AuvFCD1C=<{MYHGZl3C@ zX0n001#w!~2F;_61nAo%7}MJ=wwYXfw*d$eBs`G_1n-q;!w|iYl%fjK$B8tj9{N)% zYb@tmtXt~~m`?Q?-PmRVAvwo-vZ4RF+MQBq^kjCpaubA^aY_~AR}D+2urMi!0(qz6 z2L1}!Da8lGXkD0Wi5gF0Q!oNnY2Aqp!KA~VOr0DC$d`hNA>!PF`Ykc;O^%m5ksu&s zV=CzFTdK60C&ICT{WI)-|3K`R_KH(nS$->8)El9TH8XM%6@o&z@uUdi7&P@B=l(1N zL(Fli!hK>b%;rVrM0Lh3b6X;Rm(L@t4OjQ$1=o!bD|0Oncwq$*kiz`3v=rRY1jq+> z=845!deqswJkQ&eJwcc&R<%wi*CmgP?d5xP%xxcDu*=OHwQxG;W?J_+i(XCU6Cmb zFDd928Og^IYRg9yX4(XA}i`@yVS8ImZ zBjRHF33faXJ9frjIR)*k~*+HsoV(9G z&x$p~VR0PSXP4E@l4>f-9<$*Du?=j8`#gq2ZwmP@Qi8#C0s&qHaY7>~6gDvVC?Ld9 zvy^~Gv3~#_2!^RRhQaP1`tT9_L*#xUUXsnCMNn$6?+SAWH`%`@e2)?7LXl#{lcKbT zQ%R;#wl2-RzpTsana=KXntj`ORoAWO;~#aWQIkbWLU2By6_hE6qlZIjQ9e6keKCko zSCAPJIDPS|w4N|89oAq;xx~7^0PF{d7muVm^Pc7VzTEx%+@o5s==40Hdcy>YY2 zbn*QACpFji+_G4$RAnjsIOUq)81-Y11vf@!%|1A+SB}N}D;NRUS{${rq!Udsz^+Z+M z%C9J{Tl7dXP;EyV&J~uue=A>~U2|yEY5=5?oD#7;G7Kz?CZ%xfy9|bNL z!a5})y@-57?G`&jym|KehX%C9K~$^65w8iAx1og{_NN848T>i^+kOO{<|C?}pzc9l zuV(`?T=H<$Td)(b>80zfnWQ|kFA)-#;#?}t);SxyCO7u0c>KZ@dw9wOhyaeT2v%gRieB+d!Hf9W!T zqRbjGL=5e59&A8yToA@bx}BiY3FtzzGOD5w!m3C3qx{N?ZXZ+jl!PoF+AaY!jbhxN z7RLP}4K~m5J+5Zo4P<&WWNrulXPudzt%gY+TxM-BSCrwhKznQFF~6?%XC{+-HdJds zJwFa#kow?U9Xy5#g<5tpo{7TaP|2L52avyzMYyj<0MH^+T!gk06v!&}AsAiO0unVg0?&{0z^p)ufVm%9DxcTyiwDE8xTVR)^3hdUni>>I z9KE)}A&T|_qtVmLS+WEHsYw>qh{47V9NjI3&R9u|NdTQ#NTZQSu-?szXXW>SdK@nQv9UwXZ^K&#pC8Gb(j^a#^^sos!_$>kC4mV9qSt z+$fR9NY(YSFl!6zfI~}I_qh~|ag@vtBgBKH225#@GfvV2B<0(M)vy%eQw9(OQ;ZKY z!>~U}a6i4x*Z(?5708z?W1N!_(U3&@Q>?QUdGzA9oLKy0m_w|0Jj(MnTd&T5-5YHH%UC_WIlDCzI8w^iDTR;Ph9pW|ngQciJOJ+4(em*EfVK94{Mgrw9{@?icIXpfYXJKd$aNzrCXzzu`SYrECOAoUjlQ2StC)h&S^Ce_$m6;5` zXmuV!=)L!!eQ%!g3hT_wzXHuWa*DZ)%JF!RhqJ7Oa*?K|hAc#!ybXq_V%$7LU#Q-9 zeZ59waYHS9XMSR6*DxK;h{18MpVn3=YK|T5}rYh3iV@$%k})68@87CRmYpf zVk2lj^IWx`XBAQxB-jrzDu8Da+udz9U{Z_O!zm`yY<9ZVlL?NEZ|=vmi>N6_UhjP^1wz;3Wt`S}L?5t_Qmo(!L7MESu%g z_Zx&WR=`5?hZvDmA1Kuaw+7BjggYISibwos5Qov;VPNGOcu>ydApbZk0mKhZ*Lv{J zAY2sr0UT%utJvzt?3&5skE3D$1~)vrT>fw87GyjgIGmY+x6b}|ZHhhPc?26)T&6Py zh0Izk*>3VmC81|Z7vXrfYe6qUt*v!c_F}F>f^Zq2m#H() z0;u~UmmhvZcsGW3_*Vo(5UW7e6VX!{7zl1PNzVUB+0DbOvZ@FiZ?#~519c6(<@3Bf z#qtlx0}9A3pogt*m6tZ2QhQ%JAHn+0J6WE(Tw+N^jda*w9G>|HJr;~6WQ$(j%iAsi zKK~v^;a~q4X$fj|!^vD}?Qr{AI;ND1X|xnG`0T^O+;Y6O`knj&USk&}LDWrn1RPu0 zMD(ix60=Q}6|8loNLM8&<~J$)Zi-67H%N@uCAl| z@-TcdC`_zD4IuZD^3$H?Z`y&jlWVU_DIp-GI}f$nu$^~R?Y6{S-aJC>fXp}eH&|6q zF(Q4aoQa5x_JO4uI&M%<#O@Z*xUn6b@dF@*Dm|a;#atiTD8~z-H++rhkm*2K42WTaKb=#Ct)Y%HSe-YPEODX%(-X@Bst#lu>1iu94K(e`rwtF z{`h_tGM2wy>ctY_syQrtzmWk%?%?55TJdo6L|`!k(FlbLkUk@$&;Y0+%%1rEOnXdr zcR8~w8W2@cc3XJPvJxV7VXb{+;GNSN-{Eg6i?y`)I8-6@rw(CQ1ddn~x_Q_?3pCP} z{N6`l_Sv3Zv$PW9GBY5#2+qJfq!02rH>x=DI}2X?&lRmgFrzzN)ja*(&gan zhL|i>GoQ{10mTgiaMz%ZDSR`0FYO{b%j0A#@t~o;3FXbUxXRU!J)O;Zhib(c_FaWz zZ@j<~PFVbkaVoGC8~C9LvHjjk{ocliT*Y_GEB{(eWH7t;zv+KR49Cz6o4}jVZ=?*q7E*gWDu&*ipJk}ypxoEFUq3sti6ZE zfnlW($br9w(V_kj#Y+E+wQ~rMw2Rtx#ZJezZM$QmW7|f@w(X>&j_su5j%{~r+p1IF ze+K{HOwX`JRd3Z*d+)XObKmQdv*S=xEo@ahOe02~!F-U07XtU!SWB@Q3QOAA< zp+@7})Zt%uDjsiW>VrXa8zc1#BJRQEU8I%ZhWjn!(Hg__|0JWVYR?zrYgNxnzfI#h z;8r8pmsk8WSb0#Y&F=0AC=XkUowAw6I8YDuimWfXfXD`X3zI*#AB+wsss9xlrW`$& z!sx_If4}RTN_u)zDCYLL<#ToksSScEQ0I4s&v4;z8y)@5b>(Y#5pYK>OpIsH~ew<`2xLGYZ`NRDlUl-uvcV;JNg#!(+jd+|o*) z`(yk|o4MOx-4$Jz0N)h_49Nlj&j}k{{%g8az^UrTEMR>e)N`ge$E~>A(-^MCAHGK8 zP$r<2a2j&F0b=&@$ZWBZU4>cLr5_=l3_wZ;_Pif+G{Q<6=iFgC{ z_KV+r^d{L zH*|^4Xx%yqiuXyt+fh=X_<$-i>Bn+XfPw4@{?`Z0bHGJ^X4Et4@F%`a7W?lR zm=D8^(^&@`6J#a%{L;+MVHxM3(^)tYqJLTq0=q8I1$a<}pu*z_OPEzHQz*kjo<~@b z|Gt`}cH0Qe&#e!yGoeh;W!aq5La*0h({)C;4j6STQZ)Rq*Ck>dXDg7G#kNM2_y=7X zfwKqITB}vfsNtgL{n~JD^Ae{%jlhK&#?_YGMe8>j#53zt#8c;_q=%@q>$V3jl%*PPoMw=pN2UF{b46KyFdy ztXpe)qAz0M7RSr^1%h<%*QkKstR<#abCc`|J$CEzO}6ogbMxOyG-7eqb=FOEi|u0T zF(*5w|4y=B4K0o!=Cbr<%!^7JvR;?0U~N^W>9mBkoU;BN9Kqb~^tcAq8ouMQWyXox zP&!(YF((1yCkMrFgM+>qi!rDo$q=|b+N7wM=XpoA%R_Fu-|<5iuaeI2G4v`V!D0_+ z8+w9{W;Wap5Il$l>fTl|acvCv>O9)bkCq4IEadNl)wH~qTcNo+CM}Yny7zv3A|#CAQa%LpP_`bGu? zGLi*a$)DpnG8+KO`x2MgouHuwPLVpow8^ffH1t|jp?NEe#U&8}2QaLaX^)DiP_Ony z7AhtDfD6Lg{4E0qUjk=_oRU3D2#Rh@zE*!Zuk*P*xS-0W2m3-;t>&aOpe7&-$-*T@ zzb6AkpoPH1{;C@Dm}z`tt5sp6xS+8^zT)IH2}daLE`ufaOk&e|I7GK4?~}e;MtjQB+iz$JWrSLEk{DbH({$@+{CRv9RvfK zn3&K)NSK+96BqpM7m>g5kvX^kDfSh$aNYDrYh+t0nH1Z?4765kLfL;z-={fkHGGC~ zUd|5X4t0I(Tp)I%erK2k4<|Bsas}m)7U^g=gSLSP5pP28?$-K6L+G~+ zJbAiZ;9m7U&PF!`u5NLa&GxhDd){PQTu*$~Y<{ZN+2TdRv0Q}Co;U7I@!*u>M1Z^m zrv=C*#tF?P@-!3L+7v&95jw(w=$0gkIq>z8?rSE5ojsV<)}i)8!b7F-swEOJ*a2s% z`9)sG4r2D7=Nnl}C$!6lWw6}UoyNPtt3gc;k7;$jLmn8J?X)&NL%6Us3zkmd2RHsg=w^Vi9XLUd}O zs~w)oP1(Xh&y=ojgzj`wv&b-u9G#UG$NgtzP;3UHzAWkt!sa3C_x-Ah!9@S0U!pPI z9mNJfP-q3G{Q-&^n^Q}X=NlsD)ak|h+oo=MYyO3>w@Qa_f9OD-kLB~nOhn!1K;bl$ zf*9*A9y$P7CIu&`>9?&(h*31jUXfC`K=jqQI0dT9gp!L`H_;69x4l@Hkg%y?ycL97yBX%8A^Zl)nSYHrk0;;QXDMW)Vk&qk71G%OGUs zQnkSCbC{v_D^CS`4H6npzm{>fo$xko>h)?e`U>{CpLPsT?m+{OJX#i{WU2s5?7?Xt zM8f3W^!9nD5Ac1Ti!;T_X<8gIoMQ1kzf2Rx&u;jbQ~)+wvuKD^0j$v1K?!RNU#GIG zFZ>evl;oXYU2$5(jD1GZz+s{SPv{(T1GZ+|GX^uDQT`I1OHC!e)WJ9$NtQIZ+=Rsl z^}OXHa_d9)SKEXSk#%dh8!{L3RZ`)g+vi$g6?E# z`!@|sWJro(qQ%{f@L#*tK(Fl;zl`p763YI2mbKC+b0@R~(Y4N|$GUUQCqB0WMPNO? zs&5Et=pvWsTgEK2t+e@K+)o{*gzr3GSQd6XsbUx+WiQc@C-PXn@`0%ck8IG0NVuWq znes7PoM2jM)O}J(^FC z%ky)#Hy$^7?YH0e_+$Ky|DH5joHZ~rd>mdo{^_ai4%-?*Ebcm?ms@u(Ko7Ll4vaiz z;arP;U~kcc|IXkk9?dY*alS10aT-y>iybtCP~eNSFe6jhTe(dM)~s;4`mjk`=eI(e zPmlf!q2koiCEyxgYG1Nq@j zWRD9X*XxO7X*m+j+@j`p{ziINKurCTk7!?{y*?9iU|+ActQ zM2J4~$NRGBdtKnIHE3CqaOHpVcO7a4+CJ+~FAVN^@<6mL zbY`sp2AU$esC_ztfPO8o0}|IS7wim(gEA$gk5lpy4+dMj=c&~ga<9S?U)zIH%J+n? ziL@=T`(^odAhK!NN^w#&X#wQ#h7@7P;YQ1gA%6NkNw5*?s@0v76Swf8yo^0r1z0~O z>)ecPILJVnZsj#E1VbliM}x12(=Xy)){U+c{aGMi=hVL~BE4tvnK}WS9NlSH=gk4q zaly{^6xOb0G9qM3g$OvQl=4A+bb^vlzNLmd*dJm8JVT0^ZR+%)ZO09HmTxh6q?dJq{1x3{Jes}dJ5FSk1^X@rHH*+v_kRZcwdKicY-^2Uahf zK>>I{>7(c_9}AY*kI#<`xJCu@#2xJ97)da6ryq0?2}p(jJWwr5X+$D2m0`|9@c242 zeGs1Br7VTe5i?yBJ{$P^uYOZ)s?58Wq{0d`nbwwf!QpizY(XRtG8puM02;xur##tE ztx3|It0^kRQfnVn6BR0oKtEkFB}feh#CTaUK=LAT%N%Wu+1*A}PdI$5xHx$3Z~SSA z890I^MgWe^6oc61=O{H@LyV91zaAPbZX8Dk8r z5T+>s&7%v*A*SjXJS6m6d^Jr!j~@PV=f-ya=wz{k8JKMSxt`i;-2?CbB%k76^izOX zYL-ghgkt|S4FnxPd-qX}^%0xfIc9ZO*>riMc7JVtaSJTA7}?lTe|D1PI`W_KIshDC|mwL}SYoX)mo?8ewMNNlXYc?pFiuA;p z|HpXi?p5rw`f9r*+rUk+0l#Et_ue-Jh1hDJL@WsKF zh4a$VYyZ>roRxTAexJQuO_s+^)IjL3ui<^*C;EHNdhO2LL*EVewZTgqKJ|;!KTJqp zJN4^q0Lxun9^Xv>@WV;<9TPY1J)*~DKNsk^T|`fF)1al~86bftY&@l)06hMM5OALU z=Qj9&UyvOi=WYJ62{<5#ux)z+akO~>RvGaDeEawtaNl2u@|WLPkFs?)`)g^+)&F@K z>dWmxn`3uV-ai8>Q2{uezTf-#@Xs;tq<8Yj%E)SKu{FSFBpB$u=`(S~4gVoX3P7|M z-U9aadY=5tiT5$QWs98@dRUqN2wP(RYWp_X>Hg$1v~M@q9o?%5nTROZaQ$%I>w6&8 zsr1?|t5uA^iH8`EeWjyIq{H=SxXt#gnI8A13a6g4t(x?l4nWI3Mt< zGE6~EO6JJ{iw z(II>kpMb)3gzrlHzo_jx0@j-Awt0MBn_F!ynrE&*fVd}d3hzLGKJb12mQF9@KYIRu zIeu1l0H9C*|K<4qGp&LF{)gjVKoMsB@74bYJu4XCe>wjD-T?rxJj0Xu&jtH`IsS{y zwI+j+pE%w-M$lkr05Igt$eQX!NRtP)X+BI9Bd+Nm^2fFOuO08HXeXcP%+?z^t-<)&r8HZKnk z0Bl~<8U12m|7^+jY^mGl+HrSraWU9~%opUTdk^^f7ogj0HdhoS0V)wX?x#IK@y(wg z@@*oWNwv|+`6DMY6Q_tP{0yY+ta7jRjqkU%(qB0Zel443!nqU}Rbb4_=Ho{Y{O$Xf z;gGXDYLYMP^>;H!w8E-0HMgQA!Pi}^WC?jj6 z8@>+8WZvEE4p1xQaSS$^PtADVUjXx1g z;9{t;VDLrZIx6xJYBFynVDw{(n}%J3e@UeJkPP)lTC&K^F_eS^1p^%!5d-6>BYxvU z{*-A7bw@+RQe-gb(t<1evlmV8(DPdA(S5Fu#@zide?KFv6uxL9p>5#zxKx(sZL{6u z7m+6{9*R?aNS_{)!JuE%UGH)zYXo8u)36OZ!4fp8^@7q4>Zl^T#rV{WWAjH(%H2)= zimy7^4ed=}H3*0qLgx`=LuBDZWZ` z95(*AQujAdf_#*J;ew`UqA`M?Rr3^=)ynR%Q_m#a-8nN=n}#usFx2XckIe_lUnq#K za~N&n9a$Yrr{tUGs%#iyV#6g}$>O1Y73A}Rop^a;W`zcD#4$AvoS5xo@Yuh269$Rkdi_171xzZf z6htghSaPAPiu(4&$ZHcKNX2mQ=kec#KNp1kKsI8!29hpN#0`xB+iE0%IN)i`(&KvT z^B_g3=WV?Tr^9#cE={vq59D$Bxl>tHySI?%`S~a|BRs=j#7m!E$HL?c`5o45PwBa? zvAOUr$l(J4Vqtw!{=}Tla-fDBHHqflP?T6h7-;LFgj5U#w@auD(DJ`($`|z9Apzcj z#Jl2D2a<&F^&p=l762i!aWrWmN3bIn9)%g7kHARIf)i{29+~%7_M-c^F2*^<3^6Ycnv;V}u>UoqeVw&<(SDvyQj1a{R z>?BPh5Zs6+s`sptvk;nEQ2=D~sOx^xS^rZ)WtuzN0%d$gg$2`MdXVUUaM?~w`HLT8 zEUS?E6=S6LAZRQDW&A@bHz&x&XZ%TbmX6dP24e?A9p?i#j%l}Rt~(wemNxz^icN7l@#S6BfPkRsJ&>kXW#dCNncW zOIHoo1}*5kcU`aCp^mgr<783v^BPUT>Rnt$X$je!Mz&F52LlKienRNs{CzXVnPgf|1<~4-=eSFCeaOAb~$w24uA8%39-v!WOJsrG=FSSGvVC9 z;1zGgOY|T4-XLMnVgoWP;t}}YvT*jfsrTW#@hHb?t%{EW$?=2)e^FRpff-?tE>|Fy z&E!$NE7K+P4t)@R*9hC}<1YYge_nauMor~#H^J|IFz83aWAqKLu%X;vQnt)5H2q3~ z??7Cyk_y%!Lp39`XMq_0fG6R6V{T6d4!=Lcvltk>&aES;2d){@Dd6&2+MnoiJBj(= zz5eZ*rF)=J(i|>0gjG>jn|dUfuAmg?d+mb}sy9DhHkFDO2slB(;(=qvyFf61k|;&$hiHG5#xtu7{!HQQ=xOC7qRIRfjOHz zR@3@Sd_jbM6bMvJef1Cp1MPq2|C()Jqdowb1+UW~21(l6nOKDGN#*n*7>GUR84El|vr{GAVhc4Lx*n|6Z6`mBh z%fRM{qy{+mFV~L2t~MY?5V%vQ_fJpiCx9YibppL+toGycA}zW=u0kbRjXxyO)`6z|(5g zkA)0_YuvCDh-bIn9CaKrSDb~6;x;8CTsHdDN45G^kc+GZq#P{a%2vVGCa4BPfLw5@ z9Hr)B>ZbrUwD&znio4#UH+suAS8CsIvV*PXSWHqb2*<9=k%D@!3n^hf{|l`XZ##d} z_ncwjR_(vhG0FbjbQm*l8>8D~vjpg1c#Ik;)3g@t(MIwo9#(E7(5IL%3R z!9=vU6R9U{{cgAPD@ecclr-6FypKp|aqSmK3&3BSDl_n^b=#)162ydqdyEI5gKN{7 z#gPPr_MsL95vqdNRb@pb1x=5Lbg^Uw!*6yq)^Mnh2>x(;@8l|Z@!KYU>z5Arh@mV% z_=%zxudElxgDDqu@vj@EUj6NfJfR@+vTjNF1cQXD@4Lc6R>&UfzZiJRPwb^f-(N>H zfF=OmIlo-YE9O)M;8#I$IiSpbu+SS!;7j_UMPGf*8xb5#Ub5^%qAEYUM@ggm-JC8&aa2UNo%ul7$9qrL)NY?5`5+8--BZ&g0 zRXCqyF34~iCHPKEq`aP&{F?0lzVX7mNq8i5^%c4_69I6C8s_e-;FsIKMZ&S zg!|KV>e>@`g~r^fz`xLKS(DPs0Q%YC_jtHlE%&ZRtFK_Z1b4!*q>vz|=E~$KjAT^Q z6%u&WN`hxZTEmBYT+My9ADqitIhAL4p4q$&k#mQW8*cYgMER%FH@*IQp)jy32Cnv( zfi>UWpRSKly=0vpJF3>iFS7zf7@%f^10r>*do2WAwaNEPK{Fey9VHntNZS5t;Ghu? z_jF#PALUO*lv|dIUxoZuzWrAT5CF?kRMI>KtK*lMCU{ZV{p$tfmE1tLJx^^jnGn zYLY9gA;C%X$8P{p)L_HQY7M{(di=8D*suiy@vtWyoHZ@-j$28aW=jL=C8zflUv}pG z+o{s=h*@Sdnf4raDVLGR)T_%N=XkYz&907mq1u`@3oiOz!vzDOWQ;7wwhlpint1Hr z+Ba;tLuAVpS1MFNsCfR44 zg6sY~;r#t?l*hzIqDRBZuQFxIJ6aGyqwRFviLUev(}J6TG}6jBA4- zVtgryleY}lb%#@$UgDP<=e!nMnxKhdCS#JF7z$WUB`0}u&CY$zhq0X7Z)6(42%@E7lnq8K zO~P32!Pn!E@l0X`53KXf+jFg}b=+cf)(Ai@olVhj>^MpeoOHydfgVOd z`FgQ|i+`z1bHs?y0pJ{hF&OSojrBQCF^w@6A+@Z}>5Q<9KF(3dJEGXzcX=~|qMGL$ zEmRpF#3A-tGgpQRWCUPdsl&mYu~SlHbRBvPXYfCI-p4kX3A}?^%Vh%_h+)mAMfT*- zSAUxP`tp(WY>nlCb>hvPNzS!VeVERfOs96f$t3#PA+!hJ! zRl?ZuW780umdr6GrE58ftyw5WpuZ3*&9N9p!W3o?j60g7(>-$pg9?9i}j}cuXQCJUw6MNF< z&MY~Csdx3CQaE}X>EWqE+N4}qC?VUX!5zW*Rl@cDLjPTtOI z4Lcne0SCWE9O*?^_YQhJKK}@Y_Lv;be`4(*A%p=#uV@GCUJM)As13QeH5zolVoGs9u2U2|>1641y^;;4^iC;~b3$f3YU98yB*K zm=w=on^55fhZKHeL)~?FQKVh?>7UwSHfh(5Hi9=us}-tsI6WM7ttLPh75jV(u0x;( zfn8RQ9%I6t>_{3iz#F2kI$c!IE*hbvIz4Wwhqzq*`-?L9=r61==@^+>PHgorQZJ*O z*H5PO4yq}Wy`D1D%%drMCXSeH)8lwuhwd<0tI3?zCXX5Ho|`Zw-#zp#`eyML0*Tdk z-*x<_(kP)_rIm8mpj`iOl3ZFy(5GMA{FdN1HKIm7JrIQ>WX3Wm{zo~A>0C+rv_J8L z)}&)JD9}D7ONz{pkV_2RRA3cuB3G;40Uqspba7bPjBN!~UH5Pji_0uYUM5CK>@@fg zz=+u(wtf2aLW?4FyMQex2R`I(J^9=d!c z%FNk*nrb1fiA%DNZbTa_1ajE_zH|yEpEok0HnP^BE4y=V6~o#~7W_)!^Fft+(=&w& zWTF?wy^ayzNFw0y- ztLAyZpxS%VtpF2O$Wg-;z`A0a*0%gjP=~;vJMOUutRP<-5n%AT48F(h~n+KNY?n_L||OGQBQ?}mP5tChZTs#+_{U*%XK z{|g?IEw=>)hF_cZNvVkK(o(bfr6M074@x3&(c?iLb+@JnjDYw8_S$W{3Iws)sfn8a z;jr>#F;tmSRYw>6fAy`^(sVbf1ljy6mYzJ1KrY6aWnR?^svQDaub74=P=u1m%h?y!#q-hYI{$b8L8m=&q1%7Jn<~GN zNWgQ}G~O>IrCRW#9FjO!Hq_vAxGxa7sl2N#w{b;s5Om*GYyVL9kzGIaikVSbw33U;Kur-WMR3bn24 zY&AfXLI<(-1c21uW6UAY+1z@-hlp-_K9>Uf&nYpn#m4I8?#aC_E zyPIvx%`@P>ZXfk+A#sA_9r91%BsZ!|Il0OOrRand{P8G9QYF{_oNduZH(J>L(Py>D zA)=SUo&6f&Wcx>if~C&JvwR;0i5_duZRe`zaUgBCq|7Ex+1p)0c|NA@-nK|v8Mikg z^yN$~P1JL>-J1Y26ws=?0G6$aoXlZx?un}z_+NfZi{6dhQmL#o(@F1n7^Sl|=)F)V z1vbZ}wC*807veV}XTZF@-)v^VDyps=JQXI1Hz|{~l5%Ba4)``I7fJE$Q9uOt7D9## zsov{iBv~kqc9>dnmvm+uRGM;#6;3n;0*#7=a{t~4WlBmn+K>jANG3&iLr+alzh!`C zL{EwEa776ssOuVxjyN?LDP<|#X1zdl9?}f=!`uo>_E*-*12xAa-{1Iz&s0?OLX14$ zPYTpt<9|qqFyE<1;$P#-%ES$F@r`eP3wyeO{D(6_&l}u5w@6Butqz-evx8qaEY-JI znE|VzWi$*ly?XF{xt%-k>>AsUh%;EpcOZL_>w_Ooe+(Ft==U(qK5K)YLzq?fnAR=X za7;d)j`9GoB59Hbv#4H5`+;P|H(ixTUum=RD!Iq#Rk-gV8H^Kv6&mDPUV@EGo>KNLBpKJN28u1Ndem0C z0B?iHhewxHzS4kj{Z9sLe2Fo8^RJNZ9(49g&QeC$VAmWj#~vEGE}-u$9v$K)eM!IOx%J?3Q@G1`RD6 zuOX68wiE6@T!VD=|2k0m3OyJ7uFaRxTl1Zzew&k{?d2$^o7S6s?wO(DibTXJQp3E; zY&5We%N{R-0?hMoUHjjQ##pD~WcQ*g$4lhBH?b3$yaOY&G?E8!LKLSth6g71JupSs z!A(W7Wj`gU8{x9-Q#c1SE@H38QTn=W*`FvW;K~r=Mi6mphg#{PTxv6EIP>)_75&;( zI`~n%c#|rep~}{ARiBaIqoK3zvWbjxeB_c@>yf1Tqe4*S3BY208-U3k@;Ia%p0no5H;C;ZC5;>UsI5jo3 z$K}>kz4jl09(PwU<4Y5S%`Bct1{oJUZ?yDV+{bKLh;d$Y6dhpUEE}6 zXMpBaVsc>YzN)~0s@-7JiqhcOjd$+No@C$cm2|En>fLIwwxC^sq5mVMtj>^E?@z4eenyOC^-u3S*q5CGh-Oti(NsW~?TWWO>~O`Z4XjcW-sk#iq^re3Ek4 zXN3%8^J<*VFaWE6{f{Vw6%@a80I38{kMjbMJGi%9?)5!34`(3;qGbk7<4Z>HV$DbJ z4A&W&!8lButLF6yNiZ|DuZH;d4()>}zni4RfKM8GEDce4JX%nWOe)m}$o>*_2j_diSth4)xZAZhE%4DeN$ znBRS405*YzK`bvCakkw3{zRMSGK-~&wJid^F&ATn~!Y%tRx94241(HpNj)WeXKXM%e&Ae1nPN-}@$bkIvfTPrX*_1q+8m zB;B?SY;CGijz3Q2f0}WT5b%>Zr;L}h-j`kS;5H*rrV9ZFJq9ZHLF){#Y-m4|c?@(*6l?<8|sNnntzyo!@l9CQK@tJVJa)!|mrnZS9MI46s-z#7R@JBiRI#_rf zlZw-~7MKYcfOXeiLT3?v!#dB_MmWk@CihTPy={m7)1Dz8PClsQf0y#my9~x>gf%$= z+BF`AnEjonvRm@{JPn3z3ew_BC^BB+SaK4WfkPRFeuwH^&0+!1o!9H_tp4B-`}Xco zDPn|~ai=1`dAOS0$bSu1hYdhyu-I24;5ewMkA!yCpTUQ${5{+Aim8J9PH=ZHkZ>qO zFq{v^Y-1CR+Tgy@tPr^5+dn&l zIM~~L{oI^%3c%EZpj~di2m`%;i=<&L3GsB~3+bbIj$r^SFeb`~iM$T}y$tR;L!LVn z8#~dM^#<&lIiO)*zMXmmSTYjW-YZ&2y++&y$=|l@p}pn4N_#Db7nm7QeMm56?~H^I zBx&=8tsjvUa%f|xT4`^wD5^py3Wz=zAms?Ek`ol~zHbA+w#H-_M#7+B-dU?8F%sr# z#z)|pIdqLiFiGy=D)F1CYAK*Gk-j9FC=_Mh#XMyp3}r$Uwx z8W5D(VEb01>eV4I`D-obpsOiCd?6h^<$VJ-sGV4sCeVWzBWFRt!+dB(UXi|i`u)nZ zRC12uD#^UG8V&zmnvPQwK<&CuLz#~!IyA&e>sTG9%d+GziCA3;Zs44Y;*DwS>?;-W zVnQ+TLtSG!wnov*OCa5}I_9Hue;IImzL z3*9$Sbix~qljSx@fhJzJr6XG9-%4`wsBz>-D=X!G1MGUmXq=({Z&;8~#&23f`_We>k%G`e$ z4;|0 zb+h&I1XWNRn{9Hsq| z*s28Ak_Ctca~lNg-;U&q*p^aat?7Y{`fC9ZB?!yK$0h8)U^Sk1Axr=>dEc3($=u~9s1siMhhfR#Kuj`IW{aA29KuYJR zb@8i`z-_2hLw3WaqYPMJ{RRo^`faVA8v?W$>_UDa28l%$WDUp?SMhRk0P}#S45kjxaqFf4yZ|^AH85Fh2I1FSo zJh0r=s~PeM%S=qCxk|_1G*SMZT~r^VY1loDZRs`U^L)|b=F^$$d0XAgtN^<<`S?f7 zKOB$7vGC@SYN9+sBB9Y&1AlTFWkOVBs*Mk&$4e zz}KeWFB?^k6KO8Bz)DX9)=YLE4+zj-$2v#0(%LBNH7wyntq|*)}BV2JO3ldag~W@hkGMICLF#0E|y)V z6A#vwSru4DQHn^>$`?K~GDubMtwQBgW1b41@{4?VsVfIs@*uX6EBDJxoU=B|1F3UN z+>(;?@ejCo5v>Rlcp>zwzv=1+;#c~^2wLJffRM$obZxPVMFyP)wlC6kX9sMinx;>p z2Sw~(ZW(ks0=75SluKV-+{6cBD2@isITIzxVIB9B$ z-FlT{SKn5?LW?-QgWXAk-3o(YKun2|{2qSlJ!O4BEfuK|`Qw6>8Sm>X;9Zl^;7}lY ztXa1Ss`()W+Ir<$8csV}Z1@4mQ%(ayQ|+8%Mox|?tNXe88l;HRC2Yl_wJDHHAIEBC z@MV9B${lY;jRh5O38CxlxC{@n9{HRWe zt|BzxB)J<4V2h`z&BDrSt>S3}n;BxZc!1&{vJP^Gb)repQ zqI??hw9r>!on+B%%x-3wiiM8Fm@fQp1Mv2lh}kbZ1$WPlE7%(%9}6=In27J;??!4# zzSJTwzY9@D>buZL^7wea&G(-zfd&z5T>mFbI;;Dj*~MU3T^Fc1H9Vl% z^?8(Vs5nw^_W>?B(k_8FHs?PsrhX?bpvA8t*h^~B8eCY=WddiF0>D4q$B!q+qxHy| zRWj-5anAk{P4;~* zknP})`gXq^<1gDw6DC9yBN6#Xf)%I*W9EZ4Qs@5uGK$^%Fl6`=W_Tk?M%e4W|2Vk^ z#Zrf2!g2-&F7EfRUl`Fdolc_axe!b&j6_Yt^*0t04~}oSIyIGbV4c*%Hu^J^`NHuL zikRFFeVF3hrSkWL09^~uZKWa7do-*uzW!y?$ zyb_pqP*2V|rmU4jg6@lRY+-Fe^#nHE-Cwo3+vWvt(%h!$dxF6Mh1e3T$GJaUTJbW7 zkok*g5eXxS7DQ*cqxROsRN9Bq%I8YxbsN6M>Fr^GsMy2>j}kh>D21=I-*>gjz@ejF zF#Zn!_&^80uHA>@Zg9ntbKNkUAr&^KnvaI7I5bLtuwOWm=#}!z5jx zOQHa%hDJE*b*cxx`T)`VrC<7`4}IuERZe=X<5mDz6#}!%&Y%=DeN9j(7n#4uK|*fN zUvPypZEA+|UWHfeI9iB)!wuW+y6fHCE#5_mR|5%}5Lr>oIp-ZrkN;*WfQBl9P`OKK zaL${Urf`zF+SkxkH3tP$X-PO(H;bM}I^IeSQt-t>2ICW}3`HEXzwstPvx#8)ID{mL z32@^8!vP`Ip#U+nw0fCRF2uZo^MO~)Ekk5_i4Xd;bSl4*WALE@u8ui7u#>xj^J*(|M%HU|`Oz z9Ln>51X>VlLH{clv@-H#v|{@k?oaKFqen_eq92Q%U>Vnz|HC)}cmWCkic4?en8w6} zT?PAxYRDpl^rv;K2a{#~YshLZ9gAR2T3r8X!qrS1Ia= z>#yE=!wq-|V1qwTsD(*GV~Ch^$p^iABvdA1R75tt9%UMJxn{$TyV6s#>;?m({Bk4k1+eSV_x<8mw5(W9Hsa1L8&DArp2a zbYVeoI)kOvUmHRvMbAI?+{!C_*~vGYYzIT#$09>b+aClS)mIt)Dis7-Go%1ycn zK(7Ewg3p}AW@c~5)hD^Mx$2%3qXZ3|8>%7uS*L`6R0T9 zEWQ8u_KXbO(7=B>mXJs^`1nGMp=u}Ns_X?QfIgrX>iGTRAO9FohEO1UVgBp))!6 z0xOFS6_m6@w9L>Y*y~h)l;9HMJgy+uowL)e0KM&x_o~rfP|wEKkx9-p$(eM19{DudhF{|AEYAh+PfrFsM(gtNlVoU)F*BPy%peCuo zGY6mf!4Do~s6O+V&$#|-&U5sd71UTEYynX?hRm=;n>LEHmWbfTZb?5KU|??Cd7}op zh=qa~U}tmj8f8iWf=ap$1iOYC!?UugW|k|QwY;?)t8Lln)`!-(X2&UtuUmV=#@CEa zoTvvr+LCW;Kl5jya0tTox?*4(Z9C-q$IeHZ0yI+SRWzws))4)$krtW)AXS|nRWjRVA(Bg9y&RTx1Uh`?J4R&+wSWUux^yo4E26v*-oC%0i?|8F$iEAg8 zf+I(dT0LwGNJS?YNY;Bk!$B)uG0F$=`xT(MKzLmWz|2=#qI5vEQ-E$m9FGJ@2(A@0 zG2lXtUdT8%cbXBhGN0hcshWW@xo?upIStHtMZ^M#1A520Xsc!GGAyscuBsP(4dUNu z7`7ayYgVSm^>l1U3AgXqjuWO0K&zD)_L@(+btxdNO9LxWM64?53PFTC1`&ViQ=d}! z{BzHHdCR1L3Z)w0(oJ?S_iiBE7}|ba>R&1(AT@f!>ar$`H4J7{!#OlE&IO?;6SL2N(;xZBM{LaviDwr* zbhi{t3rV*wT_%#AYj&-?rdg!0Kq}&RV9gre`u1rY-rysTJmTX# zZkmvmXpn}o%w@&EjtvnL90(=?hUoie8BGBv@nQ9IzZ++s#p%?5;#b8@0Kl)38pw$7 zq{+^8=5-aKRvex6@TXLnpHufBYE~N8#=nNVxVXqr_h(A}OarKhrxuvM zK-?kYECNvqlNw98MDc8Hi5ZfnfY&Gmr_!LcLNJHQAX_bmv0dYyJ$sy#+Zk13%^HMZ z%em4CxWTVP0g-Mvv<7p5x})T&k-)+F?RjT1%CrHsD>-LL~FSyk0~)exxEf)dEH zn;iRjDF8VGPB#gB1&9I>J0b4Rl+AD8Qy?mL3CTW27ZZR(p+Qc{9w>yT*yx6?xCl|{ zhEK~jGPNot(p4^jl$EUpvM{N1i-*H0log=iT-vW&7Qohc{s}Sy<#ZQAX^3pA=zLCW}N~oKk@pq`6@t-=M^wVT_tv;AE<&`I?%?b z1S`$ijdc3FT_MZ^`PoC)ezdX*16qcdGrBK|&q!H_ow!kbl;zbSfv(6}h*kTFmAArC z@YAwh<$z06JhP<|1f8{Z*w|RwG%GY+bxx6 z&5;;`P}#05?=L-hhJnURh2P@~s#771#<~cyG_mO94t>LEX1+A0e}f zm6c`sB?OZcWFziFqb4=U2ZB*B_VWj=O(kYk099RiFFi(4$oq2o2_IhB-o2Bz-S#dX znRS6->7b>E^yu`Rt8U)0S%aXtxEN5Z8Us*m6jmG7j(^GcRd%}ZMPe}xpi;m#$P#Vz z@phLkKQ=_ON;N{!gc@wUVXI#~^q7eEA0|0J`l~NfWp6;-ogX^4@N@7xcI>b|Fs5%} znePP0nh=cBi$skRx&PTHwParG$kV@+e`?Sr^lG8er!XAiA58EIL5t(6*!dNp#?#-q zsf8@up7b=7l;IEA&vVbA^pA;bA@NpX0K*9MhP-qjD86N@)7)pCIp_z6oX|O^Lui@+ zy$aQ(aI3WP5*o2ED^|n4=V~+7Q5Pi|8;dhgghIWpS&eh)6ks@d4LvNm-06P;MLb&bH$odgeGK00|G}t-9)Hc1zy#)g{Ue-qXS{PNIOgA0hIkc<0VC1#r_%AK$2bb7 zUyvIja|zN$DLKml7dmXV_&T0KS--)ta=DewH5SKl@c}6d1v8UWW+~AjHgJV!!D$Fp z;V@h9b!mky0Z65*SA$9JSvZ!0Z-#VTdu=bN0u z#F7b?$omFX!s8foB7XXbfU?=>;~Kdgv-dXCzVChS(-MO;3f>@fKIAFNH;$NEdO)4P zTLAoU5G+XwqNh1O z7uIT9mudqIHo`jzbvA$n2wbG{`{w;JTxE=L#dW)5cp0F zs!HGLSaGpfddMt_;6aw6^D96uObhtg^fg31&o^L+*%8wLHnr_{OUd#SxBDgx0l1{R zH@)EuhqE%t`Vz7w7}=6ys1kHRAQUf(lwqa`ag6KxIm_Z}%)BX~O27xU>`FD3HBH@f z@Q7+$33RB&Ms(HIH+I-1=07N2|6yn7;`u|tJXgD!wXFmidbRAiAY9^d)9aXv!8O4k zQ55G7uXJ0(yn4Xs#BbE)FWg`Us`8W&+}oBfzxwhgKK=gB@5y=X#@xw4AnS`5>!GO!=fr%n;_*8B+7k4CZT3mywOoY zVd0JJYy=DU)c&Uqy>JK%u~t=+{z&&Wz(CdOCm`;nKj+$Ocu+n0k3>$A3pU3!@#)YFX8KVV@jDv=#|)Ja zn+56++!c#gG~B9$l9yK0nih>0PFPiVPFyClF6A$ku&Tt{VkhDkPMz*q4CB3!#Y-%T zArDFE2LC5L5k!uAtResTpKnscHU92GU2<67^|?zxa#4uzvpR z&;G3AQC<^hDgP{cyorVpfjBA54wE5zemw#+sa* zBpDVgqTfls*`yYPDCAN%94{zPi(!aASs0kTP8vtb3d_qHkD!+$wGvb->L3Sxs5Ipw zW$?g{+ntQpPlM*cj&Mvh)3_{I@O9}j?~6%<+T_WW@s<1hi&aIe9TtQzyy1yUAWLbxZl@fBxs~lC1++z==gNO&*rZJip9ljS_p%Ljt^KgN<{s#^mOhB}`d;Xew5F3C{0Q=z%a?N|@ zub>4s6>?$BO-&t;3{<0Ua`AtWOy@TLivUy*3J5q>WI;BVK`VM^v}9cgzQ)u!QCSs3 zU(y-PF@?7>%{jo%x2hSk(GCa|R-i<%sR7|tTFyznCS-Gru+D;Ny%jsEuoZTX`Var` z59xr5MwSAwZQ-o63{Y6_+#F|NJPXL@0kuw~rRskHJA^wo5;I8@yovx=GL9x>ey+%; zbS?2|^Qdn=`6>^n7kkp*`UlwV{kQaiqQa2U?O$L34R24^3)HReB}oJW`Y;E;pk-2% z5DR(?8&@qNR-$Dsda>LsicP=vLFRk_Qq+uzMrlO4#Ge^q2ykY4toc}jVePC{{k-Gv zu(9-5`?2g;<6&0In5JO5!+-Vl&yKA@fRw3B4DJMnJaiNZ9DpG^WXUGd7K`(9r3d+W z&+6pKc(ZZSrcLbqAOGf&z;Y~ z0(2=`oiqt-&*K8Y#`o02%IDrU?7h$=KIvR&4XM^i!DvyRGY9|l9A{GB;3EH`%NQoJ zmdqdeP>et}?j1S_CCad8C!zd zC((fT=?V{nwXp*x#K`M7!6ta!)mK0G;MW|Re)<PPQLe3}1l5zwvEfW)KCE~a}fIU<*> z-MPo>6`%+=SG)7o6$xgxLZGoiL>VhNSGM}1?715JFI(OTdanjWhZKfcI9YPpXhjE* zlsjYt;GU%y&G#qLR!{2)8<(K~X|Mt&)nv%2QYcw7vSQ&JW{c-=5IIKx6 z=v>+2R0@0-8MH#>tvMyt5jRw3CLDbpJb2K_m2T+)%N9kif* z*ujW16f3)QHX3C|v<>k+b9U*}QZG^XY>(5SY#298JurUs(eFEI{>A_O7wLe-!E2VD zB#<{)_o0*if-dRev!Be>880LOi3jNcEA^mW%=|KrCW|E>S}U;mGP^{=*X-|k4- zB^R3nW9Uu_QcQv)Z92oT0Cs|4WGH`y= zwInYvN#W+}`HW0<^24{w;tnY|fn|tt(krz;K4w%mrQ&U#`2I`<>MmjT6VJIVc7xy) zA)ctji(qlLN9fQ<(FB`ow|J@hS=}f@ib!h^Xk-|s$C~}bWKA~cJtNFoIHOe*gm5Q- z02BW@XnkqlsVnZY=le@M2ur%_=}xB*`_Ll(9yoA-pku4_`m5JHxoh`t{nl^&>aYIl zjW^xs;iFIadVWlz$Wl2!ENtVcgpRWhUg8%r*zH5ksQ|XW-tqBh6Pdc@0@T_EcbnnZ zk9VWy1HV!fLMg=g`I4nhul$9mP>o~Y?9~+4P6PH8DlM*Ab(-L-NAcOylPDr^0^|JH zb+^28uZ_mKn=r`wN}w9dxs`r1*_Uhmau#(JwA3a@lb@N7MD`MQ6UmZ)h-Rk%d7qZ6 zAY-Qh)jBu9k|BJ(CZKi;4JIyBv-m9x?8wF?TG97(yj;&6vJSlR(o1i$n{?os4Su@j zH-Gd0{&)ZG*L{)Fi&@VhK1vzT!gCYfX7fnJ0q!@Be*-?wk_8TBU7U8h z3{_JDt#D{V*P#Cpqri|0K@ek|`dNfkpY&;er*W!O{7FxRi2^g-=1 z;myg3ElYc1nkbh#g~N!Ly}H%}T3iY9l&GL*tX@^x3$g}4iAyUQWycb&R^zd%%aLspzc}Cx zTixe_@HvO00o^6Q6#ATcYuqJ93x^|W028soN=Oqek|XjTex@ocB0=s+V2}nc96EID z$WhH4nED9pZ~o?Q{@Snons>^aoN|PY96eGtw7}MI(k1&6l@5PBLjFzZmtn7GSAh6# zdJlq7kr>&+m_dcs#_Kn7Eu@X@&?AdoGD!;@pyv3M2*AQ`)N?>3V0bR$L*#EVS$w)zb>+&~eDg}sRP zp4imm$HKjdTVf|BqvCBeBXDHo`J#L)q$mJ<}tP24lkj}+cLP@QXgxh~n zlA177^n$6Ja~Tj$rTE6`(ot7q_TEv$9e3Pm>0sv3j_NvrcHdh#aNy~*J*Ui(CU`PW z)%|pw-wHzq@B_v2*>bWq;!k88L<_Gc8PH}JWdZ@p`Yv0A=Z_oL=4#r*B%oM{JM8q9 zEt~i4+sD+B0g~xWn&1C_fB%<%`IkTPkq<2lE5B9) zF7Yf1;FS)NQGzArw|f#y?%hwKj>nH5+q-AaE3dq2=^^(uQc74R`=QII$Ix*Y8voG{ zhn3}8K#}ATH3_;ap|j~7I`lls@JsplX+awJ3!sbyWsuwc=73 z<&j8b#x-Q}JjF8yEmbU@Sh9ZIdQxsl)deP;rDvYuH9a*sImr-cj{%&$&6_tP!iEjk z*pWcPj7k}D+CPC&iGHa;P?{Y{st4se+6Qy}7ytUgB1E*$*0QKt>57Xx_}@cQg z#n9?VJe>WzA#HKqw{O2iLAfK12OfBUocO(?4}S22zFJFoYG^UwG5jzkP@TN>>LqL>ko_Tr|IHLtGORSDFD;##_^WEVkKfz70xUe z@z<4DOb`*wcp*5p3Hj5-G$*xD$Lbq6pFM3NZ=QN%!Lgu`a-VdyJ@~)}-mlX155M@L zEd)*2wR@K@oGDaG6Si;PZdc7B&`gDiNJP^Ae+)&ChJP`!CXzB<(yt(e{laHB1US|k zw}N3k1=aRBR1H;g#%C{Q&q;Fj?Ab#snIxk1=YRg^ym24lQ^;&7Cn}AzHKJH3jUqDr zTw)fsjvx5I2gen_`>f>ED#JoME%6y6oVKs!G!t240H<@KSCK5n5Ef2wW=J!Qgt3nZ zbK@Wy*H%xRHbW9gVn|Q!o0n0?XAT}zC{R4ulP%`?l~-R~zAS#6l(^a*ITWa+#>fV# z(Zz*{gaD1oHhg@Eoc;*9!~k!^7wcJ?P=<8sqKl8$=cXtux^Oyn)p4YH_AG5T9Y?v> zT)Sb@=8Yfv&`&$SqQ;yhzY1hIZ2pLUbNGYN`7V{AWATGAQppalr@VoDPH%(yw zdie{8HAY$GHl8{74Y^ld4!IY~$1qq(SG+#I$XBw{I8HtL%rhY0ar+&9Mq%fUohIm% zMoV^-f^ly)ZrWtZzIw}9=Xz0{nuoEBQbDsCdS=J3-cwq1hNv(0 z{LVHSeVkM27!C5icX(N3cafjXn)14}Ne2!K;zu!Vx#d>hb<&tJpp0)*?|NdFx0w(G z!aH~FqzN8APz;L?RVcJ);`#Tj1Wk%`4Y5uE7@hvur&Y}}fo4VX^S7_z`&A&texwtB zlMp)oXF5IJA?~s;^a0jC`IA5Sm0$Uld+)uMAbGC8nXFB>{Rr~XNs0Z7>4!(Nzt)|r$=BZkO8+vtiJIZzB8 zP@KQ2$-M4)au1LG_B-yd8r*o}jUfA$331f7e4))-HcuUxa#rJu`*xqEN~oKS&>l)P z5KLr6X)PjAY61lfDx}V*-gEhRP048@ZqXIT3NZL-R{+h3X;&NTM&cZeLX|^Xyqxs< ziBM*4C=&Ta0&JlC=;k*Qd<4@HiU$a2%f#QK6?L3Y3v{q{I1~8({Rl&gHpB=GGaHl@ zN-KKO#$QEYHmWAu4vAmA=86+Ap9f{c*6oPXA<_$0LI_-xo^=o6(1}BSf5iC&9Z=#0 zREaNr=}SNNb3f-vnFAtpZVFI%M)w@0=8&Qizjn2Q{dN0hh}^>uKWvGwa}v^DyH5=Y z0B{K_;Fu&SIdJg6-aUJr1=Xl-C&UlpB*|dSm*Bqno$?U=c!@rQQi4D~{=_a{?eQT1 zK954s0P$QRJcu)Aw4vn$@uinvwoZAN>;yApJfHY#5Q;4zwLIv z9ID8Vl5>!}-DfTR`q#gX(3IT6OCRuxWnsw@gF?0ANg~?PfEVWPY)qO<(9t1CG{B|= z9i9=w#D(A^T{P(()UGn=Ip{h|vcxH3^a$G!l4jWqCo&y+aU}Z4A&Sc}C!c+G-P&~| zgj;Sbob|-h_TXb5`4RG& zrcf-15P>KVgvVp*N5B95ef#%Axz1+*H3KSwxssYiiD`u*#2k!V=yGiA3LbmxM=MvX zxcQb_pb~gZOqh>YZLI^FHf?ep_3+^r*RBm;h`xo!FIf`M&Yp#v#)TNLN`!{3k_y^r znUz7skiQIGPmRC0n>r~rsjy_J1LBUI$^3Zdb5UF|r;|E~Rcl*I2U_8nCx0>h1q;2t zSgxK#3|fBL7;V7Dv_^Z|uy5rdK`1R|p$5CH8R(g)+l{4;caM$wxk z*SFIZ%>%-m$OrLr*5byvWK+k)tdA%P#rZcIobh;oFgVM$6!5wYr2MN7eD$CHvwwQe zd*A!e*S`+E96xXhxzAt`7m%GjRm4*zR2obxfL}Od{VgYDizX~X zejbnXV=zx<@zM|}7#Inf9uvwMzG5ATt0;uV#ir%)gE?x&o^gPqkWA142Hk}@Ulw6* zYYScy10~mAL8C1peQ|>?G{9*nIN)cTdN)vjDpzF|<_we(rFFG>0FtDPC@?&G26bY- z{xgeQ79yzS{vnisVafj-q`JEVEhVhf&K)~H{pnBRlS3r8 zYae{@!TayOA9<~Qx7>V-&uC%;$Y8>4Fr#S}77pU7ZQ09|h*8S$BLM>UlKobO8)A-h zKotRt{$r4X>|r;9N6{=Dq3F`LgC8N`a&w_=i_`-|MUwz*c1|e9vnt(jr z5_X;8m*|usNSee99CCvqK`61@fe(%3G5hieQBVsLLle_5`BPI0JwWi-!RTZdO+r@s z+j3-AEjSuiplJg4B;xq!KzFY$NZHf0C>t~#6yh|Fi{T%{u8C+eP5F)k8U+5XxaQXl z&_M-EEYLNgNrwIeo^dfrSg|4;AuEGr+S#*R79Z*1hrh*=ICBHRG));;pbCDVF&bu; zjMmSUn}1&|1(#F$gZ~CL#USVs7*)H@u&kaq1gFB{?yEM=>;BPw z_c_vlPqza6@CQHi;q$xS`(7ZF8Uu)iu32H{{>lBM-)U5cexAb^n|P?wnE8}gaHgY( zs=9d@ZQ^&v<1J>I3zPETi`iyH2(*cHVBI=kiGuSi`)P+oV){_~1ote=6VkMe5+htE zudsw^zH$)s>pw;tG|#;XY~6V(KMi~hDn{+tw4@q9j|7Y3&1*{(S1t@ucXiYxy~vjn z30_tr1~qAl?WbYWv%;G=_C0-S!O6JKn0Uo8D?Pz2msky@y|XnR#B|K+@xC>|^qDSf zyY05yZ@u*vzAOsBL@-cJA{4dpqDor!k|)f)U^f2aYEUF9@;?}~P>rK@Bs8q56#m00 zsKx!pDRfpF^G)lPd_@1^24yBf9 z$_#X7%MDw(IfZ1m0{r&x{x<#k!292?&ls+SuI+`xK8uS6xZ6P_j4}5x0!4$4Ac{io zYX~wTiHB7fz5yTwL2NkJIkd3fCEwUDkPox6gH%g;Bv=-P6Lpwfk|a^$9%3uR`_Z)y z`0HOT4Y@iLbF)0%47LrP&I} z7EYgGL24C6A&w;D$ox4R4}wJ$mD*Hu-HVT6K64F$+V-y7wr#Trl$fC*VY9Q1|03b{ ze3yLmBOleKkO>7K)_?Gaf8cR1SEK+nL?z71X70;h{<0;emg$^nP^$j#iO~JkrJrgD zkaUDpf~u+R@T(xJna!@*l8M@Ui%r>sSVmfJv*#%ZM~)n&7}}$sLHs+3m3 zPHE4cJ%9CAe`PluzUjn5>TR8Yve~Hx!^?j6y!W1ueDouC+;IoZfAj~Be)F5(^pp3} zWfAZW7ar!*OPdHdmNTT=IaSk)Z)kR>LFp()w$uNZu(X**$*2NE{{ktMAt;(Y zJw{ZbJXq{w+F7~^De)omv7sv2kBV@9DunW6D4u$yn4n98?-ACWcizcQ;adg^C_w&v zwFV(T%Y3+Ul`~V%6AD7C)KBc%<&)x^p9Z(;#HIw$*B*Qj^U@4=f~?A5+MEBv@)af) z5=t}ny`R`M#$l0fFLn-+y=AyT3l@f3eA?$8V+$KXlhO~Ep(ZEe>Bil6-)(0@S6R^g zEB1T35pK`KguRZEb?er`$4jEiSHuPESPEj+LQFsZkM{(KSCzsuHVnBq03IY*8aFI} zlIXfs)7)M!%*Sp)vveKv&tlVYY80r(Dz-4(JbVwEnE#ou#XyOBj7#G2f*=t?pE-kn z$BrL23pNRF*s7g7cd|8i+<6CYC%mKNHpp0I1Li3kt#Il0+u#27Lk~S<$E>}NX5M(? zjmQG+m}6U-%Ay$QQiP=E7@#DL1wJ+fQKijL`psV`CPr1SoRv^6G;D{7N{4M~)9LS4 zsmkS{Tj9-D=p{c~9Sthf(13Uj6U12>w|89}diuZt6l1bw(^rY3Z4w0;99zGB13Mdd zXNVQxEBD`zVO84ZpjH|P7hpVJATF{wqp)z&6{1<%fXJp*E&Ea3!!Yj$3Yl zN-DqrRFrZ0NeMO=vuy*6%Me0CAtRuTt8vItUIYtm{uz=^=@*snSk}I1r(?NH9r^N4 zr3Rm3;GdJTzGTL)(&3Fk;p?ceWc&;3V>q~+VY zLCjd89YZHg1Bp4qe4168;83cDe#Rr{nR9S<#{xTDVRVR#sRdfG#a|Xhh>$YYs?%%cDBAO6GVKmYkHTeq;ON(iXp(!(GVaA(K#T- z#shFAzU1N2wVjL&)>T)nvB;M8TC!e#=_Risl;$%<2tgSpCnp^}d-lTKQxAI^Kjlx7LHctCwTKYw7%r7iV`lL! zdNPF3J(B^YV`ksBx>sC-1hf>7=#oNN<&{Ra@GbEuBsq9j{0)~iocvf6g;IjzpDD{G z<3a&+X#-V)Eh3o+t`-F6%bw>tW}p=c+j$+5+9l&r!)MXI|3Cjd1JC@IAy-jIi!t@D z|K_jVbpe5xnvz(#Q2o0^(W6dH(J)m)3L>N)>z~O4$Ao2qaiV z2RPF!me*+)Dx~_wJ^t!ts+~lJeeR?`Y;>*+Ae7WLc}2Ji$}G9Gg0qqMi#zG zB9W&+(h<&<3I+3UrCuj6(AKS6uiv;4{+1xN$4w#f{mpNF3wg=98R<%7c|qmbn;Q6b zK7LQhV`Gmq_wCzfwD_q4Un1jDI{wSsgn0R$yY8|e*d$m|zWJ?hddAGHLJgZGOzjvk zJ)yZ$!^YA@$(Um=`qeoVfD#m`G7ip#)H7c~YaW?i+_ObEw*j!16B8dIB#VX^f#yOa zVj(zI7d7(H8+ZSj4IA8&;Pa4R1A%W|g7%dKaM!L~9{f?H z+i$;Z`;Hy!uU;Ro^DkWNCi&4LM`7aGnZ?~Rdmhn)4?g%izwPYJSjq4Rb!mkV!oX}#K9 zlpuckN2gchFGeGgLX>ntXw90nnO2^OM2@qx2{{iE+n+JJIQIQ1{=mK z8NY-eL^ZvxnBPDY0-QYakbA+bEL#!s9XmuK9xWmSh-f+;y54D3szKFh7f$nwFCr9V zcygQ~#*^r>4usg07=8O24cHMA1~&=O{aQ|xq;^S|Md^SBMPD>4JCsTjLgRn(TGhG; z8jInhR=Pr!lYZVBJ!HT4P3}8z@PMUK+I83Go_qe;=blx?tTEDF=CPUCym_-{9ex&W z+qP{M0ft`#qR&&ZB9mjYlW-!W7VEudC(zQ4Qu(=k=LZy?N%NDKpZw$}Ysc&{!ov?g zeDJ_QC#|tV$j@0rSSZX}qCjO$ax95c*=SQj-RH{%>KbR|kcrVgLs7V51rn9+!A$>E zD^t`%NyG^cGWilmKo)>F`HX*?|OKckQ)T ztyu#r&B7ydzbL2Qfr_D~($~L&S3@gcPRhA<6K2dx6H*ed8Js*x zu#~K^#aw+Q@{z*QPlVMzgg2!m(is38rezhIsZb5jl5&)=Mp;+hHOTJ0hJ-FfJszkQ zV+up=xz+QBo(IkK5=Y6@KsxPMe6XlyQAi@ME=)U*=EU*i1kQ_ZYuAPvyzs&yptf({ zZk3=LmTAEO0Jb^Z6FY>VqUIYCkaIK@f}_M9>9%*h>t}xEXY3H1GCla<*X%tko=hNQ zkPAMB5G2efCl%RBw|G7Z;4%5MkmNaD0P2IWBpbjs5T4f1;;Qp$$*R2C4Skf<{uU13&AlHZzDJl^=SR*x{@7%God`{Mc7;_Z4i(?qZ} z9&&d2bv}}Z%?RZ*kd-a=nbG`1X+;pULejRnLqM5y)y$Osu~Cd(?W;UYPk-G|39Sf) z$Y4ela-j&JSBx^k7)&L`9Tw&s?O>PK7_ys8lKq&~O*0m1h(PsP#N=$quPV ziWvs3vxj2GjvtdHC`3nstRNFefk`nbt(`2&D$l_qYDA2h3`O*683nP9#IawjCjBm= zArMtLH6>LDMaG-Cz5TY^>_m|Z-JSV(I@4K@q^iaSiWS{|3^{z0y#~Z#y;v!sZj#<~ z<4rbj^qh5%fAvj(d<()=i@4jg#H$xxWsd*gBLGe-D4i=+g#HsB|M=Q<>pXt;P`OSH zXel^lbE-?uAUvNs0Ih7~y%6Dy2hD0z*I)b&F{-D+J!gxdpp8j?m3IL154!m)?kL$)JD4N+y5Pc>eF3a zbkAUts(`JLQ8v?ohT(Vw5{_9cZQc#_SeuG7y_yMhL*tW+PcG&exu48UcbQ}=&X4sn z7q!TQnuby$2D8PQ1)+06l&}t%LQz1hS&_KWq@X#FM=PH(^Yn@YyO*m0c6L1U#{zT8 zCoSF}NZ~p;bjr(*0A_$#w>p`E^L?}1|J43E$%Z5Vu{NEt_3t)4F@a(j20sf9U>bNYRO62 z${{||i~jQd`@i$u?|Od8x05f_kQq%~3rHe1tr(7Fxb^qE@1AYjwh=ojclgD_PJt{_ z<;DajZSo@2#x8bvGUPT`oBIS5rf<5l?cmd7gT^iKyt1LL2JH~2NA8Iwu7%v^tD7(s z;@mCo^TGs>?c2A1;*+1a^Nu@Qko@XbzxuuJf6rVk_?#jua{(kSx^y8|giIq-*5v)4 zYEl~jbi>%9-Y_l*Rnllxg_VVmAWhC$0hGx#3@w5hvE^m(V+UNxppsdLtE2Wqu!RPh z5lYZ96_FJZU2kG1tt(6xo#1sG;?1$ldC%aRR z>fYuFr=ALk5_AdbgeH^%L|IdWz>Z+{&y~y3sqWgS3mp0&4R}DyPzeN73?*uyn8D-q z`rQO`rUo=#m2y&t(;&pq9zW#v3mqGLi5Aof#hVs0MNt$7Ncq?}8?-JGMiF}s;3Ijw zZO51RC+qIuq2~{wDse2Z5*Vx&F@;Jf5exth4v~EeG-t;uXGD{#o97D6KuO?|(!)m! zLpgAuu}vF5%#<)0&A8Iqzkc#{eVz5$TKJ?UUH!@fUv&|~7dt#>VEAGd-7=h$3$~>Z zOaCW|dIeBZtkfl_+VdrvWT=F4i6Cg&kCv{IH1SE(unRDy2T6!H|B2w?7X=7kQOHOu z6R8-q3PuS@>f=b{HVF}!$aE8z;s~?IQ}A^_Ul5w<{{R(tfr!52>s#U8Xp-26!^Q zffa9^2{o{3RJ!IiQs6bw57wqKV5)L}Nxw%NSFBpaJ_x+%bRLGd_Z(MCuGnzRhNqu?%K6xbKm1|MTKAR+ z1+YD`Q+5jBw4ta9-7-jf_Uz#Zp=nS!11JqpOQ=Bt*@~N~kS#Z4TJn2A4=7RH3A?{0 zPyi*y#xi=eAB2XBj^+u&S6_LR!@-eL@6d!Zh7Er$>zC1%tRvXu}^8=+WL zZN!zR>FXB%GGL6q>@-VZZI!l)8lCNjfLX6tw!(q5?`Go=!m?I|s9xACQ33(7`L4WT zr9;a)xGa-T8Z2&}MOsfx#K~9e1o}YMLDRR>1i6skER4CuGeJ1e3_X+^LQ60OM`tdC z5ePVZ^{Wp&^2oO>8q~nEAY=+!3AK6$NjjmAglDO>qDJvJ-BdNkNee}^N}x|5OBZul zm932s9AJnb6?)SU_JLB=bTw|YO282XJ_8AoHG1F}f4M*o&X#zh;?c_f{rf)t@sDrc zzPrNlDApx19XCU-;MK&h zB{;GSiPU=#45V?G|0}Lw=E*rVFc(5m56Egq0k{Ub(gA59@hDJjFB`g0GUku5VpkkC zB^5ruP=qr^}fch*w7J>p6o55`Hoa`gqRRp5O8}^jea%PE^2t6G_^5>-hhSn57 zi2@FOm6Wlg25rECUk$WJT(hoKOZw0YFIc;5`w-Bszz01|FoZTw-$5}uPdrSs_z@^J z&|_1|v3s!((FY%1G|E|xGsipbyweK6f)aFa@DI6sx!yXUjm94RlId7Q8D@v~VVq*s zRt0kb+Rzi$tXV780N0Nnk56q=W7``p0@qHjPUQQAh#Ei^9HK?60`WM%CA6L3(p%Fa z5(-QoH8fQyE;*yU)WET+fozfg?)!gd<4A65FRr1!P>m29gIp1k^j%qJcY}J01A){m9 z>O0bfNe^K9Z>IpNt-lOsO#_OW=u8KQ(d6W$;k6N_yY||zq{{V``2X6w6DYs0t4{E> zmrA=OS&Jo0RVB%e<1OAGjvYs~V}~>$fgTc;Lxux0G&J3CW`I6(&YU?joav^|9ELN{ zVdgYUpnHa;2Lb^Sx(Sc~Nl5H0b~YzoB$djRWXoEteV3|gKKI@K|5tyNN>Z&-l}oRr z-~avI?=AP;_r3SsxBT8OXbNuB-IxVHf-0ZP)2+aU5>Zj(f=B71$|5>HtV(5i`A4&UO{ z0N6k$zd{D;vjRZR8y!H-k`+FqR%EU*|14Sv0#cg@;nXqi%Z!3sk}QIu|GIVS8k0Br zYXTq$mv!izl(-i&bVEay4`gRDR7+%ST^rLwVczm&1j#2U05xcrADJUV&UHTeji#*% z&2kYbj-oLaL5`CSG3e~XUqIZpecNlVzJ}6OCIwclQd2o#FE~pA%E@9`%T3OW*C?-q z@Q6kiQQ&X8=N_}{Pd@qN=FOYs2)@daN(5xEe9WPNvyE9&VV&WD4wiTr9BWa9()wr) zOR2mD9jnQP#I-=@U6 z4PFT<>O43ZYpx{70j9d381<0>b5``_gAYDvR|O&d+fVU z5CE+uj$q*QG+haam{AEX6W6PhMdXvl%+sG8K0j_&F*7IER2y?CMX=>e8X{w zayDj_Tt~Wk)oRm@8@f02_V%v7`DRKat)pBaCJZ_RIU|fHa@vVYkNj1RAVCLEineCW zO=Qf|6ac(L2{Z>P19NdzDX>PKYq|_{O;1J$*pr8Pof2D&98R)WRi>$xv_z=4I?n+q=-SvWsREp9JJCy@}dJM%D-&vCFhEU_#{AM z384aLC=@1<@{xb#vUDU>ijN=S%cv(t0@eU<47Io2cH5nI-dQEQ*fT5up3z1qq%r|F z{$bTnllsd}Ctz1P$SNr-j3$0gLe~roPlEsmM8tRnK&pz+%)hFCb<30fyhgj0R#6;A z;7d^DUpaOcFQ5&7sbQ#P>1hBHWFZC%7xH7ee6n?Ah3T1Lk`X19Y9eFPrcKo3p4VTu zQQY)^v%MVB&Ck}_v15nK`}+D!1W{(-Yq+W1bi}vbdTV!gH#0^N@|3v|f+h!d1VBH8 zArk+y?g~N`f~CFj#ueF{saGcVlq=OMm6TzQOuT-f!BQTIP-M)AoPqUww`$}LC8k@H zrCg537FX4`5##OO{r2TIE_Vi$gIi2z=^u)4N?KEb;>S(gCRTXd2rR{wYF0%nh(EM{ zS^rW5jMF|?g!yV3hEH5OBIq6U;#^w&S`Ny07H|8(iwc<10TTN zD&fVR3k1Mp>WVT$SvEPkEg7!6c^w0{Z^w@0nrO<-2>d+hGBC-?Ymp1yVf7z3Arg^1 zgr?@J-TA390)9#VWiFre~J&hZTqyKXbsB%Gkhn5^jjR`_P(JiajtfA}~CqgJHoRI{MB>@po4)xs9jBlK1L;_2V z8?e!O8L2~?rj=ZLaeNQK8bJ0EGEpc83Z5Fes7vbpHY9hwOT5{F3atJBN_md4& z695IGgi~C)n)at+Vi;wwU~O$Ap|8rJ`D;Bu#Bi$>q|JQtim+rw5>WZI0w>TFhb6Q= zvt`ScicyW3RQ{0yAUlZ-93@jBb-*Y#gDF+tD?WPUNQBp6s<6t>lebA$UWFC3H-=qK z0FWi0WL&nF$_wE#^NMaBq?Kz3KqUk9Xc1pW;75%(EhHoXr-C$)Q|@-`*r7L|tz(G1 zcCFSG6Vp8vnNvEyYw&?Cn7`n$M;{|I2C@*(zGP%-C+gguQ1SV3pYDl;_>NAxhvJku z?XLgq+umnV9i~$s++W~r>M0~OS>pg(MkN4k;>5usr3qYb=>B*UB-7x&aWgwbq z4eWWxJKllMHL5Q0Gg1IV(^JP-_>I~EFf}Q=4eQsFOeejnN#L2a;cs;j%A1IyFUJ<7rq>@Y2+6N>P1^cCdM zyVR-x`>V--4uM?`(9|=$N~LX54A83-&A<`-`|rEoxvGX1a6l=5QBe~yv!oyyB2~`B zW8uO$t;sF!=@uxF^&8d$x+XUaFdbyL30p|k{Nh*vKo!in1rBMUAE|Q4Y=iugVa-!U z!xgZ2Jgwa}{3OsveUwQ&;&6rxz@^f4esl-sDM;ra1SJ+z8I1-8{r&waZkwe>m9VjC z#4ip@;y1D_3>hYe>F*tefPJ_|0dSlBfNKaq;Xj^d+LCQt58U}QMz zhYouD@yDr~D($GAQ3OD|<;#}qwUw=URE%CpoxNbDXV|@acVeQ-4o%4g*MjKk5&kXeZa%J6}!w5pPE$yj&sYigRsQ0KEV)80;trP#^ zQvMPz0yMRd?FNTjAnO6rYJ{i<}0 zP8o{h8q=O-aPJzP0`VdU0sUh8gb5U4P(nq>n2kl02CM-|lh$rymL=yuwO*_Ocx3Eq zrjEa&X;jbX0stB80je=%xqMg-4|{rg)cLvRzGY@wdB{qTxuz4SjKp-IOmO)v5Uj_F zp!ADd0Xz~mU_I+Bq$0ZVR~~UjxQv{E{5(~195Jg6d7SbyJeh?@VYGB~U_DEh80^}$ zo0ihvtoB@B))JYmsZ1kUoj~H)MAPyWD|&l-@4o9Uht}`gzYj8UreB44GzEeOeBfm8 zys2dss4?xR{?P?MRH_cq0FmV(NWLs8O2_PnN6JWo@|w{KeRM~dL4mUdauaqDDzLU{&CuB6zq`esn$@0bqe}GGQ0( zd_@-at<(wOSkg(hJ@#GG?GABotrU$=%F@ggQyn{|mb2TZ@PY(KhYS~ zM>?@0V(pr#M#ib%H$JUk=OM(S5L6RgS_?5IMt$6b3NR6iY#h-0sp$x$W=jc7SJZzH zFMo3Bnhxva8Ow1g24y+8UgfB!rp#u|Ycy4HT9e~-)s(6A)1xT`KKkgR-QC@dX-D;6 zQUJuJ?{&xCY+^GY-G_k<56FPF>+9=Nc9kQ9CCCH@{A*GgVYObW5M9N|NQvqyZltZd zDrX*Hc{?iT1=g8dQx#P-sV?>>4FP`+Ht9D?gPtzkf-t=S8v$Yeey^ABnH*~ zlvVPNaR7hfUS-1*$H4g=9so#~x#bl?G!TYIdlknM%~&=*gsusXMSwVK-wOZ+b&bhK z^CzR5lX&7ue@a9q%tz_3u94M+F7hl+F!0y^ zg;1qC?nVCNM-|xB6~}aAqgeI=(z`dUy~(ZZ9=Jg}pg*`#Of-Hj2`ks=wX*tvgp2@t zQT(w5x>)~F&q7|Nl2c})2<%FvxmHLkEoU)1wJ2dQ{q1GQHYk1Ki6=bGl> z0Fn*vMp#M>cAPzDjnhd_ECjkxoJs)rD=I}cDNJF6AQb0OM&PM?%h)#sr4}pXK{OTS zf|YgPra!MDT*hVE#;eGAn;uuCD9{!0>9l3+p^fQ};98dMk?Pas%ydm<0MOsxj~gO5 zsR(3=1YVO0IA*f58`(b&vD&qBmww>bkz-BmE&aVG4lg*vU>Il~>OQs1p^coY}akuwh=ryz;fUW#ybYjyOhQFf%=3Ui1{jg>>Bb4o?gqiCi*>X>y(NzpQG05u1}MM(p3>e$^j2C zik}N=W1wBKEk^)VrR!c|+AFT{(t=DPZmD4AAQP@?vN}@Y7Z2kaUAWFXx&jTsZ~u?E z=1h3`>lFk*VmV@#?-U`67%7`!V3Y(YK52DZ==K{PC((bLnrZQEAE43jND_R@R0PTG5}6 zwE)D%jT^Nzbx74=5ZIuDUR8XkhkCapZ4zCo;X)E(rYQ52T}c#Ble|yv5hAt<_tMvKS!4oHsHElnB=ERxhE0)`b?4|X`9(&Ag zEoP6*ZK=?&T3uTflg>ApXQ2wg)`2-nr+v8a_$v;S;x5&8QJF&A2gGVFZ^ZJr{1YC+ zM2s9wq= zywyks&;$4N`5&X;T#(LQ1r?F5!6{luv{1B3%?bh}H~Q`J7b0K)K(oC6{qIk0)sVsH z-YXkPN>w(S2gR_i8#^_4YTLGLz^E<$4*6^SBCcgnn> zfHkm9P|>udl?>EUIcZmsX>OAQYR&cA6}NTkCqMa#EnBvH-}}Dr{s-*_5uyclt7ixeXJ?l(GUWBFm(L*v9uhJ z8q_f>TViLw4Gy7fT4VQ445LE6A|$Gid>g)Iz;N~J^}e9`9+!B*;Y`W{W|+XKuLTYv z$uThrf4lOBga80Pr%6OXR50Y`m@Ec6k5s{H5cafk9w7{Wu+hK<`fIBc2Ei8&(4hrC zF*ZI2Kz8eg63B3bOFgA%E+6rb16qm*n5PjOkD8mE2SWx%B)`<{m3sihX=8=hvc+$? z2PvwjL(q7daP8W)mQkIKmdL1C{Y6BB*l;l+sP{YK_@R9Gl;tUZ5s7C0<20 z){~#%f1wR0;c|e{kYTN9OL-bTcv{R#y+QN{b<0Sg`Y1TsY;cgwYj1NPz)Dg~>i_j$ z|FthFct4Z+|J2E6o(YD=A71Do+{80cZ#h^=}w$ z>w(cHh?H8#iDRKt%8S49<3~V*dHc9{%5~V-bDOS)ti)HDL9IWD-b1I7CVK5!8+m1T zL=;CU1zN+%LxAS?^(XUL*1Yxqcqt7_Mk^XO>3UEOh*)Rllz7OeZoZYFL-qj17N=;K z7zN-!Mi7&$^DKT7{g|K5EsrbN3m1(QO-A0_J@n8+df>+7m+@b@2gt-&00TWeJvd?- zTiJ#zFTD6dd?5PHJK4t!18>$56;OyC0Jcj?O{=lus>RBMr>GKW1|?ewj1Eg25>e(N zLoDdC>=VR|r{i{kjLW!7JgriZQ;mwp1^ewCXH0Ce`wWExs4-qUeM$pUb*^HjB+v!S z1HHjFzy8gYpZi$zk8Wsg59_~jYI1r_>s^n%&9L{5#ejKzTA#XZ0bpOhDwKUO%Ai zHT>qyn?b>_SQmj%mRd;?v~v6!Ci?H}r-HT7tPv-#9+$u*+0$TE^^-T_x)!B4t`s6C za1@@Y(MPisE3?cn+gTLvbcIlGX(p*FnIH*3tfq8<<3^UQxZ(JB%|E}ca~LwEV(NN& zD2`!#VA*Y~3aPM*)-aVGb0Ftpn-InC^FIAM`teiFp`k+u+wX~G$)#XbVO!9Phm}jg zaGPwr{8cQPG~)VKLA$g<@+}&tJTARDsmL?WYJEhI$5J00R0Z>aVzLP6K2ibPK4YbH zG9Vg+(2wfk1Pz*=%K^Eq*U2}m6U`vNHFdvCl6cuB?h{1;rs4!@0)XTS+@hc6XIL6p zDfjYqubz&z2gn5YqSZHU+Nj^%f8c=EYKgdoVVk;43jO3y{-k$7{`g~mY#swmV!Bos z#$vU?QdtoQ*V$l4b%8$mg)?VBfkx#;dt>&&;S_oxQ`v0)RP{m(DY)uJVD9+U=T^cGRDvX}GbIXY=6CO3?ahoBEju3z4$8QdY!VGF| zW<9b=#@p8&`d5yWfLrp+Wyg9*`KWB~DIKYs9oKWKi#u-K4}MQG1pv`4aUC=UCM z69;4xjmMG><1*N3P{CgsZ@8^u7g?atcQwK@PFDl*I8)W5rvQ|djCxQox^!jO9w6}D zwbC6FJxFtfwhrB9mv779$=l}*cA5DfMFQk=u5IvhN51u$Kj@p^HqY!rnLvzJ5ssZW zE(iHwCe;{)6!k^`qQv@b5rgPddD0(*72;e(bHz777)KNWEd@hED= z%Wq7P+>=?$K&fo2DWZ_H%p6f+7uN#dt!box;Q>A{PEKE@Dxx|o{bRYWkn+M5>~1vJca=H)80Mb^= z-|?A1RhIa!*-)%g6Q~BHU#$ch!)@HSkpNg06@lH1GLJAd1V9T!4-j)isoK>b`~L zgu;qp7kIFsW!b#J{ilw+xR>#8?8LFaachf(2GB92Hg*)E?g5H2(M~EF9q|a6D^Abj zQFu+Kn6h%8Rt8xTR<<99lxALj5^He?r}}wIOO)c`Q6MocW#CJEO5I_tW*ipXh5Ts5 zbOn(mLJyOL)9s*x9M0JAC8_N|B2Cptmmh+2Zx=<5v;V zR#%yD^1>pHCUV7n&OyQ-c>f2cw+BedP^r}J?rxOX$AVwN?QP`gwW;ggdrgP`?(hEY z(@#IGm(U4XB+W4ormG3YM?cLU?_Q92*b=OsCi$|230@fgxkp(aSHnD!i>}(Gwdsx2zdDT>AnTmF*8vHB5@lBuu2$Ot-1M3)7g-n^1W-T84uk5z+HFWg+i<*S@dHFjhpgX zDevF>&EFh8bV!MWuLy5hb|?{CR5NLlvsxszS&OVlK)-||KsYIoKaNo!B9GmT+XSkz zMkNZ$#^A2LzU@>DKJ@kVX(R(;V(<&U@C$$RM}PF`Pk)*W;ENVL*LseEiY6>93roA0 znI+Gtdfncu3YoY^V>Tw1cVNEKT$dJrlB$X~oChxFP5oDEMnLJloG2b=Vd%`>=pH(? zD&|tI;0Bw{tY|&f-LkfSN!NlHLYPRQ6rHYO$AMWO+|YRv2D1*{nWQ*n2q+{0hh4dZ zj__=Y#7BZ&VBY36Z~4%pezgi zd7?#QD6jbVLY*_3h@5C+cM}%?WuVX1nFVI+wr}6ANszm%?2z#Sk*!fb@e@DM-QE4C zfBL7+YNH0sl-V-HD2$$WC=AE=w1%&0*JsMJ=3}RRw3rlckfn&mr%9^d#=vp;tCYd= zVK*#ELQgFi*w=lYmb{$$uhOY@*7IEp=O@-pCtw0jT1jADT413*v^@~S zTr*K15Q`VPg~+sgQXcZQm@>6ZI9X-agibg!L(`a2@iQIa7#+hCMu^&q@4nWrWe2+^UkD{qXW z5NLh3-}&~p|L_n0(>K2P4Hdu+CLkG5v5;F8Gqhqw$SRO^Wz|_Pv+*~;Sx{f#n0FOm zizm5>ut|tOtwb#`TsDgHRw+Y6J2#(x^|tdl&>xO-8J`z$pFZ6B>3z&_<0uS)8~6q$ zFX9+vV+sR-7;xT0I(afaCN?x^{a956Ib+t6^%e#sTgS)d6wWh!jz=^=|c*H}~*=#FwMW9o4YHF^<$4uu@ zg=LfB0@EkI{oDWd)|XyP%89Y~2(GY>0K_B#9#ywW#~*YdCW`Ouw1WPNzxa#Tav6SK z{Ag+fK)#66mG$-QF#JU^r`mct(^9x3&}SO{{_p>uIO)}?G3~9@gFzUhj%CKBDAx+P z7gIZWNG(D6ks%?d5NfcIh_3;Ew7erhpf{R|8@-Ii(9xn$`NwY<*mnQfvx~;2{xd@s z_pyP+e|4mF*BPgT!z~0rs)w3fvl0d!sWyf@Rqzx8nGI$bd2|#oov_pJV#Nn#iU&~C zMa_UoyKpj3VUS7Oml3b4;!3@1;{ip>utGe4b(?~R5<6bGgk8S(6aL}~rEw4{0!$AC z4NXI+Wtw}>HY#1z9PuCn3!DeMV1e%_f8-DVwNEO3zU2v@foLjSQCrLqojx*^rt$%BpLbO0y^tAjOCZ_KDWQu zO6V|QWAU+<7H&Vk^7ChgPUUyX@F@TaufU!3Wiv;&P^=A@0|;>dg$$5{ln3+C(xM1Z zfR)|>jv^J|F+(DiM@f}x2i!`xAS(%lODn{qfN$L?y-YC1MuXy@m-Y?k5vM}}cDJ4? zLMD{lynw{EI*Ti?4q5 ztGaThPljMPqOhFMCOHJ^4aQD=#Dw<-b3Z2bMzeA>Ys3xdD4x;cjJ@2@V(C(52Aez9 zo;!2jsR71wUF61^n=#x#Q_GiHj&0Xl#lk^YDyg*VdxN1Nk&1pe05#xF%Ud@K3z!x> z%&%4+J?aybM#>hLOBbP=4I3KtK`r%Vh(mEg0>ZHcHU{x!QzZn)=8H1HTaH=_Osh>m zwids15h#hY#Z@_5AB+RBmE&;u;xYotLE_a{U-h!6Ol7Ay$ko5W0P-lukAC!{Hdc6~ z`l=H}mcfswoZ`~)t1?nuiB{A|*EfIu&;8ubSyYi~TyE+GK=F8OqQYCXPULipxsE>D zv}yC|HLDGtXsl2D)n9$)@BVI&!}Wdd@rD~zEGMv48~3prcS2RvVU|@Y@C`4|1t_m$ z&TpCRsoxicUI1SkQ8mCb;*@+c?{Qp%zTz^A^Mg&x=AZqkC;D$)ZF|Ut6pt-eQ^7m- z&;RYe?tcAfXA4RjbTdp-^VmWLid=*>!;_j`_E-&JFu;~DVHEGUg?%2M&@<0q6+m*G z2}$61GI;_k1u2EWD=iebo}!%R9#($ARa{Am+sL%&_|U!eZ)qypgTP{F|5*6vw5BWC zDzYg%y_3mY7QY?THjrvQG_a8%*g#P!BLBso|M^FM=XdmGRW~IQMP#1C=r5E2l1HTn z2uZX1?Pq@GXR_R}alIC6QnNspDoJH9aPy{3jus{N+Q1N*?ziK`ISVBI`+wjEHs5l~ zCqMbg7oLAX$LEs`%a$)w9j_g0U%=8c2hI{sbv$1H`l{^u)1v#ybL_0B9$w_BJ=6Vr z6atWO<9eA?7i2?5t9$8f;C=X({SWsXj4APnx|QqC^xe7O|9jdY#-`TG(NrPJanu}e zm?ey!v^XU~KODQJCuf1|Py{K^$t1vGRDjii(Rnd72|ySqA5Knkh|aPcn=ZuofZ#>8 zAz&eIVx^q$gK1_9Yb>7VD6$iXx99M@cIb4lvre8=wgZ{Sa%Br-cSHz*l|-4%ijt%} z-E?680k(kfC!ToH>gmV+7c z&Lk56+qP}jJUH^ou2+s8J+g7*Mu+y=Lu!1ic+e8B61ewWfO!(*JUQcXv0VKop791*ygZo;u0r<^KME{K6Lk;$;It z(jSm7u8QsR^yE&7Gh656CRN$oBg zr#MCVjW4dyju9maAES$be)GVG-?`_$o}+BHhSJCFg@i?2=es)2fBog9XU|tgR@Ot# z@r8DP(EP6SJP;R41U$p|aE*{+qx19RRUlFl-((}Yj51D$ou~N{`nlox7CCbTaLkzE zj~TkCSISjhGZWVs*XAgte3P#jt$|w7MQ*71_&iQ|k5pTuB*_p#KBmh(LZp@@b($MJ zQW@AViD^tCAqmX8N|LqUNe=LY2~;YovP*AR_TKlt7azvWcb)Ao|MD+iJ{ijNUr4!p zw97oj8nEV^Bft!WG7AeN0bj6y3E0!y!((h<8ws}R2hY<~K~hh*)F2RfW|{&+I+iv3=jd z`czE_WknLPP#XU9&67?zPd<%nQRnihl4p<@IZ}pH zxTL>h{yIM56#f{3mk&T0$ntT9zgbE}NzP|zM>D({J&d^Xaj*#CF(j}sY*kofHOjUY zF6U_dmX_~4AKNP{xM!6E^~hAw@)l&)6xjv<_uqfNIAi5?L%iuSff|Yk=+dmFw3$7- zcklKMUE0G7uMp34voxm8-itMafBLDX`gZJaEL*vdk^bEl8@WT%~eerXj`&?AKxFizAVnzTQf>j<$KJSMI}3s=i0$?QNadrj+mRb%X)qhSMghl8X6M$q5?x?dfTTK3_o_FW6 zTQpp@-;3rkoYS9f-@fhj*Y`S&{lFUs$Oh}sJ7{{)QvC^3bAm}wnLiFP1~sS?0) z;TJQ&9d<Yo*K+`G=j^VCyMQHZsT67m=vvd+yy^J)7>AAR&=ANv@+Sudt0__;KkGD>RJty{;4 zsN#&*b2Y{QBWOx6vHuMGaYR>$L3#tDWHx4Q7BZi$AJ!PSPfrjMVnaM)J}@>0hI*?& zS#;otLgMj7LBj#HXBS3EJPk)G*vi|47e9}A+T{xd{>As~-L&fDB*(suWu>1QpVu+4 za_PV`+vguXuEQ(SmQYYeM0rJLda#1WZMs@+QSqE*$2<%SQSR=M{<;)?-wzSXX%xqru-`Y3*@wApAq9Xp#;X`WH)6=WO zY)6H%kj>y)F4(zi=bqR0pa7| za;2p{^o~RCy_@=PcrbNdz0Rt|X9t_xx9?wRD#h?MXA3IDtf<8O#Q+V!#w;YGF#d4Q zP+6dup~(YeepxlV(NDmXOYxKy_=p#Il;7GVp|8Zs5j^9bGW8nMuktZhRP=f z@lzdaO5b}|PE_&BB8J?8mxDuAf2{)fL~-&{aSSI{=gt_e+bJYZeL_el)Vh{%6=~IUe#vEBscegnKL1i$|>1`=IwJB1Q znqI?=WK+n&s6d>M0A!nw;HJh%Ot>{RY$tK05i~pp1himaxfn5NlO(n1ib>sam{LHVgZlt|P+ag|(iHGlwN6jO zElDKj&nohB@S0+v?}1CWcUwHQYu=IsSQlQB#3PPCgwPbdCW4qW7;z;OhYug>>FF_{ zB>M<-=E^{PmW6r4RNl62o0^h;2ATnR{1SjN+lw1pwrueYOpLR`*mK0uZ~*A<|cULIN(r;$;P(bRMN~3pUbT zs+u23jjYsELOg}|W%CFA#e4SNdee!?EPFL(KB}J~)YUQ2Ie74EI~MLg>gZ|br5`Xc zV2lVlkYSdL0jZy~r;t>bpfo8NcB64>8UD%^%~Wi31Vy8VonM?=zZi%K(F0KTMHW=B z7Nh=?31uUIpNl+;?T;qapN_^d%r zpMxnIzQ*sY+{%Rt zcXvg=#5^-d0I~wXqw;MI)$_$6`$@sQ|K*o`h{4fAKk!d~;NJW0{V#v@U%vM2*Dyi| zgV4~Kkc1*!yhvl4X^6IklyWLbcnVXCEY!GEo`)m^vqeY{)Rmd19@V0jAEy7x#REU~ z$o}OEy~~z~8lO*#-ypWR`RJnkzjbcj#(@Plmyu^MN5xr;(fmE#On?ATIk(ws_4oiu zx(I#;5<0#oB(7UuupBhQtgyA-<^K#_1E+U`gB^~ios9+v!AxH)+=Uqn8H0VSlqGL zsr=g4(5Q%kx9=TNiHC8dE^QO`ZAt>Hr!Q_J$}rMv(hwx1!pn#BSZCYd4?XzCJsXad zqZ}u|=LsemYHFX?^v#8P|HILn|J4w04H-46Au00(3f3SKg2BLFVNRbw9`p}9xCdko zZMPEdo2OnWyCJ>`%OHw%+%Y-@YnsN$K}sF*`6zEgyO%Gj6u8F#z$b2?JuD!)KZXsV z{o`pFHDEaF?pxpd*6Nk3{Jn+nW#%Jl5?nQ0;{+q7xZJKy7- zR$SKJUg3yk7u`i$eEw0|;A8ZtX&Hq-g&|=M@PYY)^;!cL3GmdvfuZKx*Pi_6PrkNt zksb5dN7Q0ntsjDVw(az`v#oaw&R#$1c+mHyk+&p zsi2>pEU3zaCn4VG;TJ9Ac*?;vpvPuWO94!O;t|R|h5^GyPaXQzZ#*puHUf1xcP+_UeHkx1G7U~+W0|2EYsx4>uTC#81Z}|1Eo8N&_H=$KZat>D;J&r zm+yJ?t@iv}rGf9Ljo8xGbh!1z%cr}(Yp8?Odx0h%HNmKXs%Y-mxy^b8I%4;$ukPEo zmw`bxf&&dM*iiIEBHIpMiPB-b6nSTg5+^7smc9%09Gzm^~#@teiEtOz3I= z$LQUBOBixh6@=@gOpQGE+_#Z!^r8pwNs-Ac09TpM8UaX(v7Q(#^GV(tmYL#!Z$c2W z<7cBcc-Olg>F(~fvZyy88{S9LZ%7#D@A-T$*HQcMx`qIwY~&g*irG_iAxVhgr_tv8 z_Ms0yvggsa9Guv@jun@uz|-8?-t=nIsbgmses@!wb)N~!K=giP@sUwPk9av7S8Q+= z$Pt0`xT3ic@#)XNyaI3rrYAsL{xSE_a_EQ-ml$l> zvc*`?G{}oDy=Y`ia}Wb#CL&>(S*K|w!2(e5-5|06Dcd_T&-OQ9E^9h}i@tUa~z{e_;(6LA5_BW`eyS!g0<$BrFvZkW)Rm#1(kNurZSh-=D5<5PeR-oyf2 zS^bqNVn*bQM$LqE?-CLbTOlw!JVo)likW`#dGzYq(G;if%E?4av20muNTTL+T-DRb zcxI~rWSy~$)0LYE#U1Ze?%lKZ_{rn1?0kj7SiNf1&;Q)dzxLXzPN1-oVuTQ4aN`QC zLr%P8pe>{|s>(rHHU0~^!TL7}=g@G~G={!bb1K`e` zGdp+h+JE2x`{##$=!bnb{L5ed@@GH$Su@Scm#^@hcc;_Ex{$&2v18g5XDJmu_385i zy^9Y0*s2$o?K*Pel^haJhQm{y)&OP4yjTDAz{CIhz_Pn1BLg^QOlc?u@MX?D85vhZ zL!>LtaFxfS3{MX~2iYY_b1M4)cTx9ctv76+2uR46E6{TpAR!kKioL|2MJrQL{OOFr z#6d8DydcUc+vjg8_s9-R?U*dOCH_wsEM=ZpMU;y zpa0y8FT4ourrYeorCLm?niP+jpw^BXyUu-Z^|p;&MierR_LyPLafeN9uYG6Um;cSF zcmCSog7v;ZIxz}i&H%uXQa~KKy@l=$d>2MWKsqetQA9rC5g{!Zu_2evF0*E_MI;n{ zy7C+2tfKK71pt&IUc)t0Wa;wH;zm@;Qz6vCnX@MkzHiy{4=mnSc2QJW&M*({ z*xb~y^Qoa{fA!Sc|MgJEQrip0OCls0IN^i$+i?u$0F0;Rps*|;HliO)l_Ek7z93vS zIh*o?O>dDg|H}zRHMEDGuYVLZG<g$$cjxY0HJdDJr$2q?)=v!0UwP`@f7R5|nY$#$UM6IJ;&gMoiIhWoPuzVa6jwfDETmd}+<0dJ={3s%YCP{(tBFt}*lnOlE6wB(o# zHhOf&jvdcf1aR!!xpQ8#N?M^XJ*7hk-bJQ`htSCtpu#`+dch?QcgR370ae5__Jzlz z@_&TxCkZm-GNIz~)|H?72XWgR+|`Ew5kAl~;1eX?3%6a=IzUBdQahRJn_K`Y)+B$u zN@icAcMPlv#GL7|qmFB}{IhuJ^8e=1^{c;jp!MXL8EyPj^+}J<3!FVU@2kH)*tP21 zhVN|}9MfRH>r@s=98t`40XSIaK&n_fp^V*PR~kPROnK6YzysS>d&5s%aIt43_7+B* zGVfW?UmN?1EG7s2uA*m1R>E!NF~(iDcCFbS(K0a2E*ZBDv>LMO92jr;XiBbgMF1)m zV>kVm9fIXyQ$TwU9BkeD^`)r4N3X#R@xH@^MylR_}f z?b^A^Mq2M1d9MmL%9?kS4Z3BxYuT^7IBcG=ZuWQzk%4=bO!3srneYdoZLp1b&;e&) z#pa)47y)^70wEG5cA`ITT{BNt$vOFxM!IR!=FL12)X<@o-Yz~T-rL(7Po~bzEdfZr zRU9-z=$toydDpqF?SImByg$CzG#gz@Q`^B;y1w))Cm#8=!6mm|ECVz;+^e;jE_?3H z1$&bQ$S85ws4+yvv?Kj(`)zaxB{0-^%xq6>csHK*hIs zE{D zu^P%Vgy|35_uhN2Q#8xSqX?m%r+kFc=5C*evfSxt9HmytC-0fd0zm1V>-f$`J6`(4 zRC09kZ8EVOTADg{eClZH>eqUHl8r~3Tk1B}H`DKJN!!T0EC#W#WwitBOob7Enz9cF zgB7J43O8gRRDOKJDqvmgoPc|H$~ZV67#2!FeTab#ZvMn1HW{b7Rv{&)h@ACPL@6bL z>3&Wr6o6QV@E1~(uy*Bx{e-(~hKtXi6c)h8rILa47>_~!x^?TeY}sOZ zx)fMja0t4Jv$`Me{ViTB+u{07g>SA3fcK^LeR(;I#uOm<`GTm?H%HGvX zG9tnxZ|9@{w6wLH*fsB&Uu`|K`%Q!X!~txs=sfeI>-t|m-TCT?`R%Ro)^jL+p9urR zSgR@uxZpP*q)r9R0?LTa!xiv{K@&Hi7q-5fLhH*-hY`W$4fk5dP+uJbebMp?z9Rry z$p8=CZ#N#(-`?h1ZoP#}j)*L|d`_P|?KMtzof1bxnEdVB698uJ`IDXB_(=P%FTKgo zpJ<>$Z(Dfihi~5bTYZ~O545*2RW3kbsL}Y(6;FY@+W>iaRL~xn4;d)%kKQ5#py={| zX-S^(0F;fDmJokUSc=FbJtbd zToZtRL&uIk>wM|YC&RTFo|c|`=MAs-tzPuW-QFRF|4_rBx*b9ullx)c3#9Xh?BjGc zN^A&?d7uD#0e3{sSct6%9qvGBRWH?-MF5MS>m7jeu63*7P-xsXW=jbjcqdV`|va~-@4ans02Oos@B z2t^KJGJnWWnm!Hln{Zqa2GA)Ch6JWT-O~4tHCVN3wJ9GPc5l7)Eos;oYFDW$PYj)h znr>RN#`#B4ubN>dT7_182t$qK)$i#)Q8r-vioG$!Bg!{ z+hw*>>Y?hx#U##PTfA&uU>zSPF4INvp$cQ=V(k<(gL?p(*%R20*-rPN>cc>aC*@86 zr|6P-KtD{Q;%x1NdviGK94{>~eq7o8UQ9LQwV@~pmL!wAN^3+ei4j7EXPoJ{&K&`W zVOz)6KkRtr^D%;(1CH9id+~wyuG#Zvuk||Gs2cnhWlj#_w8x>AGGGlf2V$d>Fn!t@ z3j#1aY(5o}NWw6JAU^ON=+AB>F&g1z9U!+nSYtM~!+_beansG~Z}y@RN#Np3l^tH= zwn}_~;;-aK$6_D5w7DT-x*KZF2!M}6?ETWbZ~s4Uo>~7ZL4!tx`tM!2v;WMBZyZ_G zW)dhSLBk@CE+CvamYA@2gbCQeuLm({YNQZvNW(_N3(Y(fJ1N8ntP>KK`kI%$5){2# z=`7Q->}mI&9^XCe-O#<>%Xi=|f|JWgn4Dx1+bhbHs8lkn6Vp@#}! zx0@RRpjpoz?tJc}tw;C0>5ZR8#4y^Iwx9hc%lmeoU3UDeBVhtn30CXD&Ls3cK){zA zl*612J+Z2_g>Ggu739Z!XaF~Qh}tAih=qJuDfUtsp{9l7dxxg#*REZgc8(+_aJ+!w zU=F8MM`9LFndZ-7agb8bpB|@}$%_2saztY-rqw@J1fc!p|J?b?-AQW6Wvw&jA6T z(N65_eBu9XK7V#jssF?c&{_F?Vp;#v4IgY-ew#1avvw@3da5%hvy=gG@J@`fyaADp zcluf&-=67m1yhcnYeXb`L%Da1NYTkSwDP1|VlX^LF0U2@vP+jj@OXm;=W)!RN06L% z>xkZ~5B;pg91wt}p|b2FbR6G*&-xurcYYAssC;OHDDm~RB`J3CZ6yVB& z65(1wKdX6V0Z2|&OqpdKz4^}Fr~kuqXLfCBvIA_+I@|ZZ`JMgk|9G~0LuX4X?BYaS z*~iWtk^q{C_(ZS>^farz41P`fhcILWwtRTXnAnKfupz}ppj2r=5g50j_9OFlUwsru zKVu<`hZ8g7^NiPx=!M3cyfOZjF@e@fnj2Hj4nH1${)dxo0lHJ!_;kJ&-^R8#3> z(Ssddc>2(i#Ydfi(iEQx)~wEoWSGP*xtROUrLJ1Zo{9#*X}V-6sEd}MkLO%K+@kP+ zP%a`2g<0R+5mI5y;3H19-}%^lBj_g#{V1a@W9gsdbxZ<)bOJrelCD8=gR0l|41VD$ zO{9^dt>&_89$NJLwgpc$(VsMg)(^N2gEsVgXnwyjicK)Mj_}ly z*RejFJ|9fQHAHXf-iyDo0HB%4Wwd;F3xT8AxeS9DHMP)sYX4(wI=5bU$=iMw(10YN%pH>i*LTpS_h3&Nc)%sU}`&^DWv|ASpogQLrpKfWY5Ya zt>4iJ z%nmDv0BGHvyGE_>^}yi3tFH~6JT>cj03e*&0RT>nRRBb_fhl-58{E+0qeF)d&jw1b z4UI!5PBgu7aCQj*I0QfsFty{4>1upEz<~ozr_ao;2A(c%T%0fdSk}ZM zp4sC-e+s{hRMmSN0$>bKzFcrE7eOkzFXyoYW)0W^aBUCE<>Jo8XJI0qYdU;%76}0L z&-yn7H0A9z4gpX$0niD|f|0b}!GKSs)6R`E85!C2KT^2jdd}1?dE)a4XUzD8F=4 zir?n#X*)~o(gn|+CpPoMZiv~@MNR-TknJqt z52Vq#)3**gZUMkrvr#j8XM|C@_mXDIvfUW4I`vWTCwBPG`1y?T^4IjKe~g`o0O*o@ z(8hJ@8)m9^gG4RraH&SPX zhMO3MCzmR|jn>rBKEpcz=r{2{-IFd86aWoiBv6jVI5Pufpt-H(bjSQbhx=W*AJC7@ z0!v_Wh`%89paap{+Pk_I&14CHexpqXZ%sG4CN2P`M4e#z`s=TIz4`(MU+Wu3KOLXH z{KbxkPTTJ|5fkrPS74gUcZ{bq4R2YvVWksGrn#&MXAk$zoVyA;4<;@E>Skq*iT1`D zZ%nwJ)01$hX~j(^I#-=Oe){@k(8U~fib-cjXWL>Yk6-+7MubrR> z5prOJ&v8z7S2Ye^IG1MG{IL{OOp1_=kRqpJ_c+HN9d!Qy>7!U~)L*f@ zs}KPd&27!|R?fc)WzEte=B=3D(r!=J%pxCttS>SBuckFIH3EQQr^3$|85nY2S=Z_* zesySOfn{>kU`|WBrk_C)0R86HeO%)z4161zH#GtvM>3%2G98a$iHCS;t+rtgMXM|`Ou-$y5==%15cR%R5D;ZaP4KlOU$bmb}Z>M<1uQr=Wd|+ z_4#e{*DQ3T^9*4Ev{>-ECg{h3sT6=@sKtvM#9Tu$fO}(rt{WFz7l08ccTDiS8y9q~ znkk$C5(7x+zg9;sOuYc$I74XFs#R7Au9XZZ*Eny%+FA1w^9Yzu-z|-RE%R=iPZF-J z(?&oK`u4hA%~9`&T2G4rD8qn;3|zBev5BD8mc^TwwRW`597}fu>#xDB(NSB=qMjRC zyKIBFmc}9UGx`%D*MJWVMNXFhD8g`_d^3lPY_3UHu=KVY+ZSB3UZ{b|GsWwOX9Iv6 z-n!x%q8}3Idu9*xPt!|Y<6`$T3P94q9P-I;TSwhqRWUHMTC;Fr_tNXbfMGy{{j+$% z`X$$-SIpKo-i3#0-Qqu7-!b^6T>#LaE3mpj;`3OGt0M+*-@d40*&R1tL;G?J{J*+U zhMJb$y)q`puCD!~IF41!A~4fG6BK{N_p2cQSy(P}U;$5Zq#|OPJZAcq-Mz|ac*w_c z=e(m4I&PS^?5>rD0avXD$cBdaMh!Tlnqsn>;l^7TodD-s%Dy4)np>%-NlE+2;o^-q zEWLHv>-$g4mKVUs%g=Gd9~@fTd&81jmiu7jI8se0D%?w_S&ei}gmBZ;#1o?Kf<#wa z0J3m=;e2k^cV~TDnWtje1=LOQ#-vAQ>#B!u-v8A-gXah5^lS35I^MeK!L@B>1*S~^ zszx*eo|r#!%A@x364s>#~F2 z+UGT~i|ao}LUe{pH!WX&-|3gh=UOdnjkki=QUH=xK&K~CRd}_4 zFmPVB#Je>dh1_ zR)5zl>V4uZ3l=V#{G>3T!+k>E3J=e$=GDv+fJRmpCbv{d#d<>H)A9LlSa!z`-TTFV z^XcaJc=Mcd?4LV+_}-h}x?#@Md<+$yxdJc-;>Qu?tv~p-9iQ2@_lrB)Y>t>q?)=$- z)%UNz{YUPbOO+l+UFV7bFn;d-@3;K>T_;)x1{km7;LvQ2I_Mp%*5>#9;-jtYb4%>U z-e_}00B~gOTUQ&zPp(-SZ$Dml zqjO+x2>>4KIeGrkPwsl@;Mu0u)S#pDW!8rd3~gN6@!1dDyl%l9>f|TTYI9CEteM~T zA0J)4Y+mb|`2zw&O-nmlerL<->*&9tNX$6_z>x=6b^ZHyt%{nbk$*)d&8`Su1{xgt z&3CTaa?{l~S#5Ul=3;8+q5$B+hc+(y?JcX@89`TfWAMcdFvTH54g_xcKi{?L$8MRH zFKSIOwKE{QIX4Dqrf!Uir^%o377k%{=r@0Y!6N0I=|{cAog@ z&%VC<`1x5Z0|P?~J6eAK-D|$TdlDN)rT|W}FN^B}5N-DI@pC`;H+!DlbFyhRPMMz@ zyxXw;kyQ__nm79Zc&#bEE&#O_*mr8+fBx!$-+AH4fWzcw%a8!6LrwqnTbBH9Z(m`0 z_gZ6VR+fKV04`{@kMB74KRms^@6g#;DxS#;WyZVb2fLSc{IB1&;=|V`d@g9i@!eb( zfRX&#dure}pF8yH-#m2ktdES%NFg{s)YZ}Qb9XNN@9w(cy5b+%e&f8qE&!u5?5q1u z|H`v(eBzbkO+KhTbHu=mZFAH6HZ1rb?^|v;Z**9G8m-1>*tPJZOM!++6F z42EKk=vws$44zo5XnNP$`Tx_s%O6`ee~zwSUaqYs`0Tm>T$yqI@ajo}UrnwpNC8+`uY*+1?( z_W3<0zkc9MIc}(!Mw1tYGN);8l9Su+UeS5qig_R2yy$H!<}L2HZtsk*jm8!Gx&Vxe zUBi*j4h((g(AmFx_2j2^p4f5p?90c_o0p71g7+<}>u~goe9U_L@TzNT^M*z3J&W7l zyMDpD*Uf+H4W095`__z$YCJOot_#2vw9B!xgU{?g{mQX(FC9JirPoircH;aiC(awS zo@Xgt{QmLaP+MDz4r9ZDwlxddVEus=ozVZl%InI03ff}4SzH%@@pA0S@y`qly>k2< zi{|rtPW|)G?`=GZO5gCC53hWD?R)~TeqsA{HGgGpPIHX^AG*=nqJE$q?*IS*07*qo zM6N<$f@xzgH~;{^U5QXmM-2)Z3IG5A4M|8uQUCw}0000100;sA007evKQRCR00DDS zM?wIu&K&6g002Z~SV?A0O#mtY000O80f%V-1ONa40RR918UO$Q000A^0RRI400031 z0RRA?0ssU6000310097?0{{d700031009610001I>KaJ^06+jqL_t(|0qot`vtGw_ z7<%Af1_2TP0S=gmfs;6hq9{wUsM%KNl;wx$A-So<*RIO7tMU*he!F=|Isd@Tbt>gl zZh5+8yId|ugC)x`MTyiv%0nbslqit^fdh~L2aw=|i6AkY`&(=6-DiK_IXE}~0n%4- z6YSID>eXxN)xCS~Z{gI`)Pi>+eD<@S`+xr1e_OO<(V~e(t5>hO^Uil~-MV$fiWSS2 zE>p5-ar~Q@m>8lqhU(d~XRCIse2&6tXkFLog88S;E*R}R$W1MnnmRiRJ)?ZVEaf$* ziOf2M`J=RO;leRm&C%CbMLNBtPiHuF`qbGoXD5(+;l$CSN5A{s?>+e7*Y@t+yLRo` zE3drr@ZrNJPo6ZyvSrJzyY4#7y8G_;Y~Qi{nhn=1UAnY@oH%jf$dMz555IKy@QWaC z+_>?E8*b1ZL1waYCVILEt$g7EnfPyFQI)Bhu5Hcw1lk4(&zv!}Geyrcr_TuHCA@j+ zP1!f!eDjSr-Vj&9j84As#-iW&jo;{@ynTP+@rQr-2hTkFtSK&EzT%c!ZrQSR3t|b2 zCl&`ALJ7L5p9`&+@kIw+i=?y0bAp{klzSk>!iZ-uOVKQ)gMotoZ!HXfjVFhr8w7Pp zvxIgM5f)A?Uc6Wdp}FFUD{j8|rgy#TT@o+7^wRUsKTn3#YDHOIef8C+o_cEEzI}(D zKSYEWtmP|~iXpQVT-{-&F5 z+_`fn!m%YwmMp;ufz2q26oCd)o=(P2>THJDSg#uZ=`RIsGyWy7fWBp&aZ zEozQ$F>YhcOtjIfYAlP4hDLKDjs=~dpE-T?)mPts`|WqV=dLZAw=h`b9=|Y1AW1L3 z{PMp2`*!czy?_7yW5QVf{-v~C4`pnrg{>9>ET{sgf0MUn4#g%wx z6yPuKzyEvR`!1qeC2zj@=2fd!A-%9<@shMnu>uUqR5Tci-;x51!5Yh6kOEx7zGI*+ zQMM!=Fo_mRgfUKCZKMv_%RmjJ2@30uK|lS|KYi=1w<@t9y#D&@5-b;J$B!R>_St85 z@8137lTW_-+N&74eA)7qSFBvNe3^CNs;jQz)1G+!^+V4;&pVTJonsluf6tK+PS4BW1P^n1!3wY76-Ls#q-r zEMxp*@kNj}$)eF5Y&Xd{Y75SJfy@EnBCF;?Dva($f}OKl2v5oNtj$uk)I^(Jc`Fq) zecz7q;e<>qTDU0wPymM!>HSj!VeOi=?|ILA?z!il_3PI&`4$9RAm0L8Yp-nIyz7Zw zJRgmzN388jmabT~{Hir;WNohx9y~}HtP&?>A6w+20N;_t9fx>XI+t#D?db?*{QX!}G-Pt^`(C`z(T;1Cs~A*HXij~4R| z!(OwbjWaDCqk~qiA4}hscAacB2qkma&;pgnpo@;HSFd(%@!osh>lBDlC--ge)J=r<&{^i<}Ahm0u_jZ&+cd< zXi((HxS!t_SZ#rA&E}iv<$l)o(?=Fn-8U|t4ddY2RL9FIpZ|g|gflY@ zXb5pi$*l1h=8ZB#v#O(JVqzZv`~77AOz{)f@sTRTXS{B_@x~8+@PllbEi!Q@XJz%Z zRAM#Y6gfn)8jya+L9s<%wQ9|pH5wd#@h~;ObXC9`bU09qSlC3$|05bcJ)I7I{0bm7`Y*VW|Hk|G3NT(?`O(@7Re;%HDoq(VXKm*+jp}RW;pcZO)Y#K#gQH6V3crn&n8Tn~>olzA9DW0>| zDC|b5He<7}+syeDgysts%$ZXjQU-;?Z_OuF5)Sm<_rCYBYdj)ittCtEghRqf=;Y+& z?p?bito9B+u$~&w0s3HLaNxj!W5$>8P@-_hmo-fcK&m^#JsovG=TzegnD&&C-wkN46KhUSFT|G%l4N!3F$Z0pn8aM z%<)3PunWS?>`DP<7QUsD5~eYzW7)9*Mr+?51xU$HdKSV(*Gxwhf>(>QvL$Ur%sJwi zmR>lvXu=i_2N_|p)qomo+O&!1gVCg!4iI$BNk6B^_P{3C`knRE|(wjW`ucf$ZD82e4K8_9Ks=i0!Y{Q0NpOHo!qm)0u`vKMC|xTJmJy}CV%CM+#fq8TD^*Qz+WP^bRdq2 z7JEhz0a&5Zk!3wB>4`o2A0ANJo|5^Je*4_`FWnvHKJNa6)z60jb_K>bb>{6>fNy>4 zTNXP90xaW>Kb^OIkAY*<;>i4^UWTg>CW)Ke&U1& z2-jY7ZS8=q5Bv7-=Mi~IaP_*Y*+lJ`e?Tk+&p&^NTCBfjJ#$$j;$sa88fkAvEfgTP z!B!X&AI84B?-S;q%!jCFs6_6AqfqhNssKd)kw+e3^<(=R#y@<=AesMi1t==lY-5ra z6=rrXstx0rDlx3Ruwlo9cq}_NtP5&92dLQ;ASGl9X`|o3#yiA}f()qz3RARBUG!sxZOlT3#Ew%cxVyUw8yHPD84L=$*E?h$c^Y#5vaZn$QHRf1Gl2d-Rw z<&mRDECtLqg|UxtLQNgVK+XAb#5KySOs(NsRiRYbSuhzB;<4;39Y%{vG~Zer zxRpeV!LITG9x{_slc$D}>HYp_H3-A3^e{VUHG8t!hf(WN3#oy?Dz90)=I;00ZGUWY z{LC}YaDuP7_8Qv&XmE^~{G&&Y$gaP7z3%pgTq6lOpz-R}S6*|?H5AFH!*w9-ejNDt zkIGpA3>5xPL?gF8L*i@WOZ=?~93bn&nX_-70?_`ie)X#*`c^tbVg76Ti&my8n4a`Z z(iSnRw$d}>nZ1T3W9=KLVVn7+XJQgU6_OPq=wCc%&61ILHqg=j(uFYVZQPh%2R#y% z6sN?8)yxNH;tH@BA~?(zg2UQ5(rq@{RJ!pLyRAF~)awNAe)qfiIs2d5|H`Yc@P+J; zNj9N$J&Y#ULu+Fxu#hOH26hFHJ@yzU=K75rZ4AI9-wiR-0d}YWk&S}b0jIQwy{`ly zT_W#AgY)(&!1uoQJ=)CtlYTA`;ScXW!`KQi2Z^hy5xOOc;;rMbyme`{Ezz2dB{Ue8 z470<&Ej>)IL&NOblrGU93)f>_m;4`tZCKClUriz$0eO_hg1d~(MnJ9lp1zWt>X`jsoM*sx(ed(T8W9pW!= zhpxT$TBk`6vJ}`b@SPri{Ba4FFT6(uED0v}5-dK!55s>tN=gTMW8aB;?0ItqNcD(k z0>az00$9S_|A9K&Z?jMKV+Dv4pxpeX%ZMn!o>2zOpWL&yEYdfa!$eEZ+EL-G4HOo| z&(%x$JFfsK-VMW)_sGSJ2IAc&*b=QvOQ@Qowl$kIQ5_RN7ilIH2OhchlUJ^e3q|4= z4n2ST#0d(pVf_ZO;i=i-!!NS$Tz`kBI+B$&Yu9=MfgpK+wQ2LFdcH>1)BML3hHrRNiuQ0g@n zTHh*f*|CJ~O0*GIdJZYC24fH+Te6jLnAgF-u2$9D|7HO?zoG&=XgiejNVf(OeW>}$ zwM1Vt{daD#kqBZY==hoyTu0Kr!Ovzjyt)J%qku<s;haq1h!_x0Z?umF1rF4=Epz& zF_AN2p)iADq|X;(CK-Z*p${?<;coa&Dbsa+l(l5cY`qf48pJ7BgjnUy#)Ar%^NN>i@js#j$3ZMg-U45o7l5=&lkS%g-?9q6W%Md zMED#<*!z6i8}_D&KH*Q-sB?jweuf~NDFiON0@U3PkA2O@R3pyAi5Na)FvLW;P>C+< zPe|#)kmxC5y2ycmZIhWtB3~UgLla{>*a^#=Sg>vi5)_e z6s3&L0>uDeFJ6qe>w}B!t?yS0eCVu&}3KtWj`imxy_u3o&FyW-dsX?aQl?mYeU zQ#ajslQ#F?e?J-X*0N(G9?yy6Cr-X`63mcsX3+o1qjZE$#p*BQX;|1oi^ep9mnZdi zfA@D}nA8%iIQSGM;`Upq0YpukR2e)L?lR67Xc20Zg`_!15JXrM6pEVY;(`3pGe;%(h0#7kU=<2Gp%;DN zm_o+h%U(@<#mXz6OYx)S7XDWLobjKuB(>BAnE2$D!?%~ISu1yB;Du?WQV z@k-OXNNAUTB-40=CIE<1L9$b)EzOiLR*r0n#MnR+;`!zJtG)frZIz|%f^oqqDu=fQ zi?bu2#8F#;mR6!ugT8$Je;b4jU=BUqy)!L?g3$(OI9)d*RTG&}JXgWoU8=TdT~L%% zwlKj^j6QzsIAY#((@iX$-EEPS47f-~&|RiL{JnekkRPJ;q&mL&i&b<*uBGUO5TueAToC)^Tj-Gvmt4H6$(@#Hb zsti2UC*S7JC1HBag+SKCLbG!wF~gjv=}DNeA*!%Qtf&&P(u@ZkLQhgEx>{+2Dk_#B zAIGhR&^xzML1m+xpmS?R3uKF#)tNrjbDX*Z9Gr`n%oj~9f<-W6#`v-NjTc^EejLKQ z;nlP+aJ0A7+=xN(*+NyRMSN=D(336CXJm_x9X+PJ3K>Mbk@}WfZuMA|;SlfMz3X$I z{oH*&ci;By+tD0H009QU$-KVl5Or&QL*Ozh0QunmKqVe|aeN4Jc=dVjpXb91TDXwY zy?(=bCZDn%Jb2J!j(wAptUdgw1RtDM@G;89lSKxYev^9v2{bc3a8iOoSZy^zmV3kB zh^*Zh$;;T6-T)Md02Y7JzMln$a-NP%f}yC1y^O7brx}Wo7H(RYEW1*RFZR&hf^RAF zB?Z^Kgln(6wq6WJ6N*sEpf7x5`2OT-8s~0(Y6a7YPTc1$<6ha9h5Ne(y+0$fXC)~;D|?RD2tfM=e4 zhMqXt4ixziRSj8WW9PxlN4&5${j``^huQJqm!(uRKR#jOqZKkEGfsPCamnfmdATeU8U%F#a)a#yJuiK5=fX5!) zauKT*I;>y6;hJkVIKy_@ZHW)lkjo(Z1i7KiHxWswBT?)nLd!=A=g2gXsy4%{^e{UU z$lSFv+bfy1OAHgH3F@84H8R^C5EfN3IM(xp1QU#HH*9lUcjQQH)lf1-CBMC;CF{vm zcDK|is*U2uKK^khNFKiK-@lIs#ecR?(*c@O4w5up>v$PgfFJ(whgd6gGiOhyrkHSC z_r?&ru7UfZK9dNWa;C5Jnw$fp3^jk!ZGF&~px6;xOA00xAbKG#X-`n9h?@f65jG&C z@#cSr7v2k(av@y#)QBR47+Hl>lDtTmw*{3@gJ>9v6bmR(W>CW7$0d`Ky%NP%0YI1} zhOxe_zQSjJSBJi(KgOx03@^U;f)fnQ%!o&4KeuVqChLQ=2~uTf7D}C&*Yqbe%sRo2 zkNuPp6QLBIeElR5zhT=A38_-oEu^Bqd$0b+qP}bKKI-=zVQuD6}E2O z;?TtY7%;5y7MOq7gr_JhD8F0^z?{3_3Nu#U1Psp>lJf1_w{!G;7{ZGIEh#Ixkir5K3gO@GK0M{rA zm?8q73V>$uQk2LymaLZ7MbJwe99M`22HcD0u$~wu5_q(?bJ1~ z4?BIDH9CI$#1p%oUMMk>1}~D{B7brSzEy+O8oU-|Mf!;Jyg;Rb^K=Jwrtt_)KgD? z{p(-1P7xhat^sDNsy8IU3ihXp5GfKA>s#u2E3R&l`uZCE%WVJU4zG9pYRO8SG6 z8Dy;mGL5XrYgwQg+#Y*4TQN+kULD%Gmk5rfVKcOygT1~JGwn5|EeL{hn>KI0HZKKO z?m{6)IX5>E+SIDqSa#S~)f}~2&QUu@)o_T0L$z-%(6CKYfT{2|r$ao`%!!Ljd=cIo zGcJ=79u!k9Arx>25>7>V{}RIOcie6p@V)PT_qk`|`zw|X6xKzkF)oV&VCwJx{_oQ~ zB3`_=Q-R;NANlrUKYHx=iR0FKJ{*JROjqm!V{t=n(PFv_vl7R`PkKn@VbwH|E+kbD zDn1wFVH>cm)JiJIR7p_~1c}gqTv(YX^R>=0Hrf}hwTbom4I6@!JW=f<Lk$f>gvtuWY1G0R@itDetZu|D_oJKanvH+T900deY<}-T@&(-X_K<4gyF0eDp z+dv}<5VOd(Q5}g`hx;H^@>5erkeK*_1xyA*=`Ays!aAUfeI{W;OSRkN1ap4=q0?UI+wq#K`^l2H8i*Q=9|4KdNa}Y zEUE4wKxl|oWvgV!fU!1h8kZyic)_o@!R)e(aHNuUqV8|)4&wSl=t-XBU=o!e^a33a zaM3LEE_UJpP6))^P9oNQPe<@Sr<&;rZW1;hdG*LEuf0lj5R(E>1Il0njoIZ`mO?gE z&y^U`u(heeS*uzbi8&g~Q8k26X^pG>hyuuj&~{3YElWBhMw!}(AzjEm1*nhzq&sR& zzUk(hc>k!X0TLlg4dE2-p$8xGQl$q--aq3q*#~nqEwk|?CylI@VSIw8mpVM~zytsD zfBsJ+0Zhs#CntC9-p#OCxjephb3ayon04+_V z=2EOI5=&bdl5ueWSMHFKfQ;i)Ji}9L#EI8W#Om;RobcjC!13-#oNp2G*rZuKY@lC$ z^%ZB~pgH=mZ+44#)5c9s%xaI+bgZ!Lkcp8Zv}{WZ3$w{A(XjC>)dR4z){Yf|RsacZ zs|wF2EPp9LE0)0Pm`dI2k_s{s^w^nEgfnOq$}qKn>+eIQcAF^4t&kwr+It4|mHWTq z*wk`rw`uFlCEIXQ4!vbAqI5{Y18A0(5-z*2x(78nTo z2h1ev234pe{GUAHh_&L(nNorpQQ?M`t%)ztPL)+qP}9uDUh?TF|On zOZw6KzvUtHx#_<((;<=D+%+1er~Aw!rQ@(~AQhNd?#4RT5|FjdJx6|lYHGLwlmeg& zng>n15Oekn^KsRcSNTM#Z2$t2Y2J|mko1Ws9{;mH`y#@kn3riZ*uQT-X{?zhcDXK( zq4O5A2M!$g&;R*9A3Jv3!w8QsEJK8z)wg^T{ldHF)rK+@MWvfaD6v~2$Lt<8+$q*z+2$OF^QQi>;Nl;!JhTJYB+xy)D zzFOJ!bzHV-(`Ihv4O_Rm7g;8zG`Vo7xwIJ#3mcL(&0*D8`_^)v3EhVf&Y=M0zFtuq zf!ZtxGTC0qnobx6;dJbOD#+PV^7S`w*?jMZ?zJ`&cQRHhCtKq%=1X7t5|v;wTrbee zlL5CR3$f6m;j~9kxY*2p|L_0({`>#Z8*Z*`SdwnN^;UZTdlWuTc(>{CRXiL48jV=e z!mw6a?aaclHkT?}1GV8=qTr8ZM+{aoVCPVPTKz{Bhh86Ww-y1bwyQ6gXEj&o;6mUi z(G>hPy4a%Op(1OifrD2!1D$)MCM>ra+;h9o#fN^$w!S{X*|z^!w7a+)e|s3`WEY&SqtWy)+2XQQ}MV4xww~1h9ksJ ze)5y_zt#adU>3~BLk~Ul#V>ww&6>5Iz<6H9YM_;lr6ia5jWC;!FaO1tUw!3OytG4q z_q*@fxG{bL1LJLW1(QP^n2qT`qc}FSbR(U_tcAk{moROt3m9%xRmX(8u<8nf*TL%I zd5fzAK^*er!;j@Y9LApsFKTLv5wUjHvwf5-8iqL7Qs*~tSQgI;QJcdFK0EirA3o+% z34Uwk>mxuJLON!Z2o#Z9wveoN*1}t|SH(4gN*5Bu+_vY`l7>0R936a7sq{ z&6G3?VA$sR={{!zRs$uP0pn=MRjh*tpV_tRiOrim8$Mb(P~%=Q1>lTwvmSZmVGfxC z3J3gHi0&c!{e7L7XHRj7Hl>*DXvl^^rj{73eP5PB71aePOE_+li>QdLUagrNxv!;&FoZm)yot{D2{vP;_Q)@kEuv!f51;Qob; zhAIKf7%s^>jd_9|W?T9~6CLmr=+?IcJhM4;)#fQsqfbjrQz?9*)Pg)zhOthG z5b+fLh^z*t_S&njO-@ca8?_$tg$##2mQX6;A+fEnDk92ds;Wz)0BpZQC~*Jkul~y8 zUU#yYD63LPzrgqJz5hn{q;^DR)M#`G6Ak{%#3dZ%f&OidunA45P^p15geo3lHnU_{J9oN+ zIUfZuvC(GZkkj@%*fJ|sB;Fh&_!~D+B9MLsl^}Rtr*vy*+qP}iLxt#}(Eb?3zVqGh zDDjzz7xUGQ5YgZGjo%m_@&fY~6tNtg{V1V#{QV}R3x(E_bsiz?( zfB6*PT=OV2&Q*JP%P&X)N;ryW_2UWBG+|;fx89mS?(G?nlekj=B?7~fXC;oB!q*{R zn4o2C60o0veDu*rJ*>28*2^Y=jWTPJmPU4*05RsAp>ehdE5=-S2AjPEPEYG!z=;*G zY6EPjFt^W_q4b&lXSA4l1&;#K!IMkXrUBu?)zkMf|0VjR17fo0-LWU1-0e}ZiCY=; zRmd|WpB~6s4VVT#3mo360$54_LV`D5f8+7TpFnG-B^KFS0myz>{|rSPs6--_etMEM z)r*P?>{vXLC1VXbH2TsSxzF{aAH`>^b}SqGs(*8@XS2f|9ch?rV26d3j)!H7&>1iN zAd!2r(gfO#b0xTd2#jR2FA#RO(07>EtzC;jp3QR!Tv_*hLRIldKGi_F1K%fPTj5MB zX00vG7MllMVlu&cQMrOav&@c?Wn{Qq0URz<=(PJzK}+o84i;sQu?Cb5NYiR*6ZG8_ zr~dD{?Olul9RQQN$Z4ecPdxF2N3oi*-ET<&fUw*kG1~F2TyzTtWC+urp%frynFOsw zOuMQ0=;R0V8c%k8NBtkubCy) ze~!4iq~OFtYM14JEnnnk;asi7dxe;gmMHF)8{J}1L7Jkli&tO0&KGBS$$R(gwRC&C zoOto9v@cBv+06C1Lw<%DC~u+g?Iz2pheeOFd{!IG&JfE&K+USIae|nIGWj$wAh7%K z+$7NF^F+uEK&2Iy0?-1YPtaXH5h+BIJ$v`K|KoRE4WgEyqKPy=km$QnLisOV0gO%h z=>T$rZkvUgr2vSC0U`hSvx5F=Q#9yEHdKJBcndL@GkbRf4gSn*b3r9F%B*9Jz=6wP z5vb)p8YmyF>Iz3X&o7gp0+aeLffr)vOo%-h^$-9H!o#UM<;#|-A_3$_tg);rp0OYD zl0Io~h-x6qP3XX%#$kkC3cLqunEss2Qla14%Yp%I?1pvjy z7YW!Q=M4My?IZUB&5MtVl;dweZ@s7YKSH%jp#VkwF|G`WXWW)890J?XP$ilbv}#tq zFp7@>uQsi5Wi=a?#E2;pW#J+_3$;#!(xo|ziq?Ip?>We!McYt=*w z)266mI174}l+#TDA(W+T+N*E8z}}xtwL}|kZ0NCU*DPOui=|*p;v1CnC9r?4CLv%W z_0wo%Ir-24`&TVqX)OG_@9z4yKE z-L`F89kv!JYM@4z$`-aPd^7fmoXWHgiEFgX{~|GMnz~siEFfM0s6>2dUKk@_&01k# zUAJp%&tG0r9uoCuqZ9yCs_$|dTk!t(|CHGQdENEbxtl|1C-+TWi~<-KjS4#BZdXAW z()Nd$Ay49Rd&DuXw>KEkV&~M)afd5`5)4~03}PhSsswHLLr zAe(fI2sQ8$Prfc_ zUN&%k+!VQdlEaGOiLwi18#ivU8dww92yjXhYGm-wE~Gx^LoV!o@qZfQE*~@Ocs=3k zVW~5*i1neDpe`Tt3i8LUApTT{?Lj^zh+T3hMmT$V`qIMc)z3cr-0%MG@BZqq{3(=W8YRP9!=OYSBzx-?~k*FSwL{&7M{J<~O|z z^i9b8+mh;g#0oI#mcq0jzS)3N0W9 z4<6(XxToyIg|ED6(MQUBN{1pna)jYieX%Iv0rw8O%tpeB<8i(j<5@V_JuE8cm4Hu-+edp z=?5(syP0EML`h&Z(49h(eD|PAx;u#v^WTepO1~(DJ|2rji1i>AfLI4)dlJX5cKi6q z7r*qyU-*Sz@IoRTzkcKO%;E(wuMlD|>Iniwee~m@hrZ61AX9kLf*(H(Cnz?-j0749 zI@YX|lrh8FA$Mzv{e&;m$D2>%fm zAE5O`S|1Rh`6)r+Sf>I8iTD(*-G}4k&gq+PG68qqap!G!+~(6GVZ!}aoP~I&6rB|> zg)jLI^WV#QEEiFypli~h)K3svC@lD!raZR1Dcjv~Cm#gA@?)^|>6!QjP21_^Hdu$I z05K9hSN@Ct@)v*Vr+&(;eRhpqyD$Yn3*G1zaOV8vle@q3o$rR&XNYgh<~)qTf-bIO50|}ZCSy9Y6=N(Ls z|7I$HhAM(kxl3tq&YPH~aFV*(*U(ip2L)7VNjO+Hi=Ib1-bxNq@Wny~;}fe4MI5ug z@g_mDiD3IUgd~XxaN_{O0U_3*05P+)dYMu##Jqy@fmh5eLu7i15BjupD!-9q@Sy^* z>t{~Yb#WXi29*{>obUT8uXpQMGTnpv{qc;f^&Ff2#lQR)+i%#8vp@Rrk1p&Oln&`u z*gI%)?H0+HRT4%G%J7%;6O@qs3^DVZf0r0eG-z-!p(Ymg+a%BW_4&taMSlI)e;rG> zE(UZjT(C63#{f0-d`1FOW4+8scP_8bl3_NVftnjG%+BktOLK_tz4u;UYw{r)Um~z9 zMElio0d_b=&?*EP9qVHsKpavVW9kO1^b6;$RdRFr**6c0Odd$zmsMDYshLZ9gAR2T3r8X!qrS1Ia=>#yE=!wq-|V1qwTsD(*GV~Ch^$p^iABvdA1R75tt9%UMJxn{$TyV6s#>;?m({Bk4k1+eSV_x< z8mw5(W9Hsa1L8&DArp2abYVeoI)kOvUmHRvMbAI?+{!C_*~vGYYzIT#$09>b+a zClS)mIt)Dis7-Go%1ycnK(7Ewg3p}AW@c~5)hD^Mx$2% z3qXZ3|8>%7uS*L`6R0T9EWQ8u_KXbO(7=B>mXJs^`1nGMp=u}Ns_X?QfIgrX>iGTR zAO9FohEO1UVgBp))!60xOFS6_m6@w9L>Y*y~h)l;9HMJgy+uowL)e0KM&x_o~rf zP|wEKkx9-p$(eM19{DudhF{|AEYAh+PfrFsM z(gtNlVoU)F*BPy%peCuoGY6mf!4Do~s6O+V&$#|-&U5sd71UTEYynX?hRm=;n>LEH zmWbfTZb?5KU|??Cd7}oph=qa~U}tmj8f8iWf=ap$1iOYC!?UugW|k|QwY;?)t8Lln z)`!-(X2&UtuUmV=#@CEaoTvvr+LCW;Kl5jya0tTox?*4(Z9C-q$IeHZ0yI+SRWzws z))4)$krtW)AXS|nRWjRVA(Bg9y&RTx1Uh`?J4R&+wSWUux^yo4E z26v*-oC%0i?|8F$iEAg8f+I(dT0LwGNJS?YNY;Bk!$B)uG0F$=`xT(MKzLmWz|2=# zqI5vEQ-E$m9FGJ@2(A@0G2lXtUdT8%cbXBhGN0hcshWW@xo?upIStHtMZ^M#1A520 zXsc!GGAyscuBsP(4dUNu7`7ayYgVSm^>l1U3AgXqjuWO0K&zD)_L@(+btxdNO9LxW zM64?53PFTC1`&ViQ=d}!{BzHHdCR1L3Z)w0(oJ?S_iiBE7}|ba>R&1(AT@f!>ar$` zH4J7{!#OlE&IO?; z6SL2N(;xZBM{LaviDwr*bhi{t3rV*wT_%#AYj&-?rdg!0Kq}&R zV9gre`u1rY-rysTJmTX#ZkmvmXpn}o%w@&EjtvnL90(=?hUoie8BGBv@nQ9IzZ++s z#p%?5;#b8@0Kl)38pw$7q{+^8=5-aKRvex6@TXLnpHufBYE~N8#=nNV zxVXqr_h(A}OarKhrxuvMK-?kYECNvqlNw98MDc8Hi5ZfnfY&Gmr_!LcLNJHQAX_bm zv0dYyJ$sy#+Zk13%^HMZ%em4CxWTVP0g-Mvv<7p5x})T&k-)+F?RjT1%CrHsD> z-LL~FSyk0~)exxEf)dEHn;iRjDF8VGPB#gB1&9I>J0b4Rl+AD8Qy?mL3CTW27ZZR( zp+Qc{9w>yT*yx6?xCl|{hEK~jGPNot(p4^jl$EUpvM{N1i-*H0log=iT-vW&7Qohc z{s}Sy<#ZQAX^3pA=zLCW}N~oKk@pq`6@t- z=M^wVT_tv;AE<&`I?%?b1S`$ijdc3FT_MZ^`PoC)ezdX*16qcdGrBK|&q!H_ow!kb zl;zbSfv(6}h*kTFmAArC@YAwh<$z06JhP<|1f8{Z*w|RwG%GY+bxx6&5;;`P}#05?=L-hhJnURh2P@~s#771#<~ zcyG_mO94t>LEX1+A0e}fm6c`sB?OZcWFziFqb4=U2ZB*B_VWj=O(kYk099RiFFi(4 z$oq2o2_IhB-o2Bz-S#dXnRS6->7b>E^yu`Rt8U)0S%aXtxEN5Z8Us*m6jmG7j(^Gc zRd%}ZMPe}xpi;m#$P#Vz@phLkKQ=_ON;N{!gc@wUVXI#~^q7eEA0|0J`l~NfWp6;- zogX^4@N@7xcI>b|Fs5%}nePP0nh=cBi$skRx&PTHwParG$kV@+e`?Sr^lG8er!XAi zA58EIL5t(6*!dNp#?#-qsf8@up7b=7l;IEA&vVbA^pA;bA@NpX0K*9MhP-qjD86N@ z)7)pCIp_z6oX|O^Lui@+y$aQ(aI3WP5*o2ED^|n4=V~+7Q5Pi|8;dhgghIWpS&eh) z6ks@d4LvNm-06P;MLb&bH$odgeGK00|G}t-9)Hc1zy#)g{Ue- zqXS{PNIOgA0 zhIkc<0VC1#r_%AK$2bb7UyvIja|zN$DLKml7dmXV_&T0KS--)ta=DewH5SKl@c}6d z1v8UWW+~AjHgJV!!D$Fp;V@h9b!mky0Z65*SA$9JSvZ!0Z-#VTdu=bN0u#F7b?$omFX!s8foB7XXbfU?=>;~Kdgv-dXCzVChS(-MO; z3f>@fKIAFNH;$NEdO)4PTLAoU5G+XwqNh1O7uIT9mudqIHo`jzbvA$n2wbG z{`{w;JTxE=L#dW)5cp0Fs!HGLSaGpfddMt_;6aw6^D96uObhtg^fg31&o^L+*%8wL zHnr_{OUd#SxBDgx0l1{RH@)EuhqE%t`Vz7w7}=6ys1kHRAQUf(lwqa`ag6KxIm_Z} z%)BX~O27xU>`FD3HBH@f@Q7+$33RB&Ms(HIH+I-1=07N2|6yn7;`u|tJXgD!wXFmi zdbRAiAY9^d)9aXv!8O4kQ55G7uXJ0(yn4Xs#BbE)FWg`Us`8W&+}oBfzxwhgKK=gB@5y=X#@xw4AnS`5>!GO!=fr% zn;_*8B+7k4CZT3mywOoYVd0JJYy=DU)c&Uqy>JK%u~t=+{z&&Wz(CdOCm`;nKj+$O zcu+n0k3>$A3pU3! z@#)YFX8KVV@jDv=#|)Jan+56++!c#gG~B9$l9yK0nih>0PFPiVPFyClF6A$ku&Tt{ zVkhDkPMz*q4CB3!#Y-%TArDFE2LC5L5k!uAtResTpKnscHU92GU2<67^|?zxa#4uzvpR&;G3AQC<^hDgP{cyorVpfjBA54wE5zemw#+sa*BpDVgqTfls*`yYPDCAN%94{zPi(!aASs0kTP8vtb3d_qH zkD!+$wGvb->L3Sxs5IpwW$?g{+ntQpPlM*cj&Mvh)3_{I@O9}j?~6%<+T_WW@s<1hi&aIe9TtQzyy1yUAWLbxZl@fBxs~lC1++ zz==gNO&*rZJip9ljS_p%Ljt^KgN<{s#^mOhB}` zd;Xew5F3C{0Q=z%a?N|@ub>4s6>?$BO-&t;3{<0Ua`AtWOy@TLivUy*3J5q>WI;BV zK`VM^v}9cgzQ)u!QCSs3U(y-PF@?7>%{jo%x2hSk(GCa|R-i<%sR7|tTFyznCS-Gr zu+D;Ny%jsEuoZTX`Var`59xr5MwSAwZQ-o63{Y6_+#F|NJPXL@0kuw~rRskHJA^wo z5;I8@yovx=GL9x>ey+%;bS?2|^Qdn=`6>^n7kkp*`UlwV{kQaiqQa2U?O$L34R24^ z3)HReB}oJW`Y;E;pk-2%5DR(?8&@qNR-$Dsda>LsicP=vLFRk_Qq+uzMrlO4#Ge^q z2ykY4toc}jVePC{{k-Gvu(9-5`?2g;<6&0In5JO5!+-Vl&yKA@fRw3B4DJMnJaiNZ z9DpG^WXUGd7K`(9r3d+W&+6pKc(ZZSrcLbqAOGf&z;Y~0(2=`oiqt-&*K8Y#`o02%IDrU?7h$=KIvR&4XM^i!DvyR zGY9|l9A{GB;3EH`%NQoJmdqdeP>et}?j1S_CCad8C!zdC((fT=?V{nwXp*x#K`M7!6ta!)mK0G;MW|Re)<PPQLe3}1l z5zwvEfW)KCE~a}fIU<*>-MPo>6`%+=SG)7o6$xgxLZGoiL>VhNSGM}1?715JFI(OT zdanjWhZKfcI9YPpXhjE*lsjYt;GU%y&G#qLR!{2)8<(K~X|Mt&)nv%2Q zYcw7vSQ&JW{c-=5IIKx6=v>+2R0@0-8MH#>tvMyt5jRw3CLDbpJb2K_m2T+)%N9kif**ujW16f3)QHX3C|v<>k+b9U*}QZG^XY>(5SY#298JurUs z(eFEI{>A_O7wLe-!E2VDB#<{)_o0*if-dRev!Be>880LOi3jNcEA^mW%=|KrCW z|E>S}U;mGP^{=*X-|k4-B^R3nW9Uu_QcQv)Z92oT0Cs|4WGH`y=wInYvN#W+}`HW0<^24{w;tnY|fn|tt(krz;K4w%mrQ&U# z`2I`<>MmjT6VJIVc7xy)A)ctji(qlLN9fQ<(FB`ow|J@hS=}f@ib!h^Xk-|s$C~}b zWKA~cJtNFoIHOe*gm5Q-02BW@XnkqlsVnZY=le@M2ur%_=}xB*`_Ll(9yoA-pku4_ z`m5JHxoh`t{nl^&>aYIljW^xs;iFIadVWlz$Wl2!ENtVcgpRWhUg8%r*zH5ksQ|XW z-tqBh6Pdc@0@T_EcbnnZk9VWy1HV!fLMg=g`I4nhul$9mP>o~Y?9~+4P6PH8DlM*A zb(-L-NAcOylPDr^0^|JHb+^28uZ_mKn=r`wN}w9dxs`r1*_Uhmau#(JwA3a@lb@N7 zMD`MQ6UmZ)h-Rk%d7qZ6AY-Qh)jBu9k|BJ(CZKi;4JIyBv-m9x?8wF?TG97(yj;&6 zvJSlR(o1i$n{?os4Su@jH-Gd0{&)ZG*L{)Fi&@VhK1vzT!gCYfX7fnJ z0q!@Be*-?wk_8TBU7U8h3{_JDt#D{V*P#Cpqri|0K@ek|`dNfkpY z&;er*W!O{7FxRi2^g-=1;myg3ElYc1nkbh#g~N!Ly}H%}T3iY9l&GL*tX@^x3$g}4iAyUQ zWycb&R^zd%%aLspzc}CxTixe_@HvO00o^6Q6#ATcYuqJ93x^|W028soN=Oqek|XjT zex@ocB0=s+V2}nc96EID$WhH4nED9pZ~o?Q{@Snons>^aoN|PY96eGtw7}MI(k1&6 zl@5PBLjFzZmtn7GSAh6#dJlq7kr>&+m_dcs#_Kn7Eu@X@&?AdoGD!;@pyv3M2*AQ`)N?>3V0bR$L* z#EVS$w)zb>+&~eDg}sRPp4imm$HKjdTVf|BqvCBeBXDHo`J#L)q$mJ<} ztP24lkj}+cLP@QXgxh~nlA177^n$6Ja~Tj$rTE6`(ot7q_TEv$9e3Pm>0sv3j_Nvr zcHdh#aNy~*J*Ui(CU`PW)%|pw-wHzq@B_v2*>bWq;!k88L<_Gc8PH}JWdZ@p`Yv0A z=Z_oL=4#r*B%oM{JM8q9Et~i4+sD+B0g~xWn&1C_fB%<%`IkTPkq<2lE5B9)F7Yf1;FS)NQGzArw|f#y?%hwKj>nH5+q-AaE3dq2=^^(u zQc74R`=QII$Ix*Y8voG{hn3}8K#}ATH3_;ap|j~7I`lls@Js zplX+awJ3!sbyWsuwc=73<&j8b#x-Q}JjF8yEmbU@Sh9ZIdQxsl)deP;rDvYuH9a*s zImr-cj{%&$&6_tP!iEjk*pWcPj7k}D+CPC&iGHa;P?{Y{st4se+6Qy}7ytUgB1E*$ z*0QKt>57Xx_}@cQg#n9?VJe>WzA#HKqw{O2iLAfK12OfBUocO(?4}S22zFJFo zYG^UwG5jzkP@TN>>LqL>ko_Tr|IHLtGORS zDFD;##_^WEVkKfz70xUe@z<4DOb`*wcp*5p3Hj5-G$*xD$Lbq6pFM3NZ=QN%!Lgu` za-VdyJ@~)}-mlX155M@LEd)*2wR@K@oGDaG6Si;PZdc7B&`gDiNJP^Ae+)&ChJP`! zCXzB<(yt(e{laHB1US|kw}N3k1=aRBR1H;g#%C{Q&q;Fj?Ab#snIxk1=YRg^ym24l zQ^;&7Cn}AzHKJH3jUqDrTw)fsjvx5I2gen_`>f>ED#JoME%6y6oVKs!G!t240H<@K zSCK5n5Ef2wW=J!Qgt3nZbK@Wy*H%xRHbW9gVn|Q!o0n0?XAT}zC{R4ulP%`?l~-R~ zzAS#6l(^a*ITWa+#>fV#(Zz*{gaD1oHhg@Eoc;*9!~k!^7wcJ?P=<8sqKl8$=cXtu zx^Oyn)p4YH_AG5T9Y?v>T)Sb@=8Yfv&`&$SqQ;yhzY1hIZ2pLUbNGYN`7V{AW zATGAQppalr@VoDPH%(ywdie{8HAY$GHl8{74Y^ld4!IY~$1qq(SG+#I$XBw{I8HtL z%rhY0ar+&9Mq%fUohIm%MoV^-f^ly)ZrWtZzIw}9=Xz0{nuoEBQbDsCdS=J3-cwq1hNv(0{LVHSeVkM27!C5icX(N3cafjXn)14}Ne2!K;zu!Vx#d>h zb<&tJpp0)*?|NdFx0w(G!aH~FqzN8APz;L?RVcJ);`#Tj1Wk%`4Y5uE7@hvur&Y}} zfo4VX^S7_z`&A&texwtBlMp)oXF5IJA?~s;^a0jC`IA5Sm0$Uld+)uMAbGC8nXFB>{Rr~XNs0Z7>4!(Nzt z)|r$=BZkO8+vtiJIZzB8P@KQ2$-M4)au1LG_B-yd8r*o}jUfA$331f7e4))-HcuUx za#rJu`*xqEN~oKS&>l)P5KLr6X)PjAY61lfDx}V*-gEhRP048@ZqXIT3NZL-R{+h3 zX;&NTM&cZeLX|^XyqxsuKWvGwa}v^DyH5=Y0B{K_;Fu&SIdJg6-aUJr1=Xl-C&UlpB*|dSm*Bqno$?U= zc!@rQQi4D~{=_a{?eQT1K954s0P$QRJcu)Aw4vn$@uinvwoZAN>;yApJfHY#5Q;4zwLIv9ID8Vl5>!}-DfTR`q#gX(3IT6OCRuxWnsw@gF?0ANg~?P zfEVWPY)qO<(9t1CG{B|=9i9=w#D(A^T{P(()UGn=Ip{h|vcxH3^a$G!l4jWqCo&y+ zaU}Z4A&Sc}C!c+G-P&~|gj;Sbob|-h_TXb5`4RG&rcf-15P>KVgvVp*N5B95ef#%Axz1+*H3KSwxssYiiD`u* z#2k!V=yGiA3LbmxM=MvXxcQb_pb~gZOqh>YZLI^FHf?ep_3+^r*RBm;h`xo!FIf`M z&Yp#v#)TNLN`!{3k_y^rnUz7skiQIGPmRC0n>r~rsjy_J1LBUI$^3Zdb5UF|r;|E~ zRcl*I2U_8nCx0>h1q;2tSgxK#3|fBL7;V7Dv_^Z|uy5rdK`1R|p$ z5CH8R(g)+l{4;caM$wxk*SFIZ%>%-m$OrLr*5byvWK+k)tdA%P#rZcIobh;oFgVM$ z6!5wYr2MN7eD$CHvwwQed*A!e*S`+E96xXhxzAt`7m%GjRm4*zR2obxfL}Od{VgYDizX~XejbnXV=zx<@zM|}7#Inf9uvwMzG5ATt0;uV#ir%)gE?x& zo^gPqkWA142Hk}@Ulw6*YYScy10~mAL8C1peQ|>?G{9*nIN)cTdN)vjDpzF|<_we( zrFFG>0FtDPC@?&G26bY-{xgeQ79yzS{vnisVafj-q`JEV zEhVhf&K)~H{pnBRlS3r8Yae{@!TayOA9<~Qx7>V-&uC%;$Y8>4Fr#S}77pU7ZQ09| zh*8S$BLM>UlKobO8)A-hKotRt{$r4X>|r;9N6{=Dq3F`LgC8N`a&w_=i z_`-|MUwz*c1|e9vnt(jr5_X;8m*|usNSee99CCvqK`61@fe(%3G5hieQBVsLLle_5 z`BPI0JwWi-!RTZdO+r@s+j3-AEjSuiplJg4B;xq!KzFY$NZHf0C>t~#6yh|Fi{T%{ zu8C+eP5F)k8U+5XxaQXl&_M-EEYLNgNrwIeo^dfrSg|4;AuEGr+S#*R79Z*1hrh*= zICBHRG));;pbCDVF&bu;jMmSUn}1&|1(#F$gZ~CL#USVs7*)H@ zu&kaq1gFB{?yEM=>;BPw_c_vlPqza6@CQHi;q$xS`(7ZF8Uu)iu32H{{>lBM-)U5c zexAb^n|P?wnE8}gaHgY(s=9d@ZQ^&v<1J>I3zPETi`iyH2(*cHVBI=kiGuSi`)P+o zV){_~1ote=6VkMe5+htEudsw^zH$)s>pw;tG|#;XY~6V(KMi~hDn{+tw4@q9j|7Y3 z&1*{(S1t@ucXiYxy~vjn30_tr1~qAl?WbYWv%;G=_C0-S!O6JKn0Uo8D?Pz2msky@ zy|XnR#B|K+@xC>|^qDSfyY05yZ@u*vzAOsBL@-cJA{4dpqDor!k|)f)U^f2aYEUF9 z@;?}~P>rK@Bs8q56#m00sKx!pDRfpF^G)lPd_@1^24yBf9$_#X7%MDw(IfZ1m0{r&x{x<#k!292?&ls+SuI+`xK8uS6 zxZ6P_j4}5x0!4$4Ac{ioYX~wTiHB7fz5yTwL2NkJIkd3fCEwUDkPox6gH%g;Bv=-P z6Lpwfk|a^$9%3uR`_Z)y`0HOT4Y@iLbF)0%47LrP&I}7EYgGL24C6A&w;D$ox4R4}wJ$mD*Hu-HVT6K64F$+V-y7 zwr#Trl$fC*VY9Q1|03b{e3yLmBOleKkO>7K)_?Gaf8cR1SEK+nL?z71X70;h{<0;e zmg$^nP^$j#iO~JkrJrgDkaUDpf~u+R@T(xJna!@*l8M@Ui%r>sSVmfJv*#%ZM~)n& z7}}$sLHs+3m3PHE4cJ%9CAe`PluzUjn5>TR8Yve~Hx!^?j6y!W1ueDouC z+;IoZfAj~Be)F5(^pp3}WfAZW7ar!*OPdHdmNTT=IaSk)Z)kR>LFp()w$uNZ zu(X**$*2NE{{ktMAt;(YJw{ZbJXq{w+F7~^De)omv7sv2kBV@9DunW6D4u$yn4n98 z?-ACWcizcQ;adg^C_w&vwFV(T%Y3+Ul`~V%6AD7C)KBc%<&)x^p9Z(;#HIw$*B*Qj z^U@4=f~?A5+MEBv@)af)5=t}ny`R`M#$l0fFLn-+y=AyT3l@f3eA?$8V+$KXlhO~E zp(ZEe>Bil6-)(0@S6R^gEB1T35pK`KguRZEb?er`$4jEiSHuPESPEj+LQFsZkM{(K zSCzsuHVnBq03IY*8aFI}lIXfs)7)M!%*Sp)vveKv&tlVYY80r(Dz-4(JbVwEnE#ou z#XyOBj7#G2f*=t?pE-kn$BrL23pNRF*s7g7cd|8i+<6CYC%mKNHpp0I1Li3kt#Il0 z+u#27Lk~S<$E>}NX5M(?jmQG+m}6U-%Ay$QQiP=E7@#DL1wJ+fQKijL`psV`CPr1S zoRv^6G;D{7N{4M~)9LS4smkS{Tj9-D=p{c~9Sthf(13Uj6U12>w|89}diuZt6l1bw z(^rY3Z4w0;99zGB13MddXNVQxEBD`zVO84ZpjH|P7hpVJATF{wqp)z&6{1<%fXJp* zE&Ea3!!Yj$3YlN-DqrRFrZ0NeMO=vuy*6%Me0CAtRuTt8vItUIYtm{uz=^ z=@*snSk}I1r(?NH9r^N4r3Rm3;GdJTzGTL)(& z3Fk;p?ceWc&;3V>q~+VYLCjd89YZHg1Bp4qe4168;83cDe#Rr{nR9S<#{xTDVRVR z#sRdfG#a|Xhh>$YYs?%%cDBAO6GVKmYkHTeq;ON(iXp(!(GVaA(K#T-#shFAzU1N2wVjL&)>T)nvB;M8TC!e#=_Risl;$%<2tgSp zCnp^}d-lTKQxAI^Kjlx7 zLHctCwTKYw7%r7iV`lL!dNPF3J(B^YV`ksBx>sC-1hf>7=#oNN<&{Ra@GbEuBsq9j z{0)~iocvf6g;IjzpDD{G<3a&+X#-V)Eh3o+t`-F6%bw>tW}p=c+j$+5+9l&r!)MXI z|3Cjd1JC@IAy-jIi!t@D|K_jVbpe5xnvz(#Q2o0^(W6dH(J)m)3L>N)>z~O4$Ao2qaiV2RPF!me*+)Dx~_wJ^t!ts+~lJeeR?`Y;>*+Ae7WLc}2Ji z$}G9Gg0qqMi#zGB9W&+(h<&<3I+3UrCuj6(AKS6uiv;4{+1xN$4w#f{mpNF z3wg=98R<%7c|qmbn;Q6bK7LQhV`Gmq_wCzfwD_q4Un1jDI{wSsgn0R$yY8|e*d$m| zzWJ?hddAGHLJgZGOzjvkJ)yZ$!^YA@$(Um=`qeoVfD#m`G7ip#)H7c~YaW?i+_ObE zw*j!16B8dIB#VX^f#yOaVj(zI7d7(H8+ZSj4IA8&;Pa4R z1A%W|g7%dKaM!L~9{f?H+i$;Z`;Hy!uU;Ro^DkWNCi&4LM`7aGnZ?~Rdmhn)4?g%i zzw zPYJSjq4Rb!mkV!oX}#K9lpuckN2gchFGeGgLX>ntXw90nnO2^OM2@q zx2{{iE+n+JJIQIQ1{=mK8NY-eL^ZvxnBPDY0-QYakbA+bEL#!s9XmuK9xWmSh-f+; zy54D3szKFh7f$nwFCr9VcygQ~#*^r>4usg07=8O24cHMA1~&=O{aQ|xq;^S|Md^SB zMPD>4JCsTjLgRn(TGhG;8jInhR=Pr!lYZVBJ!HT4P3}8z@PMUK+I83Go_qe;=blx? ztTEDF=CPUCym_-{9ex&W+qP{M0ft`#qR&&ZB9mjYlW-!W7VEudC(zQ4Qu(=k=LZy? zN%NDKpZw$}Ysc&{!ov?geDJ_QC#|tV$j@0rSSZX}qCjO$ax95c*=SQj-RH{%>KbR| zkcrVgLs7V51rn9+!A$>ED^t`%NyG^cGWilmKo)>F`HX*?|OKckQ)Ttyu#r&B7ydzbL2Qfr_D~($~L&S3@gcPRhA<6K2dx6H*ed8Js*xu#~K^#aw+Q@{z*QPlVMzgg2!m(is38rezhIsZb5jl5&)= zMp;+hHOTJ0hJ-FfJszkQV+up=xz+QBo(IkK5=Y6@KsxPMe6XlyQAi@ME=)U*=EU*i z1kQ_ZYuAPvyzs&yptf({Zk3=LmTAEO0Jb^Z6FY>VqUIYCkaIK@f}_M9>9%*h>t}xE zXY3H1GCla<*X%tko=hNQkPAMB5G2efCl%RBw|G7Z;4%5MkmNaD0P2IWBpbjs5T4f1 z;;Qp$$*R2C4Skf<{uU13&AlHZzDJl^=SR*x{@7%God`{Mc z7;_Z4i(?qZ}9&&d2bv}}Z%?RZ*kd-a=nbG`1X+;pULejRnLqM5y)y$Os zu~Cd(?W;UYPk-G|39Sf)$Y4ela-j&JSBx^k7)&L`9Tw&s?O>PK7_ys8lKq&~O*0m1 zh(PsP#N=$quPViWvs3vxj2GjvtdHC`3nstRNFefk`nbt(`2&D$l_qYDA2h z3`O*683nP9#IawjCjBm=ArMtLH6>LDMaG-Cz5TY^>_m|Z-JSV(I@4K@q^iaSiWS{| z3^{z0y#~Z#y;v!sZj#<~<4rbj^qh5%fAvj(d<()=i@4jg#H$xxWsd*gBLGe-D4i=+ zg#HsB|M=Q<>pXt;P`OSHXel^lbE-?uAUvNs0Ih7~y%6Dy2hD0z*I)b&F{-D+J!gxd zpp8j?m3IL154!m)?kL$)JD4N+y5Pc>eF3abkAUts(`JLQ8v?ohT(Vw5{_9cZQc#_SeuG7y_yMhL*tW+ zPcG&exu48UcbQ}=&X4sn7q!TQnuby$2D8PQ1)+06l&}t%LQz1hS&_KWq@X#FM=PH( z^Yn@YyO*m0c6L1U#{zT8CoSF}NZ~p;bjr(*0A_$#w>p`E^L?}1|J43E$%Z5Vu{NEt z_3t) z4F@a(j20sf9U>bNYRO62${{||i~jQd`@i$u?|Od8x05f_kQq%~3rHe1tr(7Fxb^qE z@1AYjwh=ojclgD_PJt{_<;DajZSo@2#x8bvGUPT`oBIS5rf<5l?cmd7gT^iKyt1LL z2JH~2NA8Iwu7%v^tD7(s;@mCo^TGs>?c2A1;*+1a^Nu@Qko@XbzxuuJf6rVk_?#ju za{(kSx^y8|giIq-*5v)4YEl~jbi>%9-Y_l*Rnllxg_VVmAWhC$0hGx#3@w5hvE^m( zV+UNxppsdLtE2Wqu!RPh5lYZ96_FJZU2kG1t zt(6xo#1sG;?1$ldC%aRR>fYuFr=ALk5_AdbgeH^%L|IdWz>Z+{&y~y3sqWgS3mp0& z4R}DyPzeN73?*uyn8D-q`rQO`rUo=#m2y&t(;&pq9zW#v3mqGLi5Aof#hVs0MNt$7 zNcq?}8?-JGMiF}s;3IjwZO51RC+qIuq2~{wDse2Z5*Vx&F@;Jf5exth4v~EeG-t;u zXGD{#o97D6KuO?|(!)m!LpgAuu}vF5%#<)0&A8Iqzkc#{eVz5$TKJ?UUH!@fUv&|~ z7dt#>VEAGd-7=h$3$~>ZOaCW|dIeBZtkfl_+VdrvWT=F4i6Cg&kCv{IH1SE(unRDy z2T6!H|B2w?7X=7kQOHOu6R8-q3PuS@>f=b{HVF}!$aE8z;s~?IQ}A^_Ul5w<{{R(t zfr!52>s#U8Xp-26!^Qffa9^2{o{3RJ!IiQs6bw57wqKV5)L} zNxw%NSFBpaJ_x+%bRLGd_Z(MCuGnzR zhNqu?%K6xbKm1|MTKAR+1+YD`Q+5jBw4ta9-7-jf_Uz#Zp=nS!11JqpOQ=Bt*@~N~ zkS#Z4TJn2A4=7RH3A?{0Pyi*y#xi=eAB2XBj^+u&S6_LR!@-eL@6d!Zh7 zEr$>zC1%tRvXu}^8=+WLZN!zR>FXB%GGL6q>@-VZZI!l)8lCNjfLX6tw!(q5?`Go= z!m?I|s9xACQ33(7`L4WTr9;a)xGa-T8Z2&}MOsfx#K~9e1o}YMLDRR>1i6skER4Cu zGeJ1e3_X+^LQ60OM`tdC5ePVZ^{Wp&^2oO>8q~nEAY=+!3AK6$NjjmAglDO>qDJvJ z-BdNkNee}^N}x|5OBZulm932s9AJnb6?)SU_JLB=bTw|YO282XJ_8AoHG1F}f4M*o z&X#zh;?c_f{rf)t@sDrczPrNlDApx19XCU-;MK&hB{;GSiPU=#45V?G|0}Lw=E*rVFc(5m56Egq0k{Ub(gA59 z@hDJjFB`g0GUku5VpkkCB^5ruP=qr^}fch*w7J>p6o55`Hoa`gqRRp5O8}^je za%PE^2t6G_^5>-hhSn57i2@FOm6Wlg25rECUk$WJT(hoKOZw0YFIc;5`w-Bszz01| zFoZTw-$5}uPdrSs_z@^J&|_1|v3s!((FY%1G|E|xGsipbyweK6f)aFa@DI6sx!yXU zjm94RlId7Q8D@v~VVq*sRt0kb+Rzi$tXV780N0Nnk56q=W7``p0@qHjPUQQAh#Ei^ z9HK?60`WM%CA6L3(p%Fa5(-QoH8fQyE;*yU)WET+fozfg?)!gd<4A65FRr1!P>m29 zgIp1k^j%qJcY}J01A){m9>O0bfNe^K9Z>IpNt-lOsO#_OW=u8KQ(d6W$;k6N_yY||z zq{{V``2X6w6DYs0t4{E>mrA=OS&Jo0RVB%e<1OAGjvYs~V}~>$fgTc;Lxux0G&J3C zW`I6(&YU?joav^|9ELN{VdgYUpnHa;2Lb^Sx(Sc~Nl5H0b~YzoB$djRWXoEteV3|g zKKI@K|5tyNN>Z&-l}oRr-~avI?=AP;_r3SsxBT8OXbNuB-IxVHf-0ZP)2+aU5>Zj( zf=B71$|5>HtV(5i`A4&UO{LI&!y0zl6j9YD^K6+WX@WUev)ELsQxQkw|j)G_YMjDlN| zEP|o`x^?RslQ;Tn0w4&Nb?BUwxEC{YLqnDiWM?u|OJr?b8`DE!-tuGw$tNiQHE5R~ znIl8abw2uyrmYIiauF$xqA?dij*|{C=8azW9YN8qyd?0tQNCl8h3$v4OG;BQWKph(XF_jhgx#w9-TJq5~+( zzia@_Kr+AVCFhEU_#{AM384aLC=@1<@{xb#vUDU>ijN=S%cv(t0@eU<47Io2cH5nI z-dQEQ*fT5up3z1qq%r|F{$bTnllsd}Ctz1P$SNr-j3$0gLe~roPlEsmM8tRnK&pz+ z%)hFCb<30fyhgj0R#6;A;7d^DUpaOcFQ5&7sbQ#P>1hBHWFZC%7xH7ee6n?Ah3T1L zk`X19Y9eFPrcKo3p4VTuQQY)^v%MVB&Ck}_v15nK`}+D!1W{(-Yq+W1bi}vbdTV!g zH#0^N@|3v|f+h!d1VBH8Ark+y?g~N`f~CFj#ueF{saGcVlq=OMm6TzQOuT-f!BQTI zP-M)AoPqUww`$}LC8k@HrCg537FX4`5##OO{r2TIE_Vi$gIi2z=^u)4N?KEb;>S(g zCRTXd2rR{wYF0%nh(EM{S^rW5jMF|?g!yV3hEH5OBIq6U;#^w z&S`Ny07H|8(iwc<10TTND&fVR3k1Mp>WVT$SvEPkEg7!6c^w0{Z^w@0nrO<-2>d+h zGBC-?Ymp1yVf7z3Arg^1gr?@J-TA390)9#VWiFre~J&hZTqyKXbsB z%Gkhn5^jjR`_P(JiajtfA}~CqgJHoRI{M zB>@po4)xs9jBlK1L;_2V8?e!O8L2~?rj=ZLaeNQK8bJ0EGEpc83Z5Fes7vbpH zY9hwOT5{F3atJBN_md4&695IGgi~C)n)at+Vi;wwU~O$Ap|8rJ`D;Bu#Bi$>q|JQt zim+rw5>WZI0w>TFhb6Q=vt`ScicyW3RQ{0yAUlZ-93@jBb-*Y#gDF+tD?WPUNQBp6 zs<6t>lebA$UWFC3H-=qK0FWi0WL&nF$_wE#^NMaBq?Kz3KqUk9Xc1pW;75%(EhHoX zr-C$)Q|@-`*r7L|tz(G1cCFSG6Vp8vnNvEyYw&?Cn7`n$M;{|I2C@*(zGP%-C+ggu zQ1SV3pYDl;_>NAxhvJku?XLgq+umnV9i~$s++W~r>M0~OS>pg(MkN4k;>5usr z3qYb=>B*UB-7x&aWgwbq4eWWxJKllMHL5Q0Gg1IV(^JP-_>I~EFf}Q=4eQsFOeejn zN#L2a;cs;j%A1IyFU zJ<7rq>@Y2+6N>P1^cCdMyVR-x`>V--4uM?`(9|=$N~LX54A83-&A<`-`|rEoxvGX1 za6l=5QBe~yv!oyyB2~`BW8uO$t;sF!=@uxF^&8d$x+XUaFdbyL30p|k{Nh*vKo!in z1rBMUAE|Q4Y=iugVa-!U!xgZ2Jgwa}{3OsveUwQ&;&6rxz@^f4esl-sDM;ra1SJ+z z8I1-8{r&waZkwe>m9VjC#4ip@;y1D_3>hYe>F*tefPJ_|0dSlBfNKaq;Xj^d+LCQt z58U}QMzhYouD@yDr~D($GAQ3OD|<;#}qwUw=URE%CpoxNbDXV|@a zcVeQ-4o%4g*MjKk5&kXeZa%J6}!w5pPE$yj& zsYigRsQ0KEV)80;trP#^QvMPz0y zMRd?FNTjAnO6rYJ{i<}0P8o{h8q=O-aPJzP0`VdU0sUh8gb5U4P(nq>n2kl02CM-| zlh$rymL=yuwO*_Ocx3EqrjEa&X;jbX0stB80je=%xqMg-4|{rg)cLvRzGY@wdB{qT zxuz4SjKp-IOmO)v5Uj_Fp!ADd0Xz~mU_I+Bq$0ZVR~~UjxQv{E{5(~195Jg6d7Sby zJeh?@VYGB~U_DEh80^}$o0ihvtoB@B))JYmsZ1kUoj~H)MAPyWD|&l-@4o9Uht}`g zzYj8UreB44GzEeOeBfm8ys2dss4?xR{?P?MRH_cq0FmV(NWLs8O2_PnN6JWo@|w{K zeRM~dL4mUdauaqDDzLU{&Cu zB6zq`esn$@0bqe}GGQ0(d_@-at<(wOSkg(hJ@#GG?GABotrU$=%F@ggQyn z{|mb2TZ@PY(KhYS~M>?@0V(pr#M#ib%H$JUk=OM(S5L6RgS_?5IMt$6b3NR6i zY#h-0sp$x$W=jc7SJZzHFMo3Bnhxva8Ow1g24y+8UgfB!rp#u|Ycy4HT9e~-)s(6A z)1xT`KKkgR-QC@dX-D;6QUJuJ?{&xCY+^GY-G_k<56FPF>+9=Nc9kQ9CCCH@{A*Gg zVYObW5M9N|NQvqyZltZdDrX*Hc{?iT1=g8dQx#P-sV?>>4FP`+Ht9D?gPtzkf-t=S z8v$Yeey^ABnH*~lvVPNaR7hfUS-1*$H4g=9so#~x#bl?G!TYIdlknM%~&=* zgsusXMSwVK-wOZ+b&bhK^CzR5lX&7ue@a9q z%tz_3u94M+F7hl+F!0y^g;1qC?nVCNM-|xB6~}aAqgeI=(z`dUy~(ZZ9=Jg}pg*`# zOf-Hj2`ks=wX*tvgp2@tQT(w5x>)~F&q7|Nl2c})2<%FvxmHLkEoU)1wJ2dQ{q1GQ zHYk1Ki6=bGl>0Fn*vMp#M>cAPzDjnhd_ECjkxoJs)rD=I}cDNJF6AQb0O zM&PM?%h)#sr4}pXK{OTSf|YgPra!MDT*hVE#;eGAn;uuCD9{!0>9l3+p^fQ};98dM zk?Pas%ydm<0MOsxj~gO5sR(3=1YVO0IA*f58`(b&vD&qBmww>bkz-BmE&aVG4lg*v zU>Il~>OQs1p^coY}akuwh=ryz;fUW#ybYjyOhQFf%=3< zJfiQtU{!{UC0ebc$dE*KbrqOSbqd&O7l9F{EUi1{jg>>Bb4o?gqiCi*>X>y(Nz zpQG05u1}MM(p3>e$^j2Cik}N=W1wBKEk^)VrR!c|+AFT{(t=DPZmD4AAQP@?vN}@Y z7Z2kaUAWFXx&jTsZ~u?E=1h3`>lFk*VmV@#?-U`67%7`!V3Y(YK52DZ z==K{PC((bLnrZQEAE43jND_R@R0PTG5}6wE)D%jT^Nzbx74=5ZIuDUR8XkhkCapZ4zCo;X)E(rYQ52 zT}c#Ble|yv5hAt<_tMvKS!4oHsHElnB z=ERxhE0)`b?4|X`9(&AgEoP6*ZK=?&T3uTflg>ApXQ2wg)`2-nr+v8a_$v;S;x5&8 zQJF&A2gGVFZ^ZJr{1YC+M2s9wq=ywyks&;$4N`5&X;T#(LQ1r?F5!6{luv{1B3%?bh}H~Q`J z7b0K)K(oC6{qIk0)sVsH-YXkPN>w(S2gR_i8#^_4YTLGLz^E<$4*6^SBCcgnn>fHkm9P|>udl?>EUIcZmsX>OAQYR&cA6}NTkCqMa#EnBvH z-}}Dr{s-*_5uyclt7ix zeXJ?l(GUWBFm(L*v9uhJ8q_f>TViLw4Gy7fT4VQ445LE6A|$Gid>g)Iz;N~J^}e9` z9+!B*;Y`W{W|+XKuLTYv$uThrf4lOBga80Pr%6OXR50Y`m@Ec6k5s{H5cafk9w7{W zu+hK<`fIBc2Ei8&(4hrCF*ZI2Kz8eg63B3bOFgA%E+6rb16qm*n5PjOkD8mE2SWx% zB)`<{m3sihX=8=hvc+$?2PvwjL(q7daP8W)mQkIKmdL1C{Y6BB*l;l+sP{ zYK_@R9Gl;tUZ5s7C0<20){~#%f1wR0;c|e{kYTN9OL-bTcv{R#y+QN{b<0Sg`Y1Ts zY;cgwYj1NPz)Dg~>i_j$|FthFct4Z+|J2E6o(Y zD=A71Do+{80cZ#h^=}w$>w(cHh?H8#iDRKt%8S49<3~V*dHc9{%5~V-bDOS)ti)HD zL9IWD-b1I7CVK5!8+m1TL=;CU1zN+%LxAS?^(XUL*1Yxqcqt7_Mk^XO>3UEOh*)Rl zlz7OeZoZYFL-qj17N=;K7zN-!Mi7&$^DKT7{g|K5EsrbN3m1(QO-A0_J@n8+df>+7 zm+@b@2gt-&00TWeJvd?-TiJ#zFTD6dd?5PHJK4t!18>$56;OyC0Jcj?O{=lus>RBM zr>GKW1|?ewj1Eg25>e(NLoDdC>=VR|r{i{kjLW!7JgriZQ;mwp1^ewCXH0Ce`wWEx zs4-qUeM$pUb*^HjB+v!S1HHjFzy8gYpZi$zk8Wsg59_~jYI1r_>s^n%&9L{5#ej zKzTA#XZ0bpOhDwKUO%AiHT>qyn?b>_SQmj%mRd;?v~v6!Ci?H}r-HT7tPv-#9+$u* z+0$TE^^-T_x)!B4t`s6Ca1@@Y(MPisE3?cn+gTLvbcIlGX(p*FnIH*3tfq8<<3^UQ zxZ(JB%|E}ca~LwEV(NN&D2`!#VA*Y~3aPM*)-aVGb0Ftpn-InC^FIAM`teiFp`k+u z+wX~G$)#XbVO!9Phm}jgaGPwr{8cQPG~)VKLA$g<@+}&tJTARDsmL?WYJEhI$5J00 zR0Z>aVzLP6K2ibPK4YbHG9Vg+(2wfk1Pz*=%K^Eq*U2}m6U`vNHFdvCl6cuB?h{1; zrs4!@0)XTS+@hc6XIL6pDfjYqubz&z2gn5YqSZHU+Nj^%f8c=EYKgdoVVk;43jO3y z{-k$7{`g~mY#swmV!Bos#$vU?QdtoQ*V$l4b%8$mg)?VBfkx#;dt>&&;S_oxQ`v0)RP{m(D zY)uJVD9+U=T^cGRDvX}GbIXY=6CO3? zahoBEju3z4$8QdY!VGF|W<9b=#@p8&`d5yWfLrp+Wyg9*`KWB~DIKYs9oKWKi# zu-K4}MQG1pv`4aUC=UCM69;4xjmMG><1*N3P{CgsZ@8^u7g?atcQwK@PFDl*I8)W5 zrvQ|djCxQox^!jO9w6}DwbC6FJxFtfwhrB9mv779$=l}*cA5DfMFQk=u5IvhN51u$ zKj@p^HqY!rnLvzJ5ssZWE(iHwCe;{)6!k^`qQv@b5rgPddD0(*72;e(bHz777)KNW zEd@hED=%Wq7P+>=?$K&fo2DWZ_H%p6f+7uN#dt!box;Q>A{PEKE@Dxx|o{bRYWkn+M z5>~1vJca=H)80Mb^=-|?A1RhIa!*-)%g6Q~BHU#$ch!)@HSkpNg06@lH1GLJAd z1V9T!4-j)isoK>b`~Lgu;qp7kIFsW!b#J{ilw+xR>#8?8LFaachf(2GB92Hg*)E z?g5H2(M~EF9q|a6D^AbjQFu+Kn6h%8Rt8xTR<<99lxALj5^He?r}}wIOO)c`Q6Moc zW#CJEO5I_tW*ipXh5Ts5bOn(mLJyOL)9s*x9M0JAC8_ zN|B2Cptmmh+2Zx=<5v;VR#%yD^1>pHCUV7n&OyQ-c>f2cw+BedP^r}J?rxOX$AVwN z?QP`gwW;ggdrgP`?(hEY(@#IGm(U4XB+W4ormG3YM?cLU?_Q92*b=OsCi$|230@fgxkp(aSHnD!i>} z(Gwdsx2zdDT>AnTmF*8vHB5@lBuu2$Ot-1M3)7g-n^1W-T84uk5 zz+HFWg+i<*S@dHFjhpgXDevF>&EFh8bV!MWuLy5hb|?{CR5NLlvsxszS&OVlK)-|| zKsYIoKaNo!B9GmT+XSkzMkNZ$#^A2LzU@>DKJ@kVX(R(;V(<&U@C$$RM}PF`Pk)*W z;ENVL*LseEiY6>93roA0nI+Gtdfncu3YoY^V>Tw1cVNEKT$dJrlB$X~oChxFP5oDE zMnLJloG2b=Vd%`>=pH(?D&|tI;0Bw{tY|&f-LkfSN!NlHLYPRQ6rHYO$AMWO+|YRv z2D1*{nWQ*n2q+{0hh4dZj__=Y#7BZ&VBY36Z~4%pezgid7?#QD6jbVLY*_3h@5C+cM}%?WuVX1nFVI+wr}6ANszm% z?2z#Sk*!fb@e@DM-QE4CfBL7+YNH0sl-V-HD2$$WC=AE=w1%&0*JsMJ=3}RRw3rlc zkfn&mr%9^d#=vp;tCYd=VK*#ELQgFi*w=lYmb{$$uhOY@*7IEp=O@-p zCtw0jT1jADT413*v^@~STr*K15Q`VPg~+sgQXcZQm@>6ZI9X-agibg!L(`a2@iQIa7#+hCMu z^&q@4nWrWe2+^UkD{qXW5NLh3-}&~p|L_n0(>K2P4Hdu+CLkG5v5;F8Gqhqw$SRO^ zWz|_Pv+*~;Sx{f#n0FOmizm5>ut|tOtwb#`TsDgHRw+Y6J2#(x^|tdl&>xO-8J`z$ zpFZ6B>3z&_<0uS)8~6q$FX9+vV+sR-7;xT0I(afaCN?x^{a956Ib+t6^%e#sTgS)d6wWh!jz=^=|c*H}~ z*=#FwMW9o4YHF^<$4uu@g=LfB0@EkI{oDWd)|XyP%89Y~2(GY>0K_B#9#ywW#~*Yd zCW`Ouw1WPNzxa#Tav6SK{Ag+fK)#66mG$-QF#JU^r`mct(^9x3&}SO{{_p>uIO)}? zG3~9@gFzUhj%CKBDAx+P7gIZWNG(D6ks%?d5NfcIh_3;Ew7erhpf{R|8@-Ii(9xn$ z`NwY<*mnQfvx~;2{xd@s_pyP+e|4mF*BPgT!z~0rs)w3fvl0d!sWyf@Rqzx8nGI$b zd2|#oov_pJV#Nn#iU&~CMa_UoyKpj3VUS7Oml3b4;!3@1;{ip>utGe4b(?~R5<6bG zgk8S(6aL}~rEw4{0!$AC4NXI+Wtw}>HY#1z9PuCn3!DeMV1e%_f8-DVwNEO3zU2v@foLjSQCrLqojx*^rt$% zBpLbO0y^tAjOCZ_KDWQuO6V|QWAU+<7H&Vk^7ChgPUUyX@F@TaufU!3Wiv;&P^=A@ z0|;>dg$$5{ln3+C(xM1ZfR)|>jv^J|F+(DiM@f}x2i!`xAS(%lODn{qfN$L?y-YC1 zMuXy@m-Y?k5vM}}cDJ4?LMD{lynw{EI*Ti?4q5tGaThPljMPqOhFMCOHJ^4aQD=#Dw<-b3Z2bMzeA>Ys3xd zD4x;cjJ@2@V(C(52Aez9o;!2jsR71wUF61^n=#x#Q_GiHj&0Xl#lk^YDyg*VdxN1N zk&1pe05#xF%Ud@K3z!x>%&%4+J?aybM#>hLOBbP=4I3KtK`r%Vh(mEg0>ZHcHU{x! zQzZn)=8H1HTaH=_Osh>mwids15h#hY#Z@_5AB+RBmE&;u;xYotLE_a{U-h!6Ol7Ay z$ko5W0P-lukAC!{Hdc6~`l=H}mcfswoZ`~)t1?nuiB{A|*EfIu&;8ubSyYi~TyE+G zK=F8OqQYCXPULipxsE>Dv}yC|HLDGtXsl2D)n9$)@BVI&!}Wdd@rD~zEGMv48~3pr zcS2RvVU|@Y@C`4|1t_m$&TpCRsoxicUI1SkQ8mCb;*@+c?{Qp%zTz^A^Mg&x=AZqk zC;D$)ZF|Ut6pt-eQ^7m-&;RYe?tcAfXA4RjbTdp-^VmWLid=*>!;_j`_E-&JFu;~D zVHEGUg?%2M&@<0q6+m*G2}$61GI;_k1u2EWD=iebo}!%R9#($ARa{Am+sL%&_|U!e zZ)qypgTP{F|5*6vw5BWCDzYg%y_3mY7QY?THjrvQG_a8%*g#P!BLBso|M^FM=XdmG zRW~IQMP#1C=r5E2l1HTn2uZX1?Pq@GXR_R}alIC6QnNspDoJH9aPy{3jus{N+Q1N* z?ziK`ISVBI`+wjEHs5l~CqMbg7oLAX$LEs`%a$)w9j_g0U%=8c2hI{sbv$1H`l{^u z)1v#ybL_0B9$w_BJ=6Vr6atWO<9eA?7i2?5t9$8f;C=X({SWsXj4APnx|QqC^xe7O z|9jdY#-`TG(NrPJanu}em?ey!v^XU~KODQJCuf1|Py{K^$t1vGRDjii(Rnd72|ySq zA5Knkh|aPcn=ZuofZ#>8Az&eIVx^q$gK1_9Yb>7VD6$iXx99M@cIb4lvre8=wgZ{S za%Br-cSHz*l|-4%ijt%}-E?680k(kfC!ToH>gmV+7c&Lk56+qP}jJUH^ou2+s8J+g7*Mu+y=Lu!1ic+e8B61ewWfO!(*JUQcXv0VKop791*ygZ zo;u0r<^KME{K6Lk;$;It(jSm7u8 zQsR^yE&7Gh656CRN$oBgr#MCVjW4dyju9maAES$be)GVG-?`_$o}+BHhSJCFg@i?2 z=es)2fBog9XU|tgR@Ot#@r8DP(EP6SJP;R41U$p|aE*{+qx19RRUlFl-((}Yj51D$ zou~N{`nlox7CCbTaLkzEj~TkCSISjhGZWVs*XAgte3P#jt$|w7MQ*71_&iQ|k5pTu zB*_p#KBmh(LZp@@b($MJQW@AViD^tCAqmX8N|LqUNe=LY2~;YovP*AR_TKlt7azvW zcb)Ao|MD+iJ{ijNUr4!pw97oj8nEV^Bft!WG7AeN0bj6y3E0!y!((h<8ws}R2hY<~K~h zh*)F2RfW|{&+I+iv3=jd`czE_WknLPP#XU9&67?z zPd<%nQRnihl4p<@IZ}pHxTL>h{yIM56#f{3mk&T0$ntT9zgbE}NzP|zM>D({J&d^X zaj*#CF(j}sY*kofHOjUYF6U_dmX_~4AKNP{xM!6E^~hAw@)l&)6xjv<_uqfNIAi5? zL%iuSff|Yk=+dmFw3$7-cklKMUE0G7uMp34voxm8-itMafBLDX`gZJaEL*vdk^bEl8@WT%~eerXj`&?AKxFizAVnzTQf>j<$KJSMI}3s=i0$?QNadrj+mRb%X) zqhSMghl8X z6M$q5?x?dfTTK3_o_FW6TQpp@-;3rkoYS9f-@fhj*Y`S&{lFUs$Oh}sJ7{{)QvC^3 zbAm}wnLiFP1~sS?0);TJQ&9d<Yo*K+`G=j^VCyMQHZsT67m=vvd+yy^J)7>AAR&=ANv@+ zSudt0__;KkGD>RJty{;4sN#&*b2Y{QBWOx6vHuMGaYR>$L3#tDWHx4Q7BZi$AJ!PS zPfrjMVnaM)J}@>0hI*?&S#;otLgMj7LBj#HXBS3EJPk)G*vi|47e9}A+T{xd{>As~ z-L&fDB*(suWu>1QpVu+4a_PV`+vguXuEQ(SmQYYeM0rJLda#1WZMs@+QSqE*$2<%S zQSR=M{<;)?-wzSXX%xqru-`Y3* z@wApAq9Xp#;X`WH)6=WOY)6H%kj>y)F4(zi=bqR0pa7|a;2p{^o~RCy_@=PcrbNdz0Rt|X9t_xx9?wRD#h?MXA3ID ztf<8O#Q+V!#w;YGF#d4QP+6dup~(YeepxlV(NDmXOYxKy_=p#Il;7GVp|8Zs5j^9b zGW8nMuktZhRP=f@lzdaO5b}|PE_&BB8J?8mxDuAf2{)fL~-&{aSSI{=gt_e z+bJYZeL_el)Vh{%6=~IUe#vEBs zcegnKL1i$|>1`=IwJB1QnqI?=WK+n&s6d>M0A!nw;HJh%Ot>{RY$tK05i~pp1himaxfn5NlO(n1ib>sam{LHV zgZlt|P+ag|(iHGlwN6jOElDKj&nohB@S0+v?}1CWcUwHQYu=IsSQlQB#3PPCgwPbd zCW4qW7;z;OhYug>>FF_{B>M<-=E^{PmW6r4RNl62o0^h;2ATnR{1SjN+lw1pwrue< ziJZXoS_?oZET1)xgY{O#2Up_jae7IHAaO7l(z{Xl`w#5*>YOpLR`*mK0uZ~*A<|cU zLIN(r;$;P(bRMN~3pUbTs+u23jjYsELOg}|W%CFA#e4SNdee!?EPFL(KB}J~)YUQ2 zIe74EI~MLg>gZ|br5`XcV2lVlkYSdL0jZy~r;t>bpfo8NcB64>8UD%^%~Wi31Vy8V zonM?=zZi%K(F0KTMHW=B7Nh=?31uUIpNl+;?T;qapN_^d%rpMxnIzQ*sY+{%RtcXvg=#5^-d0I~wXqw;MI)$_$6`$@sQ|K*o`h{4fAKk!d~ z;NJW0{V#v@U%vM2*Dyi|gV4~Kkc1*!yhvl4X^6IklyWLbcnVXCEY!GEo`)m^vqeY{ z)Rmd19@V0jAEy7x#REU~$o}OEy~~z~8lO*#-ypWR`RJnkzjbcj#(@Plmyu^MN5xr; z(fmE#On?ATIk(ws_4oiux(I#;5<0#oB(7UuupBhQtgyA-<^K#_1E+U`gB^~ios9+ zv!AxH)+=Uqn8H0VSlqGLsr=g4(5Q%kx9=TNiHC8dE^QO`ZAt>Hr!Q_J$}rMv(hwx1 z!pn#BSZCYd4?XzCJsXadqZ}u|=LsemYHFX?^v#8P|HILn|J4w04H-46Au00(3f3SK zg2BLFVNRbw9`p}9xCdkoZMPEdo2OnWyCJ>`%OHw%+%Y-@YnsN$K}sF*`6zEgyO%Gj z6u8F#z$b2?JuD!)KZXsV{o`pFHDEaF?pxpd*6Nk3{Jn+nW#%Jl5?nQ0;{ z+q7xZJKy7-R$SKJUg3yk7u`i$eEw0|;A8ZtX&Hq-g&|=M@PYY)^;!cL z3GmdvfuZKx*Pi_6PrkNtksb5dN7Q0ntsjDVw(az`v#oaw&R#$1c+mHyk+&psi2>pEU3zaCn4VG;TJ9Ac*?;vpvPuWO94!O;t|R|h5^Gy zPaXQzZ#*puHUf1xcP+_UeHkx1G7U~+W0|2EYsx4>uTC#81Z}| z1Eo8N&_H=$KZat>D;J&rm+yJ?t@iv}rGf9Ljo8xGbh!1z%cr}(Yp8?Odx0h%HNmKX zs%Y-mxy^b8I%4;$ukPEomw`bxf&&dM*iiIEBHIpMiPB-b6nSTg5+^7smc9 z%09Gzm^~#@teiEtOz3I=$LQUBOBixh6@=@gOpQGE+_#Z!^r8pwNs-Ac09TpM8UaX( zv7Q(#^GV(tmYL#!Z$c2W<7cBcc-Olg>F(~fvZyy88{S9LZ%7#D@A-T$*HQcMx`qIw zY~&g*irG_iAxVhgr_tv8_Ms0yvggsa9Guv@jun@uz|-8?-t=nIsbgmses@!wb)N~! zK=giP@sUwPk9av7S8Q+=$Pt0`xT3ic@#)XNyaI3rrYAsL{xSE_a_EQ-ml$l>vc*`?G{}oDy=Y`ia}Wb#CL&>(S*K|w!2(e5-5|06Dcd_T z&-OQ9E^9h}i@tUa~z{e_;(6LA5_BW`eyS!g0<$BrFvZkW)Rm#1(k zNurZSh-=D5<5PeR-oyf2S^bqNVn*bQM$LqE?-CLbTOlw!JVo)likW`#dGzYq(G;if z%E?4av20muNTTL+T-DRbcxI~rWSy~$)0LYE#U1Ze?%lKZ_{rn1?0kj7SiNf1&;Q)d zzxLXzPN1-oVuTQ4aN`QCLr%P8pe>{|s>(rHHU0~^! zTL7}=g@G~G={!bb1K`e`Gdp+h+JE2x`{##$=!bnb{L5ed@@GH$Su@Scm#^@hcc;_E zx{$&2v18g5XDJmu_385iy^9Y0*s2$o?K*Pel^haJhQm{y)&OP4yjTDAz{CIhz_Pn1 zBLg^QOlc?u@MX?D85vhZL!>LtaFxfS3{MX~2iYY_b1M4)cTx9ctv76+2uR46E6{Tp zAR!kKioL|2MJrQL{OOFr#6d8DydcUc+vjg8_s9- zR?U*dOCH_wsEM=ZpMU;ypa0y8FT4ourrYeorCLm?niP+jpw^BXyUu-Z^|p;&MierR z_LyPLafeN9uYG6Um;cSFcmCSog7v;ZIxz}i&H%uXQa~KKy@l=$d>2MWKsqetQA9rC z5g{!Zu_2evF0*E_MI;n{y7C+2tfKK71pt&IUc)t0Wa;wH;zm@;Qz6vCnX@Mk zzHiy{4=mnSc2QJW&M*({*xb~y^Qoa{fA!Sc|MgJEQrip0OCls0IN^i$+i?u$0F0;R zps*|;HliO)l_Ek7z93vSIh*o?O>dDg|H}zRHMEDGuYVLZG<g$$cjxY0HJdDJr$2q?)=v!0UwP`@f7R5|nY$#$ zUM6IJ;&gMoiIhWoPuzVa6jwfDETmd}+<0dJ={3s%YC zP{(tBFt}*lnOlE6wB(o#HhOf&jvdcf1aR!!xpQ8#N?M^XJ*7hk-bJQ`htSCtpu#`+ zdch?QcgR370ae5__Jzlz@_&TxCkZm-GNIz~)|H?72XWgR+|`Ew5kAl~;1eX?3%6a= zIzUBdQahRJn_K`Y)+B$uN@icAcMPlv#GL7|qmFB}{IhuJ^8e=1^{c;jp!MXL8EyPj z^+}J<3!FVU@2kH)*tP21hVN|}9MfRH>r@s=98t`40XSIaK&n_fp^V*PR~kPROnK6Y zzysS>d&5s%aIt43_7+B*GVfW?UmN?1EG7s2uA*m1R>E!NF~(iDcCFbS(K0a2E*ZBD zv>LMO92jr;XiBbgMF1)mV>kVm9fIXyQ$TwU9BkeD^`)r4N3X#R@xH@^MylR_}f?b^A^Mq2M1d9MmL%9?kS4Z3BxYuT^7IBcG=ZuWQzk%4=b zO!3srneYdoZLp1b&;e&)#pa)47y)^70wEG5cA`ITT{BNt$vOFxM!IR!=FL12)X<@o z-Yz~T-rL(7Po~bzEdfZrRU9-z=$toydDpqF?SImByg$CzG#gz@Q`^B;y1w))Cm#8= z!6mm|ECVz;+^e;jE_?3H1$&bQ$S85ws4+yvv?Kj(`)zaxB{0-^%xq6>csHK*hIsE{Du^P%Vgy|35_uhN2Q#8xSqX?m%r+kFc=5C*evfSxt9Hmyt zC-0fd0zm1V>-f$`J6`(4RC09kZ8EVOTADg{eClZH>eqUHl8r~3Tk1B}H`DKJN!!T0 zEC#W#WwitBOob7Enz9cFgB7J43O8gRRDOKJDqvmgoPc|H$~ZV67#2!FeTab#ZvMn1 zHW{b7Rv{&)h@ACPL@6bL>3&Wr6o6QV@E1~(uy*Bx{e-(~hKtXi6c)h8 zrILa47>_~!x^?TeY}sOZx)fMja0t4Jv$`Me{ViTB+u{07g>SA3fcK^LeR(;I#uOm<`GTm?H%HGvXG9tnxZ|9@{w6wLH*fsB&Uu`|K`%Q!X!~txs=sfeI>-t|m z-TCT?`R%Ro)^jL+p9urRSgR@uxZpP*q)r9R0?LTa!xiv{K@&Hi7q-5fLhH*-hY`W$ z4fk5dP+uJbebMp?z9Rry$p8=CZ#N#(-`?h1ZoP#}j)*L|d`_P|?KMtzof1bxnEdVB z698uJ`IDXB_(=P%FTKgopJ<>$Z(Dfihi~5bTYZ~O545*2RW3kbsL}Y(6;FY@+W>ia zRL~xn4;d)%kKQ5#py={|X-S^(0F;fDmJokUSc=FbJtbdToZtRL&uIk>wM|YC&RTFo|c|`=MAs-tzPuW-QFRF|4_rB zx*b9ullx)c3#9Xh?BjGcN^A&?d7uD#0e3{sSct6%9qvGBRWH?-MF5MS>m7jeu63*7P-xsXW=jbjcq zdV`|va~-@4ans02Oos@B2t^KJGJnWWnm!Hln{Zqa2GA)Ch6JWT-O~4tHCVN3wJ9GP zc5l7)Eos;oYFDW$PYj)hnr>RN#`#B4Y+LfZ4Qh)6MH| z_M#F=;NnY_9bV(MN_>IhujEI^VjsJ-xglb@8*0u7fR97${nET||37b@S^q0RgGPn= z?_IgG|ICVS99h+75-27?!y=C^Ae=dtn6P(*3E07}2Qg`Cq!4dN!$!ml%{&x4DZ~h@ z6B3vDnwPy26un#NEYq^=Y4@HU-#zTz(7oQvci=99lgmh$oMaN)E6S9pR5Gj+(^L*A znzv^48XFdcbD~YRn;Qb4S4pZzDx`*xmPcKoa(VFFbN zR_npeB=kN&z?U49!<-F0v8uI&Ze}wT{|m7g;%bAy397asVY^+%o=`0m*IQ8w|LQ>S-nVuy$k02>W*oQit*{_tRh3F zAuQ{~esYVo=I~f093J|u!5zeb9xA2q9-IliJO3|w z@F^T>d%b`R&c!;9cjZ2Rc$n*cBDtKZMAx<>CB%B(>(jup_gv95V{`02F!;%|ct*=l z4hq(v7Z`lu5|!nsB1WxMOp!kI3PIm-8Of20&-6U_zEXzwRNhPRDzNBxJK};MHP@pH0X{^+Z>t0g``2QFHLn!c1onYSg9yvk>#G)thUHYV?({R%AO||6sJxKSq%VNRPbzP>p%XTh@Ucp!^WQ zWr5o0=QWf%|9zpFwpTV+>#Nl3H%-5<=`TD?{0ZeG%xR+|2zWLwq9HN6VSO3x+|R$b zgXTV^lWUIQN>Blxqh$Pj!_#;8vW83J7Nl;qH1{kfLnuwW?k5s^<+6gN2y?LJ%Ex|K zr1o&Mq&|!nR_YIC|3L5j+G@dE5|x}Aw__-|ly@4a#zBW2rUzSktzkB@O0UugyPNl; z-(|vnV$SC<-^RD@yT9A5JBjQQ=JRITx#HaymF@ z-#KR7iRGoKoy_44ox^L!4dK7^wFv-o|4`;?jy;`8gw%*4~@_lgHUx@#^ zEt`3WdQ5sn1D^*bi;BVPGMeSfWxA(I>{uQP$0m6-=)))(jxmRG+D}1!EQEpFrLtn3n+o7XA)5lqCmHB{Wf|OTEaTPk+4)42LE3(I zP|6jOp$pQ}Q1ENlg%8sY&bE_+j7VHwRd&I96;Q41%3q-K2F4w1lO`Z*<36gN!gZNR zloM!7A0tLn{vj3B)K(;mrQkVH59z5(Mwt_2F+TKmV`LITM%kbV=SalV+n)b@Al=w( zNGV-_k|Ecj+J3{)ZJ&;r{}Wr=V_W63-1+Fpe5Fo?aasidiu)NKR}hzr?L$^V#rv%r zkD!jnALFKmB!LO#mS|bReD(fvNz*X%PuBcGtf_R_`gVRwVgN?H<~h6(T*t2v9_#S? zduxmaNa!aw6id}}J{G^fVZorV%@<76d~3Sg6|l({Be-ne0XEB7ZL$@oSDmY!WmPy? zqI?HF0GiB2B!EivXTXPsAQmqH@}-Hje3=N`ar94BI59FzD!d&o&K@rSTQ7q&?cl4= zf~b;sTn!s>d$xXJok1wd#p5HD=-C{OhMAntx^q2r*$W$zKrC>y)~qiDvMIJ2$k3Uc zM2fYQbEgK?3TGbK*#U2OYkjQO+KevSPT0?#_yyo<{9XW!ck74`7fFpDY2w95a6)Iz z1}&gYp-b6&*qC2NQ<6z_jP6+tz9Q0Q|BLa~*$+9FAobkr7WD<#{=L(KkqL4eL&PP( zgL6Dvj|MB{&LQgRtsQS9#s)$tV-K~+nQe-J8SJj{1GatiRYdV{3T-@_iT|R<@%zH$ zM-BK&W+vlytN|cZawR-_Pdk3pxGvwNxKyt{{^;ErUcqF`bQ7+KBT}b+ojQ+?b*Osl zO`_Fs?ed#y;Vbo1CXd^(+q>xVbI4^WFgoiyU^mfkqVL;2xz!(63(hf%^P)fL1j7+e zG9n}4?8h?_1^DM;@u*EU4b)+ ztu-s+}~SF z^ip{IP_UM>M)*d@nLIknhN{62Uy{dHA@e2S11{5EyPAfMyQp~T@ZlN@rdTaJHjW9R zowfp^{|f9ZHa69By0Ai(egbx_ZwLa_nRCK(S-4(L2vfqUGL(aMc10>| z%25?ELPs4?&dL2$Rm8{5haC8&`0w}|(l@TgdXRmVfNwTdL6)vFrYeeUMLAOVfT!&_ zf5Qz{Y(Mfe0X}3Jx%as{?-`n}p8~%gUnis45yzO})+Z3T4S-FT*Ixyah5%E|?k0Xu zw{G*JaDt}=!Lj;|=7WFRhFECS8VC*r0>=%xis`!-^)3TCy_t&jPqv|K$^;M$)%-w(@D(-Ao;H2bxKSKGk`3-M@tt=#0z>tb|2;U0#leCFW%TIIFi?s40;Pbl%!4cTBe+#P9MQvfyl3pMtbEEBjQu=9$2BcM zKnU!;DWe7G>2kQ4BB{C-(0xf4wYo>#IAr5B(C@m_p8s0dF}3Ni@>e*LP6ft5c=)X> zj$nTJm)eZAJ_FJ$z>dKXi1s+$ptM*);bCBHJx1VN$G3%p2i1{elFt6&ahD!y3jHPD z?nHy+2h&YK4P|E5CB>@F!+RpWQyDDHx&wNYs`rX43fV)ey+#Y}~k61+@2GH{y4I)Renj(I3Rw ziwSx;R(c>?y3PEFfap=iWjnKGYH#E&VMjEEc*V+Q{AjPTrGgj($jYKJGFLQn*N~9p z5b{LJ11R_e!S&}Uf{*v_N~`NrrTkTNWkq%2CUU=Y}OB( zD7yuj3w^!+^;U(%Z6jsQLgP)8$fA90s^anELeT6@75GW+I79Ilm7LMe4**xd@$*U3 znFgz>xoJLp=(P%BRK10vQwj3@WdT_(p?I^Eag*QY>xN1t zE^wQl-cq|`7sdW2(}b;?ia>c9?3$X|sKeg)Ama`NSw>k=#2~iD* zoF_jbgw&aUq4VwO!L#B=dT|2qBjac#rZGAh#r>#f<)Z8Kte~EQ$JzE5+nqeX>fZ={ zj{76_GAOB(j;Hx)FAi5Ptqd-z%ANZw=;!7q4uLZx?g;b~athSsmVHloN?j3(*k*ABmp16qy0fQDK)jA0KMtGjv*mw=PLGSewyOd;B9{j+QTOxmxad)NT0x1K z*~xP^PmZUP_G)SROjTk{%b>7yhPlsr!(5YZTf-#-7WQr zfuoR<`&3m#Yy=7fZPj(@J_4c=u-VcLoK(dQ{}Q{ae}#daty^y616jO-Pj?Jc8wdbn zD@}|mD;RGNTLFe?F7VTRg^&&SBY%bsZ?ZUF-8!mST=!)2c0T zfaToC^BV}7X}Lr5(ZGVliNrItiRk&%nwtzt2rc?FhHUdqRl`>D+army@S4)oW3$Npo>vznjzzBT#F)SVt4czSC8@GWB;oIizd9u*L640m zy<-sQim2@iUzMke@e>Ep$WO4-47@5ozau~7>HxEc zZ+t*YM-{hdICbmfVqQQlN zMtG>AnVw&9Vc)o-`O`=Ew4bSksiv`tu)Ht$V+tI;x0Ll+DD$@7!7(nxcLEOIUfmig zJGnuSTFv-k9}#8P>c;WG-T^c%r5J)JAFr$~%>>S3&#i55?molePX`;*nSptznd2Kf z_rY+?$Gb3QMke2zhj2LRO@(IBWyt@cZfj}yAJo0zf1$2!Xz6InsQC}-3b`CSYA^zI>;IrG_&4hQ|3Yn_)Bzfw|MqXx=Ke+hjoL&zMeDy&iv@Hhy8IjU z=0tR4ToUMq8DGr*pkAM`)6vz{3{C%E)Y~Prfy{)od58atdb@?4or8n5dguS5zRCOM z@h$V}Ap(xP(Xkr(|EXz&?#ciEn(q65Yx+N!|GTCg5H&sipPK$+7V!UIHd9ws6#cK7 z{_qZu-}671r@x>gVa3G!8}l?b0RcX7@V_u`RR{%25DJ$53-jhkXlPhyaR25%n2{gv zrUJW=CvN}2e7$=W3jb=(%?m(QnvYadk;B0v!$LF!90mCg8i*EvxI_kE{&Vsrlc_+Q zklYYW0#G?dv4<#pu+&wsQdS1ABCat3ND*HFsQ=u8ILHtO0DzK<1VBNYk^Xrv7x`bG zA_HoLv?E7wi1p9rbcYEaU%c`s5g`Vq%X8CK=}7Rhx^5C)V%y(Xz^ow zYTwh-Hw^$kaC~mK*`F=*1*sk*@^*6I%isr3gvfXH4{FidiCHn&PuEbVl&=s1PL{i> zD6h9i*~$x*vMZnmCx~^sTWMmv*Rj5IRl?*VS-{1pfZYZ-YJj)`nm~{MWfii}6}*W7 zR>C%YuAsaEUuydPF_*pLG;a4oxIiZ{se-QN3@VFNbDqzKITOSXB3!DXXQ2mM!ztj~ z^{9)T@r_Q-K?GMPIE3wHglGIQ(0472S`_|RH%{eyUYZ|4E#`WAYPRtb2V$uSXfXk~ z-mODOIE`DsL+>s~I1B}W$4f0QO@Qoea=4T}hiCmRzSr}05x7*J7Bn6S|p)Iz74@PNO z3_b-SI&b3aq$dnP?eHr2eN|Q6&l|25kGpaB9sF+D3FvuuUMT%5u=!csd3AL%U(Uqu z>|^b>&4C12Agh@~las_HQ~p5nZ-8SFk^A5%1Nf}o^BuH-^n@ZU!Cwq9d`f9Q(Jg+5 zrEc0hR;@c>+|-m7$7be8{1%d6pQqA}Rz$iJE~olTd$kcK@w`;C(<#^^z#BMFsF*_I zzMqgvX~Gjp*bJ+Nv-@~82w^26 zetFs*aoWQUzd4D}Gf8>aU(z|}Q{j;;qm%0P)-4>VBWP~V3ef?I+_)G;;sV}1^Y?1^ z&{@zQd|W;5NF1kVeR-`i)NHpyruvj2N~zHrq$A>Pb#!9IlK3G;G608p_n{DA41BtR zpPM`nBPj3%Z|R5^&-Op(5v>Irw~!cn1s$o*$zsqmv8p53YB!E zLHSUTtP}Q1@$}7?QOV_DZf&7!2RA%j&C+Q@NmI8^?G;B_tjHb)$NPMvbBvLZuxFy zSg1vqIRV!T|FA0digTtgOajifdKFWI2WgVk6b#qIE4mwY)*`64`(Je^1l?iGN=E|T z6Y=POEUef0f>tI6vE)z?>8`8CdzK;aQnS-pVMZ zc1f8=DnoD}5Yw&vx{&-RA%}5>4TKIH1Vcak*$$ zrH-yDc14)jM>Y|SPG|l;@E2L9Hf#4QqzU->NJ< zp;G6%U8m4=57Swh&mJipMvwP@AqZ}0wbfiLQyuX&JL(qK?)&J)x9)32^Q60cGa81) z9y}to_=kVYv>B?IlOKtn?Y!O^X()N4O>5ptK4KG|5&evy8iIgBdE0rmy(_Iatxslc z)}?R1qE9qcj{eSPtw7ZQ$zgTgj{_0z@>jz=CJ2%nCw`If-EZ{ogfARrLduge2HFSh z38lUUpVv3LH?cnr$F>)l9GE0+SJF2}(s9bojLsb}{&1<&gDvl2_}LqbIGEEfWQ^*3 zmBUosaN=eNb$=HJf( ze`}-Yy{zRi{kA);YV;MkbaQ?hccWgj1^LD}wKoXY#yT|b_4nNLzGiNn6N&BcLHIH zTFwTw17Wd`l5Sg2HXCMuf{?>Odb~|=E8#vKY_la%3r2#L`v-mtf6AwqTzF;rpk zFIW#<1B3m$KF(~vaPl@kEd~z1^=M?|ZLL-|@)Q$bqw^9|IH5RFra5u_uxyX!nD<5? z<7znz6lf#ENNy}`yYSR=QkVf|g-S@5b}0Ty6F2q0gR zc!I(ALT)4uizW|LMejmO50X^Hg;dCF#1lW*eAh8CuzlfK1lW>|a9~rS_vo;`-14by zaB%Be<(ybW~1 z^Skb~&hbCrORKXE*@mHl&bawSTI$OMwk}l#b5Fj}*2oGpsAI&8DSUsQ6po^f$6{U@ zl)&Osnd0r;cRgR^QKp$4nMLkdm?r1ttDSt6Qr>v~%XSazva}uNk_75d)+M7y=;q$P z`G(nq&R0Ls-J=jKRE13%$KP#}@YY`)(6jFJyzM0ZbV*=9yLh;Yuy=W+fm6uFKB+aU z8q#SJ14sDTpJuk>1L9JDuC^AD53xVF%{v0yx;+&AQzjg+gNV{#cOK{m#7_pAacMgh zYaS^s3qM}B;n9|K)h-w{+;)vlk>9c+g4db`-2QNfW@g4m?eXfp<`xOqkd3RSL1U3# zzF8eTJesPpW$y~lsEaB+^${$zK=peS#|xu}Me0~P?oypr@>0Ox&rXJ02GURFWh zld!#5T7m|DYpIteK7igq7pbZxqG=nAu+gdcXhZ^kbT*Hzdzlw>%!SzvsQh99*&g#! zTwjq~z>b>ZtUsb=@P06CQhlhp(KjMs|12o6HKjWVu^Nz|;Ki%?@rxik9G@!^MnmKJ&t8>0)7!Lf( z$G!WCv4v^RNsiqKW4x`%U z0|hTwrQZz2gja;PGjdPo)GL-rbSZ-}4y??w2_?~RzuSzT^IKf0ni`HfV4Y#y3pA(D z4W#2S+4gLUs)?aWlKOEH2HwM`C7zOS0Lr-%aiZ7rQMm7^MP#)H)Ozw#p6NA(t|hgb z%~U?3T0!WhXuE+O*_Vqktt~Xuocu>^>H$qW%P|nw9gSt&Y>}O7%6`-a0khGo&72;^ z%=D>6R{PG~hp}}Zx4o}Y49aJ$1QKG>hQ+_vUyZ#Hu^j965yVZ7Y1m+j1z#ijp4%zK zYycyC?ts%^opavR?oFRcmEEwLIq`c;Dn2f!*CFn!VMSCkgJw7!Vf%XnL7Bo=7PY@e zBj3N^O5&oe)|v|HYeWAn!>9==5~;ZqAaf%4!4%*E;@4w`FR>3m)W<}o$g^7YQm2)r zE_(33+o)EWf3-2^lXqraHxY^9H_YpqL4LjTa-HNr8x0pbvX}xyf`v%E1_K|Om|&uV ztdmb3C8C#h%kiBq1*nH|nZA4AGw^o6>swd$pBviqQ!w5(u!rhlb^y*6b072Csrw%n zXf|KDqyLQ69f||*(xfl@O)QE;qw9WIlb_x2coqkIL_FQo{K(N}Py(&~4EH*`yVdXG z#=jGx{%-ZCkGW5Y+}5M@_wMCRhWMO;&}u1zBl}&3tuwlpgaFNoJqm`Ov&?UMRt*}# zLCqiIM}t0E3#V5i%LQ}fH*DhEQ3Kz8?hEc^ruABxfx?lQ*CpkAhfY1)+-)IEigA5A z`bd~gOa<0x3sfy9S+Zw`&FKfMaqoS$qhjI>{w`Pcq@ce1T4T@{`ATaz9=ab#;kYyN zrET@#B{cGmuf>r5#)!}%I_8%d*WWg5wwmGKFxQ)>TzNai2F85LBFKSB>DafoFjim4 zFF%8Ij`8}p)tVtcdj^d-tK{XO&!a8*$*7T8>qT<$^(_>m1$>;=tIhnohRt4GGWyag znSy4(+p6dNssMN!489Km`yi%&K_ZrbU+*x2p7~dlX#h=jBqO2#Jqdx+xt!Cko1*Kk5pfskBfnYKk>uw7I0gLC*o8V9jXGD+~3{7kn8B-2PzZz#>vb`Qghmo~~d%d;YBHp}f!WVR`N}J4WM;fn{g=9QhO*v+e zgnK)dyvx8Gv`Jixe36l1_yE(E%`kUu>c#mw(?}Si#PjFPXKl`FsVsYyPxCZedqLVL zz%!@k(ZM9A=X(e^U?WBVAq7tsQV((7lq8tIu&fNQ%j=A`9MF8B9eRil_Pt*wG4cC- z^X5Ly;!?xXU4*9oMw0XBi`Okza$GtRf>=79d3E?EYT_ByO-%tyJd60LPa5zsF)Zm0 z`&pJ(59Xpw#kB_y`1f*&q7<*%AWoKD-C&vStEY+(?f?Y^8IX8 zfF;W6-@A)(FQT;?bJUSi>ZA`@${5ZazX@B?D8G&^d93kPwoUdc!ZDW$<$&eCmxf56 zn7@ZA!dL6~s%6l-j;_kfCiq-eh?Of7tVkwFtpwEn7;RRIMeZ+o#Uy&k^fivJhD*0; z>01m8&du6Jr@TGcJ~+@wPM@9AaQ70_!bvRn(7@9mJY&!x`=(^*M^ARy;gflP_BX4` zUpmHSXGRBk|=*hc2qZ7Et%7Yc$O`PA3#BWiqLcNFAVCb&~5_`kq`qD;O*Tw(9r>okjC*j zA7sKz3ZT-fb<}wx z@&fdtBny4Eg2O6&TKVUGc+~W~OUPi-?HpU6dV%~f_Ggh~XSZj~#qthiJNgi4EX4<2 zaqS14Y$A>PK8Fm$5&-w56f;; ztZjkEnLN^n2r5>gzi%eHd01RH^L6jM4!p=y3h0-8D?lC}Pe#Lxf;7YNM*QSng*nHO z#Tv^FAKFlMU^70Lv_rSO?{sy@5Km-$E^O9~Pe<5bp*J6{_+%#}N1%?UUZUehMBz0q zJidwA_;SmSl`x1D@5f0mcBdkZ5;B$VBlq}IA}uoEWe)BWWZ3$EtM-%vUiGwE!(0H3 z$UWJlMs2&?4S#DHHP_6MfBQO$OraIK@{>qneeqY>RjeGSZ(FJKL9IR|7b!|Z>>`e0J5oXpqNsR^XYWw?zdx!`t-q~5oOUzGN+P+Ga9Qsus{;94 z?hi2=8&(0%Zm02gf%eQ4vxiZ=#A6_EB|xl`Wc3N zYYMMt4$b8QT;|Y&x(cUNE+S3_xz+Fx&2;7Nx`gD{%yfv4u!xNeskEPY>k9_FWIIu=%K>Avul>V#S(oLj`uFa_7z^W~6fyrHF= z`eLZV`Nv^0oTzTPuq=mdH1GZb##_dmfOh()Zgawb@Ws*u!S6^m8R{N3kKe!%IMOk) z%oK;Bq&ndC);@Su1CdZ@&q4iB*ex^{mSAc!mpiiu^3oXP(pfLykbV)U2O zR(v6BFbl19?~5-ir*sVRAq*UjJThgP=9J zZ9liKh^2OK`5nRWB3L68gu_EW(xr@dVa1%R*CA1KQ|M!m;+D6$*ZMkx#~S=@ekXc#%707O~`R(zU-0`hW*I1ieBCo^w_0OR_#Qpto4dx_?Jf4PSz@fcrJeERav+#^2x(nKiujT{ZyH;4+i zTfgVYcu)Y_s)teIIkk|9PO`zBq0afqcdrZ6=Fe$XYRgn(2a5B6)@-Z(`M@>ym`HP$S8SisZjZoF1HS22kG6wEq$Hit* zlJvOvML8Pj!0{XA? z3!hH0cNpvbQ3YK7Q^X-wbR~R+bk#x{CL>>O;oA>2s2`ASvHV&C#Bb8XO`4D6h{KY- z5*FPHle8VDxtn0$iaZ$(pe8LtX2iz^8Ox|}o_L%O8O{!EO!$rpNR09w+{PJZXFlHY zS&J1e>8WSZm_S=47LfAzW+^iynf|J2wLy13*7lmE+CG{8yzR`WF!hbqlT|tr zX3mWdQ;({A^gU?wv;1zI5^o1pY-O&P;n(Jgxt56sAiCtN_ef7I&7BPj?UT;SIAK{> zo9&jKb#a{VhnKi16BT5>A7EDFqZUSJV6+sdZZ9++U<)|bJ+qH@T>1X;iT%FqtnrUD z%*&O1*K#9&U=U4KglADc1^s~f%>Uv zXWTq51kd&(H~QnC47qR0XPvE3@_eXP(=DI8quyZH+c61MCD#RSYX00NM)xa1zKR^2 z<2BMyM41qm`-n%GSc7lsf#}#9Fy-I>&Vhgc z_0U2YZFzC_hk6)}#epo_Zkz(%oZ?5!?}}>5ufyd~G)tcQ4%eP_08ZeiYxpw^ZV>Se zer7Y_PLo0es^$BHSI78`T3oV zi8GJ4Rp~J_-v0H)RU&Xy7gaHZLbj;R8Z|r-oerNx4fo5GE@^?ciH?8LIW+SGv4qKF z;(s-Zm_bT-Y+|~Px#CQ(Xrj&W?x()etjbb{b(5xNV5Oq!zTPhlc~!m}XfIKqmKL15 zV-r5|f=awcW^@7xu@hoR&8}?kKl84aH8-B?NE;(G8pOp42`Yn(eO(wL!p~BRy#ghE za6Y`Ai?xJcEU_`G6Rk*^VB3e^6gg!#5*!TCGNaW_p!IGdA}|5(#*=u@He=v}(3Acv zPjCR9@dALKeUzP#LCPDZMsbzykRz}WOSCZ}g?X&S$qC5fXoOpa3_K7#8QBxp1eA`2 zra@`CN+{G=*7T)Q6 z1xadWb2-q8G)Hp5fyO@maS(3pnjQgkVgXxV;N1kPA#IVQZP)469{ZOZxc}p>y6WqP z%V-BftWKUTY`=Kxc7xHTh69A{76s>#4>q>hL}<=)paZu|q2wmZ+_dS%kb zv|f?O5Oo*&YA2_|))61J2%|oazds#*+H!2JzL+u<2#*s#FNt{cUmLIMd72X7a_H(W zahixY_}emvDi{c^0*^r%#o_C2w)8nz;PigbM8x)Ql;JxJKuriUR^Jp-L-0>d!@mS; zA;pOwNJn5H%2B@LK?9Q05at(9ad8Kdz=jhGRPNMyZeyEf{4R2eV znRkqXMEKVhq~&{_+EFd?Zh+5mRh=drn=suLwk*n}@gdc5-hT(Bl2*b_v7=^^!LD z%ym#X3+ERqA}K9*XEyj1Y|9(-hT4?H<_y}9gM;)qUA#7S5V`leVbY+R2$X_wz%_bL z?IE=bh^b#P7-bU6l2BT5H>V@W&0CN6Qv9?u-59`C6 znFNKu^f3UWmz6OTLrdN4m^^Lj^hjkPHEjgOoam+NNcUKOK-r^kq$kV|gynWWUmT#} zGBql}w=#26lYhMOmS77+EGrZjtAeLHrXc+Ax)Gg@L^i#nLfQ9xU?k}qcO8eTXpTn? zF?NZpd@9Pq)|L12ZAw%2p58_ZZJTa&&&pw~dRA>pKvgzC7*mr^=rAF_qk+ID`S2-vNmK!RChqJI`v@MlsyGw znTCWr1|N;#m!L#7t{u=&gzTx55&@x0w%i5_@Y=v#^Wt^lr&%<`wHAk_EM&>RmIDe> zPJz=bxv%CEawR2z`;HQc_|EDEREroxxYQNYeQ^;TTHVS*lswj-(~vTzJc4o+;X( z((gAxAO+WB_tbH?oR&V8Tb*%(&8qH@BQ+B_4i>ZdvNf;p#u;d~9I*Ad?i!g4K|dL( zb%;mA&)mt2y-{N5T@F+$jJ>hXXkSC4JLQcClHP?x-SC-12<#2A$uSQN4K)wQGAfTI zkzbUrMb`;Si>*4c@7}WYn}|REKve+>_ia$VW;Wy11#F!zJ_ni5;@1|j0IC?pJm>1r zybozIFe2*QauaIZHJqbmRMD8JXk*bQkr-b+Hs3)W4S>93pZ+$Oq%i@NH(FUfY@taQ z-!{HEd{Iu(_V9!89uQnsP_j0Y0{d(~`U0Qc1~;yk8fdf;sWOhopyI3-je7Z}+PPxsba1q)q7TfLqp|#=O`CT@nX^5VEx>dS;oFE##oyl8s;<7N3{*P z9Sx1)B=Tn(*wfFxGcTP{S`FGMC1vyY@#bq;mUMevo7JIDa26>{ONmv((K!v}&HTaz zb83`iv&iGFnF;N+g3n1(U`S{B!Wat1Zd8Ece7jg?+~oADS3lNTfZ#(@{zHX3;-lrIk{ASS;jtv) z)#)=^zB8JRq7ZYrRx$?YyD4Q8@^N*0YsPl!unLEGMru2Bq@u zIGB6>SY$aUg0Wl|{T1jNQ?6+-kGxt*$1PtxnnBwXQg`#A7iEN_vX#aUOz?JXM<+U3 zdydXup^ET;I=DYsj;nq{`KEJW7ML#==hITJiiC1lC5F5GS$dk-v4im5l=#L0rxS+; zTiQQqMHG-dPaALOwL+0a@S!6iHByT|?;$BEZd23JdOX`jp72(Umbn5$0qN;;Ly(A= zmZRRC`==|(X?MautAYY%9&}R8cF5*is!Sq#ff^_;Y=O%fFFN)NSo7*-yj(a6dB6Aj z3!H<@nQS~Z$Py+ z*V-$3MX9UfahLiWG5eK*Cdi6C*Eo>D> z`r*Qlv>_O5XcNpqAn~ZKm0emtWXS59j;W>9NoGj0p1-M+ZBvIf(S46g9dyC&|0--U z2W9sz5E@JAcPxW;dIN;t7(d&C&@UQlW%2GY;5;n{uY!I`dCD579OcwYFWa0MY@Hg6 z+kXG6Rgg#hd~H8$CcsO-_U(0?mcSwD0$;>0D7jS|$J$c`fUIG*=FFiu+%$ErR<*?S zktbXQ)z_Xx(TkcTaK>t_Nc*mF?DS_T74j=1lRI=EFOpg##pRSDr%%2iVfS6tyPNN< z1*aW@=~yGyp#|6KL=jO+Xu*3A=L+xfP(_717=5Te;ARUuH^XVMY2yrpJEb*Gu$ypm zss7Mg&6mC(c4u|IXAq{j_WtU$hVxXJY0zT`YlQt`;VclXW%tW*{E;9ei7Ydb9>P?K zOmrWh31Cvqeq0z2kX_;vj)&MzNW?PF$ZX=A35kn3;LM2T%F4^dJU5e$OVez0FqRcL z)gB+(F)rYAT_Hp#L^G_|c7DBT3Net2fqu(IXM_a1%H+Z4-s<%&!?1E~^(8@*jejYy z+;RNsr!n0n^&MB?6hi3&qpb!4HOp(oEr+kX$wTD!bk0+Url|wVm-WLNevcyU9wHwv zV+`qCIxlUXB1i^7-8*;T^E)YGNbv;XDnGY(O#@ftkO4O!(Qi_p1?d=mQ?Q?08cOM_ zu-@?(q&izPtxj}X;TnIuIP0f2Y4Pi=?^;jPnlECuO#QotysbpjOm&MheT1iBlJ?4^ z2$@ZxlA>9M0enOaLB5&8QpYVM(&v_Z+CzD-U_x=s_7A1v2@Llne~Nit)N(tJ;PBV% z4@8y4T-yKK!M_GflYcofwX~NMW{^810j0lzjIK{C9D5{*Ie;|_&AwmhH= zdP4P*Zkg_e+NBN9wC~Qam$v((8Oi&F-?#Eiy2C`lF(_y7HG_2 z-1Vf~6f%pOSAW=52=_T)KSB@(R}Rsij(IAORY(T!kTDxXU%eWcHhZ83KDzj#jA2S$ zbAlg5ZC4Exg}wRI#P}YoZ^F_ge zN&fpve2%FH{i05kU}1E%3i?RQw(z!^0tk!!2FG1MJw`j3u7P_$G5Hicwak%h+O5z; zJ;)*LCV=lUz-LD$%2mJ0qRgMWU8Z@lU=l=g>q*F~8AcIp8>Eg~+9c5>(9OMf+tNl4 zI^5b=EgIEVHEca>M2GckLG**)T&PMbyWPxV{LM=%^L3lj+;guN{QzRA&JH~$_X1OV zWy<&+uLqkD5dacQayS-rq;0khnE57>57d83R}6Va5r`KSts~h;AToZtgpk?%d!(4c zvCS~v8x?ax108chiPBmny1&ut1iU`(5u)sz7EF2v8WmD!`tX3`PMl@`G6U<9r~5m3 zy4wB^Vkq~V*CAT7#kfZ{65vU6VWMB?Q<@CdQ4>vmWKPLR z(-?2w5sKW@0BjmP3&679Np9Q&6&DQZzP z+p&z3P1N4|m;fzzy9)f=+86eiUi(KcqOxd3f!@=U{ZaK9msDqzdjNI4MT`81+Ce~L zIlA~qgN_qllI3xmHMEpaDN%p6wmKCAW{k+HHAwPckm7lSjM&_uhU|QfI3VUI7Ubtl z=AaX3haE!&HEUk`pophA4Z+cMm+l$5H~Sa&vJ<*CnP#BF-!SF6W3_IYtP4SGbXi-0 z!mle|Q!OSzgq(>ZE9eA!@qi}JYr|9|UN_60s#!k6T7g@!zaxC5_&2i1Ob6UfAzg4! z*r*7@Qo5M#C;X`Bpp%4}I_t4{j_Mu@mr^DI4`v|z0WzNTFvPI&OD8tJn4HVHnw0_7 z4y_Yt2^9C~);y3=$Ib`p{K-F6R4zsVK=qJG?+2|=QBp$wzcs`38MOW$gw%>8Hg0i#L`y{$9xs8*YpF ze<^o*i2M}5lJeQ$UhXeOHU_(MRek5HRqo6fv@ii-zi(6Ds{=1|;1CmUo7IY@mG^kB z(2RXra*&;0E}>NGRC@KQ;o;rhUrwuLAZ#j~$A!%IDcwjoO?a8Sj_2K^-)V!N#zc_C zm{0!^K1Hyozqn;A9<6`u%~d(H()g%y7vRR1G3!FQMyDws=A?#~Cv)Gr6&Yhf>y1hS zMU{eo%KSe7=0F+0@c0K#Wdd|xxOd)pM<1AUrERKp`kH>7YbzuwpkllE_%-4(aQBTd z;CNz~ZrOnGktv1&TrqR*q)KeeZ5`%1_2-;ezt^6S`_^8%zG1g-yM$M3eNfT_UJY3L zCn|FZR|f}#&aO+C3}oHT&7w$Yhkbdoytz}Vp;0Pzew-ywqX|or#LO=Dx5rIQTFm8^ z-zsoC8`a9)1NWc>cGCh&iCWTk_#|UBBFqCGdFYYAB-{D&<*zS(4Vr)O{10dXEBIdZ z!`?>^#O(^#5zvDW7Nx7V0J+*EaJ3~s2T&DWLT5$D6am(gUQ>r&Ad#POKN}) zc;%H>WQ0BdT@H@>lFb3ud*Qe+gF1oP%Z-7*bYdw1051^!7wo$a^w;Wtg}2eS*}%LX z6u_^4{cCT3qnWiSr!8C?)^j^!M||y{K}3Th&}1W3wS1DdXWMIYfr_}LF~cQ&5hFTK zSs=S$5M#X_B*86Q)wOv zNeH$MGUF^ISro|6DV%ExTFx!k?V~0&ZR&?wrmtr9ORqdIXZ9*qDMd>{f~ArKn_`^b z7Om#*I%T?O?IfvI9p&keX42OVDW0oc5Q0UMHqF-S%09tT3vMNT5VFe_=#JZoczx_n zsXF{-(%urzL-26pZwYiI@ZawD;}>(9RU%rM99$)}z&2|!>%0NlN1!Yl#Y8>B}cKf3>fM~)9UwfMW={Z8Q5 z7rs93>-OckYKD169dxLbA6MXUWzUsvGe!D_1kYx~0U z*BVHYAm)A;s`msEEM@EBMffn-yAMpKBf)x|=mO=sFNj{AvH)`E*aIjXPBOfj1Vt-{ z)(1Tml_YG-39sYKlLFA?f?eA)3&g%&>Pd-HJo+h*O?bS~3AQ{t>6M2L(eBR&H~OuI z`7~b6*+wg9^EzTX)wu(OM@gi?QjQoTC436T$)&H|2yT@VS+XyeG&LIAvwB^0*=iIf zi;dz3=)bjnZa75G+;G}^CE=(e`BY}NhQcHJ9x<{w_Li4FKlj`*Qvs-*H$Pv@8$%6t zIL>mH1hNx)!fxMKa)@iRKlb?JhC^!tf2Ke2DjE<5t*5g)K*W_|Gf2PaRS#L8^r z#EBF5n>t9Iqnnp5XEZUqF6F4wG@|BFoZ(bX)a=d20JxqsbYO;s(Vj0sY;?t-zvmTz zY1F&#y~~yj7}d*v2C_gi@R}=EsGMs9lb@1l*Tj~>=<7x!l`lsu8tQ9*wZbwO($lg3 z8#MJ&@ST#*=D(6K^UJ<2ObkBiNc@3afP6ysJXPqO<33C6*U7NlUb=Mt!uhu7TOafR z-bw=8W2g9UY7pFvq}D3~uV#KQ#m*H-9~bB&>O^9irZJa5)RX3ANAxOEv_lD#OL3PC zfex}(6;#J7Ru3M?OL574?{ukf&Pdm3Xo1Ani&8RS@cN}_lc*I9pOV)^M8I4|dDQU+ z)hJgboaD6W1rOq7>@x6y|3M4fSPQgtwWMw3kTJgZDp4n~FqM^Qw0d&*m6u-u)20Gn zojZT|;$;Bs z=up%Rx_NC!K=>ylHbNjV*!Kv4k0{i)Myn$Jo>0u zt&~RPDCC;hP6bV26ETWf0XK4KhST}T-7y%4ph8|Me|PB~P~Gz_K;+icAO44QE}7!R_d3GJXxl>a3evxKxH zMT_{)v}koudV*wT-wAoCi0SR*3?)to)|`9|3@ye{28KcO!6SKOrWQP-8?+=l$Ui+c zN}f?vMZ;v&V#KcT)CPM%JpUfJ2Q6?DEkJtP?K>Frjv16W`uX$V*pX1DcNp~s`xj@v zfZQnivtm7>5Px<4d{6{#lx^B@;hBJ#MUOlJ^x?ySeFtn37#)P)%F-|Y^_OSQo&C4} z_TPFW%>XfuIKyrK{;#2#EdjMLB06x?%rF#aD6;8nMaje6 z+X}!Qx(yjsRTE&(qY(r4{5AHc&TI7Ff(C`XSrlrdaJ7GB1tlGmZOyr_q{PHJ5ooNTU;?sT}Wd5U6ovu`S zRI2+d1Vz^2VOt2BpE+s@5jUJ9g`p#$WU}wVZ!}Doq$9$od^k@|P&v8#O(02%kYy!l zQXTx3x$3!dgM{lTg@r65NBa;`=7`P$`s$@wA36N!V@D{1#~wR!_;FL~qenHkcGl=| z4{HpoFF{^-s5J-k3}N>P-oQY&V`oGlaZ#&u$XlLYkE_tZ?qGUkMzyn>uoY z&Yo+gT~9Ex+2;3#6K=z6ZP3kRG}2CTyF*4zHfY>(<5nWTLdH6^om){)Jo$uIiavhk zaZ`Z+$JKAM;a5)=BLmrx*Sn_AoIOh_8TABPEThL4$}ndeF&P>DOd{&Ut$Dy7{_uxZ zGjsrY`v)0eH8q#ST!e&Qc__~W`~UR3mhBWjmzakoaetPQ)N z++R3W%#6avUGKy(d&1<1h6)Si12nB^wIR%lyc58Mep0V>LCkFKuTwSdG<@wy^oy%0Q{eQrZXCV(DxlmTuUqdk%P(F~zPxSJmW3rBoYi3%PXB!r1f) zR@TrLpMODe^(H=Av-;wQv5`r_k$8IfNO&t|*_*fR-&2z$li#_nl1D2%a1UDG7PY__ z6!sU%Gp?SV@xmX7Of0Ph5lDV-{jA;EJnqwD!B_#=umWNTLFNi2c$_PR6>hJ7bk!o# z;YSWX^n<8})2C0LI&}(f$3uVeXFsuO1WY*d4~Er+uce;W5~MX)0wcENNPJFq1UfRf za#$n-a5)S%-`%q_0KoD4-~V1U?Z9e`v_~N?(@={Htlw1I85*<~mxk;0eH+F(smW%~ zQV-DAofrZmDDMf5ciwsD^yzm#_~1ip`Z`#qVkNf_bo8j*PM;o&>pZn9iRD@|Z%q8jD^j-N|fK#oSMkF@)o7)wK$4W@uZX1{wIdFd3pKF3kT~s15 zQ{vI4Mc|TjIa0iRmQAIQ-P9>?JwwoALTnVkTvOGoM#@@ew1KgbgRYlsoR~ZNcI)YP zPb;TpH3m!$jvn=Lt|L!9^`ynqUQVU7Dd1^-Aj?`0?B_F5!QHr8Hl2#Clykjzlk@@2 z{nP?mN7gcA_<4vHNn@%z$OrUU^iMtY6acXx;H;lmQWUHKHg#IhQjdm8cr6k%Xjm5# zLbjQ+5Fdgad-j+#Cr_Rv_GZw=n4>|KMte(u7CDfYp7l(^Z#>uw*;GTOqh)wol;PmRw;tguqZ)gQGeFY&&sRH~CI z^)lB-yC-y#Ntx-zAOHBrcTb=G=;Mzp^4prP_RghkPaI1n=TbeqQ3fg^118~C+& z13gcbIiSkZE)^)CfX@^ohmkmvzATcEgxgxF32Z2W)j6$oux%9$l>()?NP>>lg&}EkIdK75Afj zTb+t^Gt`elZY6mTa<8{Q4<7=ytVrarMZ7|{#x9A6AKH{CU4;OwZ>Hht)2FTML+XQ1 zJq4yM`xqPzID`;ehK(c*7mO8tOeAOEyM-<9zxRF~C;s*uOkR8KH4FjKFMjb0(*9>Z z`7=k4q_rbyDfGg@1C~-128W?nGH_}DRlx!g8K8Yr;9yXVTWR_4RRv)EU5h(z$k6uJ zUD&-Ca4M&U+6xdkkFF=QOm~KG@%*4%({tA9A&ZUNQ-3=3$2b1yOfLVKvuCtdS@sW6 z5omOke&Tx8rs*+p#Sh41+nhD4gt2Ao$bUDw9L0_T=qmmW?7|HH=%EfTt{ceSWF0u+ zad3G;G-;=(>fQElN;0C0jr%EA002M$NklbP)bgEFi) zY9LVbopgYx(csHkU(=sfOVg$O9=O}u0_1ARe`@&=v?+3gPM(#tCA^!&dn9Nn%0PZ- zFXSbhr%s)6tkWS*nxG}KBsOn=z$bFTxf+<*GhpBm39&^Bd$3P)m3#-hwtWIQZfZShS8wtwGT{6;D!zfR)xid*aZzxd^; zQzuWo_12$0{^TRaiddUiWL>CM6)e>OeOC+p0LDeCZa(?)Xw}ipLK=@6Xi#!pJmo3> zq_H|&6i*|P@-*9tc@VK6!~JF#4AQAG*N=L7((4;yn&_#sgJp$lQ^7E^jmC zN`w=b_fK%Tpi{Ln$hDr1?nR@N=gK!0jy!B>Hq9^q`gYYQcyj^|-@o4=(pz3zQPxb*)+$XL7P- zGS2fqc%GoywWSGq(1`^C1il5>z;BEq+xQSmU#2AW&U#vWMnfyS`s%A_EO@YWn7lv! z@sDjb965Z%mL#$sa4ClWhz^V=*<^m(iLH#_)S4f>KG@Kso zym0RJMJcV@0;_D%RKKQR6pbsxR-XZ;{41@{1s3k#ZDQL~1WK-&2@jy>@sRc;Bkv%Z)~r;pUN{$MSLLwfN{qrzZA7?bbaeHFAZUL= zGY5*}cXflij1hDz6^F?l7g+3bX+Gn&gKCZ!#Yu+>(bPS7l7G3>sWNr(oSL=N3twNb zt7eamf|mq6d&*S6xxL3}gQG_+8L_9VtM=qT&s0lkj8aq^CE8S$2RHXY3yi_F+q%s1 zkA5kuot#ic?Du?Y6hHjX!=#;o@A)JntG^JyfV5}{!O8XzSBC~Ai0;&%-ZH4MCB0-=tc*DHivniAqsgB!G5uuR_0aAzcP(7+Nd@rR z-~Lw7p#V(n`e;_o$3ME(EgFWoC_rqeGx_t?p2LrxJA2LsGU(^tJo)CQAAh2uOuqEQ z!v~$?$vRKYr$3sfi>)%(q#|8~Kc2miCyHNSz4{?)Lh=-5@1ACrxk$Y}p$k?ZUysWw zr;;x5WmE5kM>}*4%5H~eij#c7K`iUF>z?d7s@Qh!6R1^UGNI%XPiztwlR+S_f9=Uc zk`=+#!(Q>2z1Fv|+utqB|Ltu0Pi5;|BIy-bxSY)?%3uSgvmR>hN-D#xl5|n~PQyyA zOo>SmOhhJaQQe$*iAdLYs*BOJdhkd=?Ih@!b4GuD}nhh>qBJ;{wYj;Dtrf(*(UmOaXZ+TFw^b7p$vSA?k=!Ojh-w98$#*?>^NHW7a zZ(MM?!u!@0W%KVWgao|G$$f&J3ZROnw4HBWT$naYUX`Rvn`@GlzOrf`_>Do)9_{81 zyb5U{LoX!xN@p5dvsy(esU8v!)!R797nG8)mXD<6cWq(ChhNI*fVm1ZMf+V97J)G` z#byB~59xH2@pSOe-h+n@>^X8o0SprR$2}etyH;jBsdvFPk|Ek1ZMUE`0hPcx4nSHW z2?w(^rxmm}<}$5(lkL=`-G-xRE4Lwh;O?~+*gPI<1da}l>@Dll@*t3;@wezA01n?= z`o>*Z=Sb!u*q; z{KORY*=LV2qhvw5C_$IaD6WD`NI!}+M0Z*KdrSeSxtIR{ZiBO`kr%OY+D;=1(YSlR zNe`*5a3;T}>CBfrUH!{{{a5?xfBMr~4s?3{NCNHU5GTl|w5Ez*1=*(oYzS@DES2C#Z(KKbK$8Q|nFP*ZI@T zcS^KAcBfQ#HvH}^vt@-P$P-?#-x&>+>GHy@^mrtsolkllJh1o5US~;8o;(SlsR4!s z3;|!nj}UfRg58<`3Q3N)a4=&NxZ&l)k3U2JF}x(s=r~o5tp4Q3KQXs>@~I~sPv_=v{#R#sdRG{?lA?R*vx;;)NSVqnl%llVs10It7Wx|m-hYO2S3ncKj(h& z^Ecmo(|$U2YRqamXq zfV2vL52rzrN2F?F_$8>_271gP4}S027Fc=1puV5FzvL~#FuS%(yp)W9K^~&@!nn+j%Dt1?Gio3rH0Lf&Hvzzm4~9K%eNG7ifgR4aaC*j^v2|D~6-)V|;R=GVXb z-S6M{!y9?;(z&xJphAucTC$JTU&jV>t|#b24>(i~_Z=Fk$AN1Pfcc*%O()GP7f!xY z8PfU5cUh#RzZ?DDx~?n#gl|)YAYeCzH6`Mn#;EgG0<-_IuZ~@c=zrC8+RE=x46P5` zas*nG+`B#XlGg)0@x)QLbbu>|OcxV(SIBLKp(nZwUF^za8if2xl+5)Pd^U@*`b8{y z#vMhz=g^*q4PnK295*wAi{8ua#9j8*XrFr9$ER<+@duJ(EzwAO)?(r_dEUni#z-a{ zX^YM!6|1Z_w^9fmxbIsFl+5+c9|t$$JpB#_v5 zQ;{a1#O5Hermggc9V{BcJir)w=bb;DK7AVJmtK4cH)J-tOlG|?&P;f4z{IG@K&XkS z;j_{Y_mTq8WdHCF|7c{k^6V;UGA{U0$8t18rdmfH`{2C~-gxtmzP9s@Z*QJErw!O= z&gH-h$vLpEea-hhOkgIZ5lztA-luytG$q_wXuiVvsp<4byV0!UmS?nY`ddul1rTth0qU`s(9a3O&}M+w>Ve%56xqbo-G3MK;W~)U!TXxdXvX5OMj#kCG@h4vQDw0sO`b!*88+lwrtj9 z4#(v%M%MQnvhEi!cGlZI4ulg#s3EwKZZ(Hrf=3)aLNB zMski+)e}SJ0X-4xYirIauhIa^e7gm8+Xx(<5j9%*PI=Z8X(eiFnO6uJPe)+dV|D_? zlJd~_S59ONfdXJXR!A&1fsxO+3=HuHCd8DqB~{Bac}$!+{@ljJBP4m1(~l-{g|{gT ziaKy@O()d)-EEgqUh#w~|1*!seHErvfnqh&>>0$#MJhAGqfb1Ma$LIX)ScJ7T)6nP zgWmQG-hTUS)`LkSHDG2yf}TD096N%Fq9h2L5$m~xlaaG_{`HRJQr{!S5Yt zfkk)7eWgbt-J1!PB}`v#425M(x;dvU#5%6Ff8U|~heWgD9XcnR{kCVKB0GZGvWD#{E zOA5gHkE&`2qpMrTK-PBF2dGnnkn~t8X6qZ;59-zMCbD8UrwhCGJqZwo}C`ITU_HL>vuE(>syVebDbFYF9pyiF3b;g ze}}uK*J!J`_cGe5BWWY+IlQv&eEDP4dvfrREED<2t6aUgu2+Z%9{wMG_<;}7QAFNQ z^xSjLJ8fnI+4($=AJYVQDw};Kb)*kkx|Fl4xl{wArVl*deG9DY`o=I=xEL?{cfj}< z6ARL{ULruWh>^t{mOGPz4zPj`%>_a>l1Ogx5JFH9_aJp8I!rEfrEs=EQfA|moK~;gcrcq&SVG!CK zeER8UfB5oCR_!hH|NiyYKl0YLGhedTXu%j?UhZE@2U^e;idh2aXZ~x`)IUqZSpe~Y7l9c8`~7R{IR=;z9NOV#s07?=&{Iuawyk|?*8pzI0;#O= z!GTQD0fsI)WZ6+YSxU+)0*@M;)@n(bXU?;wpwiDbrjsjqAN016g`$|GM zqP=Mdl;j}LVkWqDn2tSj?D&b}>`{Tla^Ey{`%{0yzxi#TUM_m zAAa=FpWb>42lB=O<~jY&Y14u)zWky`2U+D(p4p+1MQ67ugPGuN{58PfJKd<Quzm81k3ybz7A+HJQ#}VpE=xq!*GJ&=>diui* zM}V?UHxe8o=+z-pX^_o^GG#Cc7=7Avf@K-zsC;xt0ZERdXONr0#ggc`37+-Fx=X~* zIU;A(>_0Kb%rXs3`%-Oo^n(R#j;r;YbwH^msmtuP`mZUOTa&C0nQOG3YF+Hjb6Cgy zJB*vne?75D2`q}R619P(p{Y|3n}ELXLHpSHa(=Weo3eB2ql`Jtoa5tHAG-SKXP=Tl zg7d-;Ua*3#m&r2`Wm~{{9JnP+!7frn-XuAB@}zlydFQcbpC!QWzWZ+0Jie)CkF9Ax z@dO3q7Eg|6sQ?TFx0v(XGYY^-PF5bJr#|X$5!Tp$>7^gid!K*)*~ycqs0Z%=F#fZ4 zd?n9TFuRUDckKA_<1fAR67%11O^4}_I_4OEximz5eGun4Z2A=<*3H(a70Q*{>!n{w z{(ZjPWU0+VuLE<|=$kq=TAdQ~-}{XPrGtC&;?K6J$$AVPmuv}NVj>V5psz`@o>TAV zuxHB@5JHfsfmR8bgG+07AZvytCUZAnZpWDrWbZgLM)Ja$(Z^ETQ!?4v$sHfY3eG&t z6wjT!`|jyq|N7TPxc9@bSAOt9=AeX=G9Y=W+KfDF#AZabK+Z*b@Oy8xK-55i3Fz0| zCJm9sZ}&O8$y)bLhZkRZ(dska%6`kZR(LcV$g} z!r1V0J_(0rf>D--_V4xdv1mI=-Y#y0VrYSgcA*Ifr#XR733>Ghp{0S$)@cF{ni=-? zk_~bN#*iLUY_%8-KDBwtXB5U}3#%`F3^L-B75LLZUFHW$3K>coi?(3~U)Z{Q}q zq_nSHUr|g4u-2B%8-IMm$u#2ROxknLori@VfAXo(K&Uv&A((Gdlo~I_f~b3A4}f+5 z@BjV3CzOV#gFTA=Oug^dr$XL;_dTa;4P8yp&t<@I< zgVjvF>34-cK0=hguLhFwMpPg&zwkD)j4^=gikA@B&3fu*&~mVvT;+|YwR@c0Fl(OQ z<`dGj9%VuxuUPZ#@|&#jYQ191nrU|S>^WBjK+M*2{`jdUpR{l6buorC#lXHZp^?fs zwHKxL+EUxvld&-n`lze8Z@UwT*?^epKA)6B1wwv`8TzOn;uMdUHowHqu zHYtFWv_x)=)I}6PyMNVUeN9-KoBfh49PG;0ezE3)NXp=cU?U?TRxEIEeBj4F{xL}) zjV(DX32wP%eT73pOaDLr#V>3LkSLl1XRAQ(o_?3W9DVXAMe^yVpS<_p`*Z+;tsRP? zzayb1H0Red5Fk>#cMEJ&9gJuj#xY3R_#} zPKos>(*5KikyCZ%-%&g5tnoJpMnrishsZEl5NwZ2bF!I#Gfcw8Dyo@*@djiK#Yjw@ z7OiKEX@rZd0E{#qG~`EGW0Qa}n>u!Hqt zE?>4E@RZ4gLm=iC-c9oU`|nvGvNCBNP&(jw!#ER?CywS_Bi|lW3kAkP^5ys+`8@F4 z`4-Sgbjy0NEEI}f>Bl68x~c6SONyRQJb(858=t&k(UJr@K|lzNi^gbmByvju5Nk#8 zr62x~Si*Pb9=*r}`Vl14*!SN5z?($9_|~(nEMPRmcq_zVtK|290#I}sj5DJ#q1HOi zf7=6~-_%y!G|dx7pM3rY&%OHUtMq41+cxeR`g<#C zr8Taj@LQU{ae0Nq{PzEq;M``>e~JErHc)nz9wj?#3azYr4N7?b(@h*{;UM$ghttbh zOs(abUZ|lD3>dJ>1P9FyfaY6cPd!EONMLpg45w`S=6}sBY_ApHI!%<^wO2j|YtTmh z~Y;T%#_VV3BoU;w(j?4t+VVH>AG25+mlBi z{mWM!WAZ03Vp_MwQ-_iS^1#Dc&^ABt0Z35JZUFRROT6+>B;7EIh81xC;ma>uWMrV2 z4fsMj4L~cv#1B9C&`JqmBSQ9y88IW_@W{?U*#Fmm{nsPt?)2E%_}jnzTPG{**O?|W z&Gy9s5NAZ?$w40)J4y~EG>V#4DW6tXu^s#g&(NJuWV=-YAb^#b!Qn>_dylNu ze>~;MV`^cnGUe=j zXMF*dkS)jgsL!^UryW*Mep|W@Oxv%x^mg0`UcId(DX=x4c_$!=>Mb%LzE_6`q3$LF zfDX8WG9hHFIlH#7jfyvd6CaZK#TQ@3Aii>iSW4?r6~aSjAU471NkAuTapLdWdq4p| z|3Cimk7v)E(IU3&G!Uc7JlsI?j@5tjZ~pT5iQ}N3wU!R3S(R~?vl@#9(^28kNjwO; zal0-0+5}C%3~9<`@)iFe9-b9~@6YXMz4lzA;u{0!8ia{nPrQuHbNJc&oBkkgZ1MR| z-?yI;)Gr6gR6O7Cu~NiXH;~xl{*yW>jZ9{mfn;16uq}%Dy4cY?$fKInNcug0xhN-r ziR<4i^=9oT!>@Hu2zCgClQl5d)9cAGkP^z!gnZdv}Naaj7U ztA7(81lz~2rii&ea%pa9`1-$j9xz%UGSMWYq2-~T3nUEb$hznqSkDPy-CE!FU=xc( zkE?aarl;3~TG}A|>Q$IThf(78)6YL=J&+tADUiTYC9XJRy|Y$&Cp6v;!Ecliu} z`ELV&L8RzvRhsRbIgWR9TJ3rD)z_>DX{ij10`!b(1KGih1^SDc#b(p*7!qlTawEEL z2Z8Y6pGdd+>EzqR-1A|hW!kfw*4S;Gm#BTsH0z0%u?5A0-*V8d`ZJ<8REr7{9uj3C zz8@>g)my9|&Xl|*&2X|D1Sx4l34kJtHC#0p`z?)D%?AW}Kl|79(CAt?=n!A);7`LFWgmjov_P(S1SMtjqU%-fBmK7t|mi@?xercQ*m~BJ=kT)vDY-2 z%DBdaoA7_*A+N4&H$jg^er_~qH^jDAWWxKp^lVGK8-kq@-gDu*X}#T6YMT+YbJF$1 z%h-(UU5@F>4mcNmMr-{EjiA&dnwYWlJ$jaJhWS#^Lphpl#7-LkH6{t55u0}l9?b#~ zKqqf4a_o{4&q*Ih3%G;ch2SZ5ndeagvZyU+vP~0E2{wU&zL9JcPzLY3^NwrwBradV zghGnZ!`!jJ(7O@oo3K=J*WpH&jqu?aG+RA`W_Y&Jd^aHquq?y+&h0K-6iicWYK;*! z@!2tc5YC>Zj7?$H;?;wrk%wnamS2Hf3yo`_-^B}~=2;yYT}f1UEaICAs4}=C7&>}X zzxnhnKVrtQgv|fi91U8MAAFoRAS!4O%ds7Q1g6~Gs{qs_uXgzT?_dA@>l#XBC`Q+3 zp2>p`*8MGhshFne`lwQm1X`e*Mg@6>zi+#}y7IfZE|YI%xa6=xxs1C1{MXxz>snx& z5w$b;i@0Ky6`{Pa8FbE?0^AJ> zpiA4$o_;Cl&y=ms2j}d81INC+U2nbBiNe)r^d^a->1_ouJbI;(2sIM|gE>Ffxx_j- zc#yDqU!mtsyb_JL5*y*>0bKLPv+2cl>)nkCK&@2UPKav!^dEkzK8g$IRmM>#2j-rA z?pcFSO$fYREJEecRQ(9oJ<>w}by(#Zw>MvhrLPr=5u*LW6jaVFSf? zTg;M_Tyk!saDRl5s4OeSvM577N+A@)kL6|0jpK1^Wac!{g|B(sl33v!adlggIoWqz ztYi^IkRwEsf#~FTIkHQT5BO%0SwZbd*q%OOT5#mB)gem+4h~rwH6dUpnjbiqgeP+{ zrOg7=2Ff=>*UBw}V&s0!*10a?YjV380wo!R!RPK*09{$l9#3C#GrgEWe-RP^K)Gq$ zf@xQ-(~8o$OktJ;86!?Qx1oGfQ$$pzMN6XTnX->Q`taRA@EvqYyY?|nYaJGzg|}xj)q#c;YJe`UN>rQ1ePx_zWEP2Pn644v%g_~WVg$0 zy@}zhT2>2*Y4(x}a$;IU#&VrxEyf4o>rDdFf-y7beF6hdRhTaZuqc{$x(^UmAvo<9B2M<3G!2F3mZ(N$@} zsuWAppDt5uVLd?FADZo)*mm1RZm5uLV{cR7N_nbFcFd~fW)a;YG6gI*kXuMFUj{~y zcKBP7z?O`(t7#Z1Nds>rS z1Sp$-hw#VdcsPd$tQPv9yxE~!sh9<{0#WHC@*)Eq%DYYhH2N!?Nx*M@`*VX|=ceEGy3DS4PD8yHjT$Lx_-9nrY?Yu6tIaf@8n8!a;6Z$9AXA1TM;P{O zd84q|cUsD%VY{J!i7CX4= zh*i`cBd%HXY2BX9e^QW-9l_#e+lW7|0{Kk_q|;ye8oddhH^&hw=5LmI<`1JH)~!w1 zjU$PEcKC-3SSqw^?$9z(B!7J{R1(TVs)_!tIt8SNOc*6oK6BfD`ImpOdgaj=w!bGM z2)!sZj`dC>=+$-I zD}Wv#uJL$yEX#Z`{5!;zB&Z8;AJETJ;i5`OL~o>bh=h9wLqGfcb6=hSh8-dh2Q<31 zxpfnzulMa^{?h@3fx2-hND?*yIPDx=g@pOtqW~=WD{*zOWwLMIc;k)V{N^|PYIPOp zS+hcO5#-WfDiBW%ip+WINgshNCEXnD?8nTRS zKboz9w>xB|V0Whi&1df5rzpk;m=)EH84q=c$ z!Imx1{_9GD6ASO{DDy96DsikdI0>r|D%#xyei)9$3)ndiBV6t;nqZ(lHVm>eGYD z&X3j(fnyZTlepMX^fDsAVZN+9rB@2DQh}+lhG&;uJm|OYk==!+3cGR32Jb=Ky}ac* zwfZCk+jP?$lZsdEgAVK6(7xT>n`@?T5+Ev^%Y|_RbnWqrAidm-^=5I(4M{ana_Ng{R4+VV_`h@7#cGJ3IHXC z@~%(-3S@%Q?Uiy}-2?rt{u5>EKRqutJUBG!z^Za>r&)V$+t+5Ya&_Bo?DHfSmY07v z-tMVMfQoeC@9gzXHQHUF05p9g&Qjmha0-Yl|GKkkt01;~n}=8T5a2TT8U54oA!FP0 z+|$Br83UW661aTc%r0>q>5-deZvmhR+^qO?<=%l@?8qmGT)_tm&v!1$r_()xF!1Y8@+ZkjNtA55;}EKx(Z4x z|CXl%CQO+3`4@?n49Z{K-F&uGVNw7c-cs6QWVZg()M6XZMJ*w|{S`Q^5jELzexPx5 zE;;$|qYq&^1ppgKM^n0Dj)upzu~Epy%NIZS_!E}2g!Z1EeBvmur3089;1t=NR{)9v z{ksC)d_o-mSLj0jfIgX4RmJ7eLs12V_l^E00sbvzsQF0Pjnba@)4dz+8fdaXc;|hp z&~6lE4!HL3NOB83E!?+|;VxNSqswZDPOKWEcggq0$U2La>sD6FQi)yQ2r=%Zp==r- ze$@FO!f(08a3$kLtQ|b-Lc|{y2tmMNEICfUZx-id-2GxIVX-*hO0IRMY!@HSyJaw; z3En#O*6DZh35`A;ZVH_;`EYqD036zd{|;^$we;^me(m7hFj+y~M)aLoZz}vs+GV?S zBc=eGQf$X~r5`tuL;?8MTv|#sqjmo%fd0vG-?(WfppO6J5wcq9@km(v2kv)l2NDb( z0O)ES;OMW11H4O|R5U?f-Ac6tLvXLJzx?uy)smx+J?>C(zmr@p$nLNLPzaM#;=Yss zD$5QqIX5a4ktC~?n!|kmSXi~vQzk>1|LNWB42bR^oCovK$w|`3#+&HQUAuTUaNT{8 z2lJ6+ge*NHwmr)z{*_qLwi2?uU7vPctbCS#OQz+&yA;5d;%y(WXsSqHgfC7Ta=?L~ zncrXyrh6S{G85YBf*~rZu0{EQ9ViCRY24qYW7QMNM)JxH#`I}u}JqtXA|E|N8H=*+){u~opeUYD1 z2|RFLh^vsmdQApJ)&~&JdlwZeS>ZcLd%6etIhB$Y`g?ma!$R+ybYf*Qz*GR|JFEa4 zaaF7?akr+zz4_*wjy4+!9Zp^;fM9<)waQD@Bd@Xg8UI#_SP0~J8sKTrBE4{`3s)J za%syZGF{IT*s_Dv2C7Y6VMeK?{5Akt^#VDA0xC2p4B;PnHynuNF>HDSMFEs{F(ELx z!j!gl!i`}3bzRkTk2gB?r0Jqwl)A(FIXf*pfq;*20d=XB>eIlS~#PaBI zj-T_BsD&l~-yZ%rd+waW@m_^V1~5W7cnZ<;Nn3MD3o1Yr?mH;pWQ}mU!I@~@!A)SZ zQBFbUM$E4X%gK``eM*nruU9fCQj@xt3eZtw1|Z6fo{ML8OK#~`{XR?ZN+%CA>7>hq z-H4eB2dZv-$F3~XZppu=(3?y2sJsZ6m%(-b30c+zEAXqsx! zBAIGDbih`jr-WFDj7n<-aN?5hs?KT&6ZyK8dQ`aCj@#kcDSQwN56TrcqK0SLv+>6O z&)Rn9yK>D^_?%8FY;Q>c<`(HUHPZm z$ioEs`cI(b?V4L(JA8y_i{a;K1u&<(l}ADc&ue#dY%n}&e-QA~5HpjhO~bg{=#J!XzCjUK3-ID; zZ>o4Whew?tm7}@%C!c^4J^rcn+I2eOYA;l%h(ONZGDyLV4hQ13E89oG*p6^ zY-~H(0k1ZOwm65@P4^%4N6U+85Lq=ZP298*s zYZ8QJ^Jr}aFqo_;fX%TP=9|Q0bHi?M4pE4&!)*qMh};$h&|TAbGOod8bC&3eX@O{X zCb;i)p886#kja=&vgyg;Fc{7zP00YXeCO?V0J?_*Xmp0PESgf)1}gS)pAJQjhDqVQ zQey9b0#JosjcTdtrq>HZ?bNB0=AlX-q4XZC<@!&~er6w7tx~c2*Z$njM#@S@idu$j zbbN)b;Dv%MiN70vS7G;gjhc?SZ}sd{k*$==kd-nlqxe^1*Wa#ByC!xeYNvd=<2H@( zD$q)Yt}EnbL|8|8OXHMz7`EFm-lFr=y%ICC|sQ@$3m6hZ+qEc4! z8hM~s*j0UGh$?vaU_M&;_!E!Q2?nXT`03L*FT`xL)PSd{hGI9q@T_#uxE-FYbriK6 z_*)4^$jWHl4Dn#J2)o5v6!<3>Uxy`t@(Qo*T0TS@$)?n6e`0YK2(>jHk-L@xSUv!Z zx7Onzynf+fr!N&i8SYv5sVTs8i8h$j!xUoS4kZn)^q+nHnY94XKrX)^2fbc=@kNIP zum$m3*%A^MO99wYlB0>Ag9Ud$0Vu)NrGM36as2kTzg6=x`D5v~YsibaQu;s((giI> zqNs#++jitiV8meIR`6n{ak5BpYrIM|-1Tx)$Tbc(oDp0v+pYBYYz@3)AkXIEa%5YPO|i#Y(c`I9W-QbrZ+0a=%VKPFCLLr4`3F z_OfM(Ok`WJB~G9O%ACLnoWKFV{r#)WQHKM1Qb*@TTLJ=qw`jmSD=3TPLp6#Xz{D}8XKl|*n)&zzDQ9V>hF8MS^acGvo zR2aarsFAcw>XIJgEzzkeE1c36{}7j_uUtLKPNc=AX&d9z_{xv3_+21WrkIupz?TQ= zu9u$*mC)yXrpY^uW@;3YcKxkV*N#%MBoP-wL|p#aGx7H|4p;^t6QRaziq!32^JY6d z?(TX|zSLd^bm^`zEsrt4odX+3#sIBsu>Yh7097%eAk$j#mB;6O*Z7rJIC&LQD+Ub^ zP?qnXCN-jcY9eX94K&|IZsH~9HO!hxb<+)cIvVh01=ggxpt2}1%JB?L_3D-xvKzQr zi8iLAyB9ZVJ%AyPj3YpTUAv$rZp@(H2^dVa1)B4=NzK2n6LTW?w?jj%HRw+A1mApi zqgYQHGxsfSXX%3W0@LAeQ!T^d+EdG=DkdQmnc4iTe4mG`E#MjvGBa|Y*u*~`yq*lw z|Mhuzv%Gvy zzgdv&f2WrVa6Adc0MFm4ZBnD<*c?ovY1@P*X!y6`+TBL`#~@-(nM#1ER7f;ETBB!O*~G1}R?g5T z9r7Lis!v3vLVF*%z_VGGbJ)=AQtjvm^9{m1A#fW^_LKopxBm{qz+RbLARRpq!@Iz& z`$ImoL}POg2sfULuGHJcahfD%N8nf2fpSThlSC zdgVY5G{zDP?I`&&*?YIVX8;i1dgDz~NLxcsKlSvZk3I6mFFs`J*=7_0=%;&V#M_U6 z%dVW(O4+KEwZ#392M>Bz&5K~R(uv%~pqpT{skUf%VD!C-G=3a9O$JwPd?Id1F!x1i zk9$4&F3#OU;&=m1~`%E+_Q#jHp- zqdk^l31jL(-gu)5DE$&WvGPy(tspCe=*;6Y@Vi;@ix8TAtmC+QSNnh;yq`a3yaZ|# zpiR((r27C+6c?o_t3gV$lc0C&+wL4X7ijsrY4!C4FSWF0dh&kYmIG|#z3SvX zBG#J-8d2ji3|5g)R#byP@iw}JMK>$`dPT%J4`E_>z$Xl@w_R`(8+)$?0=@Xz=brV( z`RAYimCy6}IIA}kFdOGqd#e^ll5a2kq9!eKBhE{_$T#I!VzkSarWqEpB)Vi+4?1(M zzSe?&l(aL)n9C|T6SeLmm2p&jp1GwRrchJ4p3iakkhKh$5E%t(O9*~2A%srtTS4Cb z`O}~LL-?IfZUA6%rvIDGtQvV! zRlN&{jdo=K0^@Uy!>T(1@@nIDXMGK(T-(m=@Ht7LrI^CUXAGk6pMKw9>CVraufORn z;rVo01>T>MHF>&seE?eJDUgcV0@6dOV_-$MI&7;KLHqp-13GcNUig+fX_pFVgUde#e2NW8Yu%aqIw|oJ7l@KK%Zy0X7V$Qf-feNz3D4`U zcWQ{Wun-^9HZfouH8;rRjcT8gY z)KgD6x9tVRhJpL;zwg+c$813`m1%vsb$+%f|I&qRrfydDGlWsKs8OT1=)AR^6ZvaU z&;fFM|NY9&jq6($Z8MS-O37d@~EmX9Qji_TdLPCd@DN0-||Z2V^ObQQ`XAZoiE;!Jm;a zBOU?&gzJyG71>Qj3_(E+q^(wSpiBw7E<=f%n31#(fYM_c6{;Uj@N)V*q1J!6_?4G0 zK6&+Xq_18b#)I{G-pd!KF3kH1Xl1qzC&kvC;fB-mP3NyUsIdgoMyxLu4s9xFUy4{`qM6F?r@8#^(EghK%g{1B)Px`9G+OZf zAD_)?U)NcwirjLjn$`xF$tPn=e4QlFV^ zwS8_1am|+VufBTH{kP|yd(MQ*Q{9#UG^P_i_~3)L-R5&?_kQ8N`|i2>?r9~SX?*^) z_h4A8BQtx$9DsZJe{C7Lnh^+=`C5vnuv`Q17lIO5RoQD^cjc^z$%X!JfN4jND~E8? z&AL4wkzvDMmCjOS;Rw1x72RzedFIMSsvU;#CCy4x4fUqQ0(;Hbm5~f*opkl~Po-lJ z6U`Qa1rwm8%4C)?C&VwV)&Wg0r%k_u^N6nzTuQX{5S48xZHZAtM;)2of*=i|+g`O8Y{rOf6w^a`lTPCs}cN(ze zQ<%+5FTUvCGtWHp@WYRM=}V89X0iu1kkx~?xaVeWaoOR*FbKShBii+q5be%Zbcp|a!xg_Rm$2>;Tp)=6}t6tJ9;TdzG(K2(vQjrwZd`(*f zVbbQeHifzuulTeAwS~UyIHm5X4?gnfBctD?>zzHPy$sLCj@@;~ov&!VPiO!zO=$U+ zmDS#MEuMY$S=JNx5Rc!#lkFl9pq9BBh6m6HptaeUk-Cg-X1_8qD$;e)AvG0^*}&z9 zoyGoz7i9n97hc^(8=Cg9#~%CAm%j9suYAP-pi5iU-EsRJ?*8d-T{UD_;lLW^C1i$^ zsp9Du9r8=@P5)1=E?=lg_W}WfCXUJV04Md|LR%J*a$p|fyQHV&4 z^4bF#iiFg#?=#nt1~38oI!UxoKk71S8Napa#9h}K4|XoAvt&f!xZ2q4iJ#4_&K0Yj zvwy-VE#b1zO$<+dtuM?Ahn@Go-k9wT5bwO5F`k_m8>BEiT~dQsg!9J?h#fZDe0QVP z0(zr~{kdzsL*nCljw}o<1X+%G2|Ic6$tM_Vo2X%cyWxp=Vb2yA2{r{=iv4N986@T| zu_nVVykx>GvDMvfyx|7j_Gdr)nE}{- zz+HD8Gm1+}_peteYim=t+itzxoq$hb0N^9I_{};Kh-a~vtum$w`enO+FrarW0Nb_< z-K0lvqsFC9K9g{o_UyTSbUUuUf(;*?Q+Kkg``XvOrgw4uci(+C-)i008sqErKxB4x z=v>rQG;8=Q#ep__TgfO0hQCF^sZ%zi-m;tU z#v8A{@#dQraIe1d>Z!N0P%uOx(~ykp*d2G=cKhutjhuI8gu25Z(YwRURI|3pewG@E zy-oh`vm|xPsxYj## z;7GlvJ!eL*{rICZ@1H)M(IqdLN%-5p{aXjSA9(P=M<02_?Ez-sNoK23V@8Yd+0)?8 z5>pUkZbi9PkBfr>*K9pqTJD~|>mXd8tAJK4SprX?(t}Aze#E&`(6NdHM!hK!3)Qkl zcb)P7{OteSapxW1|Ni&&J8hoz?EIZ!7{^6#e&gm(VgN8HAgfkiiYxlgJMZ#qCYn5s zB$#qO(|lhv-hXSZ7+z1uN>@vUpBp%>e)g>6x=%gz^xypKXQuvK`Mvkv^Z4Um`}ViL zZ2)k?%~lT&O9$p_I;~0E>Gx0DDH7yuWl|Lda|7^DmC6n0Ko=D@+uPpgeG={DxK+=dZLpOF@*3WQ2M$|S9Juzw!yhpl{Ws&v zjW?Pmcu}Pr#XtY~o4@{5P72wJxc|QUEfBLDymq>2G(_4-=3Vept1KxN4rZIw1?5N` zz#{P?KvmC#G?xsI73SY5I=goS?fv01Ec9mV{5)_w_r4`u!vL`U#o=cpusm->@@{NH z&DA-`IcSKNUViz6x7;NdY!%!3s z9n#6oQ|f$329R1eXf^i?-R@&?#ueK^@4fY2yVK@_IeW@Zzi{of4?q0yqmMp{k1V8m z?RDe8NgG5)f_|}#y(6%?=>HC=84Mg9afI9qg@GRNb}}|_&9s}Rh>L9Eb^&qx)fLA8 z(C$9{W8ox*E_MbcPG@V{A#*%uQ8&Ypappe|`v|lu`nn6-^Ae1_)C)$h(ZPof+$2qpZMt$8ke>Y9G#TxU9-MEMoHCx88u z=Z-(8h5q0V{@@2c`2DYb^>Hw7PEkFkQXOw=4$aqJe``MY>6bQeYjs^{)d?^p0gRpx zuSB)Be)4sgPxM|#K8FksViCgl}RHqefCo<(K zKNUC=*0_~~3+ld2%-NMS7LTbX7q8iCoX=%U#C!W$*7Up$x+EU#m&lch=46=kx;zUy zIRR>CLWTiz3)VGe7#dwrVe<&N+jGet{S(J`>A3Xv<#vTp53tv6UexBbjY!HHGbO zFC$&hAf0?Pe7E(Sef^h7l$GU|)a%J*U_AUhp5WF^;7Wn5Lqr|AuO+Ge0j86oHOImgSK!pfad@1=RbEsM0eB0I9v5iC%x`^7;Sxd2Y{ZUZl}tF zubs^M#|h2^5eOs}M03q&>MF@zV;^BG8n@(nWiVZPfb(2#@&TZwb8 zRz-jFw}0bs)YrfM^?&&<|K;!f-tQedcBeVLxj6#8$^sp+#?yo*)Ot>9?-Di8*_TY% zWp_*0-f>bcBrymBuzhDF=xHOt-hKbwH{N=Kg;qlzatFBIRCDha?tS=?hyVFM|L1R? zdi&&SCv6QorC+c43%dsmR*@TZI`5mh|mIMk|X_X3rOEBku2b3%yuh(#s^f^YqW;kyz?%k-?gx`(y@~5>h8aLh0ZGu}584S!KJ#nH1 zoe|E>;txNJf1zI{!6>|Q#Kbg5P_C&NY5FX~ap>Gur5y{CmO~#M@-Pz)nwWX83NJ0t z`d|IZSHAS+uYBR&dwlMaQMFx7wbV`tt2$@cnMd+q+W`hu&S6emWtgF!`x}xjlN*R1 zuL-#V+az2Q>a=Kn8(+pkbu*ci35IO^Eb?A|!=f0@Jd;t|b$$SyYcHy{;p24?X4Yw0 z4Ro+h0o-)MWqcW&t$zc+rhO9{{@1_yH9P1QMiaS8GR7zB&R{kH$_C(Jv|{8|w={Sn zt|N4X``XBXY5VBt?%lqdYGk~ufU>6IVm(;_*lIud`pIAZ;+Ic7{q!RbKm4Em_>WDQ zEuT%aeYMpKSX4UqPb={L6xG@zXS2d&e`Rh>PIA@^ap<Q- z*)+r50@J{^-hRuWA+AJFDR+_|ee}_9efwL#^E`Hi!<09IQ`lQ%3bp2JptiS5L#@vaSQW*nF`g0<)%+dD9Eh{x3k zITB{MsX@JXl#hm~z37}ROB2PhPWsiXWi61ouV5G{f?TTf>HC!SAMkXO>6KSreeuQL zIE=`Pjs8xzI0;bmcfYxnlAj8?hF`Q(T+);XQ@4Dx>tl$SVU+XB8psk{+j#$^{+csW0OgU#gtRCEtox^ zwEpDi+YnJE)b3EXkl2!cBNY@NRynmfV8}4=OXMoGS=4nP_Yvr}=~lrOz(l&+IP(9i zU;WBN*fjY+{D=SNU;K-I;WoXaHNN&>GHuCrmqRqij_HbYmHJg^d7IThcG4wYn_KfI z<}%ty%I_jliwZ3UP&*n7xPEsggxhX68r&*wBv9FGEa>#7PFW=&=2;&4;zM8l@|Pca z_#xYpHXHgmXA@6F*hgw(O~dgudkm9kMJ-ePbDjs2ohAXh8^$Phm`&Rr`A5>l0j=20 zyi0{|rowb9Y-ZoYY@2;2r0iX(v@gBc->(;cYS6T49pyCG%g!k|{TXLe6S&UE6EW-V z(rK{j!Zi-~l0jCD1UD_sGh)|Wi}<~F-m@#{&Jbfje&|T%rwWxZq1Iew>^;tu0raH_ zDlKi7wq37N<2z|vg7plg)oxiy+c)m)^pm=P2Qfgs#*o0^h1sVkMQ9`kn4~%a)X2UL zd=X|42tMOPah?Z{E~v{z08mF)Av&x81IAEqQWni?MAOH#ptmqwzHb66a{K+CbLR|3 zCb_12gp1=Z*Y8Tu9ss~hAx_^YP@z*}PBj#$TH{^2o_+0>OOo`(zd^G^g4cY(;Mo8@ zj*R;VVB4*=E9Hsf!GS`5Iz^VHF zg~1I#>=Eq}THE6a7*%TknJn6rwV6EHOt`~i0k_`{FpN>cn{U18l@{E+*OnP4oD}@} z*S}^kK!5Vpyd9PaVPdw32^$dBDu=(Y4RU25i`4F}-D}tK)Yi~2kB!7F2xRSRy68;- zwoor@HL`AmCGj$G;3=~M41$5bAmD;BwiwMn?K_XV^q>1WHMASubzB`kAj&-<$E3?WsoZu!qNA=fAj}{_Y z)`fE)4=z1E_z}dDFB0UkmO9892t@^wjwF>B@Cs-vswUZG0O+qjDzyA}MgR)o6{DCq zb3jH=0jw_w(-BV)?0hh^M~Zb$NBH%vS3())YH6sCyc=N~Q zS^;QL>yD*1Nle2Q9Xh-PfUQ8*5QC@?3t0eZx(eNjQpCC;)dAexFkCgvqI<}7o(8XM zYXiWN6<9%QnEi?_IjaIw){C+;^4e=&r{Fm+zMcY~edgIW-+0qL$)k@ws?8{2t-fpW z?4G(tyvJzsB=7)a1PcR6u;vB{=;&GSpsMLEHecYCb|+pq;V}ZKs{5Va z`OZD}-eU{##eaO!QDr>Yt6V+t?6UT9UCI#p?z;W6k$*1+Bh%8fh6bXJt)|%QcfxNJ zJ|VVcGKepf+sFvmo%51%cNic2&>oI&5Z-JUzxgIR{d}12>`FP>NT=qc;;g&{B>Oov zpVPGR>c=(VV-4EDlZrZ-twJ}t%tpO##Heor5N!s4GBg?&?YZD2TXp)i_DTh}ID=}b zLhhyRDPrq(g0j}_r;Fd?0ANP0hIOmF{CVz=@Zor5G^io7M}kf*w_ig*4qo?IV1WfN zz}C0m>v7J-R#Cu7A-n#J>Kg;N-`x3ic;v<$9?|tK%K$(!dl#Eh5k;?d#!;^Z^Av1*7P;o; z>}bh$5H)_|8{c^3kw>T{=DC=gY*(Ahg^PTT_b*-hV#iCoOx06bh{{JO#gz+lxZ8S4&%gR39nUl-HBPSxQ+B7@BzY-$DBZv-&RTLh!G6z~Wkho!l5HVga@X{JG#1V3;?7RnxJ95wJ`zm#8&ogMiOsd4qswI!`ypgx&H~1@t{@< zwS6Re?fzsmXy$!g2{uFOB570&T18wn3P0oOBxt|7(K73@3;-5!Mw_JsiWfDT^FWaG zzB-VpEJ6Eb?ho#cZNjgOHD?o~;k%Km;V}`qrVWf}7m=%?T=o!l_Xs=bcMpuuLozs* z2C_qU;=~E(;`3y7-3E6e-tqsheEG|s=v6^vw6Uz|c?!5Z$pq*?m{El;3cdmOh6iQ< z2nLKp2N-~>I&$s?0J}r(b+=CJ3t10+`Ac6ucI-}z07fNhJ%m^mVN|r4^tF*}EGGpQ zK7aS--pOX**QT6VkIp3z8A_ZPJH*-pFgTo-_M(fxdG}J9^X7Fa(lT|)kj|aj;7V*P zSP-~s9m+Rbfy}Cj4>8Z)e0MG~}WZt~tumGpeRe71gD=)tcaPL9a z(1LP}!@;4iHAOav<{K4DnOjbrhhZ#hQN_hMt|5uJ#$;gl;7+Q#IK>$Ys|Ruy_*w^q zphEV0>NP$!xz8!7IQltIR03q?`rhEuwn}fdbH0vk5g4j#G-@%b zFPD-(I`ff3B^v9p3;=qm-HcZ5kN}{u>~VVn2!SY>Dp&?;+38MzT#(wbw}5&Wn0n@X z_J440U>Th-pU429tYui%ba!}au{s^EvYLe7_rf0^Totv~&-?%W@Bfp>AOD)YI;|Cj zRM?&EWldFni`}Ix;ZZIcFlnU@vIZo<3xgO?b;_mja)I?#F5XGkyYITo#)6x`?ie{B zd+$B>dZyF}L0zo2he`BHKH*iE79iIx{o*250L*W)K&7>Z?`u`2OKdS*)dgl~ph(Vh z863_-H*4&?WZM$#UdryNyD(g4)OMQ2zK}7%Vn93cYhEnk03vs$o7?e2Ka>NeEZU_s zd3#(>DyMqQ0RsRIG;I;!G=y8k{3DEzjbbfZ&CxDmC(}|y2UVEC)o(Y}AQ;tzS<(|J zQgs-=6E^V}+Dy=BRyqJ(xTCH`TpK#Q2Q&`@`IVeY{jVuMgpdKCnB{O)g+fm7B-O^x zs6wZVI*W3@7F&m|Ijot4{a*pt{ILbp5-bJl55IT{lYQA1Y3mNi96@iXw{~nFF!(E=KlZq zn}5{KyuS@`bN{b@{Ts&T&ZVFRy5d`e_@p9jbIa0pudh_n_I2cHZMEK(NN`upRYCef zcXdkvf!Q2Ce%w(g?w zX$?6_S;Oxox_c=*rFMy%*Ii1~4f$w$LGBOR)At0sHsy0PE6+ziWALU(9Smj!1(^+I zvhN9Sub#W+U=IUU0LB2j#~wRpEEy`|D&jYIiMWRfLr$>)16tZfF5x9j!z`x>mPuPG zJG+F*#>&EE8X1G{efo)Fa`!=(Z3B91zkNPa2e9}&G8a@01oHq;Lq~VMzNtPjg(8*6 z7TFe&A%EjURUa&)pd7gk);U`8U;fK~S!BFa5O>r6Z6rH*z#Y!#jz4EkM;b;T*!9^m z{r839YSb**vS?#Vc@9VeuX%q`Vlu9Pj1Ki#>L5Rn0iXp?T!(F{JSUA`G#WVeQ4<6l zG`9^g1Au9cBXKstz2g77-~FzepQ?{c-ci%F5(nE)23S^*B+KX!fWVSEG!nd|?4-18 zg^{s=G=urfNNF0qI~}|l8gc6ifGt>aB6dsJNZ@@VRu3riD(M?;a9h}eW3Ki@a0po)6-6*Y zm%b+95D9>?%UYx9C;>L{kSqM0-pIBny*Yms%KBoU{xP>W_cGVUyQ1Rn0ZEx3q0cZ- z6s|_QB;_`6$TK!%6R6XRsF;A3qIN>dppF2kUDHwhMuus<-Agj}yUzmv)>^1bTFM9W zn-_lb+;h)$8D zkReP*yE>bNcWO7)NL^D@;~~@H6=2qApqq(u*@H7eEadp{} zrBke{FiKwnyEpf80BEN_t`n0ouqDH7;VxYjwoTncuGM{$U|?D&BYHu=?xpOWx_dz_ zRbRXA;BHw_4R`a6Hya5As{-E5l2^0BbKi9)$S9r(9^x>exBO&i*Yr*aX;*JN7sbII zpy$P$5i*ao1i0Q?qwF~yKEib*)7BtMn+IT|>|PhCDA702!~_$jjN^C0Cf?3}5XbwM z9ZuKs-dwy|%jDWYD!?^K9jOFh?inDIZm0mvxjOsWbrNKfFF!;6t~E>p@B;ci4~jp< z?DGJ?`)}_5C{nBIz|Qk&5<(CNzzBqrDiGXXbd z&|FqPp`*AV!0sg?Q_+^Q^SU;$%-EPw!I1E6QU`rz2K+ktL#61${(Df48Efpzq z1cmM*;kKA*UH*G z5I82s66^?J>r6}Mk}9;>w2;fITuV{6h0!$G5@^cFuI64onOvWd*RT4k8MH=~}pUbYQB4 zGx4v~yUclsu!1$}CKKL3YuUxyDhjC*CMjzcpg&L1S$G!I}{6%wZNcI`wzfogWqqoB3uz&9{biLDAFwIv_XB_OFO zWf}OcW-CGe7j?Jlo8S1RUErTS`2<^f?2*Sz2EB#jd);m@4Q%r#_=)tD%D8*u2h zgIuNZobD={8~C1~WAewJJMNQ~9-Msi(ML_2xKRZOY(|AOfdJX9uv7tL6D*ONFjhDe z%Ul9`4eE4`X_I`*6fI?UxAktq0=h)TFhXNGH4Z+L!(1w74t?7D0Yi;R&Nij)j3V=9 zBIOt+>BKtVol|e`^8jFI-ze2dfRCN!uxe9w>(RiYMZ3=j@K>RmN}X3s&es)Jl$&+E z0x|OpLZfN`>M|FIjUGH;m$MIMky>i2B5hx;-*t;xHlmlcuH9-a2{AUyYUCj64mQZT zjke7=c#hgNFs`i!S~7q9#y7v=9ik^roNyw@zOePNNz-6EfKl$J4sFQ zw~hm&o*3}YH{txe`syn_CFf1kS~-XP9RcszPu(iZlt;WS*JVZMf%!8ZTFP$r0&UMC$&vC2ea&_Lh7AThq73s%MNP zY5R2kUnw0jv#%i99bc@Il#-z@|7y?3+n$5j%D;O3pdV?Qbwop@eceo@BGXO&N7&B+ zVB6`xLCt4ldH7kf z+`uNdze@9*Hs(+_WKI8p2^!n<-}a9{v*>fl=2XDh`4`-XS;Ub#BR=V0vMq~@up6zi zBxpD-TW^Rpb)|i-UyC~FXkBc3R1l?;Zog&rZgm(0t{rW-$)dmtgR-N0zjhVSFF8Oi zqiftoYUf?bwxsOTo%oE@24rktL}dhuSim!C{^K6c`){xzL+)ikgqhG>kG zg>itJc&*}VcF@_oI7yi*T@=46%5zu zau0>QdFo9AfMX#UP)p(6b+^sFR@~)?cDaM|S~McgRv~Fbm&lR#^9r<1`r1<*u;7u@ zV@EO!c*x_#i5Gk}(%_zNTGwmO+fmX9%GM0$(SR*eyGFiUZ8iX97%-iB?)Y=3-ahrv z+YfoCthXJ+ZPd+0&WHWTXgeCG8i>0c(px2UTeM&>OEXsVXsIrGx|WC1?b--sX^uT2 z1ZJGG^Ojm6#Z6=lz$V_8rm2mha{n0}>UMEc(MGt=92(vt(i~}Zb(I@-O>peTx|!ei zyv23b;jo_rz~&7A7$3LtW{8%RKvWztsx$YoA2M|yJYcGVU;3>*qLDh+t`CpRlmmMO z1FFzQ=B5sVqy*6lUHb!l7eoVV8>y;7V?$}8E6psbi9i;7nxJy*Hm}@dA`4{maUFaX zg`5ZMLdnp^yA!oix}cp1;JYF%(i$0i9R+b2XWuyMJM}%CY5W@f<&LV$MHJEgWli9V zV|5XwUr|}Q)>J_%jjQSJJai|A?xPncUwze{zH@{2MPa!09tO}Xb=95d;6d+(I^xCv zC&QyzLj!*K)Gu8v7Cl&FQYA=g<$F_+7ZI3&RnfWl&vcvfl0W3o*b+aGdjiT#WZ#KH z>d(!`|7YVTov3$%vUgR{pr%kVN8sIyM$p9#pvsRZ4N!#+x(UL2p$Y2YzE#*LV7WB(g57+o`U|ge8Y78SWZ&9j|0LFOzJeK^qtr!W$g8_P|XCZ*sub zO=7nMUVQ1Lw@;nop?acf!-q&(e+Z`0WryMPjT2^0hz%P6(ck>$H~c9F<69aA0RBhN zvltEllPbDB@5;=iHvcZD#Vh+dzk$UF{be$rzL3d#cJ?PHp45bi&(H&uyS+8vn;d={ z_9R)+%PX`nEurikEuPb45mJB|TIwiF1GSNo%@xiUhN4jJ+W^qde~luQN>C+cQ}SfdCBn{xBao4KV8ID068AfO#&zEY03wf)8t`pD-*xKSzcrtw zQX0Rgh6vs_?N^ajzLqjGHICs3022@-qQ`M}H4~tE<_P zRNbe?4kd=R0_KsRip$ndiVdPifm{nh57)W++EMUkAf$XP>Ll#sEO$=g#LM zmdw}SE&SKgHll-glC5oJE16nDP`zEC>#x23%uNi{r(kmOLA}ge-hJ=gJI>v~EciGo z)jVC_X0wFDr$PgLZT?N%*R?Fmqyewx8qon@GT@P`fo<`l(2-GH26BzVg+?jI*joi$#q}d(5l)SrhzMxcM>|K&IN_rb@ zD#>l$ez)kUQ=EBTF~G_60Kg3R@%A|=Td|V37kC0gn%e7A&pi9A5yCUW4hXp=aO-Wi zdUZrkK;;rJK`gI;vV6Q9NGV|g8EDZ!goJNz)k!cy4*>m>=p4{gy2}K>G*Ty@laku8 z3=TE?VAWh8lqh*-nP13dbYM`9APa?vu2FW>EGk3hwrLlN)E;K~%aXV-AGq?wwk^D_ zS!pIK zanl}37}vqAVXT%IQTR@3z;%>6Fc$$VnShstE|WGnFH@KBCgmP6=0^_yP*nGGJokm| z9!uXxGGY%kcH{0N^Un>dXy)Jn)ArmLxcxToP0>yC3+|hzcJ1B)BZ|1)2)xjfCp$v2 zoH%j9-~o91owpx;_+f{~92v3?ZiJAR6O@q$(^nR9?dQ}`VxLo;nUT?ZKa zqZq%kx}OQ!O)3?^JK_133E{q;ySd;cbAq-z74HMycN<415Rk}OFD=l@%d_iit(rR(h@{A7B9M-zY z8e7Jo7f=-6kT*#jIpX$!y8>PyVFpn1W!AA}dfxV?i^+%{fT!D#wjy*TuO4!2;J*9s zyX)*(%ZsK#z$s{DHXAc`!+j%nv?AHpQ@6ivLi0E_P=CcZ*Rzh^c z5J<~KnsWVvUUY<-6i-i2YGo?cylSY6Pym>j!gLh-n!;pbjOZ){_L(^L-78x$je-1 zoV!w;*eh1}G|x_Q^xuT_?7_Uy*XJIGzztxMFwNu(tm!>@f^KIYbj}IoC*8!Rk6!`= z@GtkbWQ=*ItehS9g%YQ+|9@p<`N(abMg)T7F@pM=1RX^ zypmI`qM69%>P^KGt)$BwVG-7ZYEuYu+9;aS7d4wvSJrR0;#Bu~{*Zi{**f5`!#;-& zGwaj0o$ifG@4ok)0DD9}t>hIEeIU9QQq4zRt;-;UZuoBOZhNLSD&M+nyh%_C&{uf} zw?RLEA?m<1AQwP!qefly8rj7~2lJ7g7?9*wskh#I%gWLVGo157(8Pl>Kt!9vY*BOk z%KBYuUL9M9L+8uXD@*Qum1fnQw`H5;gB%sFn7`VhF%0MkxncJ1ySAO}^SiUiu!qLl zGGUlU>(eHZT-*`wZ{!d>GwelD$}|QrEm;t_3Zn7?T)Q9fUBKoKzwBIVQuA;vCqR-c z9Y(VfsWE`SMN{XQa~3q)PB7VmT9M5>rr)&^mr)eN8)ZoXCmPQA3vmqrwFRBRrS8uF zkYXwT45!}ncj~Qt6+oa+$eL@We3*%N!)3?y=ag#XZf-kXbB40(vLnfroG>bwRyiC; zrsCE{96i_#E0)c~Uw`v;yO!3xZiP7>XN0gyVEx?(T+Tv zSn|6He?~O0byjz%W_emT?*^-PYPMr#4{Sh3;L3Fqw%xn=Jdf=YK9G@cq3~ttv1Bs_ z^tzZafOnGXz4zZ^LMWXMlFFilWq;{t4Ffut$^Jn@%FtshlVl~_K&}DJzl1T@U-@+Q z-x0rP5^Y4y{uy-x2HU4YS?ks(k1du&6lTWR#k}dZh45rSrKLfx*lyk;p zcU_Fnd=rh+DLF_(V>LdN9Hh!v6)omcLI$eP^b4NrU}alJUg2>yBjMB}CF_8@)}5+# zGV#ChM(yp>ogaMkf!E%$*R!WGone$rzk_o`*XFaSF>4EmK0? z#_y!GAzKrSX!CIunnRc3x)ZBSd}kS)H}hgXQkBAx90z><`15W6^v4p?ZE8wbw!K-x zXA-lihtFg+ECLk~aX{c$>7`z!nrvpTL{9|SPAxe9T0lZ?m0~Co$_)d?6YnT#PRKX&z`zY`0 z;N0occ+fK)SIYwSgUtJNtq&ZNX!CUyvPkfuq&n_K`5o}7Ini|Ep^JcA3NYYEFlG>i z74_TZ26WhQM{y)3ezaWFG7R5a&`a80OY31z2X8*2KLHwL5~Z5!epmqki!Z zzi>8`6`}Vn?w+mU4+z_G^m3X7647bsE{CxVzw5wATRdl?)n7kcA6S8Ug z51IX@DTJhQW%}Q+>o&!0MOA&bqPB}6Q#bnR#C;e5sLwzez6TG|z8mh8)81b-8lPsW z$M9OY-Cz!)LdNk^XpPndPL2k0JNYME>wj00U2a~6lU46ggsWtq zvIa~8=b7j(l1z;{yDXIFuE`8Jw%RtpgxaM@W9J{9{!owbI88ivWi$2p@Ixm@^VI<+ zsPzWn7cb7J@3cfVV1!$Gw-ZVpO`O_p6Ue!o6Np{yPrn&VwJc_Vk-63+VC7}%9ji&@Fs`YJByt6Z zJvQN}L07+2vhv7AM+7~K0Z@Bp5w*1JCJ2TR)B}ouSk%WKpZ?%9n3?&*kUncTsIfsO zaQesxxbuGS3>1Jav;~l{^-q4VLPbZ?kXAvuO%rtLNE$$=c614Macvv;w4z+$m1XDW zsi&TL;e{6n=7o_&RJ)uV%w{ori$azqE;)~)FAi9;y-*H&$-7Cj6n-=HLS^{u)2|Qa zzM;7EBI;s637^dlC=(Woq zALcK?FhIbhRCLxb&Jw|BY{?4?fo0s_!c2zZSAy)Z^cZ)Uj5mF23mDY z1TdO_H8VF87IxMIq6UPF3LwV#@!4}9owI62&@IJQvLYz&@8q>VO!OH6fo?MzGzJ~} zo%#tGgDC;@5J0>BAv>ucMM=`afYE^Ql;o*@`?IgS{EFux?Ei56#w071CRP%g8QiV{ zpHU4gT>su`FxouLUyejk-eYRYOHEhBDRz#w`?Lt}8lrU%pl0!dqV6|D6xxk_6&4@iOh(&s) z#rG#_AM&FQKJrvr*yZe2g7HA}Wk|@9Acyd*vJ*T@l8^P(jHw%YJ|>ae<<@0ykz6}S ziv3q-$UtPPs_xynJLd(0PW!l}fwwao27n0fc3+(#Zjo}m8b0qDnB7F`$!yxOLJC1~ zIpuBZAb|N!CgA+Ol)CwK=+m1Tbh0JAb@R9-vznrAgLLRKJ6O+!gOg~F7gY6L*lgJo9_ zQP-g;rptPjTJGF4SWo$_jknU4)rT1d(eOy^ritcthB`F?5T$%OD+Y<{4qj)-)-XM1 z(Q^QC&>x&W^KNGJ3GTuj%y8a8i>Ldzw`^dj@u8d2HEX|`g*B!{b;YpRnw@zEs<+^| z7iaG85eOTJ_~?7~p!8n0G5Y+{Ba9kd1wLOIm^yYEotMZ@Fo9|q$h9`F&Axe$rincs zJTpbgSu-16+hK?I59NG{Tr8P^xiUo-%!%8#M8L-Z1aMl^yvH4hNH^_i%Y|4G-IV|WWYD$ht)8S3 z>5REluME5qx(ba>t~m_+0HhFAv_sU#3)eOJsSpW+%yoYT0MeWLXS=q7dUi=^!wSUm zO9JWS>%coSFjD7w?eLq2mv>0|tw%?_WpJ$tiXAHHq)r7hAlD8}GcZ%MEV7$Y_kh%( zXoO~x9VBny`Z%B9;JdSpG*3XVomzMP=@w^fVOZzB5ck6k1{EC=bGFD9Fu(qW>+d{z zXY8WwQ+EQg8zrk>s<;ZuT1Boxp9c-B8Z?bA@#NMIl*$Z zJg+f;Ik7j^C&d!8#{mAE4GWNrT&ih@B7B*WD?5JtxHYKh?6<%DZBL4IUa4qUZ~Jk8 zAiJvqJulD(*+_Vyd)d-Z(^azT+QbElR2PcOGedbpSpCiwhc0xm8bFmZUA9H)JHh+E z2rV0Z?#}=~I9js>qzJHyA(ff?@dkbgxMCB zci(rQ{_R0i;CU-c>q{HmoxT}rI$1`pgNO524Xf?Iiou$$w*f*I&tA*i>S;D8mw zXok9gKW&IltRd?JMw7oIhmI`Y5jOh&kaI6}9K=_F&!PsV8^1k=Dc_Y^EKEB#Fh|`2 z02>CnrahrPoM#34p6s;k%U5Q2Q1)*EueXdHl_jB##iAr$)~GYGnHgDu>#eAK-hg40LW>m?1$^m^dRZ^Uynv+gkWmGEu5Jwiy;+~`??tB~6DU)2SMG2oUo%+hG{T<^2cSXV%U ze4a&MRMU9i<6oL|NGy6?6Jorp>&nhX3UZu9OCpc2An<2;4O=l-u-v2Rk~Bu ztjRJ!Or{B!B|ybo4X}!+bCAk+PjZ?1h$KV%{RLFm#24OwqAkx3R<0@_ zz)dv0X6}hFJHiWLW})&+&Q95f6@Y&GkH7gxBPt=thjr>vf<_&Spbm!L6rUcS1VRj& zrF;W!vSm$2(hRKBJ!6)?Tj!8y2f383iOX!CE*#B}u5{S&<&&pNIWPaT*4zvONHdp^ zvTH|n89qjVHO2O6jd6@ex(9g}Ks82lo0bG?8F3r!71p1JKvAEEYAot7>;Y~x>mq#x zhH}-QD`-VApgD2M5WdU2gKiy|CKI-EW)7`z{@({$hNzGNw_a}Fj(l!Ug84DNJf7;% zp<8ag*Lz*)v-vOy21Lgva;b(P-SmZdb@g$}sLb6H}*w8S^#vkwD+d%vEc7-MOePay>f zVpZPc^n;pI7qG(+bP0sazeA%bQ;{V>!JC*R$rg{Y?0Ov6mAN3$v|B2$f5QM(56&^f z^4Fs{fR-8ry8~FEUu(O8?7V|>m)p_4HfC8wI(Ll|OXRY(H?wS^+%9qvS+--d4ep{j z?=j<0-LmZ6^X{Iyr9a+gJSpWUelPIzI1D{CC|tL$@P%tE*CHR7OK08Nky#4-ds zv9Nr@4*y8**-h#E+8UqJx7A5e;!t5K7pdG1e&cKq%9GbY##s-YlB z0s{~LOWk(;U?jPY-WmtHRO@4qZ2kZD|Nh?%fJ#zEhYW*}X`22A zS#Ve2>UDPwbmNo(#HL4d9GYPOnNb18*O^?= z{Mn!VnbSc(_`wezeDJ~SBF#$y(_aT3@Z|cPhxROun*rRQLdz4bh5-e4tHs~P^JhI! z2PL-1kA_r2sB|H%D_uq0T?jdDv67)pw2?W^gRQM`fo~9 zRoxD~#HIa~@jb(9Q`!r5Nqa76-w3r3Ue)?(egVAX4$}miNlP+tQ?4N|Uvy1Qt*@+M zta}$`jnCy1__NOWyaw=Dui)P0xAC$@oO!e%p}UUPN0l{l$tLXJo}I{b^tz)xJ>}mW z5*@qz#sF>|PcwSnpRecGe30&^KmDnj2mka3|MZbZAF&RQD#^&v44pUay^!7slBJD# zi%D1iwJwS8&%NMG{z;`Zn+c#{(JdLPhA>jQIuN2Ameq$r5FanN1Lrq{aMw_Y5g-RG zGAY00Euf$M>}OsrbJLACHv3ti6l^4Dq-%!hF1{8DOIfeN+iE}?Q`<-q1;Y${V+O%R z1Itn_n#nTbCc}Z*FPWFcU4?02X!wfO#=48%S=geS4*!(v+L=`4tNN?HGSOy8$OB)Q zXy;g&hqQ_qv@jQgCWcf`JD(IBR&iq!oZSKN|qN7NFJFk}XAVxGLY(hlmjQS?Hq0S_&7r ztC33bW%?$(wnW-6gv}g#!z@>na+#P6L2J2y;nQ$Vy5gwN%6F4@~1bI?+Vh5MqeWva5EqnfJ9*XMEgf{f`L# zOBF&{GjhL|GypgxYc)hu7~yUhDDJuV-_fbOlo^`(sfeh*3h*O9fRmDoEwquc6YzN( z08;&V3>bB$Mi$Wa3JGpSElp$)pBGr>!uh=P7eugR4j$0~ToAXb#M^3MC=;;nEx2uo z`+(F_)44mOiuV8jKmbWZK~%R1>FOQ)76=mypV7-ayvr(DbgSqlG!q;;cgX4EtSYwh zRElneXWssHW4xAa920zj-{;2c&G5X$$|leupCr18!kq{@@#|mz=udxi>g`kC{oZ$t z3W%)(x>x2+i}g+|&$_o8>}gg@M-8@p{EbG0Z`iddQ~CMn-_F&2AAvAh9F5eX&86|K zjgOO0h)Uy=D{54e#Z^Fg@dJP-AiS-&OTw}1iKCJha247^PKj0WY;A$A-_`358qnUI zLUoioqC1dZMPJpxRSj&Xfm#jNc;S+2_Md@(U+0LsTXBybPBGK>B(Rr*`f{Y{d|rNd z?X~=~F~E*ZIn9H7z2$=bc;dtfw~qU^urZ)-3BV(^7i0|ZslLeGL5j9{?K?R&HegJs#$tOMkrD81#hW!3A zG^U3g@~D#3Wo52Jn0_XIyP9QxDss};unYo#VW^ZEm-yXf+dJj%d@e3k>7T9u7bo`B zT$f7&Th4&)R!C<4dSaZ_rH1x|xU?y9LGi7J8TUCwr=$L6j>*S`1iYx-Nmzc zy^SZp_oMH@1FsnzUG}*sb>!< zcM&4SBuTtXb>+fq14m0gYp0^M3+4Tis&8&st~yr!`is{Gl+Kunt!|PsJB_}nq|90?Oz7! z)itIGQFDbBY$@J$nfDW?*fn-t`XtcSRLD1V`XPw z7Gr?DAg-TVOKN7Zc20DcN~8{r2M>ZJJdm4!9POO5DRj|WK%Snk`_I!W zU~9n<58wRIcxqImYK^T+X{3%3zAGGz+Y~)IuNw+AwaTi*%DMF>?WBk;`#t z=rtef-~vA`i(9^42V_d#-^kJ`%aRNgOVd7YmnY+pLg0vw&=O)9;!qNUDs(;NMp-iwv{IEiHG`Paw)Ck zNTW?dLh&r75C)mlxt>&Z;4CjhOK6(9%9z@C4Nj+&-eUn>oZMkRkUFns&LEd)Gilhh zfzI1->vmaIQGIHcs@+OOpPC%MWd*Nyu`96pjAFJ6W`5p~u4=xO{xCcG%G3R2Th~ts zt%ttuUIZFFPFLcxvh#DuY5a0QaX%@#nMiV%7Nvb2FAz2Bf9&5@qqbC=(Z-KdMvR6Ym-wsj zRSjH08t7hKy=hyi(nm&EfWfUrcOonN=*xp@FMwQnP*akGGs?Fm<65_rZI}Dlu{(M5 zS6+FU7w>+@rz4ZZ0k|3%IE7bUebvE81h{?m@vp+)dh2aD6eL-wr#gm>i6W8!mCEzDm!B2%0k=9w%GX zReBg#fRT|`*DyJgRj;j2bMWzEb)k3-Vd6Dyx2oN8Ax^f;#| z1*QH8gsP)tpwdj}n|S`bd-K#nEo7p2(?pFoOUc1=L#}k#)oik>*WVruu#+pH=^68+ z^ZxB_G2<#}6^Z2!dTFZ^pxP|r``VCGw>3B94eZb6MnIL7pSj$rL&r6l)O)b&uDk>8 zkXr)o_m1uf;O=znTn`_1+raVAC!c(Ri{LyABJmMguq;02?!Q*=NzOIw9NjUs-PBCxmyh$V-lo6c0f2me@+W^nDV8%d zC}7l~VFfghwDSG|Mkv28)>#%(1xy1>yZ@SGwt)71QyXEPcYJfNQDGI(Ly@<3^nly~ zC<36W;fvS7_46~-P0>d33Y4BfAfCP61Oj>H;2EXJi|`|V^|*Eq02u@7Q~*6BZGl$OHe9O(cp0~oe11d!p3j*VTUd#o#VrvYrJK}x- zSN}(U^hb|>^>K>>-xTBb-AsP#ty9+@edox#?-(tr^R7x9jsnm!tX&l+D5#4bq7E{t zo5)C+N#s1BMeO#^mG2^sfqFCQ3opFTK6jM82B=#bLFqOZ1Zn2l{jZt_6Ui2#-FjuI z`LLvFj(jF6<7*AFN2c+XALQ;`ha@uMEHum3V{yv>JIZPRz5&-|Ksf?D*p;tuxRbzI zx;?)ng8`T+y}OcKaijjAqP9rCB5wS38t zlcx&L_}FeGU+aX~O*h}<{*v2Mf>Xv-W2>Kxh2b~fUunr<5VR7aAERLEluC)Vq6W~> zl4wi24E$B#ss=7V1A8!{rR!h$TxFDPGS^XDPsJKPkRhyI-No#p7#x#UexKbQBrIq&bv%bNr0yyyYA;b{!ev-EGN|NF@&xq|=rNB{9V z-~NuS>Lt?lf7~ZcR-|O$Q&pyljaZ)l6s7rN?dnpNA<98~0UZ#lAr0r`(W^$jI zJ-JME4`*KNr)SFQ)}9d{m(ZmHHcM&8N1*_zG6FIVmj8oEGz*I%awV305>7#^IWVhRjG)YKrKseA9;H{N*N znSpR^jnrx&4;9vOf<3iJ<`j3{c_%{@045!`-gfH^*WW;Rsxwsd!pP8}76N`U3TNv5 z*GnWAey+#W->Vw9Fb#BnStuyKlvaN^(0uH~jdcGoq<=Rjr-XE}b!w)N`PbumV8n zCIF1QFu`_OM^dFIp#m|JPGEcT7d8Nx`~US%e!>B@yPP@t4b!M#aSvd#8I~A%M0=cw zj*Xw!UU&V`>u)%ELmr)|sXc~cj-QImHDB@>Fp&gJBG82^w`qU{3+0ou$>XmveU$}( zNnZgl%V@)+=mwg2QleU?0tn|CRdudEbRE=z8&b!Gi%r%H4<;1-_u)sz2J3`(-^n7e z#{=sL^Mcf;!~?_B67nsdDt-Np*M9Y@Up@28GnC^!R&2{KAoR+>cpzvahR7V*-!)lo zCJ?6?w)~?~k@IS}s)0RgfaRRucyuF=+-BNk#@eiVnys5mE^Jj`?+>iv7XzJNqy^Wi zL7Zj-$=guQPv*9w$s&LYN@tlx2Aji~lA6(jUwZlFGwvXAAx?@MI-@_L*Xe3|BJY#< zo1guSIV?V3``XuTzvFh3LVa0JfA?KG1Rp4}516!E`sPBw6&TrS3`2lbM;jRMh^7)E z6(-ozUbW*?ZYc?b#L{mj+pBW+0D1MBiK{IwE)19ERSX-<+vC6lyxO-lV z?l>%fK-^~1)!xir&ON@qJ!xMx48Rp$mUf`IhdXf`>+){9|=-X0v31(Rmp8XDiY zPF2YU264;jiU(v&C7|&z4HXra)-h38%8BadcCdkSzIyT>UwrYU7hm+W`fDd&b3@)y<)@x{Dn%SVOo69d)p z`O9KHvF!bvb(&l}J*93L;4kQyy5U#{=z5m(nQN8FdWI9ns-XKJny}oDQKL zfBf-#@4c5_(qUz>7~t_N2uuW9WX!jfoji@_1R5riFz}t)QCFc3+q4%dcdvHwMlx{{ z+0iNn*JPT#ZIRm|?O^=!m%o%>9omC*VXo*K*53W;*I?iZUBXUt0C*huJ{mON7p$3X zxg`%78pC1&XM*D?Ezqt+7l&{LW;%6*bLKh92M#@O=o@*Q-H8GpV)HpRcjXxC!YB?MQ)b{c5&*e>NWkGrr&Z-=(UOhgh- zcWZDsy(L+}JC(J>4;V1uO)Zb4VZD)|O`0IA-gc?0Nq=Vic6>7ZkM|EYJws>ov@j1N zqx*dTedo5@Zfnl^UAd-n+>?6g%GX|h?Lkxgqn;5v^~4iDE#x6z6}EKnF_~-uXCd$o z4~Cl}M;M~6)q*@|B20yre;I|T9-L(O2wk*Q15njrI)hWm#)+lFT`O-h>ejZ2r)`CJ z*b0Lt<&AJ|@*_N2IW>px^9SaRj^1$e==Dc$yZtu9Bkyk@k*}xxxZx~NCH5SBhq`^5 zyR7w4MINm-uSeW6ppO{!9F~PxY8%T+n2f9e@jQt>%&jOEmk{IBMp=0L7wG{BM%saF zxa$o$0C4Bsd9Aq#16(%fdUyct2O!x1^~N8EclL$3b)>H!_f>A-6#eP1IOVb1nBig znOy9Tw5@Q3ininzdR@zi)rE4nlIcIK>a1yXsiJ)p>4I6Yk&Wh!C0Fmv{HzGtwztS! z{b@5Vn$dlh^lLCV1v(jOY0T(u=o^CofM@o6JQ`f5LL0-><3p1e43xpAu1s7 zJLCYjY5=^<;e$D?C>1i3@q_26$<)&>m|)_GY&d;nmji*0d#)F%p0<0^?J z%~@_o#Tp8xg7#Yz8_gPKf1{&lf(=zD0=$gwvCvefYLEp?LCmpPH8;sHIjDL%CkIAS zlwW_=2X0Tnmz(yyLT);__59{rZ&|1s1Ds&w#ddVq;S6_Mc@BjYq5o;rRLk<=h z3AB!@U|^_Q3Af;^y{y%=LhPyNT~#V=MpkW-=kL->_E^)aWWUWCh!u6mG|yo&)kiPA ztF}!J#jZsgKRWCn+Y1nvDomcW;BtL>nV?^oHa+mb13H&rmKsN^KawB-M|Z@W%XI%Q z{^HLa{Qh_U?%zH7=%Wuk{E(G_z~9B`)$tI-xTQ~>JI8+x*+lVjMF6>0wUkr+anSD$ z;O{t-VJY{g_dWpp`q#g<%S^1{=m7*0{DL`jaLPWHBU5SDa|cW%9Z<3Aaeq>vDO1ad zZ2o?NJ)C#md7Gn;9(XLe_~1iQ;=s88XxVSqs(sL8H1#gIi)+-~b+k)r7q`^7O{Y{k zN3B*NdbwnWILaD;`M5GDgl4MUGYZmlU=4Bb(0kXsM>*9-n&Y6Yf3Ez#`|rm=qm+_Q zn|e5pY)E+Nr56nYubh14%{ShJv~l#zGtU@o?L=807z{l8a_p|VyyMW20K;h9qBj`i zp@l<7bcv4-e5~Rv-m3Cfq0g)aH09E^*XnWUCcR>K8D_v6^eF{#S{$Q?ez`E)unu(a2~vCHh?6~(rv!MoAOH1_O@)Q~@4w$LQ1=4d zR82sy+qU<|6X$TK(3OfytfD&_c`9@i;fy%XFUo8+n|7Z&OOidU0Ql%1{pd$#o!mD^ z->zRdCEjZ8PvS0|tQ^EGx88F5ZMWZf>`v}O{iO;$X7WU|W*6HnO{*cZeOvgvTaLZg z09c%-Ilf%RlAdX1B*;Da^%+A3hS5If(ZG80X2x|)4Q9!laZ4rb5Gg&;N_QJdF^$U{JKpf(|;M2z*b~5BDe~c3O0;-ae#N%LeV=^ zL7RD za}TUVwo~0!>XrdLQNvp2J9zM@US%)H9_$Z)_(O~J#~w2V=y`8>ez+!W*<8Y~dlLY% z#Uem=eAjM*#*gUKE=?5Cdho~Li2*=}oXxi|;VjHNDOcO{aN;UtT3v<8Zum`0nA+o4>?Rd9f{XnPWNAPAs}&kmj3;7I4H8S19_ykv~j%IF4bGI^CE5VDJCZoznty_P|9 zMN?xuoqNitbig}|%q7klUz;JxN!6!-Z1SsueK|uygh|cv42!0*J}?rz^2#edk;*&` z1HEfxd|(EovL%$!{r21MzU%H|cV$SxBheTZ|QNZY5qAPiZVgX%rPME9+AbVtx$%tipU{3>p$zYcergM2DAe^y; zyX471TmVPF^;_T=-xMH;cBqCc;re^lpN)tQPODpU8~uOM{zDhDIlZe1xIx@A2HmD57C(H1jt%tCA}M*hJ)5}P zuA5k`NtxS9iW`l{k(;__KFnd+_ufmM9KWtWfb8uYIl?CHx%-}LzWP;KMEus9r(S>K z^^+%0vT55zb~#^u<&`&TaRC11pP(Sbtlvmri2%0slpVjLH+0>JA+*A)mZ@D2 zgbJq_(H7(C^;6ft%20O}g@Lt=HA$OpV9hAf*H!k4S4v3As#jTxj2W-Un@5o?k)3zb zRA3r8fMu>Fa#&i|aY@$jvQkrO%WhmK0hw_#96*zqFxtrG>2A6CW+zK#&_GKng-j}$9j|nA!h?N$nX^x|Zi??MlQ{U%p*$Df!vMcY5}}5f zNu{l;ZgK@lZ5dkl-X)B*{Sf*`ArMrmo%%`}bt|cJE&Y8&>A^+~`$TD<@U7Nvdu=C6 zEP5#J+Z&d$D8XKkQnFc8)iRdJWvb%Tnpd2cblL8tKqhhG#0dicQ?@LiPZ&B>*`5R# z2HZIi3&H(~ND2|f7i8uNh8?37eUxhw34Rq`5R6HBlBuE!)Ym==bi92uC zQ@QFc#c@TpQvsKZPY>#{7M^NwC45(*fq~yUz;3&U!3V2^o;aR9{l>>1*G%Y`uBmr8 z|C6zU6UvJsuC1({Rk;&7yzWJ?3AAOXov=x+F0`I<4%;L=z(bvXwxeX72 zMLWO*NLv(8;EYL+D*_&FraRyz);B-=(8K2+0EnxH8vxpurIZSB8SlPJBZd)Yn;v}d z0e1|0!^dP_m4tWRHY(=yVYXAs36>lsSaZ3+YrD(n!XE`?T+Qn|v$lBuy$`%|?{r?Y zKxWdKhuWsdg!IOnuD$MvwzKafBUeRiLZE4RS_5o;CuNa{4NJv+X29MY8u6>P(3Mrs=`zv{Y1QkHC4_aI!qaSh=?MCoogP zx$%5AhFMQkm^jQf!TxX))>4D%KCIVzIf<cI*L)xpr zr=NP9BsXOk!|+=Qy17vmP@A@!tc&%+p5J!nUpa3nnB_aGVX@sDrb{xPogI7 zwri#?IU&sOO(jkKKgt+z>utC3LT;$>>9VGqdLlOu6T}JF1bOD^r~kh{`!jp6k68jd z_LxPsjTSS`+&`#yO-G=`77%%s(cSB~d#Z^m8Tf7QtUmt$;M>2-rre=8fMCsn(HFjO z&!dk$`tTPY_Iwe!RfG4p_A#AAKz%B2Zm#nF<*wvLKOdO$wjavO?(3B}ysAJT%T#amQq6`QufMs!=%o3^| zZon|CYFNr+XyxmvtN9otmZL&u3Tcw;r-dMkjFg!5NH)fT42@9h*K`am{(fFiVXS4$ zsPgKL>yLin3rFv{=bmqU^V`M@_YWx64FhwZtPg(k!pT=(d;a<7fANc7&?jZuBeF(x zX3p8L1kWEFy}`IrC8_hYc@>d%4d^>v?RO=-ns@;k7&a*n4aeH9c=b+}Guy*nYTH%Y z3uV*9^PiW0$)S(%c)DSAm$P>sI|lZOVQ&J>Yq$=Xs^;dRzRVp_v6lti$TeM;&(MGA zOJ8!cP504>5KKjH=)YTJ)Vcx0Eul_5g1sNh?Vi{9)wmVwj*~}3bz4sIJOhAk{KOMa zpp(BRo?Jk?PMz6azs_uCr{Leb@Efl)Gw2~mtt$*IQ(_#}ya*VEH2_H^V!0vMnk?6E zURg)04p-%E+V#hoDu^edP);`kalM33nO*<5%d=-WZg*Ea<;0a6dENaydP81}Lz1|; zI=`h{#DdZdCoN&|%PzkiJp;g-Z@giQx%P0LkK#79@M+INYr;$k!pNYZ%FW7k4&_Fx zBjJxHe3LDKnRxZvRS@qieT5tpjmfj>*PK1*ZB(_YaOL9jFRSm#EpppkHDWiwAjp72 zk*C%@5=hOW=aAIoZvA9EkrAK@uiYx2wov_&?SQFHcxJWXneyEHaOjJdw;cT5_rAwS z-gy0uH{W{GoWL1khGbx{I5>X%IO8)a_jtexS$~ZNw|aer6Vuc_Y6^(7H6XPqFOwQ> zZ8W^p63jA5bS<^Bd~U9nUOVc-r0plOfDP|+eqEVw)S|j5R~CEzj2BDMGG9>c&3eIX zB9}bZQj46ciGc}&lFrKi=e96sQZt-ey|I8(HxK?va+(Wva+(q zwH7l#3kK9StiO3qHI*W7`-6yp%47`O$qfnW>LWYT12?CY zA7%rJGca<>>J6Oer#moKRb{(n#Gx!K*C+%g>*M)W&tK%A_JEg+vT?OxQX=`~zx^^d zWNN-Idzh@Dt0{mVy!Kk-oWNU7GOQSK$0;w}ga8=})9z1YfHsP5gpRUkkfp96ERj1r zM6pcj8jU6&bOx&pQNKLSUOY^6iXElP>#^P(p1Vd=4ub3#j2^RN$UoZmQPhZJYBc~7 zE8xLKbgQD%5Z+n(#)3A5uxr=*^(3Rk6%i?sv-UDG@UjsSrPiEt0t0}>0pH01ov~$v z811rryyNyevQuP5dFP#0Y@8KFDRY(usS!q|`HTU)dY#&A5&2SVzX^Y82@ta8XlX~_ zS8!7+%&D?0TOrXYj>6~IJPN9^swZ#ldNXS~|H`#qyDQdpqR<5maa#9}dcEeYx83%wZ+**$ByFg=bD^{Jh5;PiAKEB- z85q?MGa8}BfCQ>VG7fNFF5_P&hBYwwNpO$7@ngr2p?0+a0LSKTitqWL(?0_ML&^FZ zugu`URV;UUKFoPHx=a2(NmwqjkP)lG?Hfqm{pelKu(J9}?CqL7a#xf6RI2RE-aXl- zm$RD>JgR3~W9Z%PU9z+Cd3@=>L05EkUz2g-hU;$>HWC`H-+28^gAB^5DG3cNhjWAh z4I9i;GaPaX1-dZRCm$Evd7bX^nE#O3}X+$&Am#}b^O+4U;J_TAHSd!)a zh}6tjV0(uhcW2aiV5?IHFoE#&nbj`q|KiVHG#ChLqnFOU^z_qDn>0B(c5rjoUB-fY z=vc<@YqVZnLAL!?FXz&u@K(jHF}2|DH^I*&ff%S(ohxVGBJ^x8?1)N1wkXVwNS_%v z^na!BpNv*3{MB*c#Lc4UaNF*+j7sOKtBsVP>j}Obrcd>~AP{VwO?i$>JAu;8ZRI=0v@c?c2BCWXl*J^=bov z%^#ONT<&HQ^;-`tcxQl3pNAfNkXghc^V&1$OxD^4EKB|bVY%2W{E>yKI>@!zlH+5? z;%~pL2uq>1&Ur($NqST>CG|u_7G_WOE=oeNvC>bYao=ReArof1Erx!+juLCK+qWHZ z9)JNmT}?-c0@?5g92L!4(i#s8*lW&zl;IJHkN`f+eTF(OzNc+@~GpixlEf;-$Tn=cdrX8Y*iwHhUw(0|R-Z~V_H0n#2V z`&nu1m~ZD{VI6fbMy&enI#M)jz?R|9z`X2o4Qi`^1274*(+{%7FT2E#vdzh>EY<6! zu9z#z^0GV@$@JgMHP_&7$~xfX4}Z>TVxYU*)T1x&vMm0`fBZ+YA9p3b_{A?8D%#pW zNO!F7uQb~!YZgVg`ovByE}d{UGkD6<;A2hbWC)(1)&(z9yv0xE{8&@ zxu{(*%Bc<~ib4nYQt&`)ZNBB0qNX!|m~SVXbNQL3EcA?RG6K(iq=6J{qz?U@ntmjFvH;!wWQZ%Qbca&t z?Ikv~K~W&ch6=wnP}})b|aKx4->uH*?&m z(-)W^GhhTp1C4-;gutyJMyp+`&7!a|vtL74&p{)4(`_epM>mdk`W06Ireij3m4OwV z(+GC#_;D;^NL24t0Xy|A5e&`6t^Y8)v1^F`FrR}n?I?14Z;1d*Pq_zZv zZ4rT&SuaDmRc0lnEg%_XFZg8WyDa_?ra*^_@?^V(g z>)I$WKSL!3$J+v(cYs?IBl?@bl_a2b(}#9)E9<^3b+YfJW7`rx2GA|_N!oNrLTh>w zU8#;%COZa3uBa}pEq(2%uz+sG1yTyuy(S=2^^l3J<0?2=|AK;FzTv`!JMOq$$1<(4 zGH37}!8zdc=Nmjs7HssrtJRNfXv6YL~%O;_G3 z-k8f&YnT3F{@R#V9030IZ~xZOD_hSl!?rfD^kyOvFdephzs+*l_!%$T98q-sQCa-7TRgt4k&k6kMYDxh6b5x6xHdMOFhrOd%n81S!0$bI`f z{%eKdECJi5lLi1H5Juln(%jNWEvI2T6UG{u;YtP3#vI<*dKI@K0Iox6ABNuTN+ZJqLiPncVYwnla8)|5{gqYhh8sb)d)OT$ZtCxk+j8clT>tz2nS za&$mfn(TdPa{;HM#%~K!&%j=AWri)4h27&*_`!+F+N}OxKloozJQ;A}#KWdQcHP0p zEK_*k3(TD>a@9dwT#Zv5uOPfc6ve3O!s&(^8ymV$liGOzVCZ!<9nV~h)wAv6HS)Tm z&S~d_Lre9Lh$OZ-Otf@5rNcM?CkPk$?WGt7nf|`;gcO@Az3@ZQ|EdY0hhv-vT6Lai z`bl$LQoYbGVdJWDHOizwDYv#|7uyUd_b1v`d!zPpa7GRGwm$UcH7=*8L>f3 z6kisk+}q9QvwN3uF)Ieyue**(ZH0FJ-%Sb!GLd5~l*@I1-=dqiEJg1ycw>$v0TBMO zULy)P95i7*B31GviY`tS03n#aud-)IQuB#;^480Ra$hJ3X^!?KHL&x#N}KeVH+?rl zi|pC4jLUveNr^ADmt4886_Kb>s+l4(bwo!6FFApB0CE1q^XY+1_r8p{25CEQhYue< ze*Cy6{VYa3TVR8Tb2_CLz3`KtJpIfwRMZ58mbSA~c@|x1Txh z{{b1(S__p(jY=M$NGgrl8Rm>jnS}CI=_-xt4xA2m6SO~)K5c;RaZ|3go-)$0uhIO$u4`tPf+z9Nbb zyfuk;GdZ{6?t?M~AYYs5!VX%$1X}EV1wE1=T|l%Z$b( zTW`KP0|>LJrR{1nj!Ci`7H%)^@@}Ji3FyLkPq(5^x`<(;xT>cL;gue@KvWi>U=xSU z=kU9lZLNyPLVz<;^E3RW#+MFA=ABmNa!8v`ws(|0XFq6?C zHI>TmQ! zxDJ5Zo_1&{`*j{O*w{GgZ7FZQdCvUE){qW!=FAxzM0(5S=B76F3Tv;wrnGjG93H#H z+qbsH1ha*#l;5~dC4s2Os^Kf^)JH!Rna_fplFN<2XQ49ZSyO0SpS+FGiW)FzUihxXXgRn@BjYqJ<4nJ zxZ#HDnGbAvp^K9@ozQq9bNY4_IS4HSEDL#2mv)d*c=O$JuK#VG+0deQ@Q;iP&N!48O_)tY6$D^{*h3T8^F|)q^O&EYq0zDe0XV6C9utnmV||qi zX#8mA(QJa-{dw@1#=Oqhesd_xs8okh`#+@JnfS_c}F94^TtW^+RmnDg7`3RWi zPtm&95ORd8*4>o-7=f{Xm>2y4&0IGwm&r)2WVNSSyQrnJp6%yIzK3mc@GXL=br3X89)z71D zG_Bk2^B{gTnyNY9*XVvkTdJG;mDc{%2uOoZjmxm?_9i$w?Dap!fKaGT!LV>l$`e0) z!VJc)=JDgl&1{^vjapN@5nh&;9$Hqeys0y`5lm#>AVMGIC50UZfM=e0#@x!3S0A&A z#&oSpZttC+QEKx&qH6)27OyUxnnAw*?)!SF37W~87fG_I`JhNmhY^964)5Ne*3Dz3 z>3h4uix=kB{53o!cN zdv9dErhBseIEK0_&Io}Nd=3$1;oY`F&F=SlK+K9Etk&B!RRyW+rL-jxF$~C3Rg$GC zj4Cv5q7Wx%7^`H_fz|=Yom~K=`2oQm2iU%VyfMq3<;uH@(#n7w1VfQZ318fm6~M*a zdDJ$$I`i<0Y-1Wq!$UpsSedJ#j1B92)$2a&lFmf+p((ECX5%|2Tq_Nie{<6d!W!~yH&_uiY2yg@bs|1vbX1k`@1HzyQp&jO- zvF19UX{aq|HUz{w4!Y~fPPOzt*766BKmOnTHekg$1X z)8%eE{#o+u-i@X?Skg*%ri=_ijGp@`&L+KQ#!mXaVfxc%smy#vgaOe}0RbEpU-5_! zM3yC5r-BcunK?R#=;)EdhduEt?0BNdMv!?G<}Sz7j)4J*LZxbJ=mSLv+i2?-g~IcK zigV}QynN{dq5jgN zkAC~x-|`YJJ;$oRXY7mw&pq?pQ%^ochiwWuUgn$`7#Q?GC&pQZmLm|lha>g8LKWAl z^`9bI+LRVIt%*LA4sPv&|2yP>4J}S{b}Womw0bX7L58*z+bg|Y!k1ar!q61TUkk)u z%`>ph64k68yre)?=iJlSBNahTM_^-dm@ZHz1yIz~MAXX_Po6xL*LPkXPK4@nj{sz2RiPx~*Pa zKB;=e3o!qtaRMesl>{yn_f8FIePS{w^(~6k#j7sDLsAi^wZBgZE;{0a`x=m z+_$UO(%tVJw)frVoH^5-{Tbu6AQ7QI)pRba*`8zn8Gn}oY2&kK)nBtfNSg+7gD!}0 z`vbTPTqS9sb<2t_1Hw^ylL(03kx_TtZ; z(d%CPsmd^YMm9)TNgaB>q0E?Q&H!7$>`a-?_`zgQGD@UBW`M0mFm@CgoFRxR9zbK} zOaL}<<{^S|QX5k&v`T^kiZ%vRrprf;95Kyj5Q|iBg2wvn+xJd=q~1Np-Zua@AU7RS z@}Mvr?abEnICuT^H}rcGSSThgbLW`LD21|cpnbrIv*^ zI7eZTz6yqv<7J7hytP%R?^I8zI}_b!F67fSg}F!5xJrcarLIRj zque7>BUil)1-5-$BKp#!U;4e@|2-GaSbq~Mi-Qh3ZL=lnsRBQGJ;XhNT8#F@D80kKx4Li5(=Ovdb=~9<>rPQH{0}bXHP*U2uF8xbtSChl2c<q@Mes90?4!fnDK^*@Y=lItrbA8BVTnmdl93R#s*E#?|{Agm%W^3)p!uD->Wy?z-#F zYwM$ijR4;ZB>a1I?_NW+ZyOjMTvxsBx|HsC0R4z%w>zWPFdPKs0LN~fR1gN)ke3NT zzK~p}5}2}~#P-$B5VV!6^-XCSO;U~0nUX@cUt_A?N))(saB|Vsxh*qmi)>#w%fQBP z2noYemkwtdUAk(}2!XjjG+JiyDV-M?0KQEB+wK`VMV^Tp)uxiWzN?9spo4y1(+#*+xB_jpR;kFC5n_wWU1x5oc;Y~!V zErG%f=WkD1Vy4Qq$F8|E@RG6J@VQCmjPB+DvWH&X|Fk1v($ zvNc$-OIfhloUa-N%_xW3^=@;1Q=0|ftjQKLSZR|y8g1!!am~P7bYwShFmL2zxXq2t z{tSM-4p4W2>=D;fRB+fO`HTT&-(ymv2&D?>7dmk{=gBKeGc}KGW>E96g7Uf_WsCw^ zIpF~OpPZzLGVMuZ{DUy^HFBDuw7h$1YE=dCAtapi$ptw|Nq48}i#86j%W!0#8>Q1A zk!Drt{u5My=eSx2E8yBnvl))+{}Dhs^mBz=MvVZgxaPxBUb#%%J>yPNV&?Kp6( zl6mBixmN=4BkUSg5P;mHLi1@)$uRGzP4gjW39K79EpQw9OJDkuutwMV9s%)kI*S8W ziKwo#D^DKaM;4U16f`qHpvC+aucf>NRFTfL9scWud@iM^e%;4v;%OX>xOK~+^m(d^ zq%<Rx_-(Sy1Kn!r4%(+%+Xd^eB5H8m|@bS+lpxB(f z`@94bSM&`W6@fkb&Hj z!XZ+H`e{4PX5qaxrFCeTDYyf_%$p+YblJgiiffh+8NlL$9K_M*)Daq2sX2p)lxB{E zTL_KNgCCuD5~HS_S4JjvaH45?aviog{k%RnJVgiI)<6{+kh28bM)6}Wwm95IeJ^G? zKsm8Aa={}?%9*aG6XwqY-g#|$hH8zpFDteT9%NXf@``jRlgHEiGg81u%7%~Kp|5`R zt4zOhwv(q$KKsnGr_P-AoQjn)-7(f8q(7rn{s z!i6J8j_CYMp(M57lFBKAkI7+p^mL%Gy3|kET+CJAG8u(w9x0koaTDxQInKs<{<|;fN%;q zrBFgVi^5S&0WQp7CwvC8v!gR?LGvN019iqgw40&}j>&<=(n=xI!{Pr7fzVZHRd2tideCXyw*WYm6wFj^3 z;(A`$oFLBh8`vxeY%b|_DsI3)%=F6bps#-It8e}BIUg%{?z!jOT;MDRHa9nQOV>;{ zHjX~J= zgi_otv0-cM%fi}FU5+tv%mu-`li!_DfKhdM!QQ#!}N~D>jqRqa?78D+8apn+3Ly@sjiuxj&h zXK4&Czf5hkiaM2U6*Q^_TIwlpJ9E$0w*_(0{7Sh>^a)$D%Yv|lNu z%#!xle9Jt)L{5b=JL&#otgY)kKRc6G2EF*|Yp>eU!7Ah4byS23wBV{L9^cL)0*=9; z!$`UN<^kd_96kEzquC%b{kI(o?hUn`jM4XWMMntziWH1z*P?mV@S3>x}5g*h%06 z0@DA2E08$QSDNM&EzxIA|@+Xm%pc}b6RDCc8AmCsE z)S+@hOE-94vLxJ9xG|uN(+VB2-1RBKrwCWc_Y*R zn%j~ioZ;{w+*~P(1JQn@{L)J=frA(`EnkY;?5Hs7-AFQeiZ7J+>;cgh_^|6k8&EVq zC3o4e?KxrYprwGiRb#C*H^ash>&dhl*GLevn}0m~P3CyH0t-MdU?j)=UNg0w$%NUYX|0=+M~n zWi~Te|L327-b*JahB3g*z^0JZfIfNn$YGb|9IwfkI?lWyOnTgabYQzba&1rgcZ9Tf zKaRIq5!2?Wd$lpa=C55tLT5B|w4la=$}r){TNZyA9pUW(?6qv=66Vv!CE8jP4oFw3 z(YixbuL56ijPn{B*zV+-54dG-3QbD;3UH`FuK?_H&X6mTRbX(q&f7(c4pxO#gl{5Z zBY_2u3eFERsTWKJgw?V?GZFOnDuz?YK(G6 zU>X3t`RDN;{NO$-+&i+u9m!V-G;>&I{r~kB|LUDwRw(4sv*wgvoT1R+5$*a%wDu|r zhd?xA^rxIDvWtAI#B?RmTn}zPlQfBLiv(qyXf};R3L)T#J{`T7=--k_$z@SJSU`j? zpe5`ALpCnj9H>Vkt}~ryAmEgg9S*;8?yYkW6h`8%4@rRIF1jL>YrB|TP(pwod{{$r zK1x<)PBonjTiNtt728oGP8Bkbs8DL)e@7csIa9r?EmB$XG)CsxhgxCSMGT*HED*HP zOL#qPkOZPGAy!U1L~2E9v{#Eu`sKV!^paY9hYF94ZiH@KNxk9(*P4>8%Vg!E>iNP2Nq+zp3-0vW0DfMaM3e;1Z)gApCf?} zo#l9ETrOFA@72wDe~G!z(W6K681Nl;7!|0u)i!F{4jtO7G_pg`4y-qa3lN;u0A-NT zI>0bs=C!%G=^=UR0LO8Lgj1(Z{rKr0-+RwtXKP0{j^2Hzw@tW0dTqwqx(d=2pQ?}O ze;oH|IM+zswE4{+*V~5$Kq}M%2YVM=Oc`?^Y|-h0!p6qN!w)}v=FFKv6OZZ9C1E{})4AI6hjq&RP63^5$2=3T+M9^s3&8&mOIViQ(k7aBUVghpn85iT@u($BYu zO@u|&s7-&P#X4MbpjHO`DVx|474N!Zlx5L=T3SZKAqvb)todXySBs%27`z%Vc%k33 zkAjS7TmRT___ZEViwrWAB|xs=$zBu#NX2VlEsO{=xJT4Te*_E4aqF zLgJ?Y|GMZX5~(G%v`M#6v9sI)%48+Y4kMkk(K`F^7g>Xl?hJn{{pKoah&edvz>r9>XfNMme~C13J?w9ax4&t zwEWr9XEcId@$NPJE*f~S)?D!=HRoL;*4!3C1Id<4e~1k^fAr{v35_di?%F_Z1-8$_ zLWx#KNl;sqVn@^*yW|z5zwu52*T(O#XaS<*9uJgE+;4V?f_ zmQh3nV&*|BFK=>Bz2Z1B-(8A8>d^RQ&Wo?kg9wHij6c? z{zUu)G__ukt4T|3OPy;}?VZesQPB$Qf>6^xv$*=;12Yd}fRR9e>jeSK-pCT|%~OMc zk-%F?j08Ro(c=V~S|p1l>kmC9N5!-wV~>bZD1aiO2DLxt;Y^dMFF*EW|2#fsktfyK z_e_R?pZ@&imw)-nEAPGgUc^$G6hc?+O0(9f*01D#U34&k)RI~nMa#@?3;=1*R<3Tg z%9kdrdFw=El`r`xM1hYR00?c#L-OLHTnG3|!A84kem&3{nJ;O$30Tvt>Ts>W)uAjX z+iItSr9-jy@DrGK{B>bU#rYA2;E$7$3w%aEw;i|u2BdbNd1&Sbw;g~cV0ldk1w?1$ zd{B?|w}eHIvhmH-V_RBRqqn8FH2}2q_~|zL_C55_gAM?ux^Ml-0H7!Q#b5k|@{S)r zKJEW}+yDR>UHsW0efq~w>rukJ`J<8w7VR1~EAY8dbSH_XhMgoa!9t1O_*4But)>`l zFG|BY-$b)$L|#=~l0pF+DWm>RrOYLi_B3x*WK3b@N(_?PvG1hJcGwZp&WsTtAA&lP zI|L6IvF>ciU@vOh)JzvgbzqRG$d1l)v5E;jpMFY4*_ztqKFEVi7h`X%V<({ zy5MkVQmd$74LQwQ`@v1+^9wU2&H)G=ZMpF~NyUq5kQ-;tmuR~#sI!9vzdB4(i*o=t z4R1P)mh{An)J$)ZxoB+}4cnnPs8esg@ms3=ZUU`folN7i)bhE?4}Dzhy?5Syhvj9C zMY}7`jgmz*V~<%k`+kH4fsp`B=V9hU)*smD3&catkbT$M^if~ zikxkm3<4mn!;rgkiMHz~g%F7I6hI9ZvQ;k(EXqGSqy$rFx#p*tZ*B+AjSzp=Wg86o zL^@VE*29oPFKuJGqyPN#&s%8f3ta4e-a}jmb?`EO$;LjijX?Ro^PTV50tMXeG60-9 zb?X2AAOFKcF^+#@;Hs5^nr7d*Em2pLIhQGwvtOz&qw*qDhD7s%I>5QC78}q z1{G6(?ZL(wwE|!dn4+(|&cNUbcR;uUt#7JZB0cZ9EzWo# zIs~qDx@sWsmBNQ(z_5;XXMyrlU_1M!GH<{Aj*b*Xu46QPT(!~%LZUAz*V)6R-0w1M zpyoM#AFhAkfd_RT3+Q9Vj$x1KHzm?FPM5$^j1@q_rP~FYs+0EBiW7X9FW~}5>w1V?CbvKo-|e-OPos6x zNSzriYV%~7GJr413S$Yp1DzB|U{O6`4qRNI)p$y)iM=q)Vbxn*`56MHZWx3N02l4# zXU|YtNNsanIu4~apIwXQt*h3l!t@1PSAra?z9(;2vj}h$?9Q93_TZUoR%fYQ>THw> z#IEFBx$Yt!K*wp;AO7JV_NHYsu=FzkSPuW=KmC(?rjYWqUYaR5VXi~V9CKLl`pNyfafoKgpBdlZr!jiguQT9B>vRAE1k&QI{f{Lnd7L;^PSV8i}O)m3hcU>*Pz zxBQnLw#3v5%cyX7+eX_VwXB4unM#?fvleYO0R=j#>)ZYScGB>dIDn#DbeqOj43GtH z>n~~9xhhOcW7gYgeGy7K17yqq;4h$P^fUleIV*4QBhajl>VS0J`JH5yOIgF@JX%mX z(+fBC8TT`rn=F!95rajf&YUtx^nWCx;gg#ARGx3mQ}|`+Bb-5oM(Sz4S84npj?s=` zyn64NSQFj7%NJt|0LCJspwNvoxJMSGfupbvs1md#P$_B|5L?7}RYncuw^*p9P{|@0 zSC@ifUPp;AWtA?_8K=@!qC#nLiT`NODLq5e;eHieTug9UQOyzkK<%OBZYf zyl>J+Vv@l5ZKQl#hP{AnaAp{kY41WRXZgTjXrTSOY+PiFadn0Dpm5Wh%*y(gI?lYQ zbiLFF8(eDCd}k>z?Mrs`rGj)AKb-1RruHt?g&?qc_%^*J|C6VBPy$5(0>%`?wS_`c zB3Htho(qA~QTQ-B3PQs~CwvpDx|I|~sr606yF}sK@2pSFU=hQHOnpjrs_2nOvc3NL z>&*Q#!P)8v&FuTNS6}y%O++oACaTf{@bO zhOAo-`E1$7*T4STKdp<=&ph+=>C%NG?rt9aiR?mv9*y=OKzA(`_lJY?aOz!BjxFx1maN5Aj|yZgTzI!j?3AKqTQObbbaL9Si>(uR5=>dOGr(6bQR3r=NI8Kvc94B2 z#?6y=-hEdG)}Pz-w~3j|eGS5lk^M1M0%VDV%}*H}^c2=Fhd>s=F0naU_+h1yTc6aQ z-j(tN&Mg%Y!yrhZ$#s%wCMC4WB-Xx^C(JXZmOxYEY(1$uc_mf^(UE;n%a|c`7DbDN z-vLRDFoe@^6h@W~G(>pmWXBr`;MV+|aHO`W#gKKa)VAit3+H^@u<=Shif88W@-Kew zoekz6Rsiq51N@FH9r(;KIyg}%C!mdRs1NjTqC_|#Xey#B&W8!2)q5;quRRca^ypF3 zdGCzA?Y3K;zYZ-#+=1 zlTZEl$3}w9Gn<~2bP8=lc4K4X*zx1KvHL|{xo8I0(!u#_F3b!R~tr&J(bi0f0mMa|+ zf)3{XdiYtA?dTeCAb@urK^iAYSTj6V&{$e#8TA|h*yZ25*EeWgLOynE$kZlHM+OGZ6JA+GlIBWfh*y8BVPTRoxf^qPVLphLC>wFfRLPh) zn_@|eIcVm|y~QUl6xHNp4liXL^lCV4hkkA~z;_`ePl{c4t%ebWA6v60FAnn9(Q9wK z_U0RJGG0>6YGo%kBL_R-?H6L;s4*Orr(O8UD=wmlR4T0Fsos;NXbu=NNt+FpK+tAB z1>{(Au1ytmiE-O^ao?r=#F^KI_OgbJwSAS3a;BQ8a4iB@eMO+tJU8)%KoMyA$aXHi zFdz<_fzATb?3G{_jaY*XvP@9DSd8fy`xhsIpv5GJ!Hc;LX1!$-Vs+9zI} z3Ow`7(?9yrla3tDo;@r2$$|AA548-0E@(srbO`=v1^uMM z{C1-=>R(#C`eaOz)Xvy}rirT#Z1+bkBo+I?6p)=dT{`H2&b|9D@7D`JJT1fgm|U8J z9zJ~d#+z<@<<(a*yFYNSdwxT@Rv;a1;sdRt8F*Os(tDT&F0=if-N)h= z8%A0-#;IyvHREsWFqZL}hJ|XgP^eovkyy>s;uU*}A*5!heVE!3Z(KnD5EwF;^sJFV z8Hh&n1~L8IhZ=lk&=Wv9tyIH_R0hJkG%}~#y+>=A`YW^T8Z?&c(!1|eDJp8!Wg{!+ zxP(wgj`pzdgM+yt!m%MC4s*jP4$sZ#2#7{CXA9n<01>ci%aqqZU_X6t9!(G+A}zk( z`>M-=XHZf>en|`+c`KHJl%n*eSxNPKZ=X7S>d7BHX(^!Vd2yD9%M36lPCR5JIP6W-x83HA zMwtodZ)P_0EChX3Zg~!W2~o8H|921QZ@3@!BbH=z*adU=Z`Uwk0e-jd@Bzu8;Ujma zijF{&5^W-#)ZtdePO9EiCViIJ1czGGMB(#0P~pj}2cdA33Zuwou&B1G4wm#d%uw>C zBZ^dk*FA8U4Q>8MQ~@-ZPsgr3aFAY!Hd$Ms1_0NAY%MBYDND_Oqa%}-sk(mQp2B*m zi}>E}eeZj^oP9~N)TrHp(J&N)IXNX!aa}^(uIuLJ=Ffleb3GG8uWL<}lw8$0=Y08X z;Kx9;abXU8xs;#0^%E7S(Yt}sDe;;3U^N5A^1OGDZRFCVWyYq^yrl&B%e5#5WK~ds zG%nkrdoy7$vr>yn33O~4YAgpinfLKA5Om<@;0nL#=* z&_*ESXV<_s(BxKT{e!pPeyc3C4V?GA$ib7mUgnb{(>KqV_;V3*z0cW@A&cxv?~XQ= zQle1^Ht3=?ft7?uxOI~I@BhN#!(IlFofd`|4s&mMZtjAJwlIF^Bt`|P+KbUQFj&JK zyzZcdh)!WKCsn|VUi&E>=UCGE!Wcl4Dpupym7z)5r)dzen_&3|lj_}A?__`Ui(k^? z9y;+5K5e2})a%J-&TLwX8>M71sNe^C8d^BF$?CpJxBD#4l(Y%zli0S{v#)cID>EL@3TpgXR8ntAKA=K5!ojI#xb1TKrR9Pf7>8_aYM5mwwxGa7R zsPI6l?UKEx4Ek5$HNm8pDU>4MgL$Q)q9yP#y8 zR{#SdptG>yEFx*Jq3k<7X7$R@+wA#(X(mqvd**^_WkWqfX~cxSuGZA5G?nQ>RVutD zrcJw-l&&J-+uIIitcI_B4EZ0DK8qbPNi`}cj~e)&Qs}&`J}`(2d~lTjvqVj%FqK39Jl|Y zc7&impFDZ;^y$;qmw6X_pT=i7m{x-`bR=H4`bBXG3= z0Lf%tWe=T(Y+V6t=V35B7!znTzGfQm!O0RRJm4eHKt5G$ZPcA&t&OIo+Dx>+xJ=e3 zzgt$L0#vV!*LJ+;A9J|i^)UdDj2eYsgjSx?r3{uvV0(Qf4Y70}%3OG~zok*4$SU8q zAw8rg)P_#d*yvvUwff`09OJv+{Vr=W3f&&q>rE;2{@BJb>)B2of=}0`*9N z#?vGkkK9^0)FK4?5In`l?icaYk;#xT;Mu`%4*4Ug1MLsw5rDZ2xAZ{LzSA&})n7pX zQds^~t&?|=UlAP(@=7@50~~(%-~#O$xgpg@vR5w9`}It)cQaT72pA7Isb=Mj?@c$} zcPvH=Gr_J&_9g~hGP#DFzGj5pDRXCAs7ji zE2H`NZMWTi=+G@U-gJX^O6yd4=ZEf9--+u106zy0UR%wVLOIN6BZHbTpzQ?#5=3(q zRwK~JT3E&DMplINWPlANuM&FXkuU12uJkC9$B{oKP4{7Olx! zMq4HWA~3>hS$2eP!AZk!DQHWoK9@xQx8NiTh0V_@dvTE#Su1o6(r5#x16|(^kEA6h zVPY0YSHB?*Y{*gxL25!3tKUvYi}WCnVZjg(Gw#qeD%!4r-Lk_^OS`n(E?I(`uA1EI zPFZVg4?f9ZB(+R1&k1+5F}>Fo5Z6Wm{{Zj0`!4Egp>YyCB5lSFjrmV8U(-~qO5-&^P2qBaOJ zBu&$TUK zcUzJQl40jE2g3|~y}0*cU&jP*m(r^?<-47ky;5RFR!ZK|Y6Rd=&)YaI8Ir9kj03m7 z|GsT~&ML&528vyyd+)vX)?05^uDmD_HGZ;?tHWWnZk<=O`IctY)-d&a>i{ms`r-lI zRQ)7HAl03{`A$eKM#mWDQ3LB8BMjoUr@Vjk#6u^3_jiBy)ag?{_`%~(J@sSfBLDI) z|KgcXuV{Sukw-Q*Hmv3Kd1KPlntl0ilEL~9meLIKDsUu&jPU=qz?BLe@DXU1n)&uY z)}n*$kxPI~TEQSg02zUSpSe!`6-0U*fQ+1BfMo*_4pzC+plEn7_yv?*8NuSpt0ASJ zHBFiDD|4t_*$5n@}NyflzC2UfxEjP?vfDXkB-yJ<$>X!hmHIl(nHP)=h2gli5Q zG`-kLZ->}k7n9v@^I2@OkFGW>oxZ=>w#=cDS&Z-5=M35;Wpi`W7?9DU+^B{?hcP^| zcRd?EfCAvlq>MvpyC|t9ZOn=%EI2%2_V?ZQUuk$q3wKWgI#yJ!{bo0#_1L_N%B#cnau|mmz zqHD@&*N84HPWx+m6nopy)S$W$vc`ZsyjwO}e*e^8TiD-m2B2MF8U|b{rPVr>%_6&_ z_Jw--ubDV#lH?L*`qLZA8S4Qv;_uJKJ&uSsLry*FF)L;oxf(i8?cQW`$^2wx=- zi4LZ>;eE^&5H{xQ_?v26O*AOsaJ$3X8n7X9fe(KW838qIgvRfR4}a($e9{Iltxm9` zQK)|RLupt&yf0;PveOk2l5bxi`jZ*VM>6Km4y`V2;t_C$iWE`~a&t1c9f9VSKOh1P zRGkqJ&a~bVxEh+0gD8u>(NFR{bLI@2)3XzZ0`?aaYG3hB{^U<+?~?xZ@Lmdc%PoiQ zzwdtA6p{zK)u+lP&6yNjoa6;?)X_aQmX5j)UKJqmCr{-xOp9IqFYuc;N_e@sXUzk)P zH$#HvSbo1#$INOKOpi!gUQ%UCAkZ8JUlOiF%lLP-!zfIy&?XHF&MfCqWzjyTfgVo|g17RGE96&mK&}!% zN-BR6Y-wzV8#qKEz_p&U`2yWEa|0#bP(Gg|rMjB_=+UEe(yPf$TzZj8Ey1Ir`}Td~8{hDvs;kJY zllC63&mLL%Is*vd;i~0EOaL3PQ@it(vX{z@0YDPI8@VeWED&BRz!z=@xiN>|@KC@3 zSEs>3vWBCEB$sw!AQSLMxq_K;6wMqYRK4$fqm`I;)8 ztl*Gd&-&b4c8-LIbs;FI&nSUFkq78Oaec;fL@m=zfqw6$rzi@ z)pn5XmmUb>Gt%WmQ|k-aS;k4rFYKmb7I_haF{wF;JZei#E~B8c0pF^8pk6=?&icRb z!V8<5o1RW^bP;jqLuqk}G9KB&?S|_{Tl1l>L3WImu3ZuH(+&X50CAHB=}X;zUDK(ppBAi3Lh z6AJ?&dlk+*b=VV_U2zrZJ{6xd{LIrt5u8WMeZBmidnv2y_>`d*s4*-w0TJ5ZlXj*N zS1G-df-QtybeBSpM0Ra?z>2EH<=%P*MnN_F*aC(KlJ%+%Ca0=6@kKTJ+>A}-(7Bla zw=C$l?$X!xgIvt`=5K8g*tY(d5_TwF4s;~&RY&6yqNob;@vPCcOXG-!y?IjH0Rrrn z=1Y|w8g+@$cVF;5`w4Cq=Ebe-MyyLdNOIuNwTA$&J9yn2*XLC>NO6}g9iT-mDA8v3 z@<->5RQSyM)oLHyNld&2#lzw9tHY|4$oqFhyv4`?z)<|rJ1bBE!34f2({IdGS&L9NzxoV4n6ihD^sdJ3RO%HS zEC)Vw8>ZK?cRhlWx=fWyz5+FzFPApZmj^_k!&S)SLF-VbwtjS&->CxN`m<>(tHBBL z35~cc=h(iS0eXKl5 za5d5ng=4tLrxPIuUXzau!}IurFRn`&n1lHdsA4A%a`P!;NLG1-!?AZ(0C(Mex0`7U zE}d)&lFa>c*($GkCdqm*V(J=cM6DW`RC}fvtmO5)b=1=4Hq*E2r!mOs#(v?YBC(oz z-=2I`n>|)(?g?=ezKLlsPk!Be$V-aycG5l%i1IrJHrGFpqyD(s2m+brZPB%;N;P+F z4QcGsG3BSirj9vj4;ua_7x89D#=So*5{qk5hYufq(BZ%X53u_rW8rTdZYS1pv7-xm zr2_a57p1_8K(L#pC9{&xTvY!?a$EmxNynImij?yYuaWoohiGErIgyyqDu3#wiJ{Lf zIMwJ$*g7yk0l|5w?c_QyzuitKD_!l>R&QaV;G%eypsmTK=lc!k6F7w7G)+SUy3e*A zK}f4{g|3w*_?*2-DF;f5xq?#*Df}Uau6yKCZb~Xs*5-zIy6(Z;v)juOeZB=Vr%s(R z<U*NB}r(*N$Q${r1ZJs0(zb6O(Me%Gkc+Hagj4)I=0OcUhTMH5tC7N*1deGoFnG5rSsL7XV6=iD?WY}I=p zmZd=JsHA3Wl-@~zI42N3Ik0HLl*(=Wn@mYcKCGoX$D@MPZ`1KW!i;Z7TP|{S(>b4_ zgi#fVC8{wMCN?0|#5&Ja=C56_=b=C&Uby5Nl|~5%H3D=UGZ_Q&`sj~dd;PWB-ni|C zn{PODb8NANf{JU|8eyh`KE%{<_@PaO44KSME~eJjQ`Wa>NsRe1+s`J#<*Z^AH)Pck z!=F;#z4)%TkrNIPdE>>`ZoSp4|C_(_&F7wf?#Dm=u~*l5DChg%|GxV|U%3AZ#~wJg zadg8oqBtby=4Va*qal`o^zAFT^!gprR#__RD!K#i4xk96G`7>CL)%nknGNz1zva9{ z*bZs@_Q@S`H92o;rYU=kxFacOlUt!h%^Em5wsdf8@DWrPV{j)l^kd1DOD>==-+C0j0wWEHi?l#SKXNanek(DO#x4?%5=)cNz* ze6a7sbjg$&V-dJ(UUk7sz=zB^2$y|2v~;LTZ7DQFIGIbanz1z*0ZMJ;73Xxm8fagA z2pHJ{0C`sjtT?&2SeFbvnL4!4)=0qtfe%3dsYVbT4p3gt%XKP)>q?{z_Z||G{bH^f zHyvg_qNq8Sc~^^v)UqVscaZ=gw^|A-f?@4xnmR;Y+2_mkXqCsflZ>lvVXj^-F8oKJxAOaMVEWh1mxK=Y15`*NLM7 z{(GZPLJ?QaHc|vkDrAbFT<(DfA2@dW*dPAEAKD0d;zv(B{`li3e{%Bd*|R=Vys>fY z;fGH+Hn33enmNcJLNQkcYps_5KGBv>m#wDM;8)}W5j2-XL^(|mzgExrseQpr|4Ru1 z2}-F-ExbxoYTi(JrJ#ufHB07Jsir9{Msg};6fHi&R}VC1w>AC+;1fj$M&6Jsl7$k* zfZ&XgR#=a64jg?5pyv=;+=k)Ed-cHHKmYSTe~VhVReI;0u`gRWSCagEj#?t^q|3zT z)mLA6>Fi5Av2AuC49pHM3tmd3Qya1)W-4C^6D9_V2L$oZWOc6eliIU_7R*|m_z9;K z4nO`?M+#`>Fo3B}l{1;UzzK@CP6&l^U1%c;FA+LviCJikCA(E^7Ph5fIV~bRPoNT= z+a`tqvUjak7DI(=9qObNLv6OI5pVcaXb3}4YkvWin~O_Ccq!`U6MPA*Oq196BXtBy zvFSptI!E>DXt8wzPN6A%dLw2KatM#jkD2&X#uVM10MbA$zpA4ur3-qwh#K8{_+Iyt z9Ri!cx`aa$F4j@5BVs%6gyu}e{x})xr4cEYz8(ASr#q)!;YH2)TC1shrs(M>!ctl0 z?!PkEXi$B(_z3qAZe-go;c^n-5rwl~Z&o=_8$MD7g0NO=KdrLp|7aKaN#OmXsFYUp zjRe+z3eC7ywxoWhQBxzs9GQ_a7^uEy2r%SP2~RDs^>vy17Q%3FZC~j0>C<*-?GedN zjQVwzx1uu$PW&ijR;RV3M8m@pEE4=FOJXxton#Eu6gp!yaDtSg4v4^Z_#iR@1E(R| zrP`1sy!4|anM1^wwkQcfr`fCNMI-jM`MPIb6;4^i$vK$i5)QED9#N)(Czfyz&!vey zlB8I4+HY>J&wSkp*jFCRq_Qo&t)+n9Cf<$uDgEc z8{ZJVQlU%u-aq@ZKZE+iC!X+VnZ3(pTC*~>Zguc{2=|%;s_iy{&BxxdP=`^C&VuPR zIuofyOJ4;ul}~4kYdX=kitC{hBm&tYCVU1|(vvSlva47(rK@LmM%^@IYsMkJe~A|| z1VO67ZwJ!RP6BKqOFxB{Tg%uuv<`L6R$Cm`P>}TAGG{LH83Vl6$uK}yv>Y5$V6!%1 zBE=3O=(8^TsMYjI;Zz{ycFFv1qy@Kg=~4PomkxJQ`)f54RSQXct_obCH?UKOFObqR zK4qJwn2guiWa$9PfTGx5=IvBF`<{BGTt|-{J$(4E2UtvM42LSHlj&f-^~jtV0K6TD z{RiQipdQ+cC7SL%yV~_hU+r#Mca753>tA!&8JkJwywqgJ0U*q4wf8@G&tPW}VCkkJ z?MnSKdu=&)P5E8T1~z6_HgJPa)+dp0e6iiT`c9GJ@dJ*}W~`fe@^uk$to!UF9JYty}Eq*I|pG3jHnC8nG0qZVCI$X=9d9L z%Ub!p_4ZrYp7Gr2fdhZ^U;pca4?eiP&>bO}Y&ropy;2EtiXD-_O~mZ6suIpz(y(!1 zj)pffSDP62 z+#F>64FjpNl2*NLl-_ND2TqCGW!tqF2+jQP!DVTA@==3dM4}hzD((d3>Drt|WXq*C z9JB3`hjTM;ZcbD#8NJM>+s;7eZ8%8^tOPh?uR%sZ;LD_8!(H9f(gOg}yU*m0!X0tk~hH(Oc z)9}{Xu0s1%mQi4>$`HYdz8JKx^;)2Qn|v+I^>aJRrff)g6zW!&U~bEc0j%7wxh=0G zb53lfmC<7V{#-5n@I&-ds7sLbuK%!I)nF_HyCXGkqY6Q(HAHhe;A;V?jR1Kh?e?0x z=zEfL3XqQi z{V{E-Y$88KlL0cw4fJM) z39Us`y}M;z3b10etjsus8S8*!8yjEw%2%I#?wKc_eCoMpp3V1jUVho}fQg|83yvP$ z@I`ww$F31S?0)`kt(0X{xTUuQHb)@s27}9X96$kRowSZn+8ay>3(I(PrmONYatqPgEDAU}(Mi}#*3^G+XkX&xDAJVXNiE@gY zb~0=f`v6Hjq9~&|nFnav7EOhH+)us7RyyW_o1AAIOL<1CmBnM_?cfh(H^)PU`MdGo_)d5y~X2+7FSMXwu6ZMrJhtOmWb# zRB0jKLLGb-C~Z(F%Ql!GGbcf8G`@D9c-z*Cnjl}5hvlj8Bmqf`4}Y5Ge&mAryS6!u zsro$qaWhDNc1_4%n?=YhAD;_RKWUp(t^ab{WS;x4N-fqa(~9%1&YIsiO+2(`*QDrl zQBNhRDV>b1|dcW%Aq*8A?i&lq7zxj7@o~>{Mk{p`4&L7 z;F@7bbl!*I3|h>tyx@T7pvmuWOQ`8Xm^dq`jc(ioBSX^g*QteCBr`a1R2pnzbt2Sf zG9}<@rP!kSYJzDT6pvIe@f#cYs-<@uQJ{C;d&e+v=FAx<1YWpJ<;)p#_KZ~(>p!U75+VrVtLemPMDJYkdap=sF zRpGf#`Gvy?Xy|29+C9k=LU0N*(=2UficoJ0H(9h*;Jn8X8nD}G-~2EC#TdU<;#Hy< z03_3!?|tukHaF?Ym9X)nc>R;!n26RB^RIb>JN&RZ_mIvmN(v;>sY{wApEOl!I4i-( zH3Bd?HGV`<>Q*+raPql{$fc4~`X?Iznp$#I-of@VP>BGmK0m?&_)qrIUYa#(P{5g` zf;JLgQC+XD`%*Gmnu(#9REu@>S@f37^FGn*a`#*t=SEg7|9h+}I~w(_ zQo?fyD&3&@QC1ht2U$i3e0u=qz?TJ81O>Qgc{)^r)yrars*@!}(v+IC5bi9)@3IhV z`sViD{;z6wu}!G^@*)xxOcJBt=j1*b;{d>OxdsfRT?9bW-%q~ASVe09P&>utUwUJ_#F{Huy?aJ8g{6Uar%fdKpMIL8 zvUGf|Xvgc+_LuO+2OpRnsN7pLd6|oRppp61xbsRqQi}FYlYr4E@G}g=j7LuropeTY zJ`bq3mj@$AM$h~PQ>386T+yFOZyrE)WWE{hil`vhkP*&!Fw1RD&u=ZW^H_;?6E$tR z`OwYo0h>k1a_`~8fBeUP{0T3BHe=HOU`t0&^LA-klDWXDbyobVj51h%PPNT|ECS5k z$O|hf(6#NF2#;t(R)i|lz_lcF8ZChsx=CrECG01G6;78cq3}P^3IK(s6RTVz?h*r^ zb+O?M0bD~{!e!FLl{IKhYRv~5_Spl>1 z002M$NklNIAlLM2nxw+Y|k-?h* zMm5Jy*NTl|VO{}D{B-{bP6`ZT0#n5Y5Ljv@hWUy%8vaKVWH=Ormq{QE95~CsXypM+ zF}3;L5D3BXSNgyw83sPkaw;Adb|`zn(zZ$~mBMFHN#H4 zAHW`EeQaPF0N}Yb{Oq&ODr1lJZYCT%u#O-5KL!B!O#fMLD174$tTcmN7@TFPU56!P zph@bM7+D5#>d;2Nu!FF&vy)iiaGf{fL(4IVBGJfXtu6tpsLdJ{++deC2}?NW<%;l1 zh)cB5u8ql;OIz1(6*wm(G861T<(;%&c+OkHYQJXE8Ec*F>7rbwz~|l~X9f{IIop4; z9hA2j*L5%y=xv$`7i|pcEvHqr%+9o9aS;y%ZW(RqHNTC}Eb&KDYINFFI+ViYOkiY?5ky!Kl-6s=*Asg@HN#%c0hgx;%jm#lXLD`59a|*&saie zDOsp-EG)*X1GEvIF>e-Sz)(5urCqwXs&2_Bwub-~O%QREtg7)^)JZQCN5y4dqxp!0 z?ZVZpwR4y?Sr<{wKQ$R55XvTpC?>r=i9EsGt@41VHw0{KY?wB>fM6ZqF@n>lPpgG2 z&40Q~c9R*Plu5bLeVQWT*+Y#>0MH+iS5VkR6zI{%0UTq)96)@k>EdTi-=vVXQWhxd z!N_B)10j%xh4pfgLSaTjYyC9Itt2XJF1s@1B~rqsEft*K`7>?uWR&OyfL~!W`R%6i zs)K-Zp<5YDOieim{1W7rzScl>uSV?jAhfN@UO}9w3qRgcew`=&vIX?=&rOwR?{~lZ zUAF+X7kU-QYRBJ5LNhiuH*Y_9yGuW5@pTDjWV8GsGvfq&W#dt2aG)g6ee5cQT`6h1 zus~A&Y$dOh@pFPJmqT2ox(Lw`9)$vSL*mrbVZa5t#Rf!P$eIqs9L2mJBy+Cn4CE%& zE`R4zq%P&op{0N$0e6HI;!8dq!dhDg`opEf$^jKCHOpmW<*aN*V9+HviiO8dWb zvGx9SD@LCAR}N;^u6?jAg>H?&SwmamFT-2&RS|Q`DnKE&gUnQ@gb@s~SLc4J&7Y>f zS^O7pI_M94a1NXkK#-HlkF`!W&VQ+_nipW2{l0BO#7wxqWNVI-X}&iYAM_lM zGaKl#rRrIGPP!|s5Df^P{n^E=TvDYLALXq<6y1=k_{-`*EB?bktEOl{QmLt%6i%JF z1)zz{8|V)$@|I0?Q7AZTZl-T?YKujU;iwTfyT%~bV=!6#a%`F1YZG?WSAS|LAqJah z%5bOX+Vxr!GWc*U0tA%KFPcSH$FdIaU_xC;MHOa`H0IRCCq8@1Gx}t7{V*@Zl9Nvs zc$V5z%WMU)8&wMx8LlM7i->YIP=SQSFkW`QfF-{y6r#*&Qo5r-3zHZLrn@Q1$pw#i zvy01BMLXz;ii|}+>e~&#ddgA~ zuZV^)7v+uX(yOFed`z58id}gNEkq{Q$V*>>Cg2a?CsD#BerMF;0__h`i*gmsyqz2( zse<^ey}In?4=1U^X+z<(Wm*$3zx8r)fiyJ4diF(9=SnLS@G?pxZ zc+62cL-wfzd2YxX$4*<}xLG1NZg6E6N_S5@Z zRLu~ezt+&5?{O4|L>o6{|8fB27tu(?)}Nh zpO{!hGpl>#ryT{m6bq$$usfFI1o8T7ulB3S)x5C;Fg@ql;a5!tw5!)y1{{!s(>{2` zCvS=^x-QJdiJwd{cTKG#6FA!0G(#zhS>D8qaHlq~34%1Rsg39oxq*#_M)yC_AqU7q zIN(cCoeNXdsFTo0xZ&)Z9>^$Sm2R14It$e^otkzJyYW*CflHZ%$uDK_ifdI_`cSEE99inwr+Q6Y89G9W`h**pxT z&{!WXy=Pl=mFqBZ-^5sIJ)>(Tnzx{FVur+Reomr&5!aFgJ+~r_TXgZ z^n->QJa266imGJ|DYcM_r85Gz zD?F$;Oy+vW9JZmTEjz++96NUG@R7qe-+aha4Z1i0pBN53oh|510RO?n;2z2LF0 z4Q>Fq0g@n4Bt`0ak*+LRa@b?pj(P1@*Wv3gI3HlbFC24YCZzFoTyK(0$+jqxB1M23 zxPU8mV1ED1bE;22-HnX^r71M($;!&g%F4>hTFxe`n>j3SG`s=1>CQIbGT%^^Ir~lS z?p<~OCIEf$>Z`Bn$M1jt`-ak%mEHuoIsq7gmS!%&wS3|(tZmdp0w&HS^%Db{YE5ba z5RYb$BHPu9)sjXMrKSRe1_nV?;GYnZM5UJ43K}8ES({0g3BiGzZE39v-u4901($K^ zu8JDjaN*Vm0O93!7+_O&)(YIkyNhQyN+?r{jY9G!7RGVwMF(k_W*QLa+$|^oqm=&8 z2(Aj)KCl%*o6C9h7zjuev7+3vn4m5UhuDA?V{XLiuiSwe#0{)4Sq{ANXpRxm0BTXA zmpZ@m&fCO3e(dRYW4WRC4;B&ctafIKM7WG_cjCoehoRO)|cUsQn-ZOs6fd*|D zjz%V$mO;DJF3H8RhRh3#jY6wYD@sGrL+YxTK-Jv6VAT&6G8N>yaNpyQ<#R$DK>kyn z8bKx})nSFw_4%FvRG|qA|FiCuAfxtlOPq0Z#TCU}hh@Na+_mVg!yaqeQhcdDndw-i zr}ANqTP53lN?}GS0W??BmOw2THRvrVe3-cGXNPZD+Xp=L)KjNVpEe~}UYvd7EYEWG zY~K1#ExB8CZ#~#tgO1l!YxZ3fI9)2XVa0edlGbV`DJK~fswI>AgOCLSX#A3tm3f+V>W!6DLl15}{j83&^@*gW>G} zUFt3xa68QJl!h29lB^`RW2hAZy_Mm`_!x({i+4=1jiDu9Hey@F;Id?;VZV{(*&;@S0E!sHfqAHu~mBiU+|_2|m@ zv-)W*c~|)-*x5cXWpu8>A+)fnKm|7ot(|1 zk3G)eaz;E+@4*%4*)>qmQb*ES>PmQS4f^LA%yRqseV8$m7bSrR?W?V`jKOs#9&RMhzCPTLZ z4hh92KHcMPwTNy>AI=d+6o*i8rSy35h$VZ8CYN1jUx#}fK*YbwZny|Fa zWJ(8F6J%6(d2oCqmumQY^{ta=?=K5oUuuM``h1cl}>rL{j<6Q zP_W&{2j@RvH!6T3Rsg%;TASE@syhd^hVJzno+xo>=)zs8Vj5Iw){u0QG=uFjXA}W+ zV;a-ii55x~WhBwTtd0rpQbiUW4GtL)wK3@7+DyW$7&Te9vEvX_lb3Pm=jIG}04SGX z=wS&UkQ_`TyZDF;(Y-z4!ZQUj=*ek4oJDc10T^64%IpT((Q}gK1+KihX8#eNxIL1W z%GJ4u8+7zwY#F?^*P_`fZ(W&C$#Jz8_R9WJ;@x z#^6j6^-KXKl0EOno#(yKYUohvtafQQ^Iirshs&P%X9vKY*H>R03y~SI_VA^2E;Mto zNi{EstGA(eg`Z#wQXX5HH|3P)PNy3)RAZGs}5lVUNPFwu38xjnQ{u?x2e zmA|SRpfW;hUP&_vP4aP!=CqLHX|LB%&kWHsh74+Zrrbi7_v|@F(-5n=uhk2sHN;Zl zl~-Od6SzX4%6dIXc4>s61o4m-w-$TY)yR$fbED)glLNm*HlN#!TR%}#sk8wx>q~Ir zugO`ePp3T^JLgOnghMq4Fw-)r2T{G=E2O#v3{{kii2-WV)dFlMHl>Zmwq}MZy6q#5 z)m5Us6mVXO#2TBAO|xO{VHB9Z_=~?_v)k2s6Z{rC0Ms@VXU?2y^NpZ2gkmV=#h8iJ zS?zZBx&rA&f)giBTBMna+ANxk(11G%cTwby_}?aRX?}z7HxaD+ULOefw*@q^`0yQ8 zbD5BuNt1yGrBaY}@=wvF>vGMZw?=c*XpMY5&mS4W=3Sj_bPWd&%F=*?RpN|AuXhZMP?) z58B`0OjO3k^9nsU@oyyY%miE&hwd1MH>WExCYHUF+!A&h#oZiwh!Sd-KpwxrB>0^= zt|^HsKxSCD{kPg{&9ru8&7`4wWN{AdP!vIYb!XlRa3hmQ6sywE#42KAnU5>9NL|Z_ zTf$Jzlf{s^nTN7GdT8SRGbd!d*ufWrecdLq=3h;j3@Y*)`I^|fiVW@A8~AR811@^g zqLU|2nyPiz^Upv3^Pm0PrNMvycW;;b^((*g%D2Ax&99z5?e;CgXJX4K)7C*nsl8-d zQVh%hmbn8i1sPKVu`RGv%OIkd(wj^jLd?vTnIlVQi6i^!;yry=cMngqgfyc1%L_!i z{HS9AY$?OY*HWfSAwwtKc5!1mMn6oVd$HDPv62#hT^FizG-@E&OuL3ZD!Lj}Ux zJAL}}i!Z)t6txQJF&0#tQzqi+5+gR>v^{j_i6`Ap_OuCud3BStgm6MP39up)vZh?( zkU$lLZj-GIE#P+S%0i zU}6v>i_%++V7%9N$3d>px{P1r{&bKzfXnNdfR}ge?(KKpb|~cOs$<8GbDE69*@%N( zuhe<}gAZ&P9Q>LDxCtGDYoAd7_)y{rWFurt*v*l<1V$1Py(F0d-Q$fMv3qJxojT=x z#ou`T8z;YXl04+coc4NkhEhQf)4BxZ2*TY?9)MtA+GSSY+`+`wMopG%?++>918GG9 zh9`p}EeS`4tysvqzK-?d-}@9!r5rrb1s_`4o11^=GZR7PgG@^uh^O_C0!-rg&?rRO z6uY*T5BvB;pM6758=5AdB#FR=h2n!z{Pa3!R!~AD*I2J(B}F8KF4cS*vt8VjQxT(; zcwH+aB<$WyIaJq-F~oY0PsuJM$+U4jpCqD5!ZR=$JN?vY1Kf(xagf)A{o)tDa5Tim zo_p@O-~HX+{qmQ-OtG$Z_aGgOY9S>7n~{xQX-Kp>+gO!K{%ms_XiJe3CTgA24Mf`& zV%$-Ag;^EJ>Av8|+o84{R;ezlK#B_4Jk?C94);5`r5N3iEwfslExVsdWw2${koLBI zVBoi8Rm2EG+^{%6QLDm2HP!8Ml%FS5xifdH_cXvi3VMOxv>PysTg2%d>o`3H;HVyW z@IjM+^|eO}2xB2_+KzDbMH;d3FAz^qPj}W5*i!6nyI846JI@LLhbkveoMfXOGP3X| zHmh}`K_;!wKDCnQOG>>fXWJYxOKU#9ZS7D+(pS3XE_7#ZAjRr^}@G{0rK3A#VuBA^Q^U2Aj-{(Plb6{UqUs*%5teX5o z?MwVD+VmUF72JaqiyNWPa=U8)g7Yj#Bd!@xc=P05TozJ@VNf6qi>Zaio_XdOheD@L zoqC~y#|0ReX~3ldNVk(}d;5n`CtzZ`&|xE6j3wnD9>`hm`CZ@slxrR|4QLCX+B5-4 zuKWIpyfGm|Te*|}pXLk85o%7D7#7c|@R$)8X8ld|mBEi@cEP#G>aSdsP271a$&~_2 z%D(OpQ;q9)T98X$7+N4%c(@R|Qug+f+fD$6%5z_T&YQ%ISVK-H^qL|-O$L{)Xqk*;`tG+}6?UOMjmzi6LM<`ap8Pn=6e%HqtL? zm{hZ(ia`kp?yw}L8LV`$QIj2dtv3}E+-}}x#~KX_FK`jy^Y()MRV^(N8C`84-tIwQQc0O4S~9vA)a+sp!YH+;9LHd6S9RAcUI$D zMdBuq1;)yUhTK-*Tju=sFRT(i$~liu9XPvWC)VtBchC7;1m=;hscwh1umzQup&#U0 zZC{ufqqop@jLie9lzY-lPXEKhA3pTRLv=sPgs~Gc!C-EsLLQPJLr*{Wn3Eh`?pw=) zW2HiVFLxZdVv@a81xZclR&)cw(RO{r*u=>Z(0E?XR4$Ti**#P#nh{zRN~O5V0ZyCi z$~YzvS(RtW9rt$K!rLbnW}j_@A#h}rf@OqQ36Z9`b;e!A%|y3Sb;jwnT&N%t0j9M@ zu~nU!_4>u@rh!j)f65yOzBB&x(@(n^{No@0_}_l|Q}+llu+yhc|Cew47Z(hCX`q?6 zTsQ!h0)rdbZMiM+yS?EwX=A%m%ZgI3&5R{}>dW8k2HO$#{s7<~PM_XfNUI#M_J?0yn^5nnto*Y^)$7tXBVNgoC}@kCw+=ax3Qk1B{=b z-~ayi2CL1r+!C|x1fZn;*1>#wxRe&BnxfR`&I<0pC;2#xn+KkK_E~o$aVaPbyroQF zq1Ds^fk%TIlN#m~%E0`lfEIXJJ4`1@4kkR+iyKdJxgB@zvTVb0=OWWlNn?z?!Mr&% zJ)}B^L&*@W`s5Nt-I2sJVEN{p*P6gI;H>-8xFK@@e<$YtI9rzo+;s0mi1*i17Ia6C z9&^VxFM0UGwU0jJE1kHwmXKZ(a8MoZusjixRCSA?YH+Kfi*#}*L>5@g+p3Z8U>K}| zQx@z|1isiU)*qbz@K?Y36*H5|w^r2}_g~UE>+U_damd==GsT?vEJIJdE@_y< z${kl1Yd+F&bBO1fgph|K6Vf}mkR!}*l_-Cw)2Zm{jVy_*`m@$Aw^;k?EXgwsP-a~x z?yRQC_h~AOnLuijg-mAIlqzcAmNCj`vOe*|PI&)rcr7v4=k=_1IX<@&RnRF~?C^&z zJ@wR6KmYfizxaz6-5?aTzkAMZ0G@tY3^8e!B7r;E@hyeCIRV>PYGX6;?DaJvhJ?5S zSf?c9CaekB2r(l;kWg3qYl3LnQbm=WIfbn-*lw0#1(V*M!%)Hi=A12lLz4qrb!F%= zGHVwWZj1j1fA9xe6u52F7Iy(H%Nj^t7d1qj8bFwA2jbK1ACDYaV?FHKecJs@0D4Lh zqiHMS;6B?5$)tn|qUD1TE zk=YEli(6F9=^{)ufAk1%|e&UI{I#C9oDdRj#ZG*y<0$!G{5L#6_@jR#bT zDpdkUunL`J+wt}1zU~HdCp`Re2lDXOT#9#FoIP{ql~-P|Hdm+_sI-}oN_)XZa;=8S zN$uaK@g#9)kXwabdiiB%)4zWGbsU`VeD$lRpMB<8-xbg@0uUBAJ}YOH)0^5d_?y2b zq{cdGQ;|D=)R&214$2Y=AAxwdHB5Lqq&B-N9uYZZ;?M%q8F*q|+DI+citu55#j}y! zYO$=IlK`0#bZt66e>nqI|GK7;CxlzYfQAcwTTvJ3xVusZF_ly|9XL<@*mA;W3dj@0 zo;}Pns5bo$h1_v~z#35}dLKAoN}15?`tgBHf|nRC&rT$(Hcu zChH&s9j9rt&!79PaP86St|^oqG+g7vT-`V6+>|EOdl81*@Eec5&jD>BaqIP-98?Ca&}(nbmly8y5S@v}vRW@&q+12Ifo6+a#!o6$ccmqMqLx#~ zb#7zSA|!jXt>zd8>U;oyu7i(`4=L~2O)#3QV#s4Gp|QXuwty9k7$(SCUPuy#ljWm% za{|{%(GJ%G%xmsJc8GE5>T6G(JaK}`@=eWpc^2kfN-My) zb-g?|7^PS(vGxkxSZz{VlSu`m31IsV^GZ`{P6d9yJ1en(2ZgNas(hpV!wNxqu!dEb z_mzOMhp3V{ltnZR`gLSmN)vRfqT%ye?oDx6&Vf|OC;sKa2q5Sqo`rZVe=nO*Hho`x z!H1$;`EXm;lTYM3rY_68_4ZqO!R_M;=KW6fazil9h+7Fmv1v$|O4rhg3MR<)6ZPB3{8ZkHEU3#q_NsF%`sHe2$;XiOWM zS_IOfw8l3Fuzmp(RcK3|BycBe9R!(L%z+l^zLRdPYP8_ScjJs_&mYz<%*mYMO^U8& zc>{E>|20-c1S2;3(nC6>G2DU9uz7OXg|gw%N8vf1sveq0yJYKR;3d)5?454Kya@&{ zA!WwvCp2bDxXTU>*e)?!z|sBm*PPMBFFIIo18GkKwK~QratgKV)9_Fdrk=7k4N!t4 zO?tC*J%{+HmlJ+=@xr;cO~LQbv;OV*TidCT@^NUEfL6#xwa5*DsVC5_!jK;!cqE6Y zjT8?T&GGZ|F-4)9T_DV~%AXhbS?6nDri$1AIFkmj{(`L_Yo*@pq#o@>&`RhSekDti zt}NzgzWy23Su*6*Cj&Zzy!G?-=broScfZT*wyX4Z@VA%%urSyJbGy371dv{uze)^m z1a@a5a1V+tAqz-f&Y>IGsU^O(FjGxt0f`>6bCaAQs2*aK8!(jjdIG>xQ0EYA3AAC! zE!&D;5SG$*a#>^v&&4NP#?;2p8E}A2tWwu9pop==(Fa{c5*5aJIk&pxB*o!IJtE6w zE%AB|a4v5eFd0~zv+3N;^1uTo0$r)calnus+SV~`opjuF^#DN7qINsL09lsd&pr3N zFTF@AMHD(L+Z)4+6CpjOHjn@<(ntI zjzoS*uAjNu5IB(h_~TEU(meL)qfb2X_@l>3t^j97DRrq)b!5XQCl_VvV3=+N zF$lJ7CK!gg`%Ec{Prxz`P9XD8UCAG069u9i*LV9~pjd9KNC>_{7b#RD;;8zs%TCxT zGA4(K!1zTDel8ZZ8tq>7b zzr}@{0It;gJcF&CO-5XNrZirwlQ|Lc-uv1K!)MlL4i#6Ir1-MZr94Bh28h=tkrr0y z=8H#wZaCIHVT-g>Wr&x}*A=kJMeSmY+jCdyy3^#XCje4d81m^nMVe8arJ5|t=0GG) zBST{EK3b`PH{BB2B$xm?9)Ki?a?4rWH~x#xqYU_HdI9#k1*+jipG2tNcHDrz7K>OM8Mvf7&aBMFbv3vqoLXP+?hTmS9i>v4*u zZ2zs}?+BXhZoQg<;`8DxHTtT~(9Z6ZL8cNy%I#Rbd&`^%Q7?DFle!vjE8q%q&(OM6 zkx`tA1`M#o&BA}9i(e9K&RTLqIg+h9$%Imk?(9>X^lmp}8to*(?+ z2Nb@craMpEdIG?c^j34Z8f?K5v!(C+Gq9uOS|b>5$P8ek%N;1*4y5m8Y77qD&K3_D z#f|pMYO=log!izr~tq!1;-39&Gf=a!BG`Ph6@2ED$IIsZ1Zt{=&1gRD#7Fz>6by%`pyX zPMtdC07i8Q<^A0^K5mqF-o_*$-+{*IUqpH?&Zo-wz1N#nWpz2t% zph8l0lf~^B8lyS`&;$@6hPGiV;2<|GQZ>nA@5F&~X8lX_oft)^ZqeAI)TTt$QM^o@}FyQ(0%uoUsIe z*TNxL4IcK<`1N872=V5ykP0d9P_80spNt;E4k!h1 z>lu&&Eh?}ajVtx+C#OMWL+wxbN(EuWy2)5|9MpRPbAU43% z_5^G%OP{=OKL$Or56FdTtTb6ecmxMZSqeHjyPU7VDkv9of@xrfMSo6UtD?0bMm8`p zz!Dy-1TLP=;!o1gvDDIGr6=SBN449A?If85+U94l>wL+ca@NUr<~68hN?u-;j-cck zN*QjG{N)Piip?O_*ZSBT;0|3<*$13G>s?_NT`6=NSG}3tfWS_TLo|NYYupYhvTT5h zK=CDE@e77X;v|VXyXjUloPG@YYG?x2B-cQ?*fL+an(sw0b!?Q+u9*vJItXHfsiJB= zc+1d3BWZgH5M&J^R0ZeAlx}M5^8bhe8wXZG3V-=4U-|y`zt0G_tn~Jg zw>tsg=!R|+0M`(K-9yY+(|hr=bV^;h1FV)#M*5|Tm)#FwTu~PCA&?@%NCG4gbpKUm za=S<;*W+O=6p%rbf}t#afJo!t<`8w4%0rog2`M;*mS;|7tcKgh&@?MC5*@rOJ_s;r z7|@(X*6jdYyi|ox|7}qYXIOC*QF4hvM=75U3>{N=>OGEiPy8)9y zg%3R@W;~#=f;)Qj*xKPke4i0cJfj1{E(mT(bT_120Iy;4aI3P#IAl9zW$i$eil??* z-@V78foZu_)NElMU`vrRt*C}*g`Gh?A?!S=-`0jn*AgvN#c5v!_!%s-n3a=yJvCjQ zvu4BgI__Grrm?ks{<-G~NrU{CvIbQ*%45jHaKEajD%|+lQ<`=`ww+Vy+)YzmY9FDM z&`E5U_?B>BbT8tB*=GTh+fn5NGsqefS2*YOZxtuu?T4w{HU*}7szWuu12HI5j{q#4 z$=pr=F*88J*K43TzyzS{&B2n^7FL9o)j4N7Xe+P>X^+KqJ=_-5s!7WSd5bRzfS;E8 zv-fY0O-|~HOBc)nC2hw+l;oKJG9xEp)(3)eGL*?6-v;p8#1o&gcJNY|AyorxiNOqH z!xRGFW+0ZExLVX%GTZ*q7l(?>zS6ku-07(c&uI;|cMaUZ1fbXTrE6;dLhvnW?Y;+4 zn}KS8p+R7TSX3HjVncn%x8B+r)r#oU$ZY@;upXqMv|NA|C2#ikD8r}zbslF*r1=dTv)r$Z9-T!-MMV zGNw{Ruz(H(Xm1ipCN)lThOX3Ky>JHL!w>2;leam*BfDZA(~t<+KDS`zo>8lKMKJF{ z)guS*=7y4l907OW6*-){X@hMDvVGFV0(kOU^&@EvTg))BQ=8aVhOmAU6j_1X-pRN@(l zMzzY*#+^GUga)jE8C(gI9RS(767i_6D_zF2O4wQ~mtcdqok$a-X)?J@kY?~eQ#*>P zD}w32{4TT8*t-iGmbDvMq~cVQnyF^ zc|>kHa0KTaB>L2CS9W&FV!v#7^SNnEo(%y9l{Ttnk4LV#P!3pVkB<_ZEWH6&fnE3& zv*E@#>5u=fMA=K-8e2@7AZ?r%Kw!uX0&Pr`olG_$C*3>;$!KpLvs_YC$;c?kzY0xT zem{3Ez|pET%3uA}Um1oQtGb)i9ZUe&>rN}CS6+EV`w2sp0m=cf5gLTdNx>m|y?iF# z4ch0P|Omcl-oesSRsdo zOONobREkm)SPNI>Zc&>^ zNHZ=?qH#$`R;yPNQ}}j5G@5|(C`B-smG;V|mMq<8;Q>JkS97YPMI;nA0G^Y>ZuCP)Nro_2fL`&()@;^O1#SK8pYB;Vvkl#eW{10MEWw_4akN^0OEwyh}>+R*; z*#v;6UNmeRq#DVwziU~hk(8xoo^>hoBDeLy!-aRj61S}CI!<-&~6 z@+UcaGy}iAed4%*C@$)ml^ab>6To1TjTO2LtW!Guk%BX!BxYS{1I@NEO}rv%XEjpX z^qpKOixPer2HWib#GSR7)pkXiQcQk!!jHGLNERls+~{+*MHt+ITrA#=Nu3KLmx&ty z+$9g_{(6&vnLtvhng(?8nrVO&SEOeeUEIDKAH}&{F5hb-Z5`B0tRC|?XyTBwrDZm8 z77sqi!`eWU&&?MO^7;2jWrISjNT#|RAk8z4HaR@MxcBPiJy|t-iFP|j!t+)H?%-t7 zz3LY}wZHi&i~7r#;Vnzuv+(}=AGpKl__0SlH((yh{cQDK_*7J{!_sy)N?G03^@ZFf zJ316)9B}=`?WFIL*>SlExj_^%qo6x6L={RKfd&9ZR+;P3GIt3ti8q&8FTV|>NR8e6 zu7+}$1Ia4XYh0U9`B41E8lvYaxrSz5n_SbLZ4+WReuGwBSuu~>3i#v$yk6Gh)XUP) zr|fk4H;u%`Y>Y`TYqpAzt;i59hTipoM z6UD@;8CDNsFvQ*{-1FA-S&2@^J57OD|`C1|g)fh#LS8fC^d* zjS063jf}}r2l}LAf91vkl>eUiY+MYcU&6Fpd?-`2o}6lzS13fqrK2n;s>Bo#CR`Q) zW)LjU0mFZuKO_}gCRd1WKRHC|B1}+dsniJQTQ~;*r9fK0uxmh% zf5+-Y!1eb~Y|BwxF#LF12T)cU1aoSPGr9R?fZ~}A<{L&abhFcE`>vi{`{>-EPuH&O z+ke&FDcSGmjT5O7F!&L9web0 zqAKg+IFqx(W!}}e=wB^p1c2ngrx^**IU;};>5V3dQ!3vIkEjU%oops6a3{lZ4x2G^ z&Yk_@69LG$>8~4w-9$6F)lj;ho=STf(1t=;-jyxYKtC1=DwugsfG5 z^<_mi3QdnP)BAh@0P(1C7HQkx9(_Hyx;YxyNJ+)S4fMd&`sMN;v4EhLJ4UY}PXE1& zd)q4SB;#%-0It;6TwnU-ORb1BW?FZrF_~&_+|b85)mU}SM>m@T*h)7W5LvrllDIG|-e?c4Td* z+a&?G$q0XUVQ`3?6$Zlw1`_|qjJO&)`F+TVRTrp}pXjBDs^g?s*qlH8DhR2k5ORp^ zb76YSSUYo+Hg^1`FLwD}7jNvLUMGJBv%o(pl`-bH!1vti`EY~ZoguaiWX2GITJRTO z48I^tI<~u71zq#2I@Vaij_QsQCvaABj?Ut?;iN{r3c@AZ_lTXURqnbykBm; zx+Nam(2n=_;Pa)-0bsTCHl=b?HH_O9yrUEVX^J^<;z_4c1S0?fQ>TxV8H<{r#;!eW ztYsicMre#I0TLyEX2Hm3JZ`zC5P@5eIPwUj7O`17iAt8w&#@1qwQi8i1R&J>-tjl7zAzL*ax)Ee^5`%UW>fNh`@N$t@bC z2Cy&8aEsRIyre5T`y`M{#fM}pK~~G$HR8#rEYHuqbMDO9 zGycw=J^R+1Z~1_M*PD8u5AjVixfD=ADVWISRAJA>UmraG+M#!^?A@_;b^n^T6kgql zp@Hm=sICC`7r@%T!WhyT+pMhj9S3Pk#{ZRk!_r0RV@HoF!g^83vgbETRVm>J{Os?C!&G9bD(xF5%zZe;{Rv2 zt}3vSQS9YQe5-C1AfwcIe6hD^;Kp@*K=*phuCb>9nzukS(*qzGk6gtuX{EszH>oMa zxtDo&+_@`_PQLo0!up%D)5Ho8eyt6};5LcaN|4h9j1)~Ovj7?uAr>#;S*R_3NzTH> zf^6*qh{1(RBqm%pi-+#fqXnU^jI&x+I!3%pbo(=K&sE1Q$r~;%| zb$FCAvcdx3EK6G)7s=)KF+cw3ag&xtx$dR0dP^w+EWKcO30y<*2R8rU%VkJnUYP+# zNT~`i_1h#9zbSA2nMmg3z5Q*GTI^eLUXesuT|-^Y+Q2j^Frpsi@&1;yJ8R0ULl|=f zH^BzX0X#l^D)`BVxz9>Lc!9ljH(}V;xQN#$07%ky9I?ymR}sBo%86E^)4C|(7tgq; z>V2?~+&T+dPnN+{R-9QM1K}C|H4TW>2qP?{S_O@lHc}a7GZ}P%rfW0+XkqPk6+G_3MEaBrnN%_IUx3y zh{K0y*)SHU4_G~TnHx>SKiDqctvNH zttH8($7=$rEO;WQWOp+;8|bFZwKQSkWvQlI&)XhqZkzKPfBSq^ICQ~LyrperL}Rn} zFY{tru`?!?g-7YL@yj+g9J1OI^kksPl4>#j9R-*Gtf0kUGXbPkzOz6NVcFnY2XGOt z{#oO@+tLQnT+;gk9GFKpK`Eosnf)i9ee%Kk?jrLb(L0vj^SMnv`ncXMy_(P9-gx8i z$FCp!;Ifl{x3TWJ^x(Avk9>A8L%n=L4f&k@_NY~n=BQdLga*2H%==8k4oBm1&*Gll zk3aS}Q@4Y0p%^PVGVpsHh`vL-NVyPk`y6<<&L!|NIf}rKtS0Vcp(l@1uBKgC`LfhB zh30dUcqY8XFG>3^1Nc^d-H;{6;YI45IQ~8W=~URJ%2A!J0R_SRu1Zpn+%VIrf$6 z?mGFoCIBlzi=!7`e9`uVQfgVfnh(-91XW%Br!pF&G}f*#fqv4VEK@ zZ0{3+&1C!be6sJ#uO9s1tplH=74AXYwd25zeGgnau z2yv|u#pVE0y)MX^VLd-YEsp7Ot5x2%ogf!dz@+u8LJeny|ou@KdbsI~*$5!wPz zhTa9DBst`;C4i77=fC+NaFc~^(TDlmWnE(nFk)ojGO_gqrQ`_2ZUPvv&_EN&ij&I( z;1$aim}v!TmHc{L)q#V5^EZD(W24f~34E>zK;6c$esaW5+YvkK{cEx~@^<0J@@)95QGi7=EDypvgOS z7C&q^fGBf;f0+%(~u?i&Y#r zfUSaEi^>o*AM_-WTf;M(3;;f60>NM<9Fu$Nkx$O9eSC3uUMHu?^lrc&*s=H6g+sRf z@zCY|<*K4>0#NNLvzV<&R~jQ+E&u>P07*naR0>+hkUYzB;>1bjq7`N!t5j#61Yqj! zAT5tJIgu_Sj+G=4pVP5q&2ny0MGMoE-9Wk(z@xZ%n=hA)soVQww*Kt%J%ADvWhf^sgAj>uCc2YlTG&!vZd-62H6q=ZZeb& zFde{3Xy%Zci?49I=gF?Is|EvF<`!whdI}tM$L(htC`+8YfAG~2&7qm#BZ*d7YiizH z+)QYLo3;;dDC7p4Tp_6A^=6V!dcEg9qEC!`n*$t_5>jo=D;RPCVQ4}qo?JiIAYf~m z=_uJy{dCvmGf#Z>(cvpMGCrZ58Nsd3Sp@1h8=$ekY$B zElo5zOE4W(4pcc6^czE%%sGH%Kqxu9RcDRlcEAeRlHrzcS~Q0(2_T2;fh&}fV^ke5 zA&>sD6)@8Eq)tp1Tb|7{`U!@*(xEOOU%#%q`3C`8hkm-44P;`dEhei_lnxa{JlHg> z#stbG(T+Us8%@{IVhK1$CGSeR>wkUr*=JYk{XEIfI{~P>w3dhE`ZfTV zR!!7uO4zZJ5?uz^@2{Tw@G{7Ymo9Jzp2wt%4$C%7dTCEIdz`3&GNYv^%BV__;IiZw z0#pngU=ArLq$8Ss%mJyO@>ZiR3acR_K*DI$zyKOKSC!vJE-?+mkFqPAmke(S56+t@ zW#K#sR5xlINTs^=#(+nN66erez$~po;22+R4yJ5HKxkS9tW*REql=3;m-38f7y*IN z#TU4a>J`)$(T2Aq$LdD^ciw%c@4oNd0AkE>1K{TC?fzM0I7EU|qq+=F#l6|>qYIyY za`f8e)BCSIq?YxzQ^cMfyC1l^|M;p*8^u8qN4{wz`Ss2#_Xt0j1t)RmUpkSM9d4@#_8B}ZecQ?#Y?z#8!z9-UClLqbC zk!O4i^f%vlQ_Ed25T1VKX;w&qFTC)AX@ENPf!wl>zJV+njDqql_vtcZ` z?&^Y{{aWjnIhhWsQ96veH?%KZ2V35}e2PNrzGJW=dE>)vR0`Cm5;wN{|f z(Q>;|+#1%EFzr=g5Q{DJ#Lj|rx(lk#4&4g)=38$%SpW5}fBo`HFF*hM^I!k^*PXU2 zh_jNOQ`%muQb7*x(tUKS^_vx!gK6vu| zhY#)Dw{w>}_q|9iFMJj>)P7X5E9xQ5()@Eu>EEHXoE^RT>Z{J9Z34(|@8V5}@1A?t z3(mZ==*;V{+vpHdlZ7nmeO;iZ|AlARxhqI~=k*86jit3CFv`0ZVA`CUP^vPrrdft6 zx{dQ*j#?d-A9JR3s;RHJ{=(sJh&Z7}^(V2ptFii+x^ z$rS1Cr@`w~8POtGuuOyd!!{m4Mu#c4`WW2F&Qhq8DLw}nxl-a#SC})m{%!iL^FAN8 zHF--OD*_NX7FMmZ$DulWAi00m+4hH^Rw8=XIxe?z+t?YC(z+~ewL%Nf#c|N%CIhZq zCO2w4_cV|L!))_Ewi|FUz|F*-EBw#@{LgQE;~TyrdHSi-Y)Pr+hJ(OzAbnlnOJDl( zE3drr`s=Tsedo>h_J6kPD{D87tWE&ha&_nRcXpqDh`A0T5xdR?Q~QU1*n zK)H^o2l%!4QmYZVjX%t?gRXB}&2Qc|S6*_WZvB^Wmho08bh8-;Tm~8Cl?JXGGLirt zz0%h!$tCdwYgG%qp}u9+dACXjKvU@{BUFO6VC1q2<^h&NtBJ3A#b2(baPE0Kx7@jD zlc=r?U(dqAb)I}>HrI#ty9m!_Oaqnzy3eeDIGsY{?X}CLg2NKHr62&bf^e$`8&c1=cYn^tXYMRaQ(21;8R0HrMzy!|S^9hWT${Hh{O3i=@ zRsHlgzF|-l`8|v7%@L6G5uqLNuxLXA?bPnTZWtGZdqiBRM#?jcW^am10Pb2Flj?W+6-fx8PA>S{i!Y)nDkFs zDhzqLD~C>G>C>JGZ0&Wrc=Xuu1COnJ^7XwJzqEJ9-s#HF25oRXSFV5Nl|Qe(|M8nHMdu0x2^pRTlE3oh zubet{>WLFioIG(7Lq6T>AACB-d_W$q-@R>_0M4IR#M2B`EvoMMLj&zU%ods!k5D(i z#~<-G<$&OaB<+i6BbUc|w5JOxf8=CUgT6I3X)j4eR}L(^Qe-8p;%3b^hxJ~Vz2xA( z!e?`G;=E+E307*7%a8Uhnp}t)O68h6*|k^!^z$4H$_|WfCqA>I>_M8VF%}ki$}hJA z&}T0BT+Wk#jG^rz%B;}*(QJ^hMy=lIAj{7axyTpYyyJC!9^%o8w1@XGc!wN zB}zZl9O!!;$JLKF_CQ4v*bRsuhu=5(d!0>lWGlsbP z8YODrwLRCI4QZ@iGbS5oI|d7MDh_3RQ2;kE*KZL=2Fp5uJ3z)@zy`UaA;&>K``OQ) ze)?%oyL|iG-*)2Z+yGlu^cqmo@}1+p^zQzjeDdLr9iMF35DjoA(D{e2eRlc7gKu5d z(L4?rIC=)=AvH6KFIa$aZczyaneX20%OlDhx_d8j9C0Kc$mEGM#2JNFNJjF@S#?Tk zCu(y*l3DBLJ8S^|M&N*U%-7}1Cj0COZd`x(p@*Fd z)ZA$3)%LwE^U%`e9hW_1&MLB9SX&{>eV6jZy(G)LWaD;7ARCQ$U; zx{m9pJ6fFld215nOjcRZT1wPS0UFo}QDkn`&3+LJ+B*u~gqj-rGFy$Qtg`@vo<^~% zWUOW2?;&t6cL2oF=8UM_dkkiM+(M@_%C#CHkow8E%3!PK1B@;`U8>oiYzErKme8PCcD9YieD6L<_RH%_U>RdKy+{bL+AZxI>Sh*jAmf9~b zab(-S=`q2r>@d&7s)oFJl=*Fht952WSq+803|yXh;@b|w|wI&zPobbhHr|F5b0}J z1ek_Zk+xct?c&2cm;?<|0JZCaRgYX|-tN zNorlkkcuw}xO;1x*nW*O5aQYd_{|VY3!Rmf2L9^5{Wnb>obL&6Pj>(%5pf@W%XdpZ@gU-g*1&)-~m~$|N%)>v9S;ZEkaq z0R)yMENEBV+P;Zwni7!JfY0(O5kCY076o9~4eT2`%YvqX?kOn6L5FgO+X$@*Y>6 zt7^#}z+mH9SuU6v7~aO7&7Va;UnfB3A#)po7k}~M|Mfrq$MerU_sws9^LKvdcfR)2 zQ;$9FnXH4qzVgls=U*~j(~;W+P2Ib5*Eb$q+q>&a?;d&N*wM#0{zvo1|DJWa+|<{# zd;vRlaGk)RkcsDGx2zpG0ybMfx)X_6IR|%OnEzC;ftG58$uijl1Q5vC z00Nx5$&JA+fEmxCwsb8OPd{5D>*{@gQi4kooFrgb*FAyn%LKqeJoj<@_%U-RU(e&0 zr=0|*3;peipS0~bXJHO-=4*N74!j@#_{X;Ywufv5S+}`WsXuUrNgsitEYW1E22zL3 zdQoGQ&>va#yYlXM6EsgXR9rI!jzxvIrs(E8Y77GsEbMG^)%O-NNprVM zHS>;&(p#yn3MreGPeYf|ukX6yS^5dQ^4iYb z&mR5elixbD?@;dY%ol8O_@hKMaT@0YYw?I-=`Uu$&NK5)8)LOTkMr^?FS}dGX9@@m zqqt@m$7Kr}Byb#^6SNJuyGg|Q+DY6^>8;D)8DHmT)g?~!18;E@zXV-FpjJwxp$$Wc z!jji3rzkO(H0%#VmhM;qr7~J&m0_2oi7A1m#3owB-F;pVTtyZ`S!c}&`dmG=U z2_Qnc=Jx1gj{^Am9K+2)b9IJWgLN3#eRT{%7k>FGU$)3H@P73F{%Fhry4g5HR?5qO zYHdQ)pqNdINzTer4>-m>f39z?S?`1j2S441wL(cUQ-5rF>RQz))%FvHz3`c9pBa{w zRJ{Xpzkwm){jO-{KWjMYi~>JVVyv{3zB99?S@r6bUGe{1ziY#egK{)W*S~b!<6pRED7}C72DP-3Xp#u}g|uz(&8akzrXwq3dF+?T{ zfIM3#Pw&?pP|{M2X@G4|tRZBouX!dPSyo()^iOqVGZ}XRnA#l&(Xfq|nK%} zH&=cB@%X(a&^7F&`>Q8%2IJmbcH;^&-(q7??V6@`OeDLnE6$>X0i?_4!1-rls5u}# zrM~tTC3bqbI4BO-E_92!QG-Sj++(MaO^_wOf0-2~?=M%|b3wq3FEzek%uOym*&%h= zdmG>5i@+1_Q(mBtS}%cTXOVXAc4r|sr&z1U;^=rOywz1}YDQXBg2o#?A}g@Bqi zoQ>w7js4~FN$EGDcr3@h^7 z|0J!{Uuma3MK?E9N4-WyqIgxN%YI|QN$N4E0z`4U$*8RM!ogO+1y5t!93Ze3N$h1Z z24pNm$d|+)I<)uMhj#zLu^Y$sv8&q*mv-jk9uMzbd*aZsy*u-^8MY^|E|m&=fndk! zK?QC>?IrOZP5=$R9;J}(h5=q`#bH!&MX#z>T#HOw-m!-t%cSBG{noXev7I$YgfSF{& zVuc!R>x%Enm@qU_Dp^5f9Ay;Eqk{mhehJNaQ_Y_XaepTOqM61mrnF(cWum?#N&$7L zMCaMyYbzT4r}Nw??nXT`yh}Z@6zu+vt;MvW8wML|s0&avM4}o0Ww$cq=0?0S8pe~X zd0^?@q$(?TA@oK2FN`*1%_R5jcXlz8083%+D@<=SGHAWV z$Y@||K3Xk*LhohFl4gYXe86PS>2${_<^plM{vHotfLt-gbZ(fj4S`cOK0DWkf%iXh z==$$Hc~^^oEaGB&RK1&&}co5EQR)lewOE+YR52Fm;vP1_;d0<|04AZyq8~`oGSFu_TK&VmFdhb`8G99EGNu&?uf6hFZQq;70Oi{ zAoLCr5mxDF_h_l1*7`6gJEY4JTyW|sLOk6tN>nrmX4-Ug8d5s|aW`MpDlK$YDuiqS z8Ut=JfUM5#ICjpt$I-{#$LJ@LkO>(tH7zt-=Rbe_*MIHot2y7>_&y#1&4Rk&XTL{p zDB|sNZ{IKsujHnYp;II6%$YOB&xsQ!G!mK1{eSp}f9OE)hd=z`KmYSTqZAA|>fZlg zu7k(8^l*ks?j6gM!m+K~v+hbt5_2a7kdcf2=zDl&1V+F0bhtv==@K2 zUHZvKFa00?^nX5g^@)e}Kd>)1k>{m#B+;~Zg3Eb$#TPC!F?{-|2gkgtPH>)Jl0YDk zpPcBGVnU5+3Cp7P8qmBjVgTs}=*fq%aBEM7V@> za+kWkFZs~bzc$Xs~08;M{PGD6KV^pMwWD|4~!RhFm28K!_e^_Jv{B6PW(HLadFak9J6H14ec^{~#NfB1)g@Ze=@)0@Uk zCz(8JpH%9e%x*;oG1j`qz;5&womy6UtLh-rk=-%!ioh)3>73YW<^ppGaR>Dbpk(QD z%5Qhr0&zdV)czZq0O)AZGKRWHt%D_9tycAy#B{}`Cf_V;ayCcaDPUdbf^Yo}RFV_;#G>4UVyDnKFlWY?zK_+2l+}z2tZc&af@tLzoWr*c2 z#vJ$aebtPbGhnL4$x1sij)F*+$Gy2`o+ijiX`Zrt9wp0#=dyLTX;_bVb%dw1%>j0d zM&=|#{ixkiQkP-C@D^)TZfx=8XlNJr8 ziIoND;=!Z=NC4svE4hP4?v_~ti6F|UZ68gPGBOE7&`p(&QlB4KcZi>#*G)K3lLtWE zgRPzKoI7WL4dOKL`Zc%l`uLw!&yy#f)PHm#{`zxYcQm9&|M4IH(F{TRcIbvH%z-@z zvY>STN%dQFS8J%x zFk82z^>MkNxv!wR)$S|*?U+;P$ZCJ3?E}-mM3L+$vu(Pv(hGDehqk-dC|Tc?dFLxl z4srEwCH+nl00(VUOJ+&7XUEki_TTu*!D|O{ynM$&1NQCw?CzcQiTI~x~q;D8%U(E6;jxnx;(YSgr} zm#PB*ZH=9^%Z(9&J76N&rRK_~-zYYUOfMsDxZ`ek#{&;&GQbpCV-qztnFysY1rXw^ zr%%tM_x|?_ng9q+H=f~q^zp~E>fF2U>Jkd=v6S5Fxi|Ffciz@~Q}=@pJZMDOq@O;0 z+Du@E|Iv?fYmiaLBN(VPC@jo8vY&376qCz0>)SmlG&o=~2SlNDm=mxDnL3L&1chuN znSiZ;ZV1oW94G#1#Re11#Nt_>FEFeK@CDYqtSYX7d2Z0fdVX}>az@v^ql)(9w_^W3 z;^Itb&-Fc7{JYw=8|>Y3btg7NHrE{_0OBBz?Y{Bl!`Hm`jlQ?3cwL0;ptZd_zH;cJ zm(E>!`TV|vxgXSRCy(xY^Z^F<;DhQSx%WUQ?O|YFG4PlYvt)H*UZ%Ms18CQ9@se_T z+K8pqJ@0MX$eOat%4IU4yteD=&U&p_mVI%H2;}yUBAsNI@sqh*yt0A~Cq=}R9J5Xq zMdx?+kTv~=wy29_Fd%>+Il1Cm>#Pr==9?alH=IK%h0BkCy??4N6Tr$&E|ZcFIY?4# z{@iMnSwisBCV>i1JPOM^(B`d-Je{E%4q4Yt^M?C^4EDTd{(9aU5xrGT2{CJ=^ z{PmId(0FW*sbs%+^{TT3bAX#?u*`?X>LW91P;zS}O5ew0+9t3G|g|2sV zTxB(}r?&Nc_%55q7Tn3E2x4Q`1QFGt&3^(o3IEeS{nMtuAMt&^2)rV+E_AS?7t^nG zvqwz7mJg6tg1X9SEVAC7>nbWZdGe&O${O^djxgAI5W1EarB8z=%Itm;w~a>Wlv?)M z9T3}N{bi+OLkZtugQ!Kf0kbio$VS8$luFZnqoLZJwr`dMCL>9-QOKDxI*Vo3K0wEn zk=K~dvM0m7TxwQ8xiO+VY~gnA+_(GUa|f?|{m}ITt7GiWl)=*;-M{0lixIZ zXFgT`;rS2SgNWIcs}d?DtCh#v+7Yj zu2dC#ZcquEgrtin;!5XIq;m{OKnFR@zy3Mh&iO?OhPtkxokh)}j{F zjy>+X-P!_4s@|3XkQMustu8HI$w)aGD6l#lr($QUL1x(%<`pvjqOIWLeCPF9EF^hC za+8?KNOn;MAZWY8ef}Mio-+sFQ|y_Hzow@~PC+Y?=_cAxqv8&exeldhkN3dG@shaJ zMQ_npX}fd^tV{aUYiO$NY03avwv||2jZtF*Z9-%dAR7?Nl@yp*&2yD7608G)g>yYQ zUw|s*%``L72#$_U3o&Z`>13)KfuV*_AJYChgW2j8$>W!=Y zx?NVl=_A%#j)T7W``^^}-r)GJ|N5_9?`wcLA2frT1H8h~J)}AhtJrMYbUS@p&T>0c z+O_BY3TF^;_vw<*mWk{7p~&i=>=wJ*2LAs?0O9YUwDacw^Ri^~rTNUqbZ-pgj@|SS zadQbiCkYOSvU+0_yYoe--Pb=nb?veTzjW8X$;*}CvtmM$kd4Q%0Z2h9 zNpWi#rgJ-9b|@OT4v|uK~&$m z&zQ-Y-K#EU7ukoa*@>6ycb#{~wT!TQ5GVYRmy^r#Kf-#ZEN0+VEX&Xw@w3L{50>si)i+<1lDx3;aNvhmO#LI@H=92c1h^dUkWUy5V1b`DI%H z$PsiD$1h8$%~Cvw?h^)01JO*W0q13wu7l-z{X2|va z&V_@r?Ey>YUeujAB}vh|RFx%uLai})z$q9Yoes79UW zdXIYx(d$|Lt4IaDp07wPI>qnMPnh)eVLt`PZQh3N48=C;4IG@^hAO|@6_wIk|owxJ~1?Wr40XX`!%-36P z&p3K4FA32HH0Z5C2BCuHfY)AoHI(&|0R4xjk*TMz?z-ybAv%%~LRx=_`;ZBJTP^xj zQ)Vr9C6p0NBWfk<3r>Trmb%KRoyb1Hi zCf(+Iz(BaR^U|T~pPtxxb&YfBew(ZLJiohkTw2@l;qhzx-??~Xciu&66=H1j>|8u~ zD3Sa3t+E_D)RSn|jOK=%Sk|{o>vX8kh%z7Ku{NuZwYAtrO=I+l=^D#DLE0?a=8?GH$(;sdui5tni1v)eTn4Lb$?2@Yv z)#sKgyiE1PW;tVvx9fN}m%cZc48g@9tklgocWZA(akqIm-)7MoJ4@OOhh@6q!7zQZ zF{F#E9u2U)J1#wN{e#D^d7E4H*+#m%PT6W4+5O2;U$6e~!S_Bsa$wD6UL%tiwSZuO z*7ZjoIO5F{79|$;K58wrD`$Zw1M5d*Tt7?WlzM02c+yhFH3QcV1k!0Z`go!W&B3-n z(>M#srjg`{z<_r53RYjol}8VCmyvjc43l!)jcE;cDHwpzG)=Ii$565ZvcUnS;pRYq zQC3!q(U^>{zTB3HARN2^O0Q+W#9T>WVAYy-T7AXCW)#i=YW?z*^^Y=+m>-aPHo+4^we&hQ+U2<5A%`P^>wiCwOzY{`GS2Z+-Jy zT6FR9r3+`zYS(CKq^qP~@+28V0wKXY4a*XS*bQI^iAJ)40WS%nBVqWQ0wJQWOd?@h z1QfCByWjn8m%V@SZ+HUehUrw_=-9Jw?`yBVs%t5Z3YY>du^Ge9X^+w3QDJjHcZQqx zd>{FthaUdXkAB$C=v%CLn1EF=i^GT4Tua|`BOe{Tx+BZ3=v*iI64Q-3o$QrnojIhU zErO2khrysuk5;T?)*oupWn4NkNC#f)Cmtj@P)|w!krrv%mANpCH%f%E70!S zX^)A)+sBmR8aNl0te_;glQ0}m?`gEiT;MJg(*Zjdx0_<00C7(P+A3KJ$WsWC9D_JE zT$2OA^=%k7O;flX^LCpI}4Hk8q!sFL1ci22XO?yiU~mTTnu>h_;C~ctFOMwk6XD@NmM}* z*Yg%YZ)LIaa{lA$G1VA64C42G?{|;oD<6;j(?5Gs>`j9cO|B1Lz4G9Z2i-~9M(3(B ztHdyR%t9fb*|f_6Ud0aJ+ye2-|Cy-QG%!iSpP6mI3tB7K$4 zbpC7}V7E+~d*Vkr2B5%J3??3c3=Mz*l2|k>xeT%f4SXB_vN*UylZgiauSh4Gr$%+V zifS54O@;sfFQ&Inst%@0iQD=5;k@JJ3Io}brAd=+vI(oa_W14SQ39$&m?Z4=j?loW zb1-1vZ;X0GfQI8SYN?Uq#K@u9nM!pOVXPf>1SQiLN>0Q28s3Wr&RTT#XUtP90lq^g zJ09&F1O#ii=YSR!*}yN0gX8XH$MNTX&LYTMYySczg9uaWjmtJfydJgi)(v!J@??A zOS^8o_Ng;v>f$V{HVEccW7cITI%w{%1RLIh0|&{S5AWc$Cx3nAvPTLo)VYx2kOD_z zRv;gqnDjJ&A@7C>ZzXVhafti&np^B>Q8G@hL{MB90P8Jub_sydt&&~EWt_%w17b-rnFUq1TABuC5R;i&mP(TaGeT1~!%?b%nh#2)kvTO| z4kW;0*Meh({+=g)YbJndnf`M_r^`lgK_7kJ&RarH_c{ONMn$1SR?n`qhK z`R;eWV`pF{c;ST?oC4M2EKl}7^6(=*=VahzjdtZ~?nbMw!7Wz^+z6c)lVsl2f5RP> z=X8nKeP;K=_pEdx!`M*El)6p#y(o%L6!rN7p4)royJ5eLP#Gar6a8nb&LPmQ8y_BZ z`kz|@?yW3yB^hpJ{T)xO?Rfv%zW1-~y_lDIa<43*V}6`~Y>8uHBw}9M1Yjj^NpBhI zH4>bVEbcj<5wJI?XNLKgo=7B_D109r;f8tn!^{EeGXc)E1m+dls;thIs%olZA-Gt4bADqgJS;1<*7=%fDW=!8(=6PX@A)yDJkm zwYVf;L!Tn~(w|Qj<@~@LfUQnJsRF%7xjKlRt+K!O{qNE9#f$&XtH1h{S_t1VXqVcR z7)=8s*fO*#I;)!xBPniRbE!BTax8QRY%)XNeE0=i08rg;*96e*HwJwC&n^G2pLt!s zLbej!t99x6CAZ}n0fv@03-S0miJ=&nZtya+Tr~f;fBUIx299doE#MAOc3{cI{_HW+ zwe>C5RHQc@{jm@RRIdzDP1|06-GNeH!02hY1-ozY1{b$T;#zg3u zfm(!8kjbR>4fScZTsEk~qAn^X*!j!nU5qhb<^iJ&nrdLheG?#^=nK-=0$5*axDK)p zc>J-)zxRiK7>9D{(yv~96&|-1X2=86YK)YdLhBZ>AQQ3BaN&aNSmCipkLg9-uV1MT5as66+a3}4$k)J)DL2WQ1LW!h1kXPE?6ITA zObq|>FaPq(U;dKSIIcB?=y=!j{y+BK`^l~&%kQjb%lm>a09Eh=L69Ip)6F&{HQloj zvoRxue%aWK*w{a1{u2Epg*H|SNxPC-qnYV3-Mi}UX?7Ea7On_y%llW)?B{#Vy;=9Y zdQ~ti^FX{T;J(b0C;iFqNq6&dOpl&Le`eG~4Khy2m>AdbR~Jh9t&L_aWlc1a>;#)mQ;=x8>XYL&$dBOmK&`}v%0NLmT zC>RimB@Rxb0jtQGVT(R&;A$XPpks(1BWy}6p^!SgfS8G&2$clyB7k~Q2e$TM+ny3# zTLdDkk9lc0*0Ma?WEe}ZlS*voH<~LzBq2BhW?>=qnsx`CNEf0%-N`eUBpo}3NF25G ztBecP`5!q=?TN<>3}C%Kog(OmSN9??*J3q^AN?txjP5BKF9#UT`cRq|$(L4^#wW(% zXYZZ_sS}B^&Gl73rvF%}Gpp_=hJp^DZh?^xKl~76V3xpj#Y77~Ne~sjUX4)JGs(Jg zyT<@O7L3=DP@XY99MQ;MHh3tGe4yG)NU5aKfEZ0L`xcu}JT7ZHAZo=8)v!kIP3^W`nBe`TO{$e&13zR#tKl2ERo3AdFi5qD(u_c;UI zGZTp>JSXlff0i7Z-?^HE~#qa0^i7J5t5yUHt@d-O9-qV^Yn=*kohWLqA0 zA{A46vzOK^mfXrb@~j?GKwL@^#SvJLtZvs8;8RU%qh1*l9Eupq8NSX4u6 zt}P9;X-H2d6$=l*lE2uHe<=z3@R|H=RzbV-=R_z${;^}o2^5OSll)-e2_Dl)4VFc8 zFOe-LWH>_xZ8R_3kH8EEm9i+M7m&PCt43owo;f z4gLC8zrOI<1!|L*I=S+TPmUrM>aB8(*!i2_}ZXPp=hAvEODk#-nC(>38;i(du3vGX{nto zZOQI$Z3fKEEkkohQ*E={Y8kCWI}#y0h=ijgPoNejb?yUax8uYM!vLQeBM71hCr^G* z0yc_AAJ`5shZ5Rat<@;GFqc&e1ly(sCDd820fF+ml7afG)7^1Sq)%b$=tpI{T*AqSn@V0^hCv@i*ifOHa;HU&LLu955 z?3V$qbb_Ev`GICRC6G@7lzAhPJ7v%6KFWhZYxE)&VHJgy5gu|#K#Z4{5usaPpq8a; zO(TNi(77vu3uf4KVTXofC2}TXt_D<=N@-z5hk6O_Ye6tmhvps08&O=s{PV5-sAs`Iq(l2@96?EAmy-BS(13dbu*~wF<>ZY~n#D-P? zR%;A!Qp3XsxVD{lzR=z@Dw9L?93A6TFX7Y>YM(d7(3uRWs8)2qp@WD1{LlY^BRO#&GAcIFjSVS5z_Nf3CW(NMm3p)w%#JH%F zD5+qGQ)%UOFvjh`4QXKTaQ)zrYPhOFw+%NHR*0QTwWhS_jMEEZXywCI5hOiYeu+*p z;w#ZmqYATl;2ZHVU0_(w_+QAcISSFkLH0amcM=03gD*sPD=)4-0e7&TcYtkG%`HTN zlP68U6GM?nhnTHaTUifa8{p>6o2X1CUZl?~gj~At>pB2kWUkz5u0XD|B%t^8APx@? zfAYycW840Bzxy302|0Cwm$1Q%gM$~rpDMN$9%s^m9SXjyhO+VJMqauH0k))}ZjC36 z3Qwl8rS?Ww09JSnF}RVD?Y7%@?y#m$PcY4?TBd2x3her_=ez&F0giSdA9NmS7L@07 z(B8d!_@2y#3m1?N`Wwr9T=&Sn97n9+0Gk^* zqPiNPq;}gtbT!%aYyyxPbz+%iNKL;W6yEKfrQJuC4z@406e1W}FSbpm3;jmZ6O(PT z1-?8Iz4+?2i2`6Wh-?5iq>K2eW)1WmH~?j2Ny1~311uqXcwulNaum~{4Q33`#3php z7mgan3CpAyP!R3?{gQ1`U~Py=0a#C>$R~do#2W*Vr|oC0F7L23N*)MHmmxx)7^0;+ zV2RQ-y6427#sy^S4^D$AK@)T$Ar|a0o@wAO9{tzD!^X%KmD>Y`r{knJ6_m!uMVDvk<5A}L z;kr6blq@YHkM^F1t{0^;@i}tj$VWf@h)VwS_n&eN0c}UL-h#^fVBwT98e1>8_#DPLM+T$0fJ1+*TSMP3ofnLUiP(#DlcR> z5x;n<+%mT5EGH3R>eD1DcXYUph(o<21B!=drEd>IU`VcTjaHMe*${pn=y zAFua6nafw~uF1Fw@YE~jep~4>E12&`%2_zKljscOeLm?C} z2l}y`0pM}iR9;$d(d`r%=O+ef0-Fj*704jz z6eK|f;zdFjFjN{S-dAu^IaAKJ318MQ3$T zCtT`a8@cY~iNauE0Z3B`aS*~f*2C-(I;V&An4#22f)_K?3XJV|t6=OP0qPo9J&1m99ib-iq9Qr|gpx?fipSF~&L%oieAr=#8x3)QFAiz|HG z!$(Q<(%!y4=spu?bq|e-8n}4zBI5#<2z}>Ze4M>yT`A?#MyEi4b(^D2>V5TuBm|V) z?;0I`B3pX%Ll&aptjP#m7Ngo{@n{1lErD9w6I6jKH!P?IST_Vw%k|A<}OM zAUvY8F$B~Fl{_?y+sJ4~;n@t*Q_E-s!pqB~wU)u$`h2SS*Y&MSq~ip-;$C$H@)lTh zKW3ejR#&G~-b#Pl^6`Unrw+`61LUnvfGx;cO_a>eU*9!%{lM(aC+&Bqv<*!igNbTv zZV@zY2IaH~3&~tq&5K4fw9X#H##=LX38XOYM6R$aCesQCEy5h2884g&9tI1{JPip2 znn*8CBwtu&gLm`+;~?Velo@i%QW;k^nGPs`+F0PB`;#tu0mzSD1YOt>fu5o+PdkZ0Yna~(5@jtl z5&p%jLi0M&dadF^AIhaGXNRZG49|9OyM7(s7fbj>Lh(xS*~8P1rrREU-d5mKWw8Om zf)!+9^oUbW(h3#{Qovr{@Y32+tQF_S$qk5ziG zqNMo|EUIQmJ=BTk8E1_lV@#ef4)J8w$9bM}Wm|_t)m_Oc>%GciHp`^|4BZqz*G)5x zW2b~BWEfR$As&Wc=W0bbi$=lwFH#5T%6NV1ofWBhe6{$Frr+msA0$PN*hE^Z@$UnC=c@K8~S&>4@C^ zHn%XxY?y%n_l$Bd06E~W7lK0l@{TnDfBW0tUb=h7sA~ZWQ%b6lty|1?qW};#Q zfrNQ)Avt7pT|icUQnN=b|I95Wxu^!b&@+i^t)5$AV;%3yeVD zm?izOF}Tx|!LU|XBmKaCUyG9FlJdAgcxC%#uzt(cd{>f|r+tw0P0`+_|@(JQ+tf z+`W4jy2@Y%Hjm#ePILz%o8G`gy_?>w@1UfB5=Se7B2~z%H>lU^(XEizUD@PJzDxx_+>v`MjjSlk!FwYCy?S2{RjRa$quyf{2jJ zu$u@mBxn4l8!RdwBoxN}@F;#UMf})E9RodCw-nPOaH6BRS4BjL`4u6r>9)0ota=x; z1f&M&^%#f*M9%5LTqVgYfz#p416Y@dI*!GwRJ3B&u)ypYURxH67+xT}vq0PiOXe^C@-M$*GE^TLl8X3_ z@w`}Y(q(ej)!hwblM~MRgTgLztnLBGO9gIoU!b2rWQB)RXj%k<=_|*@__hp0W{^-i z+dJV$L$Dk~>%zc%kSBKQY|l`@fT=G6rpVJXB(Yjel0X{`fi)6y4C6F8>>u7XRN^-> zt3eH(VsiAm8Ztx?ECxWJ&*mBimHBMx*x>yCe1803|6-y$By+mmb}F)`l}mvt_b2jG z^ZAu{_5UgPK>oom0YVIIW)-p;+TkUSFy}Yubfgws8f1Im@I+#rZvLysYy@+$m*>iZ zK*ZvPmZDveTb{_a%REjufdZRmjVNB&B}1@lnL>HCqnU^#5qh?rVz>=ODDH()8MPrL z{p5yNv@FKc(i73Vf`_dNgddwy~E0@b!8lzu1X~*oXi_ia0r7qFcyp zyODbK{6i-cPAnL23oITLlPp|RaKRc~ZM|IM(q!!hDgi5n@7A3rYU?J_U-48UuN zW}+6+)y(gB{qMoU2WWDd9ACtaDJ}~^G(2j67lt_tgapu#C@n00K#0r1FJ8WQ=hkgR z8JS{#n9l)nrQ^^bmx=ZvTQk!$Y%e2NG`x)W6Kf`yl8}=ZZaBqiLb6T_$#gs$67;Pz z(j{AL7&Px{Up#+mZh!xBTaLEcKDpnxWYp`S{-qzEoEV>N9UJeYqUpkQf>gChClrEX=o*3I z!Es{n!YSaRXe?>zsX)%7Cst?-U8Pqz)~OYIQXxX{jAx_-jG~mB81uk@!PDphN4li* z5D894c_oBVDt2HLt;0%{FKpVVS@D_?ml>7R@<}Nev0>;4Sgcrj(HAQeCI@IXqzXIX z&=znqif6B4pl_g~vmMo-?~qbL@7J0XXg&=xQ277_2M+P%wK7KKliVsZImOpXz!su1 z^$^WhJXb139V1V#NeV@cISb{3N;$las3qwnluausCYqp5gquvN5~)p&dwZj!br~5P z9yyE>;GpN^)Ff?BW2@ddUr|{0uS;^v5?cbgp2c>1818d(%j;)e@9FLN;`1-AT)o1% zptkw>$%zRr6J_m)V+5!crj41|jPDK+t%gkM-Ztu~hXOBD^o<%`!i}89Mvg6AF4?p~ z#W`N{OJRCiizkPd-#szQ8vu=i{^XR>cjQZFM&`b{JHtBxGfP?Z&NZgGO=55B@RE$! zc$Wr`oS;zB-CRRwiac&d=)eI_MkhRh7)08cLqKc z#y|mN^ zzN$cjqYz4!V>L?6vf99c#A@(uvAy(5KsB{Q&DxRwK4!hNBDGEXLZqpX02Tq64KNeL zu32P92f=aEu zmt?}aTn$5&tOv(>(MPG*#PLkd2$!69z4)Igre|WJ22br<{N(J+slD?YCVZwa+sH&? za5gE|RvDkp-JjNbSalL#uePl;HAPbUCL)m#NsjE-;*mp;Aw1%lwBvMxmmBvL=*EWe zb!^qI*w~Uyl6>1%uNpw+u%^9rE1qcomN=u>1&Xqhs6FD;$rbt1(A7+wTR2JG`AHBz zqeSZ)5wn#DI;mhBcCn~dZTK&(V&E9+*%+g07v`IR=&&2u*RjU}yY+d%82d zo+qO>o>1uifB^aAPB5GKXjJaaA%(%|Af0VBHC(PRrlhnuZABr40$_dsTu>;_9#6Lu z38mxd#fOd)6E~#Iv%EH?KB-$(m8nTJ60van|KpE8CSWMqHdwwtGk{Q8v=(4}@`SF2 zzH#G*mV@%V-6(&&me5G(9yn zH#^JJfw=-Nj`N}zss#x_i>MbPe{q{V)xY%rSq&*aD{DPIYOS}u{O0Jw`O)dNEX%d^n0Ay#mh##1*?qIu_IKQV z(*Ag!>se}ZxhdHeqQ)N~@e(_r@LaEwL9x}k0f0>W_z{l9Cc>QnCs-0oG{Rg%TM&v6 z!h2g*@zGL*{K779N&qKxwiRnn#W9Ys!mtSLqeG%Nonjal!@Vtiz;C&THiV;`s`P?> z&q4N(GU?*%w(3Z6Tg=oI~i6J)NsU3BndxQVuHfH@x~kL zhX{$|TzXl!12C|d{+4@bV zlVP>UQlHZcmROo67)h+hOxl>FZW(T>)I{$TeQA`n=4-Ja_o&(jjYHfALSEkUVF$9WD9tq`^w_o}S_dCuWWfEznJ> zl~e;2gN$)>dt_+wjiU=+-Cy{2oSlj_eR^$vfwv@=!MBYD1)b{|fE!e<+ITl81#y!> zhyk+SQ>1jB12(4xKjn^u~ZOr&H%X5rXBy5-&8eoxn~JlLHqi^&DQ; zF6*UpB#C}vcQ@)VGSbGdLouuS*j7O#v&p>Fw?7{OJ|A~gieY#dQET1R%->616-espSjsI#Ec^NnqQ<@S7ev5*~m*t$?)eL!$SmU*scX!)dz}JXf#vuj^2%hDx6B*Y3!Q|MO zpJFlNKZ1S>0*$zN(hHQ7u@MqVaFQoux4c;P@bU~Q+sd+x^*Se8qsQXsb>+3>ATI2V zhxVfQXU^?fk$C`x^3wT}f>=TIh{{RX<)TpWcwc9e+A%zMdPu~gC?MnTy(JDB~9RW!}9(&2k+R6m(jIQ z=mrFZH9*t=!4U=V$1ma|Z8I~oNEP>V=?hSt8;|Cg#7o?(FYG+EF>5;hQ}~~cyDGA{ z$Hl~2cTBfmub(PX!hvs6SOaq)28suQuGILTNY+kP;Q-E|3KggolSq0*RA^AD$6 zcnb0Dj|Xrqx*J_YApqAP%)YM~E&wtd7p&8xy;J)OJwE(b7&AnpptcVTo36I7_cXCA z%*|6U)^?cC!waVu({t`mGD&crOae>`=v^?-pIhY#0)GhN#S9eDX(_2?RW;KugMR&( zO}oGV3!!-a{O3RCtWh%E?XkZS3LsP^3V^8=^$vW~c+jbR8U$g-$qQiIAjB>)lK`{@ z+djw>Esfaq_4m;{2r&Z*dI(?j;M#V+L%?nlJ4M7p%tQ-=WSYfXY!diqGS3I;&&OSr zfm*6RM)Bm;J;jHfW3fEu5;OKy1?UAO(n7GXAY9n1G&rjXr+nmS}6 zyNr!H5mBuE)cH7plLd=@jTx|3JTgRUzfn9m-ME+AEvHzOH|VibxfV5kVhMGk;!}l& z@iv~x?r(2we(=Ad6nsVun z(;r$P=2?X>9KNp;7IWe?hWn!by89udvF`I{zFJV#i5) znGP^sKw&^ZdGz)5)8uR(AO}N3gKPuL&dzBsVSb(^CSE3BXNfLrq7|O%sT-ILk#(Ktuk1S|j$cudH_M2aJ^#v;3{U<=}$Ij{fdTmLR!3gp(!S7PesM8W}o1`D8tZ z2wCd|6F3n|WvCnpLoJ~)A)r23w4ny29y4iHkk||W8N5PBI?}9GjZ_*z@KUWA`g^Fz zX-%TGL8)Z3Y%QEWe?F1s_E}#o1rX{L_y*0{{NbVq&U7MnG!>{#kAMe}LJM#=94*Q` z08Zuv5d9TFD9jHK8pa!JBIyKCK8M=iDj_rqF>^+U{R3Lx#yA@3(2FHE7J1p;QAesm zW}XkypN~5vB1CTVL|{6D8Zkn z%aXDXzx5)b37bU_#bMB6Ov1Rx*w!yeC>4v!LKQF=Di}QxnpBl=l#us88>4wB z=KZ)B7cv!qPt_4=H7}vcs3bIH(Wl?&<29zND&Wn1=a@%i`rmD$xbu(^&(9EVmH!*c;Rb93v(eE zFS!DEE;*&l6n?n3#NrQ!e_GQv^m?u+8i1J=>u%#}k<6p<{N&>5#gK8ww$6>i`hUo; z8sCiC-%Oy?IIWh%g9U@dgZNVv(V~fyWXbjb;BrpM7Fi;;c~)^TS&> zCA&xBIX7|?Cw7k<3mHgyh3sL$n6F!~hay0(GLF=cuy`o<7P=aU$IfVa@wX=-?2qeYI5J85|nqP6&K`3jrY$CoO54 zIYfmOBDw^5!UBbMMSsA?awT$!NWhCTDPXZ2iu`o*eB4#>Me-hKweH{{OXwa|)E9D) zW%ho5c5d#&(=&TImp4rIg%a4VK>19C8)cTt`SHb}e0#T!266y^D`V_57|ksU&|nvn ztsb<=Y28;#Er#jH0D>2cJ3|R@2D|gq5N_N{?azN83P9Ezz|cHKdvn-B@Z(XbzOgGs z`@vgoGiw!qA~b%_23%Ok0HJxhGcW|F^O#eaC4!^`i4+@$-~hx<29}LKcCF&SC`1mw z8=Ukk1A4=E4`I)k1wFuDxHuXz;BWUV8Hdfttpce+i=56*ho$dx61=asmmW%gCs8-5 zuGq#eK^V;YZU7yh7XnxaA}mes6%3DCs11$vH3jfBplAwU_^*wc9J>yw7RLTR_`wgN zB)0wM`}`ijvs9)_00VrG8co2?J`4KbK0^l3oEWgsy6g%7>%1h)f)8VVULd@F{kkN} zFdcrHpQz|gAc9^Vo}M8dl!s3aI@&rAvH}|kCVli<6TD~1;7iX#NqMFA#NL(j$L0?A zFRXT~m#}1%*{{z<3lIbs$cf7mSc6>nG6Zuh6^m7*qHJp5=7!$I9KlSpd2t*^QQ z@cO1{5o=!iLKE<98C1HK`&UXV?xU{ZStu?P9%v7MB_oal4G#~4>{!Oe#`r=#LlWk6 z*35cs7+u1*<~bpN4nU(YPDH9$5yCLguoWaEl~|+k6k-y0x2lvgp z>E%5wNlDske3N0Z@98Q1Pv>Tj?B<;s>yF8gFP%Nra=2&lhexI!eLj1CT8mj=0cUF% z4EhvMVqk-1;0nAj>|pp==5!e&3C^xCeiahgNESS@^Dj_zB|ZQ6gQA}`G64nSb*q5< zIS}%o(|@AkNMX{5BQ9YNgX2fc4182)9FX-N(a64?%V{A+N65AcBug()Mf_Ne%28 z;+??Z1H;#@U;FaQFK^wtg%}|d^kilWs10Tbh!JW4!$qX5FR_cjwli}D-aBLSSwlaP zBx;G6+g3BP6*eEpL@`sQj6Q_qM#)=V+$jMjQwc-q60RyMVd)itlnl!ajs0zTWF#b=_>6oV@yF<if2u?}fU5Ps zC=$y$Bnq`>_wM}%w)GQWQ6A!-rUHm-!hR75T7`Ao<0p>q*}Lc4v2VFn8pY08En1hh zTxnUEo}Fg1m)n83FNhVPH{LkI($F{Gd~@Z>l}8UBaxQ@m&wP*(0S#@Ro9|*#XmWCb z2>}X#YbUyUx;jej{sC(w47RFFY^NinEL7M`5>}8kP3-9r z5ZcNHtJ+(<39wJ}2p#_B0j5)hN=%ggv6>Q{bMlMK6HgQ`(*RgoKma%onunYAoCl7y2f z`^{T7InT`-!tzw-44p(FMbMUBVSzUT@_8Ef*s)`8z4_*y+qXFYqV;`SGhi-2yl{JJ zJZ(0>985~V;=!I_EkOzGoM|e6I(6?-#60lisgsP~`A+1-)C6+CiVyGhex4+4(u>< z(1_k%Cm$~}iGD&~JT$QK-s_7e_RQy)VQb*~eK3@Zxzn-z^W7bpC%r#e>ORB{q7IVH z&FeWk&)|UTV>uu=!DmFKr|0bMFjjxG6==u)Kz|M$a`vE$tH?S!q$FBfu~R$9o(qpA z3-O7@Bt?qBwiZ;Cj)eLiCBsq%_308<+S|Q5@+t{`NYpA_`^5ky-V)ti~*?p4~3Xao*BXEQ>QB?fsWlu$xM1m+%PY6y9{(b%R z*G%!5uOqOUVzKez!R!eR@Q?L376WKghWvbPmWv#|`s%9-7cQU}(D90mS((;<6h=Wf zSftlZ!^D83Qj8Q?MG(i%nWh3*zwT2hdL|QjmirmJv(>{uUM~ma)WfjMRAvwY)`j|~ z`v>~_M~{s1F3IrlK?I0RA%Y-Gd|b@Vi0G?!Lqogh?`$M6_GhT5wStZgZj!)@9Zvt`fk+uIBKeDiOcbC1E373s zet>1ZI)t!cm?R)8JWvB1RI`N(4g9?UwDiHMMTPs{#E^5h1RiQ1it0+3EjD9zOAPj*phi#(afH-4 z^+RPsV~EBP4>kjK4GnRO$CFrn5;?k7ZKL~u8SjGM%*%`jnOf5hvV zu&Qht!G`~2o;3wh^r_)$%sXLgDu9h@-Ngu)#-Ikc5n8MH3%WUu*2I^cJr?(Q1&qJl zdv?=%QR&=2%E};Lk7GUY{=NINb2AHkk|3J{@mV6~B@&hbSS}zQ1PG#(Lo2h%E@t|? zii~POjAL0SdFrw=T>bA0wd_Y%3Z{um@=UG%lTE-`&2^a^U7^bGCxl`nE@p1ZYny|zmiO93UylS9pOCiq-h8hSH zA*&YJIw-6lGk}ifrlO%%^nw)zjA8v+i`eJ@A?9j>*ti1RMj?-RD=wCZ^ioHN40Vrukjj)lb@5-p6 z{pz>0KKz1cV2E;bbd-Z6iMV&b+Ef5fuXJP!Cg$v%5vHZt9A+G+;a7Zs5Bt>26c>ng zcl9tAfb)a*KR9vx#FeX8E?v5K?b`K;N!=aD;xCE>#7Fb<%eTguQlJKSLPUAs+gfg& zPtPMmbY+c4G;)y4NMdXRr|cp;NM+Qay+hXNNiJkwJTHRV{b|8GtO(f3HZqKOEdI!D zKaOQ4)>Zw%c|uxP$O_tIZ+G$IH)qc5oBLj(zn7Y6Ku6}{%FN%FI}fM24%Q&4Ly~h+ zdmJrcEpYd42uJwuzDNaMD17wzAr|fyL?19qKpy0no#iwdZ0Hl9UvmO8fD11hXh6YG zqLo7jb}k+fENNn(S!7{F@}VtI1c-5{PHoEAsWSjec5MN_h*2~Nqo@)(0DHo7Bdc>N z@R}e=Vp86qyO?2d6jmXz`wL&P3jJ*l1x+CIhk{^JSm_1IZ+eCCyE);u2p~tA!!TPn zRHde;rn&c@&qJ}9&#*nI^@xUoscy?GntS$&juB8GZ z;Fq00nkCD;ffzQbrmgqe;pDfzfSZ|G|B*8*vjd@-a~x;<231iVz>B z9~v6myKgUZf}=-|e(}W@|MXA)#2)eN%nWM+U_AkuPr!y@5KF{e=^STV!LSoJkf3fDghhl zPFSGdRu2%NPOh;8fWVQ~7G?~{V{|tTYf#!c))51ALpoc^L;e9jvt`s^M1KhRvz}|hF6y42cuWt;{*`TvU>;eXS1M6HC z$W(wcKR@~DPfnaTaq;5C-~ayiSFc`0K0tg8)w*#H4KVvMvB z(*jo&u|;g%ww@eT<=@^0vX9(VktKx?{E3BpVyam-$3>)V*~*DMg`b|AKG3uH{YHPQ zvUE26q;vU~rHRp%!S^d!Ug&?OiVgMe149d?x5T?TvkVb98FF~!Fm1;Af^R1v7`_52 zg)ljx%_aM~HET!62G=d6M!Kg!-{}S&msXK z+ku&QT&^?{dYU2l+8C07+&Azjld(}qGjs>2Du7md++q%{|6~CSQ!dg64BXWgQ7Bgn zf_^Q|*nB`N?uG}4A~pg%PsYcuUb{*+XQE~Li06NJKMTKnOz`1@ z2Ta|F0lmzyO<`2e3PqwsLPfz(ANS~X_SsYbFH-*q3a!W`9xMuR%!9!i{SXOa>>$A_ zR=7Tby#bDXvS>6kIK(;8!GQr3{)NvjT)24Q$~RYT-M-BrjOq`BLPFPy6DsMjkK>(0-Z#LLIV_29Y7#fkqk*LI-Pa}*(X-Xg@c7c~c!oN3LlyyNj8 z1KnL+j3f^1-_MvHqK`T`hBm`Zv>Y^=oSL1VCA!OEffR`p*$c@18;Myq_+IUFy9Ee z{fC}eF6#WhsRofjbi4Awg8D;QQs6{fgb+m}UFu0O=jZ2G&!Nw zQjtB>t?ved1}9D&=Qt1w0M*OD4nQCS#%ndKRjlZ2DANVze{nQB_S;kdFIe?1puFt^ zaHGs=a!{MeJ9NZ_C0Qg1vEU{lcKld1q5~g4ew=qqPQ7;O(@#JB!yo>@E)&NF=-w;> z_*-LbZ5@oxIy*Xz#DZ12F7V1KBwu+&z9sLHGw+|# zs3R`fD*i_ZVGqb-3XgtS$hH={MnISESioe9xSgs-B_MCa9GU=}V-uJ~e~tAyg38QuaS7pJ z(FZ&O^pU`6rj{H)o|q0TkR>jZzIRu2_BP5W0kiDssd~yM-Nj57Ize_ zrx85^XJ@YTgOl^0oSWL;&5dtSMygo9w+=e?QfB%7N@-WQeP1P`SAVz8(BN)pM!%DQ zGX=c6(JsSZ%w_mf3|KUb>jVgkuurY(qg)t8Yp+{=QAzaTsTKvi3Q6)Ss{+zL!|N3df^Df zK@)WMbdxJz6k^(5UAm!ZwQc7-ZBsLt~(cglrKv2C=H@3RWs z?rNYZffdl7R3vdu^@Jq0p5D+tlo_&ujoSFfua2+?}l_)BxwfImAcKkU?Ujj}l=gH&k_Wu;fecWwQY>rKdwj zJqvKj>Jh_NQ*OcBe89jps=0J=a$zqacd+I#o!v8YXR zu~x|eB2FlPqZ+l^_|#_sG&D{cf zgdQXJDok4_0;&Um`(YknTuRfr2JuQE5ZWkC6vWFJCx?X$I3;+ol+TgwK|Uf&<*cIs zq+evRKfUT-+#0Y5S zwE_2||_|co{@;9Ve& zjB?W?pwAdcr-kNb8D%is)3yQI1Gy}cOtgOF!@DhVsU8#Wgh8xdU_rij{z!?0;3BWD z3Fx!^?ZtPG&Hb~plf!*W^&@|UsLb>h(uMKTN_V+^em?-jtcxY(KXRvG`h=%fEKCNF@0eDL$r3g9Sz--*|4zloIaXV4DQvq$L zF1p(V@9H(iE1AX$i#%f(7y8E+Wh*M}4e(S%5f^FMVv%UD#!puaID=6JOc^ku=k1)` z_K^Vfg!OumxV2HI8^g*KDtYYNZ|PO&U{?LXeJUA!z!c%=k)zxjfl9CxcKFd$09)Gt z^dhN&{(jE+Fj!|Qpxt17gF?eA(gxyL+hK_x>E?1UP7(0_A-lw=1ekdhh%=@$voplQ z;*u}@3-R09aUzT;08WGx2~nXTxIl(fcu!p&ffF9|XRflfZ7AWp&ciVfT3?8z24tel zxl*u!phDqS{SO)oKBxCD{>%3sjtnlb%C)t%`EtZgXP?dR`H1w1N^4(BTIY6l)*w8f z7~=+i(~!(Cw%}Y4hn1OwB2IV#QYMhX{YVDt%>W2%0AqiYDleSbGn00!PlOvuik(A-AK5N~Z=V|-9S!B(SxZv^ zY;FBZL~T?BpmR`&O{CnV5r1_fx)-mKF5^exhiezn41b<>*}^DW@HKZ zVEGkHM-?C73?%$~m;W#r@zla$~#E_QpELWFv z{0uSSE>eaJYU(gnaEB;rX>%!_Fp_YHD z1kue~jBe-{3U4^i4@}|{`Wr1;4iXc$_4ow9e90vnzUT-dASa5#j9Ykm_V877W`do5 z3iNYeZ}+ZU-Vv-fI9Jt^%=-+08YcLC-*mBFPbD)kq%+c~Kl8Vul8EH7Ma*aCxLS^NBwi_C4VPVFP@ch= z3o%F2@;N&f)Y*ZM@iGv*1Lz3^m!Sx0@%j(~=ge9csdekN|^HTlwqKa=zGpw1q1o)~nu~s0_vjOf(s4aCJH8 z%AkbVByAiNfVVed6B?duCha(pqh#K320V2y6SU~z{O-W+9U(9T2=)lFP`+F#hO z8MDL}84hx6Q7CdzqCZ`pr5NPYkl!*U!8hUeoQT7mmd>h5Ncwta=#O09$l5G7J&0np z;9fOPe5f%E^|Kn@F~_=h_a3S6#t#(R2F||s-n%0sBXHJ)-6>~N0qj)Gw=i^PCdl3Z zvcLk4qdy{xtdJ+fj-x(o53pOul|c;I*c(6-a78V$#f3218928oX7RrTesdKcF+4;?YkWWyL=_A97%a<>6@hBIWF*h}F5QM=d*)TZc z=}r%e(-AX71;9ykV=%4p4QxpZJQ#um zPf_X(uxP3vCb{*MwkqQp2-(1bAp<<=U$A;+u;AqSRo(_Tbnu`?@n%9+Yb^9CCINIb z0@C|#-MqE#olP21LrjsC1H;3J7q5b`H3#Uk z+JhHjhA+a$@geS|V)K9*!071c{(bv7MEv>ZpMUn*AHVwQ>+4sqj%6~46fq!Pl!y+? ze3QH6db+i6#0D}SD`2}%@1*D!$u7ej z{QAUj?-C)b<2*IotH{8qkhc8Qmc?I`CibrkzFW!ZJ*>ttj6eA9DFfAjcN^!8riURQ zAo7WQ;~+J@B;=7m$AS~Lcx-{xV-u1a{=(oUL=s7w&0Xz1FFVp>OtziogMQ#&urDM0 zk6@kL!pJiujm3!}euXgO6zAv3Xbd!8A8UId!;GN*MFR{z-^;8g(r zMn^_@cZ`!{-+c4UXPb<@lSIO2_L>E(lgeol)@cFRS=GxBG{K zsWv)dqXj-MpK-(oz0>`APSD z`hP3-y8s-`{e2KFzb4U~s1eYLP8uU@^x z=f^Hy8XLQD^V^$zQAD$W{(fcv>=EcNvTm$vXHkf~0Ss$RB!?PcgF%Nzb#s6&heXzS z;zdV72SC(I0oD~6fQ@ubec1o-{qBkEL}`VKj#^Wdl}akdSWL$(Qn_>mD*W68jfD#$ z2wAMl&_X~yU#@mZGUU^hg2%9ifzclKi$0aX^T1LqYg3*CUD)%c0b-oV^%#JvIP(;n zo4r$6kSh_x^DwCR)K6QcC0%T<{Id9XFCPpjb{}sPX{=~Nh*>5^0L^Xn5!70{tdmQ` z!%@t|wJsM7&f~t$3B@f{PmSSyNG|lIradUDIKZ}1L^MBzlW=NIz&Kw$LZWIE#X(_+ zOcMcZ0z_g=AVc;fC3rcprU($74x4*nVH^gmYmCn-sd7};sNTpPSs(~~iupP{%`kEqxGRQ{4Lel4 zN(>K50`-IN>cm}9H(JMI!4%*4?a$4>*Z26H+=JZhrR9wv;^}34g*b$%87}Qamu5Tv zOMazNKJveo+xKv%4p^~+_26)f7$9l1J2?T?TmcAQjDTcK@d)Q5!(YSE^!{5$T5#D0tJiXv0z+AsHt^-OO6N9 zZC)(vYb-B*j22`xvPOEz4QvX^+)UfS8uk01IAUW_E2)^EU!26r6GP15=|pEp(!F$1 z8P_i0O_wP2SC=0v;kER0Hfl7a_BoQI_d5;_r=L{%kdiW|m zV}H;YiD&+f;4?qK$!*dO;;j7q_{Tqf^UXJZ@{^xjymayMrOV6)?%uu2F(J005Ja{R zkb9Pc5YtQt-?7lf0~-Uj1^t8r42)cu5NHnDm04LSWh-+>I>z7Ydb~S7ub$UL|L1B# z!x=)PD#h&doz@$FwbHY%vhR~}uHOLTx#HS1W&{BA=Oi)b0@!3i5`ZT_OPJ*&S==z$ z48Vnuw2ct$4C93E0RuzGpCcgrNs5xdK(z;6ass^^(?psDo{}wX?2?pLbAiyOH+Bn0;kLi2|cqb;GfE2!IrBbnO=L$ zjq_}{3^h*GG*(XR7#VWN8)V=(lS+1p`J)eWA%m*{bO3V#aGa|n_#Oy`lR~^Y@X=2{ z;=Irw|L{k)i!nED+&~AQOOVLU?oOoK*8%KG08WS&hy=qD6I}vgS4Zwd_tYo7_jb1} zWHRb;P4i+kpqeWeTgShwT>dxZOi#<656W2%hVD#-((b&fiu~~I2(3)kXzrSF0g8gY z7$sqlGd96qCt!^4F>eH>B#I#!f&?S{2I;l7!~PK$S!qj&$i)zIDiCZ?w96|_vnpDc z^w2L4`;%M7XAf@Y>7kND?8M;oBpZ}qED04H6Npc>C_k1W_|I^PwcL%5kUllSEn;XY zfGw)nEr}M9(tcewrPG{kZ7dCOK@f&+j7q?ZAqMiw*?+z{03B7q(1S}xM@L4_o;?ft z^WqQBhY$H=#beHc@^(9yo%=3=<36t)aNvO#)96X)670u%Cg1P6f3jn$Jq>Y7%JphR zS}f%seUbj=|19J?R|d~l(i zLqKXnAAe!#$Xt}g_+gPDLU(#7>{y(L#k~kZqS!G`3|?4>1Yf$9dX`=0AJ+$-!I0TV zF`&DgO1_FIP0*P)uuH*6iy>DC&`_490%)k_c&5y_uyS^Tg+-k;?(Rkb0M={UFk*4Ovvl|UzK7?#9<*nQQJ;RMJT{rutpTAzX*qlMcZGa^_DpA?_iQ~_SvUBm zc(ChByMOcbH#!%AQcL9hjUW|0JZAgLq(_hjN?siQ8H%^RaL+QrokKJ>PFLsP804Mqr zW;Zxtpsq07q|Jfpwam$Y!A@{Rt^$2ioV~&pbZg_NXJvVa5Og&z;X^fHgHxNiWZJ-u~-CXHVsLN2&7& z-)98o*05~}0VLv1a@zgTlgG>u8Qy9n&7eLW$$F}iE)%e1wp`?301DlNEaC;|xl{0G zc6dZr-Z6(Vnukb9d( zm|xYv-_p^8Tq(JDN5S*prEp)*Hgqzq6fAq5d8$h%Cfd1ED{q-{-sow%&}*nlEqb&Z zrXv(@-O!77cPAI>8Cd^V`J?7vO&Q6iih@fyqCXuzVdi5zYuln z?~=o2*y5#-{s+t) z$;hBDj?`vF!}AEuQ6Bh$n2wv+RW36QqR@#MXeNk5H=aIxhY0#ZH25YLrX&RO(k_Bp zSq1Jk0np)k@hdnqe<&u5qKH+C+i`kkaAIK?nntF%1M=kY6Gx96`S8OJuUxru>C)vZSH8J@ z`}X+#>-*2Pj~2li&mvmuj(y9u- zqmHHm*wO0WlEl?v`L8`*se;|?4+HmnQbzAGmWn!PVbTem^&&g*+0NAwAAIosJ8z%o z%dywKyi~dQ-`~r8zCXLfz3N+%$7ZBn1Lbo2^p(=}|G3<{x6=PXxwW?zT{g9%kP0A- z+1};PFZ-3T%gZZ(vBbrMrY1GVM0v#q+>fn7Sb;9ez`)nNgv9cQ;5drGbc54DZQ;L- z!L_g>M2@t87M7wdV3vJAVSJ@j0DIEmmr$gKpVBaWV2ODm5M?}jSS;pR^oQtP?ibS%^oY&a4pY%fkh@oZ~2zR23l-b5kS27jerYvy~gFzSzv>dV^pbFD? zrl|lLuT3^7N5~u#d^(kVKC+b^V}z2?AA7}20!YNAnfryfE|d+fj?PYAB|CZI#EJZ! zj`s4<#GJ0)+N8`hqp4p5Kx=U+^Wbvo^1sc#kt^>0u$<1<=lsr8nj6D0u;2XVHzD?Z~f)?Z0A@#iAF{m5Mug5}mQAfLJ{9&|$-= z)Dp(gpZdXzCr>EwL5(Fp<+_mX&=b1avRdfbKP1)~AVi0a*Pc)xykx^Fo~e=QPpY%AOTw-aFd8}D3tb~Ee26aWtLGC)WQf% zu@bKZ^jPiHUkhz)ia{9{*1ILz{c=5Hry+=+Wf^0vl5K+E<@6{IrXh6rN87*cJw3e) zu$RDo0t z`*S(LRRn;y!T;*7{_5d_hYST6mI8$ic6Fm->PH}{0fDk(z2Mk`gn)+78chcZJQyhY z@g*M829$l>#?fCCp|yVWFv!-h-;#5hd0JdN#3N&Sz>&)a@=gv)7 zt|$BY|542EW|+Mx>4QGY%LR7$|L*Vpjtj#X&w`1L{s;pww>A}|!sSH1+79(GZ#2e_ zMTk=_whL?M7BJ2MmpGM5eAp&NVIQ7rK(9;O2q^g1s9AYXJZKt0JvOd9#3INRU0gEZ zsgxt*untEsVVDoancTuN6xJye(1|=Lm)i+~1s#c|Wa`Wrp1u3_vW}{JvNGh_FhsP* zqL~brz!o*1<}@7&U=2$H5t<61ff{2wv*O}T2k7hV?e6YP7iLm7E_dJge{-|9o9Mrt z4UtSMGj*?H?7tQ|`^$&^qTId@;7j^Gx1IASsWjJ#|Ms`P<=79~KQuSDg%}L#7!MYK zyDhxbL>{2fe6^~9vIsm8P8&eO>CC}bh_1mpAnhY|ew7MDL3unRH$6a5p+7&$g*E`l z6hA2j@U{V;^z%YC#gKn1Gu1i}C_ZWSS7cVYo(bq#025RZdhfV?E0I`}e5C+Bvwj9-oo z7*6s8|9t>1^mXKvAJ(L?WO?_C6O@`2XimU;?6%h`r&NRO|4NBSr^u;@WhkRtJj+gJ z_^+~YbjZr!?f@7|3YH@HO{RKgTXKK7SN0hKtZ;zmPOZ*_sC zUB#S85*jX>D=38krV0xvn-K%YH+dR^5EmnVF-0+mV!*8*A+q9?(y_>W%}4mpsY)fO zz_v^T>??p0v*1ct#!^rm6h+BdVq{0LCQM*X(4-!4hNtTQj}yD2KQjPyz|o^e_U_rs zRxlZml2aX28OyRL(hw;=n3^Sq<-!d6YOEa*u!_6!98Cq#cx|$MIZ~g^&3xW+KCz$ag+F&K)V~7!Ao1az3IkUPUK^+rlWNNO|bjXaK$2jZ<5`IDP3q z4r#&d@DDa;3s0E6TC8Z|^mzkLE&FtI@a7lh!0_;)!-pBgO953Xyb@+bi)KY3HrD~F zSm{4aO1PATS8hf|Mp#C|88{oz(o_Hq*eKhTrAj$7cRh9eKX=^yyOyHf3}~80r~v>Y zy|S3S{>!D6eDTacrDr6|F?*f71Qi9cz!2YS!*I}$@A>ct-0(8T&6~Hr{dVl}<3}9n zWgUajD9ET=p%hf`1XMAMd#w8_GGD7SuVDSb3 znuygl{xOZ$gK4lfZ2w_wwU97I^1KMxE8zf7?7L7P*w~-@D}8R(K{5 z7wjsuP&`V=Fk6urn}R@*pFH$G3a&|n&t-J#UnPM{+sLx2Tm->k=ug~6fE<7x3o*gP z6Fb4(3uiE%rKl>1%n}d2NeetM^JL|Rk7t1X__FsqGBR@J%o)!AwDGMp!dnbp!^z-l z?Wke2GVcv=|4;JV)LPC4p)?ghgEYjpW+PpiPfvW&{`J4fKK@GMuo`68*1R+mTc-vz zS}*M@-F|y{`ug0`y}90{Qd?*!fH4Gfq_Zd3DVDX+V@RvX5d#8mLI`4NUs03fMdu{a-_;XKK5ekJo-k8 zZu-Fp=jE**9*GgQrV=sw6DtO%Bfb?-zzroTj%Qj0m+>(>49*#Q*7+qum>IA$Ay6O1@m9(Yiq|52*|TS-3#b z!0c!)bhqGW3Qt#{uM8+JO2&SbEg`w-4v!)tNFRu528MwC5OBu4(rWHhoMxcF!!ssW zO;~0E1&i}A-e={9)qnV@Jx2X+yz$29$SAjEpq`ZZL_EphZpT*!y~sZN(lQsojT|1~ zJ9{-*S{txIC`|>>APuo?*+^w)ucdDNP3z5nNEMe8c(QF7XeRU=H2}`#GsSbA<4*=! z=L@OZvz@*sucdiIQE<+q1BJMt*^XnT(mIRC#l?br2HRTMSp`D@ufkdcP8P^JD4QY? zUR$)rX;5qE36GYY1k0;L^#Vspf{*eyXO1CG?5*C-}IKSl$8<_$!u2oA#NCb)eCHV=!NQsk=+CY%c1QoSZ=B^5s*K_Z zB{dpHi>0oC%fAF!C>B`#=e+?8`|O_Ex*i--w)f`)#8Qx=Dp3U9++syU4QKZ%z#WlmOQ|-4hQA9n*!}+;Wz2G$&&WV!@v%6$bel=mW$I=fTx9@W(|CAR@pS zxWF?(zZ+qq4ui@7zfRPason4#!K#J83ub3?iy+^CTR(=@Jah&KV=<9!!MLNgMXmK8 zGYSflM=A^!sSwc&ZKVnvru8f1SdTAuxWiO{!sGNR@Pwim4Eh1|K!nFNfiv)A*@tz1 zmVM9xL<=)-?eg7hmj1cGhkzk0%o`Z7WwaZ?wOT|nd|;T##50E7Alhy7YAS$jtLhz3 zh&LS<@{_-<-1x`*#F+YL)4U`#fC3oIE&QPG@$^dWcaQrPS8^KY+Uk$5u6Q}X9Y;f) zAglwdurGd7{C+_!iv2w`^Z~>En^4_QY%)i0qIX&%l z$-%?53)lM?K{Ud-L&32s1|1^80s$|@Y6f&xfLJ{^5sDk9V-FVW0I`R{PKn&h^#N*t z4ReP44E#ZVoMfprm*aKc)2C0LI(f>k?n%bx39a{plRR^uBB!$sA3AJbSc^-!UYJIr zHx)o5wZ<0ag?>=T%#LL){;Re}pS?^Hx`lP}(nL*rF)lyaI{k5f=D||iwW-VspuXm< z-F3?&JcV6fD{&t1F~iDGtO$TG2zW7Q#%5TV1YyZbtOE>=eQYl!06ZH8;_Y*ZL?#4{ zk*JBsb_o)OVkU+I{E?iPOrg_i_+yoBs#f}Pu;i3I59 zw2!~-#b>X%vY(H=1Kh0p3%)xw8whbG6RK->PLBQarPZOKp@G2xlyX)6>IQ2hVp9P$ zQfq8oUKlUV-OhdWuk+(yx9FAu#(RfM>}SIw(H^C%Dy`@?d;(uQv!@+ z81g1g?u+1~V*#}6fVRH}fKt#H-|&tw;H`Y^)DH_91T+XHSpk}%u3124TZu9KAV0yZ zCt+akBX|jzmiSH3qy{4RCMv~7LL4EIO2R0F9)|t~+N%$|JxF-2rKr^Liob`1S@Fix z8tl9=hf46xUY2Os_~o<@dE~A)F6?~s?3;%U9%^lGB_59fOGt483(R~_lh3U3LA~MO zVFllSyCJ=r3ZNmIWs@_NN-sXhJ^CM+JHO%eqUPeyCfDwEMF$r4WS38OPCnSx{>x(S z$%0mObYjOAIvlgKdIAt@i&*!4de-0on6yD4Kfx`lKziz&CyPJ)6QeJZ82IBB67mF^ ziCJ!~*$~!YK89{jz@`DRdLXor(sF`;*sPWZSR!y1E70sF@OT{l6h~}42ABo1afD9v zY=^?K!UG*AYK-eKU}n6JVeHSE4@>^+&}RA4OD@M+|L~!MM~@!avuh8xJ$q7w^_uj^ zH-3^9=)Y@d7b{3U=h&FP0ezbapaC0Yle3g6FK5Otq`&%4x!HR{0PF3WoJKRc?WuuO zWmoI`d)<$3Ep*JUWR^=+lK`?Htkspp8R2z=>>#FK*OY|;u${=4GO(RwL$2hyhhaPc zBFI;iQUErjcktzD19x$V5+kx+=(0@-`CC>(ttbn;cA$~JtiHYjptAzx!LbKs>`Ef0 zD2SN=&1)$hB*XwTv*tt5c)yozUeF)N=c6tdvcXhf*X~_x*p7@G9vB!PR7fktu%3nY zu~D387nXgvfrG7M4Dp97trxH{=}iUDnC-GzxuWNmGgDvX@BH`H2cN1RHPL^ws&*&h zp#d|g^0Bt5ANF-j7P8kSyF3?;K8}y@0IS%6cz-iMZSH{1cUDjj_J)^c4z@E4vj)+c z5(t8$F}`5goE|lhEo5gZVZ4D`%F+D`S71zW!_V+$Q>e*ZORT@PPIE zJrAdf`N`$hCyU&pZ%a4{3qjG4B`Z2cJ!`Dyw*Uf!CC9bzX`VVT4p!$|QJ!8$vlE=UH z$*3Rfho4hck`C^Hhy_lFFuNTmBXLgmv*3ea)X!-j44QydAPfqCBc80+clUIo23R2Sonhi9 zY%=Bz>|}d+ahZfz_UY~G-GK9D@oFl7m!+kiH=is46w)(S@;82wd-A0uygB@T-YRTh zewO(Uwa&iX+j?`c^XrMu6;5?l=N23-wp6rW4P5{&Q(h5VYR!j*58f0LnTO!d5nO|5 z!73CXH7E1+ff$S~nSfbp!B_DOH>EUO0ciN|OZKJ@l$fa&U8Z303dkzbNUv!6AfDJb zj^kR3#R?d2Xk%eDg)^I&_18%bbr_2-~cx{JM=@x7lntDAwTnhd~2TaF^VUk zLqBoGxv2exA&{_B6wdr~c6a*PQSf@HElmaRQa9fQnNOt`9<+Y@@6(U3@`38+l@A-# z)Jy8cDfZ*-Qy&g=JYLK_m}~U~{nfI7ScctBageiFfEH&!J;vEs48o(E;9!StqiSCy z5{S1{5T5_Py)%!k?YQszId|WPKQ`-&F#6}GSMu9pA+yW^O zAaRSj4$uHClKhdN=n|w$fF?+rAShy^hGVCN;Wg2cq?P2zlKf=p>Fp%n!tGs>cRl_5 zX2`?)$V>8G^4!a_{D#E2bLPzYojJeXZ+`QeIdaH^>vFz??A0=)Q&c>LTHC1s#9qoF zSGk%Z4I>~w!${yd;>F4DMhqKGYGMM-t<>67&qTI4NjW$OHx9|2h{WM)RXv8bk8=1v z4l2({oJjpVA`Vxi^?WWrIyTDJ5m4QboUBYy8<|+Yf8{Cl;TE<*4m2I9pAZsqE5`nu z@*icTzu!{pQ3kLLka*m3+VgdH?)mgv-$<`7DD{s=r*3NnRB9|;J5#xSZnE4;6_)E9 zXU3YU#PUh1(Ao`hC*d3NIyBnIzx)a%MS;ZGH3-Bk1ISrFM#MLi9BL>L$OB~#bdwuH zJt4%*2)Gx`(PsWa+l7NjL`lNWCiS;PbG478D%O!9MAoR$O+v}b(qDVa)sTI;*hhq* z%t6D$6=NPKGRv@hwq<-`oIq4P5PF+7ok_9eo6Tg!Y{%^RA`>2&GKGKOx3$=I^(!E8 zT(41YkPxNkA@ZlT5xWBLNbVO$R89tY7ZQ5ginf$WC$;rBa(iNRjuA$ZtXn$ARLS z%9vj%-BN1bgCO6j+@G43zv=0z$;m0y`Z$;Ygr1b5-KmR#4&`I%Pco?p8^LOIa-+^L z8>ySAl=W=85{Kf5Ol3$#T{#wXd}3mJY?Kvb!nehb`mHj6sP|x(`e$laGP7S#oqOJC zHMU;JE)g{-+6X|6a&G&9(!yi4`MK4?)wNMW?v3UGEx*t*Nc0v1u&pHybXv$T7D8eL zgIp|B*%0Y1i+B@FI0(8B)(kj0LIi$sa^3KW9!My>1gD%#%1Fm@i%n5W&Xq9FxEF$I zsH$0GfKZD>k3yf)x!N3vB88d?b47!XM9+dPiX9x*J1o6I_;K7K{u6`4}(L&fz3<>EH!QaZi ztp8fBAo?C*1DkfFkmwH4n1m+1Nd^u88?i{3)@vM6-{i58b!xy5wUy2bMgLepwl&6{3^k zNI9dbGuPje8M(RyAOP(=mRo&SW#MXS=F-|ps5sL6M!SVtqZnZ%NQj|xkU|dv3DFe7 zi449erV9<-RKXjPXox21SsXB1#vp=(bH!~_-1`KS9C+G!HPJT`LV4{|;xn^OF0>xCz9shWa^Qdm=)QF*e7MD=)3k%!na$kBW~d3SYxM}k z`Sjh^jp{-AQTdchWn(L~p)#`h#P6yMAYOf1UrwL80@25a@D5qDQaPSX z7V%t^K%4p>A@ERf@-Y_}i+4`9KFCNw`z6By*L;E9L)U56u``8q*m71YFK8o8MgJy)6NJ) zBSTtJ8L5noj>$@|D9{MlXv$)tx`s5xD(tV+rFBW8T7BY2S&SaDA zxCC4AA3xD7ttS(*wOoWN`EQ8%M#%#MSX*6#0njKWD^Pr)`bOD9OpA>n>~bMFZg;@s z2@z~UN{1D}dy2w!Xr4Jrdhf(>d8!N`COyf~j;Xcz%$r}%F1#%ofcVQUG3mIVn!yJG zj;E?09GQD%{D~!ZCc~1fF<3;lHI&*2^&BCckuRc%e%y1cT|DHHEwrNO5t1qUX`%Q$ z!dZ>K5r@V4$GWtvvgTzWLFO5lS_;LdEUuX1u+z#4`;dC<uhj5R4A_5{r*5G9Y)M(QMzzgen~d5lzelZcZ8&d;21-|s)Nfn3Q87D9bsN^l?; z57eJ(8>S$P!A~1|k1~LGbg`Riu4Wd#m%8{|s=g?efa;QmK&r8NqW=9$jWfB4lbMmJ za;cchnm3ZtaB{B`3f$#)^~tzNl`AyA{}p!0k3L zH)!y-(2iRh0g`H|w)OB_s5r|EMh2%)G(573TdzUu(N$r&gA7hh&|3KdK54BZxyhEh zj*>bScw?std^`g$6vUX zvhLiANEH1hX$ua40LRyyf3@)wC$By75XyZyA(IWUp0i$C zN9e|b<_AR(M3UDQH3;vokmE)Z@fhmQEso-$!q(v@fVbSQlOfoSU4S*D+Q{o{Iv3%d zj78EnMA)(cm;#T4)?1299LpOpR3@5~CkxLU8*0`S#LJdthi$`T&LirHvsEIYUyZIF zTLG@L1rV}K}`c2;I zmtS>X_(5Y%X0_@PjsOJJTX}7DX6kY|>&hA}JU}Le&}&S6po~IMWt%XcfN3N1Lm7n} zv+*@!j7811Ec+^>e+NvGSZ)Eew$cT3nQ#&bl{eD>iDn~O-=<%ZZt@aIE*Tgq0X_F` zH}&t-VIzMjlKdpT?<{%oIRO!5_P{(r7>hX*w1Byh{Mi=KOffpju0^$*{yTAhFJkED`W_W{d#pJ{rb7`eW#YDPoZ*w z6bjuH3MvU@UfZOqi)*bIsqvpNzUGMD+$YYytWwhF$wGAEtNNefZ z&D5J$T6f&jO6Q@V&{_y5njgp|4&sQ^j0YYdL|G`ad=@5Uj(4L%LXOSZ&bD&I7Y~=M zU*ofncezTYCe=oA8_k4S(rg*WhQBitn>SafoG>MR(KJwh{)mXw&n=pL96Bx<)k);7 zsNW^q&LY%r5D10=DAq?aoDhYUmYvtmI*b=C z+Sv4q91RhbS{sqy`k}EKqQ2b>z*8e@TLLX{ah94(${w7|3-~tJ#z8=&wL-n*gX30C z7TpZA3jjt@W1qu`tlq}f7DgrVz8npQbT9kR;9gY1Mh<~?rw2g7TFYP<0J<%5;<;#X$Qch#6ii0V zBp0*CKfNKRD+sEOL$)5>yl$p>-ZC0<7XLEO@WIp1SL_ zQz=2AyB60c{T0$~-ImYX+joB(1=uQbm(sgdDSm-aNNnXp*sfp*2Lgp`Hc>Y;1eoB@ z&3S+Oic_muDQjpYYeA7g0HK@V@!eOw`RigNrtatvfI_miadhSHT5>Q9fb9E}BX+Hj zKIjPnjxV2`bKiWMZT`vtdO|FLkzfGzy4VO@y6jZfbetUF5P&>}lR^f@05AmFHv2n5 zxZ_^5=giJ~7cYCas=LkWkvi+I8>*Nt#JFoWt{;WRsTR3A&*B@`rhRR#sE-q zvC5%5U~jkscxo-L+4_6ol8FDCD{=wktXael0fq+*;Od+^JL|DoB-tymN7B8JMAk>c zX#)x%K{9}Fu5uhnuMetFsDG>NF0VM8{Ijq$s8TfFm?5yb>Rp|4=C3IOIFgRV1Sqt$ zF)#)IvdjQ<8DPL8%;jT!-ud&+^_v{NJK(Z3`}iOrX9ZrkBq!|eZjT?I)`qMl;WPlX zAO*<)LOaa~*?2fyb)friU-0I1P493pCM&h;UY&O?UF2xZWVJgStqtu00PqCK01*Fm z8DOwO4wY`L?wvdD-ds>oZm>|tDtFJhanrkag-?HBupO(Gg}X5!UjP8SfEhq&RbAm8 zC%x8=L%h7zb;N%;22PzK&@%#FqYe*n=5IK4-8j-SYC%MlqwYeyNj69Z09?!fI*%C% z0zl1U69Hye>osR?&RMyo8#^~a(cfMX5TW7nWj-L$YXXfI1Oj1R0HA#VGXRzW;CMmc zrg`i@faSaOb=lR)$=kY=w*wvx*av~tHS9rAA?o1S2dLm4TdjC-`30X-zzl!}bL@3E z&%FyCydmd^4JbSNd54t&3>oqfsZ=^l@*$)Ro&m^b+d{nPF$7Ep zR)ps^o9^tqyu+#j8W_rP&Dd)+yv0SgdW*e;acyI$+7*8N4{!y~0LXN7bX3oT2z)Gh zjh3^#;#I3oQ?0oJM>hc(at;LgpT#9t1&G1-HUtpyKg>G-ECvH%033a%lYqd-n9B|T z4uRkxXr03c4sH@MWY)-cf!RZjDS<8n47kT(0LZ}uE+l~L2f_fD`@sEl5)eTD_*wAw zuC4NwSI2%^)el#XBG5YmNDqrkMut!h&^u~<$At8;_8W;!Q3GKB0H#}l`yMs>CSVf) zGXR~x?+cVh=!yU`M7`lHEqTlU)D0K`7(gU;L=BJu@ReYl1OzzDJXAm|0H{+04vYZH z081di4uk;qW3msRt&6}1 zg0H7Ui2o6ieFw+@Kr1Tqpq@JR$RqLU%G;}_IUadWM6&|eX0%%F>Z%xnDhC)$A6oH0 zLN<_tWdL?ln63l_FxXs&w^t8x2>{6?XMqVBqlk}0sv8&rAwT_XTe}C#0N{kWDQp|! z2fs!3=byKi^#FB>z`+oxH+2$uFy!_`T?J4aO_*I4cMa|mEVu>NKnU)^0t5~24!b~t z1c%`65Q4ic?(XjH?y|RkU0vN)O-;{E*Hrh{-u=G!lz!!--|8SalEK_|`$b@>Z3u9V zA>$R#RNTal)1GDm1_JPYPTMlU6Hi!4^atN%9fPl^Wa@)5;8aa*a0@V~!DG@d`{ByX zNH~B$@}n@=g8&{@;k6)eF|`fj{j=n*kRy^rFV}B%mu!F#&S0Pe8xuSVU2|Z}Y z@WcvZs$mQ41!y6ho`Bz^rbt|^g-{Q$c_Z7x0EK4C>_sBR_|eAxG&bZf;7q#Yli;-B z7-B$V7x`ycS)3Z|om(Tie#ME;5KB@+!Z|?!!^zm6&dV8lVgM)>XJ-DuoJ5~w!HD2L zr95DW(=>t@uo#>y`Im42d1r!B;gyc%ZTe}G3xH;tklqx>5{SinWWbT;a^>|(+z~J_ zErUU>5`nVv=K|m+Ney7$zHW#WJ2x(thC^SxymWf~ej(YHxgWL?3=~KZ`|PZghw>HS zN|3P|XA1sPAPJ5t6oulYvSw7;96%5)%JjrdIO?JON%~Hz!%fsofDe8ZozAZw8(5v9 z3YkA;d0j^?p`@gHt2q?-U6_>-NyZPKeS*9F2ietKdfb0L)In5kT|DgE3wq$ zqsKHeV>pfOpZdSS_`b#BiIH4iXJp8U>ubmg=jZ11$ZfNa)kM3`3k?O5%J+Y>C93Uc zS*R2Z+eGSG>975W28pC?ju6oU7nd*WaSy<@OE$F`wPAGfq381NMTYW4m9q)0t9-D? z_~rc()YEqA?c+==S69kjd?G4Uj_>T=Ah>rumOjU}Ji@s3$UKju>l}gD4$-6MXw(^y z$)@M}F&Qg?)sw!@1G``F@kedhZHT4DrB&$MC8>{wsFw5Ti~$4~*CH^cij$KM*MMC( zL!|k!?D}~{bJAb2rK1{l-z;-Z4E*-j&u$hz3U}KLjImw{!w0c+jk_0FHZ)T}4nFTx z%3U|O^^7Ig2MBk+;v5ba#{Uo`N!A!1*5Ss^8i+7+AQKV$a&(9a*8?&G5s--XVtM4} znE;Ht$^~%@^z_gK#7s&kcnb9RJ!(ArcSml#>q-)RO%9VoXW);bhog6S3QSLVZ`w7y zLqC&ssVNzo(eO08XDzFW1o8pek!&;PcmjQS?{3Y8^G>VcPhItl*8HlbP?m9U>~i+K zOI+*Iac^^)gJ^MTg6yI^nK4zi!X-4PHJB>B-YZWlwr1z#Bs2ly*utlw|jew(gHlR>sioph>l^9>g^^|%Yz zol21+v;i6d4^XQpVn5Er@r=47$bH-hB>@abzS6ZN*jr_s@A0I3;ZNv3va~X{mb`>j zzv{duO&SKHV6}N{;OAm;GaRzn_Vt@QOMoUK5||ycO=}z3;R<|mr^YFiQkrsx}H8N zqErH6*e$9PTG{DtXW5ef(mhDOn*OMyq*Vt-YvpCP1sCq4!W-jq6s;pJqci%=A;Q%F zV#r2|W)OPjSrMinsEqJrl?bR;0*PQ%KLW=}&Tv!+z8R6WVx&@baGZ_vLL?@&Sq|v==mYtON zl#LE-RmI5c(&Q@O&ougt-dT|&EJl{ znP6h#1AlPQjq!wGXcS245f!)BsYX|wIOJf{F;|Iz%}p|pT_IG-X9Z}7QikLr!$*M6{Z31IS zQ^_TwbL_AEEqm$;0xtqB2m{>kJIdRDC7n39i{dmhRMb+VfMP1-K9itN{TuG54Lu^p zEIWnj1sZfsu?F8s%P=tk0R{|=NIi%U=A7%pGtck7p{TUg;)P7BrafG|K&4-B*;rDn zMIvC_P!c1P=7HF6PUMkGg_1NKZ=F+NzqzQjktPt$9%x;WuH$9YK;TdSe0Jgo6dn#~ zfh0#tYRkaF)vZCasq}uif*EFD8})_|M)H>8;DkQ~e}g2Fe83akL!RyDjTAl(w1@sV!)dVFhAWzMHaXdo8+%smnz*BnD5JR62l9$!ju6U^NyP7eKGjSL4E|*J zViPtpcEBR`-WTS0NsQIEmbz1rzM{z`u4QbbQaX3S7TfoQ6NrMDqIv1rN=R2cL8mkGMEnLb`fprXr0A^hKL; z8!nQ)(qO2MWo4oU^mgR#%#>(w0ubrrFu?Xf4yL4L;^T>p-P;BYj=Aa(7fsFPBZ2@? z+z#t(SMQ%1;2G-x9al;*V{3 zMTME<@|GH-vOf42uUZK1wA2=+2`&9-{R&rHlL+HPxcHOg+5DmFI&JQE-sd!G8E_A5 zKjTf?Yy{BgEm=D1-?|jTGG1HP#0ZYm5bEhV9yjT{v?HqN9(&Y513)x*0to-vorMn9-;)f~B4&G4kGRu-2e9T%C|(7`{500(kLq$LRsc z@QqH}vNg;t1zY{7>M6W>*^|$Vq_eU$OEC;$ZoegvfvV7{!QZ|TwByM1LX!ytTa)`AxofiOJsR*PVxD=eeV}@+L#n_VvVasH+o_{}tThyhWWV26q`_W9X z6)lDYd$J($a%^xF04Yj!@d^=}0G5aQg#fN}IM~491TJCf1`1CmBqVsZjZHU>Ahp^u z3*24othoG|UDVGg({85)`*vt|UdmZ;GsOCDp@rI!9)m((TKBBL+e=e$b*#9aok+yw zU%_SN<#X12$#97#v6k?*S0m;_Fl;9y>umUNWJ0rb0MbpI443|mcrShetndsV)D&`0 z4xju#_%6&1FK48yG$II-;&%c*m{rDU12q8v^f7rENlmyO1Q=t11$KtF%5IKqd{QSIskpZPYOp&0 zLLS_iks*f+Yfe@~8&9I;fnnRGG|BpoK5=b9Qz3_rW~JST4GR3b+F zx4VWOcY`dl4|5{`XwW~xuN#-oS>7Lw)Q%sRJa%!8%bV+I0!24+Jx{Zqb}fI$JCojl z!>3N9LjV>w#0c%sYw?m9aaTY5Ei`6$6sP?gqq;4b!xGRP{-J@MUBDAE#}Km!N5-hg z?V=^xb^()s^s$&N!XG#cvP;P%k8vLCl}pq6x}!2lqWs4Yj z@t>))Z2++cg>Nj62Sr&T&r=B_QnOlGs-S^Soc#KJ%ar<{ao?vwl(}!EzC9@PyXehw z7}pcw&^wr7!%_vPEN+@)Z6b%tJHV!alnVO76YaMZ68x>2kk|{s6$$5&9agKtc#7Iz zV*5+W_=OMb5J21;Ao3n;nyxVkaWj;``!cYRR=c);XxZABqd5KAt&u~$_}j&UwD@g| zUE#NP0T7%C5(y*6Ps~^6Akg77$X_r+g+#rv0P`vux!ZoiVI20Ip1w=E=S_bUTz8}= znl}1%0p#`%;`-t}d+j_c*oOr`e>2!qe$tMdHfWqRKYd@^YdsI2{P`bjUxExS1MUV> zR};_o>TR+@y#6kvf?aTtP0E3#{dA7s%5q~YX7xKBgy(83Ml|j|Q~G@Ij4!c|0O5-E6+z2st#6`cP7QX<)fs$E3kL$Neu$ zD4Ml-ud^>t@gQ$8dgPy<$c8v)Ag8ahjA#BYUuiJhCWT~3Vew>|pS{aoqDKhcX7Q(+ zi6>aUnMBQB9Sq%%Fx_IiFn%hu7lIE})S-anwm)u-j*EJXZVFc&(f*bgevT}%c z$dIL+bhoOL4uoyS$GG-z{++b-i3V+UDbyS{Q;T3U!gJ(piuyM{oUFI=m^Qd0WVTx* ziy&ws{>c^#arC`4Ey@xybWOD;Cav>#@K(uUe_NDOVZDvk8~@b|5rRi?yT{0N4zIf# ze>)#uZ~f}VJ@keQcLBJvtrvH-d_<=b6fF-C#ssX=Tb5u@hqW2m)j~U=O;@un4e9@` zz!TWu#QVt>R_OJ4<_FepEI*@9<$*&0+IjO%P0zAjzatx+_J_XeE3h$uLY1BE95(C@ zXSEg;E8N3NeOz4p3rL=L3(0=D3{iPo={8jk8tpog3X{ltU2xqT7jq9$hpM`(kh8${ zBCy&#EBSY(&!|LPw)UwgbSND;0mx~8Ckt&L53w595$4GHF?*TD|K)pQOh9QrztQMv z_;KCqhoqgfG%o?^8VAH@YrXwEnbbG9SYzw!JJ|W$$?V(m&EkIf>s`12IfOEgz6f*! zRGzej4*3-NA2W;Y@faKMwPc_$!7qf#OG&?dcR$^47kuuEY>uBS>yqO|TFTrOY=3%~ zY4`Vi3boGeeUA=j4j4Q-Z`1ejFL>ku`9Bssy6wXG!1KivDV>L?5cuD}_Gis#tRV$f zSS%sJA{$D`J5Rjm9T&BGKmKAZtyWUB1a_04oebO8XNhP%P06WHu@;7C6ei}PfWh1V z3Z!M+uYVh$H=_xlr;H{((dJazx6B5{-;$c@wdxoxjxrChlvAge;>MR3}Xl1 zxaXVMUr+76n@`Yc;a*o3&d>m31Sq320rb(QP|V559EZUw^LNMEf7v9WIVF=IrIW(m0N& zX1~C)NAy1E9KK)NSME^t(dzprY7|)SiV*Of`>N5v?rp6!y!~!opi(QT*JM`zX6>B1 zFVSZHv97NmZOQp?UsW8sprcQGk zu-&U7%6nIWEbAVY-}$>E7MACYf$V6>P9rg+*Nk)aR!396oWB-Q4fl*_RO4P(o|igV z$xkIMLk;57!k?%=z|w@Gpd!;inT+ap_ISMB)ZARfkF%PHpze})41$(SaX=?})>~TA zwOUFfRA&{Df?$ojb0f9YAS+G(d1`rf{e>)wz&yz73PW~O&^~$aEkjr#OO)p_-lKRX ztJQ-#kWCv2r&Af-DZwb&2xRYm+V1OcS(jE9*bfM!F9DbXl3D#xrq?Ie>?}s0GEgxRCL;BYQQ_=>G z1?@wqgj2g!|Ff57W4!hPtH3ezGT~Q4A}Pn(Kg0G}9A%szr!lkFwQVV}CoPQE_tEB{ z%gHb~Mi=>GZ94~B9M%62I2dlyYXEgx=&2{4If*KcDJ$LZn^nEhNjlX-n?o+ z$6p`&A5MnF+M=z?Nr zz&B6I-xbQZstESd8&LRK7VEf^GVWgh*iZhGvP`*@ao`VAPC)=raUo1qSq>A86b<$Y zQ~tAzIsgEK1%UvR|872{(q*t4oQt}g6rg;J{1EmaWByg%LP-h00t=%6;6i=?5dMn- zyGUUd0Ja+d2Y`p&;r{zC8~A^3fq-oI{}2Bc5&fYKmLdtje`^g-xRcc%NtANVRNfyD zB=D#^-|fVZ<5646#@%iWt*aV!QFS=@Mj1UlWp2|g>r_YY#-0sKjaX@8g#bbj?kNCp z-)Wpc(9pjpM@21Ey!_v3@FKmdx#{Q{20$|GT6OnFv!y-|m9s49V-|F^{n_KZ?LPB2 zmB_=ytf1vDR_!>MimdN8wJV)EZl*_p2{eo%qDToP*mAe3)fYE~Aovdy*JG6&W zF>lTBEOx5 zv)vv(Kv=dMiyynpB53yY<9#Hke0|6Jc9~b;dsSb@nyAks$G29`yOTxO+8=T|r(X_p zDS3m5hsBCM9ptHqT>d=X9ZqV0*b2YiAK&ia7^I692@GPrAK@8)0sCzJq7s3=)QnU5 zT$SX7P>DJ}T$pU5W6qmv0Gf;-&W~I3L>%9mLzf?~iP#N3g3eZ&(2YQBEpk{C-Y1v+ zPCj=FH897BFN^BxNut;ko}Z6b;|lf1E!Q(gn;O>%Di8OBhHJR+)ETQmT0Z&POPhW8 zl9AaqhcZzRg?q!-yP;jy0U5)T40`W;VIAUV8>tBcNGr4g`czR-^XHzk$^CI0`Urho zbpUxhUKL0sfE(Y$95*&#&bCIrm!GQLb_QZ)K`bWX4G!WrjCli%IRN{@cdmn@^zE0m z9v_zLNY2SqV*NzVybqIF&%YLZKvOer9IN~~@vWgDC7RX5o{(Wa);3qU6|sm?8hb- zBSWO?0>%jsI3W|XDvs{6jewvKO0k=Z?vRTfHt79%h^|r6^U;dV6`wMXL@BLgx7XK# zksAEQ){Gz>knp{eVHg(R!yA9Eb`Px?-SMZ5v$nW#@@Diq<)KEKeNvUzG!Y8*<^UaG zSBukg3+6bPNC|&Tri14Kz&FtAE%eIhZCH-VFQ0*yaOv_$EBD=||5+2!H_w1mmATKe z0fd6mxkwV=EY@q7{QBzs5n6$wjua$sSvc`@DPmq$xjU!7SA^Hyx*T(5DeSwQXY&KQ zqs_2qQ0!m6K}Rh|uwsqE2V%^`1j>a5anI)oe|UsYkPAbwpfSH?7JZhtd`YOf!sO+Mfec~&Y6!2OO#?^8jo&UeI8xp{M5?Ig<;`&(*a`?Q6O397>~ zO#s{Opg79pN27tYPwF1!xn#}nMOAMoQb?rnj|C#Tm2eBl9)u_A^4jIlBr>3H=g_x( z=+$+_7|5J9KgCtB$>d7>`J_gX>`G|>A+gs9Of5^OpKNhJG=gFgL-c#a-=C(*|4k*0B7Oj?H^t4Fv$06Q33UZCa{3oj=Z&{vcRnmes5!m@A*>??i7hwJ)!&Z!aG6oE5(5{_~KK< z6!6(wNM%FM%E#{!r>^<0n?EWo=BgQM z2=CYsb~z8YBbFFkHwza?4)|u&4T{`(gsbsR{+ejhS288O5Wd-XG8n2WdLd40JV?A? z5L~|d6GAyO-~N-gm1obZ+=9dMeAa49>fr~{L__&#P994cf(}Fut>bAN^jZPE9p*8T zsnFrTEByADgA_`DYA+r1FFtLcbrgV#GqdGt2P*k=ZcwB|c#^%){FaZi&;I~aVN_W?-ap=o< zbk;|EWygof?tu<2Qz}d~D#az!&`WTRHoPu+HIK2|!L*9u4`9j8!Zg-)tws~@{#$Zy z0G5^I&)oA{hA)V~>Zub76oEypP=ps1sBEd`s8c-_68$9Mvb)S`#RQNSv^!3Xu?lP^IKp1v zX^PWaCqm5r3w?mT=Fv$kzOl6pPI&GuS`OWT0{uEa&FrB%c$r=lfrc5}>)-P>S1K8L zhzhXMdWy=Qlb&v zKW%!TA@}7W>*G_;<#s>lCXcG^WoUR#^HYGu9V91Y(D|gNoW*h?tl;ys2^kV!Ig@hx?XNJ!36TIdc^wyYOJ6RK@)Gr`hY@Hytz$ItC&rDMMeK=z&0P9qTW(fpfH- z-ue*nO)_h0mYmv-e4JYOb$=rQ0NJMa>pJuhrYv?+IC-oh^7yml7{=T$s7z`l7AIpB zs$-;Yjp|Vd*p&^jV^yScZ?nAF^{#GVcb6VZ;%=`2sRdBtg@eg%*XGfT?>nk6%`43{ zj6VF3bwL&*mD$Tig?s&D+P*CNf%--cmUS5573x)2b8ku1kWF7@6a-KfgiML7r z>Yoy<57BN)S}|{kmhDPArF98hT>E#3nT%i>x?orL0>q!ntWubME;|GderkZ8Er+)~ z2eH>1e0`dwlZ_l{ju&buxop&{YNLt)tp*`zgrDtoW-rD+I{D8=b0OIf+pEihJ*cJI zUBNGD!VV+gUCR2SJJK=XtG-5b%6{3Vdy>=QZ`>Aan&QstMZ>y>&e18d2Nsy|no~e~ zGLMKRCVW)xINTb$MC}HwoIQ2wOLU(b)sTWCDC?UJZ?TP@a+Cs#vb>KbyF&&=ctfs4 z)FtiE0LZ50Bv%5M+^HRX9WZ-Y1cb(8crrHy4CZL6mBc+mJ}fU$R*FZ^)EiGZ5Ngyy*^s6bm1g2+jN0UEbHLXK6 ztp33HCaOH2tK-XM$9r<>##1;(IX27`l62@1@ZuBU&J+I-^$=Z021u4GTU-KnW>Br6 zkixPmxd?ZX(2cW1j!G;;Smj9nSj1%@AC*OlcqlTsEXege*K}5`LaBJC5+v=|!X%SG z0ud|JYW#}d>{i9tVB8Mv68TA>F^P5{6`RqzXHP^`6hVT-mxBQO1f3RtjmHEiWs66P z+%1G-aZ?G)Y7eOP-PSLl^9tE(}``rgY`WRQwo`Y_t3dx4*e~T1!o)LKIICVVHZKENb4J6sQ90u|X(~0R0{U8za-`pDKnjJcZUjgj@P9M< zJ3;t$*`OIIhf_YniCntRVf$tKoCj zvYM}v(*@N`_@Mv;G9RZP!xU&#&foX*-bgmuazGF9WR+2at~7^G1djTv>rGW&X5GtW zH0T93x~KS(AqE&dMo!u0R-`nd2Og{eXZDt7YfYUM!~U(};q-?2A97p$uBZZHA$yr5QZ0Ys;pYIydxU{+OvJ z2vX$vORS~MaVME!tNdkwdiO9u8yv+?D*?JMwUw!+^WexvL0dffBfW|b+zNC{QEOokG&V_ObEQwqh8 zu@(1C-tv~oeg!DXN`V~cU(U)9$tzRnPX*{k4PT`+Qs?RIztRal=QTp5@>mO^NfHYI zwZDctm7-xsE1r>Yp3;5w;~T+JEt+~}1A}w3))7ez=X=M;>WQhdbLy_1A2l)K@@49H z>V#(W>tueb|bjj8eyL>RwhZtPc~_+Gedww`@mc#!XR{{o=|M9*)Zm& zpm4jcIVwol@bhHDonQcO#`*}A?8(xgW=Q#>8j<{k^{%&Ba`je17^Tgz zR)p}%Nx0pvuw0j&zae3Xk58R;oC~8uP>VB=X3N+uf~S@K90i9@FE|Aa#y`xlg4Oar z4`XNvCpx;kX)OI~Q?j88T8<)@;T6+<*2#RQp4VrWW>5^^y7>NE^mS&M(1m=Y=Hs>< z_5@nbG9^Gu)+|~To{#~D2=AoyUfI$bbe7H|1+$<+1rm27>HYK4;-!yk#~t`OSJA&; zmO+3lCWe%n2_9~SomlMrNtr3jp4k%325-5p^w?^AFn*tQ@5tfygg)low<{r&Zai9o zIy2pcV1-v3K{tdoel`s1<>fJ}+G3uW>X$ zf}1R?*ZE<~W6tUea%jctMio>3a!B_14i!Sn%|S4OdH7r-d)@WAz1MP70G$hUEP)w$JGtsH|uZ@oQP=7Zd=^2rv1)W zqVv-%_R1`hnlZ52mM@D`otTDp>brps$6ve2;CD6C1*KW6qq$Gl>%65*v4|IcYj!5| z2~bxi@N>dgrKx&Y-E%-AP`ERo^c1^-gc`vcLM>&s6oiNe@T_nSc`nDK3Jx*{Id^Lb z;D;3lL`E@(&E$O-0G4hi8Fq2FGwcLAB#~Sm*O&c6aF1%>TrX?#C8D|BSS03eV4Ocs z10HDGdXnPRO%5o;=sn48<2NU^9A)*BKpMCoG)^ohd7)H&lwALY*(JeU-OwH$Aly8#=G`FZpPTir0}ZHI z@=NK5D(Yq5M;g-t#>pBi97P7+n`>2v*R`xaOuS_R@5k?@;2xqfrcI;8{bw*#JdQl) z(_iPyV%yx%jEnsLxEB?tVK|JT&0Wo|EVW}SG7~v-<|e-3dhUy|E>x+l>Fd^#DmBDy zvU}MDju{q`nEhhiK0^}eEXkfO?;xch6K^^If)f4?w+|tVo_U_d{bNa;Eb1T$*aL`S zu(ke)&ml&>%Y=4b_ZxZj>QGZ`2be4RRALpYxBhqEYrG%QdJDjb1wUs#&Q+-9XBP-8t=j>WVYl`|r(qa!*#-5g?zzr++4 zU7$ajy2k|H7zqXv%TXh`%`0=c^n09-2l%sYxEt1AQ3)F9Bqf^6trYWqI4(p0o>km}4+0x##+iRq+)4pTf@-(A!$ zdEW}F1S^&-SvI;?k+GKAu6GLKrN+gs{~?kLoDnmHo@*kx9R^0&W8wb;P%-T*QjilT z{L}_}S3SM5;>Vn!hwQYDmje)|Qzi6nW`b4lS1gxl(4gk|K;Bt*n|JH zIwPOHv+|`GZvp9OQmgYhW)#lMyF>H?@|ItCKBwOlVUPuFF<${~rJ%ae$d3o;-g6a# z4BP{nZ?nJHeTtY-<5@J}uSCz-CD(#@ZTo4ihIO|>5BlR}qoyGf!ZZD}RRkzc45mz) zqg@Llp1nNW`_FA$-_lGjy}dqXER{K>Hh*}-#FNt_4cq$(f0?#-hq{Jl*)Vd7#mf<} z{UsOUVyFv?pk6=%(-E>3FtP@4M80&yBsplX3X0%%Bb??pCqw+c82{Nkn`JhITw~aEYJX_S@N8`j?)5J3f zNn+M(q^Fwt(F&gCRR=vMcLCV$AVeeGY&9G*A3% z3j5T3ZoBNiiNfz~8hEF{ddY~FTs){BFOR~u`=giXvx)!BI$FTzdQ)zsn!kF56C>N& zFbY;A)&%Zo{MjQ!@+}13h7Hc~8mcS6Pl(BV!lsBT!CqW`m(3seuFew7lezMf%zsE; zjD_#zFsj#RUdL9SG4ClSYaRlqT`rK;{w&7!T)U2Gb}Y+!5G{{Ar|=0SR6$hW8>NFRdnAsgvG8s`?FjTicQJZZ{|ZUOLh$BsW$S@ z2nZ+3d_d{^*pJ1bcX%19QMZsd8OM{mxhV5_hf}SUJ-{ePX;o z?2A155hO+U0}Kw7z=)%<9S6k_G`L`qx|az!pYD+A)SX^9QWOvww)Rg|XLDg$FBt(o z{}K4gy1?%PPRsoA8`_Wf>h_=DP~dNWt#*jm2xD%dmWYBO8BU@$A_gXJ!K994Vg}41 zdm0V*&q2oL?NEp%I3sAvl8vxAguVb;=+=r?t2@*~3h&o&(pp9{yl~zJ68N)OKXVfR zBp%hP7C3W1mv2To&)_O*QZn;O<;#y(y`0NhE=#c|`#4bF$3G6is@~MaU!GXR5E%F{ z!D2vDC}G`sv3tn&Jqzmha-gPy`+O5&XMonh(~035W7(=d+E91Q6njgKGI1;=ug`&e zi*pG(hHE7lhztEU`BkubVg0w7GAC_g?4iU&&{a0l2vYS{t%pjyB{E!keuRdPyV5Su zMZ*|S^R+)~$%F_^!nxc;h%~z%#Grzr!-X%2OdxvWF)u{@b`afYg#>!#m6#-2d@TpO z!T6rfV2Z&Lpk_%T=U*@I&n`ti?{&H*=kLnuGwH5`Z&xT@Np^J;bK&I%vKNm?cs-K# zVODrmgIS^c@JA^Z-my{3qeoG#63I(SuW(q9niE~6gTqPlh&OA9VV`@~AG<%+>^mE% zQ^o?p(PCG{AuoQL<2606Qv#fJo&CiQ6CuZ4O>+nz!R;08W6STwpj$51bXjQasr`_N zki8uE;YVaZRS*+e9}LJE_=m%w3x6}HC{BiCWIZG6uj=WCp|Tx~TaO%oD$=^mB_mq4 zL3S&zBo=s%&J^)H!tdR0<>F<7hbBj+{ci!n{F{qXpLX1Je$k+qUED12-|r^lR*strTp-7cLww8lfk)6PCZt~t@%sTcgxmg zCHKEEScc`BpX-GYV|-~3@I?o89+s_ z19Ul@Jh%75Iob|>NkQ&I;PZn4cSt?eCsa-l#(s%F_(?Q#0x5}uthN9bFJ0a*;Sx%K zIiJ5dcD9%@T;w7%9C&w8))jHjTf-db_yq}i$N-X?@<{Tbm2Nf^o)$GaxYD4i7W{7< zNF`fvPiTK3nWIp+R}>k7e>Q(mj}fsL>lLB9>DkGNzj3_8S;4Svg`y&r_UX2%d46ck zh)!D^tL|xmEH_^}oYcLmj@@kp`wP1$n|MYZB}GB=8u#ZG#VK14FGKm39haInrC-gu z7A=Y(6;{A6#s=@7!vy>iVlBTsu<$aFA*mm_f(nzK1@O=5b%cKjKohfKiBv*tF^#6q z30ih|UP>`$!NV|-#{C~;SgLvpu|3l8jNb$=yOTfzYxn#pA)eI2jT+}4C}(A}rN!o= z0Dhad-}q@sTYxaKdO(pH)nLk$E&2OWb#Yf@K5B#S0dcCF`;eg!*$YWUd;+ITxou|9 zoxZEaHE!IOSww}+CcB0Vpai(-n4E+|;37lrhv~$4x!nr{Z9C=E|69YY6iUg`TYL07 zuuB$n8}x!5Ih(3G-+uuU^9@LGb#}865-`opL@W807Ti0dKP##LT`^r>tpMt@%Nf`g z(kDkqc_>vJFEu%&nJpK`&hv?o{c1+qWQ%}!ypyQRCUmTo{h0%eEL@B@q*wumsdo|H z_GL%P%$97s_r@EzyR%^n_petcQ`V2#>(Xx~9>NK^B6$&U7$Uf4zVketosXgtXLnRY z%s0<{wsq_fWfY|qMj)YrlAed^q#u+1LT_;3KgsJ zp8-!VA;wRN<&7K$y7P>$Tqfy{0T7DS0{8dZy`^Htec7V|kd}zbCB*kZAK%=&)A$OO zh&r@l&z4C#N;HaE;WPyt3m$k((?#l(`+XMh4-AclmcJOPwuyzr%shS;C03;GT?MNaMBUq_wQeHPUhswl zNFB_F-}9Ny>`SPd#EQqLH$SG^8EY#6CkiOzj$*dX6?ta<51yix!kHC(n6I0yIcxS|>v2^S0h z77M`%MTGPfglu^C>-oSJ|i^+>m$}NQcSnzHs^O*VI3)fk& zew%pP%j48fqHsbd%I+(uwV>%)>DS?aOu~&ZRG`qSaKizcOf)@Y|v9lk*`Z1I~P{WQgdW4tqq(mQuVqICt0g+CB%uu)jMTE zXTQDK^%5_cAhhTK=VVmv$~8Wk`3w|lf3OIqF9f3E zBP#{n)yY!rI5z{Q?kdtjS(xi=YlK1v?geuac7Ss9xy$TUUw- z=l5n4sQtv4|3vxmbNSSZ52p>oJOIGn(_8^95e+;ssHs8dpCa zA9GwbAjRtQ4)`@8XD`Dr2`^cMZwJ<*1eFz9QvT)rvVmVPcN&7f90x&^21F8NEmtZn z9k+G2%LhTrwX7u*|9<{!T20)n4@l|jpCIZM@pouZ?TqDoGmuZVl4%6h%eU6Wh z-qA3(9M5$6Oz@ye!&HVW5BI877a%OEX|Ma}`sG$)+Lhq%#zz4YcUs9t8z4+Rmr?i- ztPYQA4O&%4Z9CLw$*q<4bYd^y4ej@XIjX@8fF6@TB`JD)LEa1<=P`+G0HB(RE&9$B zZcUkH2DhOs@(1C=;MYMjq={9|Ln+$=TQU9aB$}2SlA9Jr`!DkuD;rH@*za3tT5waJ z_2D$b^U+aG6Wb>Za)HnBn+x8?KngYdl=VecGrwp99(U1BrUknz*R%4|4LTK?BZ8@r zr+|lF6&xJ_z8)tJmyDU7Q+TmnfJ$x7O&mG}$=kDWr`jx0zd;@#gvcYIUi z^MTINxzITV-M-azv}|iV3CLu97de_Mrf=;uimQa4{RWg=FjZi*Szn-Pb+f4H1jmak zNbXSQDtTy{3jA+XFSsse6z<>zc(#f>q zugL)c_Ye^`Nv)5x^f}~g=QjqDdde)1{Q1d_W(^w?-4I`r+QRP(_5IW#q_f_Hjc60e8w-10`` zXRQB0Dqh>69%QePZ%djk$KvdM8vS4dS(KF{tv3EmP|9cYk*Sr#_+R?5H7z^tY8{?B zU+VCxGhraZr{#BJ%XrZzLhifn%k*Rc0!X_8iPm)U!lyRiZ~DJUh6Ql#s1~j&SqL<` z%(2;D(;m*Y3R~QWel{eV{b@q4bWTW#308aSTVWa`y^{>|^HSmk_-!5Fdxi8=S#}1_ ze_DR78^M|msH-FU+_g<;++j!>2#gu(Lf@+fvwb2VZJ-W?Y!<;EVE@rhDJE2j4C8ZP zTII~-QbrZHjqY)(x_&1N;lA)%K^i(n0a?zVoav#yu#-F)TwcDe>Vl3wOD%A;4! z376wm#l|$;|2NOeq-(JqhOFRk?B6s213X_KmckG|)+fXU^Rqk?+0Edu+x!+Q*(I`$qxjk-zzyzwzx~O|O-!i(~%7anm#k z-#-=B;*$d4n_Y*y;~8${Ru_z@+vMpi=0aWjS20!!xl*w8u`9$Y73{Ptfg{OvaUQ9z z!*gSeePgMvqgc0@bfEUyd;mR|aBak2`Zv-wt4$KU5y`jg!KHr_(JpSJy_w{@$YFbu z8_2XunoEN$9Domnj8{Om6FblN+|0Aj!tG0!F6Jc9#fy#aAluGw1B+DP75ev7zyJ~M z#5oRkXzRfvNCq(-VbQYa#3!4h`GsB1*no#PB7TYn_cHEW}xfM66)q1~fWB3Q?>yTD@ zrHuWpHLVWV8hj^1c3-5eltLN~Zc_zF%+^eAQ(h0)ZrcL4wSTwG@qP+y={5P^lLMkr zkC35YGY2U7y-V)-=bnH2g%{p@^UY5_`Pk8Zi23A`PguQy@2+sa4~90TgK9jvW545d`vKO1Y$Ql~{P=NXR7)p!RSe@RpyexOS0DN| zmOAzuMh)~<;3g{-kjuD5+Z72-mX5+LlZ)Cso^GozD?Ds@?h^%|=C8m0y2ge4nzE`{ zhpwjHNh5b)E&NaZ4jEU?F~|hsxl|kNA8j{M-HfM-wO4O7lV$F^XWOY1Op;pFvap!G zzb1#BvRjjOBtbL$*UV`51cT>QhwBzDr#Ln*mM$VUyx;EII8(6^dILV0Z_57$Sls#~ z!{o=>>7J3nFJ+1!b_zp;EjTCE!;akT-g(bKq| z%`4r|*QQx>Mb?zv=xZoBA)PR{=&ALPEcZ!GhNV?}HY&r&=JRZu1c95HOv3T;Aivr==Fl z_dovekIe>7y>;rn_ud2C;J)!4WW#G$3Ip^5AIm$K7-g;v^h|q>pGKh@91ZgQGCa)^ z98!P#?YE`%tg$S-w~kUl+2*{{rb=PF9r*U(mRjJJ=Bs_*=!UL*U$xpw$TD^( zKW3Yv3LghZc2CR%36=#{^N-v#K&KzQ$I1(RhniC29jw@%u}(ZqU?*;j{=Gh&O0OrE z<^#+Z6(=tXdohcPBFhe#${SXu!u3eYT;@G}VytibiAsJcmsMN(N5Ti52Q6@~w7?MH zF`SJBg*Q5Ub<&8ifb&=X*I#*P;H|gbG7k_1UV6Eo0&ol51F%=Fc*Rf;5BU1@(@&cR zKz4A?Ib-S_a~CgPG$(lO*mFWGR=@quI|4l@@Rxu2moL8fVso3??cznnpib;&QbkVr zeseXO)o~f&B2!3(Z%_}SL;AYqiH%<&c?2Na1VR5wobdPuPGtggU$}SPc}E|Zbfs;o zb^4lqoog#3DxhM!`S>;BGH~~eG2nP&m~PpC@sTNp0bDV2?xad=%xxX!I`!wASijev zko(qNy1rqzZ@Yw7Ykg4C1YQkT`zI=M30DUPgwC!@m<(jy&ds7oX@`Azv%I-es-aOT zb$*;BPooJ-lElm|_qWGQOV@G`LpFu=}BG6c3=Xze7aRvqQ(kY>`?4k@0iT@ZprlQzxP z>&iaCQVVV+eh{+D7U+)KiFkeNPN_QlX42jg&O`8U<8KLcCGg+w_u{tct#IES`u1_p z_&)ZSje!q8`tZvyzO-0RBn=B_PRx1*pKO19>FdwF_{=cJ?#ZW~G6_InLIB*oX2L81 z+#94vA3wVPgh!4KIkot^-~CSD*B8D%?(6pDx@v}bMjdphl^<8&ab?eyZZvsu%G}o^ znYWQMA1T)lkr_7zYV?8%254e6j#T%90x@6_q4x%L%XJ%##Ap<$_(?GYiDNUg}ASQ#|@9 zk4<>I(FwLZJL#2&4$u6x z-3V@#6Irq^mozmR+p~IIblGYYCX0>Y2k5`GeQr2J&)jg@dnMthBl%Qjw}!$a`yMf} zIQEv8KR@@}F;fAkoi{&U%o{@ub~w&*mjtpCdctnsSaOJKv_JOvPY;7U4VQ-_B>VS zo#Q@B?bpe$++MnL{=)gT=vyE30p3ai++(NsZ)yWXQnW(}lS^@z4S^1_RuxpoD^?F4$V+j_eeZOsaL!2AX=s7O*Najz zVetB;Xp^WF4WE+NL`1+`MtRin2GuB6C7k56=>-qsW$ZHWf&W1Z+*k{=bhV^y<&ZJH z_bO2*u`rdDX|#HB_?4Gm0n?@eU!6OD`Ql{&?c^t#1K%Dnd|HwXXL|-D-$*hZj1Ee| za|ShV4u1OSXXX(0gkOB&MQI&3`qQ8OB#Dj8zxa#4uxez9NvVtk##4PL<|_4|404f0 z0IS>>9W`h(-VN9&0Lw(R)KeUS7MKaCC56!TFoy&8yaM=#fB1(_KK|t4haaV8wTTAO zC_G;E7U*c1lFvp$;g3G~hzuWj?C_IM{`qI0er98U4!F4QVg%bO zKMgWsSKs?()PM2C=YiciJ`~}vb3v2=>&S+JM*@r*r-O`T+rlOmpn#6K@|Ra$c<}`* zPD+&_RwG;PK^`dc?PN`6{8Y)&pqxfy{bo$pNKastLRbtNkDn9ndT%QL#_|UrexRyw zL}dag^>klgC1@GjlRx2eW97HNU0}jTY@LG)Fa`3pG_HkGh5Jn@1io>;l}oZr&YPUB8ux^0_(!Qaqe`+wD6T z^NtymIr{nY;MkE+r*|0j2KyIhzJS~)`?F#_q7Z*|{(MjbZj^1>aN(JNm_?5~0`%d- zfqe&T5*QtX-^$W2|Miz=&z=3Z|MuT{B+URZjyS_@|NgI`nJodeF(Nu})XXpxXehG& zl@hAokxe8#QBFaI!I@tXK#ls;8UWyQwaH>39Q<~EllL1 zVEpnYL!D^{y5iG*9%TNbRGqF=dsM3XECfZ?;bB_{o1Zyq3K2J)B!!_Ppk%V|!f!N8 zm!u=Yr+he1PEa|y`%NH8ijZX`X;K~hmbvP=bAyEIDTReBBS-rXQs#)x0{ZHuSsyw4 z=wnAHgU22_a`!U|CxOUd)aStETBK|GiT%d%eF!fKjYu=bk{_0H$gKRgRMKgrF zb}kv)ShNh7)eXYi-cYWHi!Fa=SxDO*UxU za^qGaz(U44wVhj0PdxdASBgG<=5bSj0LRsDv*A}y7b64Nkk`AW&zwC=DjD?zTP&l; z7s@ba8!;If{!Ak3#I1S2AO7%%Rx@+}diw_%VKp_E#9V}gUwJ6cEpuI%l|rK5!%!4j z&IrvIsbTBdPK>noumaHBe|SADRpxUoCJg&=hP1AvaTPaTRXgq2;M)s3-HLS;rQ2XU zr}ZgeS7@bgL_u~>heLe056-ORojLO*QGok4YD>Rn5fHH?5<)N;DJtISu2u%s^vb3u zs)MEr8X>M7)%{e3nnF6NubXhWf<`RQ8i%Qv zd#c86$zD|SgClB}ZE?3oxU3Dkq1<0Me_msLAg}tT(T*JPAf@-MTRfr|jyOtTv{*i} zU`iQqnbrAMN)ZQj!JmA~Iz=ex->Be5Gp(y}RcBP_&Y*vnZ@muKX!NbM*(mZ@vlH0E>^>n=8`+%~J^b5<^v~8cn^fJZ1Q#cCvJJpV8}g zzI0&17wvElD*&p}Gj&=)n{paR#a7$~5CpI`hKTTX8}!li+LUt6GDYv(xauJbO+ZR# ze|h#x)4h+K0Ra4;eWo)SfYA7!kX0em16`{yfJ_Gq#~;|)&mUMXL`DttWQE5ydC#s1 z+>Io9ERm%f6nn{mf0L`7pG_fdl4PUqtFhbNcP`t-2vd~A;HtSO()@L3sg`cot9uT3 zmNCVyKv&h|Mx|66Yzw(`(ZbmD30Bt77oUGYa`h%YTC@7%h_R7L!jX7-`AB#xX4#v! z?cYvw$uq8=p7FvTh)gW41rbPoZ~d&@+C1*lW5HMf z*{}j)2tnowC3u`Gg%xhEest9$(&0xAKlFpBhtsD|pE`94Z^uJ_@@GG>Y6MI;^ACpA zg|DTa))J&OSOO!q7~FVj$q z46NT&+Zh_P7ng?X^?e)0IjPBJ&r%Q2*PR#wBPj0)j(6U9=k)1!KKS56Yx+7^reYgl$sKX_$kiXUC@=B8t5m9!D)lngN4qCghNemQ zOg}=FfgL+tE5b#QVm9P;w=R|gKc+e?X3ga*4hM+!Tw1CePBZrYVlD;gGkc8V> zsR?W-g4H>#b+By}4V40=xk^DqfbAccS?7R+$--r)@jO6(RC>mJ>_h5uE&CW84LF1lTZWA!4Ht|ReoQ21;Jbw_@4xqc z9w+|x8%$n%?KKPm(Jy}S3)22)Klw99kEFFDX({x=!2_036$Xc)S2A#F09C;P5gDL; zQ{Z4wjazB??^Ok0{auSYZphH~*In4X7;q}5hT01dIFGI;v`lw~aPj=0T+?&b>LH7b z+*5x#^~X2<=u9sEnX_lKS6TKCQ4wf#m44!S)~4w(am5eFW80iHtAw#->&SmMx*Wxh z0_ZCK4(!4V|LCC(FRmNN-ees(;c;+zLNsZosOsJJZ%Q(vi;ep!R{#J&07*naRA8cS z>94dxdH^UkPy0KNzeGw+)5i2dseq^fZoxRsTPu_)S6CE5cauB06(_Qo1~DK)uW|gC zCAj5dF^R6Ua?5r?O`_Kx4$R^2LQzk{osQS3`QoM5VCcjSN^om>S`@i_*sZ%FUz4g|gKK|q*$BI~+SY%zORuwGO0ex2s{Q$;A zs%}2{@@Uo3%|aTF8fZ{*T|DI}|D>@xTog|ulJYd$iFpvQAjAD;7Yx#=GS`oKdeZ9~ zVw&iwvx8-YYg3muvd^(aH_Jz7f`CW)EBk84ZpjEzZ1avKcKnuWU~}Y;oTVw^>NiD; zC)YR%r9FK`mdXjA%`tpaTP|-i-D0p)M58s=fAN3f|(Y$;SEB*LQo%u?M>phN^jHb0CrA{TP6b~->F-gT{7 z7-w?AGJEseV}$i!7m6AX!i0C;!7|SCKX{&?*|nt!deDgl0tCJV*uZa$BHQ>7OJAlW z_0D=)d`3eny!z^^Xe@ZJb(p+A{_&4(HXJ#8#Fiwo9&jm!|K$Ss5KkF?bM!mr`%y1)Q{u0`y&pE`Alj`gZPi~YKa zlEA7CA?5-j%0c}+$X7_6EHs=R?YwaA_C+bJ+XAa>(Nw>tU=)oj!&aXGru-|d&;=Im z-{i05Ed4oTbVp-zA&0FG6ay2kbq^L~!EIvOQv^z`nh6h}=kbvCBqQ%2n%1mTuwFP9 zXjkR1Yw zYC`f9X78S6mAOd0KA{U%AYYHmDyNbz@nuu*g-1Jd4a#nZXNr@2!9gtRwdV6d`cGx+Tq5ZeS-70d zDav32rn4Su?n)}dt&((6`%c43txSnY5lloTZBgBvd5K8Zc&dxhwR-SKLG2{xH5)XK zr6LD4NP?rfHum3p|2@h;XMuim0#dV4ylH!Lvx*`NKHiR?2^KQq>ujfZ8Cj6KPXIF6@>B7zLc8kRlE zgxq@yz~gh4fV31>t(KL9pg@6z>Oo%D%imr$dVwA2XZ1h(%rn~2HX_jf)vtc_o8SBf zA5$tQWL3vUosD_$pJPC^?1Qs2C02;KV*)efD%&jDP!horGlNI7o*}e3R|eq}_(2Xe+lNeBkc27T7!< zYXpuCj_fV#)AAsYr17`tA^;BGT>8cqE;;w47`vW;fqU@XqHd_Um5@v#mYkDN2y8*$ zqsbu@E1b0!bOMp>$=Cnz2g3Z5pZvrW_St8TF{5NbyC^}I%_y#dOh`Y9G(>k<{(DRT zsJWN_0B(b`s*xA5a@tNK3emWGzex|Nt#Bs4r|Ha>JYD_EfBjec>3{muTMl%3{zwAt z?N`6xZR~u$tC(Hr_s%CC;m=>iKBNlD1R1s(7?Qq%0Th&*>)4 zN~nK4!X?p1a%2j5HqNbSY#0@*5SKF~+B2B&baL0EuOrWXCF$hhQ0UBLvcB4ZomVof zDVj{9c2d~9vXL7ZnL$$tY%60GPkTMqZF8XL`R8Z?5##@K;aVgoQd>n_K~k}LZ4S-UCc{T z**K6|AOcJzj0ng&`P$H!#$<|6PIi7%wLB_3c}r}jNBL2vSkYJ(V@(k*+NyKCaNY~N zzVPtJ$x|nFbj=5{OvI9KLJs=(0P!kY?aR;w~cT${7x%R=5`3cw7A{v5+hl`;;? z9#kuSW7u9FtN*2!wA8-e{N~rc``z!~_`@4{@6x%mD4;@)3R<#{)nCU3bFL@oLk~Dq z4)+}zsmFn94}kffCru~KEEi6`QyJ3v$#+?#rN0~f-nyL^^(^EJ@Ld*w{(CjhfEg}cUQ=5hM_0A z3|;KXWEzD0N|em?7koC0vHC?Udd3|^zUR=MhYexHcpNt~gNxqF?ZjR7)@Yx4+sCJG zyzvK;VlB}~d)8v&GkM;}48}+%9BGTrB^9fzH@8v<9=PvY3zW?D&L0URS9E~ag}w61 z%Rl(R3xtQXmkifZYOQ}#2_%r%cTu;DASVRtm&Dn^`0#P@<8CT#9yDs$$FE=FH3)< z6eaYsjIvI#qNwe`<<|SOQnqZ?WDdvWFhmPjZ{@Z_g+krO|3yAmp zbI+M6n_`RVv{LcAHvbVQWVm}t0jS2uAAh9i%bEx@IAHuYPyDX-E1%Y3^9b=wFWo)I-# z`A&J(6lo=DYnfLF8c#=H+GBPC#gg*S_*YJ34S@n+Jyu98Hi41PxC{*O2PVXnv?W!` zGkHv$IsV+n#UmtnmD7(Va)q}k42n8%ZA~ZC`rU1pQC{(cD*rQ&$bA*2Re@qP)9e|< z$wewN!lO?-k#bzR?9`ptyj-~WwS(UF4BmeGZPtTHBQ;=VK!Tn<_8dEcilQV4n-S}| zgyd3Bfx1?n$uzZ|-Bh;u_QCHRXn{p{$bF?pBHfz_mL*JIZVZKGOu9LzEyOymwtwHD z{f9)e;vG6Coc*?Eq9Qwj+Omy+;PI2dQ4D@Y2Jy74N(Y$!N_ET=1ADHXe&-!>NQIm@ zae`ff%$(I)5K2s%8-`u1;pyAH-b)I=`j4t=2&1c8$3WJ0)(5ClgOKwW*B}1y2MQpM z&t+nj?o@M)YP&!ifqu5gU z_SS-}iCU@Y*0k$F3f>ywmfK~e88Q+?SsEG~(%>~hIz;F4=wD8!3^ITo6rPKsv~J5>p8r#?tJ-U)O&L9kt`GW z$g5nvxvp1;2p;|)e)xe8(osa-Q1sk$&pT~q1KIgJj~~+ncq*HHCUv9_TDp|8tGQGI zqoxl$-+c?L?E1zqShyH3`**qH*%yHs0sH-H>p2FP5FFa! zW~c<*YtU0oUAC=#YS#d6%L1va@xg&i(gB7pIb_*UJy}Z1D*}%ioYrbdnrF_lrQ{2L zbxOD^7yDTA5dZu~XC;5*%@+$v+pIbQ|b0XkSWtbg&kBc6Yv{XjqMs*c2#=1+y&p9Gz)$Bhp$ILPfO#4!8cJzYun-oOM8{CaKHp zw)(FrnOl>r51DJUo@!m}&2w1C{X2}C&3`?yNeL{9uoAU_q@k%(51W9#@Im|7`f`4> zEt|4)>Z6P~&79-oS0B3i>1UskK!Wqa4_>f>t(VC&5oKGzdK|bVOu;TvMBXGhdGe%r zfO+S!XP+g&@4owP);zwcXOFFEKk)F<-05JZuc6=qzRxrDcJ$LN*@#8PO^b+&ma7~BlkUHiVf4MY7 zeSHw;Ic)kBBG%2;s1?eU+v}xYN&bDl-DIiFL$3pK*65o$Hd>t$^xyl91*L;~^5W07 zsmXc_9+zwhUt%H<8=$XAvz}A$=dfqX6c9p?sDV}qnS)DfcOYwqB_?w>U~b2m5M=K- zGe+{lnbF5m+fy>x*~uLr#tP0n%oNX^y!-CyU;p~oM!5IGuvdQYLgt`^lQJNAsoIP@ zYs6+mwLs2Ad+>X2v_RBAfeGl>-6jo@#&7pIyvbVkPKOs?deQ1M1D9YC9xz6h_z8Jr ztema~fXAMDj+7GY-@X2O-z6c*7Js~??BvN)&IMh#aGuTKP3bk)jj9-uGg@k(KhM3P z0Mx-)(qx9VDtSx4LG#@6&$F=1cf3E(3!Hn%vBE@oPP%6Xt`=my{y|!`OzTK8`HPAG< z-l;Mr?Eab+a7I!0Q%CFR(gf~!`hd2MW+(|!)Tn`e=BwTrM*z6i5NgLnz>W!Ta)3UT z!=-$e#$r)kHR`iv_K<4gjCW;Ce!|%Bb3O@&W`a?chxYIF^s#6=O5QGRgkorch<2d~ z2&XxLPYHST2%)8c%+_fF51JYF_L2>91;&sbQ*5;u4L-Gb$!8SCTxEH!aagU1U5VPt zb9D4IDYvFwNphQR*XO&TR4aQbduZ8}@>FxqhDOD0CEX--n;F>xuAW)py3H*Er9<&= z3ql`}#x@tcwCco(6VRL_2yfsfy`;3SU0+d52e8(b%^QDw!^t$_fA>A7YYkmZ=CCcS zH?*ik>q4x4t7Im@3ca;ZORdNO@cvrZ+Ti!y3C`FqoG#9H{H&2 z3?&O;W3zJX06p11cTK~zUg;`KR!Z~zpn<8@kUf2GQaRPvWzi+>x!2U z*v)$CXV7x6nq1|Lr?q>W+%RjN-{uq2wH{?cAg@^S?ed$f@oK$d%bIC+_Ut)V1whQ! zbN=|LC!e%$>~%4QG{wNaGog{nIJYC+B}{|VrZuL*r!21ZvwO;vb(DN&tMIdT)9+Tw zts$Ed+_szb67ci8d*Km9#`|jnqXHK)Zj{Vtq|mo16WTEgbC1)_$?( zf=J5XhhQTkAyzDKaD3p$KmIXEAdM|KEeUS9WqpN1LQDTY|HUtC36Ln7181v1@1A~_ zz#M(@C`Iz=r=Ps{-urX_f~_5jp}>1%2Jo|={Y-^b+oyO9VsF6kycUbfp0M$0Fmozw zW_{ws3G1zN06mFbL$B$)Aqrbt=T3?BDAN7pA(2yc=HF2}?X2-P2u4JCGKa`8SrBZG zOLMZBe=|(N#VV?qf$;`p4aG=IoffTUjcJ67tpJQP9yH`fT4R%dG31F~ol4ESuS>3M zMA>1AONoqjTT^o>8c-<(%&>#?VlH2{AMljPg+n0b7v4?s{`>D)AhI%P9#A^qdBZpp zk|&PlT_fKfR0{>hLh|MKANf4+-1!#JNp#D4u`CpdUg^gqhq|flA4`g!P&|M3{2QOV zVbPKVIzd1Pjf=);btH020T63N@ueUBkXXWZ=N`St1o{yq)7bak|G=9>z4+F%tt?

Z|l;mR9T8U@d#S z^3lzd9xY3+llSc!R@*l28v1)HYNa)|I2EUmR)%*jr0j9sHq4aG zMG3+$qPFh$X05aA80orMT-%dJApOf%9b@t*Fk)J_#Z!lp1oFVcSQkr}ji#ce(Dh~>$^-Ee_q_Sfu0 z%-(DU7+S_#zB_;T2#NA&2+Q4;uoWOL54Ssj7!YSf<;g)G8aql3B{YhfRVklVSFs)Z z3D3}-Ph`7Q0w92unZe;l4||WS)qgzY$zy6^tTN^73Uai9fgLq7K)*mFtrP$W|7DB0 zUAkq|wtmFhhVK;E%t!3?CLalJm1lhcmXIyS`KZsfnWr6AP<~sw4ousxxb$}12wuId zBq^{ppLr)BiRvvfAih_J2%+vK1Aq>=gEApxtU0^3u#Jj0f)gK-`NbDs#vs0Og;+}K zQ5C{NXCOAg=t)2)Y;of6+j~F(K>t7f@sDTEoY5k->@*Og$voUZ@{ZMi^Kbt0_=)47 zpS6|_s9BY9ma`g*1=CUC(MdcAx^cTL`q~6dzYJ;0W%3pOARe9-g744mXubAaqv9I_ z=Ng2GUQfJ?&2#wK`ilS(6S;yODNzyC&f+ zkCJbiY<8PEcl7e`SZ-PVrEysLuB(3&9|YUSucnB(KXPeqY54lTc^)uYATrS;q@m@Z zo(m)l>Bze19azr^VBK2Z_Fxl>M31X=$fl>)gId}k{OVPhM2Au0_S4TlXFZS{ASsZ* zQYEf9WWBRifjY--!W79vmv{LLfcb9&fI+0_YE_!;oH>qnbXx6s_0`v`2x+Mdi~{tG zY6IE9j0O6On#E?*?-&wkiE<;lZwG$g$Tnnaa4vgq!ex;~}rEZ8t%WMt*KIXg9>RS7gHby7X*I zyc>d@65ey+yJ@}MR%)9OwR6(-#LL)>>|KuO$__XeeMW2j35}rCB$}A9^gVi(Z-)6& z&_g+zZNyF+05v8Fpb?vQ3m(k^5kMzzEpqIV63Pb(!Z;06@@ra@XNTmyPh@88llxgJyWP z(tI}|3a~80`p)exTog=GY-)`WHSyUoeh|){rHoBs)#BBIqmhSaPL^MRTnmkBpx?y{ zqvly18eK_Lcr4H4Trj|5tvn??nBhQDvSy}I(d zxh|7$Ww_+9Lb;5(|NPh6jO$upn-R4$`Fi4IY)1C>V}2`llmSgY>BYVTG581-`bbx8 z9UF%Y(8P~csA0(x&#@w-3#FmzowBVkdIgE!%g1|EZ(y>b0_*~X;6QC(C%Xvoe#70n zN%Kg-;YXi*A|JJX^pV5t8^hTnFAO6?!$zM&afY{iQ{tL-=$BNa|83R*qsvfs3k=WJ z1{ooPW_U)_ckEGsWf^qNngZMn3ZP5d&7OWK=+Bg`&Ijl0fdj|Byj^d-)rrE@X!ItD zqUmh~F+6&ukq9*t0)sg}*SW+xIe3t;dS9XEO}r9~xDp%T<^f#u$Fu3hcI(}Z3P7z? z+fImT{PZ7wsy>Pf=vBs1CkN)9eePLm$(S@&h+>%(~9C3A9k~!ITU94mgMUW#zlY!{ucsa65kPrA~ky%0QN!Xq~Vp?$I zu+BPmxL#CGNsJ|)CS5oL)XeJgJR@<&DOat;%jod83H94g~8|U zR{&jE%^pu*ax=Y{L4Oev0YJHF+=6LWuG5OrxlCb}1Q{bvI=7*GQ&U7#rbSDl>6x;R zKKk(NnRBL(o)E*$x{%3%gv+$e;jU8v+JcQ^t4&$ofA9UD{nO8!jv%or@2wBeUzJS7 zYY1kgwFcE*`*<}x(-2%YU*u($^%0W3xFPUba%=y%E}6IgzQ0~q8;*urw&6w-6<#-L zZv>VvFuwT@I!~0#RI|Tfe`L4IZM})%tXftJiD~wd3vyywM8+8>(joY;2TMQ*5&ZDVg!;7WO_ zOLokv+0W|t6oJqiMe*2qKr%u_5^L8W8c5&ohB~@6_xvs;~Q^8XwEdAXlJqCntbkTMC zn&%3Ktr=Y>*L#n=rS`io_cW4r%6JCx#Ovhi>gT54_PWfjcuqsT7mXSzYWQbV)ohiZ z4y(;HpBk`7XW&76Y9LdFBS#qaY_sXq+z?Ee~Br?jO2Sn%n+ahCO(G`3$G)j zXWx6*lV$l(l(A#9(@`{&*lslZB@af;IS$XVe!KDCaKha%--$-1%Y0X{c5|}y#?-DI z(fv9RZAv?L=%&EU+?IDcNFeYI)reKp9wV+<^=aLn&3{smj~&6{X4{BAt^)Z@2Bgzp z`Wn3npEt)5D&}vNdgc$KBG#=<*^MKKes=hW3|K0(Z0^u9Q6ztTFjNxCL#m1Xt~v#z zh)ftIQ$BOsfBBbxv3ljv7`DGBBM7}HHRO1>u0}lmEnK&32ef|~BHZoP*d^ry|N0gn zbL$CiQ|uUE+w)%*YSWP4p8RG*m)R`_KiK8^1QFXm7JGeT&d@cs4Mmd!;%CR#(IEz! zShq&J!7CxIlQ>6%sc|aZBCEhHx4j~2GGS@p(R!G`?lk3LZ1?7w)uEvYgrj%mMwOe7 ze&iW}yY$>Zl3j9ML)HZz1ncqojw^s3Ag=Lvcr43&G5kBkl_aPOa39dmQ{kdYNkng? zcZh_02SY#m{BvKP0EQhR5C=56wYhZ@rLXtxWB$_tgn_zoC`b}E0XXd(U4?}C-J<|3 z`YUmDuw}Au-+1GV-~8q`{c3d;=vlKua}ngyU@8z#4T{Wp>q#GhEhXI??e8n%WfTI< zw01@{6MKd*s-PrI|PZE=BNg%dip4Y&X>P z4zU!K)qNYwb#@5OXYj|aiB#hpH4tSmb*3a-4+fgX`hdsE7b+=1<$Lmmvgqq3cRrSHYGo(EjU6zAbUf<+f3`HE@is?Q@y&F|m61Ho;W0 zjr2^iwBgy@){)gVP}ubQ_8sypK^^g=4{D&s9O>0ws7<8p2OK(N#*mLnHmB)jNV&;O zA0AlH>3a3Zcdf{*|I#rM8tT)7%Fd704uNA7&Xc&q*z+t|uJf&9(uu_4kv4&@t zT|DTw?~&bwrV6`p%Lea3+`YWzI<@*F1lx4e9FvMy?Sl^M-O#?>-J5HsZxSFXoXdrA z19a{2iy*z+jP-5r=L+%b8Ws)ugQjnV>)+^rPd@(O{STcFb{s}uH@D6u9a^els?eUj z_Lpwj&Has{q>cFaU zZKqj#Zrj&pvT}9XZtU|U7nYZQHs0>3Nq~xU;qUDAPBq$Hp#U^}BhFIa)Nl%jEdRQ* zYO5f&e4B??_YmMR`5FDw@gZZ|^xV_JY#9TaqY}7$-pnp>9qEyqW^VzY3f!#-gAqXo zK#E7K2RWW(uOZKrqWw{t`NMQo0ICEdQ3L113zE_xTrzmJG^Y-Q9e)RAEv89o|yfV`R4e z)6`-c&_yjFzWo(Atr0cZa(zhI0mFrek%TkG5;0Q79rJ-yZAAZ#NAi{6C#&9L$Mywq?>q5jI76?JWVk|jMzi$@j zWZeB?Dq*oW-%74^r)(D=&bwtWp$XnP_15Wk@(GPT9&QSqGWl?MDF7VWh5rt28MXB9 zKz{Au-7r}}-bVDDS#K)*O4?<+bt9$#n^J7Yc%>gVkwgLb*IZgkHKTR^D1iRSao@OU zC!mi1;}No2>hVZe`v>lKYzGny9suZS9^mM&hXcGzoK!SHU)@Tz1VeDIufP2AjMb8( zk3H^CaleyXF39e%0#FE(Q{ujq04mE4FgZ6W6pd8rfG6{?WHraA zhjdmlXv?XLr>KI12XSUe8A_m^)n$~~B>3`+FM2q552iJr)hvqn*1(;J zx20SORMeG_t+#2m1xV*+U80S+w(*m6%nBCVn`a8Jkzx_O@CIZ|6c)X?h}28bqWPO$ zU_A>wh5xR@l{caDBK{l`T78k9QVBe8Ux=%azUhT$Z;7TW715Fm)d;7fUq+PgOlI?=iZb{bXNZ^)?x{=3q8Fg#@*XXik zp|tC+n)cQfa64|xQl(HCm-!2x#ByoNCNf>m6WFqY)CQ_eU13J4rTjJkS@i-rg90iv zC=B5rc{d!0IcgH^i~d!di{(5*+3@0;?Oaopi_{p8qn?F z+@7r=>%&))Z@t}#;gYf&ZG?0qj@vDsjWSD^SeK2k4f2jn0k(_Xpc}r;y*UTN>?i=Y z+MLt)Ts~+t`#Yp{LZf^UPQ-m8plQVN=y8sp^OLBBCIH_a{y2N?oWt>6g-Hf5LOFN} z(ep`Lb4m*;Ko#ygDBxs`aJ#{oXx_n1V6#z9LFY!yuL;Y^lP7&jkKM0VGAL4$x|Ryi zQDX)m%8j0jXLd_&=~w+eOYll34>akd%Y@&Vf0S)~j=LmV&*Zvd*XP(Rw(a%0a=A9~ zmT|l6ME>PpxHbJX2lF)vu3fV0p%|rFcv^ZlL|4&El7$?6=%9__{G$!(9}sT@Fcu7K zV*;SVao6st?u)5Ru|(4pBT9JEWm#yNYS1E?YCLqnR-vbaScr^DYXxxPlJBa{Y6%nh zx|MoVxY>@|;n^vC5DX8>6*r=WXW6sy#{kdTe*dXoFjqzEFKhlAu3Nbppd=(QQ&@W! zViRvQ6oTEjSpjtM<`k0xfWIB0(He_+V(Yg?sL6n7mZ&B`+a|f`5#fYAeNV&?TUM!$-Cek23V;T) zAEm6ROgSDkH!by@FPbm%TF!XztNUH~r`yQG1p4|^BTjf zbq9YC@Y4`8lc`O^xZLQDs7@lx|S za>-fVg&fP+4Z3Z8mFZxfQ&|bwqyRKjf|%p!#yB+jsgRl%%n_vt;B{ilUtphwAf7V& zC*4tvKh>DxQvhwKn?8L117`}HmZZ+%C!@YO@N;PLdfKad=m5(tpMQ~;M-c$011zJM z5ZoCBpp=&W6?CQV&z(DK`eKU9WgSik(05zC7UMr+G$VifTO?^EM5`_Pw7YJE z=z!t>jy)^o{ElAZg37! zh_AzK28oE=76s5<(|9tj!DVxn=!$89Xm}>L?{%K~O0ba0m`}3l$>A^<&L&OC0JMDP z?RNmWhXZJIhP5o3Qq=}3_Hv&NMURF_;k{B~?|=eOg{OAh zl*^EnGAyI`S7O)Su1~urb|q@3e7oZ|jqobaN{6m1X81Y_!yXr>TZwH@@(!bkMjRo~?BhwH)|c2}a1uXx$9)V6+Ik#aa~jCl_CbC4lk@ zukBhsL>tMb)N6lYaTW-*H6D?>mI7El0F1ZR;~>0#;bEsQ6+jv8S@@|bz;uZ=nAF1* zV&M)Y4X*T`eg2uXAP2o(eDOtx1+WG2TiFs47)t@zQj(*IpMwQ=KmjPh)}?>dU~&BR zx4%{MGWlcaw`<6Yxl;N-3(^HGMxv;McH4I3N?^ob;a2csr*X1KacjIvHQe=bRLC_B zH=Ge%FWas3_-qZlVi<@nWy^--ssg||2&Ka~0 zvZsG@*k^CHi-Y-jaLnjnU|IseEo(dfKYMrDt=DnfhaMmRlHfdmgE)wj25PpYWyMOe z;y77JlywuwuX4XmK2BEN=A{+KH}n&=Q-!# z05~8)3Vne6R99D5S65e854-o?M~~Vv(lw%|nsu&9Swayg6#A5V0_I(^$e!({3jB!o zPCxtXv(^NL0Z~0vNG|y_M{#JD!BiN)vZ#@?OX`vy<1NvtDl44Q7XJ{Jr>|T+%1)%k zrfD1F)cDGeulQXcRHm4g2*8&I>aLfc3YF03eWuAfjAm*Sl6L*AQrC`BvLq1~LquHu z*)#F?H4az?AQPd+Y>L$FU-M==Jnrs#PrlS%2XyJKFfETUz?}mdN5%lHY_R{N2LM$u zp&-**@Ri5seb@MvS2%eUQ!54y5KxxypC&b;eQF|Uy$v+qMsDII=QYflNp;f=dO8~L zWd+uxx}dTsFv{@^O!exP8L}I=S&25Lqq`S3YCV7}b!fYCc%%QEwx(F~IWyorMZe5~I6|w#JYD z_hdLq+|t&5KCz&-ynokrtZbJzAq@UD?E~u~yE|Cmr$~{;E$zr9yijxxlkomvh+A>{9LM2lEZWJt1%# zO!kxkQMdmN!@ypdTp%4i55v2_touVgv_xZb4+!TpWZfGxZO`2QhI~2Ij?L-!&GSyP zh+ZOy2t0-M#VXcgB7dlqUt7~Lt9s=?4>ZOS4DBfSGTD2#yk`Iq-g@IrQ%GAwPe1ka zqmMoE#V)B=$0qCcDXvEu(fXl9&)=Jr`l(oeDkOvQXSIvuHw$h2*#h{yDw5hge zcwqFsi8Ov3I!y*wZhRtcNig?CX^(q7`Q(#2^Z)yw|Fe6-PRG}BE%}gX8fM_7YLcFR z{&vSK?ZoKxpXdN!2g=B^(#5PuH={k4VhLmFLEd)Y-eIu~g9&vEp3 ztaN)bySNgcz|B%QlcsKhrL@Et)d7euQ}-PnWlaT*?tOWS-BOS4`CDA?mh{UK85Q5c zeQEXe1TVF;W_t2|;FbeySPwdLuD;fSf0VQ{$C%41ITN++Bb9Mfe4e?b9i~uIxt`B) z_>i>>m=GBSYD)-yFd>9a?OQ?K{`u3N{N$c{?*6y`=HGfijC{pEPyAA^*0<;5xq$~B zc+jI^bwl`_Pi_EUa;E>A&8!-EQ&qhSh>dn-00QH4jl-%t0`h9(c4vJJrd-?3?eIBC zp{1C@$7c+p@1K6(VCl}!o3Fp=E#dieS_R&pk~Mj{cYOd_oUA z7eV{|3+Dxz?aNXf+?L+6a@@ba}X@ko@27HPQNo(Di`#LG{ z6Bme*H_MDj)fVwKmELV})Cte)u6JsPwXhH$(>5_+8#On`<>SMLVR6YXpHg zn*;vb`k(%fKlK^W-~avJ=k9Svsh!hX4`)lzQ)BKG+iW+ATM>S80|1{z{es2wF}bB{=@R-2W798&~ZzdPpH!z05PvSos8mR0v_zWCsSRs%lU$4=Nx4(`pz_AZ}V_JwmC%?@oUX#uZJk*3ACepW2uE<~P5Ipg99H zy6(&vEZ6S7`>xktd)-UrKA{1Cp_nfW+Z}SVK)e4&EmC%B`wlQ*c@-VG;I&u&ZTOTe zG)Ve((2UI^82c0yy-%E0Y*L?@ZMA)F3USSr^RK>o(*3vRo_o%O%TwK!0W_u)KKS5+ zx83G*Y4?8NzWeUE`|fEao@spkwD(|GtRpjf!yJHn`hRU1xtb9OmibzWr?6ZD@E3v- zSykCkSm99)6Kd)ACY0hUzN^MW#I_AK^5I?9eL);Myef#@FmSk zQw{Z|#R7ZH*_DwDXPtEQ_D`i_5EIQ7f&~+xq{?KLF(?!@#z7Jnb9(H!BUYF)qG7`1Yy$Vw>E{k7q9rV0=0#{>^Pcm~w z8V`0ZtFvT8;keq^?1`VvtVgTSHxcJRF z6NqQAm#s3U3HoKbe=wkTEdblL4BezhZ==SgPCk=xn)d9uesnvozk&@Pol|$Rtoz#6 zzNU9^{deDeH{WXA*BayN^+05Hb?98wRWxh(EadB;^1KF$CCw(LX^gY=pardGu3pdK z<;8(Ed|SyV2!_8!!l_d>qu#Qc@Wvajzwzdq7I3e=^6II#vrsTZA=8kI?ART5+;;oz zERCFZW`w%KAkn+S%v7_s$$pj^iM>t!@UtX!%c`}jp>ud#D>ECfm+{uGnF_DL zaUy3XG&O7h&YbKgJjwG0JKGS?(G~Rz}t{7fV$4XaAhMyZat$y~b}RI_T=~8C-t+k5U;FmAzij|;!_8I?4@(E;YdWn--0AmE+bI&{ZDmpw1#-2e z#nSLRV79T!UUoIxR-G)uF!(aRv`Ebz0CsfrZQOiqiZ4I-a8~r_Sf}1@42buC@g=*8 zf?>e$;PZ7azVwp3JPwOA4BU0>uGw`Iv!c~77k{c)CJ{89l#>-B0yEogfy27j}_+MDLT7%1nvFdGc5FG?EE}% zJNLdNT*Cmc{>9;EB(OYhMDlKIL(SDW$T?_;mtKDP$3OnDuJ`C8j~eDJ)D+4{w7WL4 zaiH5?_5pS_JA42DKmbWZK~#WyGPBrhX`-Au^`;rFig6GK_^F$U zD*Cz$+w&5PywnRuuhGGW4&=BUmtjcOcG>UCNbsJ;9y4h3&`D9o2gO>99$^-n%~~J< z2djx#(MKO1d|x}B^^!T(<4&WdcT4G&mtVR6f&1^f@BS}*;a<}h4rmMm#h`RsP`6za zUC>SqHn&9QuFZg%G!4~q!OTc>BTH^bjIbaGZnW$ge$darWE*qzyuY-^XE&Lil4$jM z0Ayq6H#dBHWGJT(pHMTcq*SWkU|L|I zKIA-ydDvwi0Jfd}tFM{s8*jYHGDya@g!cZk{F!^E#cB+gkkx`yD|#TJ6epL|)>0F1 zQOA`XX|cQc!Q|&~9rba#?k9i!ljn{KluHxe)VxMZ%$D?rcxbmYYxrV zUw>;p`01B6aBFp4Xw?ZYBms<`53fYGhM658-r=2h-AH`z{dZ5jbIL$r?8!h-FEBPF zc%IP+{{3x!ZHe9{#loq|AC- zZzgbc$2WZJDSi0iN7?W@fAfu3UVTN=JB0q|V~;*?|NZRQrcic-s#%ndc^^Fj zK*k;oy$fGG%YL5UFl-jNxiy9DZ!aTV&>)?BG<>)9oPGV5NtBi4m(=UYWnet~Jf7gz zP2fs_twTf|y00av{sE?wp*79V4p433W(|O(h6mnXnAxuvkAt>wGl1s*?&m*uLPU4d z#W-8_O((tXdKhhec?W==qHd?kgRh;;`^O2+1Q7@%7DRK+XX+}+USl6&EE>1udSx(O ztC7?evGjE{T-kAAchHc82V04AuU18W^S6KFaMaho{`G(PFaPE5{od~#J9eizy}3C8 zy~+X|vBuMcCe(UPYwr>@(Ak$v*kyN1*WPhbE+jDs1F(H(B&l9;xc;9{Zxuqdk6Bq`J zC2klRNbphCg*Ib4Z|x~f-H%OB1t3Tam_lfFZ%dhlsL)l(kxo1iiB{`=N}mZXjisKb_ie zV8*Hi?K#|b+a2$}pI6fV!{7hk7hX8=$Rkb)eaXpDJBl7i=M)f52S_@u+xu6ALVVX4U4|67F_3zqclLq;o$QfAK= zYa{OOcniH)U(+SkQRm`1Zn)vb%R2za>>Xj+qMgW^B@!#{Za*5i+V^@l(Fp-Hrf z5-q&{-n(|$-Kyh=wW9vn^ZnM(WvY_4Xd&@eP{RP-l`)AD=9Y7}u(oMDsC&+4iqvFGJp1oGwxkV8Nfx}4w{&GunI3N(E4Bf%2&Sh<*$6<-g|uRl2Nr?O|{fc39CA1*qKN2 zVA}x(RnB2fTxFP{p8Fe;E|VLGAFm0y0^1~96Y8{Rej8uLLUl8llnI7x{4DZbf5V~} z&ODP*+jV{boog?uw&CM-5oXqDSq*fsP66C>!)1IKoUMNYz@~i@8vfV6`ZYV~7Df}f zN;1YL>ds&`0m=s8VYFi8R<|^GBCaEJh5Opbfoc2b=cc3 z; zwOQ14AomgIw&_;E7QjTh+c@(7t6%-fMA$U>Km3RP=U@Dbf8jR0qcy(vU@~pVc9%mm z$ByZWbd~y5XnC8}Kz7n4UYlFL22eX147h%GCWPB=HyYe3ZX{6I zY%J*Xr%qWVAm&*f`r<=h{_>X}diWvRk~SOqIcF13Mc7AbV@<>HHG2$`Xhkhk{d1lN zlbt33yBo$Rc9>1u9{ESo#R09@&Ady6Z>GX@D{N-p#B7^=C#39MskAS>+25}he`?UQ zX&vP>*vrl-IsF-DR1>()$P+Q^?$T+n>cTY+_>w_ZjRZF>%`;-xU5og=ciyur=*|#h zKz`^*=BEmkF`?F6W$Zo9lmYam2`VjZm$qH6Q{y{nTY~irrPXd(N!vH>?DUhmfCn)^ zy~dEh;Dyw4k>zT)uAtD{}k&pL6F7MkcwYdxVSQFW2u%&>jH5Od(F+C{UqOV@@>`s9NJ) zyPkdRmP?ZK#lJzbM1t3R!Qj~dJ&ug~2w>Z-wdARdt%sgJ{ygQl<@?Y6*+0GW&SQ>G z7!Pd1dc}&9)EuSx*D}|Dhrp@&{)NE}Kl(m^W+Dy2^V*$6{ z4ls;S!kcfs>6I4Tz1Nl*C!7@g`q#f^FhGCu)w~^+31MQkhzT1I)+&d;unlr$AdA%Q zuH9?b^3>MQFprJIEeK@oYr5!70k%*tY&EiOgeCDZao{Pl0}O(JzaZd(GPW4aKkYk@ zyY!#?IyJN#-E~|V%|Wr6_;;>qrOYmCP8^%TA)$63(vEewgDbPjH=N)mJL7-tc zE2<{hWdP`}KPt5RcSZmT;T5BpICDToPywtj2-6Wy5bS&~v`3Dd@(VuefL=zBXH&cq zYU-y0)*|3O+6&P8^fe;^jd=6NJGxFMNUZ>zWFTS1v zpMB=pH{W>EKFOnxJ*v$pVXeMv^6Z|vM!d&p^Ca*9WCRNXNwDSy3Fzor9VK;xy4oY^ zGoY&JE;e7_m3AjyIN>n@sjB;(-}%lx_ugX*@x_09(NSeQ*{fVV@$9npa$U+0`tG{@ zvyp!<1|!qbw1x(vj;*HH?03R%6h0xgWip5_l-tM%*q!r|a(5UX{m>qcZxG&W7{B=@ zJN0}yQn zfHE{17wx&=BwKa*wf0H{w>X1psY33h?kQsHc7n3j?Wc?1;{aeru7-81y!?6YkMQAm zWHhKDvqyqXEw^7oKn`B_SYUw#Fu>Ng;OlYD#a2?vp~HrIp$7oSz!lbtfNM-l zXU!beNGp0J3;@i+sIFmX%oe%k=Im(6b`Ujw;~U?25+CQmYqpxi@g#b=1LO+1WS6)tLj@yjR}hq<38 z3pJ=lLBKJY5MXXQ=!F+vutV6cKvFvj8Plh+sJZ_n7@T=g^K`o?WlaBq%#>*xtq$YF zWC^cTcHNLVd3^xLQl~vTdVs*g^Q|M=(|7oRsa>3!)Hi(%0$EB~4&-@EYl3)xu0QL8 za1F~8)PKbaw{1h}ytUTqh=opPQmztKU4qy8($!@d0Q6-0ylX-z*uZB>`^+=X7>io( zuJalII0Z%kl6^8Y69`Sd2v~JY{Q=!Bjm&j#-XG;I?jWgq8(>jhXc(M_@Fq|v+!ZQ7NYw{8j2S#%zK;wpHv=mrt3hAWzUu40e%v%Jbw-oRQA%~^8bL634 zESk>UIVUlvQGp-N!4~`$|F+sdN}5AwCP9R#u!IMu$ve8bZVUjV6`G)7y|pm`^2Ap5 zZAKDrUk+blLc`p9W4ZqclkuQd3$=YDd+q*YG-&31T?sZr>LO`W4O&H9H3~oD>Lh5t zy3sQ0vJ3zgaYmb^1d10moAW@B^}afgsVqVJX6_H}j%~uPjWuTzq~W`ftKl&bx~2_` zXcv*IqFnY6cJ~N7>30u|&qFdemj<#!cjCke=i>8ZcHIVdBHr=;uYCE-p6FFUWVErY z>3IscJjn#;K$ua5E(*Q@_=X2&00;(*LkAdut2%P-1^~N5?sd0L>;Mb;{S&z;o4;f0F89T(<128z8m-eEI zzb;jjRw&sBMu!7DGn3~=v3*U*A;jl;pAuQf$Bh~^s=OPO0voQGj7Yf;6;Ij$jz zxyEE*_~1^ex;Vud3#$im7x-ESgrGw9d+IelHM!3zsW|#MP*ehB=K9|BT+4v$`Ah>t zCjGrsD7H#(wsXFYZ4nr%Ycy&xsxOz4KRWY~LnRvPvJ3!vsojiL?vMbWvFves0tkU9 znJQQYYT4;dfLxH;vbTVG7?^tIeD;5EZeSUmFrUZ(psZzB)^vAxYOy*Uudj#~=Tiy*jNGg;dy`?PX0>ev93uEa6cu8Zc?44zdO$!3%>JP<6_s z@p6InRW9C1*Sqh!%f^D6!0s41AbamU_j;z(2ti$}wueddOFrRMm=++{E&bvmR{+d! zvOuM^hwp1urb}!wT-60;XrM^Wa~T}YLpN*eyky%F>|V<5sk<;-X4H0?#=ej-z+yl< z@@rl!;s7FdrkmUGLqC)QrYzc}Ge@d4m52M;539=#QY>QzZP4Et~sokg#BLu*!-~t)DkQO>kq$p z3Y6^KG%L?XKV$HwNF5Aj1O=H5X0q=IaIc=b=3oy4RshBTyT=|oXDk^i;ws`dc!{`& z3PVn@0RvjvMlRtcO~Wjw36@D)Dm%M`$;QgUWEvTR?|u4-VsiIEmu&-jYrlOyQwOm4 zJTezl4FvN5P(w#|zP_nGF@++P$QIcaks*KML{%Rwqo5qQ4c0kY@?ZYTe_3R_R1kO5 z|7|2Yc)%Ub=Z-&TPDdI>AlUWUGX3|3;%d|^*|KP3OL-1R1Fw01QerZ$fQ%0HS?VA^ zkpZ9uP+W&?syru+Uo;vx_E8fA95lBLF#~{Ujw5k4!M)=DyWjn;o1dzWOx{t`wGs#0 zPX<_4kR;3K5P-mvIy4fzr0k@$Y=x1rfi!td2JvB};5+U-_P_)8H6V(ORHS>(^!pfYsPhaI3l_EyVvr5|;x**EE6R#Dx zd&b>U*I4Zm#b*S=fC0d<-&=0G1?(1%mq=+Ey*nMe8X9ry3VR1@mZEk-%b<<`sa?}i z{YHjqz1>SP_q)#n0M=TlOIpeY^P3lb^W1aKbsLloRhg#YYW;6s!KZWxIAz?*Q!X5jJbFWYYo+%wO-j#>g5N>tdJp0N4q+kg?DN<)ks}aRO2Dj;uT=lXrP;ka@m73 zLM-I?@#DTY<@SL41hi*TiDoDV6QxtEt1wDm0=qZ&asX(jKduv#GO#7XZQ(9m6}C;? zM6T6+lVD(4CnI`6!0x5&p1ONMEmdE;?%-}&Q4M$VjW-(!1giqx&5~EM!gJqsCdeqB z2_E7wp||{GXxH>k329evJQu~m9-!yNoDnjQv;?@`Tchkb9X`T!B-7R)OPdE^r0iZ7 zsVLDm(8L52ri|lv!Y1C%e-Ov}mmN;m^4?s$TFd0xK`OvCNgb&KVD1?ply0a1%(*)I z+I13Sk}p3){;oAl1MmX+J`ajN#q9F{!256R|0q(c>%h+QX%a#Z2*3!0k}4N1+NrK- z27mSAchq4G5M5$;|Hka`I(qoFKrJOA!nR?p2z6Y;tVNq3O{{U}r+?iPaop4NU-d1= z9((Mu#~**Zk5Y{6k_aG*Lo)$4X3$(#K%t|!A;9h>B2&?pv-7$(u*}$)QNfV}BeWUl z4q!_n-ayZHIK({?=g{>M*)0_*a|DI%BH^~JJdmUGb%5@f)zbTG>a~BBx>iAAuVL0X zS9~utoe|~UiPy^7J>%}FYpg^g#~hZaGj8QWVR&b4=j8eAcihg%sfEG&U_hP(I^=}U zQ8e9xl7x=7{jdQp!ZrQo%o%*llMpy2#}e!aVe3pw=aMS4*|d<$t6WP_w}sI(*%D~V z$*$&JKABvtDYpy&95%;276j+K3aKCdQiB8;gfsE4)Vs`iiLin->LwH3Kx^5>+bRmF5+*5Y7N9>* z(OGyJrpz7#HaQPWcjFPgl7XoW`yI<(yI%K8AkAg^R5jtXQ0t-fBNY;+^LFh-KY?m? z(W9WX=)gBHQi-h!Bef+T(Ip_MDPu4XGi{}*+)>YLyArd{BlKKTS&dhC(MOa{G) zI`@6*GnZ)44*>?Niv+0l(zM(1bo1?}U>8}R%b0B!I~+*>M|b?w!MSY1xg(z1(RAM7 zuIsKpYT?hE%gi-oj@6hbb{lZ$wu4-y@|^A}n;ZC^p=0vLpF8f8mL8mZ^wCF6o48R0 z32a7%HGu%xt*}%9WD_irn=n>56w6!!dkyMzj%ky8%M>kTcenL!!UDQP#xO!-IyDYH zlfzspXAXVZ`vF6ZNzOKadRV3)HGW|3NIt0HY*uHSWw zS~jAWwXWT2EeSC;%WC8x>kc-^x{bEYICzfQH88HN2U;?J{KhxG;T@tUPMmNe$iA@k zu}RZlJAhH{rw(*2cCY@YF61X8K(~$qqn;S>&o|-xy!z@ZJ|*W((^@%){T%`C*-zan z%aljFF4tv6=z;k&ALNt8b~)_-m>C?f<=};~?mIsA*khg^clwBT>wLNh4?4qJYRM7l zSw!mowIyw9MD~_^09(_y#;RwGC29L~{$D8_GPAEB+8tl4la!L7FaK)K$lIQS*~-6q z{h%Ldn{`A(rG4E@r6SW!{zur)0btwdzd_AsV|o9UMxD5N4WKzm)_y-x?OV_7k=v|4 zLigyExqX7#_O+`$FiYgn$dv}DH92Wu*`yYLrvG+D0p_zjngIYAnkAWBqwZ4ar&C>{ ztF3B*yc`M{@(iFgEV_p2($|nhwPDM1BQ^2J2xv^#Zl+4>yBXl&IA6PVaMqKS+Quh;vnR z+4&dTh*`vuIwL;mU$QNWjIbN6vLt9YEn9DhHFc$Zu3w8f>1bVSdsGmmlWxCd_HK0; z1g;%zxXGfx3WKtvd%t!S&@VYaE~9JQMr!9>%C@BJ)SdW@)COd1U_@mEn=!FkUpIBv zL|6Avp)(B4b0^MA8TJ$2((>nF+_I5h1JTcj0!9VT!{W1Q$_9j~ zY60hiA+e2OH1q5A08PbcD$M2Nml!fHF#~RyN3;RR;MMqSzTasK5R3u)HUN+YyR}k9 zPk-pG->*ngo+wrF`E{KS|HR)<0Rbqt1N^4_!Y`P*Rgg%9K{F#hh2KfIxnv?WsHJ9H zen&KIJ^OCH%>Ff2eQ+y?N?Q1At>88Bj~%-F3IkzE<4jh<3Sy^I9|_ z&Q>95M3=~s_wx$0PWswY9I)V#)MH0740y=n#EBPtHqzjpZ(7%D&)ZSb3Ch+C=h1*I zQ@cjKU2Qf1Wf(A>dhYmhr`|sG(Ay7rr>wUf#BJ2gMb3x)$Y?tnry7X69nxDRbz8Jx zFiSI5^k}Irdb*Z}((T#^WoeE*A_Qifv-6f(A;nE(4ZtSem!_$WqH_Nk9qM**Q_)7a z&Kw%vBGMdbb#;{+c1>{X$GVx{_q@e**Ws|A1Hk4D02m*)@@9yZl|WP+F{(57u^%#Z zAUt5If?xWrJ))61*RBtb&6ERs1p}(kM&_mtgQNt}3SIjHeHTOnYa6MmLSsW|qASfT zs);}re43zg>^865WFiY>^Kl(~7KNM#>_W-V#=8@>Qo5j>2;jRSEz%kpdmROF8E4-( z>pS&5ooW0U{pF6T%S9B?{$)+zi(_>WrC(85y4F-dDvhh@?>uxThwh^nCtrQlp1yN~ z_C;a1^&SS$EOphL=-@%`hC1TL04KwvSwjPU`P466EfzgkV^SqZYUO)Vkrxq|fmPAD z_|J5k^O8U0(AW|`kb45kOl04QL+a1X$Ny*JC!MHwgR*y3(V(VKGDqOui$>7J4WP=8 zC=F1B5s?z;V&4XU=Dw6ngK36-^tVS~7-eg3A?;V*Z-^D3B+PTa>D=3RJsg%rTxsx7 zv=&>+rX{Ava8k9tmN6?Kt6i{+-N?u^jA}Nr$9$=y;@~A`aF^BBCFL5~w$YL+qsU+j zvn}O7b{6!@a>1J!n?S~9zDY26E&;?S)z@`M)l49Tyni?@A=|00$b=<_D;e$@bseu{ zIWLoJqd^-O7Q!1Gxc0zJ2XAt~*G*!#1YUgUrMFL=;-Pw?YQu*}T7L+p(Pf9>^ooR;pT3aE zdv^9GC!W-ViOIWD!z;8CvQnOarx%lFb#) z7lxux?%M#+&wq^~l}c92F6t??3QMfFcIj68~9&RbQJPgTEdd z;#K%5_NF7q!sbVFXC_}5sWr=wm$&6$sNj0-3n7q%hNqDvuTlmdc&P%i@T`^~0}K#l zm(?(00Wy?3hvG34#71X$BTMsfRb2Z)j8pPt(Iv%VQZ?wCQ%5(rt|O3y$hYydJ4vFr)@qOVd!oQoV_WxQmCjcRY71^`MS^n z3`ajO4Y)Qi@^;3Zu4G$=&I>b`faUG$V6qk+_-%o$mLI z^RA44J>}Dl(`_pdAP{CKrrgPZAC5nF+zn^`=$?`J+en)=~eygk5l2qNN#||ZiwgTpnpo+`ZPl^qqM}b@mLJ!xu`r1+O zW+0?|E$Srj8d254+K9Hfp#2#D2xP{(EQqR3zWSPlSUWPJ7=Sr{Q5tU%AxDt66gwf7 ztd_83(N=Ez+$q)8%Ekadu(l(-lc#^GcWh)7b z)u&)`@2+6eQo|t+}E`%%cKFXieU^3v5 ztATCtqtKC2T?TTE!-YmE$K9*u6Wtr)e5c%kg~O22K1qmG?AqJwGcVW?%`0gc?u zvZUD^zLdPXbH1QdV(eX#HA;FLZ7Rua-hQ{}sZ*SJUNOMQ^Z>vN`0@5RC|j|TxEFW= zLz>#_Q_np6tP#R9!wv|!C2;F)w|aF%PeA1oFhMM@fUu?!A1{9x5wAe1P1W|?2eWprRrjvxz#h^|p~)GR7P z=C)}Uiqsxv`pc5IFdw+`#I`NGu32d~m%)5swgA~23CXC&lI;>6e6CnvNIP0fO=-jY zmCQM*#*p#=x-b3FHj?H)_tM>1j$HFo3fBlFJD$jFy+rvf%;O!N;Gw8o6w34OVa>19Jt}o9iC~t{`J=~SpLH={(%uZ@ZbZ^ zKhPNBHuq+P!;0f#RA}WOZeU6CiNVXFmo^H&Cidx>6@BK-Q#A(i3I|ky zT1G_zT$8;{fGc6a*fnh zl&H{50L^d>SP~4%VN%U!joT6lZVqe6U7{_P)TTjI|ma$CiI z7AtbmGHYUCWFPI@FL~5Uybs)yz=r?nW^bN7vVoK5W;$ar2nTt2Qh0MIw-cNW;*L!D z0p7aiU3ABedH(FKUPJKyvdGI^Wt_WGo!Bc@_%zQ>a`fMX_3XjC(AVc4hrkVBk}%EW z3#{oqd4g_dA9T(McdfV)C0i?@rG;;c8afFus2!1V z_}tCN73LBk3v==f{uW%mx5q%)K7gEhfUaiX@ zgl_n5>~4FeHY(q`Y`jTO3(!}22e&~#fFbI@G$0p1aid0E^cva4MF;bdofwehSE;w& ze9Owx3p1SaL(s&7GC)L|!)#G={L1=WYF-^%hePMf)GJHweU)a_owsG1dF z#2aNv0w)^I`3rFk0ks94!lmxd0FYuT01T(z^LOg4d=)^TP{^8VrhJ%*c*AAK_2-mo z#`%sm7Fjtm{vI)N2cP|MjSoZ4J($-#9x2&b-R|uOXdNN0T)$uAQqHZgSWK(*ifa5PM0l z=w{n8bS>;C`W^thS%r$HAF9fZHCXby3V%j4uys~9J%p2K2g^F@SfH>%I5iV?rpM4wA~Egk^u}Xbl58m&yJ? zL(0%&ER$p<+(51Y&A)^(*I)T`_TLe|XcBEi&Hfp6qV))Yyv+n>$Tj|J(e5mLl?0GI zG?P!Qu97YH9t@_=vp)mCLX>mHWOrSR&wLY&(tJPDM_%D^H6!8FBqi&ByVjkmbu#h4@kZ_K)14oD^nusjve&bxGM!_CgIie#thReL&!r9Kji&!I$irK{1LM{ zu3sMnFt)luaPYuKc@72!J{)J?wW{%Cwkrh63KFt`BfAicYey1oxIYM=sM;}a0nh=w zV!8tqiAwlRkA!QO5?JLz&{vOPH}!s9RmUstZMTtF+qC+6ECmZ>$~9Bp8Rpj@9W^)>C<@7GaXmU0``N<`*p1k9Fu7CbrrHm z@S&tS?ne0?@Tob`bmO6mfLsbN;7BlL5QP=>+vW)9)Di8L9ohE+G{e5G(Kquh1^oVd z??3(2(;Vwx zQZE?h)-45CW|`K+y$!WHg0B(S4&tMJ@ejXnHk1{i_bu+8t>O;|+j8`JSvcnn5>x4V zqErmG(RloP`pC4aQxsKnyuFyb9YPbbY5EVD{iZ2|q;h5Y->~a8#cf4ZeYc{viy>1t z`s&1e7yzixKpMUW57NFH?v&HsUo{$^W~#^VTDjd|4x&QF@l$Ax)&)+E268+3CtT}) zSCU{r5VGGWYi>T4tZv%>I!P z;o2+`>XbCtRuZxf&0h~HufDSEZo8o=(IJU~1|ERVGP*|?LqT4>t2u~v-T}ay*8TDq zzdZi@apLjXJw5l;^I=3^}&iHo%11 zrATAvAD;eDkMKB6Ja=U?_4)8aCr9(u0Vd=&lB;ufzy9^F&85ElZK1HFdE%S8A*{n)MTCmOqZo9^?lY&TJ)qnj41f@8y2(D5?PybwuF*fKRJ)F4WY;3?JUBLaex~U0P zoF3(!E|Sm)%L54bqY*l({UI2p>0{`%%O4-+FTpTCz@$`k+Wi+5+Y+Qf}VCZyj8e#@Wi!wR6waQ*P34-KmH*cgQ=Sm@u@X^LLqi@;t`^<;I6fEAhy z7$72-(9PF%rG$a{TmfD2uF|B0Znm%1o3`+=<#ZP7wvl9xbm|AMM-HHzS7ErnpAtBzj1UMU)=E=`!l^uG;8$6oK- z!|zYcU1wU-)B!kttAS}#o`o=;-dE5yMAi!hq3$@s#ALfBUnq zy!?viA?*Kf{l+9Kl_pjan;G1$0-sS0EL{KIYB1V7&0mq};xvAa=o~mR9U-#ki6fW8 zW^4yB8&(u&T@T!_F?&JIDzZalJrb~OYfUEilb`&=QsA54{N}x1xL5Y3kp;n!5*+sp z%Ulf#htD2L@0tc7qNOt|f{v&35#(U)0#i>v=zk0PH4x<*raJ=U0M_89U#0HsPSNbo z0HD(=)j(CK2eFNj5Yrp5OsY9I2W4?gl#TG-|6R)XImrGd9I8wP*~?{;6E zA#Ra!y&6968kpTg>d9={u|f(#aXIB}>mY#nPA1^|zLdK8b?ER%-nD*>TSw@7;xCCQ z@=2_ny)m#5A3mIy4kcW7^5m=k`~Up!ciw&H8(;s%0}njl)DQw8=2xLDIXm`P2>keLL$ks4DXVG&2anK)}KJ#v7^a<|59n5gvL5rvRxVLOzsPUnj z(lu+pnuRr{MRmon*_xes2dcN=xff^d?-2+aiTLPy_Mr4$wlVtr(j$x-T?Ia08kjnE z8l9KOPcVUM7|69Yug$)BkEV$|9y~Ke%2_iTUfW@Z_YdWKid-z2fw?k87R-s;w?xKz zW2lD?dEF5E^?~9ie)aCun66uV&RId!Y5xNSEPOdo&`~aj7RkTCY#|zgr`l%2J zgUoe*1_08V`)9khf_ipIX~PP{@=F5g7-5tGa%OvO*1f4v@EilQulz=plF0tQs_pF63uI z#-!);mG@`!wFIcyOYAddFl?^{cTT*bzZ4xSa17rfFQf80zEI#2H8k>p?lfVP}5bi>)ONxic}Yh%rirI zLsGhMbt>N~;vzX&ZGeeTZyKsZ{n1*8bDi6NDl`|$>TC{74M|4(1n zB6R??@DP!vZrZej%PQ>vGlba|mUrKGp#JSaQ{Z_kOY2J;-JQM}YC2g)u7ij3SPiS~ zz>2}0n(Mi|;({6IT_LEqn&5yH!)S)OfIn@BPpl#91V)p;BZrPG-w`(Y|B!PpbsWT3 zfzP4_rW?OKhbiBcS}aUEHZVus0stEZx~4s$KAdL-`kw5x?ab`uVqjL8Zu~ONp8I>~ zzI`iG#la0H&jh#=@WvZ&{>5MX1wzd($X zU4SrX8=8rty~$2y)D`)=Y-R#(QNrO9xj+sp^{+c`yDVW{2--lo$S21=MAumsB@6YcTaMe z`iLY$`~3w}*u)p!f1)kV4py!zAizyDy=Lx-FgwBvVP>K7OU_Q&hZTT+`;Wi*M!Xa~8Jt%=KQpDrBD zkgjyt@a2=IOF1w9wAS1V14uKMkg{t>b{RfKfi=bUX^nA=N4f`j7(g{fbDNd~YZ-AH z?G@Iahd@!EhiWY9Fzf+tH0vUL1%`6fpetxaF`zkd$`HQGyn}8Xm?jgpb7l^$aQ@#1 zT85~Q0=Hgn-;R84PlEX|zC51l(4kvyzS&;(OE10n^wUqHw8ia&sHX%B9`Y&}H-Xu^ zuWODdk97cLDR4+XxajTwZK`%X0kyKs6yt-RCSf!+?J&@+=ULf$a7g@ zz_i3Ss*Oj>-(6n1Buz$kwKubDq1-NV5m~llvkmT|Iqxy!P~EcZ-Sh6Ay5zIJuyU70cc0y<2u^rmgDYzs z^sDT1V$4Ff?=|9}TL4Xv3B)o4JF&2XR67A2Z{{eBd^#suD*>~6JJ?*mjU97;1AuqR z*(fr{=jfdfz4zWP_&mMG694?q|NQM!Z~yLhfA_xo?(0+qs=EQhaflj7a$i9JyB|=B znX6HgqIvF5igx_%{4*xl4XU9aNdf~9088C={a_@yj@}vvyHx7}ZV5>_h*8qaOmfNh z0BrsL_y7Li4S-5gMu!Z8k!hO#2U&1e;OcdE4Rqs_0mP<9bmSVXH!z<(K)h`WZQ1(D ze7B|91-V(-U5KwHUDW_z*4{n+WibZ8PHW1{8Epu0#QEQEh^9B>2ag;)dhlqH4<7XF zp*g;1gWUjNw;Y;b0GUw%#@Cr#(frw;{h8B2Kls599(?e@>>|xe0n=Xx9`NM)orm@; zjhg}7phC+Nu7&{xcdNzU$Ma`BPzNQp$d86pLaMbjMDoHn(1|<2TeBvHQlV~;aLT|- zT>%&{-71NTub|FC!}L7^i~4U$R8`#$y~L&cmhnBqYg5__c1e3KXx|965MI^#X?_8` zW{uC~6Zo^v`Md`3S+C&U<+t&&Mx1%HA)&jD z*GH8#a>*v_;GUhxb@aNUJw4^$9TFY8{Kf!o9ZxfQ-k-1M*nE)gr$7Cvn+N~&2mkbu zM<1~ckSfW@(F~n8?Y)rR36iCad5cL`|FtfO@6Wy9O#VrwHJb^bVbLuatA;RAyE+h} z9hTLHK@cA=xC7@mgmBkTi4h)M%A<*WLuzB18fNXP?UnP}%&nTNEBEy9m$Sdi3ilz-=}kl70|qivtwJ2{?H zG^qz8xc>SZe5&>3mtXq(|NDOp6+d|I2c8nBfk9F}QKuCQ8#vfvI`VNgf_jMnAJ^+= z(W5#2FY>}Zc=b+PJ)34I5=x~x}+6=H$NKyycVF< z*ODzoZ@4Po)rW`>`B~_q#aap%xvP;%@@4uaytYKzFoew1JU>cY_!HRrI95;e& zr%qp+5N(0^gs%go6><=z_KO&QJW)NN;-O#u`mIxM`SK{h?!T+A2O9$n04Dn!=#4kt z;46~W3df&&j-&Syt#5qm8~p-N9L?fQ|LykmxhJ37%s z+7M!h&$6p_w3+v{Q)hhKX#I}}{!0}?Su=9Kmoxx4Bx^N9QyAfH7%1+!_}|f~yp$Q5 z`l*PhzY6dpK!B5yi!HQ~vJ>!m8vs)Mc?=kJrbZUf_6iAZMJ-Ka5T6%V=EC{B^cO_1 zWDXwD0bCHbtHj%CU?>x??=84(iTi-mQ`5OSq>A?d06+jqL_t)y3F+z`{1ylk3!l-; zJiN;)T6C-ECNvWqI(NwFGg$g=gOWc4NGjZ5$JPf#2uG?9K4J#L6bnA)h3= ziNc)-I`QjY|L9MDbn5L>-~HZqjS7gZ1G-n{O^fwTEzi2Q8|-OTOGgd1ef*6^gm2ij zDO36R>EF)PeIJ1^S{#kkqRpl8u8ohAPl!t6lPhXelf_j)dGP~)Cm_77w@bpY>xrY1 z7H}2XLr#fR@@#E^uHV(`4jRzjokDe#JEA*~UqxTlz*P-wr-51x*m&WRYWAOjfM4f` zyIXOO9!@dS_av~FgZgr$>3m*(cwy-gvZwbI7 zwijd!@TtDj*ZOk3V?~EuTdeNFuDrdz-BC%)L1GCnD--`&PQQ_Ba7RNihiPzFjS)3q zP(cNpE`9)b^2sMX|D|Fr3WogtGBl=#9rCD>)MaI^M3{aif4iDxe=2g)*su%&fMKYV z8khLpW!pRD?tCsTRq3Cu02e3r)m)cL16$63?p8=<{(54Z)uo2^gt)XRazXK}y@WV; zz&mUW0=91S`V?6whxqrqj@`wxdA*G%!1#Keolob%U2(w5)y{{ECtrQ_fBw(^X*T?a zfB1(E3(4h%Fz=rMpuXmvSE*+YDR&Vf#w1C+Om*eV`7!K%-B;1RC%jpCNdmljh1H0Dt?pe@k$xX|Gi5;-S>jwS)2iEku_+1d8Y$2C5Lb7Q@=O z`7?-j1)6`m?Wng|?wV!0MD1S&>eV%-2~l%}7HlcrcA57Rr`R=iUHT-@)>Oz(Qnoe0 zu12qF;5-`8#fLe2*VztZfXBk8USI6=#mvVyLMHEXm(|I9f z$y6kyiXsZ9#sZ)RXSAXf2 zz`y?2|N8#>?}vAk$WW2PpdaPa^WJys8pl|lF^iYa)IR|SSD1=T(MC3A?MkE$jRy~c zB|MOufE?|dvnh1ZTR@(ku=~%`D`0EE5f9(|(0FQ8qiT(h#?SM3!7eZxh@4`yWVHKg0Ht9VC6-q*>T~<9nLXvufby?hi z2j|MTx-6}Elp>jCTt|DaGAy9h!vYboMH+&k8E~g=n`@29WsNT7>4FAk0GCyO-zw(a z+!WiEa1pJ)Y?O=0z9(&0FHUFbepbC@+l*6x(TP_MZ3|($ehgqYUH>56y&_U|K)ug3 zuMFY}IeqV}^e)P4uKC!j-0TK)--EE{j{fUI%1K-rvaM(-l`PF0Ycl$r~dM z9PldhAOHBr#)*IRum05+zxYL|y+`EJ!SVm6Prv_uK0ly%6^QQ<%E_dd+1sRQ)vtG| zMN`UZENSAtHho_e4HKNh+?7=_GK<+1t9Z(`)~ivHkm3ZiC8UADWwXp zOAEA!m)4^7Aqv5!A%u+Gx}j$dQ!Q4x*PwF2llxARkAspUBWsY}YoB-8N4Ds>>K>{@ z3SHM}R7hjXU$&Jd@QH`|Vsa_1 z3ub=akgjUJmHse0`pVP&Wn0%z39W~|?p_2MJx*8Rva<7Y$Z7m?L2*ARx|v9Fmlmac z9xo6z>woRFlZJsl8r0VAis1U|t~+q^%^Zu=AOH1V8w38}5B}h*U;S#nW^wqi(ZZkA zK)!qNK@ON&N0bN5AW&{4%!bTMcpwMRvbiD~@Mh}3Yyxv7X`mbCq6Pqbd4Ce9KiE~E zq(V${|B6nJ0)*0E1(X%(JQEU=Go6*LPR7Q-39+&v5@Kp_5K^o{@UA<{rsnSPASb)K;MRy`A{OHSrYA=9X zdQel6gfq&wCF5GRlx>&$*s(i#^H*MZnHTSV$EPEc!~wV(7&wJjUVYWUNCdcj_3^L5 z-+Jq9ITc#lO702NQsDjGBcDpCp5HtP)AH ztC$>&i5o6)sJ=?ihX|T8A08)L)m3^JSAda`SJyB(lU1*+Pjm3`Vs+6Hp73@{AF|JN z!Q}SNnU6!#))OnAB%EqjGW0m7C#Xc+*6UH%rOE zb3?9l*wt*ZtJmKi4X~3dq3Id(r1SpmZZYF3X%&g(4|-{<6rkEH;``c=Q@1rY#n>5?vPso?)Q%F3E=K@>|75YcH6-5&?ldKf{Wlld{E3f z;PyN2;6qM-c>046KX}jQliq#T)5EzTkgpSmzasGwTCglW=I*~%?@7)z>>S-OwcXTA z=9iE2uHL4<-~oVqfAS}PLMfIrG$>%yp*!S(Yq)J@Sw@(PrmK_H&J-UI@9=HMBn z$cykJfAzR_4*(eh>Qn$dB;=02QKTN~F1DByzqDnyD+|e&bLUU10~klYe+oK(rBB7l zLOF7EMy}teQDbXfVueh-o1oR0>g^DaZKgC^*KUHTluf#;n9sEawzZ8*tg((64leb( z@%_Hl^sA^2_U*Cd4YNxWU@zJA8a*|X?eD7_RCkE`Zw^yMiUz%HoHK{c+fAw*T1K$+m_uWi>>#bAQAARS@yYCn+ zs`IW&9F79eGOS${Cn%_k9-I*Nt&^~vRy#}aT z8$szd7X)eM+WoJZ2NTH_q1}3Asrj&^YL0v+D&uPnvPY)zmLKHqU56wx;w&`F)?;zY z06WTR0KNg&Wk5LsJlK`5Z@81dTDm>IB!dB%DdhU$2YvR7iMaQ}I6ipa2Z&C;{dVq| zXP`h^7fce!T8*kMT^;hLqP2XXb@}wxR~m(UNFOyA1qQ;Hm~LKm&U)qNVF!`CMg`Z8FzUTu;Rs zKae4;Uf-C}_m>0>v-u?wWn(7eSI(RcldhyQ<(=i|(z)bKPd}IUyE*Ug%FCMr>b&Ry zxZ!CG(6jV!ssH=QC%JyI9(r_2r?_9dAdI>H|x zOxo2fMP0(}7GS!jlkhWz=+M9_i6;3vSz4KKvNE*yI~TV)XW#4Zv(G-u{c%z<5U5La zje98xT;q2tdS0f8(WLEbMP_oJnLW8obq{A=?5AhS>eikSAeYdk12#)($48+6sWJjG z4wnCeNi++KDXR!#tcGU6kgf492x&(?gS+Se1}1Gm_@D9LsCb~`!-On*bhZ-n5#%KL{hUg+u)G&lVC zqcftZbycmM)h?YelGJmck+1?l=q3P+yfDFbT1Qf)D4_x|lTKiJ@)tG$nEU_rPkzDy zwY!`-`VG^lU~vy%vl*5cc|?1hh>ne)*Isx1(d%zGdP5$asHr`MV~(GS%r#%~88DFq zO(M{RE4OKY1q9ohz!ezTY79ewRYw~b@Q9`oA{8ds(_XdXRBkB=gv8QsCfloW_5gYHn~AF}EiMd~ z=2Z+E%-nqvV6lALma)pWvbcL*jP5urfI!@4($(I~Ud}zfzCCGQH4MNNUY2&CxraM@ zeCp%=&DM)yhTa|=lm(M!-WnR;xK35c1_p7<>52zrOeLW4Fbx$Im)0>+S;~p(=XS7x zbL2+8dGWm)1Jzko$PI?B1tD90va1@Cdi$NXPriEcA76a&r59iHwEAl&Uvop=QRSze zdMZU6K1_kUe{U4scKhunBgO-Z&>LZIyy3{O!hfkpXPg!PVGR4P1f-bg2zj zzlB|=uJ^nxBzupr;dH(W59za+ruoZaKC$fmoOPO9JUyju8Q?GIn7ZLu2k3g1^ODOT33SGiZa{zc8_&ypm-xsWzZn-57TO;GYM$x71N#klx@BF&};0gs3XuB&7K=YbX zsu^KA-~{Ra*`aOfL(oZX26A7b-4HsgWA3BqeWYH{k6 zX7ZL+D+Yzqs%B`q$f9*k2{G*-` zJN3j9KP}`TUlq1=@G+Te0cRob4iAQ#B1agauGNA(Xd+C7mVX(AsUDnU_y}FJRRd7f zVLF3T$;OGL!(A(HGwRm1iKlIac-RVqCgqKAZt^2MS~)d`@AC)dj*i}N^yu|RZ@c|A z!z1r+Ad#=9{J7yPP$l*peTTYzn!Bv^P(>cCHm^tAGN6wb^&FOkSZW)~N|=nS0r5PE zKFqBs7MBp?)J9o&{1@o~2}at1Y`E(UIRJ3y-Q>`DwNL{X3Jniwja^}uK_Na@XwHpc zh}KLQIIp|br|BMg=pm`JWAlijOfFmv2Hw->`s&G7eZ&sNQh@>)G^;iG3I+hXMlAS_ zj0(5jaKq6I6Wu5V4FkvFBLwL6N10sgkF>3Dg^IT17kXXGh}DI1xRU8Vt?I04b*Z9# z6zPIlv5}4DjU`v_%>1kf+P1gIT>WV?FPhPPmh@{dIR!cyYH7^qZs;3>0Dx!qd^{Rl zr$QUU)Z;^w7!M|tOUn~vytdt1cwI?9!p;3WGh^ogP1-txdzfbTdYrx8zxu1c;x+&H zkN=Yn>t%CZ4w2q^>TR38*IwtC$ooV7(Z3WokVD!=t|fI9g7nvtCTHR+M3QSTGtaj1 zQ^!Ndxteh=3)_T=b>hT{tRX5O@H^xHw`u^q%;AGMttb^TlktP+sL9mRE|_5AiEKE1 zWS9%wJ?G9((Tify{ooO4#loT8$UYiAlnNNmnux2wcv7ndYPbKm^MA| zzyms$V3rz3t3Q$;07rMkoXd3oFaF}s9sK@x|L)&C`skw%J^YZBfxzFz>DBQN#JHtT zojb>W4%tNUazy~SR<)E<{c+Im4&d)NlVK_Mr}sVp{QB3ww#!Vc;phPb68wTWba2W( zmm^bY*K-FT!Qkpea+!iERFUf<2sf-g%p&j~;j|y7=HjQsTh50BG57*Q$Nc zWHj|Ixr=Mm-F37}Y8SWExJ{>2I!CQmA$qxFhd9a_fcdyGD1>IJ-7^Z(bYKl}@X&kL zyhl0JMw;WGt$(ikzWeXTL8FwCPn&u;k8DVI>7^G91FxKX<;^$VgtT$=%rnmzZ0$r@ z9~cZg{BrEByS(GjkO0GI+@d!aCv#8P`rzf4UwQ3ymi)R+B-4Kx zmB3bHG$ObPmI^kEdU1ev*Fw=dROKrA^Pm9+na&tR;mxow(D>CF&q|-*XSFMYdDjR_c}kJyFA2=R0`ts9t3+$R6wufA~X- z^~WAF2IzTjd49MiZP{GHuzM2#vc)1mcYN1wg2s>N)GkdF(R%R66=ge$9#Bwb&R)mu zP0IH)0FdvOzx*X9PN1oX+1Z7AZ40z#M_{UW+pV{`-)+OkHiD=U4IWlH0%d`1RpJ~~6dmNGwOC(-y;XQGsD2G@AljZ;RD>WKkBh@8#0FySoBJSkV(^l;)TWLjN? z%5L~gOPJc@SL`Mrk-YQ`WvikKicV>iChEyyhVV(WI{``Nr zf%k`h_=j!=Sb4v0L-vg~Ou~@H?bHm|RS$C2NryyTDN(@aU!p5{hGGF-bWWJ82q1f8 zkjaQ;dtgrkfXQH&5vFr_Bp{rzguCR)L0kYw!1Y_;7~d2iiFT-lE8+Tk)}M`t4^FFF za~u7C(*CsBGitpB;J@rSOw}x()ycqAvnmH8+HPGb9lB*b36;}Awb2$lry7OZQqRev zc+}e325T6BqEp41J9qol-~!Au*+M1G*UYnd)u?4wPAovwwX>(n<@+LP39Lqt830NV zotGGp0-R&~qmB*q&>|^$yFHt@+^(Bgtx1{NN{Sne$dQ}6XFkkf+4tT{o*cifK!EJ+ z967=!?z#J(YrgtbT15QTo2OoX>^^_gIqc?Qji6OMYtCp!<4}=P*8POKw>h)9Cz{*f}7KMSejWtP|ZeYzQ($`h? zidRZV%Bojcii{br$eTxzEs>pf(^Oy@Ie=xZC309=*KtYK@Ul`{L7(*d$07YipCOBZ)h2*HgLbF2!+0wo?I@j86~hvKF3da3y?Kp@D(lJHT$c zh`|S|gq}E_KK;hWAJY&BCf5jd60{@4)~Kl`H{)fx4!wUM<0EZ ztF#rATLSOCQ_SMc717tmbh!-=fkivO1V~#HP~eP7k1GNmZl*inCDu1T{LsVa9{`A} zhZ_Lem!*^naT)KvOCyF6XPX{;@Bw!Wd&9?MUzLP+-Zm=c^kKGB$_bVnC0KL0z-zn9 z=)xZbWn9hcJhQfV|Gf{qbMJIsv_NLknupq^$b|I9o36d?h_aq0XFQElNUut8=T%t})73q#tgz^A1FZQVt0+Z=7Zdy#GS{!@#(Urn|dNQ z4->=**aUgz>8Jm{Kl?L#u#Z^+JocDHwv84u&fGt!cTGp2#TF2GmeJkoxO=LJD;fB0 z@2o!m0N~rd%BI|*IDlZyg3%YgaL=QUJ^Jt$ANG6^xmAPrxArleL_mEiZ*H#g{^hRZ zMn4~z^R^$#%6W(sMN?5Bkwi;R?* z^+-0xf((sN>(_J)E&hI9P+_cP%&79}j_Z$p;R{Fax#ym5ee>JK4EGNx)(r!5pR5ml z^TNqjUwi)f=YR2wU(hFI+9R??bY{-kumsN^9KFG~QYESLw0RYgb`9t|UF~-zyqb6c z8W=Vy4-LoKu6XrMmNVPKUTWJ_+Y4pW#Pgq*f61Yb@OZjmb(gbu9yQL&c=-N-dvm(S3D=}TX7vrYHWi4aUhZ|J{UWYoF=#4VvtJ%YU-%k7@m z`PH};>yDF0M0HzE@;n28Zv4a(PoR^(C!SnDyH1_iUcb(4W~boayzm>ZGc)KRNUbXj zEmL9~*1QN9hBW|5C1SZD*qSWYZ(dnPs}5J?ZQAw6nJS1UqEJpZ1982CPnli+xXZI= zIc|4XJmtid8+qORJbFW3j6;&RxjMh4T*QLX4JR#O@yjm19X$iUn{T{fjJfu3o{!=- zweV@rLTkcI3Bt&rp~}t5bq?i5t0Uo$Cw!AFfth&q+f@+nEPaI>6phKV>erk-=xtQB zs&M7v^DnFK$t`l*T{U7iz#zzgLy@P}JrYRGqUVs*{BY=tm$w}J-uJ%8NZxq;jW^$V)11H=Vuoa3usAq={5az?D))H63R!=R z2Df^Bg%i`%K57bxv^5~LDKC>6Zf!KY)Dp}xNpvl>vwUu@mtH&S!ldmdvw#inbADZ! zZ`7i?C|4GH{)`t((K269?#+6^Y$BID*HVj|tBHXLgObk5|L3+aXHqkqTfMP>Q{=Dc zmPT|qiwvNQHi&-m)1TO0{^7s>_XYsRhx%SAuZ12#dxn=|?AREc` zf`Q5u)1QL`{!<4u_rhUIXB!*`@Rxu2m;XO|ch+pzaomX>BLU9fJOHF{0Fod@QG+d6 z?MYI!BdlmY-2O`b9DZ@%@PnWH;=YdS2>ZphA}j~hqDXNRh9e53WQ!v>lbG)BpSf1; zb@n*`2Z`X;mmI%|oadgAd-U|lQHmtvSPBoPx zZ~KFYfXZYH+{q0I>gppq(*rlBl^pqz-Gl%c z3)AjTWq>w{ZiJ4qXpp6@AuN$QJVdcf>KctEA9Mz*4N<>5&R#rBbc!9N%j>b;9G<&I zR1Sjd7mOaWW5_?+_fgb{WNI}45-Z@rMs%y9(-7WS`o@Abg|KVa`}HKF#T5}Lk+b$P zGw`wz5~bFha{>c^#R1>R0iCgBgc$9ze7xiKJF-(`MS16)R&1OVMk#Za1*s86rumEk zyn3D5Y!UfVY`+PAY6%ds=4fe0;8$={E6k~~EL$PbDUQPD*gOiVvZ^OKB}#()H>MKTU>UM}NbCWbXI_(^b&z42qm zkD+$80RYG5Zi?^upwmAC07J?88?VgZz*Q`FdOpl~H@ZvyK1ouZ z&#Q{?H}&^hgVTiD=fEdZy);)}r3hkRA!N>yeM zC8eD{rISS|ofk_>B30cm1`drAR*QzGoGPu8@Rn{`TX6941KwIF0GF~lq8BC>h%Mkm zt7$|t)0ePtl}$Y0h&}~c9$1p){fN}eSYUgH9d~Eccwnnj2QY!~^qJKz>;K}E8EB!L*H zR-G$n-y-yEFYJg)LAEH&j!2&wIP`y|@SluUEBw`Q;l$0N=y2QawTw#Vs;iBZpz8^~ z9Hvk8y&w>JZvL9I*$xQkiJrOfaDds;Lk~S<3`mdo*L-&jIenk&J}BB{jbfHj;mP6` zbl_Aj1m;A&lI`2K-(<@eAoXekfXyG5JzVZ)6ZKmUEO=*tO`nGze2`hhBJd1J(9Aq#YvkI9O~gPhhbfrui|seDiEdfQ>+ zh&EdClwOGn(pO0t3>-uC%O>hBX|F^&X+u5Ug{SEImrSxacoG~I;7I@S^Euc|Q%Yr-HEt@YC z0cQK?;I$ehn$UmE({KFGDgn|SE&Ew%?3i!oVPPG0F-ENV?K)C4Y`~V`&%nIwat&&$ zfCDfIv(pc<#xJ|XkFw3lt1Q*)rLLGO%JQ;27RmJA%r)2GZpu2~58GUc!LVuBO8azV5NdImhZ)xAk)WWtY2RGy`RNgAtdC%Z|N7l>GTAW=VB)Qui% zndQZ#Fmz0GxCNmY(;hi8u`Y)~s=26LFv_V8CyGJ`_)_pdYi+*en4+dLfS7M5oOAh^ zrY!q+?-vzlgb=JvO+v8BODEti?;=UdpoC{9i!}m*0A2+WaC2S6RnxsCyLp6M9U#bA z98km~hmYKM_l&_teOp8)PoA>w+}zxBY`|T9Dld|#*LoMlY z8>(H&b>`+bzn?||1US%_8aRMg4^(ctx7vE$jvqDS%8N$Ktwp2f;4V)@t}+78eWZaD zY@`nToSJ?le6j%D{$z+NtaOJ`=%mK$h!ou)UM9L`4i~eY|06@66kEQdW;cN|vIZv5+l$`yEd< zyaQ%jFs1FXusX@0e&7=nW~8A-ax3e; zEp@W*rDNL?KL*e(^-0=vM?!0Q5?!f|Rwg?JMy{wXtu1}+sIY);#RXCd*1aYmQ}vLE zt>Y>D&CmORBM<1V*c8gR~!KT_HX~z(JNceF2lAqvGisl z5ilLLe874a*|lAACcK3kk+Lika}~DRV&bXVUJxa3M!yo zQW3Z{5_%~K)1}P8QW)^BN63BqJpOBi;Vc2$rjrH$A`nL3P}1DeNG+#fJQKzmnc+$W z(Z(F!*m@PWA^@&KX&;8(?c=;`SMeF6rqB-Kq_Z~#Lx_Mn%p6WbR3o=oI?V6DT>XjQ zQh?2H6O3GH7bwyz3)mD2Jehv@7$7vWd4naZk5$t#S*W%9Q4ov@{vwSC4q%Iq8e}jl z3FATci)3i$vjMKR83LnS8H3v#%fb~W64FHJ_To{_yh%o{$XfY=jkqlMWW*;YyGIRN zFf)MBKvRS>Hi&-qvzK2w`?H^&H8xl>!_kOV1b5td*B8EUzdcPWXew$TaY7Rz7;N_J z%2rjmiPhiy{JJF2N-|lb>1U0$R-PdB+tJp{k{>eiG`8lw(d zP^o4?@=L=@hbM$Wlp0NS8LeDtQ*v}bSDNg7X>$Rmq{eRxQqRC%aAk%qm4)5oQ~1G& z%G#{{UqAR?P&^rM;>5$IKz7~1$1GEL;0w&1D{|FATU?D(9j_p~L=?rS>cZ)U8yg$C zPm|ht0AT2KH671fjMcO4<2CZSqRwgOghNa9kccFNWX!=QWT~fW!FJa@Vay81NKqqjg32Bb@B{i_~x=Ne$nKykmLyPR$v5d=pQAvp}wU=DEuN9G~QL33DGIc~p1ur>) zb^vky!}IBZO!vNwxCUuEZ-);bK7RbTC;co&JzHRdh;urn7rg+$KtI3mlb<~O%rjKf z1cjEivs2{Q->L!ywN4kE*PwX*%nrAoIq&}g8Pi$|l}L?B9-l}mjoBIIj7yn>@>c08 zjp`1Z4tEo@KaxIefbMZquD0W)lIdw{ZU#j|XKfz=xE-D{)x5OSpTbePsWV)tFar{I z;k=iInyIiw_9SE+brIG{PoF+*gs`-I-OZt=>54TZa1YtFOKyiVwY9N9_h);YZ}jg9yI2kXj%$LQ9dvTO9mG zsR~DjvY@CrxG~I?ld>$a@9G8BLlMi2#w1&BzB&U4v#F)+YBP>WvKtm|FYoehqkIYI z!g)`(qEEVrVWYUJrwZYf9=AYL7NB4ght22kyP9pSipWBMGg9+2{HDg24oKvRY@u}< z3kx(&2rzpLaQq*Mm5{+XeZi`L-@q`F(IGXJ%I{N`Ff!QhfEO0k!^zEqvWSb%$>jvX z?mtbeCl*>WlPyC*wG`q}-VhBBl4#F`4==b5fZLvSXes-39y8e3IO=UFZ@ziX{K(dj z4s+(r85=};%jV{$HuVZ?ufL|Wc9R?)yT#kLw#Edrg{+j{xKAa4sK~0}E9=xpKNXqJ zf}E1ejlgH2GUr)SfR0YU(cviK?Bq+VG_z?`%KG$oW$lVcS-=>9xJ#GMr%$9)=qb(v zoM}<)nrqH%)vtl!)TvWH{NWG3`OR-W{P2li{Nm?knKF(Jz6HA?X&0`#fAy}(=IC+l zSe;n^njAA|Igh^Sra@=t0pRcd{_j1?YxKC`hU=LRYni%kpj10~=low5yO+*z0V1n2~ z6WH@c9^3PnpP-@9vH}4(seT?4j>Thrl?!P6Xy(yug4_Lh@R-KD&e(o)D9fl+hf(9; zaB#+*AOe7(WX&%Cr<<%*5MGxhiEH@?nC4H>y4MhLgsaxwl>Hciv4EHt{Q=EfH!hdS zNUh}LO-2+f4Dzb)T#38%QK&Vh7HS01eIC>6@t``7ta*wtr-ErRdHHXjds~)%Qp_{N z-bihhb7a-Y;laQf8g&7W?1SDn>?`u3fQd+zb#iu>-q>uy(3dn7O;N?E1DEi_?H zxPDUGj&>Ci+2%f5(l#>x@d2d=J9gF2qi!^<+wSuqel?n^Ip5dleneZUoBNg4{?!Ob zgHMgiu-WZob`ALS*59R`4Bo_WUH%9K|hvx>%atx9h1ou5%^^F5+#0i71FE}WV{zW?s~ zdZ`JT$(k2QvZ?u?NKJnjD0rU z^$huP@~XcQy_WPBKmUdH0KVt$yB!NK`rvzSWWJ_*vi&%Qx+~5IffRfW5oO`swnNSC z_j*9giXp7l+cZ@LsqCe+B@rwMMg zKJ1dtPjx6xSe9h3;YZjfwuNz3(vn`p7BIEvn?ma_Z_)Uk8-c{*4uBj%_=W4 z&7^XgF@BsDp0~xspcQoGHU28TEwnw$HkAL!fYR(Cl@a*7tE92k`u?)S9cA9?H_$}8 z`v`CY{;LF(i)Oo~*#p9$385Y4p|R#VplPTrXf_1II}W<*$xgNOKGyOFk3at3{^U;{ zdE^nhow9J)W~t14MuY*;Q2_xQ6<_g)4@8zFTBm{!shK%Chv?{$!-qZbE9`ip$wrWQ73MC- z)Q*7xi9)4nYv=<-2-|4u7lp#}f{JtJ-n@M2CXZL|&-K;3=+aoBVqu+u#*hLyIk)lO zh4PzEKFOro+eDpeSKT98fm!*&Pq~&=_%B-@QfY=9uycS#U^0BP!NUQ`V%DBlWxxo7 zgA>#L-U13FPXlK}NV|pR4z7xuO#fQ)C>?#-MZ3`m(+43e4(LYeQ*UaD&U(XLwQSkr zNxx;u-z*lK>r0cC@5`qv-J$-{qmO?3+u!mME#A1pB+c`@6sV z%fHNPz78I=PSHtpm7e=660K$CtAtfg=Y08JUw-!4XZ6G0^Jy!fdW2=qz^1Z(R%Ffa zSeN>Mu#i{Wl<{OGy1n6Kzq+knT|TLL#S1Y1rf~u$N0jWZ0e7h`kVU0-URyfulj!s8 z-re`8W3Gh8tPU5mvj^uYB9|k{koH7^hdcVxij zZ4#x~+!62~K?q zC6RM1uPHr!^7PZsJZ*7c7IOCN+1$6Q*V5hZ9k%z~=bSmyo&6c(wIC6pKh<SXwCpz!0b$!&iKJ( zP%=uSKW2cfMlg018k`}BDjq;%=1c%Kapoa{a#9;pEVN330*W>URHn;EjvO(~XAp~2 zaDvAA?A!NFeWcz!$KE#pHy}41Qu3fM9PP~3^f-6@^*8i;6IduFE_3IY%P57iaG-&^ zl;IOdB}NKu&|!|$D+O@ETE{@D9xJInHNK>jax${=C5d364KK$ZbvI^C&23Q`lZ(y0 zJn@DA2*P-Rnf{;9%vHx0oTZk9H#kROk-iFsl;dTIt-Q5WsP9xysXG(hXD;N^HHEoH z)3{26@ujXuSU7aMxvp8+NjmSq?4#TxQX^Nr3H-~T-q&scvGD~p2; zJ8iQi>Zt-xCY$I8J2^1j(X36pnI6Ji)!{!=s2emw&=~1Hr42i`?dK(zE9p{~ccs*! ziUSSi8cXY5izwpA>zV_9xeC4ZFv7A&HJFhSt?&I1a ztU|+`jrRv1e6X`u)_7Y60E5gEKX~GX>+)EQ=YDU$^A5YEok1O7xA;ccg9`XE$>3(z z?`5a3)b?Qmmj|hq};`E!l-BQ925peHl)!aF)x6!d6yg z`^MG#9E5hp;S1P#Fe?;bNA9}o&TH$VhK&H<3nct|b?;t7v~L?29$Z(w?z)ujcmVx~ zWw$${*DxFebZ~Of*10V+Ym01OILpAsa0m&*Qq1I8H#-i{PHz z)V7C6kuF}YkDh&m7A&hY&9Sku;T0xscNy~aCAWv3fBw16%}x8QkN@EDXP$k=Uz_91I)0xwK-L#>4n#mNgnx`#&#`@wGZu&tpt1W@T4d-u9T4JWkwaL@KMs6Lbp2@J+z2|_Ybpxv( z!{6TzC>zELpyxG}Z4_FucyGP^mJjOLifnt*9D?JJfAZu{Jm}&U1GTheZ40_Lh@^5$ zh8r@^cr!a&I=CcZbHcKCE#uxl{nI}ksDJY}fAhD0`~R|K7RVdNj@^38t;)mn;2Y*L zJTd~diBVfd&?L($TQ^e&eUC4d>#{Xiu}fL7*_^K$2F)mk+VyU8ep8zT->k_NGgxVp zJQ{83cX7?YTy$hNa4>J=W4O(Y&HfC2y$(=!f$S02Q&e!+CHag2W#3~`qX?x6=odP1 zIp@hMOEWc(ZDvsOu!8csA7zXJS~=kW{GXhpi8Ad;Wc-6L@-=drptQVuX=+sk@gXFf z^vMM|N=bL8>WelGvdeH}o*SjpAdze^(<*6?Y7k>}=~+ji8@n5)u+|tz#wrWE=p=oD z6=A@@>re9*cE)V-&%2xQP3<^vtde=;khxa^@FVORRS=BXbG$v zI4y7+`b%H>lCVbC`W^xCayp9xSBa>uvnx*?;71mexfC=rK%m9^7O$ne1yqsFwH^NJ zg?uihsD9nYYT{`ejktBoq4asGilj6$#mNFMBXx4*H_@3=hzznQ$JNcT%B-(UT31p} zc%r`TM^Ynq#%<}7ZZF~8Mw)T}W5BzIfu`qGU#!iX0!UxZ>2vHv>-cT5%>&(TYlx5l zmCU(TX=o!ioe(b9Jn-?)C!pAzz5Bcb6Ib*N92J2+A2u)XRz#XY|lf^a;Ru0k-Bb;-60fJU{dt(-jNWPZGN&SYOD%63kuN`5v159(yt?$ z76cz}OEy3{+TzoeuK!1MckQxiP_w^lS$w(dfDvwnf&Kf;7H$5hk%?hb;JX9Vr^gNn zax~z_|af)k} z4;jGXgB--s=hP7zSE)IJh?HiIgj)!W(1RbHcM_wfomWOCb#S6-dU74MIsLpoI6OrM z-qt`B8j!OD+(z+ZFSa<`Mtv`4IY2qFG;+ZsO3In8rxWJS1KxRUdWLF^v@a{R3?5`y zqwqJk*uaSY3}k{@q@u=wr2Gwf?VD2vLTe+!EeQ z@>_d%LSMn)a4U@}tym>@D9Y_E=ViZxU3to*RP`GWTs}bd&v)+t|) z&^sP_=tRO>-D&_p{rms?eNTRQzlv#*RSC>Bj<@Dr#V~68FhH66`+^ulDNR3-T{T?K z*j{ji=4N14?U~T$c+t*CWI8=?l%BjW;^>I0Q+HOra#T;F8e@vuF2fufFaI7yDzHVu0-8 zwF;AjJy|@1nwHqJ$37UfK%v}1?kd>DtAIdNAoLS%ogmIU8=;P^V>9~tuPmm%EFZKI zU9`~Wz?|lK=TVcCXXYUXIe>5qIi*lSJd46nO#v>*9J|*_Y?VzuI?W=G7 z@i`wWc<#C9++5%+2R1i1bxYSwH#Uww@W2BU!^B9}=<$YRXmPdT$JOmWS~>yt<`2e+ zI+9;^(Y}UNavE(y8T!4R<WHA<| zrK-8oXdNknrO1`g0_irzl0b*SJBeRHNN?y$DS7&XikzmY%2w8Cf2nfEMubSW!)zBi zDey&eO!z?Ml32OZ=+*3g;Iv;UrOcA{*nG=8zeG-jGCS%1W2~*~JwH2>R|dWK>T9ps z(!na@-gQ)j3bf#=Djwg?A_9)VpuFw3ux8uL7B5mrO(k&sL_3J z)$JP-(W+_a^ zCcpN6a`GpUm7p8BJ5+rz1|Z;I0@R^$LQ6MzUa}Q$op(u+TR(WJ@c3TVpXcN_540lOo+D1@?8RYaz}K?ZMa=%g@Ea6idX4~}ZC&@suJ zULGrH+DZ$B+6dBuSU@pt>v<#7|C-y9Bb?#zAlzIjiv!Vqr2NuLFM)#?Gc8|=+w7)l8)dWtWU_v``D7WlC1LmN;uKP7kBvF$lw?x3ZBx>aMXG&jS>6zj>f8rMh=vzvcK zLG@FSiW5kpQ_CcPB*qGuD`>TIsP%2J?DJfMj+aQ3VcJf?a96D>HCMlXGmLdhwXEQ{ zl8J4zfzYX2tSvNt+N)eKHBrcqI%QfsRjqkTu9$&H0ZLlNb))b%)q9Q)qp;E_{d?G!X3$12{dz9XZ`>67ys&= zTvjOL(zE82U!0-P;SufnN3`}T3Wq>6WAvw-DYA=vti*IB(OeI1Ka(_xZi@tEoM<+U zL<%9`h&~;?nCRb;;D1LOR5??< ztSwSm@-#-~*@s$T*hLJVbu19H(o1+fZIA?_E+JM zkY~K-8#qm&ZFdYj<0{K^#+%j;LV1AxDIJ$!o(;b2=|32Ck<$PZWE)7h%q~*$r3V&c zyq?lv5@V7QxNy-keFSU_IG-bd51r+BXIw5>d+*iFd4Gwy&(Wht^BC|QcNi6@x79Xk z+72Dst2DAh&Cx#=N!>j1}bhJ;h6PW|}lAK!b=VP|Vc zH;&$Yr?*YGLV9h++PVtT6`!h)=zkpdX*kzN-L(15AJ^N51wbm)3S233e1;6EY}Llf zHPYrlNX@PYB}xsr8C=@5hzW{GUgU9`$t&_D%tvZO1$lu-co5^tWf8e#XwJ zkxHK_4#LaGn@Y^CfvePP7iT$7))+>u*-m>3RpY8yR4S$;RD=tsYgc#>6Dv1?vl~}Vh)sk=)u>H>qs2O0bD&lR{VAK+5f$&cW0Ymlep*^a z!yyXHOsx52F;|PBC>XpNFnFQgvyXy|Xj}i-aQL+zQi}{Sl_fx~;K^PT14zYdU@eRY zGvxa>v^i*`(Sg|yv88Kxv6(UzS{%=Ixln`SuNtTEW9y6CB&6i{2~@NT1*~kED!~BL zxQP%Qmyw|4Pv)9nuo^;Muw^U%{d}a_uHCu$6LNZyVL*#mM;i=S>dnnf&jXt}G2Gs- z`_4P>Y9OQ#OcA@kM{jtwxWTbd}G zsc)I})=<7-U3<7aRt<(zy(_rJxkBQn0ROt^C=#h9wX{jMP_eVz0?K41%?=}-w9z{I z@E2KwknRkBEdAyxYKS>F>A;eQWGTfMfyVDr0BJ;rqhniLXN=A)lBOs{!KN*{q~0QjrF z`m0kXPd@tSqZZ5Ij9p%|$G~0&R0fXz_TuJ?PyP5Q9YCqNN>_Dc7ibrOK8d zWUg#5(a~O=t69=BgFLAYsSTX~PnJnEz@ls=662r%& zh_SbZ0RJjA{)AasNGfWkM1GMiGQp`bGS@DGDRra=M-fv)l(cz778aU*A#@aG2PAct zCM4Qy^BbpIr&Cjl8jz}d1d5F`RsKZ$1T?i?kgG{cZA+bNRPCM2h*8lB?1E6!KeM>{ z-~%%cV}OxBfa?VT%-+Zn?afnzfsw#lNsI(O4$78CP&4zBV&(o>O2zpRhTxBrkqdlAK(`&Z z00yLXpm}KK2e%!7CSZ9@2L(iD_z4@b(3Ks1eHY@PC zQFJGXriPs)F~LHK-}qDgL#?J5Z7)j0I^RUIXhdFBT#`Zo8!4myPo>Nyl=d`lRb)(I zT!=}nWP}e$KQts$gVm|? z)Q#h=R}TSlw4#U#l!+}rE6Zq7b-LhiXi}@FU=2CVTl>LHI+=#e%?b|2X*i= zf62x^vW-Cbzw@2%*a8LI?lJ(JI(6#*{vZFtLotqjW8kWlf|_RExh+vwlsT6vm9t-} zFQf93sa$oVX(|UGI?C>(5ha+;QU%Vf9v6eH9kAUXuDb-HRG@Rh=cW5BSEc4vX|Q(!y$rZR88{f>?l zMXqBseO$HD2STDRDc9M*nKFPc$qHi$yaSySNMKPtVGdkepw)Ov ztBJiZ%wg4AUHKUTrfwL73;-AHHlJOK=B=yNslxOHTvvh|tG*|1 zSF;Fk6ztBMtM=fTYgT8eUFvL<3dFADUAgWe9ze%w)*t@iANHnYGqChC09X$H<3IhA zd!~@`v|gGiIAN|s%N%o9@%qnm&pmtg>`RK&(}G^R3VLtyR$JnLn=|EiEeg`e`~>H< zVWWv^ol2^V#hBjMkjfYWgGdL;judLed8yO9tf^d67AXp92p{Ji1y?~~PCM%FCV=NJ ze1weg*5p)u8wZ-OQvpE88i{@H_CQ!(>%!Ek{?~p$#ud3*2Qii~mwr?BeIE8j8)9Mt zsoDbD(=byi*pt)v=rRs@C}s zTq7`&*5D%=xh!3DV)bvJbzmL<6u11B9=62P3d^W)ciTqWA+@Z8rkP5atFsnuHUR}X zsq5SR0Cv*wmpFi;Ty&epRt%5@Z|g5<*|{oAOJmmCX?+n&I|F3Q0N^j6X!JAyR5>ee z@FUQyj_QDP-T9qll}lN}SrLOpq|Tf&Nc4XsqT!R8`Ba{7 z%~SYg=_8y$hDPdXzE^4dAdb5%FRiZ*^rJ5V2=;|bm2}p8C zF%b=E-il!8s~sG#{l9$qwM!Rl1-x(4M`Dt|`E8_pTZX-WY;a~6lxgomDrfn?U}&KI zyKG!!jB#~^^`LOmo6O4kmpab8sdT;62pe2#)O=?tFzrir^`(Mz7(blqRHpVW)rBCi zdiXZICjXPCdQbvI0RqMp#I=P&QzBQwnVt)Q(^2>^I|@R>L??U`tGbmGMXB{o!@ES` z-0!SU&0rD3hD?1*cB<%+NV2{D`s>X7Gr`&F2+i#KwO3#Jw_n<2$;W|T{`t$VzxKMD z2!;XHT(4;M@PjukB8yTKQRX^^8%b#Ds5oKx9Ak~(i$DFT>kMvr9^Ke@;P~;4jSU}@ z(aj)pt{)=>S-leQg`4pHk%ExY+=i@M4*6`^#@E08+dr*~(a${d^y$-QTpr!r+*B_w zb@52&(W6JLw(aLO14C|G>-yTZ)CiattpcsER>N+wt5LW)mQbQJ= z3R?V()CFwaLc&qUE;M7zh4a*dBSqs%*UJ|bJ*#-%7w$iN@4aU>HzAqxD?DW3mB10< zGBDKBOGm%(1-)Qd?lPPK;Gh2KpLDid?Az)}NCBnYW`_?w0k688<*j}fgl$3x*$!+P zYB&>H{YLW0kab3a6j!Yw6l6ftclE3X0IKgXow-1i@LYJeOYD@QWm_>_<#ck|uZyh{ zc@j)nwKKq1FHz#-he$brHg=GGD8|i`ciw$h2iBk4^tXwb%zX{QjFJ5@RRUy*gw0PG z9rP5|FNZ)D!7i~mTKHk5kz1eCpWc=71tI(a2l1ksUwP|KJhbrwa7h2H^5jWC4Ma1=(C4m3n~>14+n3E zbkt+DIOBfD9d|zV*kk|tZ{I%olao*V_{T@w22JAbB(Bg z=Id^-1PZq$ZLJt~Ty(pO?v^VZ5`qrq{(AUXlI`dka3Fwp9YGo=N?0>ISI}5mW*PMy z0NCZ8}xH4>w^?STvcrcnlftVWGp zTc+moxV^|n#MrWh`r50n{#$X7F{;{Wfi?BOhAb<2CCJn!O-BX>&l6r*MUv)9Wr$aQ zt6^c1GPxUbdtp9^mna)?$yCXhIGbWgi#cfK$-TuVFBH|}WDYN79rS8AY=?esHNbZv zBu|Q6cddpIg&$k9Coc~2*wJfmy!PfBZ!%s|&1z*QHzNl-;q4b<;HWVil&4+z$}29S zh*T=9cX8jP{luBqhW4_CjkSH1k8-A( zscyovhCmT$`p9-JzAzvTn}N;(((ILB7mZkh4dj}Xlg9_IzxEmn4%sH8 zLJb4f*X~)_Wwm(nRwbX@F}vqjd5+$bAk@SzbbpO)eTGWIf+ zs;~N>FKS@cG|;%_790cGNYWT8PMtbsSztfso_p>-a^ybOer`E*i&Tw=TJPz%>Kbbi zq=&{_;}9mZCwSn%k;6y4ZrUeaoeDhj%+o*m(UXoG&YnFh`s9;O+BJIMfd}~J6F1*< zvnu0_Q?KTV-;NB2FlX4W`n%RHzbd(p%fEfwXPKib$Omi%8xR$5_z1vh$TA>xiLjmc z1j&K*9uKt)gf3`A1#}4hXa)VG!~AxmGwNSjy!vEJk<`xEfu@P84s7>FEhH8D!4#04 zI$b*GfzG}AFYnh2Ks+tO{Fq#tgC0J7_{N)VeC5?wGP^%;u;f8>`aY7=RyI=BvUl8h z=k{_3WZ#)HXV@T5Gn(CKA$mO0h-o?ZS4*V3ZS=0!UVojBOFbuLR?Fd1pnHBpx>g_^ zZQ=v1qZxQu_R@Qp1}?Mxp54dd7aK-eHpZ!HUNz%y>@b$`nudjHvrwp8I+0k-)8Z9- ziXo(CsePE*5^r2V01y~5nDnfXK^cff^9C{f+=m)`WzZ8qI;~X0iBtx{yEHPV+r39? znffcU?HV+e>(aaLR4FQI)ny|q=eUGWM~?Qe@PmW7A;PgCAr5oHDGtxg=m>~LHD?Rn zq5u)FYRi<@Kwv+8Zyrq$AR;Zk-}|b|f@e@tL4HXL9eFF3f|R24rddh#ux@r6GmU73 z7Gp;tuLVS#H)L5}_L_h0Tn=M>QcVe#0)TemO{0+_77iV1=5`O`UYGN;r6aYNjlCvk ztFeyqI~VKkaEtuy$feJ`_a0xyEOzaWdaqoaEo($cis%6Ve7G3;+9oppdgYZ@z2MK{ zps(V2U)D`G++@>Ft~z;3>l^!t(~yoIdREtwKEiVow32x6!3Vzet#6+?ed@^{J!vVR z>v?gOhsz8wCr&(MBslC%)VJN{jYgRX=x=5=^DG2?Rc?6>e+f~w0snUo=x?|m_9K>L zbl3%R_;1%RVF7-(@9+W1q2VKUr;3h1lM-zroz&r0#ZIc;R3?3v*aU}K)I{O)J5b@t ztOucRlnSHBX0WKXst%U)ILuJ;rXz|}f!957mAJ#dg-i8fhVpauZf zfov@*UMWk>fTJUmm#Mmb;hw^Jsf+mD?|tukx}1GUv(%{Fg3&M(gE=`RQE^>D+^*~9 z=H}0T@pC;BM6YX2m6Tl7Ip=)&ZQ#d1v~gh$e7TgLy!8_msL{KD(JAqn_+T{y#`3&( zkZt7Bq-DmY(7dGt`OCE^24q!Gfiy1Lp?fo7FtbvNO5_BEdDUhhBqI!N$6pev1l2C6 z^%4sLofxQ*4BYNj?sU!5XQ`PBFs8Cp>j+C7{7l(P$y(tGaizo^rH-Y8NaVLk`4R4z zYE8frl!TC&dqNXw1egtn(wRXzFwjOIwXU=R|iyNh6F{t1Ndm36ex5?_h zO1Jwg&Xlwai!LFOwnGMR$E2o~K1YwQ;IAGag6QsLsdPkiRf|wG;jU(QU)n4TU7Oo2m z6ab$8sP*L^F?z*?a{0+Je`~h+X}>w_dNpLNzM}17a%(aKtyO|`Id<%r*%F1p|MlKl zR{NZ7Ti}zI(ye5_^PTTl1T4#43-3L0R#f0O{a+5?mdft{SyuiXqh1$DKK= zV{zOL5Psx+19LRBifCZ#v)+G3@I-fvQDLOLwD-3FV0ix zi}|F)H3zTJ>5d;e?#j>u4;;7uqjrR#Kc75#^7QG`)|Yt~e4oZ=Iha<1GvwUWUyBYP zc)NzI`ArAH8hk|4G3MShfFp3V0RYKlUS$uRhHPB{Z0BJxJQx#bG`?mU@WIIvC_LaJ z&_F&_Y;Dw?Vy%s)rP@rizqm}+C%;=(qXJZ~jn{U(=O1&p;Po*8kc=9IUxZek(xnWR zMqqn=B@MB3Aj({Lw7;cMqR1-Wwjn*FC)9>c(%9%;{k8hzz#QYd-~BFYGYZ`v*y~Lx z^#0h!G3(h*9)eEMTlGNk<|FRJyEbP4P`1LjW_@IDUPZT%+DYLCLgy{3+zJ#6I1`K< zDjciWK%5A~Z}nQRT0)y96u>P*@85bb_YmIC!ifyUD$8jsvsIn*Kq`w%?E$L<&L)RD=MG2q$3Zw~n* zsRQi~nA$XZy%=|)zB^<;nzC9e{C%Q|F_^I3x&rvqKz4v(ZICt+e1NLRlh4Q$9#2|;Q?6|3J)NQ?9!kYT|P5Hs%3 zH7eS!f!(shPfNSB+%8#yo35JN>rPo~Y!5!kVI;LoFwY5hv@yNc77*7)0{;N-y8ABb zYU9*RoM`|cuWZo7Z~b($|I1(g@}R$CAn05VG%3t13b);|BQR&hgd~!pri`A7_V9vN zl}zfwtwk3p|9=XWBB)5@J8y7zm_OWhj;n#Rgv%Sb{^2zQOo`d654_!Y!%Z>$d29VO zW|Ks8=$3p-j1O2ZX79=b)TbRyZ9Zg&?aL zGe{!~q_@noD*XsVlv0sl?CZ7Hns-~03X);xG6%y9eZ9E%VqeDuZqE*X-oDvSfSzyH2%eao68Ha4u~^?75`)S7+yZ<4|K50=sl z^D1y8gN*S1w!oDN9qOj^MpLjW0pfuFff{S`!d9Dt0RVSr@= z5e`rT7Li3 zUt8GUaR#7WU>XKoDy7vrmCYi%qxOY*`mdQdXp-a-X8O|`${FhcGve>h#yyURH$zT6 z>M<*38o3%ePwnH9gcaI4!#7$uTi)2%c=XXP*%vx|?_m_(bF~`y>E@;$=QXG5q2gUp zmG*}e2&wYBE%*=`(oz~e_y}Jm5Qz?^x8Z%v77#Y(?D(5%Tun46;c&ad+ZwPTa)A$j z5E%hAZG^_}iVuJ29(>XUFRf0nqfw}S_d{t|J-jbvaDdhKdwY4svrcxE+D!mOmf@4OE>G5YDvT61WUUsvp`D)$78w%;SN#O$0U%wV${K+}1H9q@&_tR-@>YJ{a9xmSlj;(; z0;lF_AfE)%?Q7br&-YAL+h3SeA~!>V=2(8eQ^(9|6-P79>VI{B)Ai z6I{WRg&sqc_Hn^Kj_TF}U6hxf5BKlaGOkKB9j zJyt51F$|nOeR^|q(~uyFNXDNkC5l#ySLLP9YJe`KTW9#4G@@4ul>U_l?u79v27st- zZPV4Hbr8b_*UG#Ej&Q0`Za}UQKuRiq5^QN~hZ{IVA;7hsv-twuG;;$b-cUZNLZ&u= z{Af`H8dA+QjRd7n!GYOM7)vSZ%G3YBb_cqG!o$rnpkifTGS)|oqWkuJ;~U@bqN=ONt&{d1ug@M?`8op#;o+*~Moa)3u~WPAm9m%0jR8Os zz8kqKAS@7GE5H|S2e~na-|$et0avHNLb8UVh9sAEVIULmN4bKTaum%RBvi$Hq5%MA zv)#FxnGUDG$rTTCL@GDm!<%(1d5?=l9 z70r>VJ{BfZNLVRRd1d&((I2?w1}2;xp3aL{sYv*;&R(%P;JvVitK3gE6T& zi9BjcO)jILvjN|#e4t)H4bJ+%@WKn5o130aaC8xI=R;|6iZUMA!tI9ZMqBftuR(T< zmabh9^V1Fh&E&G6K+3HFJ4&6@UHUTh>S5xA&{;a%_`>bhq5+Q4xO6puG{Uq6H1*up zv$Fmm-dzR7N{{SGyxIX;FETy5mza_lY%XTU38a1k3@EDdBBRQ#pT|521Y?O{MZ79 z2$J=x4ko9nIPpa_``nC8<_)6hK1g!l(6xsE zuRD0%8`tMmHb`-oE*+pnEhy1u_wq;Qja2x|`_*b6+(}S#sJJd#R9MD#&1B;YI!%o) z3%}JArte|-jEwrGB8~xwBJZs3FUo)b$dUPMU^5lWmg!Agp=#Gfw_-pRRdkDS&b0b% zuArH#FuU~KPd_WycsbTDAZ?n_55?Bn*ti4)AR7SysVug2}l4Dpg44(Ug% zXKJ74)Xjb)&T6>H!bi~}7S6NC>8=ba{CUZurdbVq&3xq-ur{K&rz_NYii3 zRauKrH^2G}J(#kGkMyp_?NsU&9V`bva~r1DvUfd#le$cmO1=U$oG+I)(3b~9pu<(j zZdWt>BfHHr6RGKdEcIVRhvClY3>Pe6uyaRFHe5me8@|R@^;cb4~X(R z2R7F~kfZ*%+6V%f=55ins7f_=Z4GJc(lOZOUH&n`IC=t2@Al1Y=&sFBHU9jh&KqFqb zRlk6wHIwcFmf?S`9gICOJtv4w()YuOrMrh`7j)N%NsO@$1Z z%uX(**49(jw`oa?`7ztiCc@>cVih-J)e^&>Qr^AzuD6jB4iS0d#n*1V)vW)Uzw^!K zo`3GgKmM^-*Lf)C```b*`$Av1{|m<+IJR+g!!x2dB2&6Q&)1pJ$RAreB@)E!0yhPXzY5exd9db1}Z)&C~dyTjwDQJ^hp+(Ia zIDSwW`Da7lJSb71bIdsFzYw5touKXl9bHQbx3$Qi3_2e}(Fm?jfa@tB@|t8&AxWH< zP|?!$B6o`KGntXm+$zP_3TTB^T_~+mtb#{P>lsBCB6_(H6)%VNuEWTVj}CWWR25Jz z(IsT$x>8^!cBS-R100_i(`^Q0&1cuK`0dtGuLyeCW>RH@30{tc(wuo?49@Hrkgz3N zE{kLpwa1i=)ze7kxth@PM%oWSZH(0U^VfW^@56M-lp13ZxNBZ@!Aro0%sB{`eLA#s zs7q}rG((>n5FHLsUeC*QDue4vqz(5T5|aI5t{OKTWcmt6KgH5dP(~AJf8ItP_qZ+A+Ypj%2 z^-c9lYuY~Y?f7uiK<|aw2LtDQ6xP>?qXPbWqfkN-SI#z41WYPqilAKXfd?NrcKp~M z{=pyG2zug2Pdxtk<0pS|^6c5OK2yB0aqQuTPdGNPQ1F^L$RR>8R|adXmj6D{mQR42?7a9sY)%pN>ggyPqeJ zBML7OI%$bnXpJSiRc#iwrC~WOB0W!_5}n&7h5@p7tyUI8g=-z^q!mMLwyF_t_*G~K zLr`mf0hODJOGJ1n>gE%C39L+$*Z3oK1WK{#LasVT_3CJ`bpuYJDSdh)W)N}+kIj#n z_*BLe-JPnVDy0j0xriFwd-z`Wk{tq@z`BG(6E4ZK7Wm%bhQ z?x#DaUg1T}`C6-~d#32=C&E%$=I*~T*Jx0ExA+M65pHDLF5z+#;1PwhUvE}9P#Zo{ z27<6wYd@{B=>KRJ`AOjYqo|Zt^o<18e+tdGR<@*mrcqNP!yK8BG8m}7X9zIlQ3+2i zu=RDB`xe4*aBW}c^y$-fXzdZnPK^3>mA9fZ2u}PcWLBrOq(sBR5-bw@DNABAR-I%F z)D${nHE@EIq7I0_cK9GN0t2TZ+ojr&CA{>bB$-3Rn6@YhL8sZP=|v;ARKkN^RByo=NsP;zEYt}_})MJvp<9S!zZ5bXqmmsWm>Z`wQhCr zdIm*cShYbWNXGDzki7rG6X@Y!EXoB(M|$vB1=DomRrl%IJ6FR%~o3+*HDo3 z-ZEz{^BDuY*U2zISF{`)Q(&_;VIsv2BIvU&{HWFRN#Rr=<#x&ZZlndbbLmm~QI`&P zQu}K)5>*RHe69*yp*OHohcA%QGd^XTrI?J@*<|Se%7CKSUgqsoJNuq`rCdjk9zA^c zum@O7Y7B=esFUenzV*nQ834Q;i2VoQnxG!qj3t`xJ-gcVNnh=5T6c}o)$3n#*cqEi z=DgHo#{nSBYqj@3c+X&G5n$=2BJE23Gka}0cTM?S%?37RS2l2iPu3@qaD1`dyZTO% z;_(BH&t|NfdE_N)aP`VZ>dxou_Lw45`(^k4t$gAYEqz0e&YnQS@% zHoZ~_bBZ02z)i&Lv8oczT+*;{VUC73GFi*g5?=$3=txCJcqyY6%D}vOKvT=kHJz&? zfAZ8pP+;W3WVcOoyG&}FZZP4_-cgL1OtjrAw7de^C7_JpKEC(<+ZvBKvrS|+ z=UCr!s&0MQ7drRWxi?<-O6k{ib=(|e{S5=DvXWN4Zj|0_fd@{B+hyCe7zoY$@WEwi zdGb+%Uqqr8=_>97<>}g-Mr6ySHXO6S9IN^rA^E2q`YYN2|Zpq8iVH6aK6rbzVoH>)` zP_Wq1Fw!M^V3_zA8y1PpQFpz%PL&}9#|Xh^YEd8!TExeR*~7ypPPh*AwXc578%BTl z!yodQ26qHq8S+TMBVT;v$l)UtZ0EUQvC%wyPCl`up5;=7xyoG}#ZV(aLfg(j=WRGi z3akV;W3NF*LEy`zVdVhZ=2x;v{$T@Oft2!8yH(RAWV9$J4NBUOm)F2GcxEJ31K|g8 zCnu@FA+Z%_OSJ1S`-RF&X$FWy>jmZ(0d?)z?o7T+cl8edtg#cVb>qUJfgMKE&%xOm zUMv6ssIylFT(iITuT4}ew%zP%=L3S z%cg8dc@*kamtbzoivg_MuemL+By&z|rIpcQ|NdMp{qRHdQ>aUj^{)T0UDaSL1iK?O zZ=(u9sWn7%JK$>psf_@6B<=Q^yXbqJ3Dk%4Vh!tjI9`2fz;So%zHEAL5sBBwZl$AQ zct8^h4x4@hEs0c+{c@3!DhiN~0{t;ZX5_&{<86?V9_ShYL+}(^{on-yeQM$ zzeX7IoeVNnIFMX!u^-Z~bcu3`n|3m66#D>4J)$V1IhhA&+7?ZPecVsJ2`vBdo1nHlKfo6JS#2?6rE(4NARYzbT42VD*wNC2pR5PWa ztP#p2tlAHenrPC?97bj}08DYvuvBRw-$EUH7AS2{Da$sPATuXHY&5=hpLpBWi<%%` zm51f2@FW3Aj1PaB=6>XY`Mb6`jH&uO{c$r$e|AmCUzQ9o&$RIUGV+hm^m zuSzY}E7OYeuFjg@I88jXXV;|YbWu+wsw*TK)yUicps*KJExiek$*^d5G8b*yYU;Jm zws&s5<<|S|zt0-L^xu0(+NhPCwLHjS3Co*Nu)g>>de>mGN|-$30hdu?cA~O~h0$`I zA}CKokfIY=$QYi@as1g)wfPo6w&0p!NOazZ;S5^LuDsxY=%C5(a7(D^Lzp-#sf}*j z1S3Pz@Ykt@S|l?#aa0;?Vs#?aXEG(=YNgns`f7q{92AdKF!383`KqON8&RNl-h0O| zaOTVzCj?%&P36oPbM}yJh5TLd9$nO{6fNtI{Hqz0TKa83>M;T5Eug4lyZTMXEC+Lh z0a^khFp?S`va=hI0bD{<86qFBTSP!qn2TUbQzIp?Hs96s*$W{Oa63pZJ` zRp7kG5gM@DY2W-W|HT-;R^nBn82}{Ho9}(^dp0-e%9XJ3qj>$3-k6Bi6Z5ZmgFF1N zI`@#yE=md{(y2?DC7(1^YB(#w$Tb2mIyHVoQR-GUy>RlmiO8jrQ~DvH#88|Ow=E&qG0D?1wXu2RBt2`b&7`B7FE%?DXV2Yh<~=D?Q)RRjgNXn8tR zg4N4nhN_b#Mbea-v=Htr!|$>XZ2IQ*-u|y@cCk&U`|=_Z6igDM-{<5$8sh-KbGZf# zq~uLaXV*0ywZlcaW|@1wuGoI@x5aCmW~uc@|H>1+W&I5R)!%CVKbqM(;L;__k^b6@ zj)S72Ax+Y#A51o%Qac>9I*Lqdt+^851a7?&sU;@LIS|t2Yx)9O1bf~(DOKT47LYl@ zQde0W)b^L~#s?pm9jM$}GCrW31NBJL6cpLMa}4FOz3Tf$}1#FaH@O=`^tUnIc^8sU(e028Av z9S^7&!)L?WJl6-#->EZCMv$t>0nPAiwBLVVzvf3Un$^?lNWQ{EGiy?nIn}6|B8DTm zN+Ub9!)x4*C>-JTn?mFBOV?8c#HLtRCswO~({JOX*MYVnY?{ehIp4|^w6|>i=ltW) zA=Lyovwr8@cf2;$WA5I7W*A`oBZ_)$syeo#jqPT@q!=i4Ez(6A zag^Rgv{WLbHCJ*AY|;+%SPi;DLbH|JPswc(=9I6t=z?Dsxd!7L4`kEq!g&vvczWQd zj~hSq;Ks%VA(I1>vAMa~uaUu<0Y){)PS=W!VqsnZO#F2J2~G+OV**pf2M}0lC5HKm zHX8m%6l6FQgqKMm4IDVjz-Z+GOfj|j-Vg}E@mKo5Cm9Al&~hps7j@1c5U&P+RuG3x zus|y6cXAg#s`uA$-?v_qI9V83gBn3Hv%eEoK8u?4QYf9Q|x3e2hz4mE0w}$ zQA$DiRsb3_1HfBO(e|G>@z5Xq!5_dLWqoX58UWzAHT>+e&njb&^=>8{JFt!)`#%N% z_)PyJ~`WevmKPT8P|0%6zFZ53Kwk*>Mf^Lwam`6V{s7=1#TH_ z={3KN&@AysQsa+|0#rPN$^@m;B$i$jSn8X92`^ldb&Q~8X~K19qC6}-bqQpp;3u(tVmD;@LxuO90RxkylXIMHJ}K z#{nE;!yG_-s_EiqP2Z%Dwo(=->%quls{ss$*m+RY%aSpcS{mcG_Nb+1P3^dPjY%3eX7 zs0%;dQhuE${;~!1^3P3`XzzEw`(3vHwikL8$ZE&mNJ29I~#2)h>VMQlu{B&Y`7%BLR1WOp`6n?2tGdCU#?h_Xfka zVZ!1_-t%5I3Ya*rozOrO-PeZc(B6wwMky>(hLF)cnlX^UVKMgcmBltEV>=8B=mj1S zzMK~_aVgxuQ$3{a44GiUgJ}uko{rkt!E|RPPs3$)uUy9T(52KUOy!2SiT)40=#7i9dS;R zkuYE0P^lTBT#c-k+YEU9&DZz7xJPs71$GB?OBov>m_6o&<1Nyjh*VQmnVnDH&bAVfu6a`%egpZO?A>`fpq|A-4Q`@A157G-MJ8|t)#d>- z3CY`N6TDIzm^?*!A~JXBj!ME|BXj_6wfm~>ojT|bd~gn&6F`uY%8#{9H_m^lteO{K zn*F|QL&QwDzhrBUlWD#;7$5W;kTV=-TyK6EgU4Edm6T&M%rpSI4pr@L)n+NJSN9 zk2L1g#wR{|$}{?8bp0?d#gdax7I>E0Q_E}xu^Uwj6&bE1#fyk?Hc)|t#V}rWzknsb zEEJ;5X;QkQK?{=@38uR#%E<+fc(aSkRYg1KiHeIk9>5QAIW4^Vo_oAl$k~ARZeaG6 zS6(^$v%KTovJ4%And;jOz(Z;FT6|2LO^RK43oS$@*T_p>f+pY( z;3rYSC4Ohr;sWgtQHydF&Ago)BB_G-t-ZSJ<_{;S!)Zg|v}IZoFu(P3ae*{6#CrBc zQs+u56y;HowLsSkTGzKFBHV67iNimd4C22~9Ww>OQAkSfjVUHiw63@E+~jlsAa$+8 zt4FsC050KK)jB4kRYZfe4fELkEJ4&2vGfQDjg< zhy}6D!|o7o7J_w=oTbq&_JV2*c-zW=`5_rIKFfB;Hf^U+?$Q++F`|o&Y$0Ubu1Z5( zQn}{sll#r5j72zNZ`Y+LQZ$wBG!FpKutDtgRD$P=%l6azTvW{vpug78o$qlgF_t1@mrQYlFi#pMOB8A39H(S8 zxJjVwcuTD8c_(%isu?HAr(Ss;o>A~8T}$T}3FQ=*J+~N-Pz1=&^jrmQ4PvBr`)#)$ zx$nMX$Bx+=va&SX>dvS06!FW(X4b7mLFQqW0`W4JNLitb_DTy*o zFQ^N*OltgwEKqg`4xFiW)n|zsY6M;m+ap|n3;=QSc%0zuES+gxbZxfz8c}i&_T8X4 z^EKpq7oqm8yn_Pz!tL+C?hIO1u*2!+ZRlrTbhyT$*C6qt;?z|a*lX9+A;01L#h<{;*)&5bido*ojBuwm zunB@Ru&Isc61jnmhDP^4(IE%OLpb0|Qk@G^)u@xuNVwtbn;yt0VwG;0W;zSiGo6}x z7ReDG(<7sX@Mh_L=voX& z1DPJAXpTZ9=?!I$0+6rs$_U4*(a9AXD>%CNS6mGM z=P%TQX;pq^`pu8>6oY4^Q1;+t=JjE)o$Q2@e}M9qjM6k5;-mK@hu1ExDp5F7a|P2s zRqHMI2t03W?TV^p4Joydils9Gw<|oTI85ex#~ikys4Y9fa2z{!?C_DpH{X27RO6+W zUOsc?jBBHCjH_lOB?KFoDNaA~hfRbOJQYzt(*Q7-pF3uw@!O3o)htufNCM|~OAVZl z4TO_&kzC}PC_e#zvuj{Y_@_}oUCP7<7Q0@qM}#??FFIwg&%kfy58rP^YuHd+4?iVdDH=%F4>h%397Q ztD8A2a5TIDx#`X};4c{VY|NDm0mX+QFxjF$DftF@2 z!L@wiF05_TL;@zxCG`^nnrcmI0uYa8k0RUEiq(=v5~ZdBga!sdRN$Wwl0>DJ*a{jU z$XT07mI=Xun{8>W3f}ev&;^%q>aL0!*>K_32LR#ab{Jq&cGe2q#k-4VI7%o}i;Y6^ zCKkqV>qQ4?nPwUg=-e$R0Hc)t&aW~^8pI8(Fj)?~@@S3`(g12vqnA3r^UmAEK7Q=@sZ*y;)(xQUXW(-6JnwCMk)Go=zvWCnHi;Y67Q7cMA(L?I0nLyRt zy?jawz#eM(_QDgiWC)0RLj88zrFDSVi?>}Q8> zS=$FZ_0&_RPoFj=SYDic<1Ei|_H5qzPA$1xbZt#rPP9xQlm8 zv5lcCFjh4>{HF8J2!qDBVKI`BYyxM&5fJys6~6V>n`U1NXX|LEfxUue7JMjQ$zyVq zg5ufrTf*cPb|1pUGb7n*mG$V#__O-@`4DNl1~#5BfF=fOBdH`?eym&&_F@J9xY!*z z72kE`>Sx}nZ0W#baG5qXR-K&9qmMn#;c`YiQSZSO=h-z-&{9XzTIx!8ZVmeQW74Wl zrF1B$xBWFOX|>ZNE?avA7-Mta?!=NAIOXey@QQbH0Em+<44{*SS_R#xV7syEC1A!U z4sCi>N>k9=bjy199dBr zXUIp1a%!Pc6}jpqO4ss+(VDQd&SXjlSrcSxu!=8NT{mt(Ak!x_X+_D9o_XeJ-;?r& zQMUy;5%LtVOT^#&<~N^z{u^G@8i&uM%QX#wR{+Wzn9;5!KpzNT!fI{?U^gm&Ayxpp;98s5eyTeMwubKY8=fd} zXz0RSsbU&bY1WW*lQe_vGG`P4bYmLR+KCoQ6=fvR!K{u6?ove-9Ssf{5VbMr;o3~X zs~9y|x3S|8RFju+=;!7PcmODuVd!BAAdnnPB)j;C3(>tj;leWoGU&-^J)A{xtpONZ zIm+w?+R<~8<^`_2x@P|opSV4em&(<-h#PeDU~Cz?m{d*>(*Du>?vd13A=PdlMsAmq z98K#i$@o5Tjhos==RJ#&?Z5#W)u*0%>T6$n+UG1-Q5W}&5?}B7)7x*o_3nG`xv=#9 z`|nxwyZUXNZ_Uxm{JtMlEM!Wni^kwg67@_0CXzkx#+~QA&}!&V>a2EYIP+cxGl$Eb z`DX{fo!3`i91D>dvG(w#bS^Y=u}L*Ah^x1uc!i%}3Q`_hnm6T?=T4^^IHitO%ruUo zq-}yCos(iQoG{UKkhwjyP_YZQ36;O98=x{mYhFn+2~F~GjOMhEr8UG-;+0okF%!5#pvrnZNp@+3p#<@e7Pl6A*wx65{Bxt^ zE|UYlL^hw>j9WiZQ>nB8G3!fk;;+eBs!yjq8awAq7lcDK2QbqzsRvQL-YcZK1PoP_ zi-`eh)YSrPCpM*x#j@4D7y%ca>io_b5k4>{-?qL*|zxa#4V6)rRdlUQ? zI{?%+6lczyY4eSsHH2a)<;9qZ)miO!_qqbo@HY{x`(7Ui__qZ#viR^FR&$wP!I+Z3L7@=0$3FpTX9917VD95PGPhJd?b$Ci(-s^YKS zT+grNZ(BU6gceoY00{@MqQgON80dY2rztzBVT?x0c^{6$ZOdO~E?@dee`#I4TBt>e zXp03tQG3bX&)ItY8vllEjBU3kq7T~N;7nA;#`6k2IPq^J@yrBV6^HH^hc~AyF(#J1 zl-v?_8^zrmdWaHgmp~rB!6f*dI<6^+DnMpfxc#@y+@-;P|95Yf`}Hfo^UAlr z`OUAMKJE4`!e?U3DbvG>f$|pS9cFjvxGFF`pXMMyZoqQ0c^D=Um+}hdH_h4cWBa6~oj9|RicgI1l(7KFY8fMLk8_%g!`X;~U9Z%6|AP-~8XWwZ1h@$ugKM8r0QgYi31lN=OW4hk zy97oO61^mu0o~(`9I<<9PMtdCeZ}8+{u?L1bdo&e$DH#-~(wz0){7pA}t9=hOJo0y1tI}R=7UU29f+s(kOEBN_|Pas+7!FCmJj>*M4x>_PaB#hpd^XFhK1sTQT+5eXI4-` zB-dE4V3f#-4lbx!?WW-~IBJzD%*McK0A1jcOq!0h^JHUuj6R zI@?&4O8#tf8)!?B6DDe%(+x!16=K{`d4*XO$?3k}$lIZ|9agC>t3Zkh**w(zHkV4- z84+CbE+F(XrtzEO&8k?^jONc|<#GA_SX*#$!7;zsCo2WF&3pQH$>)yXttS91jMiVh z`m6Lny}edtqmPYRp2CnbTn{~P$WFu!#lL*<7X}`?K^fv%Dc}`>WEIIO9@%#I6%O}1 zxuqE0kS()Xo-MndNoBBQ)sXhKeqi9YWL3lnL)@@9KvAp0LN(Rxa+IGZRJk*EtoJm) zKMHz*-?SSri(AC$9qTwf1>mS2c<@1!fc3RU3kYK&ZQ71-^+g)7@h=chP)~Q(64+Ag zZo62iNITC80Ea3kPn=|<9x}4#)k zA@j+}rQhd4dvjo4R$o~|v#gr@L+wlaEZX!N&K2B)6pI_7&~m$L0D|)@M; zARfqB@A+Nd{*-GTG!1AApxQJ6Nv`|;iM%l(LtD9%|DWay%Mof$m>3q%sqmN)7-s!V z^_9VoW_H23$m*|Llug`uE6J4tOUl0P5L1oocUq83Ul>{-S$Mb*yHfV{liN-JhRSna zf6kl4jaWlYC-j;kKurdhuH;<>o<(B%_8_)|Z#unw_g$k0yAqKZKY3GT2YrWvesuu+p8daXAV6x?p!X2%*0 z3omdH;Pdu^{Z_A8ukn&Ph2s-kA9fPbmxyf$dIGS%K61qUV@KRjlMfMm@=@JRnhk-v znjxNbR-pGXCg5BCmJ_mt6n9qRTSej~kOjudhlbo%;9KVW_AjgwKFT?dPaQbBWGB|_ zb$8GCTm&iGL5LuOH$sPB0-NM@^7G|GqgduQbl!9f1SP7A) zxpl@}#mz*wQgz1ZwOptm5&@>QMX^<#nf3a`>!yKEcYn$o2);A^^wUqf8vNrQ|M=g2 z`cwA^F|gC8Pyd&1{1+Dtd}*MWw_G>?mI8wt*=@Nk@w>g@G-+eIQp<``ug#1le(KBL z?BvVy7pG)#-z;u+{N>C`F;Me^0!mK#g~@n`k>)qWYiKXw?Zn%R9|AYPV46me&WTyiVs{sWAkpx^)g_XexYwcHZ3?F68t{?@^KdbpGpr<$VF=*|l6 zz$f`QjGG6ZefC**BXKDx4ZNjHV4>C20)a<^8RdkQ3Sr&E!H2L|L|A8`V}*i%ePk58sn=82Wg}E z*x?*LcsP#Rrvj{hcI5^Hw~V^$hwa(l?EP9kQ>rgw&k zb?fduxpBza-!sLW_$)(Dy)J2(!^$037i&J!aC3;~nuL&tA`{X(xsW5wZWwUktopOoFSl6x>MY4K4NzuXC+@7K$@gh0jF~`clZ8xX*_0}3;FdAUXtF->#7=ns zZg?#**XQ-Db~!$`6IIYDTI}$LEj{(rQ$PRrpTGEv7u_HfwZD7LZUCNsS`0C1mm+~X z+3_ufyg32eSZZT4@$B_AA%=vw16ZddJfTNJo$)E0LEEz256UKce)oEkuw zYzN}g?H`XESz|ry+kM*oOaOXH5uOMslr&%1Q0tr|~3lXOLTkUV8auXVbra{dF9i@O<^Fr=NZ1 zS>F}VG6E15H$E$8mD8KrGWeUnCZxtXYEzLrf7F+WU=GR>3Lk-ZxHU|8I;1wcD;^O! zW#Z5R(;0YTUfM`4)r#<8eZ{kp-DA1U62QigYHyt=n{n&ECX9~y@#GXCOGpIKG4u#xtfWR72Cwe6r-Fi2PhlGsGG_?A3 zMIiKD;}T!04kF#5#ix39W-3y#N-z$nVY7j zL}rRoPhK0hK0_33HkSfE;-z?-VGSS6Yz?$-m$6c>K^xgg@0MEmAfUz|6t+)~j{JACo+?ruGcB*T$4!!qX}U95A#Y>YEA`y zzdI|jfCq)F>Z*LB{=*7Eda#C7nD>={vWKXWIg~{-4f=ItTS^mjtfJxbTJBA8Sk8e| z$S3~g!U!PfBc6qLEq^bYP&R#EeZhyKT={TY*OO1=JEktny!G~5dcp1E3g-Pz^>RZn z&4^nGL$PT{nM&8viV7yk`n6n}Eh?m&f_5x%-7J{MSz%~nMx+r88vz4H*>gdgI&W_l z;PmB+WBv^nFii$!00^3<0dZF+UdpoqYal&90y=1TC*fyYwY==S%n3dwq$d*1>pxkk z@%oLUM<4mizx+!^xKh_0C2uhSs92v_WVzA^p6sP+h-F0J9nK$e`*S@n&a=2QHZ9pf*ZM8ax!!;Q=Fmda> zlXn)5mLIeou(C6kBi2CpHf|NcEJLcES3RwY#w=T*xbqp_|EZ^*f)BTPX@T)VZB8(G z5N?+jRtu@XOsJRAcs5(|!)Qz!n_2|YqO`_02e5tt6IEzSo+NN5Y#judTFikK>AsV0 zt!lL3#&_e4XU`wjF3icC;!TRKW_bg2um3exMFb-@`qD!>r7_%r&aioM*@d#<(MRDq zo~j<2N4sR}WZ)&y*X*5c#k>gyFd=2e>nAj3OSsDp4%jX+Tfou%^w*rx#4kEna06*i z1GPHFDRK(6?9=d25~iNAHVsgMBu#p=bUlaosFxFdcJacww@tzC(6j#S`CHqmk@9h9 zmVj2sMzzQdfvG3Zt-_EWA$TN*sErg47tQhW^D#xCn_VExw920s_*v&`V5W-L063Ed zu>OLrAZw-G?W7*$&-)F%TvgS_?g_2-`Z?svb-?6#}) zcJQ~D0I)FF1arH(#{`gGn!id6Zv=K{BXAFjEg=g?U(TT$*{LPIwJ=jnW&w#FvU8K1 zA*dc=l^ZaW_j&@rQ&8s+Yzees$t~N8Ul5kkc5+!{3D3nRT*lPK&>3)mO{`MaGN6dD z#L)*`MG_UpdO5ecR^~|1u+P=Y$h0ny8BEiici2Y4o)ERP+iF%WD^CV9M^aI zUZ7ZRtVjsHLKi7iBjTv~uggx@Dl#UCiGJ0T2JKcr6R)pdONPu3$7-~4A02s=V)X$Q^lnocj zA__iK3si(0Nd;S47?O)SS*;KeR=>rCn*grV`#giKo=rwve5N#BtCKkq^4|N}3BzaB zXbu%um!$Zz(xp5@um*_NCXp6a=;n(@fNnU}K4FWrRAq>l&DRyM%0=yBjN5Zp>ble9 zttS9dSQzr@JVlyOou!&A%H}{MP9sBN?><_ofj8X}+9a3&Iv#)|hC^|ZBVgEMjj7QZ z5Q~MU3mD4nCxD(pk)>b>+)To&7^_H!&*A0JN z5dWE9>>E-IY~{3I1ZVBUP<6-f9SA=J8!Bob3hF*Jx3b!r z`y&aD(hG5Y8)u&|^IQMz;_GpWrELGL8ltHEvLdxw} zzI)4@2vIM0!IQcgZ!6#mbI;JaRgqDgiUtg@#LdEgql;e>Y|dJ8LphSII?04mjqdYX zOaH9pyJaJp7FLMUUxsUv>#qWp%MQ|7+{!k@4X8&I8>QWlqB=&>3T#Udw~;i^u(%I$ z3t2^K*>mI{57MDNs!JZ%d;0F}Gp{6@e+c zZ^#T_qstvA-VUViWoir#-Od&d8O4qE%WAU5pc#Oy4za!p@#B>!o&gbWIRTV{Csswa z-T{$DC`Kt>0it+z4)j!K=XRXNR*M>FWK)i^DL)@!1D3^?@Zhiwjr_1I!|q{|SmmOY zIU<(#p-*AJfTKf&v{Zt{9l(ntcg-;lX-=IweMk&nj6X{?iyiKf)sN;$Q)2d6OTKg)|c;`JNN2quXzAMi=KVv zncT|q*rTe}a&E$v%LDrkXrSs?vYeg}? zaUacOcCC9Nt)6V7jZ;}?&YZCXfY-t%4t<3#PIvGEoLEacw{S@=k!2_~Y_e2PZ;hEM z70NJ()&PvM;3&PY!Qjnv1_)&gIiYHt$htX=uehZRhdDsc;QDVPN8Et2S`8ld(fIXZ z3kdP%u#gHV?@+EHYM+cA!wx6~aO)Y60xc@A9E~gW>?fx|Wkc;x`AP+0#Jb5?bsW@t z0&{>fB69=5u(ca-nr}Qiw^26Qt~bleZ4PJzmBOf93^r`U)`MR?b&Bg(QM|MFWhOZJ z$wFJNv>BLeoaYnMn9~|2R2P2rVDohV3Mo1ls0j zu%ll59!D*4O&j9N-RJQrQQbJ?mXz7hNfI z99O-W-GIPOjYBkk)@$4jDza>Vi$L)uVet!wNa7@kJG<#tGn{@5`f6wb*Cf|Ky4W&b zxti}qFm-H{&#svZYB~sFgsGxxK6uN}LnCdh5aq@9Ok+06tPo@kB2)$E$dqns?DGGJ z0viWbLJEKRD_{Bk_rK2wx2*K`k+(Ym;OK^K69Cr`f!#yQSkrs)vvf*bxdW`0PDc8r ziD*0IU1}epmC#9Sm-v=&V016ygxO~SliN|{1T)AQ6IVFr^=}m? z;q8a1+%^TKda6S;zXLHSQ;z^Foypuz05LN_!`Ew|Ilu&<>&?NE))rQTmeo0DJ7_Dg z2WgMRbv@h`)T&9#2YHJx34ouL`?L3Nk4;YMic1&F0wrz7L6qc~05T&dVb%wNax#?3 zAm0Y?+r$%}vUc!Nm?2dIY>B}PWWy8!-)10|o48ukSu)%H(HDn`%)ZjN?cC|93(si{ zws#HO!33b!^`&cT07CFBYVE!UP@92jfT2NPgjiG>W@1Bq$hY3w8P$sD)W~fB60jbm zqO@Fq7A0@?_b9`s{&gWnTLtS_p>>@)s$2k2%20ay$ihw4RnF8sjo&UFvW?@ucOMX-Pl1ZZy(NhUQ;bB3>m=AKcjcttSpLDeG%@8*V*gd72P;1xNXyJ>@M2(pAGvrvHbk~kB9 zcLaH&-I=C2!^n1~pn>=^c_JAF<-k*rA5TANZ*}X`Pqp8IR!UHW00MaOTlFJp3|q{) z6V?&6R~k6@B9*!4OZD0et5o6{iAJ@`)5e`UDTD^Bff-y0lN|urx)SlIt}9)}0Hr`$ zzp_f$S}d1fgSeeY6QgM|xlNE}@IX^LimEGu>A(Cgv(wnS3mcZT8(E~{RFj(~bJXMA zAuglVokF_6fD??n7F!dP=4zXv&eEGY*Y$ZsZaQ!T=N%;a)NNOGcFJPEYfs%4KyuDMVSSZI%r5}Yi(0at-t_!YC^#yIJZ|F1;ZOWhh~|nOa() zB5DIFO>7$>To#dR(j`#L5}}maK-yR#hlb@f856zKj2A8VCqMSZotB*?S(gXOIVXZK zBuoQB?*+^U7IDTu_Gb6Phu0W`RUUWTZc(F#0JxP1d07>}1i);Z>JXGm9fv0blrlBs zYQqB$dU=rrqkDB6#TcYE5M-$4WILRH_4d&8d^K4uTG5rN1(315aXCgRG7FaQ?oO^Z zWu#FAgS6K%2RN=|x?Y@j>Z@P%u(W&FRitVHVB6A~0Bjfd3O+@(=8QHE$*P+Ehhts` zh#9HMSu8tUkxl}-TP67sFhBsv64F~Elv8Vhj%5~goNjxxW$wL!Yq5+K@j9lN=rfN) z=Y-HcZZa)R%~(s1@UB#fQWIDUSLJR|n@C7AE={6wNk~?!R})kCc0x3ofb%FtFqoD0 z%B7Yp-DlweK?+xMs-s0D6gL2#lfiyBQV^!ZxmiR@>B{myJv_w?K+0-3 zu04?7M8NzHVoGJW&f$;$_>V2MZ&vH=<=xo?fTvzGY#gK-$+5p{S*DScrDmRWGvA(l z-coIeL_X^CSb%*%H&Sr~qvu*FqZj4EjL`BYIeRn%zrB6pxPd4x>Y0@rO-&QPV3Um% zx(%#TI{lG?God7AU1MfD>1wXBu7H zz8fFKxm_;bYa?wP)Jv=$^EhbYkh7&_HgOgYKFGt`K$OqT7Y_3I_ef=fLaa!px*Qg7FIHG7G6J4eFvRs`VZxe2*J6f&crJ26BR zN*jR&07h1s>(MfI2``B^ms&5s4Wvko-TbbGa+m|jD%5LSn^5^s{Kguh=PJ2|W?q|I z)1GY;VmN+-R$W;!kJ}3Ps8ff!8#nBeO7z}#Ei2-FkB#Htxq4`MLH-YDdHs>f05Z45Vn(8xhB zIsO{8G4l?Y53+!(F}>Rdm8jQ>>W}{DkLYc^n!APWcmhz-$rC5NDAYcwFXB-F5p{n% zUrHgluFdjr?_NtUXMYAEq_T(`01to)S__Q{w+fAn$x#RTq+@^O#sZZ8p7?BB45nYg zv|M~BQ?#C(YL{0iM8&0}EGVkP6cHv|76E1uEYJbNf1W=i6_fje`Rr3`IpUV zKBjQ)+`A4auytzTRu)?vhaM!M9HJ`g;y9DD!)4ypxaeOkXas=dz^54r&^aQ27wL^A ziBl@y3XiA>0G(_mD{v>nat@m@bIzUp;u8VLxaqGOh22Cmxz$j*pPov48qkJ9S>BZ` z)j;V+;Xui)1S+)^U{|>Lkd&E#DVp+`2jVu`^yWPD+AOyz%vd{fls0z!rZ0B+UKeldp-lhl-<=`03}nU-fm-kvU<|(=geDd2d#lk`4i(qR+y?4Bpo*0|gi%~U#Utm= zUGE(PVkCcKsc9mdsLhD1~Ue7P!aTL`dL=#$Hl;KrN zr|ac`n=KBq4a-_^=SeHcF3Bw#r3SDs%y5g=>Aa*XXEyjIe)sA)&Nt2-@VBqz(EB8i zOT~v|EJ0Sw+%@9Ks4UOVy>sr&*)#smo;~~4n{WAmf!CXQpAYd(Gr1H{K`EHX=2T(N z#a|yh|JtE0?KaD#ChE$+G7+cd-}7M!h3wNC%kaEN%b>tENN)D9Y)#@o`o0MeU^;2cCFjC0jd& zEp;;FA?^3{^I{?AXyC?meL(km&91Sh z0h+f!G}8kh8IN4WF=?g27dNRX#JQJwcig!vjZVJ$qQd%{v(v;15Pq!<#NalG*h-Mo z1&kC;Dzg9@6(JTc;aR9Heo4;4#e!_@0*JwdOC%{6oNRhmW{MzXXC4sdm%9M_A{Z~` zDD_YNHwu>!IT8-71$BlRd*mhGS9IWjGBR^vthUtn>d?`X)*|%o{#R!+e`V)s{#R zfqCHOlL0cg2|G^7;GM)|b`KX6eXQ%?CSao=mGP1$~;4Dj992d#u_c1^I=y8*lM!D{#v3g4> z0xZ2?cnMrX@dq~l;LBx5VqTd6Mo6g&F!kFc6Tc~M{+USTsJxIVakbCqtm)5;TO-isOo*NklZ>8T2Ge2R92i>9|PeT{xuDV)d(Xjq*?`y zmo`!vWiuIcfTn9q6J$8X5rxpWDBI42kxjrfSTj7WJc#@4Z~uW0Yz#iDwccU+?k4~h z8QTV^j*nl?kqRYEj;6Ik1vw!0mWacLXxT6ps1IIofM$q?URoWqQYN1-Nf(Bq5L@yW zU6r;n5?f`_7hDJ@6<6NOF?dC1maQeprpIdnsw{XSsAP9DIUDGv&9yXP;$^9(T+iDc zYHpkJ8-M$JRycIQQM{#XWkh4M_b>BeTCp=GmW4;@v+>I|HXO3r6ZB-D$&zX@{v8FF z0IZaf48yjy7b_+1CM-mFhjk3LJj$x{`RO< zk>;pcD})BRcFg-s!wyH|a?j$P-H$)^I8(QSaiJJ1Ix_Hk9f-a|yhynaar+#2xXvZ; zGC7LCkE|x{WT7XIQ?906S^2WmG==7KlXxb)#V<+wF9Y~if8CHJ$Kgf+x-Yw@I?%KA zr{^r#;~J)8UJ8_JLdAj2c?FYcfRV5bZzr@xFe-;(DBdBxgT3yI3K!OQ3;g>N)n6>h3!Exh4QBL5rgoUwqN_gi>l}mXkYyKr{_$faOk+ zwcIPB<2i^dtZMz3A{EfDck`?=7@3Vv9M9C4+noTa?4kpJ6Nk>Jbb(<%&m0Z6U?l8( zw$d0e$~FR72%2W(0$`TO&jT0xTSr|5x)yBII3(#-z<299PLumy(6l{Sx&9=Iu_an3 zo3{cn{~7=Y_)WoB6(rSV%*6G7{IQj?j?NW=y3+=4_x!N|(*PIYfz4$5_I$GM%C8>$;H?9nq!sQ#+_mGtjeQSX zJFxG{?s&8KY7T-Lit668?I4gr1 zO;N-vmT?X$Xx|ZJh4!fOG_AtqHi&};2QmDJ0V7N$Di^ZJM(zN%!w-U+lwHzn2M=v@ zi$=U;3%QciJ5JQ@t+8nPELYL@kc#a;sI|ww)jsQoyA3 ztU?WMd+RL30I?9&ji|N*-4WUXPlnzFq9i%wuqA+yCg;ETAaIj~Z_$VO++|&33ov42 z;4-oG1*PN&#BKr@u+Tsg$cmH81mG3R6_{xSYnA+ZUDbhufAcqgLt~@T&k2042|(S( zuzqsHPumei1Iv}PSgmv$IuZ+pkPhG)_yz6LyHqw*&_%?wtg)>WaceY5`~PTYB1~4f z<75>a|85g7QyBW~*pY=G|MJ)FIl3P~PZUif$Trnc{J}(OOiW;7=q3#M0iasKIFIB# z2fD6N;{dv%ksLB;AQ*n31fa<~b{0QuH-IQ}fq$70S~DFFhVVLZOanT)CWXC5m*K6K z9i4Zu^MgTbF<`WNy3D%Xkc(9uIDoB!U5m;PG#~UNl3T+wn+yOxW&*)rB^;A`>yb~+ zu6=xQcU~u_$@Ff(9@w$>*o8y3{_)V|{^hEoZ30m3DzliaNLLyoTrL0rKmbWZK~xG_ z$B;bBa^l2E=Asp5Agfeoo&;d(?jS9XHaU?lBaW3M5ueksWaLTcJNC0EO(Mv~ub&H5 zq4G3EGbIa=25fY-a*OS)BaRHw;6{PXZMzs{XU%eMQAG>Wl-)qO6~Lpod7CenjHsHYM08)rbrtX(0{<7k)YV?*_aQX&RyZ*{p_qLrh1j_d6V6uudhpCRa zuCB4G7L!f)5VED}7zWuA18y>u4KN+RN@(Vgn~Se-yXVQSv8x6HTILpM#Ci%Gb;s>z z8YoMgynpc35zV2Q;3J7vS!-(ET-;1(gPXPwa46&kn_MBNH%_|sk0byuDD4twD*C1ePndvCmQ2lh*+~Fy^mc! za$rYZmXh8VdUs=V#rIb=g;Arna6?=GKyAm59{=j;uRd|&NflYIIGXUT?4yqy^%*S5 zU;p}b#BCMmb$NGtHUzM78h$6A8!b&VIZH4dRSr}+74#cJn9MnVWk4u7yj5q7irvK27W^`uTr7h9gqH2MjKy3(O8AYZ?(yZHwJ zTZewSmc7dp6cD|AxdmNFd0!|LcE! z_St7w>is;)&pQFAyR?>v<@z=Nm{v{HYD(C#lM-D9*zd2N`|vWziIlA7Xnla9bgVAD5N8re#`->pz>CuE()t5BS6Av)W855 zIaih6MlLZ8!;i8noRcB**GT|99Sfr|-V+-2h_DaRcDy z>+SwoWH>~EQ=_^JPsP32?V}5yesc8M<2vP2$Vrx^v!l;O`$dc3L4G$aJIUMOv?!07jg0LeyPrh$fId&X}}GT+5KSoaDd|TrTZ?Tism1gH-$D@ zXn-(O(J(xt11l@5b7P9N+a{s5G5icP)>><@nhG+WN&_Boyx~Q*U;EnEHr0Ihi0O#! z?io~T`FA(WQSQ0-^1dh1QhOcvH(=Fc6-8=4n<)fiJx9f@y#{^nu*6 zkG_E{8jOPSM?T!bhBYBx$f$MpZ!|vmpPdZs!=+Ox;L~hT?bp%L>K zB6;J(eVb)F{2&XL1)mL7&I$BDMP|eu|2Bv1ic#9gT2eN=j9Xe5z*GviGikM42bFJax zr~6Ot-*aenN31*F+xO|KM?QG+{f7_j-M4d>JNLavE-!o*G}L}nvMcH#&C>jHO6lLB zwVWNj`s%CBqiq7nZ|~wwi0__z*9*?Pv*^t0uiNMlQj>)&>U~|Hr~id#*tsi6eCPEC z%Z;VABQVOl7hu|)n^3AUvZh&vD!PsW>A4LA%ft*X#0iR^bI#VkS2FngFHxu3)+G8%^ebW7vnR$=L*Ou+$KPe>wT~T z16{^%bZ14l9XDJlp!UqD6(A*vHplBzl&*nC#eL*&4#=m%H5q`Fjd!mQUlVSFVRD)e zVE|px%xv|ya}{YlbaqLef{kpMLt{pSjgHkBs>xuuzMP!pzX0n2#P5CYd#jawzVtnq z00=;8eP;Efv)#bg?XRZfm`}et0q_W?F=Mmuaq#D!d#(q}&wu`Nd+Js$dhP77(L5VN z15~4b($aG06>Tv6QecdL_KJ$?qsbKM?x(@)R2k7CSg=fk`@=RKK}LruxB3{|$<9)! zlPNw27`am7P*<2UxBhMVt@Az~wl#T69xDP6ITlu}v&W%2d?2}h*4g%lpjIM!*g7t^ za@*J$lhV2@ZnZ)S(8Y1k<0b>HTqZYaJohw^1H)|dKeiiiF~H5lo-6#%|NPHyeB&Fw zB6<3$(`-qp=7xj7av*(O;7ecn@++^r^7`wqpMB@e_x69b>nm$Fj;u}q+H!U0^>=ok zf92|#V;>#fdwq}ffVSY1+fvuo`0-=Mj~zaEm|2dtYY|(u9buDa@`2kko_9{O>cvD( zB;Gmqjw=DCC?6nWLwa4K*irt?6F|9+sR#JA_)@D8x{W`~vV*Q~T+MIZHdkJ9qHg_{ zahCB`DRi?L2V4dj<&_4m8#0mr9lg@mE6FAC1Z!0by`jEk)p@r{2S8KlDI-*ZwqWG4 z3g!WpL#v6edc|L^rf}|gJGb1qX_Kg~3}4T}!gZc}Wj5D`_PYqrW=sQ?0=mzvfH<8( zR@vvw^LWqF#{28!b z2|$1SLU4V_4#X_OdJz3kaT`rELqX-Oql=m(gQ2u2KS45>foOc6F_xEanu-zU%3hZc zQ@X#ZBRs1s5_eWCsHP2NNgmm1h7NX9uOsMCnIY+V0b7Qf=WbbKiB#7zd3SI%XagTo zjWB6LL)r{un;FlY>iwxM8JP4>St<;9x+{lHWa-nM32g0ky5zLSuPMP{j^WMcx;yBl zmtK1L<(HY0(QPZ-t8DbDVEP<4-(jz5CV2GN@dJ;oee(6a7r(T3$KL76&<1UAJy))O z=9NFMzW?!?E=A`G1PK|Q2a>mPhN#(Y2?uHU_FnE=k8 zSH#l{RxPUT`9lNkKg<@I7LQOjzsDc(H|2ofha~NbXd{=$dbFntDSzZ-RfE1YHfb+O zMpq6jyi#N(tm0D&NYRrDHA((;|-zVzK*3mo;88~_d<{>pRiZ57zac)rw2AS{P?8_s{9J+fi zavX6aAIRj1G{hN&R!Bzj%UN|wYA0%QK$2PO=R0fw|3=_|cI5hHa_wn7lAxfXWt`@K z1!-npxl_?_E4VN-cJQ5nFq`4=%Tl-N?(Oi(ZpCS^43Lhj(?9y`po3B>7w2l-Hj}q) zNFm4=^UT-f%O?Bm32t0}_@RfL3)I|b=+*YUF7wdRwGU07SwO%$G$!S(BU zM9^7~xfE2p$uvjZbt@J)p(arD+`5kIs5@Gm{CR5<O#vF%3Q=Tk*3Etq z3)(vh-h`SO`!ZXNsjRaAgPumQs${HX;O`-DFLwaM(&mh)-FpmXecVE)Gs?9ZA&~mX zxXNIw=L3u`J>+Ou5ye}}T~I8v){UB2X076*7o9Q>FQuqEDNa3|HEU{JeMAegxy>bG z#CjT5W^?PZ5x7H-pV(HN(Us!KGyYP%XlC-F_$`mfdV3N^KT&zKvcCS$NS9jQ0Z@Ji z9kC24SAAj3XTVobGYjdxJR2~QO$m}XC@a^%v+gzx{PLH-{P+L(ckhF+Kk!HqzpNl( z`kvwsZ+!N%%dfp~;gu_LnLB zdtC|O8N4otXSsOclKU~N{at%7<@lhyYXe-rtJiP)n2bjpTprM{D>=2mgaEOWEG}y0 z9-Eo0ZYf)llsyJgq(c2BoiG)jGwQHo@>$&0^rx=UZWqZmqCDP)7>CZTQe>RU<^Ux- z7Kn9*Be#6xD!#jN;)ZXEju7c7G-TYH7HdjyE zal!PflxaZsVQxx*X%k{LJ$WNjQ8r=vA+4&11tutMH8JEH0JS0!yYAb)reHhzj&ZI% zK&b}`tKy{I6t3dc(pJOPF=@4E$B>FI3AlS}o7jGhGZ5n11o+JmOADQql?MLm zzx_8&9-Qw9a8GvtB@uBSe&k^q_C}Xp-$5G*T1bO1ZlaLTg;rax?R@R&udxFQyr2H` z-`;uq?bbErx5^|lBI|MrHEnKlj{yXhB`j!H-P*p1Y?=~~)qv0PD&#ftZ4IRR>xA9A z;;9Sg@q=M!>6J~)*AZxLhitvTokND*ofn!)utZQs>h?@V^RyIM%&`1&mTAgoK_3)O zMhPwHZlegG%b2h6)Pt684e}mWovUie9>8GZSy?Wa85rKip3R>{Kwl?7<{@(%f){`B z;{WwO{>SsrJ@?ITe)D&J=XbvL)KiZ=?wPEEzrOO$3+G=lUel4=1x?+%bJsT>T-&?r zOYa_e2IQ~cT#{Zsmy4=*)wR{0Pc5t1*p^%B^W4EjwIRZ9YK)Mr&SvsHey8)rFc)D4IFf$@@B{xfIoTdhD>Ux&j zEx2`D9lkDJ7a;SBA+-NZ?Xy;g+(u!QnuH;n!g{&JX~rwnnM~WpBxHq8?f;5qGN)fGsVM2QY%>3y_0BCqocdL`(5( zL+4E4&+|FV+gCRrim;6*Q1dfK2==6clit*7r1gze|yyl#No%d?~bQ~ID~ zkiD{LedJX2dTtP6&5WMLoCG;4+H*BG?e(l6hS%wO6RhAwSvL-ivXDaU>h#zVY0NuYcw5ufO<K+6Um9xS)hcP;!fe z5?+*1^q zpz2`)$(a9Cuz{9pg~>A61OyPs*#H8ZyUC5gEr1!%qPBD`6;D4~BkSsYfKq}>5}YJp zS=T**@5=uFaOP`yM(y#Bj;aUNW@&87hzunh7 z*R$tpwgLH!aBmLuV<*L60UNnGq8+^|Bzq(?2DARy8I*&_jetzZokGG3|MtT1e|qfX zpFey4xqX)pUfnRuZ__p-?DE>q-OnEV=9Aw#wC_;v^2`@(a`>Y}HE|l}1Z(k#Vd*bs zz|J%CP8(yjK9BSAD=)iS$!7`(45PSa7{_G`8zgWXofEVTxVuTj`r1j{P3f)6;2B@% zXVoQ6^aF2k6u$&rL!eelq@fK%iNccCE2k(imo)4W!!+|l57KzjDYVAA_rtRG0d`c_ zf{z|OY7yYjqdYL-s&`ym*?DcpjLKlAd zD_^$AGVp%%|Ndyq0lL{ZL{`enfNE_*)S#G6i%HJPQV%%BJ%6rmu37Jd3I{*khqXdU zGE;wSdg@x$Db@B9hQ08aYo8gGl~lb0bH9Nh;r*^?=09sV>5KwDQDUsLl)f{wrdjps zm0j`wT)%6>j)QVEbZyVxj5eX49^hVkfe*k3Ufi1lFaRr#Rau?RBy97ww^su!q4xx5 z&%E);`9u3}JZ3kz6b`E`!}Xmv-n{bO2UkAYy|(}1wcJk5-ZY_Qz`w;13uYQ#M#G#) z5P%QTPS>a))$W4CPk!cg1#7zBh}aJuyTk3xGUZCr|I!98l6yjA?*vP^=+js;_w_A6Zshjr31- zWiuIf0+`wz2hp&Nm*b$`{_*&|CeStPr2DHUat7nxTz2CMGv8ukQSF+h zc1$F@uPe@?gaM?>=fL@AVyHPFJ*B?(7bSLjxi}~e*e-O7x>18h65L~_kxh^#zkit( zChsp-+jBv{jW0F6V9ZS}J=r03*?Sw`{1*0?w#nXc7=eNHk^&-ppE_I@=578-lz`Cl%l{MY_g`zNR|SK0#>I^ z!2FY5T7)jSKm=THn{vYR3k)mr-2Wu4)L&_*Jw-P+RY$!>MxuCCrpta~!Aa^dr~*WB zyUD1m_QJtdzy(iZ+Z-UU7D?=7G6rNUM97!KA3C)6*@t%j!Lb|1_OYwm43~E1;~o$1 zU3=otvAsLxazvIm3g`d6fKmXx>@>Avm#->#6 z8!W4y?K(?Ia7k6?LNS*#Z7Ju%rq{;cgJqcy^Xgc|i5 z^DtX<2eoam7EG9m32g=Mw(5b1r~Q_HGr99!(xRRZtIHg%9t=TQYu+NWE^D_&7*?=u6_y4c~i}w z3vquZ0HT@3EvB?#zGb4mBT4~vsYK`5;A<-y{ipNXDegu+GrUVZvJ~w8j;+PCq8kPq zYp4rQHAJEr|7Eu_@SQq zWX&Y^?RR!DlK@L$?kh}hH8Ni-3Yd%^ne?sqN%#vn=_DdqxkyZ#;z zV1QgP#&m9&u?>M!HaxiA$m0Mw)Di~yE}PeRE(w0_W-##tK!-B+TMe3-_#XVGxnE#)3yGIX7-*{O z)fo(>WCqBg$mB=jCES&j!5fvv)^n}+`mU=vI`7eFNV9Sj=6-P_$H3#GOW$t%xabnm z-h=zDJapjF6T7b;%r~fSKj{2VcU}6)M=$*!|MY)8clC*f_CK&MH<9P1btKWWc!JA$ zc*PekGckPnsRzfrt4?s9V3I%}ke{6Bm107TX$i}s_T*au(}3@}Z!#5F?RT19igMpU zj!D{Kz$i<7tdy6yuFf*}xb>!D$?&ZiMod%Q6RsBo z?n1xt4|Wf|18{QK_m4pG$psh3B$$w`o|9cy)xQw9Nr_j7H`pL;a}TQc{;;z~k!elfDPg_@cK2k@%s3mRnL*bXRMr-Jb4^b5LP*Om5%F!nBipc?y9# z^;*kNWdke0Tr%gHV4O0UKbm3@*DzbRr1f#Rpt-N0yVdS1|LvGl>Bwq-rR@XLz(kSk zD6?(4veFB5D~Gnb*C<)vm3ikYO%8GOZzcUs695NoR7++_wr9uHC-&d?%E4;~a=d)U zK?C;f{N%`$Uw(XU*S~+fZ^v$5DT=f3veCnb$>-g)%+mee#2(I_+4M}tgFtqrFlb=f za=?Pzc3}hANm-2>jt<*~b(K2tUs2#2zBON?otPdKCI+yG4< z5n>Q+(oVAG?xIZjp&j(oa7nQqDM1MOtUO@%zD}qL6|By!<$YC~9+^g2W%ctS=nT;& z=vHVKvUcRDKcm)Y3=NrbHF*|EuRDTMyI00~cR;QT@5#n7BF@3FK^+B|GOpyZaKAF4 z-a8u?ao~U(OwjtQw7Fzic52kLw3n&_0Bwz(wabkWf;(U$*`?;nr{5?xicBvfZn)!a zc*g?|XfnVQT4NJ6Hkk;eFa;3etEW%Tr1$>!3z`53PB)(6eDv|hwCdcu@9Gi??Xi^H z>$x}d?RVbRd{g&>4?Ji@*`%L7ecDW5hX2uza%+%L$0Hc1H7G31JhGo|n-r7FH|yIy zDl|A?G6zJVb(j;d2AMjGI0S`kA(?=!fNluS*&HYSX~hN;%*5hZpD!@12=E2gysRp& zfq8Dw#d>~p-Ev0Py`zfu@{sd!z4?Vz>2JHB%0qnFNIdingmgSj8nZ6}ZJeDnbZ_uzx-BDwcKDD7cj zUor5Q60>A=VqT`XA_HjGaPg9Id)kPl)jjWR+sK--%gSXkp}e;1>dtztSeAWpiwNZQ zk0PC9nDLXjTfDM@4JSp!lpM287DeZG_K-FGhPJ4SWH2CrAUV0>TI;M2qUM_(jyIe` zDuv6BfW3dJFB8DZPA-#@5IIOvYyRA7m03dY(R)V_9X)Lndp6e6BXb+8q$vWBp~NWJ3wxVS}hew*j*;p~yzW7nDlVexsq< zowje51STU%v{A^JGCGT8*FHeUm66w&&$1`OzFcZnK)ErZJZ#~1@7%Zh;&TVDef`k& z1FK`~&XmE^9^Jp=t&11V`jmi0$Y(xP|Ka%$+k=SNm8%jeC99Rk+S(DXt#-k{HA$g` zQDYm7@!9LdKDp{74jnoR3@;31tFBZPd~Q$)n}no`C*n%yQlxVXNk9iV%)kCQ-Ol+% z3WmC_pyX>bYOF_@@GCbS|JI@w)Q&ywyWQFXNvhtK0gx5@l&vl;Udc!~8Yr+j9H(Mu ztU+ek73LK({-Ukm<9z4!Su7-ZLUNOs%1Cxm1|Vp=!+ri8lAbdM;8X0GjK8L*MovL1 zk?AJdP^01wlerG1Xpi^6$MKT5)kSa7S82O+3am@|)oW;~?PL zM5QZiTAuFAh1xuFazXdJt%Gv)=F#JiA_I7C^Uwb5&+H8T?(hEY=Rf z>vup)uaqU(idGk%o>bj^BO_UW(S`I2P|IX`C6m4@ua!#zgRMKcv-R+t2Uq+11(HW@4)l~YM@gt0GA{f2XH@)kH^ zzCs^e_|gTZI#!U;TdArA6Z0yx?b1DWuOH3UO4+x`0{eu6o>l6Xzx<_G=+$SYNWw+b z3WO_+1To;()h=<@|e2^|)Jeqpb%I`k&i=F^l zQUgFIX|HEH4Ln`dx?`wJ=jx5C{kmON!098_TaJUi`TO70_uk<6umAe5UhivwIUh8G zn*+SU(LJO(53AU0+jKj9Th4MjRNA%Y{t9Oharfzx(3Xko`k~0`pX?U9+XnvsM*!jP zp|tbn|MRkB^QHOB$8>KDLqy%4y6wh#7xm)kzln*E*5z0$~Ajd69Bm9 z18WyH6Uob!;Im>vl8}wZumMOxDM@i_8K!ePUUn!Nxek$1tUSvo0$6g895hNanK(Q% z1Squ5NoTh!D}7O8LXVVxQbAPTy3d%&n%%1|W*6CqtJ#T{>vx@Z$F+>Gd=Mx6k(ZOp z@;}0QrYvUQRxUj~y=S-D7{ZzyP7~?Q#X$$;7n}?3j zgF4jO9|xUFUV3(Oxw_$Be)(lv0mu<_6vr=1sLfJ5i0%^xO#{(PsR8F@mgEXXZZ);^ zTy1H3WEuuh=N8drh3O{X*h*aiDt@ks-1yFSzBAL@m*3ya1fVHw)LU8XbM%c5Eg41K zK7C>PWAXmsM<4Dyn~O*fKk^85=vO`Jqfv&RzWn>Y|9gu9vw{vrnXSMB4|tVtO#!v9 zOO)Zk0@G*pTXvj!suzd$yJpDs{?3JivhOMHH}pgP6~khqcPC5y>okP#h5@6!n_^p) zi3@125P7He&u3P>uNBmZtZxeTY|IRCq@7eeC zLl3{d&o-gNi^Nua#N1ZEjYRcC zk|P_Z5&b-YDIq>(R(q_mf-1BW&z>#dx}(KH)5vWv00XF;^)BdS+eX9UE?B;E!Zl{X z&$cw=N%0iQ!n{==JP{Eg{N=D;f+NpHpmz0M8Im1| zE{@|Prlw)V=i2n;jy{|tLVCAb5l>uq;@bhnG1o1zob-nueDGOb5_8SV0ljguo3G>4 zm9q7afVuhNjf?mw=%OPUov22g=z5QP3(@OY{i{d?zMij0Ejq>T&`+53^z%js3I*s($^kg~w9MCAZqGP+EH4Ss2Q=udK?b3M=785;do`5xk^udOr;(|r zukO0)kCejBr@M#Ksz|(3d3B9*>3*B5`8>b7c3fK9 z@!|1n``@{EWOv?0Y87H^^Xyzac_@+l_pP!VJJgeC){N$coLJVkOY3x~&xkS~ z-pLqrF_C9JYv@2(UCIjn7WL!T>^3eCZ`K+`x2$)=IyiNJt%_X<{D$CXD9b(fKNgbb5%+>L1s zcPSWv&@@f3q{mRQ1G2#Zrs3v5fKgUfi_w^jufE)ti69)j07|cAz{FfhU|`jnc3OSK z!)6rD0c?0Sw~kxjTm*+n`Qa`?7&**bm-Hw)^P6LvJX>nZidCL zTjNpTIZ&)QpeJ~9x&HNX?{9tcTUvDS@}&!B&uZ6bX{4*9U-BdwL;@kfJq^nehS&{Y z2#H3rfdMZGq9bAWoB|=DuS_CgTLcub>$~6mZkN4(@o#tn=!WT3-{{!0Z|`fbzN%{} zjtZCpEwLHH&uNd*;!$C9KzD|l_Iw}tp@$y+(T{%E&*)pMd6~zj@2b<5*W+>n#i2 z4+27Lp~JaRY18If)8-pDE*`q}(IeNc7~uCITtB?~ldtT$aPIs^ub+i8!9!Q01_;u<&?maL#8xRWp(Q15B9$Xwtq6w?7a z7q^>Yp8#=B1KKKC3dmCkk{p9LHe8bg0KtGe3q8EhfFb}{T^6u}<7tXp<2wtI02KkQs%!Bw8Dzr~_2w9M_+AJPhLZe(!gWCK#afLN!mu|sxqs@FnY{FA)n*GIjL+C>Z^xsK%rO?hQ|Ro@b~)Ye0aqElH+z;`b9UZRq*x*ht6=k zfF%gqYnR>LN{or}*aqYMHreYQse>YYmCkhjY#v~@OqzS*M>+eP2LP`~C!426b-Rjc8cI!u001whw@#`Krb~(2`TF6!kY3q1l;AbrfN& z9d!gH(-=xl!}=QDiw4eGboOV=Q!D|#Lnk{P?HvRJYq;lt78TjRFN=fY?q$dE=YP&3 z$Xsjx0wv^=F0RRG6`5B!-1Fpb%LGvB*r~zKJpIhCUj0?SbxSu-{;rnlM%T+Pz3j;j z*MDRgCkB-pylf0j=`IQU;y-_JVc&(1Kl#|FA?)0l1-r5-r_~-pxCyb6eEX#?Hq=e1 zEM_N*GA>zDJzS$3?zyh-lpzUYioH0=Pt1DbzIl~p6`7F={B}%V^gqkA;WxuzyRdV| zrGwW#did()gL-8G=L%Rh6!&aaC>ox`}Ue!>}XLk zPOd~yTo?fBEp&DXfYGgzUBzZa44MYo{#VEdrlklvbS_M@?cNb{XAL6&bSX%HfVg3~ z00Xn>v!bmjL6fz)9<=TYtU& z`s@0pjUX3z-K}x;N+0~`a@WT#s;rx6+2Hx^cfVt2U?zCsg%_Lx)#5Bq_CNCQBR=P3 z;AM?=oRBQ;IiC@*H>hWZ`InwZB$+6D9~|L^ zdHKW40qZjX&b0*Q71^q+*rbDOe!pHbYASF()N_Ei=LRlG=p?paag3=S4U?$pq1}_J z@$9yPg(hyA#v|a@#5llo7Rq*Z_&_#IBgK*(&O&NOml{L05X$>h{LN& z6<4EHsHFwaG(gM0UbVqGmB~*AvXQ$h6E?NDBw$0IBKgvvPZs6;z#M?BPC=;xy-2w_ zh@P#ozxVy`(euTN|Ie$x`juJ;-!f>I+Lahh10&cnv?@BQn-3!?ZeVk%I302xR zL*IP(1zZ47-EY?f(Cs${eEiQX|F55UUB5!M65XqH>G~zN81utHj%;H}Y?utUvU3$8g)4Rx{S6-!wyf`an+q zuO7I+BINF{wcR^U9NPWv^*v`UaReL_dl1^OlCZsPlu`Hp7~af`i2`y;bntM$$(%Q_ zB0;4H;}qKUb2Pb0iGRl?9D&A7QN4TM^?@7s?YYcpVW=3kFV^2 zt%*N(GFB|9iY6d7K#Wp#;EwTu47Zv#bT@nxtFE&egGL~vvVh4js%#U8yLe{}B%oMU zE!HLd4dk?9va4x@Gf}BC8pcHEnSok_Qjp1{_6_xEwp=!-!=f%KCfNDQ=Ut33U*-X$ z44P_S#eEYXo#+eF*aBEzYPb%v4|x2s$G`W7e;9{y>C&%WeH9+J7G}r;(`t;An?mar zu^LDS7PBsJZ$5wkHPg?`!Cjo8>fW&RTVB!xCs3Q4Ki$bQm?RxLhDaQ>^{b2v)%hPeP3?)t3=CksKb<1zhgbI^FxO%=i68wb zpN#G)8ZQSJ&iYWA7s;1amc}Q>;b-rj1gR5=vd#5XKc@d!sWYqYCx(I!pl*SY4?p}6 zWMG!Sb;U#rKS>Z3zFv(`)-%bva=XU>KNgJFl2D#8J{-}=Up9Csj(ninOh~Du(tsFE zFZ&joMpOV4S+$l*Gn`ik$mLo2p?O@4s4rTTcYq(RJmeS#uL@)EqAwvjTpY*_!Qj9E z4SfCDwaKZe@s)9U7ZEa#Mq40QRCT3TD6QyzT^A94e4a>Cslu5x#`EPZu772qcF3Pd zQohfnijq*Spb58?D-m~O_4hdg-7^!3CgYgzau-Ix{b*_%5{WEYk@Hbv?4ulKS{8as z)4R$mn|t&#PNMb~fauB;4rE&%cjWICE6Z94LP)&Z3qH-@Bx?vE8HV5$3w&UR5F$YU z3nUQ1W7}8Tn%el)u5UND6L)s>SM2Ztr+_%tY#&2s9!qe9lW3vv1dh#1=R`3cGqMf( z6SGu{A5|h+Sp}#}6tEz!LReHoYpyK~v}s6BCKU@0z>>e%kbfx&`|z3kZB{|M^XEh; zLH@C0$O#mR$&>tG;RznoNez}obT5%DCuBH725mGi+>gK?vM^mgW+5QP6oX=L> za%zKsoG|>+kAB2~!+-qye|YflAw)uWG2w9wPjaHD5(c6hXjt6GErIStD#j?PMtI3n z%qEL!l8UuT#`4imf4W6qfJx+C)&^AoQI%yf-Q8WsjvwQz!guc6p>L$r0aJ(*3^ewm z$B$U#DJ&PjecGEwg-$vbwH90{vF-aoDNjgKhAR_y8_9|ay z1g!c9lE=)F5jiDt$v>cHskjBjgVR&w=_%nAihw1T@fRJ0;25H^#?|M z2=KOo0Vj0l7m8`Eg5anGGDBph4D6Qyu5^N+O!ORVYL2L9P z6=4;Hl@T6tNI;C2ml2^`V4#+zYfU49MYW3C2NmP%=1MTdF` z?rT9XQ-|n)k3agDGXVeikH2D_nW!kbH!(?jR_i{!AkITplTPdy;vpam=F%^D;uUn+ zBE3niIRiZUsM*O=r|PD)>BNRs09I=Za8kp=2e`JKcfQcxG%Axr^&B1JRWISx5Ne+{ z#n71ysi;YMP~^X=cR;1e$`eUg=d43YhETFKp%+!)H~n`i`VJP}gPf630zc-UsT3tVbodvhboN+Czzh zBlJ;Uj4Aelwht{MQZd`I)YY=MyPRoj$*x~5aRi(6OA%XB<)KV*cdB(Im01uBQFZ`A zHHgs|TCEUp--IU`Q$s{-Knl+dPK-}F5k+vs;S6{}EKvqn1k+Lnt&HP23{>gtVRS_gTT7HR6GU6-IP@@X7c;FlHF|Q!$I~uW_J<; zA%ibOcPlThJ^^>Io_Bz4Rn09#f|Dmrz!O7}Nr#xNR$EyQU>o4(&6}u9CSIh^EQDOT z@asANU1YA@YOX-8v?QSS^&k!p4}bE>KV#eecfb1`CkZ)qf|sztjDv$0!JjI&6&`2O zf*lIJtA?`i=SE(-2LZOEp>B;QjS5euvZeM$R{&Oc4KcWpk?pqIckZyJPfsw-samFK z&=(Ky)?P^=tx=8g*isWk^lGAQay1ou%DJ zmJYTrwiF^5S}(Rurwjc?(-V_zvjx6962189wTS{?G>B{fH>8XBsb&rI9XJ4GWJ$tf zlLIUvdw5}RB61Ydp$%pX(8MNkD;JI$#tF-$7*G)H{r!?{QebU}NdZ_-qR1zI7{nU` zk*DovtuF7dG)f)_OP3)+o*1H~J79^@HM-}-pT-4b>km$YDM1r-A|V#+F`jAQFCP8Z z!^6hN7M0rrhNt7CI2D(E;U{7buJ}v`>8qcTi=pr@;+l0}zi>Elu6_~E)bPLwPyBail;hOQT-GVwWb!3Y%62_y@vH7IAxL62ERr2`D@8e%E{nI1yBo;Y!W;T78n zfB5uMcKNAsx+0Q6d-wPEb$9pVJN4Nq_-QN<%A4F`)+=LrMS!&k-W*U1DI3pc96~J2 zS^FxZ_(`(80RMjX#$%HNEOH+^`a27 z$L|&r^<0t5(iuqr06+jqL_t&mrW7PW1>!|Q7%)^CDBf3aQaMx3x8zef0g;BNmMgNP zQM_f6XVMS~r^m(mva<%z;zVF5@}32_j@I@ZJN5jv-b^_Zd5 zNP-tL)C>iOA2opC{K#}=nzxHcrYaON*P_KYh?ZO3^hYRald3*`;y9B7)WaV?`y($T zdGl2TPdKPvV(=FC*upb#!4NAe0l)10cRWdSVdo+PCr_S)B?RA6OLe_$X;R-ga=Kqv z7gw}v^2`?^S*N4k5ewC)){85A+`~sn^wQqGKIlFZXLS#aiW<0h@gm~_mI!_4V0@gt zWnC%d(nhC1fOVUrP3nF1gd_x%+~nn7uCx7mc8oVRs10Wbl7X{kyVV(#^D5L%L`<@V zj&-rnMi(|eO@siSlCB!NiV+)?YeVLk@w@*@;Md0DsT;B?R}7Ah?V0-7=%csx&)`fB z*VcyJA_|{_?0TcLu$cd+vHVKO^6ZVTURvhjO41eFv`Fe$GvopsoB)Jl1a^$7)rjIT z3(^JAh03cU@F1UF7MQWXiEM+;fEN~shSZuCM9^u-!IHPD1S*c?(M*87omT?EY>pts zpjTc=6bsqI4!(qX^ZdAzvq5wr5@Hr(LmXCJ#L2jde}p7w-a$NjJUwFu{>mYS^%)sE zpoFjH60RQPAbM#6kxlnF(iuJM28-r3pa8LgB%kWtan7+o!B8BYwuFmS#pKC=55wLG zmB6doPsX3H3PkaVA;-PL@%O+jd<7k+Wn?&8maO@BN34@{(UDX>5X>-<_siH(V9g+8 zAXH$|LWFA>k`xs{z;A}iK*5GWMHh~#k5D<}gqGEnrQ2m*CIvv9P=!d_u3ba=FhEB; z_ju?ygB{wV`oK4iSTJ#;6?ZKyaa$bKik)huQQv*{-QnS3)`Cz2cW&S2qaMfyV+7WY z_#PnJL5#q%8SNu6Vq%)jG9l7$2p~M7voQqJ1(iHBi`&R(N8#BF(o@T51j5V9q_vj8 z-1>Z~`PcQWOQhojy5e4S1@abHbU$XDlvY=#RNhK|+w$>)bEgi>g9GHPPJk`QT1}M9 z&R^d(cm2TZ%_r@5r?d@C9fOH#Y;F-WZU*JF2@A){sP;ATXakk>I)fpcTxJ z5LSms2qffBazbn_L1YvL?<@p|A3JP(?jTc$UW@|9$+_pBJdizV2axyjsWt8F963Q5 z=qV#bfEDfT6hip`7z8Be{8xllon8>n9tpwc)nyD!zoKI~5XmZribA>R?35XD%TgIv zHkl46fZAB#q5G3GdC=KVJ4I>{@?~XG*PEUc5)6Szaj0i+9mb2NUpqZDImP+Iu^Tto zocH`F`=*WD0yFu+1R}AhU}mu6WeZ#iV{EpEuJNkt1AnR>2*ZPNojP@jX^^tBE#@Ut z0CAn9*b_c}>^N_Dp##v>YBqaYn40A(2rdU@jfnk#0|)jqSaNZp&v4+E`5>$RzyJO3 zZ{NC&vPHuaoXG?y!1w?e6lx!NnK!vc))+i_&!_!#%dIp4R_kPOZoboRbxgP@3lVDJ z$&hE=$e$q->XZpnmFp@WV-jU8H4*;BtU~iT(R!`oLm$eeD`$tN&J53XaJzmT-WN;w zMMCjP^4Y`FkEYumeco2!Q)RIM!h#iKV)Tepc*0~5Q9+p^eQOPe7SOT|!zxM+#UMQ4k2~&I zQ$moW7)-Q4YW*YA)+m-Kp_qze$c511gih+pMp|6}s73$4;K?a@%~Z$;xrmImvA!AIQFb?r#)yH|Bb7fnH zMAco%DeJw;Vm8aA01Vv}Ki5q&jbo>TCS({@ZXq6qgQU({5O!i17#w*2gZH^*n6;oA zH*R>tkzqGv4CA@=BH(lj@tbE{pX$ddBXoY%ERd6jkv3VZmxs5`0n1~506hB9bf}KSR)`9TQ?oORXQ0O#I9tbBYl#+HOd=E-3)lx85J9f6KmpNsVsJVS zzF3?NMmP~C7PmVFVaI}Gk_(JL-TkFNdjmn z6poE$j=?!1T3jwjw4_z&VlA4fEE0YEqB2pFq7txail^>Q&z_Bfg`5!W3GnN#U(b1z zAc`jzSMPK_o1uP!q2LhZ8^56~n+Xk1A}9Z@SBR4mddzq+$m6zoeCh0@t01cd<;;>! zG|}HVV1k#Ig|v9l``o#=pF9~yH{88@7rM$|1~!l1ElzX__K>v7D2LTIg9~(xs!<F;)^A~F;0QM0lI#$r1`w0 zz?1Su7-~StQVBB~ZgOBUjDm=e%&?mXF(haFrW-6O9wZdT{_rS%F-82?NF4(`S+^9^ zBXFXlxmQI*iTM>Fuj#h6hOBxQv;?FE==B(g1Vqm1!dxZEEP>PE%mY}Li8_wOt5&e} zD|7$^KOabN+5jkc$pM?uk?Zz<{YQ0;b5*GbFKE zO_D$x4S_Wha}47&IqV$JyBab?5iABkpwH$S29^12>Db`>|9pP@ zVE=&CH>@vShOt0)6x^s=0=e>{W6j$av0H|goKi%W=FxU z+OWNGz6_(A`1CFXdw>ku!XgwFLXl7PA+|9%hCxYyTsyxCaFYzvChCFyj(Rj~wzjdC z0PyvCBfr>*huDY!Ly9;#V4_>dY`c+q_WVO96izG{Zwo9Q70;;lRH3omI8|?CLjTRM zwZk#uQi&TWjvqfR=It^s_YA;miDse}(bdfFc>V9e!v|<`njBxmjwvn+K{PySfER{2 z3xovFkSHxIen5!J!7pCEc<0t_L>ZZ4f0)k!a;4+YAeV{uAzL%kGi)y-STwwh_Y-R- zmy(c^7j8JkYC^J349Rpn8xr)bGtwnnY#21}YF|8mYHol3a$AnJ+CI78xMbApq5h>G zpPU$8U`@qbF8q3|*yHIM%5Zd{QAo z@Qi1q1dO7ToEY=KfWgz~0!O-}^AHJ6M|mZLQ7U#|6s^Nbl`m}Cs9Euv5tkX2)bdFw z7_njK2w1FGdeIjv6eb5~HlzwW;m{UvGKy!fVxVuJqq7~=pzn}ULhsj_6lgvTGEn&d z1qTlC93DB065yccPh+dzIbTs&_ODBF%Mx1xx}L>$ zdl>F>bIa>zUhnDc`Qr00u3WvsxuCZB`N@e1E)!+#h+_n(7N(7v*^KWF5v_(y>fSc$ zsfPkDRP>D+Uc!x>#zu}UT`t+QLd7{=^GjiRT8k%#m)|`x%NqcVgZ|`{(s$%bXGZ3} zx;w)=0W(Wk_0Bb>x=mtl>+q6{*m#!)kDQ=T(%oD`XNo*-N9e!-Pevy^ffz*EnL|K# zh9@|ssO+qyqpEZA^h9~O#VN#(QvhlxgGCrEnASL1zWjs%tbweUZAzek@>BqUwbppF zso)Xf6vg6KoZ=G!OE6ByNe~a^cI7$Edp3lKprIx3ic_RS%c25fL{tn2z-Q z(^wKR^%7eFjO4943lVfC0HE4`J(py{x?BxIm8=KHdeKLz*TnHm&Ip&BcfI(ZDW+#) zq6SaxTm0ng%&EQe9436GFx$vPV{kSp*H#&y&fTBZdsuZ6U$3^UG&Mz1`z9ih5J`^g z*W!^wkRd$cnY80{gO?lk73ju>@pWv~uh`ge(2hY8U34 zf#|Rs*yLq1fC&G6QebENfqS|$y`CqdH=a=F{(u1a=6A7i`>BWbR6B9S2&9l5Vr9P=!Rh6kpH4?FK{Qu*R zKPF%(+BR6eKQn+(S+o{lee#5^hQ4v*hL(f!yxk~&yqF3w#zz_$`}28dc3oIVq+KyE z@B#=M16(D2`O+l>iFd>}J2X8tH8(rU)PcDIFOKt~7^(#cL5rvtBY$z5J>%YQ$f{X1 zjjg7vx+L522yvAfY?PG%53aHT6m$f+YmsX#fh2jEp>a>rGAyanTSFQp{wQuoJ(R z9XlCT6V!0TP9zCGSYm?0zVXHz>xT%8n{4?WDgdh?P0d)mJ9Fj?Q#M{3W)xsLT#KHp z#xevzEFdx*;B^6p|M>FO2rm#Y>3ieM8Q}iXrAyy_J4TDL>BKvM6XO%Je7%oZLwg5z zl+rO!EiP3$w7&)&$Of{M1ljscr;}l|$Wouv3zk@#Cm2br$4uIoq;Hjaz;FF@<(beH z$wD(8+YynSr8Gq1z;Rk{4`32agfWGt7dymx&1@l^J2z8qT7n$6SAdVPN5 zz74!;wc3og8vGCy%ETxnoP-!3d5FmjmQa&UF`)Mx5uFyc{osD02?{Fd?Aod7l6_+JjHh zBPQHE_uhN&GezL4(T5Kn`XHShfqA|=&s_mr>&P`;t@^yo!aR5Q>e3-=Tz~OTqL4gi zwjC|`@}$8^^PZmK2PbBZ4K2`3s+Cj&6@!d%bbDlI@r|PkU)^8$cATAxHGO(*eu1|n zm%+D<1_hn#8Gsv9uG)AvC z`3S9L_>I>p#BZ1`y2j=akMQ1)*;voQr34M~6oGNkzgFY4_+h|kiNlPi;zWOXB#w;4 z=|so(fbt@`X48CI7>WVMA#)D^xDSy|9q4#roG0i2n4}qOMM$p*>s076Z;2im9brg^ zFH-?7Fm+X-)NFXf0FfLLKjT8~7G;f|g_IT^O&tG2OM-f@EXj0hlVg{M>J zJ`sZD!V)huvYo(A5t9QKDfJv)*DmX&b0mp=Vs|&{Ff!7{utPDc``A`NB(ur9)VDt$ z0zMyiRf=JF7*T88)y&@^p{j_g9dxU%eCd-n=YDi*dZ@FY)ANmOfaUgld9jckd)T^A zV0}PvLza22XK4APmorBsD=gt_|1saHsc?aXL99IpC&?0a7=oBphe-q3kaU&t`iy7|H0(gn4e-X<3ECa3j&R}dD07%l(7*KN^p`VW4F9m_VDry zD%;AkjP*JvTcgL~=XK?^qQxNY(6(D%ENFrEJh}9(zpuZW+v3s&{p3qpe|7M##I0XbWNN_5on>0UsF8U9 zh4Rw*lY&@5^@z$z+2yAyP=)djW4aWFTEt?6cKY;bE`fm81Z4Xj-+u+*f<@!8&VweP zkz91J;3ZAqcf<1jHwW+7i?^()t|PL$2?~?jlTV0o z(vfdW5$Jna;3qgwoT1W~Qu7a|TX+ib?T-g=ExH?BL?HmzAk4n687=@a92cz9qrFr6 z3q3ykR~R!yqoB4A44baDu=g~vEX>VQFxGaM(8CL-7t?d@Pclhxo=gHv3+P=i(4Sl7 z2?Boz;>8RU(P=5EWmPrPFN1#lm`%ID01Kgb{`}`Z=d4jO-R-fz5(*$xB?^G47WEE% z(|FLSeHsK|$H@y|+#tj*F_Qqa1=~Kz6D^I{_4W7BJP0uZ33>=$_2AlezC*xn5<5l2 zL(D`AgJhb;Tx=5fXEM(R>CeYqm4RBouXR^5Z@A}fpNwb!c^reV6{ppq({v-o-s*I;Fo`4#kxJzz% z8C|#i$QEHXbREn0VWyDXhMGELBD;)@I}uT={?z$6fs+M`evKKhRy;C9Yrj!EINi9H z+byS9l{e_IQ@Iv3eqsrAqT*A9hVeF@*D{VQNy9iE+&Ab4;^kwXP7~e>@%hK8p6D*P z82zOQJPsfm1E*V@h|?`n2%2)~kJBGoA?WR8s7D^DC(X{1hC(2zT&yI#*86DG776iU z0N_AblC|O0MaflxUO`5}3CieSeDX&Q)vFkXhljbC0DCgo?XkaF3IGwLmT7G6{Gn0p zlz&08Xu?T*EU&Q2&pQ7c5@N?mdzlU}UO-_$L3#A`_0!~R9v}xpLxXGs%+AhfFJXS3 zB_>`bU}uRgYoZmN>Zu!;4Uu+DxaZ@p3SJ}FvIvYGV zBa!inQx;^RU&j7|7eg5K4kDDdp-c?@4ml?+e*MddQesxUYTuYCf?UU2uq?k3^{rv=rK2_SB;t_cAYo{s+R zOO_zHJ%p1b-WIlC=o%S1KKW!lhX`5g1rszj75E0t+5F+6 z2+njOb~F{JO^<*FkwOb_Hyka>JOEDS1Q7idK`6`*5E{lCY$E9dQ9g&-;3^?B3Ndp= zi2Vav-^Mr^>ClTMHx_x>-cd)YLS~*1(w~nzBqB>K;McmVnKw+xg+4q($h8y-PxbNy zz1}&r_$TLPj_qEERtG|`hPRjwD0g+0ACBkmPq7dbZ3a*-6)iS(%K?Mefbr_#Fz3!> zkU$|m?08`j1EOgwkYPdq4Jg5%r^}ME5Wn>zq6wQt5XE88V@$%h$k^5|NhlSI%0e1z z&BJ5i>ClF+GDgBIU$nslCWNL0-25)39N$#%n<{6#c{o9>6yS_Ej&spofLQ zQnxq4m?{`O5t>w$aFmeuKpUfZDCYgR7#A`XfKSyCX*DmQ%BUnXWznbK=;J)82fj(p zW&p{-j{z=tsv7-i@fhxF)eSm9q&|ElNHa0%VvZP{J$v@((W92wc0XQG1wb{^9(;ig zaPREs;usG@cm@Wv9DN}Y%0iLR0baB>XN0&VlmP--ft_Szi;+G;fJhM!a|PB135(ib zM#)Yy_w^!7{$e-MgLvUns77VB=~ zYLU#N@%-fC>cx<8$F|Om!}@>7uNvQs+TTo|)Htn{#DfKc#e?`$6w#uIlVr*E0N`>? z$ryI+1q02h@HoW?(BiC74)eoXI3>GB;yE{R6eo6%919spdWGy^!I-aGu!kZ*t}>3) zkg#|t_ZGSuh{w)odgPpRw;Bt~!FW78p%~~(L(nmCC^kcF9;N~mNJJ|OPvy8p_kd#P z&b#;QVwHzlQ57YPl?Vbss3nk_uFaES-@Utc`DzR`j><6(YI>dBOsPc13@{#&RWciAcbUGbv!P9E$vO z^L*S@@kR0;XtnO(Axr2URn!-9kY)CMe|B!}!_zZ+I+r(0_JtDIu0Z)rg&Sp-%K7od zp?rI{js|i7fGcC{G#Je-3(#N}ldT@K$!XnJOD%@!$N+*Dj5|XKa0a{c(-3alOYP5p zAPPX%9Kg^#MtgJEL-6BKslKr*Mf<^9ZZm5YfFd-0&jwsr$N-^vx-&2Yr}LOonI(dx z1c?+Ihu{FjP6n2ZKX$F+zbHfwzZ;zNECYJOcMoCDm<2t+U${6LGT?9bEE$K*$gKjY zLW`WvPKTxMa}vC-x0fDDeTc{0<^)&_X zHK1q;VEC_%njE_hsTRimKls59q9nHc=llE~z_V1QO8^6WkQz2-RLrHn1_Qc+m^T*~6_b;q=te3E4l;u$G(g&|iFP08mpV_a^MGFuF7s!dr z5?F&=_%Z}@N?ZsDqJofM41yv~Du9rnQ%y~HJT*iyJN{}=i%tHOB70iE4L92Gh%!Tp z89sJGMaRGuBE%wN8enbwPQhO>+tV!yVo8$-`KMpt#DbHe@eFDJVjOKaP7^%k<8gbm zF{jc>1Qgkw9ui-tK=hjAiGhfh`|_ktV6b1e@Ko)JEuI{RL9G*IlK80!m{89w*G^7M ze)Z*7J>5O5$g4UN#&v*FVZn-v0Qkl-Z-=$DjgF46dc%9B*REZoalI-mR;f)*0c?#f z(3PSlT|E3~5W_*;F_TDYyREOf0`U5#X%TB)`$7}&Z5dR$mit#qEbgPO;aMmy6dq^~ zfF&c20}T%kgX~zw#>V(UJwp=abk@vzZ5Um`x8^w^fDS;TFiu3OSP{Z7(6ALGB$ZgB z@f2bbcekx={umIHiY+B56YCkIH0k9%ElEk*YkZSovG3_A{ZHp+kL>218tab9kT0D* z)pEFJ@rOsI9(_K0e_D%KU;$@q7!3LpP-0+%W#9_DFzjIXS>|*ZBMHu~Fn$#h*+>>V zvhy!cbR|9i_=BRKH8KGO<8`Zm{5cQHJ_;ycv2a4UaPz2FFbVQ~0kRs`H;GlXZ%nWM zn`}_$AZbcLVWT_{UA4fd>}HbXfEfcL`AMb(bJFSf?!loG1O_*F;dG)~qI7Y{xK@DI zolztsu_M4*WJ1z-pz_9)R`8$OyMXbKS?o zM-M@EH6gFA+8~0DP15#k8A%Q78seS6;RD0hu3!7|%P();x`h}a6ZB+e3#bid35XGD z0K-M3tS_;Pz_v4U1>QSj^I1bbk|b)0nA=t}vlTWU$V4$yri?y>CGVxZoc8gj1D$G47B)V3Q(JwqPV z7?DKn0-yc~=S1f=J>51cxG|JsHmVj4Egm(IDF6$gFCj)M07f-+RdXw`dL}6qBps!0 zd5B;x5h^ewXtILNNr5rSs{DAI5Iwj*sSU3Rg*Ow#%=uTD;ZtcT9;(znXeOK0m*q2# zK)-P)^+hcdl}k5n+&HlRK$qPQ0d%vOz?}^&;zyx$aZ8y?qn8USIqrpSYvc9Zx8Hf^ z_T9VO97E&!dM|miC4Z_!I)JM6zbF#RIwT6UXZP;?2e$PSU{N08pQZwcYr=jJ2wH`8 z+~X&X@7cTO+p%xCRvN|5SuI+Zwp?jhnVy|yvzOa}xG#tmp*P+*!_v?<-+XiB%9TeC zA95~%4$pj$5djTtpPTPuQD|~XnZYj3eSTjI8`=X)|Kg)`$G&sdr}xgzFV0L)GqanWn`8RO5`h-_Z2XG? zKqdI(kI_J9XBVQyAre-QHBId45fIwS2CLer0Tzz1fJDfTD*zpi=O0pK2nNH%-OXc@|3oONd+!5`7x+)9s<-rPtnwzV-s3C8LvQW6l8>C4ObAnI3sRg(<6 zV1uera8;2TgK}wBu$i?iJCcNxDf`V^H#yJE8^ZEb=M0@hAw|%ZUSWYZ1M+zq_t>#x zZ@u~Eo!hrL0HXDMTQguTK)i5!YCLT=z#L3U!Q#Q5VJ$%k?VM>UfI4;WQp7y)o2cfy@m|Yfid($4Y5%J zJz4^qp4OWqe5;^+Mu$hCfDY_1bI^$1UMC+fGl_meUpzFh^4{xti_}IKgK`rl;rZ?l4w=v=wN_{y={Y z9dh=di>t^wI;138Td`9+$es(2CJXV2#w10G!L}Aum5zk^9woz42KDI@SK8aXJMthgClT+ zGf`Ch=w(kumPCRmQcnm@4*q@p_18@CnXe;lb<)4)BllHx>hEQ-=I}ZkCH2 zzWVB`3l}b+7|`*Gjaix2e-uVRI9R0DPQ%22q*9C&Sw#@X&Y7kHSikO5DS9Rod6xSb zytCEAKwd8g$}q)R7l7|Y_D|YD(@a&_|eI!4laDI z9!3S`m(&7Y<KFCzJigiI8m04uB|Ievg;zB+`kVVEQ!D?CsG98|M~3l03e0krhNszrtS z*HpE|6mw~+II5U&{RR~hMaXh9l`5*C5evUPD$FK;KYz~w*($) zAByTqm@PJAc1sNQp`b=k3~_|iIrT$jLSu-=5f3&4b`1@2jK`B$eG)mkR&AsEfEn+D z5{C~TK6maM>%ffgT+J|HV}Hc!nXsyC8o`GDWS%tzQuL|eYRo%fYbt<^YTd;Mn8u(6 zxDi^b`3t%^j@HDNojn%!c?FEW-FtS^dr|4!Kg!A=Uyoxw@&3L0vvV^Ge3BrW1MyiR z<|PuA0$45}9s~%YltU}C$u4I4yo!u!L5yQrRY%2W#1fBRGz2jMbZ3hNT%1YcJ={x? zq`N&%!dxsSB*!^%{2|l^QMp%=fsVyR%BK%3y>oo_K=%^4wEh?(`#xAuY5DwEarUp5 z@+0M;?ouV{N+1I8EjSq*6z&?tpcU8|DioIHX6IQ{L|VotqzM=Ypctm6rnyt(aq)5b zZkn*fAc@Gc!n|srYfB-|3x*m96CtY>+Bzt#ATxlD=BA>dR`h}u28?0-T8r4|03qgT zgX6>)h(TPXKeb^9mE<9nK#|bKCdspbo#1tSiOT6PU{5-sxPief4*b}e8mShdH!T^w zM>7U7ntDK}qi8KCTX58vd`;RliQ-M5bc%7<-Me?$*JX{D(Z75X#xsLHrL4U8369fV zOz2-bb&9<}CIKjEMvbtMs_)9EqW$W(v_AZTXkdtPbaa%1B#F3pz}i#*Pp@=j3nu35 zoDrs_*&JpZr{Pz8fDilB%oG=hc6aqK7l8AF_dhss{KS>3S1w(;cKMf%t&Hv1gGpGJV<5Kp}j-a=}9hR zUOX>?+x=<5Jgf-V$~H2Lcr5uqQ94# zX+THj;>yh5mpc!qx(?PLsY8-;QhOXNVJ&d?ZU{&C@4iR{UnqR^_#qbV7DOK~OF$mv zn4RS`8f@qjpkH$WGk^;(8)!hmP@hNn%popu3o1aTHb|vHJ^OvI_le4+Tvi^oN3A zQ&{N*%5Qpw@w++Uwg@0cn!_+#H&msjr>430pU*?Fna{92sr874f~juHWuTv~idLrQ z?%TJY^E^!b4IE00=&UXXO0J~>BH)*uKbj@Wynz@ts-~^?+u`K5zJQySg3BDY!tswz zd;h_Gup4m`GV(D|8{)B?&x#NqrXLy_+`DftbAqEskACsR7ytB6|HK~g?92>n0$@D> znNPrmVG=-Br_W>10gMRPFhHu{p*JIvh#G>6#Bdl1X~MN>McSH($RuyfzISZl%;EX& zHkPYb)y&o;xRbF~Qkjn8uT~x$Tt56S%Q;>W+^BT~QS#S++7c{gJVA3GJ$9582xtu6 z5h30<8Gka)@nXAa06fud0xAI;=uTLm-&PM0p-!%`1c1Pi))rb zbVE8@%H+zr#Y{4!4Cn?#exA2&EciwhZe>&fH#Xjz>hh2TCdA`a24|pnDB-(Nu|x*! z7tjmYq39VY0Bm#|Ezr4#JnceIV=`Zh&Euhx_)N&{+qZAsyfu1cB%9+EpE@~}Lg9Tg z`>qr^83oU7@S($pnb<8aEiuFc>ItU8P|a~g%W0~hqrDAqs1xt@l9~$O`RW@HBEa5# zoD|*7X0LAy(Al7~MeG6wd;{xT7RXe9Ge1B1=}%6aIC1gf#ozz__gAl8MLs}$4b{5X zFVZFhi@#_B=yRMfwb=jxIbw{o64L@#6|qHZ-L{?_R^{K`2C|RbRgooy5d4XSd}69u zHpfMzZQ06+J%yj1n?BI9`29wItFm-9{iJjGm!*l(mBIHbSzhRWriu;q?*l^%rMJYp zIRec-|t!wHGx9m7HnKu)O2(AOqc9U5q3S?BCCr9-@yrIfgdFO|%>|nw*-QpXE52 zB*gGbG1zy88ERmdce)zz2?Q|!Ow241gL1lT*c4&gQ*-W;Q^OYLzlJO)IGChv!0F6^ z-z^jb3!yw9PXljrtE9I4#W3Fpy8VZqSuX1Qzo`b1L3F$F!GiijSyJFcT!ausBwgxB zG3V#!SkIx)vb@8TOKRO-;E%R;<5H15)UEFZg9ax~9OpO?3INs1zz#ql1IBALtW~V& zY$($O=6`WCJNDaD054eeE}*>a18}3vX>w4T$vbq!g(X=e39;ZNA$I&&HKGF_KYpBd zOisOa>eEj@{lg#rz%CQV2I$@_0{B~FZEYQl&N@3fnSs)2c_W#X0q&~f1bI(SE4>#Y zo5X@ux-Rg_DkNWdMZP8Pk~8n0(WoOX*(&}=2xB5iF!LawhmG0o7q28HBvgfma^{*C zi2hJ_;r-X<-a9_i+sbts$tbF#|9&~>WQ(|+ zszxOsZ^Rs$0GwkJm_>h$^*Msd%yV%G;bGASJOlKRz-gwI96+9$WahwqCQK@c0bs@G zfTM=*8kX9~A<8xFhzBYpB;+2R6TAo;v07$o*n(FwR1GmWiPH@fEIf%HgIzg`1uyV8 z8w>(_!M$uA)B-6WD>L$vmrju-AA>uBYpw)dUZzviN$Im} z$gh-EbioE@wditrJ);9Svdis~90D929ldemhL(9dyI2*V6u$5#i#CIG6bPmA&O7f= z!-`@@%`1Hw+-B80h);_$q6f{9B@Trne@GJj1o^Ou_%d^L#EVsaE}S@Y=+J=!2YAnb z39FCMc(P(kemJ?|?~`&E1fc}<8Tm7_l~*pyXK2t6h%;vZIqm^b!#9boKtYCIFO^&E zDY|bX$K44YlS*Tiw+E^MY$HbxQwA1y6s@NbJp*TFuJnVG^PilX+TYEMZ&607SiiRp zI`&d#`Tk02SGj#(C8Jk=x6aVuZfHiolYlb?yu0K(3XCWC04f(-VPV*@YiQTLz5Dw5 z`w>oX5nKbU2+q7ySY!iITdA<6X)9{aCe7sDkTrelR(a3KT3`$Ky;i` z8t-aRq1aVS4d2y!s>)?!DXM{~1FLYT;X8Nka+Z`uA@-0VQe!}be)Taho$}&6gAYFV z0RO6ZcEZtA0Gm+j$QojStn~Kuuqnrjo`@ZDK!gTSq0vc$6(J0RH0-SHZ~)-=i4&*+ zGzWB6ka&BD*9Lr!&yWGErvt-@*fH>jhklFQq$;OT1Ok`V1t#=1#L_#nNvG6;4LWei z1f`&&x-({w)1lh=^sHkPOFct3inukjX?g3=;wNuSy|sU~ZX|W${eF=$+1b)!x||v) zckJggE!$i;M_q{p%RgBmj^0D0@zZ+Y2*g1XboX?VD_<01+;4cKZ-kjpsDbf~p7j&} zzA(GAhtfHJa0+-6CeVWgPwe687J5pcgb1Mo;|zqvqQOsiQCiwSqk26I+QkWU3;sr(q6ZyKK1L*~tlv_59OG0yY4~3~1&}sS{ZTUjJ!`py zd-V7*cgaAZ7w1~_(MR)@CcwZmGBU!EAv8g#mYuRR6~HFfJe#__4#qML@kS^5*%)B> zz{kaz94sx(FEU48K+0J-;*A0Jhk4RP>6RP`=RgoE{t~{j?x*vK1)vXwZ9tBKFehLg zz~3O{-2qZjuZ{PbOc;r~J_5@lHePJG5NrPH@SCXz_pn$aYuz6)3oJ17Fm6j4X6gU@ z^t+?eZ5ipLSI?-<^DTGEg-o$+w36?$3f=B%pecbB(4XZZ5J~$4Gc#OX!WaU5&^OS> z#ppr1jmb__w+7SvrYSelPK)&|oT1}~v=vm`c<8)Mo-|21%PD@ro73f$n zLUYGxexv|w^w0PnyASpmX%T0g;TvjALLQ}^G!k4j;Lq9D%|Q=N|~`Bc9sF&dh4yx(b2V$Y(J%` z05-qQksg#hvv1CFAQNor`WFQvx#JRFUieyG)`I@Z3eeAN05vc&GQ!-QttZp~=fOF| zN6wHzVxx}|VJA0Kb#$=gOYdc~0WqbgLq|Q(XQVNZd7+TTS9+=!n;{Ap+dzpt;-3i0 zT@4VK`1sG?)1lnL?(Hu8={u7j9-SKO{2tx;6IahO|8fObF;je8p3juJj#jc71nh{h zCge;J%ZVMV3w5^Z8rpmJ@3E*&bFo&*0U}N)fTJ3<+W6FG0p!-hA^^aBWUu$LUb!4P z!xIb*cDJk-kVIg>R~Q^Zna$k-dxRb%_bNP222?+qUY_L-S&|H^@R0$khrx`ryIk{6)Jh`+i&Sr=wMd;!F?(j zeZUmq=#it`8-Yr&6n6O0Q~+Ds0Q4fMf&PBZ`7l^#Dxlq9eS<>7E7AtyS=(WWAL-_D zFisKh{vo@>s05gK6^JvYGqW?q!{U-J{R{Ei+HoR`C;(1`6A4kFA-F(>RCrHa9f1=b z^k=TJwQVTjyUxQg5L#b|r3Pf8%(+sqfuKU+SN#tf3qGg!FaFE-9*ztyvC6fzwfSdgQM zYXY+Rs3{r%T0>dS8K9Q9kk!Bz=zu*`qN8gF!hL@?QUC_tN=(SkpU~V!|2_pUr^&j* zy|4ZF)A|NM#bZymtfp&p@&g^kK;#_UV0@;YwayH{GtagPJyhB>m15=X+SRL6FI$5w z)l&a-PwE!mb8nv;9UTqj-C0Xh0c>skOGIr{1)y_Kh)rF4eZS1?gIGUyn=Nuc(5>vYqroiA|6HTrc zGMRG$r_ZWayCFM5aqazLS=U$xx4v%7@UAfw4Sn+3{)L~vGx^rRnS9pEx^9%OkVlG( zfhJoPr%Sn!O4}~#u0hJqNB|wc!DWPpAu1Vp--z0br=afq}ud z-#*8b;<+kq>zJDgVC(9(DrRH}`C$1KOh*+UB8W0?i?Nu4i0HliN`>`(M*Zvt1L(XI zc4YL(sn=c`9UbKoB1Zb0=fB9p{AD^B4;nAs%oX(^+OTFP)4QC{ZA-qwX&Xa`PC!jKWz68zxlo9)1Cog3 zu|>>h=eSyqbtGOXU=5dDVo;vJnF}#T)ABhx7u4B-knu7Qy94M61ec)*Y4Q3H0_V(H zIyEC$CxJ-XZ8BiJy-sxq^4FMjYM^7~Pu_a`{_*L(UCSFyChElaDv$t!Qd{}!(sI7o zezb)vBG#+kou~}P2TU{>YH)Qq=*pmk*(7Zo6o9ukViOvkY$okEk)veZaRxkfFB7!r z;r#Bv?j0d81PJyB<-`xRaN1wkuNkw%7a0z6Y*8q3QKCOxo~0P%)R5mYCc!u1_ne5s zoR-e2N=W*8X6TPx-pJZ4H$8}AwcuVgPkg8`4fV4c-Z96zclREt@Wu}m+Xl|Q_ujiB zBO`Fugxx7;QvvK$&9^XgXC}zr0J6XWj-x*!i>#0*#EzprY!9$o$CW`0+1MLE6L3W> zvc-ik+8daeK^);{*#}`}GQnXrMsw^4(<68Znc%*S%q)Rz132l>^c2zpvL(q<0sQG< zCmhzV4S_@SkDkeA5r266$w#kG9`0LgqW?Oz1{@SxmKMr|Y^ig!lG*vqfRIm6kLe@B zi_4cUbMYt_nK3staS(*TCfP7Jm9{G583@_Hf*}Ju>0hvVX0YJo z`c>WrICSu!M)77sR%B5Zr!}eSdDW;keFkkXgU7RZhTFs9dz#en+Q8t z_?=A}QA13Tl>@`Wh!?Mdu{8(iv)Y3fVumlm$MGTVrDF4d8Nlf1=>C2CIYj*V=bwM} z*&o09>g(%Qua0Fhh!im(UX+Lq%Y2i&B*F^oU>?kNM+fOi|`ji!eoARzLIed8cCz9i(4K*xd;w|H!U)MFEp8~(!JCPWfR zn$2D9Juf@bV@$T4=7WCVU$8GD{EuLr+``B+B#p(1A%2B0;}qxT$!H8TU*ucxTne2xgEjc(~`V#&dLv? zd#?9dTwVm4^)jb*6;}V*9^h2~{zgYed3TJHWZ!)A&1au|#&y%o1~}r4Z0Y5LfqqVl zN_NXd!ZY$`yO}L1Ui@Lo&lxo)1IQ)oLR>R}q$1pO3-!GyFClkzEU@s2q(cJ>fBN?0 z!-I=$I(J%!tw~DMK&mx;t!w(<&Ey73eW&b}h=!;#B!mIM?p?dD=dW8UA^8{Wer3R;=VEOYS}#=Vh3~?;)lV9@LMxfOG4Vfa8#V76T5pkJ$Sj3-hshRA7Q(WF%!$S z8yw%%q*`}+%P_EGq{T3d%e)CrVVH8!ZP`l*DK>(osQ?R-xZi;;85|hYRw$;9kB8yvH&X*#7mo~-@C;-#|cZdS$%mxZYT@FDJ_y%mLP_lmWi;h}T zm6b{=$5>3qEK<321uFd91dW9YA_!Tm%g{nVK3}eONiyWqm4e5xhJn!@_lrK2!SldU zEo)Pr1YOwkrU7D{$@Lh3syOo$o148;S&%Cc!}Bnx_|#8ZrX^i$ul%z3crPCeD0Uxj z6lttzLx@=>MgYxi^%2xsyR4H-#KTd{#kDRM49?@e&I!dWRZorKeMm0!rlvh8t2n^6 zQA9L9g_Cet6vaVdh)fd!Z30AMOdvz{Bqew`v8D(RoerCOVPPBwtZR(V zDyec**Qnme9$6p=eTw-yJoSR@C02(x1q31^{s<$g0Xi!`+$qFhpW9{m9PtM~{K2IQ7g-nLi^LcXdNb%?sTT$$ zSMP+fpVih*{OnDk1Gp=OkqtXkyh;oYN&@wR@an`}Q8!x0W5E>P`0dZlzt{Kpo!o=m z?WN_7AmZs|e1$lKsTnTqM3-he|4V+QQa|il%wi8erkN zprz#0{H^Tre@<80QYZeGQhxARgKrI=CVUm0RBuW48evB4mG_po|$qj4@%G^xb!5a1ZpEzP; zQ7fsKpkJKC$rD4&;ps$YNYcG@Q5n}SDM4h2(z*um;G%Naha)UKi}&v~s3;4(1COQx z*nvuat|Sp5_<4^U5a$diXL|T5J!5~+8Hs29j^Hytz{zdW4&to*{P@Q|e)G*YfAW)` zT)cGg@}@ve>w3I9Kd+wGME~b%LcEZS0biR&#{@qF6-6Vv_=KYbD4Y#ODFTCDJ;Eq&zAtj>Y0OB2N$GPD@jH z!vd$w3kf~5E8w5XVZoNGA(>u#%#HJGxC}K;)ihR4>=+qx$QxwfIFm|tiTR@sb0LGP z0dxR!0&twGBlsQ&hLb|PI`Gj?KjOU5AOG-2wu>=0Zrnfzpi7X*&hAd6+}8o@N&rrX z7KjAH5))klVpm7*MEBGuz4vyvEo3t4aZU4LHK3X+7hA`_tX%##?ZEY+KaX}D1~eq9VE5eOiwX8n1cECS^EFh}upO+n2>=fr zmEl9DY2pUoH8~KgJr$IHM5P#bYC}M3Lmz)(>BwA^#rR>7AwqY0DC}6Ah{e4KLZa9) zP7GdHhy-7{m3o$4<{#Gwp23jWNHL(hoJzimDNWFsH?T{=NQ)s?2+&ZLrUGcF=6I&e zxUh0|gM~$%HSX?40RYx)1~7g{rV+?82ZrX0I2p)ywDVcvF0CN#+09<@^=sEZ|KjqO zUwnCEY>XRgd52)np1qtVV#eTyg^+c6oiJi?zO!`q{l16iyB@S>icz0_raU&8)~x}d zLTNdB_jiSSfA&mgq4#V(SXnptrg*UHOS^yb^*1^fz9x$sfWc01MXl26j|<^B|3ilg z42@%HDuBjmi|5V>az+@f^>lV3&a*SK><#!ae~t-(GcceJm9B#LT69qct@%82;XM}=GL%n2>~SHPIB7)(UZr_5E9(=($tyXLfi*SLBS-l?uqq69a>8`mya>bCM36OaOT6ObWo} z2h2G&QGide2vZGM6%`_Hk&t_vMwnmKz~9o*gIp=Oct^qW;H7Y1&o*>2tQ0JJo_VTE zCnnmtQ!8(oa^C1^y3lK=N-cV{9Ht`_Z{_e(a8kIo5S=W7Iq|ysTN%|y3EUri=0dA>h9)~14oY>{n3wpbnEu*ix)0@`st@PFk@rv z{jx#8`-+G5b-b6qdA{;^FuxFW>+h1oX4vW)piwHb)7dNkZ6(!SKJ~v>+ICm_Z=nlF z8oM#}hd=&-RSjMZK{~+3F#ZS39LdO_FOJk^MZ@z5%~2lsf|!n**i|kw4x-SB8E7Vm zLpPp2e1{17Lp1m%7p5cx^U^MYT3H3|HUZG#dhshbG=C^2jG~BDi`#K}W^iI*7~~eu zEkP`gdO!)r5#PPMM4&&*s_c=lUdlW`1=o45RfEC`BYP`Cqy`kcq}??Ze>i~0PC!js zt5NKzt*HQZwE8zWaS0zsdKkzfb}aI;(9ii!pr2ltO>-T&zDwo$z6jn(hYm9$#o<9&s*jbAy5 zxrjU}XCg+S6${2Q;NY&{X0RmDUs*5%f3Q0x@Kavw{sjH0A`}2x69piJCnew^qG#+P zYVyqqR&K127wK}_3YT<2HcOWLRf(= z%D}+ay@bT_h~PMi!E}SuL2co`jls3BBSen0fEJdbEnt>?Kw*5PQ~-O@;g?XPho90g zePD@sA`oRfdsr;yTJ(qLUhW&pf`!N_DReB<5^Ojr6xh!~8K3k+0f?by83=cxi3ykosRbM(8Qds-rA(hG^43s13+tWDf8fR>hizMzL6{L z{;-_R*XR7sRGJ&ZF|gnK<~JyQ&JTda;3^U8Lx-^u@UaVvGsgXZl^XTOx57m_!;ziW zn$v04^KED@K2fN(^pg2$;o*9f^&Y2<^zPi0!{( zlEtDI(Upojc@mwmsDM~J^Uz_#snin2(VzOkiziO^WZ;isWtHo-m;)%FvYM!`L=Vwi zMVlB@pVS1C6{13y_}M%Pmq@TttW5>bC@rxS`7n{wWR{hECIoaZ)PU|aT-K+Fc>!4c zB-O&qfb&9}|K~K(-pZqWPyVKN{(?lfX||yT0F!dD{r0a4U4zAJf6L&T0eLquaby59 zHa7ORfBU!G|IEiB){9&K3IzK&H`W1x**tYvhuEAb3aBbU5(Q^KSl|+;jpGH-a_T<} z5pa`;aVV7bpe+VbN@bQ&6x6~9OtBKL1@u_$)n5y3Y>Gh{7uLHa+Wm4pW2YgApJf?i ztdebl;N|ov52hh>_($8n?L9rc4CH+spVC$-fKW9ggaY8Y2(4tP)O0DP*O&xL!&#dO zpy8TiD>CF=0trR|Aj*g**OYT)h*y8PTY|r0ONl$sI_DqsP5z>5;(s8#s|&JQk)dXs zH8oHwXRiHyKAkVN`fYKnv{Zpq4f}IB!Bqr+x55AFum0-cgNFBq#3OpDn`tc?ovG~KeJCdl&)(FI=0%(L**uFf(9azH63j}lt&I|EM@!Y~}%k=$$nO_ae ze43hnqMOQ^W;<(u@5nEYx7_-NQm!Za`u|bP?`D|2Dd~eg%gY6J`2X(j{*DX78P9@= zj{XP(Ft;`pq{8JyzS<7;F>f@+k41=6F18D6=oT=}0hc(HN_^NRMqwYGYe26{+z2T6 z*Qi-}P&{ZFK|MCEJj5c%7F}F2;i;4(x0~p{oehypD>HSkW9+{cI{V9q{-WHz58zAsKDV9oD5*5p ziU0Puzvb8u+dninw}luC>KG3efx9id)I=Vj(0sM3fwBlZ5l$OG!|BYySBS2`Iw0*M zc7Bx#L_v8xBsV=kP@z9R%7r!n$P_;*2Jp54pdgBm_i8;(MH!9dp}6r3xJYAnr$_4$ zpCme+Ctf@W6#}}2I9as^{aL-?B}xuba9j#zrDZETuo2LW!TqXJCxB8cSe4jmW21$` zcnq^be&Rq4^xISb4b&Jrl9e=jRF&Dx?8VHt|95`j{!<4yJCey)AyEhW^z_5j*T2ZM z_O0yyd097x+UWQ>%Mao(F6I#WjT<-E^}TiT7R!CKEIiF7$Qelpoflv1EDoixZTZ6=+Vtd+fH? zDyLL~?f*)NNT2jMrEdQzJQ=3ZRi%V@L8*E@sCsw0`-oa^s)T z)tU-mM;akY6qqR$S|_hAfAt?q`Q4R$KLwJXF+YaoJor{b>B-~Aw{G3KckkYf8#lN` z98|&-OFs6ON&%HPsp3XMR&RBIrCr6GND>+@n=2@V0Hz8HD4P)j$2WNzgAf-Ze=$Wd zh+@F4A0e{hmC~`uea%Psi!slc{O1net-60_h+SjJLN927;#Sz=^Iu_jDlPSB(t zaE7Pr0FM*9qdzkMbimQ0NA~X7%T_QMkdjj!R2j>%DAEuqKA4&%hULNx`)aHm5wME8 z@f=MB(0FaKeK}H}&CPt?a^*i}Z~caAwbsh__T{CSlGT8g+lveNhnLbf|DQr@SE2uW z)gjHemOvS|x&`oJygxZH@%YITmY(n4xqI#UHSPu%&^tbYo(6f2Q@|%*sWBLgWAtB0 zLUJ?qOUxtz**1g)){dM)+-Lv}E(=UycZBw#KOk@DFLwUpWf+b62^bB@5^_GGFkVF` zgWJL=rAT?`*JuE}+l^COzBqm9KMrZZ?eGsaXA4i5y;`hj;`Dg~PA&U%bnxaE=D_gq zp~Ht6#Y+KID!dYAMT=%dAvV_ms#xhiO-i_wg;#DyMn+gh!WlRl(9%=@4cI8#m8D8K zGj~08{Xciy{kxW;-VA7(MyLS*BfYYiz5dIkm3;BcK&59S%Q1VMyaW{mvcM4EYr}BR zknj2M2i)*7$IY9!zWsLW@#9Av>17>*(J08MTcH$G@B~ybjO7z0R!l$4DSFaKH@E=~ zZAHi-3@VYJ2FO4FVuNn^JC9cFh5H!OH^vvKh3HU`&e6LuHhY-n-~^EJr_c(kU!DCF zm{C7)5AVwBxJeOl4NaGk5+b z|LreRvy+AouTI$;y`-84EX{P>|NkaR1LMPgk!kBf2k-}?`AZwT%)vGz3(b5J0;8*+ z?4v*qj-ZvGzr5mTYa6{lmb8GDGPu;a7QU(~#GzQjel2@MtU^=aDafq#8GzY_!?u{? zemdM^o46Lc^B9nu!cbNWK`kk%5rL*bJ$B6nBBYi>5o*|NNytGUkxU23Z4`(UQ(1|f z<`aQFbc)Q=fj;mgiwXmDc&IU!Yd~#=|JYglIe2iGuP1Q~6sJea?G@5cUZglhRcova z@WsyFFSvi?$Pvzr#$3rX8_V8Q0FBii+m)MCx-gNt|LgptGlGr0K0=j**M3?pm;{biWAzGch zfDe8^izqa>P1hd&_1GN#6jpd95Etw!v`{=s$S_-x7@LAXk)J&DKMJl%gwJJk>R%;+ zOWVk@s$2xYVdziXMt~fE9t$zS#S=Th-3w6I-VSG+Hn1E8Tv3dHVX?(!IIfrBYjHD1b2pbELB;*eRB^ z&|^re@hQH7GtS*G6=2I*JFD!Ya&$35X^&Q`Jx~EhTlxTyJs5FgBP^=NS7zW>6qX;s zH%PAkwt!>EPjaNnKR)(i>pc2Kif;PB2`V%V#rz5@1^RzJas}PNuCF#YA2w$dYz~U%e2G2 zlFSWgZz_NWY?Q6eQYtllHFxd*O5eZ4U0Ly0jJ7(_X8bQw0}RSXIu?Fj8Tr2 z%wQLaSxUZKqtUuSFAu2<16jC0(!lI!E_AoxXbMkPpsx%lFG|LKl`SE;=?;$~B1j*I zY6gaY{t$4+ywYm!RGem@z{4{pSWQ@F0tJinFy3e7ht+@hsXa#hZ@lrw=*TFyWuTsv z`9wU);BLoP2EE8W{L(TPz>ORp;X8XZSy~&gK`2cH&>#)5ZP`d=XRoDh{Y~r5e@GRV z6L_+18E7W-95n#WBi zdD z4b>c5oEhF@T%OI{``gU*|C(By-QvWW(Qi%-0F(gNJKYlx3LVph+}v`OaWp4m3}V5b zC=~|z8t4PW4ClesH1Nko4j>}H8MweRLBAVeq7H+~0KZPumZ{zF9KouEzzb$)bBiF~ zfLlL?);x3u2xBpkZNa#swneS=A2SLHl1C~G7O4=?3~i+f9H#Xv<5-U`cDTb-fWqVS zD)5A&7!3LW^gx8iHGwnmWZ8#xf0ljF0YnQkZ|(BkY?l7Hz=wb#EX*4iv1POy!L?dM zGJIf|$;2~;-5}a+^J*%9ZL8`XPlz`i7xI(8t=#y>{KT00XVbhSHGl#b%q{$&@A33X z?st#-7FTi_=-TR!udaAGz#T_JoFJ?NtgtoBp?_ODf~pxoP1_SL3K+l?4Z@1OFZ;+_ zfGuMP3{oprtpVvzWTRN*mKAYG3(3L5wF}q#7(q0`xkJIRDh3@Q!U6#=#cBq0R)APN zI1!2)r(+Km>;SQc!cK|Y%k=?jfDLno{0#g-f1G5gHJ9Uc-_xg0pE`NUukK04<_WF$ zgp)jTpCYHT4j(#fUs#Jvxn7t?qBj*lBeljB=7oMx$jpvqF8-^wN1we+61s(T^3p_2 zdoeCQ+B*Glf9Anb+qJ383ZTB`t=)CYBRqv&Un_AQ@G-;6P^<`mFbH@tXvSt(nFL|U zORNJ7j(uz|B>+4d2IB2=i9{v@jghE{$94%6Q}kbPN{jez0<#AWHhE}B3hUa&pkL{j zf$kQ628EXddc1_}W`dpDp@{_O=d_Q%?Zs!Wxw4;+y#w5=`wPB1H5&+VCKIY_cTSG| z^QG0Hp`n4n0hDr8{^|y6Bw|wmG*WA9U0xV3&fU&^^{?~eU$^M0=dDYr8S|FZ0I-tJ z7Dqc~ezxo3+{(T?Gwtl*CQ|~8WEk=$PVS4~qhkTI?0~kv2Y^!07~k-YFyO6x?bHto z8U!>5CRqWRp{`j#W?P9d{UATVtS4b$?<05#n3niW(4+<;_$DgFMnW7Rl1joTgdT?e z2HL9+ygf*GuBE8d@ru8Pgjw;%(;Do&F^5X<&0dyh*!bnN4|(LSH!kda^X!|44jyW4 zZzUd&0ZT}60}IT2P?OKB@^7Og!P*A$TxnH7U;igXcsF;KIhn&zX5%l3ZMZSWs|d%Dlcco zFQmWvPr2E9LICUSo18{7y6vffRApD|{CnMxZ!L7puVj`>Rg(a+AgtAu#TnssgzO-u zVAqs|0kECOmol)OWJ9jxx`$yr0V2p(lu`gTq<8S;X#;n0h!P{RUg)w-3He)ALaitZ zymp|GzpTE#1E8}4z#IT-)_pwo&Xcv}!xPgPMV+`?!EUg!?G3iYO(3tJAS-GO; zmNQdd*jQ#`>?D7*E`QmS;2GE0-TWX##J3mhf?#74w9O%RV{jB)&FQ04Wl`me@ zI&|m|M>bgw;=+a%n>5tKxvtadVTC(ai>3a7{yls4u%JYi*X3_)kEQ}>?DqMtxhrRu z$17uhoxc8S_1q@)fhjF%3E53_#O*fdHU)^l-JEzCE76fOyry@w=A=rK zHc~ozXn5~WQC3I#VzaS-4UwqCmnfiOG$Y`T4Wn{sjQTOQ+QZjR`Pa>TJ}3jQo+mQ^ z&grvW&-xE)kD&UNJw0R1%96*w_Q|Lp?1!ISU#1b#o%J%dHeh2?nhKyX+htR7l}Z&B zGPnO*_TH~^3y+#h|C>^$jSvZ2w(Y5vvt1L923r6A;hu#82P~@$kfve2P_4u|kST}= z$bnk}KA=|@y@{$zfY>w;x`SnQyO7Ycmjmc=OAuoh&*=mCO@({w)n9!W$|h%fS0AEo;RN? z0Tj|RSMoQ0k$du`B)mENf8Hu=V1AbQ54Fy|-P?L|vGePR&J|8|R_7KREw)s&U=3XW zEmK|*Tx!jSg%92o6PbtL&ktxRYFMnx-@@%(l6O7*{AS3*`^Zc3Uh>?_v;2m{xpU^s`kgty-*0~Nn>ljG zgzIv?gzVKaq*GKphFaUH0mNR)Ay>JYA`K%TKf_4iI^xC2??wz8O=@BS&8^hhRL?}V zIY~J<2saMNoruKYYE?akwvTf7J`O6+Nt{UiJR%NPr1g9*KRPzb*AYshrn=<4Ul-;a@zBCckcQ0Ti-~pFDUho zN2hLU1ypJ*T{}~`er~edN)?vt9B0Ows>Je1s?gdEawp*%@;Wry$iMswB}IY6*)<5n zECa|{KSsnilpJa(5y%5&4s??nLp>qH%m}y_&CzE5LfeIdNJL4(&nES^MRT=}q$<{t zAw<@w(M>|h%hF$a%hiy5x!6a9pv*zT!xdv5C^E~ie70qLVw^x!JrH`EHl0ba0fe5E zqTQ*Bfez(k=}$7L2php_b#kN5FdM0xsg(6>yAp@uh)iWjMO`@-bbMlBd~B2zWWu+_ zkNT}LfT;Iim-=UFS2D9-Pn~<-X*IT9$Sx5zDB1`>jdE`LfzrZbwfVW#!qv4=L+*{{ z0xiGLGD!3m1F)?n4s=?`Fcv~$1%q5HRM`;eEsJ;)O*ja;5Y`MhIzj|~adO@8i5^HO zyacD5Ov*^da*It-OU{)r&$t(YYN)DNV}MYLM2|wB(z)6kh$4lW3Uft+k3`RcEs7l+ z);lb{LilmqBK{MBdXWnKjCHW6Og59tVV8-vs%0s-)Xg|s3OVDkeuD8xk)}`^h@V=) zsaQcN6-wB8QWk-Jl8x+Q@uLhN7Tws{2#EfLZ)Pw5MT*m&)x{kFXlpjzes}rCm0D?j zt=McC1&ERCq0vIvPz(v_62affzpVdSt|0mzVFR0Xq>$(i(U^oLz2rp(*l}`{5J6<+ zgGwC4ZSvU&F}n?fdTu)W_7MSiY_O%IK0OuYLdN9kI?()bxdOpu&#s91Q2Z&ewnVd! zqz~P<>$>D)IR}y6+?mbX#%8Dqu50xO#QF5y){W{x`ce6mOJ!p#wV^Vy`NZ$43?N>8T3=3| z`%3ZVi%xT0l|S+7+Fe@%xlN~A4;2>Y+n48SqpS6j$o@uM139yF4XNK6`;!Y&e;R_! z;%GXJ_)G+xa01cCi0}?sv{E^qOcwE6lt7#MA0hBiaq=-27>k)h7|ua)B(W7GAIii* zyCJETj&m&*3vAA^GQOemd?jgHAmuPD$6*l5dE%Gmjo0mP`s;?g-G`li9T&9bH-)%Ux}qYNg=;Li(YbkVva=hn~uD2&uGD zosk3Sv@uPLg^9n4Vi}k3MW8-hLCR%L)HVh6S`qog(Et5ENu*-=j*yG zz53)H57K?1m@gJ&a~3jnUe08b?YIP6@gG0YEUhOKvb9`($c$0{e z1J2K!aNqAgvVmO53l>6sU`lWx7!TB+Y8$2?jKNPEdyg`JcyzIwYOZD$zL&cAT&lh( zmVoM#hd`>adZPaQON}$RiIbU;sdA~9%j6m@VF0;o4mu7Al^FoXGPE7hwB&P;Ldc(x zZfN%=9rr0I9-;om*+5H7No&KowB0ypy>(1QU<2#x z>uC6msgfwBa3%+vfy_T_xi(whPkn5*eyvDA2+pz=B3PaT#W!DsEJN}Un26vfNsdPu zKrH&21KyW^kh%VBZtbeq6l-jCNko88KQ^k5q+VaW(NzVxq}Q&P0(8T0zPT2Be}_zyN;4N7I^Ig+cZB0fs|?MLqpJllJ4%qQmgM}&=LL+DRS0n#xBLE>RSja9MssYEi=L-kDYM$l zTnF73Jk93ON1po@8qEj+FaSWb!zh{vM8DR&hZir^zCPc6uCngji%1mxCTR-}fdI$X zoPV|P6DO}d@({{>IU$n`v7WPDTSw@|gXRZC5JZyK7BvX(uaM(L6Y&`8&n=GPp~BYT zCxExyuahCzj$ME?q}s^qY&sX=o{UA(H$>R70hj`hgw|V%OdQJ_FjOX*lqU<%92;uZ z6~xPyWruCUWX>b%iL+HAp~{SMEqxgx1$wZlMgOX8hE?9Y;a}u zo^t>U0DM{MsaC7fZR&=M0Qybd>X%=2U-&^|PG+_05{>`_)mwROb!O^vIqS+AEj&Ob zh0tqEeV~j&QDvJjpMYs2^FtYh9JBE?V~j=3wk-Q9qkjiXl2~p5wYJg)beV7x36(d~ z0EuQJTHmH$l5X-6NiG=}DgizBZ#VVt)L|okDU$pozV9q~@i_qzWcI*3K^TiU6SRQ2 zk^I>f(M&Nq%H?P-nMj65>Zp~~I~{2)wig}%1K6IW-VYN2-kGa&-s@+*+1aggxrSK* zEh}UQi2ZtXCH?xj@_nb4rca@AffNee6$&aBk*$%nJqHE6x4H^R6{5^BKX^nIHv!Tp zSv%!ti?Snp3oVtnR@NiFJl#gBC5kx7)Vht>5`weOxalOy_$41B-*05d(k%INUIi}I z64K9eNIrbW6&oj3wz&%5G7W^!LH%VZJ1qy*Nm^TfbKR(k{@jj$GJxChh}IqNQlsX* z^j+`m3&>-eZ)LRFptTJi0Z41<+RfCPS6X-6(@N)|pwLC~B@W_<)QkrnAVgUx zvwRjNWsY~FLPCzs+0M3d#1{{jtzYA_k9WCBrY6-!avRNrS<-A7$A-T%6Pq_zshlt+ ze$g~gfBuMw)Xy!NeH=P28r4bUt*GB6+s-1?Zx9HE0Vvi-Gn^2GmX@8@&N{PmO8t+f z3&L3{n*y(2&s>_DURi5Z((C1-m)h9$iyRFRm0BB--}<4k8=}7548T((Yg+;>adDQK zOUfRc%nSH7*v3IXq_sl5&gIv!ZjZGr1>D&-2CX< zmz?*#w>>q%!T_tVVs_J%Gx3=X;8+tDHM%%{Wr-Fi%{S!RDqDnbn?IQx;LL^J4SsOD z8b=~nXZZ(ITH?8AamX1DP83W=%_JAI$3W=2{WCko5bZ6Mx0N9YS=y^;fsFG#=FZL+ z?30jQX$!|)*j9w9ioq}dvxzQL?Mgsp7){H|?Z5ed&cZ?hTB}X$69MFd>o>f!KX5@o&3%@yGM5S-hb}!@U&ESS9N!N>ic%dTgYe= z{WAp&Mm0xGzp2}#-<;?O?5P!!I7w!XeeBPmfz8#{Z>F?tH(yk%GYM?as*t)`ytz`q zs1KixjhlakKaSb{&eGG)<^@!;m9$mY(?~C|wNBv6@3*wQtNX7Lj9g|(vB7$T-R9VH zH*gZUl~(E?Zs0aK@08 zOYTWl4}AbvaTf1LJ-|i%Z*=YYeU)j4WQrAhs35X$FKQQn9Wu*vDpqOZ^%;gP3YUQanvr{qCm}`_0l9V7{oZP6*?#UmWbHfQkWt1F0U2 zZeYW}WAWx}=pu=tSzx(>>-L(O+5|!)?oLBlEmiej7t5a1Zn3&2M>lIoR7;Ko=NAEpQ# zEov*hw=PraW^x{``!e3(;-~1PU-q;x)~|tsMi&5Rgp!Jys(xCbWN4ZSlW1(5bnNir zM^#9n9pMD&Wa8r4ZrboUF7Sq7hH(^mW(rg9bFbdp$ z^^XB!lirX#>Q1Wr#d3?rFR6AFhMg^O@K73&)CCy;0AgQ+D}YcrVTP#7gTul2xe5f^ zSiqc+szkSg*SX-GSGKx6PoLZ6tGt#wrOCoc1@@TCi8pR@K;+QWzHRX&9qu8r<~li|@V+JX@4uK#zligUawXbaxil(AdhxRnET9 zG%t|U4i^GX>wKn;63}|Z1HnHL84v)lpn~-Q6dt+JP*`_-KP~{*dzsoar5z2|n(&q3 zRFAXwj6~QAZ&omNZMQz4rxnaf58X#=1H|9L?i~p6MG@DU$Co9=r!nf?o?4T5>fy8_ z0-(2e!Lm}{m2gJ{%ssllotIyd2g<)kMgz#v^0}qtzJ0Do1mFauv>ajmklGFcypn8M zdHM3|0AK=#1V|!E?<9#60X&Y;OMyW61_V_^Spa#;5Zn+zKR}il{nUl6q=8`{x`s-K?PoVaWD+cr!-eG06`Gtfz|^MWo9}{ zPknvaLwB;X;Px)0*vJ&*$_}9n7-2xI{CNvT`s*fik;ye+)MgS+}_!0<&{kOJ% zjXItk2B4ST#2bS&Gay{DfSd$GBXV!0)qvIySpHW3!jgOyt>w+-R$5Km1;8-~){39U z>qz*Oef?72FAM8J9o3c`Ic&FE5SiKAVvn-?DT+lxik-oa&q4g+chaJxPgq7FMT=zs*r-n&I3;0 zCs_M|=HMhT>^U7^BP6{f^7Ea~t-+&v67icG=kXT|!8Dpbstdjox#x+*hH_|3NK%kg zSCZ#UPfdNKuuR{3FVudN^NBaEWGm=p?E7_9W5wKli%?BdtwpnNFTtdh0b+X4%-pdx z!8X)#*5X&nuRuDPfFqgbf_+&+3h7VG%iVCvd1O2xAClIqt)ui!7UqlZ+(OC}_b#m- znh7pDOx*S?IRyx+(Kv4gm)LypY{CbRFsW0TN9%5@ha^pT7xuf}_H4W(A{w-0wIC56 zmQbW~5~JSjqngX2(*r!hJ`+TGs@UIWIRk6}KcP+!rB_eO8Ik!#77i*~>6m2fYkO=h z9iIQ@6KJ>U8Dc&W0Q)kv4LjwU)YLz1-hR7YCVf`p*fA7e?JdxLkH6EO6K%#%8n4{n zul@=zttZ6LhFnni-R=$su*1vH>k+9?C$4i=ranNgt%M(6`{PGUUSa+Gr<~azd7LX< zx;F;SU90kA-Sv0k117+`;r-zooY@8kT<0yyt^se!+uqCT8PagpJEhGj3HopYT2L)h zr#OAwxh^jZ`!f&9qYoTD>MnYekD<@u<6C8Hx#l}mClg#`)cexnSNd9oxiGRvi$8@Z z3z<68KYNOHQ|~8kY)3KZ{$aN00GkZRyJ6gYx^ovfcS=Xbwp=93(WH=XP{$o8p6d`GO^-|Ojj;srCl;Vjplyuur;&UUQJjNL5NW>U z08&7Y*gaiSjJ0XX(I#iYJD!;KT@zCybFmX-_N&OOQ=xgL`~Cei9_Vczg&{uqWXsZd zrU^g=1r^GQB+IkZ;Uc}>=rnaG=Z%kYF8X;m;ED6hsAVEIARBR?j%Fr)EPZ#x@Sbb+ zyAKfX_=#8__>%M$$?vVPx)X6GqF9}|+%`C$9C{fbg4MVprjeEIV)A8t0o}F4y@6R7 zCG7`LxN2s4)AyV$46q&nTkaCd92SGe6bi5s5J5heI|0eVdfk9ZbDNpD390;KAbBSi zKmx$NM0%fPoIU>$uz>nloB|s*nBK06Oq)OSEglidB-LeDV)lcjW~W;AmC9%kDJ?3% z_V@3hfdTfFu?uKPJL9LGN8vFEJxO$r>!n6T-=t%K8kMk9+f+ERdD1^E?+~slBN#om z%Vpi1FffV1=}`kvi^Y^l@;AKxQ0tL!5!w%Gu%{iSt^jl)}sR zZ8GraUX%+$jr3E{9Ga=(53@0M#!l>B=gvKU?fu?M5#2BzNWq+KSC64bRZA`K_Woq* z?i&A_vm&*Wn61yohEa*9Bhi6AYJ~h2LvVnd>l3A)(rrMpVOm=bt>hO5;yFoxOBmkn zusW7zh+L0zVH+R#xWWI^w7ZSi7EqXaV}GvHsq(YxNM=TAKqu_y`b54mI{<|~ z3L9$eYhyrWC^8&d+rFkmkHn z2(5Ffov|2bIq5py&BMVZ9vmDzX&zJ7k;m6Wn46PIA!DLEDCI_keXj~}qNO%Ah-v7? z?3O*{7=;WI6A+D3r1JzUsW&-Yx*pNIPXS)P{1$D{WWkF`Z^8tterS^mOu27d6n?b( z9#TyQyIG<8suof~chm8~s|SP$ek2?p!FaL6BThREuyos(0#2PLKlr!G1eR8E=oI$M*o_!V;XD6e>L;q{n(NLL$@@_ugcbA0GD_OY6ma@ zziaPVI`>TE{OC8!48>2Onn`ruMV}GIK{wH4JD33Vuq9 zj=C9-8{jEaFuUx8CV7Gi#98nLoA}zQK#$nQN z;td@5R%DJ|NYfCjITOfNQ_HR<6z2E=wM?<} zBWVY&{10(HDfVcvKv)8EI@@XGHpRT>GQOZr|BKXufxGSA82jM8i()u-hUrUML3H-J zikSKJ1Op00FCR|$L$31M3XpL6do7FRUS(!GKeXQU2}x2>DvyKy=k5VripIIJe zVT+{tnMx-qN($3&JyKDgefF(=(`DqGPBCP_bFHoKX2aJwedlKg02A>;;C}h!HqDh! z_x;{Aqw@y-UP*m5jgQdsPnUx<_=ZVPv>n+cG}$(oQdR5h&Xr!h>EcKRm8-W! z4H*^Ujx*e2)&ILr#S@aR5C>HJ8&+hl3gpafxr^*BEanl|wt{%QTK1yoz@up!5*0B- z@4YI9>Z-P=c?J|O4%v#(FJ9={REs+fp6CQlsBoGl`Xyep78VgLk&ew4&Uw6@Y>=lR zaw{utU0Tg+m^mwXOXTZPCK1JL5wc!_9GC(;$I(6!_;yx z#bau+JQT6;nXsAjbSOe7>MB*@Xy%f#@@lN1Yrgo?C*~3EGK*@V4og?Wak)(PLpAu^ z6#!9@b_b07jLiD+1u^HZ;J#V@CAUgmmoOA=@$yP6eZyn3JP>hwJ|H}LUQSyk04e~@ zK26dPG!q!|Z+1ADBUW=B0Dm5B?BMyi;;~#Z#_I_mTFAi&M3Ew2BnG_(Z)25gzsd1M z9E@{1hnhHAXq>;csUdTteEP12>0$|o2Iq+2&n=;={K~wayn{!3xeC*xeS1UR$3D?~ za6id#=y|6>gY7u_T9OQToMip2Yw=z9z@vvrqVal?5oR6zu<6t7zN-Po3p{&e|96HzwMlIdK zy;j;}EBETD1+i%SW~$zw`(7U6mEX#$)rI%pEPXW-vyzbDdPKI!*6g;r)N&M0 z=KeiTc~#>vvVCrU@?q|LW~=1>GMJa5nKF|;*XtalFlveDbIbAEdm*&Rsi)1|kb=$# z{v9YIF7fc$>0qmc|F$c%K6X(>$CW!k0Aa>ZZn2y`lc%<4G*pUWZTV zTJFy|i~&HCk{P7IPLqzE&YMg~vBzdIEsSWpuqkJPD$|X&hePB%D7Jia-yt@8cdk+x zDTPG_#R&M=L45#R>`xVho(E&QZ0;6^cc0~l@r6{ndFPzNx4e$P-6HPNJ4!dEpWjfU zBTH9^_vcrqwc1t>i-o~0m(#pus&SqAlUnDCN7P-h7SlIX-MOACDQmg?35(I@$cAKF zWco=cs!s+Ij`|T({aCt!&V@6k{AS#b7fR#&&TBYl&byC_@A?t~878{CL}2GP>;y`v z>~g=>l&8GB3~s`kTkeYMs#GUwbYv7SlU@Ts9#ehjQ{Poo3L_IAYp5M?{BQh@VhMEPLCUjXv$r{Rd z43EQ`y`{SP`f?upg;>Z3`^-HrME)2XVx(iKp&?ndAzxH!QV!J@33=;sVxx9ilGg3m z+~m?7dDtT(->;|GQiJ@~@x2cz0(N~cK^ zF#u`(EAUi?@>p-kqg|Ax!t`fkm?dyzjOsb`Ph+Mh}wv0drU^HQmeR{PD&yN5W5 z@vDg?V_Pix)G~>$fNypXF?sf@DIs#df#K{b+{o)>G*FtsUS?0t%El63=}!+FY)6Sj zZR@*fbz;QP9b9X2Q;XN!E=%$oU+eRQ78@q7B~zXa{+5Tu9jXM@Z)9JJlW9l5$V}r3 zkjaGj1D43IsZ8RU?V4R3o9>$|EM76d{LZ+z_P^SuUX>VhM&C?yAF_JwwsHuS>axEl zCPrqVj%Zi;?U_1mT++|t$M>D^wB%1Zrq#Op$yC>&dlne2 zOegohrq=k~eVu8OUaR%pMLUPmotlgfjdx$TZ(6bMSS2H$HU8FOsMwtO%cGk*Zj!cl znfFIJnx<8}vks0`H*m+HoJ(%$hMKA!A;A<8%~?KZ^x2;0&S+qyCFaXvw{YSM1C|DG zuHtGq@?BqFePPk6)v!S19=-HDys@}tp{{9Mayh~VJ-AQ?l`0S@L98W~5#V1;=282_ zulxYWpUfKt$r5orA2S1XZU6w&TI_?gIO1P^M+hGTIKBkd;9XBxTm7rDqT*Lok*w;E zy7tcQ_FPwQ1UbC0simp0p`pGG8D3RgQ&ZO%XK0?&Hwa5GL4w8>1eJT@3iHEA|OYRsbw|L zx<^$dS#iO>-{Z?#T461)wyuuOH4i2z;!S5kXKM?ru?~4!V|`U#G}Je}xudf>jjN)*+*A$n$ea?C2ltD=;y4MIgHJ+uPe3>Y)g))0yV_x`u`( zSbOO=zmD0y6ctWQ1U#>{v!kP><@eIY?p{}2eM3|8pU5kabN&R0a!VnW(mMKkyIY&; z8=G4i>yc@1YJtI8I|up;>jK&NH4#&YphX&08}s=%$_?5-oM-PToET;Dr3KaF&fC!B_WXTKKGbnuG| zGJVIb2e)0Yu>}MqMCe2je(>o|8HV*&iz-BL$ zi(T&wfw=AWQTl8PwIgz{P<+EB?=2;(rm6nXt#QPdnL7}QptxBXOV?NX_)$YuO@-Q8L8@^mwmW=`w& zMuz$>qpYnBubdzIoL~$kTUzP7ea>34{wKt58F+Nzdy0A0E~lodApa?CKp3okwpH*S;xO|mKhi1^Ix2Gws)@nowJU=bJkRkW3egDl_TU+ZtIIFAuJ7?e8XM6v_Sw~yj-#FXts{K1>o&UsHdw)+S?B6)+ z80h)$!kR*TPsW?)X3cjQv{@kSCx`LYx8YxS^Iit6 z>mt3_V9=h~3Z_|;{ zj~(>FeEpx;J8tFY_kE4V&r^HyclO$X!@MIvVZn_*5NMOxv9WQnnPagCl$(|Mn&qoM zb$t211g`!65;!Dyy=Cct30(ev3LFx={}edl z|4rba9p?WoaQ^~c-+u~R$N!7Kbqyf}?(4q^-2YGd_7`{mrf>fvcWIW!9)II5^MB?} zO4-I2$=%2QMc*`)M1;9s{l%Trzj60R-^9h~2?^;W`2I!T&~BFhrf*P50(PQDk8q9i z|ARZ!Kl-LGf(OOI#vrdcUHn&l1Fvm0Jn;o!QdDI8GjvF{R#v3^02sJ3>i;Bf$lY5% zN&F}=NWV7y2Xe-&e%xSo-SvM`xxa{e&+7LI%qH_6#DzHcIe{Gf7yni5R*M7uLP3Ff z|EzXL(~*(BQD~77V@H1{ZgX_ZdmJ=2y8Z(JJYMvk0WKaPfxAE7TtB-DMwl6Jumez| zn*x;-q;W9GFp-8Rj?7!h4*&oJxe5WGgOH13uf_@F0%(skNdYB86g$WbNh1vzV|jT1 z6LK9L0Q5Ke>jWTjiwyY>K(;*r0bt}3i2MVjgZ>c;0i=Wfd;QOW;|se_|Ka7<1-QRp z7XGv@mU35RC{ecHb?(r_vF<^dfwmWi-mS0xy!CDHf+>j;xVZ)g+m>mlVx^{PbyIcCjAmlk9TpvfUp~5@4lNo_L5!FScfYqFp7~5JHS$S9;9f*D(M$Ss+oOp}$jNO8GxIJpVJ zbiLf4!4tWorbcZ|+I%EdgLK&u%X>fgaWB+h}d-chQrY5o9 zgQKX&RF1%ZuF35RpZ5y3Y=pmNW3yHtYH46^u&^}yz170rzEY|rGc&59Ew;3_zR2HJ z7rOr0DT!zqeaGCy#MvvYCAxsr2omBXB@NE@LC@KaO7DID`4^B2CA;HGo#bxuA+3Q? zX-_uft(~<#2qWxq=8DgZhelSSSa9c$!ccy*Yl?J93-T-y39K4Bci!xJNp?w|lAYho z(;>c_$bS3j$S7M-xxCzN+v!L_`D7YxiB!+m8d>Xp#MoZZ1+ruivq@H594Fr4se%*x z@kHFOR=0?47Mr~v3dM>jib_is%%6IgH`O<{q2fbIv>qXnpFv(SF_4VKk0i# zj*EGZ7blDMHoipt0+;e6Qz$vZ}G5^=U!l zz!)mlF`{J9ZApfLR(I)q6-u1kk1IKGIa#P)R=WZ|_c{hZO2r_Tju?GdbjI|yN)11~ zchY0iy-wbfGP2=wrZbl(;eSS_iZmn`mS&kwaT9qZcqmeP6h+^KUU& z?KQ~AZ?F_(wSAm!EjvR~i*elWR#|ThR>z(GQVh|n6MnKUmWmCvFFZ#zyo2wRA_iM- zc#U;m`F+3>qEd%ZH0fB{8yMJIj7tVJYfTr$O=#M?4&awkmOUzv9rMbzAaAYnU#sq z!4zd9v{mOO?Ie1v#j*MY|7^L{mv}Ic_xHkA&#l|gBgw@Eud7xMSzrx@FD3IHM2g>6 ziv^fTUQKe!UsNyXapx0TG?KWLqN?ZRV?wL>OA+$8d!!%3TqBnPUfimf?pKGR}9XNc44hgeIHnmq2(E32RkhtvVJBM zT|F3@i}Be2i!W%*K#Qq_+*fu1s4A*v+q{5~HMGT2_)|7&VClz zlk?;q!}n|e(}K>76qJ(+=@!h$}Cbv(3px&zWFXW^3(dJW$yMMR`PxS;!*IZofm-jt4wqBp1;w zmvfxu9j*+n>M?>1-EDt(oct@?!R-4ByD?bR2;gpZqbG{3u^)RoGKay!_jFF^_k`Z) z8VbyGPM#*_VnLi3^(kFIHo)-eXyNt%If}Jm>0#5K%IofM?&volf+HT6$uWQeB2o<$ zb#kvj7iCx8@p${8euwFaI3(aCqKuyn*90mOfmSay>e-(weE# z@P&HATZz6iG8Z2OI)5*Zr`xmU0CkX=Y_b^b z)z~Jkx)7_8I4na+B9X7X`78mtQl{kDtJ;eVT65Yl*W2%(s>8O1<^-%--3&HL)D0Pr zZ9nUf@D?#u`+ezG+Nhv*orC|ZkjBpaIf1C|jI4fv9!=)w?5xRh$sE~D-LDQmaH~=0 zQJ=cPDXSubKJKe=v)MDAllk>|uejV{QXwlKayBD`{dVajUa}YA z;Iobu9G_gb*NorexULbf zfzL5jFc&Gn;ND$HUtUt(bS-02O1jr=Vg5nz>>?_)YB!UU?k7W^t7*L~S5y>MSy~RF z0hEg@pk=_(sKRq)jH)J z1GyBvC*+{^I442rca~e|NH@C4O>cW|KOBoY3d8e-8{a!E8+!2q-Hh|)(Rx2 zZokq<{npK4@P5g{rJ3eO>qms}tF6oYFJ;#W1=hJzxzn&Q71sg22wKANp6?xoGNYzl zhK!r6(+}`cgliA)y}KycH5Fpb>uzxAZZHjTXL5wtc^bN9`@3eOo|E|pJaKOEW=(J6 zo(F5pSlU6~`1$pyzxA_N*zT;kE(#?)UD)}fJHe3l;OAe~<>mM~DhDABGGnwSNYB|P zj5>`Yx>4Y!W`;@1;*IX^F(GI_UWS#ZAktv}mKk9HZALmrflc%fH3Rouky z5#C4nrDj)EHWB&8l1zt1`sF|t6rOIzJUR$x68)RAOo;FxJ)wNKAS?Tj#@Cd$YDt-h zuzu!d`h9DHd)}bbX8IAkaKkh@QjRp)y5kLk-f%mC9Qc6_cb}ki@b1AQnb+bA$=VDM;MY9g26OO+%V|e;F^jQsCBRF%o-1$Y(OeSE3X$ejld%c~?X^{nVZ2`ZLM% zxN9v-67^>ry{?+eGMMn^!>E@quQ!uyG(eXJwjY!oj^UBCv7gSHLK+N1RFY)L{r>wWzApz+ zp_<+kY|c7QFidtuJdu^7R*~ z21?c`In`M=u})1@$DIM2=jR#PyYlH?&Cb76Q_i*TAB$RHMTGf|(-`kWx72A?vgL{) zUQI=rJk)nJUR7$6ezZCwbz6P($7KHN`R!!_n|`GVo>KUeN6q%uAD{Y^u|9jbiDL35 z49C}}5dZAmLHuyx`R&AWlFt=)LwzYN@WK}O-Py1g;&#dF?4V}Jo&ier$KE$yK_yYi&QUGvl4{BQtKLSatcHX%Pm- zg&Ze-yu)$A)o~|~1Zi)vYWzv*04J_=E<$4d3K9Rn)|bEmHIYULEwlD_rX`Sr<%vZ_ z4OXvaX`gHLs$=`j>_p!#a~+Eywh(tmi2D}A`CYDhsczl08FC)5RS^@fah#orbKhiZ z1!uY_VEP*VGNhn;Vs1h`>st)HrxteJvoC^vp&^Tpew>UNJvP)?bmmL~va_;2mJ5N~;TU~Y z9NA4YbIr~609o2|z8zu?|Aw_&gZC{%#Uhc$R}oWy+L97?(3cnQ$9BKq#x9)@dFwr+ z+o;5ggU8uIugs}}C!%v;Gu)!x`1X~cu;CEN&uB}s7y@vcsni4z*)ezijK4dpco>xtwOTkrQ)IQxM&X7e%ibZ+B^44=cPvy+}`yY#wh1dxR_9) zLCXLX1b>;DHdI&$d7#QfbSP@H?ESahEIJJ($f6@(=^%GV$Bz@sz7y8PeVo9Im$USH zj$Mv4{7|T|+rN>&8=QK7T!f1$QaJs+?Qq|ldQ)zgS==z~^eBo2HN=&cj#6I(CF`Az zAb-A#-9Dx<%u+198ucRKm7FCGEhR2L;{fII6QJ{MJh9(RfX2G_+WRid&wxkOHeo6x zPLne)I{4lX4vcftL0^`2#^&9NWPW-m7_om-{uy%p^zvoFYLtOO>b>$s`$gbQ6tOeT zptAe+G{^T_cZ9a%!uWt@J@5o`xc}3PQ zBg~Vhv1Gm&7(*BO9K~UxnzBGV%EVBwhwVt+I0|xryj9YtrTLHf!x;7+(7m^^Mi#pM z(>8gh?=;1(D}fV{OH+VdLr#VeUoBBV@hEQz1L%|GTx~z=(#frMd;`^y(z-+yYlG9) zj~NIJ^q@Uj8sQo@>1gG{6I2+64CmLd!MiX!lbh1*8u{N;30b-y@t@g8&HGbQDpM-l zF;wFsrWVUMLfIQwd7ujjs{$w1`yQi;GZW6+i(@IMLR|cpbr-k@MNSRC(b0q28wvHj z*Tdlv;xH+9>F#{o{yyM)KGt@qXz5BZvSVaLtvbI_JbIlSda`jHt(PXPWuX~!lYbFi&3<;>bNlozv}XQnxU;PkO2=D#ES96Pmd8sn9h+O&peb3 zC3^J1zlnC{`QjqMkLTm`d3{d{>Puo}CqHaP2eoT{nuHg9?KUV|xYFt5+wKpGCh67#=4H74)aJ0SD>CkhbKaJ3@7?d205qy)@ z=r&HXT(XJxEZyJrVa>iNwxrFsbgfq^eS(b>b8S*CMZuK{@|td?1S4EmUTYwkvcRV@ zA8|KCh<8gs5$oiWVu$J8L&j&xrmopfW{CUj^4j2N#dt{`#D< zBSzwSZPa+N+e#|4&*w;bfX~}qY|!W-(}iP02Yv zKIoM}Cv6$sl7Pg)KsmZzbt>X~N4X#5bcBBO^_~SQe&w|_eVdwQ(Do^VO)SA``~8PN zdFLUXL1mDX2uzw08vTG zpfbm$I#Sj9pm2@V+jB%bwf;*?qhykfHhV)SKRGSvri0xY+Xdah+i~W-!nHnAE;xFL zv08PFmH9f_$sY9>kr`J8sfnOG-zRL5LoUtdiGRLD{?xf?6nL*$h6ANo2JN{cBO%rp z<`6pLvTf`Q6v_*1{m(_-vailn#tvu}*w>pfHn`cp4fNYq*Hp`=1V?tt35Gn}J6{Zq zAbVqlv-6U}M!>JhZ$|(d=*x~P<)nJm%sk&+6uQ#yv`>xXG%hLGb-x!kZu75gE*!OZ z-Cc7GjnvZDjgWk8Lioo%8bwo+`^meSAkV3~0$#b0@?vx0;N9{NE4&y|I{#fz(KtU9 z`z$~|Ji_t&M|0CXK&vhN`(G%5vmO158k$DntRG^-I7TIhlY0V&?;Y+Ywo@L^NAP~3 zcy5iZMf`eur|`fVk}h$L)g$EpP^CXG{{yl~Vu4yW`XP>RU^JZVY{CI|Elcydrzsg5 zn($Hhhwdln!_UQ&iK(w7fnQe=am5u2T56|bmBHsXt8T6Ph$_~EKP6|335oG|dRP z2tk!3$ljIBx$|@F|2V!{`f9(8tV=c}T}Cr8(F6qnR>cfS79P1__OS?jd%D6AnKi#Q z0Hr{w#zKFssIyx4%K>2E{m^FmcC`?AMJ4ERZQrCgOx@t<)}IRwV4!;lTJzn=B3u@= zkHi&>XVB3-`H%tMw&|=nQ3nvYDlw+PK-!Ob7yvRVl|=-%F3#15)(fF(-9+#KY_ILf zUxjckUTciO-ePN+?=N-*$w&;GYvg2YZ(nDAsg=e|?ox<<3dnN~c7pc;ND5CR&@DuH z7+(P(*HxuO+|(vs7j#Ad!MtKb74es1p1Z7?joiHz3#U~g68MjtcyR>yVYqL}KfNR$ z_itIT_63>7`~eR`9mZzi?}sPfUaL=y^+O+MUVu9vEKPd$YuXdBUk9LoyaW_3W+{#& zu&HXhp{P|PgB^^t79cyJhx77@Tmw2Uy5BFV7oHrh|Br-U)gSh3)ykqXKwOy zT=Nag29_QQj%XSe+$aOzb4BKcq#NIa$O-BuA)BsNSs2$9#P1Ukk5Y^;&Tm^re#| zvjPPs$f!Zg)Mww&qIA&fP?yL$(k$@${T^h0%_rsdV6eeuWwZVtA zeee>a%+>MtZhctDxbCl1AE52^@(%GB_eAT`?z6r(&xDS}Oic?q{#N_&97nqe6@E-> zi&+-qorA|~%Z|SmMa0OG2T>Xb@+k zuK5e^;5x+e(k=eAA8R`BHI;VN z0;j;&r34eXOpbKowTG}ZX*pc}kEDbmqyV~&`MY$T-k`0$X-9x4)(AmfbR{jhMV0>t zw2a_&2N~^!hlJ8f+a1Sr&~u&I*|OhH%$b`l{fzUravh02Jd-~(#7R!ieh3&EJ-?3T zSIqyDRo&Og7{77&zmysYvx`+IW$krrq*QE4x7C=!mcG0Q`Vl}46w-;^SGR8xh6)m-7M!KClFn${P-mAcsv)>I-=)bM*z?` zN+|{?x8nEu!N|nX`cMZY`@+Iyn@6AQqt?FJ^vfthfh{->yj=x<=4Xib^t!`91h#R_ z{_(mXb0lPxHD#u_pXaHyqDU2U8&<>B)NW<7l({4JIYT_)`@Z*Y07O)x;D^zP*n#0n za5sgCv7siz~D#0=jy-`;2 ztPe_ymzb`-+_bK;oX4?t$Q+Pg61&O5KSAj&mIwO8Gq+3RrE&S7zZC@WT*s8$wvZ;2 z^~v;!@9_p5BnCPBSQ%2kcvKP|&`iky=4k|`MIjbtr38Z{480WY9>V)Gm8EHdU*YRA z)f@3-_Tn)787w^Z)hkEnBLWaM5uj9{iwR%kHc>+X{kMZ*P8xuZ6C>R=$sRJ$c{ALl z^qSZ-3_EN|@OKA&JFCd&JR6$xYBh1khZ`oJP{k<-&(z+CxSumm(Sh%S(@m7Q)j_Cu zZJsdrKaRRkqJd-$F6#0 z4sMB@D{q>_;r{iRI_7BUP%1^IjG@Q8Es57iJ|b3y)+hs5#R(^(Ga~4!@SSj0Wt53E;c zoTU)~xsRb@FQ{PQX4pe=u&_ZN0RiMpi)0t{^%(bIPesYelU$R(7;*dTqJ{x84Ul7B z{#KRp$2969BT;g@r|<&Q6*KjtOk6g~?nk`Io0%#Hu4=i}KvmXMjHTKo`S?7&oDqYI zfbA6hGt^W!=fi~-XI(RPjzl?xo5^v+qO@==ehU2zwAF?@4eRgt`9rv*!~pxn-CIG z+C+@b34{+8@HLV5FM+3q65``U3H%Gl;o$;sBVQjcg*@I9G@;Y65gDpPn^nW}kDPsC>ANEPcJnmHY+0`EiXE|`yc+h4!9=FusC zi-aKCM5yDEvtz%m{Dk5qPsjH7uK5*!>W=X7jHfUCWeb)@Wz(SWn|b!tvwQmRm}jE+ zvD^~WZ=_NJds1i%Y-z4a8!Erx5V|(_)=qY@ESkO_LaK*q<%7W*n;1`Vgn(LYNH{~_ zeV!0trNoir77PV8DmY9CT$hyDWh&3jAV5dl?cNw(2JHXr8QN~o_w%WtD9~w@hh~Jr zgKIh)(cQd}VW|b|IIzZeaV6BV=P{I^|I!VhoVH8QL8G2?(f2-tAA>1M}?#)Mvb13-AmlhoiWYQgk2>DWotEn1VmRNp$a>lvPMy!?1>#|kuT zI(jw-UJ(R&bqyQ-mM$1JYV>32H6Z%Qh>zv`M!L?N^Uw%2u@eHaotb_C^)=>+9pd#1 zrVU*ke&N?E)zn|H(l$!S4&!;C@>A8 zkU)If*t3l=0InO$l0YKh*P$%HdD)hS{a}TtA8WW$Y;dO@Y(ERd50V)1COx%AMJeQJdBGru%j{z4xlQ6NTI<+^6TV&H&+kq ztM1L0cJ@CyWYs+z%Y}Xj6EEnA$hvBu4~u@8SK^l|-WcRR@~J6hj$d0Y;_;>EoQw1Q z9g^Ca>K-Np^NzOurg($>u!AK2b~P;o$s%@Z(>K9 zygo^s&;NIpIS6q%K!4SyxfzIOzyx2bLAI|$XJDfp~4iJ*%6<~Y&1y^jL z9WzXA<1na+c{DK*Z4Nh=R-h>CdA*Z2kJ!gav_t63m&3P4K+Nojp~lQ<_fDob1bkAK z86et*DFAWq;8IEk^x?q=V(m^$ZglXo6e>TJ6-euiuMQT2*HhV-GUD|owi5SAf=Nip029@hgv#9pnkcaU&{k7lY$Mv$k<4O#Wn+G*x8ZYeXQGm zT)jPWzy+vV?1dJp0RvnKC-!eeodx@lT*!hCb{299xqGh&flkXl`2^bpSiHlNa9hPU z`fqn@ZL;LPH`CFLY!p_^ROEF^}J9Dy-ZW!y^90Bmo4aA~>*Ehkr_@o{y&YkAI z#@zG2J0<|q`58zjQby=k_Y_pik5wDUTFD>tnUpR6DlBDzM$X1IySK_!s=ZS z&QFViN6d_xH69S)Ax)|jSJQpU>SFmTByWhne~7k?iaWp|p!3XQC4oAu0)W8IY6aWf z|AD;qF+e9w^>CIs4=_Wd^K&UdkY8wCT$UT{rK?jeDCnxi;rUU}GNStv?w~+$(sX|^ zM}UhP7XoU3ee4)^J}!OqfJ!g?1G80M524?YrXD{2u$;7#@yf$>YXtJ}o_RiaG(6oq z&iB|JZCO2bU6?sLk3|`MyJ*%7iF=qjY%D{ZX@7~yit6?g?^xtCx6^qS}X>k?E` zTX&L9Zr;6%HxW~n5AVe0C(nV#q8uh(rb%-o^PlbqB7RT;ir&J8nj<%PogkAA`le$Y zE|ADm&NC-+^P42_>I8YqcosRSIjrEApD7HV}!7Ge(*<6qy5!0tPE7=en-*?*$3# zfif}U=(47-Ktcan#N7V0J&#bj9AX%3P9Mr`VM(i4zhHKm@V)E9d`{L|P6!Syoo-{c z=C{+j{Y7zh=g{tWUPvNvwwA!JnX(Uom#JK_1)wY+lLg#S$qsco2;roRoWlaD*R`IZ znQ0rS83NIv4C}VG;^#ReBfb5tus(IS+x*@0Z0bN@cj_7`gim*vd#i9biqRz=O$3K_s& zj++DRmvu|@&74^xtL`w{w;38Vx%u(_==Sb&J%H~9kH(bi{tGEh$3});UEQ32`{;m2Cl`XC(u%PMkKlDb zYpP3w4zu(Hw6J)%t<3s>cLt;C^DQ>McKuW0QTF~@YT|1gq&sRn>hYnR`9haLIO45; z=JCZfn|hCxw5dqWBrvu>Cr@aa2nAto5%-5BEpmyE2uoZ?bQWl-cMc^<2_#j zZ&EQS_}@)9%0s|z(tr`0jFKeK&n4+V*`%OBrJzeZsBXLI9M!AW&5s^!@nwJ-u-hV~ z=NkIuYQ>eK9_lk7FYFQ)B6y-uF>=tOnt+HadGK~43q1%M<}?M+hVr&9@LmWRw5$bJ0XeFe{sb1$s>BM{*WF^+CuNezgLj=Xv#} zOaNPieTLfxt=-8hb0zKZZW&JUwQ2bwZ@#3f#f&J^ejQ}``dwO{2**AiPgT10-;Y_g z0P^;qtoz;5{t0G>7TcC*w7#M+@VrFCkS$az@|f)0!^p86ycYOKlbtUd1(yjyHogd^3q`gCmjSua@%!~P+kt* z!M%?%%KS|`#$q9$<161mw3Kck!e4MH9gdfo9HVK}_0$oUt7emM8hzd7*4|5YA&TUt z?45Hp*^!VA*stNLc*dH8W%in3+(`0BqAs?q3fk z+ZT^Qx;S-9k9OrUC-fvm=NL2XDH)(I4LuABY!dPM)O=9B;#quMurDl zPl%hK|1k|ueVCb2*aRj+O{+RK0PRG|d_8Q0I?O&!i^z$?AJrGP3?&1Y)~8!~&Ao<) z2+JTcv;Tv$TO^4HCY1IadI`da&Ink;#JaZdV`%5=2tJ!p<)9j%+&eNWEv4lQ*>9|E zblx8QGj_;F1%QM9#lbJ}qu1~DiO%~FZ5b2O=UC-Rr6_p8BS-+k0JvY(0F|REJyyq0Z zE0S)_v|)rfB=|OT-MR84ajl@8$McQl&aQ02n|+<{@_a{uyRhg4$G=QM zisX3T39$#-y;Sn2laS&*UU>3_JUpg4-!{P)k1P;w=I)qBF;Dn6Q8$4BV!**)3u#?F zLQ}T)C>Kj6voCi^qW);wCIxPINR_`H4yF?Z(jo{k*v-0c^YdQayL_jU*)Yav^f5o_ zwZ>{;Ts`Emvb``4h&mkQL(4K)ZP``Zkj@?U?L`2PjDY=YKp1Ih*0{Yya>gns4Q0=B zl+Qk^YJu&7?HzP~c=P{FK*e($`0+ebeULl}Gao+PI?rpp7;}%Yl7*T(hIet=%$K~I z{EFV*{}GPkvZ+Hmd6utLWr^ldrPF|^q?t3e3_)hNVPi9qsSnzMeLsJDkhKys%((QY z-F@%5BMq?FId~;X_y)48QbS-=vMzy{A8=;9OVCgo1csRKAZn;l4FV((QH5D z6!?P#r9aF-a9}s!lT&8it{4SZpdI2}JE92}MO#ds?R*gM-NBO-+2aZ0n=R5`cXaF| zV`5c4k%|71IU%Z_9v+^b=%8d}mlgGNA(V8D49fnQ3Bi zd|jUW!P^%c?WDQWui;sE!tc|%+aJactL0ul{<(iVrhh2mgi}=8RV0bkaawAcBu=43 zLA^gV`@6kZ!WLBRn8B~v>INv3aldbN>aL5D!Vx^2KB9d-S5$qy^Y@23nyC30D878r zG{LU&V)Kj2{9jro)rulvlG#oCdE3Xoub#QK&Vm;ryklfK5c(ZQgu8(8y+dfOYlf~w zjmSeUUjgXPp{b^F@NRZl@T>XkG9|Q|kRGKr4O)qxhT>}7ebfID%%j-phoq>D5l|dE z0B~XbgQ@wp1Sg&Q8~W(`Pbuo@i2w%#!bB3dVXFX-X%gncE{}IX34>m-2-zThxAV6*+9z=}{$$asoU@v_?P8;S574+vu{FFkl4lAu{j zJ*YQZtF2Fjbc0M7ucbKmMD`ebdtgh}K(%kgjxmini4 zhA$_dIX*~xtIe8MB!PHlIlz~is4wWtnU%4tnMd}_Z2LLyh4(O$^E}O+cFE~G*=(v{UD{RaD0;O|K+JgOr^DqJT;laDhH!6Tpx0P9~E?heCMn? zynGV%;>H2WheXo1Z#JoBz$y+gcLv<7ywIVaM(YMFUiv)>%IRU!8nUQtAEcCs4O=xjnIs9P4 z0ykh9*64L{@AuF`WZiYGWx((v&(`ItyM49;$CI#?!T8mhryw}SMAVLXy1^DLY>)Tbc=VH0(KPB`z+pMS1>R6vMbbHd9sbKtz>h+6S{Xk6pwJ<9q;$*A@Mbdwmu>?twL4)VJD?`>q^G zswP5cIjFxhhEujWLff|SMbcBA2MH(l1`F3(MS-(4q00r z1;x>|RHiUIVUcY&M5#XxgH<0)T`1^M$hN9-3RRCRz1rB>|5O&yi@u2T(8!Q}2oz?V zCeOjzQ8ixp9NhQRq}0ozhl#BJGC9;oO*x*~#~$feuNH?^{({hb`Y6)+alES-csT@V zo#XF1-s<)Wv}7eUIsl!-bp-gW)mh!T++X1P+}-XF0!pv?TTHkk5dTCYA;&lGBJnoh zg}85AY{hgfdyxlEpe686d2KbNj^ik8_o>EIvxX1}RQ z7$G$;M;;S{OY5ttl{@gIcSVFTT*-{C)uw2u_dc-qO&v_H?2{LLl*ftDeMgy^pG!4R zl>Zu4?K%0thYA$hnrVtS;$!KziWSuLMD98fwd|8>l3vJ_aKZ1=%?-<;Oo%qg+Mp<> zXR>xi_P}JQ0c5qwgJx4bTpDREiOBLsxIH{FRBH#&S33?$KT52y=c#qB4Q?qdo^@Rd z`eb4+m1k0f|HEt={{>tdI))X(#l{blx~*9E9rLEvE*|^=){^E(Py=wc1^@;h8P+Vr zovo-ZmqKf8-j5r#e)r3}iXz~%?5WO|8%8BE^V_=u=fL|1ur76@G`IMHZy$E@Fd!)U zcf)2$kO|wbArAyv=xqO9Q9CEw9V-_L%>Eoc{}8L732f3^Cz9kr{&}$e#L+Kiqm{nhQ3DTgb|XVyro+ zBq&TH7_jwaA7Ll=k{;zXYeaqzRUm76wb=+D^*LhFSEX`zbllN991{_kxiIHm4iJ)Y z|82Pb+sVb>N%l8?7rU0d1nC_%O09gk=frbLm)D>ybdMaBw6Dx+kthj-^kuZDUt%^a zfb!VF>bi6AqGb>>F}+kR>G#yIxmvrDIcgx6s4y|@0B7t5bP;=HmIu!bv|*C`PVm1o zDB>WpHUF!t*@fpS&=;x1`t8F%pgtDuM?WJR ze$thRg#_7e(w%11fp4Vo=?6O=O zUX@G~`@=SY0{0L0)pgn1T;k^-r9&%po8yg*a{KVifbc%OE*~(s;S$1N-V8x3k|JOT z%Rj-h8Km>+@>w3*`QhOTQlxCH30-h3e!SJm4t$I~?)^-Q7%h*om+Nt-;fYSK23;Ux z1P{d7o3W0PXHDi_jcT9Z@Ce`&-wA+GrQqAMvJTMnLE#g_k|*7~=(r!*ZWg#NVO3}y z_}CwdJ)FX1b-Ehri5@x^R@m6RsQi&qVeFIcIAqF4DLjO@@DSIXE%6f8C%Q2pQ6sU! z(O=AbEnq@^qpn@CpzJS2`cj1HN;_j);!vp}pBpWjvolC2-dApU=r6_RK+=)mQfg%n zvAAY60b)~J`+_Y&)uEPDWVczrrE_O`u_j;r!|G~b^8UHznU6nGChPxHSfjrXS6lee zJT}w)T(B-Itzj?N&P4x7cu@@qd=dGbTTI*Dr(lsEAc)G}+EmFKZqV(RurL#KS`x^O zL1GTmSe0g2r>#=AOh!%C3)d#{ddA^%vF?A>_qgc~Ufp~N2)KOpsztM@MljHFGxT;f zf!Yav4K)rNjxM|pFOf3@cit8)mi8#$yKeXLtgg}2@lI$44&To3TI7>$BcpShZZWYGDaU3O1`DcFfv?m} zC>U=60|O_A60VAXR3p+v3 zDi$rk(*1dV+f&??H&W<-S?72O;LCAEmGHY6u{f5M>#x5`*AvTlPu$2{}Qmmk9H;!}%+h zzfkg#m83{Ap!7JN-aK(5+Kh(fA|?UH6HgN`|S`bD@%WGr5E(K#ZAbA?g(z6 z+Ypa8&_kBL15RO5*-!ni=3@W-;pkMQqhXu7PIth`@dDSLUnnEpA2y1ndGGjUFn6K*Td zo(&>!HBj_1e;Gj=J5W|*%OPCS%GcM9MK#deTI_mn%=824ps;%=NM5a9koK|3v(!)Q zs=2Lw9mL@Uc(`IsSlh5b84>?#1JW2y7WSvriV<2Q`CbvDltauK z2R>g6n!XlTDCikQ^b;zYg5Nkeirjb%Yc^~*r*CxOo^THn=L3O|;?E0OZD-o<@rfOU zd+p4Kt^Dj=H`0wU>Sgide!>Z*_p=)W@LDJXKGzkwI`&QQ)pn9a>v*T!Z`tWZC?{$f z!(K#}9ZmX1uK=bBoX`TxmbcMaoq-<>me~>YHvblQP>C`AFAIRVx7xIVC)p~NR`M1i zpvVlfzqx$e6uCWX?a8ZH&{Z|%^j1vg?or%_jD13ug~zHa{g;^AUhs>vrT-sdhbW{# zMCE87IFrLpNR{Zx#c<*QHn`&~Fo+_sOQsR>PfD?U(gHRXgbtbFO`#&{9bfJ)9@e$d z?f3`)Y}!aq5QMzzZHL8wqp~?v?Bn75^O%y|`$mb3ixSHa5Cg`+d=3wpXDW3l|%$-cwN9;pk`Mzg5WVnmi>LhU3^0)-m(O3|uM( zs=jp)&q_F5b+r3bgn1Z%XxR3#;3}@S1DtqQ<=OiT+z+dl3?{5xUmM*5ZJk`Zq&`(J zDCsMH40f+m$E{7Yb)ao6@ zmuycod+UTsM*f<- zP}$%dWe3v7HSKDikc0WlFKQ-+T`|SYCqQlf``~+E{$wf-h(x|W%G2Rv11$AO4nBdn zH~Yxnw?J3_868z_AZZu_FxrgCrqz1QVEN%7yVOAP}EiBF^RDgF{Hmh|lm+z8fg)T}^K|LpHv zjrOHZJH-MvKm|`;Oe+z(pV13GY8A%+a9qn2j*CF#HK+G%zpSVJL>kWU>qr$db9SjY zU+^^cJ@PZu>6qCYd|*Uu7<6`eSxr*EimqJ(2w%i?T&+MdwAd+BxFV81Yz+a7er+1@ z1Mb7z#@hhzpC5#hi{Q80k!Yr%Zl9!8oL9ob9FXg!HT*r9SKFcak$p`#&+P;OEADA( z7Aq&OHq1G)5*~EHp>}q2lUj39v^`^-D`0XF*#3pnVErkNRnRtz9EW>$s#XYO&GmZW z?(Sc^(bv_Ox(s52+VmJD+YYtXR>(G!Jdhi7Fotz=!|8wUxZhIM0~M!4te9O8Q!VM= zC#lr9Kyy^qJsXop-1bf_Ls^8DQb-~<{VBG12w;*&OZ-}2F(MGu?iEpPfRtSjI0xmD z(NjzY;Azo!%s{veb{TU2T8!mAzJn^}FwcyYrWfEKYw59CTGr|$&Kmct+JvLX4y`M8 zBqZ9|IZ`pTB0>%&D9@C6Ehf0HU#o!kWG|-)K&m?Ot?kAA<6_e@IiPICdB<>z6Lb57 z!VJz`!b@YtYW*p5q)1iwhugb%L39lskI=L0KR1}~G6NHDyPP;#?n$KSU|@9q?2-{a zYYofJbhynVxr)N}HBn7BjM&ws`60<_m8%OjrfeDTkj#$4{6@}*IEMLBJp7IDV> zzUH4BdmF<`1{$lx3^-JyYome04QBq7HZW&_{HhL01)6_)yiOm|NJTe_j|~DyMLR#o zXm1JJp@_qGiu>(U?Qt)SS&*1x+StxXb@ulxC={_o$jTNXScj$@QVp z;@CT*_)6y5+}UE;A-3U4nDTId+_fL@VE^a+^q0^=mYFF|U_gE+Xh4w2jEEsY zuUtsIg0(&>A3QHT9hSFEXIWkJ>uHdIF#V;_lWuvYom|hv<#l`h)VS&&qV5N(+JFs3 zdylGz$j>t8_R)IIItG)2N^COSxL}T?1~A#So7jRlCR(Idx*vh7&yjq;vkh~Sd)&OK zZl?+j%(`kJ4#6411D#G~e&QdlC!+igm%{~yEmfr!+qPP`>71!MEvT(_qKJkLc;a0T zWz^5zRO&}HhEi-ftm7S>X83-#74gZ8ARUSt>KhYyq#nn9(01I^TN8_?YXQApM!9Sy zM{t37mGp=R2i!EyU2S<76;X{eJ&f9CG-#MGMP7+=cJ(9gkM0>!*c=TDx?q{;nbF|$ zGb`P8DI09boG4?gv(x%Eiwb>a^gqNjG9zP%S}PG0rjxVizhgsL)ss)F^`fmszn;(F zSu*ku0a!nWj!z1ypV-8!^gTp=CWjXu9sITZl%~NpP_LsLYK)N*mjRs6VS-D<(CtJW zC!()-Ec*hwb5qGJ5+gHyoCEGAYAeS!eb8M)0u>yVk09)UWY%B7#@pfCtcN1%abU%Y zuAI;TuWn8yvHEFM?iM^Bf^u}_VmtDCz-(5%W6N?_ z03(My1Q7A)wfsySQ`&Og?)iFfeyYxr?|*|(ZV+f-XSBf>WM{vb)+~&qMhgk#`4wD2 zFeaxZfp7lQU?`(0jW+H4bc_7X!G02Ss-2LN(>({q=>=>$i+&F7iT^{x%cmjOJ%9(_Z zVw6!rzNG(L#?o-aeLlG#TNLrXxiQ~Fd%e3ZGa^9&g>649xYhF*Xl|lxtm#N3{OF9T z(s&?h5gT6)hPh)D6Q%Hr!RvxZcEm~|V8XnhrT1NcgeEbHXGs_rk6l!ZB`N&b?D$6o z`fdvHRDGfuPFicU-p)3G4{kV9x7HS3R*z0RX;#~jG5HSfAm&Hg5#>(1xz_*^O8o>YMuPiHO?k=JxjG?d~bf2Taq?NfaI z!MP4~aXS7=YESAtPydm|ZUr1-qaTudthv;P+Qnv0di1DK&)Y$JU_bd3rxTBlv5uSf z?Y42CBNuhDWWCi6j~ykD;}XS{AOEK-qHS<5H!L5?*>2AyJmfVcS66>SVJ}2O;y}b$ z1nFNbMa27x28};7Ic}bMWmUO5+U*qs%0W$5Ar0TSGP;+2N?|P$K^}--@-~F|1gpGpA6Z8gL#-a?qW@$|U6y_F?eaxhft{3KKI+r>@6KStB*el`p_FUX)-vI9hg@0ZuXE=V`=>Ba8y zKN89q|F+~IwIfw1pE|CKY%H+O`^r5}un5;mT*NdC!2ztys8~8^!xvon!Lr>#)JV3p6;kPnU(>O}f zn6gv*`ug&^<5j_u_7Fiepdc~Xkg_N-TAXY@1YJZy6p^BQX7xAJ8rCu}4O*IiueCm3 zdI?!I&cWa>%?>7eX#BU({yQCLAriD*rpCmsOhqvJtpgyW17}gN9_2opWt4A9FxkVO5emL;KCn>M@#NTyv1fRo`e~ksAdccGoZrUo!SDyv)3oyESvk=c8Zkpn!asU$9Ye) zA`nmX1APB)_d5Q*@)VJa`a#3{Cg?hKavWA?JvpfNCZ49vi}^WTS!C#5A{1f+XY1SK zq5P#sd(ka)|EIo12=9EqpZ);5tc;G;J>2A(^c#v(|kH?~TP5sF+&6wDcVgNKZ! z9(+(tukrzTdQ9jnW-}h{<0b$Ut5$juqL*VR2S>GO`z{CrA~yg`v|4DvFHyTl$!hLtLBTcc94_&nu`F)$NY8jq+=dJ zdV2dF?heIYZniad_`Z%L*9JPhTCM310iDt*B?2Ktva5zy2O8<;An9&4jg#`Ye4SmRu?BTVC2T<@Ozrt(~vE zuEa)7`fp0*!k9E19F786Pd_^2p5A8^?)`sCRS15UXjVy##5# zbDC3P-gf$^Kr$HrNvNiXxJs-=fC_0yac}1f8dMf*ZMFr3JU7sBaw5EL=p(~{8@k-6 z=7PGtlUPgO`HWJ3Yg=8RoRw_F2QvP=iSxv2tl@ouoZ3b{uSplpT>bG(9ao8=A{h2gi|L5OH zC5Re%OeKK_N=UmHdA*Ju(|IYNAKy_{4FhJ*mm1BbLcrL*RMvTBlJSP_s^^2U@-x^N z%fJEd!(#JO0tW5Ga62W5MHxlWv|%=1r%#b&7*a=R$j{vqrihE<07uo#Pzjk@B3CvSk;&2} zeJ8{Bm%fGy#beZtUv6}yLumN?yf5o3_Agy~+IuTa7%YTyrx)EKd4AWi@H!saxN$w+ znr2$A=LWeZ9CIJyycTi@XGs;;c!aISm3Hh_0;IhB+j9CSJN_a+g1_eB9~1gjv|lO> zseU3~{P#sQbhtm>`F zK{H33WEP<0Yqa4pSf%3F$OwoYkvH+rs@t3PUsDI7y#m}LT@9N5nQ-e$n!?fJLK zN^?2T2IavKK0rDSIT0qxXs^EwN%Uvv{O@pB&LH^@>eLh_*3Kiz%m_m$iZzprd8_h_ zQchc_pB2Qrg0Y?Nr`OYQhaqON3w>PCk0EV25W}Jf8S0UyaNei_mryobX{jT>FY42& zOMhA62{%Z}t!#u#iuzKcj)W|?h#oN8*{GG$d)qf4`0V0~hMH3p&jlh*4?aJT{UanV zFX`YX%xESMt87Bpy{T0=$jggPE|XLJ-1Jfqn9-qpyL#CP-VQfkZj47U2p~b~zdW<) z-OxMHl(G@^i)aD$zOUq=iUndk9DAUC{&8L(aDd{z(rsMj*RvMvb#K1cGmRMv|L!xi zNVv~`+9tVIk3{0s=)}5;jFY*K1^CJ(w}5OppQ1?ClHbj2{HK5kcyLc_4!l_F2+ol9 zNdtNfq%OU_GLKN`Jt*f5)VThwoGHfY6qCxOD(Ae>UzZ6eGHt3dBmKV1OOH!96w?>- z16jf3M>+_d$EeX3mu&y50eQ#FuOnLuP=CrgZ7yDKt&9 zqiOhi`zNn{xQzX8m=(R5uP^R%wz8cGa0g7`6zvXzGkZBGb-s&qj;r2cRb^KG4qwuB zFp8PBUe6m>yPhZPNif}bmsSJ+i$P-PeAG!ZEs{msR~Q%u4-n%roLhuiEF0wvyOZPYYO zOyGVi!qS|$Ul?6DQIqM^BF72dHKi$J

CVuQrAhNuH{slMCL-)UUdqz&NC#Edg}% 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�I#S{L9keM7|g(dVw`9n>8 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 a@%_-Zd5T*D4j9FWp&X+Bz5L(bzyA*jUgSgo literal 0 HcmV?d00001 diff --git a/gsplus/src/kegsfont.h b/gsplus/src/kegsfont.h new file mode 100644 index 0000000..d4394a7 --- /dev/null +++ b/gsplus/src/kegsfont.h @@ -0,0 +1,514 @@ +/* $KmKId: kegsfont.h,v 1.1 2002-11-10 03:31:51-05 kadickey Exp $ */ + +/* char 0x00 (raw 0x40) */ + { 0xc7, 0xbb, 0xab, 0xa3, 0xa7, 0xbf, 0xc3, 0xff }, +/* char 0x01 (raw 0x41) */ + { 0xef, 0xd7, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xff }, +/* char 0x02 (raw 0x42) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x03 (raw 0x43) */ + { 0xc7, 0xbb, 0xbf, 0xbf, 0xbf, 0xbb, 0xc7, 0xff }, +/* char 0x04 (raw 0x44) */ + { 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x05 (raw 0x45) */ + { 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0x83, 0xff }, +/* char 0x06 (raw 0x46) */ + { 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x07 (raw 0x47) */ + { 0xc3, 0xbf, 0xbf, 0xbf, 0xb3, 0xbb, 0xc3, 0xff }, +/* char 0x08 (raw 0x48) */ + { 0xbb, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x09 (raw 0x49) */ + { 0xc7, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x0a (raw 0x4a) */ + { 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x0b (raw 0x4b) */ + { 0xbb, 0xb7, 0xaf, 0x9f, 0xaf, 0xb7, 0xbb, 0xff }, +/* char 0x0c (raw 0x4c) */ + { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x83, 0xff }, +/* char 0x0d (raw 0x4d) */ + { 0xbb, 0x93, 0xab, 0xab, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x0e (raw 0x4e) */ + { 0xbb, 0xbb, 0x9b, 0xab, 0xb3, 0xbb, 0xbb, 0xff }, +/* char 0x0f (raw 0x4f) */ + { 0xc7, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x10 (raw 0x50) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x11 (raw 0x51) */ + { 0xc7, 0xbb, 0xbb, 0xbb, 0xab, 0xb7, 0xcb, 0xff }, +/* char 0x12 (raw 0x52) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xaf, 0xb7, 0xbb, 0xff }, +/* char 0x13 (raw 0x53) */ + { 0xc7, 0xbb, 0xbf, 0xc7, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x14 (raw 0x54) */ + { 0x83, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xff }, +/* char 0x15 (raw 0x55) */ + { 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x16 (raw 0x56) */ + { 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff }, +/* char 0x17 (raw 0x57) */ + { 0xbb, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xbb, 0xff }, +/* char 0x18 (raw 0x58) */ + { 0xbb, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xbb, 0xff }, +/* char 0x19 (raw 0x59) */ + { 0xbb, 0xbb, 0xd7, 0xef, 0xef, 0xef, 0xef, 0xff }, +/* char 0x1a (raw 0x5a) */ + { 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x83, 0xff }, +/* char 0x1b (raw 0x5b) */ + { 0x83, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x83, 0xff }, +/* char 0x1c (raw 0x5c) */ + { 0xff, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xff, 0xff }, +/* char 0x1d (raw 0x5d) */ + { 0x83, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0x83, 0xff }, +/* char 0x1e (raw 0x5e) */ + { 0xff, 0xff, 0xef, 0xd7, 0xbb, 0xff, 0xff, 0xff }, +/* char 0x1f (raw 0x5f) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 }, +/* char 0x20 (raw 0x20) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x21 (raw 0x21) */ + { 0xef, 0xef, 0xef, 0xef, 0xef, 0xff, 0xef, 0xff }, +/* char 0x22 (raw 0x22) */ + { 0xd7, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x23 (raw 0x23) */ + { 0xd7, 0xd7, 0x83, 0xd7, 0x83, 0xd7, 0xd7, 0xff }, +/* char 0x24 (raw 0x24) */ + { 0xef, 0xc3, 0xaf, 0xc7, 0xeb, 0x87, 0xef, 0xff }, +/* char 0x25 (raw 0x25) */ + { 0x9f, 0x9b, 0xf7, 0xef, 0xdf, 0xb3, 0xf3, 0xff }, +/* char 0x26 (raw 0x26) */ + { 0xdf, 0xaf, 0xaf, 0xdf, 0xab, 0xb7, 0xcb, 0xff }, +/* char 0x27 (raw 0x27) */ + { 0xef, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x28 (raw 0x28) */ + { 0xef, 0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xef, 0xff }, +/* char 0x29 (raw 0x29) */ + { 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xf7, 0xef, 0xff }, +/* char 0x2a (raw 0x2a) */ + { 0xef, 0xab, 0xc7, 0xef, 0xc7, 0xab, 0xef, 0xff }, +/* char 0x2b (raw 0x2b) */ + { 0xff, 0xef, 0xef, 0x83, 0xef, 0xef, 0xff, 0xff }, +/* char 0x2c (raw 0x2c) */ + { 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xdf, 0xff }, +/* char 0x2d (raw 0x2d) */ + { 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff }, +/* char 0x2e (raw 0x2e) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff }, +/* char 0x2f (raw 0x2f) */ + { 0xff, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0xff, 0xff }, +/* char 0x30 (raw 0x30) */ + { 0xc7, 0xbb, 0xb3, 0xab, 0x9b, 0xbb, 0xc7, 0xff }, +/* char 0x31 (raw 0x31) */ + { 0xef, 0xcf, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x32 (raw 0x32) */ + { 0xc7, 0xbb, 0xfb, 0xe7, 0xdf, 0xbf, 0x83, 0xff }, +/* char 0x33 (raw 0x33) */ + { 0x83, 0xfb, 0xf7, 0xe7, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x34 (raw 0x34) */ + { 0xf7, 0xe7, 0xd7, 0xb7, 0x83, 0xf7, 0xf7, 0xff }, +/* char 0x35 (raw 0x35) */ + { 0x83, 0xbf, 0x87, 0xfb, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x36 (raw 0x36) */ + { 0xe3, 0xdf, 0xbf, 0x87, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x37 (raw 0x37) */ + { 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xdf, 0xdf, 0xff }, +/* char 0x38 (raw 0x38) */ + { 0xc7, 0xbb, 0xbb, 0xc7, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x39 (raw 0x39) */ + { 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xf7, 0x8f, 0xff }, +/* char 0x3a (raw 0x3a) */ + { 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xff, 0xff }, +/* char 0x3b (raw 0x3b) */ + { 0xff, 0xff, 0xef, 0xff, 0xef, 0xef, 0xdf, 0xff }, +/* char 0x3c (raw 0x3c) */ + { 0xf7, 0xef, 0xdf, 0xbf, 0xdf, 0xef, 0xf7, 0xff }, +/* char 0x3d (raw 0x3d) */ + { 0xff, 0xff, 0x83, 0xff, 0x83, 0xff, 0xff, 0xff }, +/* char 0x3e (raw 0x3e) */ + { 0xdf, 0xef, 0xf7, 0xfb, 0xf7, 0xef, 0xdf, 0xff }, +/* char 0x3f (raw 0x3f) */ + { 0xc7, 0xbb, 0xf7, 0xef, 0xef, 0xff, 0xef, 0xff }, +/* char 0x40 (raw 0x14) */ + { 0x08, 0x10, 0x6c, 0xfe, 0xfc, 0xfc, 0x7e, 0x6c }, +/* char 0x41 (raw 0x11) */ + { 0x08, 0x10, 0x6c, 0x82, 0x84, 0x84, 0x52, 0x6c }, +/* char 0x42 (raw 0xf5) */ + { 0x00, 0x00, 0x40, 0x60, 0x70, 0x78, 0x6c, 0x42 }, +/* char 0x43 (raw 0x82) */ + { 0xfe, 0x44, 0x28, 0x10, 0x10, 0x28, 0x54, 0xfe }, +/* char 0x44 (raw 0xeb) */ + { 0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x20, 0x00 }, +/* char 0x45 (raw 0xe4) */ + { 0xfe, 0xfc, 0xfa, 0x36, 0xae, 0xde, 0xde, 0xfe }, +/* char 0x46 (raw 0xec) */ + { 0xfc, 0xfc, 0xfc, 0xdc, 0x9c, 0x00, 0x9e, 0xde }, +/* char 0x47 (raw 0xed) */ + { 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe }, +/* char 0x48 (raw 0xee) */ + { 0x10, 0x20, 0x40, 0xfe, 0x40, 0x20, 0x10, 0x00 }, +/* char 0x49 (raw 0xe9) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54 }, +/* char 0x4a (raw 0xef) */ + { 0x10, 0x10, 0x10, 0x10, 0x92, 0x54, 0x38, 0x10 }, +/* char 0x4b (raw 0xf0) */ + { 0x10, 0x38, 0x54, 0x92, 0x10, 0x10, 0x10, 0x10 }, +/* char 0x4c (raw 0xf1) */ + { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0x4d (raw 0xf7) */ + { 0x02, 0x02, 0x02, 0x22, 0x62, 0xfe, 0x60, 0x20 }, +/* char 0x4e (raw 0xf6) */ + { 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc }, +/* char 0x4f (raw 0xaf) */ + { 0xc8, 0x18, 0x38, 0x7e, 0x38, 0x18, 0x08, 0xf6 }, +/* char 0x50 (raw 0xb8) */ + { 0x26, 0x30, 0x38, 0xfc, 0x38, 0x30, 0x20, 0xde }, +/* char 0x51 (raw 0xce) */ + { 0x02, 0x12, 0x10, 0xfe, 0x7c, 0x38, 0x12, 0x02 }, +/* char 0x52 (raw 0xe5) */ + { 0x02, 0x12, 0x38, 0x7c, 0xfe, 0x10, 0x12, 0x02 }, +/* char 0x53 (raw 0xea) */ + { 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00 }, +/* char 0x54 (raw 0xe6) */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe }, +/* char 0x55 (raw 0xe8) */ + { 0x10, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x10, 0x00 }, +/* char 0x56 (raw 0xd7) */ + { 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa }, +/* char 0x57 (raw 0xe3) */ + { 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54 }, +/* char 0x58 (raw 0xf4) */ + { 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0xfe, 0x00 }, +/* char 0x59 (raw 0xe7) */ + { 0x00, 0x00, 0xfc, 0x02, 0x02, 0x02, 0xfe, 0x00 }, +/* char 0x5a (raw 0xf3) */ + { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }, +/* char 0x5b (raw 0xd2) */ + { 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 }, +/* char 0x5c (raw 0xc7) */ + { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0x5d (raw 0xd4) */ + { 0x28, 0x28, 0xee, 0x00, 0xee, 0x28, 0x28, 0x00 }, +/* char 0x5e (raw 0xdf) */ + { 0xfe, 0x02, 0x02, 0x32, 0x32, 0x02, 0x02, 0xfe }, +/* char 0x5f (raw 0xd1) */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, +/* char 0x60 (raw 0x60) */ + { 0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x61 (raw 0x61) */ + { 0xff, 0xff, 0xc7, 0xfb, 0xc3, 0xbb, 0xc3, 0xff }, +/* char 0x62 (raw 0x62) */ + { 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x63 (raw 0x63) */ + { 0xff, 0xff, 0xc3, 0xbf, 0xbf, 0xbf, 0xc3, 0xff }, +/* char 0x64 (raw 0x64) */ + { 0xfb, 0xfb, 0xc3, 0xbb, 0xbb, 0xbb, 0xc3, 0xff }, +/* char 0x65 (raw 0x65) */ + { 0xff, 0xff, 0xc7, 0xbb, 0x83, 0xbf, 0xc3, 0xff }, +/* char 0x66 (raw 0x66) */ + { 0xe7, 0xdb, 0xdf, 0x87, 0xdf, 0xdf, 0xdf, 0xff }, +/* char 0x67 (raw 0x67) */ + { 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 }, +/* char 0x68 (raw 0x68) */ + { 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x69 (raw 0x69) */ + { 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x6a (raw 0x6a) */ + { 0xf7, 0xff, 0xe7, 0xf7, 0xf7, 0xf7, 0xb7, 0xcf }, +/* char 0x6b (raw 0x6b) */ + { 0xbf, 0xbf, 0xbb, 0xb7, 0x8f, 0xb7, 0xbb, 0xff }, +/* char 0x6c (raw 0x6c) */ + { 0xcf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x6d (raw 0x6d) */ + { 0xff, 0xff, 0x93, 0xab, 0xab, 0xab, 0xbb, 0xff }, +/* char 0x6e (raw 0x6e) */ + { 0xff, 0xff, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x6f (raw 0x6f) */ + { 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x70 (raw 0x70) */ + { 0xff, 0xff, 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf }, +/* char 0x71 (raw 0x71) */ + { 0xff, 0xff, 0xc3, 0xbb, 0xbb, 0xc3, 0xfb, 0xfb }, +/* char 0x72 (raw 0x72) */ + { 0xff, 0xff, 0xa3, 0x9f, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x73 (raw 0x73) */ + { 0xff, 0xff, 0xc3, 0xbf, 0xc7, 0xfb, 0x87, 0xff }, +/* char 0x74 (raw 0x74) */ + { 0xdf, 0xdf, 0x87, 0xdf, 0xdf, 0xdb, 0xe7, 0xff }, +/* char 0x75 (raw 0x75) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xb3, 0xcb, 0xff }, +/* char 0x76 (raw 0x76) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff }, +/* char 0x77 (raw 0x77) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xff }, +/* char 0x78 (raw 0x78) */ + { 0xff, 0xff, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xff }, +/* char 0x79 (raw 0x79) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 }, +/* char 0x7a (raw 0x7a) */ + { 0xff, 0xff, 0x83, 0xf7, 0xef, 0xdf, 0x83, 0xff }, +/* char 0x7b (raw 0x7b) */ + { 0xe3, 0xcf, 0xcf, 0x9f, 0xcf, 0xcf, 0xe3, 0xff }, +/* char 0x7c (raw 0x7c) */ + { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }, +/* char 0x7d (raw 0x7d) */ + { 0x8f, 0xe7, 0xe7, 0xf3, 0xe7, 0xe7, 0x8f, 0xff }, +/* char 0x7e (raw 0x7e) */ + { 0xcb, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x7f (raw 0x7f) */ + { 0xff, 0xab, 0xd7, 0xab, 0xd7, 0xab, 0xff, 0xff }, +/* char 0x80 (raw 0x40) */ + { 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 }, +/* char 0x81 (raw 0x41) */ + { 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 }, +/* char 0x82 (raw 0x42) */ + { 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 }, +/* char 0x83 (raw 0x43) */ + { 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 }, +/* char 0x84 (raw 0x44) */ + { 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0x85 (raw 0x45) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0x86 (raw 0x46) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0x87 (raw 0x47) */ + { 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 }, +/* char 0x88 (raw 0x48) */ + { 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 }, +/* char 0x89 (raw 0x49) */ + { 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0x8a (raw 0x4a) */ + { 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0x8b (raw 0x4b) */ + { 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 }, +/* char 0x8c (raw 0x4c) */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0x8d (raw 0x4d) */ + { 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 }, +/* char 0x8e (raw 0x4e) */ + { 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 }, +/* char 0x8f (raw 0x4f) */ + { 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0x90 (raw 0x50) */ + { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0x91 (raw 0x51) */ + { 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 }, +/* char 0x92 (raw 0x52) */ + { 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 }, +/* char 0x93 (raw 0x53) */ + { 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 }, +/* char 0x94 (raw 0x54) */ + { 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0x95 (raw 0x55) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0x96 (raw 0x56) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0x97 (raw 0x57) */ + { 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 }, +/* char 0x98 (raw 0x58) */ + { 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 }, +/* char 0x99 (raw 0x59) */ + { 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0x9a (raw 0x5a) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0x9b (raw 0x5b) */ + { 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 }, +/* char 0x9c (raw 0x5c) */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 }, +/* char 0x9d (raw 0x5d) */ + { 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 }, +/* char 0x9e (raw 0x5e) */ + { 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 }, +/* char 0x9f (raw 0x5f) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0xa0 (raw 0x20) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa1 (raw 0x21) */ + { 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00 }, +/* char 0xa2 (raw 0x22) */ + { 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa3 (raw 0x23) */ + { 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00 }, +/* char 0xa4 (raw 0x24) */ + { 0x10, 0x3c, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00 }, +/* char 0xa5 (raw 0x25) */ + { 0x60, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x0c, 0x00 }, +/* char 0xa6 (raw 0x26) */ + { 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00 }, +/* char 0xa7 (raw 0x27) */ + { 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa8 (raw 0x28) */ + { 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00 }, +/* char 0xa9 (raw 0x29) */ + { 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00 }, +/* char 0xaa (raw 0x2a) */ + { 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10, 0x00 }, +/* char 0xab (raw 0x2b) */ + { 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00 }, +/* char 0xac (raw 0x2c) */ + { 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 }, +/* char 0xad (raw 0x2d) */ + { 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xae (raw 0x2e) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, +/* char 0xaf (raw 0x2f) */ + { 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 }, +/* char 0xb0 (raw 0x30) */ + { 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00 }, +/* char 0xb1 (raw 0x31) */ + { 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xb2 (raw 0x32) */ + { 0x38, 0x44, 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0xb3 (raw 0x33) */ + { 0x7c, 0x04, 0x08, 0x18, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xb4 (raw 0x34) */ + { 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00 }, +/* char 0xb5 (raw 0x35) */ + { 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xb6 (raw 0x36) */ + { 0x1c, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xb7 (raw 0x37) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00 }, +/* char 0xb8 (raw 0x38) */ + { 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xb9 (raw 0x39) */ + { 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x70, 0x00 }, +/* char 0xba (raw 0x3a) */ + { 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00 }, +/* char 0xbb (raw 0x3b) */ + { 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00 }, +/* char 0xbc (raw 0x3c) */ + { 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00 }, +/* char 0xbd (raw 0x3d) */ + { 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00 }, +/* char 0xbe (raw 0x3e) */ + { 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00 }, +/* char 0xbf (raw 0x3f) */ + { 0x38, 0x44, 0x08, 0x10, 0x10, 0x00, 0x10, 0x00 }, +/* char 0xc0 (raw 0x40) */ + { 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 }, +/* char 0xc1 (raw 0x41) */ + { 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 }, +/* char 0xc2 (raw 0x42) */ + { 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xc3 (raw 0x43) */ + { 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 }, +/* char 0xc4 (raw 0x44) */ + { 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xc5 (raw 0x45) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0xc6 (raw 0x46) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xc7 (raw 0x47) */ + { 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 }, +/* char 0xc8 (raw 0x48) */ + { 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xc9 (raw 0x49) */ + { 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xca (raw 0x4a) */ + { 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xcb (raw 0x4b) */ + { 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 }, +/* char 0xcc (raw 0x4c) */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0xcd (raw 0x4d) */ + { 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xce (raw 0x4e) */ + { 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 }, +/* char 0xcf (raw 0x4f) */ + { 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xd0 (raw 0x50) */ + { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xd1 (raw 0x51) */ + { 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 }, +/* char 0xd2 (raw 0x52) */ + { 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 }, +/* char 0xd3 (raw 0x53) */ + { 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xd4 (raw 0x54) */ + { 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0xd5 (raw 0x55) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xd6 (raw 0x56) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0xd7 (raw 0x57) */ + { 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 }, +/* char 0xd8 (raw 0x58) */ + { 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 }, +/* char 0xd9 (raw 0x59) */ + { 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0xda (raw 0x5a) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0xdb (raw 0x5b) */ + { 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 }, +/* char 0xdc (raw 0x5c) */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 }, +/* char 0xdd (raw 0x5d) */ + { 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 }, +/* char 0xde (raw 0x5e) */ + { 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 }, +/* char 0xdf (raw 0x5f) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0xe0 (raw 0x60) */ + { 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xe1 (raw 0x61) */ + { 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00 }, +/* char 0xe2 (raw 0x62) */ + { 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xe3 (raw 0x63) */ + { 0x00, 0x00, 0x3c, 0x40, 0x40, 0x40, 0x3c, 0x00 }, +/* char 0xe4 (raw 0x64) */ + { 0x04, 0x04, 0x3c, 0x44, 0x44, 0x44, 0x3c, 0x00 }, +/* char 0xe5 (raw 0x65) */ + { 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00 }, +/* char 0xe6 (raw 0x66) */ + { 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00 }, +/* char 0xe7 (raw 0x67) */ + { 0x00, 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x38 }, +/* char 0xe8 (raw 0x68) */ + { 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xe9 (raw 0x69) */ + { 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xea (raw 0x6a) */ + { 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x30 }, +/* char 0xeb (raw 0x6b) */ + { 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00 }, +/* char 0xec (raw 0x6c) */ + { 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xed (raw 0x6d) */ + { 0x00, 0x00, 0x6c, 0x54, 0x54, 0x54, 0x44, 0x00 }, +/* char 0xee (raw 0x6e) */ + { 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xef (raw 0x6f) */ + { 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xf0 (raw 0x70) */ + { 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40 }, +/* char 0xf1 (raw 0x71) */ + { 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x04 }, +/* char 0xf2 (raw 0x72) */ + { 0x00, 0x00, 0x5c, 0x60, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xf3 (raw 0x73) */ + { 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00 }, +/* char 0xf4 (raw 0x74) */ + { 0x20, 0x20, 0x78, 0x20, 0x20, 0x24, 0x18, 0x00 }, +/* char 0xf5 (raw 0x75) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00 }, +/* char 0xf6 (raw 0x76) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0xf7 (raw 0x77) */ + { 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x00 }, +/* char 0xf8 (raw 0x78) */ + { 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 }, +/* char 0xf9 (raw 0x79) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38 }, +/* char 0xfa (raw 0x7a) */ + { 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00 }, +/* char 0xfb (raw 0x7b) */ + { 0x1c, 0x30, 0x30, 0x60, 0x30, 0x30, 0x1c, 0x00 }, +/* char 0xfc (raw 0x7c) */ + { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, +/* char 0xfd (raw 0x7d) */ + { 0x70, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x70, 0x00 }, +/* char 0xfe (raw 0x7e) */ + { 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xff (raw 0x7f) */ + { 0x00, 0x54, 0x28, 0x54, 0x28, 0x54, 0x00, 0x00 }, diff --git a/gsplus/src/kegswin.sln b/gsplus/src/kegswin.sln new file mode 100644 index 0000000..bb2bea4 --- /dev/null +++ b/gsplus/src/kegswin.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kegswin", "kegswin.vcxproj", "{C24D318A-501E-4B8C-901A-15977CB9C00C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.ActiveCfg = Release|x64 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.Build.0 = Release|x64 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.ActiveCfg = Debug|Win32 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.Build.0 = Debug|Win32 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.ActiveCfg = Release|x64 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.Build.0 = Release|x64 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.ActiveCfg = Release|Win32 + {C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {32BE13D0-68A7-486D-874C-EA158125775B} + EndGlobalSection +EndGlobal diff --git a/gsplus/src/kegswin.vcxproj b/gsplus/src/kegswin.vcxproj new file mode 100644 index 0000000..66b57b9 --- /dev/null +++ b/gsplus/src/kegswin.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + {C24D318A-501E-4B8C-901A-15977CB9C00C} + Win32Proj + + + + Application + true + v143 + + + Application + false + v143 + + + Application + true + v143 + + + Application + false + v143 + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + + + MachineX86 + true + Windows + + + + + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + MachineX86 + true + Console + true + true + wsock32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies) + + + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;dsound.lib;winmm.lib;odbccp32.lib;%(AdditionalDependencies) + Console + + + true + Level3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gsplus/src/ldvars b/gsplus/src/ldvars new file mode 100644 index 0000000..cb148b0 --- /dev/null +++ b/gsplus/src/ldvars @@ -0,0 +1,2 @@ +OBJECTS = adb.o engine_c.o clock.o config.o debugger.o scc.o scc_socket_driver.o scc_windriver.o scc_unixdriver.o iwm.o joystick_driver.o moremem.o paddles.o mockingboard.o sim65816.o smartport.o doc.o sound.o sound_driver.o woz.o unshk.o undeflate.o dynapro.o dyna_type.o dyna_filt.o dyna_validate.o applesingle.o video.o voc.o +PROJROOT = .. diff --git a/gsplus/src/macsnd_driver.c b/gsplus/src/macsnd_driver.c new file mode 100644 index 0000000..34e134d --- /dev/null +++ b/gsplus/src/macsnd_driver.c @@ -0,0 +1,273 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Headers at: /Applications/Xcode.app/Contents/Developer/Platforms/ +// MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/ +// Frameworks/AudioToolbox.framework/Headers + +// Some ideas from SDL code: +// http://www-personal.umich.edu/~bazald/l/api/_s_d_l__coreaudio_8c_source.html + +#include "defc.h" +#include "sound.h" + +#include +#include +#include + +// Mac OS X 12.0 deprecates kAudioObjectPropertyElementMaster. So now we +// need to use kAudioObjectPropertyElementMain, and set it to ...Master if it +// isn't already set. This is beyond dumb +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +# if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000 +# define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster +# endif +#endif + +#define MACSND_REBUF_SIZE (64*1024) + +word32 g_macsnd_rebuf[MACSND_REBUF_SIZE]; +volatile int g_macsnd_rd_pos; +volatile int g_macsnd_wr_pos; +volatile int g_macsnd_playing = 0; +int g_macsnd_channel_warn = 0; +extern int g_sound_min_samples; // About 33ms +extern int g_sound_max_multiplier; // About 6, so 6*33 ~= 200ms. + +extern int Verbose; + +extern int g_preferred_rate; +extern word32 *g_sound_shm_addr; +extern int g_sound_size; + +int g_call_num = 0; + +AURenderCallbackStruct g_callback_struct = { 0 }; + +static OSStatus +audio_callback(void *in_ref_ptr, AudioUnitRenderActionFlags *aura_flags_ptr, + const AudioTimeStamp *a_timestamp_ptr, UInt32 bus_number, + UInt32 in_num_frame, AudioBufferList *abuflist_ptr) +{ + word32 *wptr; + int num_buffers, num_channels, num_samps, sample_num, samps_avail; + int max_samples, min_samples; + int i, j; + + if(in_ref_ptr || aura_flags_ptr || a_timestamp_ptr || bus_number) { + // Avoid unused parameter warning + } +#if 0 + printf("CB: audio_callback called, in_ref:%p, bus:%d, in_frame:%d\n", + in_ref_ptr, bus_number, in_num_frame); +#endif + num_buffers = abuflist_ptr->mNumberBuffers; + min_samples = g_sound_min_samples; + max_samples = min_samples * g_sound_max_multiplier; +#if 0 + printf("CB: num_buffers: %d. sample_time:%lf, host_time:%016llx " + "rate_scalar:%lf, word_clock_time:%016llx, flags:%04x\n", + num_buffers, a_timestamp_ptr->mSampleTime, + a_timestamp_ptr->mHostTime, a_timestamp_ptr->mRateScalar, + a_timestamp_ptr->mWordClockTime, a_timestamp_ptr->mFlags); +#endif + + sample_num = g_macsnd_rd_pos; + samps_avail = (g_macsnd_wr_pos - sample_num) & (MACSND_REBUF_SIZE - 1); + if((int)in_num_frame > samps_avail) { + // We don't have enough samples, must pause + g_macsnd_playing = 0; + sample_num = g_macsnd_wr_pos; // Eat remaining samps + } + if((g_macsnd_playing == 0) && + (samps_avail > (min_samples + (int)in_num_frame))) { + // We can unpause + g_macsnd_playing = 1; + } + if(g_macsnd_playing && (samps_avail > max_samples)) { + printf("JUMP SAMPLE_NUM by %d samples!\n", max_samples / 2); + sample_num += (max_samples / 2); + sample_num = sample_num & (MACSND_REBUF_SIZE - 1); + } +#if 0 + printf("CB: in_frame:%d, samps_avail:%d, playing:%d\n", in_num_frame, + samps_avail, g_macsnd_playing); +#endif + + for(i = 0; i < num_buffers; i++) { + num_channels = abuflist_ptr->mBuffers[i].mNumberChannels; + if((num_channels != 2) && !g_macsnd_channel_warn) { + printf("mNumberChannels:%d\n", num_channels); + g_macsnd_channel_warn = 1; + } + num_samps = abuflist_ptr->mBuffers[i].mDataByteSize / 4; + wptr = (word32 *)abuflist_ptr->mBuffers[i].mData; +#if 0 + printf("CB buf[%d]: num_ch:%d, num_samps:%d, wptr:%p\n", i, + num_channels, num_samps, wptr); +#endif +#if 0 + if((g_call_num & 31) == 0) { + printf("%d play %d samples, samps_avail:%d\n", + g_call_num, num_samps, samps_avail); + } +#endif + if(g_macsnd_playing) { + for(j = 0; j < num_samps; j++) { + wptr[j] = g_macsnd_rebuf[sample_num]; + sample_num++; + if(sample_num >= MACSND_REBUF_SIZE) { + sample_num = 0; + } + } + } else { + for(j = 0; j < num_samps; j++) { + wptr[j] = 0; + } + } + } + + g_call_num++; + g_macsnd_rd_pos = sample_num; + + return 0; +} + +int +mac_send_audio(byte *ptr, int in_size) +{ + word32 *wptr, *macptr; + int samps, sample_num; + int i; + + samps = in_size / 4; + wptr = (word32 *)ptr; + sample_num = g_macsnd_wr_pos; + macptr = &(g_macsnd_rebuf[0]); + for(i = 0; i < samps; i++) { + macptr[sample_num] = *wptr++; + sample_num++; + if(sample_num >= MACSND_REBUF_SIZE) { + sample_num = 0; + } + } + + g_macsnd_wr_pos = sample_num; + + return in_size; +} + +AudioObjectPropertyAddress g_aopa = { 0 }; + +void +macsnd_init() +{ + AudioComponentInstance ac_inst; + AudioStreamBasicDescription *str_desc_ptr; + AudioComponentDescription *ac_descr_ptr; + AudioComponent ac; + AudioDeviceID device; + OSStatus result; + UInt32 size; + + g_macsnd_rd_pos = 0; + g_macsnd_wr_pos = 0; + mac_printf("macsnd_init called\n"); + + g_aopa.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + g_aopa.mScope = kAudioObjectPropertyScopeGlobal; + g_aopa.mElement = kAudioObjectPropertyElementMain; + + size = 4; + result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &g_aopa, + 0, 0, &size, &device); + if(result != 0) { + printf("AudioObjectGetPropertData on DefaultOutputDevice:%d\n", + result); + return; + } + mac_printf("Audio Device number: %d\n", device); + + str_desc_ptr = calloc(1, sizeof(AudioStreamBasicDescription)); + str_desc_ptr->mFormatID = kAudioFormatLinearPCM; + str_desc_ptr->mFormatFlags = kLinearPCMFormatFlagIsPacked | + kLinearPCMFormatFlagIsSignedInteger; + str_desc_ptr->mChannelsPerFrame = 2; + str_desc_ptr->mSampleRate = g_preferred_rate; + str_desc_ptr->mFramesPerPacket = 1; + str_desc_ptr->mBitsPerChannel = 16; // 16-bit samples + str_desc_ptr->mBytesPerFrame = (16 * 2) / 8; + str_desc_ptr->mBytesPerPacket = str_desc_ptr->mBytesPerFrame; + + ac_descr_ptr = calloc(1, sizeof(AudioComponentDescription)); + ac_descr_ptr->componentType = kAudioUnitType_Output; + ac_descr_ptr->componentManufacturer = kAudioUnitManufacturer_Apple; + ac_descr_ptr->componentSubType = kAudioUnitSubType_DefaultOutput; + ac = AudioComponentFindNext(0, ac_descr_ptr); + mac_printf("AudioComponentFindNext ret: %p\n", ac); + if(ac == 0) { + exit(1); + } + + result = AudioComponentInstanceNew(ac, &ac_inst); + mac_printf("AudioComponentInstanceNew ret:%d\n", result); + if(result != 0) { + exit(1); + } + + result = AudioUnitSetProperty(ac_inst, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, &device, + sizeof(device)); + mac_printf("AudioUnitSetProperty CurrentDevice ret: %d\n", result); + if(result != 0) { + exit(1); + } + + result = AudioUnitSetProperty(ac_inst, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, str_desc_ptr, + sizeof(*str_desc_ptr)); + mac_printf("AudioUnitSetProperty StreamFormat ret: %d\n", result); + if(result != 0) { + exit(1); + } + + g_callback_struct.inputProc = audio_callback; + g_callback_struct.inputProcRefCon = (void *)1; + result = AudioUnitSetProperty(ac_inst, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &g_callback_struct, + sizeof(g_callback_struct)); + + mac_printf("AudioUnitSetProperty SetRenderCallback ret: %d\n", result); + if(result != 0) { + exit(1); + } + + result = AudioUnitInitialize(ac_inst); + mac_printf("AudioUnitInitialize ret: %d\n", result); + if(result != 0) { + exit(1); + } + + result = AudioOutputUnitStart(ac_inst); + mac_printf("AudioOutputUnitStart ret: %d\n", result); + if(result != 0) { + exit(1); + } + + sound_set_audio_rate(g_preferred_rate); + + mac_printf("End of child_sound_init_mac\n"); + fflush(stdout); +} + diff --git a/gsplus/src/mockingboard.c b/gsplus/src/mockingboard.c new file mode 100644 index 0000000..cee0456 --- /dev/null +++ b/gsplus/src/mockingboard.c @@ -0,0 +1,666 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" +#include "sound.h" + +// Mockingboard contains two pairs of a 6522/AY-8913, where the 6522 interfaces +// the AY-8913 (which makes the sounds) to the Apple II. Each AY-8913 +// contains 3 channels of sound: A,B,C. Model each pair separately. +// The AY-8913 has 16 registers. The documentation numbers them using octal! +// The AY8913 is accessed using ORB of the 6522 as control: 0 = Reset, 4=Idle +// 5=Reg read; 6=Write reg; 7=Latch reg address + +extern Mockingboard g_mockingboard; +dword64 g_mockingboard_last_int_dusec = 0ULL; +dword64 g_mockingboard_event_int_dusec = 0ULL; // 0 -> no event pending + +extern int g_irq_pending; +extern double g_dsamps_per_dfcyc; + +void +mock_ay8913_reset(int pair_num, dword64 dfcyc) +{ + Ay8913 *ay8913ptr; + int i; + + if(dfcyc) { + // Avoid unused parameter warning + } + ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913); + ay8913ptr->reg_addr_latch = 16; // out-of-range, mb-audit1.3 + for(i = 0; i < 16; i++) { + ay8913ptr->regs[i] = 0; + } + for(i = 0; i < 3; i++) { + ay8913ptr->toggle_tone[i] = 0; + ay8913ptr->tone_samp[i] = 0; + } + ay8913ptr->noise_val = 0x12345678; + ay8913ptr->noise_samp = 0; + ay8913ptr->env_dsamp = 0; +} + +void +mockingboard_reset(dword64 dfcyc) +{ + word32 timer1_latch; + + timer1_latch = g_mockingboard.pair[0].mos6522.timer1_latch; + memset(&g_mockingboard, 0, sizeof(g_mockingboard)); + + g_mockingboard_last_int_dusec = (dfcyc >> 16) << 16; + if(g_mockingboard_event_int_dusec != 0) { + (void)remove_event_mockingboard(); + } + g_mockingboard_event_int_dusec = 0; + printf("At reset, timer1_latch: %08x\n", timer1_latch); + if((timer1_latch & 0xffff) == 0x234) { // MB-audit + g_mockingboard.pair[0].mos6522.timer1_latch = timer1_latch; + } else { + g_mockingboard.pair[0].mos6522.timer1_latch = 0xff00; + } + g_mockingboard.pair[0].mos6522.timer1_counter = 0x2ff00; + g_mockingboard.pair[0].mos6522.timer2_counter = 0x2ff00; + g_mockingboard.pair[1].mos6522.timer1_latch = 0xff00; + g_mockingboard.pair[1].mos6522.timer1_counter = 0x2ff00; + g_mockingboard.pair[1].mos6522.timer2_counter = 0x2ff00; + + mock_ay8913_reset(0, dfcyc); + mock_ay8913_reset(1, dfcyc); +} + +void +mock_show_pair(int pair_num, dword64 dfcyc, const char *str) +{ + Mos6522 *mos6522ptr; + + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + printf("Mock %d %s, t1_lat:%05x, t1_c:%05x, t2_l:%05x t2_c:%05x, ifr:" + "%02x, acr:%02x, ier:%02x\n", pair_num, str, + mos6522ptr->timer1_latch, mos6522ptr->timer1_counter, + mos6522ptr->timer2_latch, mos6522ptr->timer2_counter, + mos6522ptr->ifr, mos6522ptr->acr, mos6522ptr->ier); + printf(" dfcyc:%016llx, event_int:%lld\n", dfcyc, + g_mockingboard_event_int_dusec); + +} + +// Timers work as follows: if written with '10' in cycle 0, on cycle 1 the +// counters loads with '10', and counts down to 9 on cycle 2...down to 1 +// on cycle 10, then 0 on cycle 11, and then signals an interrupt on cycle 12 +// To handle this, timers are "read value + 1" so that timer==0 means the +// timer should interrupt right now. So to write '10' to a timer, the code +// needs to write 10+2=12 to the variable, so that on the next cycle, it is +// 11, which will be returned as 10 to software reading the reg. + +void +mock_update_timers(int doit, dword64 dfcyc) +{ + Mos6522 *mos6522ptr; + dword64 dusec, ddiff, dleft, timer1_int_dusec, timer2_int_dusec; + dword64 closest_int_dusec, event_int_dusec; + word32 timer_val, ier, timer_eff, timer_latch, log_ifr; + int i; + + dbg_log_info(dfcyc, doit, g_mockingboard.pair[0].mos6522.ifr | + (g_mockingboard.pair[0].mos6522.ier << 8), 0xc0); + + dusec = dfcyc >> 16; + ddiff = dusec - g_mockingboard_last_int_dusec; + if(!doit && (dusec <= g_mockingboard_last_int_dusec)) { + return; // Nothing more to do + } + + // printf("mock_update_timers at %lf %016llx, ddiff:%llx\n", dfcyc, + // dusec, ddiff); + // Update timers by ddiff integer cycles, calculate next event time + g_mockingboard_last_int_dusec = dusec; + closest_int_dusec = 0; + for(i = 0; i < 2; i++) { // pair_num + mos6522ptr = &(g_mockingboard.pair[i].mos6522); + timer1_int_dusec = 0; + timer_val = mos6522ptr->timer1_counter; + ier = mos6522ptr->ier; + timer_eff = (timer_val & 0x1ffff); + dleft = ddiff; + timer_latch = mos6522ptr->timer1_latch + 2; + dbg_log_info(dfcyc, (word32)dleft, timer_val, 0xcb); + dbg_log_info(dfcyc, ier, timer_latch, 0xcb); + if(dleft < timer_eff) { + // Move ahead only a little, no triggering + timer_val = timer_val - (word32)dleft; + if(ddiff) { + // printf("New timer1_val:%05x, dleft:%08llx\n", + // timer_val, dleft); + } + if(((mos6522ptr->ifr & 0x40) == 0) && (ier & 0x40)) { + // IFR not set yet, prepare an event + timer1_int_dusec = dusec + + (timer_val & 0x1ffff); + dbg_log_info(dfcyc, (word32)timer1_int_dusec, + timer_val, 0xcd); + // printf("t1_int_dusec: %016llx\n", + // timer1_int_dusec); + } + } else { + // Timer1 has triggered now (maybe rolled over more + // than once). + log_ifr = 0; + if((timer_val & 0x20000) == 0) { + // Either free-running, or not one-shot already + // triggered + // Set interrupt: Ensure IFR | 0x40 is set + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i, + mos6522ptr->ifr | 0x40, ier); + log_ifr = 1; + } + dleft -= timer_eff; + if(dleft >= timer_latch) { + // It's rolled over several times, remove those + dleft = dleft % timer_latch; + dbg_log_info(dfcyc, (word32)dleft, timer_latch, + 0xcc); + } + if(dleft == 0) { + dleft = timer_latch; + } + timer_val = (timer_latch - dleft) & 0x1ffff; + if((mos6522ptr->acr & 0x40) == 0) { + // ACR6=0: One shot mode, mark it as triggered + timer_val |= 0x20000; + } + if(log_ifr) { + dbg_log_info(dfcyc, + mos6522ptr->ifr | (ier << 8), + timer_val, 0xc3); + } + } + +#if 0 + printf("%016llx ch%d timer1 was %05x, now %05x\n", dfcyc, i, + mos6522ptr->timer1_counter, timer_val); +#endif + + mos6522ptr->timer1_counter = timer_val; + dbg_log_info(dfcyc, timer_val, timer_latch, 0xce); + + // Handle timer2 + timer2_int_dusec = 0; + timer_val = mos6522ptr->timer2_counter; + timer_eff = timer_val & 0x1ffff; + dleft = ddiff; + if(mos6522ptr->acr & 0x20) { + // Count pulses mode. Just don't count + dleft = 0; + } + if(dleft < timer_eff) { + // Move ahead only a little, no triggering + timer_val = timer_val - (word32)dleft; + if(((mos6522ptr->ifr & 0x20) == 0) && (ier & 0x20)) { + // IFR not set yet, prepare an event + timer2_int_dusec = dusec + + (timer_val & 0x1ffff); + //printf("t2_int_dusec: %016llx\n", + // timer1_int_dusec); + } + } else if(timer_val & 0x20000) { + // And already triggered once, just update count + timer_val = ((timer_eff - dleft) & 0xffff) | 0x20000; + } else { + // Has not triggered once yet, but it will now + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i, + mos6522ptr->ifr | 0x20, ier); + timer_val = ((timer_val - dleft) & 0xffff) | 0x20000; + dbg_log_info(dfcyc, mos6522ptr->ifr | (ier << 8), + timer_val, 0xc4); + } + + // printf("ch%d timer2 was %05x, now %05x\n", i, + // mos6522ptr->timer2_counter, timer_val); + + mos6522ptr->timer2_counter = timer_val; + + if(timer1_int_dusec && timer2_int_dusec) { + timer1_int_dusec = MY_MIN(timer1_int_dusec, + timer2_int_dusec); + } + if(timer1_int_dusec) { + if(closest_int_dusec) { + closest_int_dusec = MY_MIN(closest_int_dusec, + timer1_int_dusec); + } else { + closest_int_dusec = timer1_int_dusec; + } + } + } + + event_int_dusec = g_mockingboard_event_int_dusec; + if(closest_int_dusec) { + // See if this is sooner than the current pending event + // printf("closest_int_dusec: %016llx, event_int:%016llx\n", + // closest_int_dusec, event_int_dusec); + doit = 0; + if(event_int_dusec && (closest_int_dusec < event_int_dusec)) { + // There was a pending event. Discard it + // printf("Call remove_event_mockingboard\n"); + remove_event_mockingboard(); + doit = 1; + } + if(!event_int_dusec || doit) { + //printf("Call add_event_mockingboard: %016llx %lld\n", + // closest_int_dusec, closest_int_dusec); + add_event_mockingboard(closest_int_dusec << 16); + g_mockingboard_event_int_dusec = closest_int_dusec; + dbg_log_info(dfcyc, + (word32)(closest_int_dusec - (dfcyc >> 16)), + 0, 0xc1); + } + } +} + +void +mockingboard_event(dword64 dfcyc) +{ + // Received an event--we believe we may need to set an IRQ now. + // Event was already removed from the event queue + // printf("Mockingboard_event received at %016llx\n", dfcyc); + dbg_log_info(dfcyc, 0, 0, 0xc2); + g_mockingboard_event_int_dusec = 0; + mock_update_timers(1, dfcyc); +} + +word32 +mockingboard_read(word32 loc, dword64 dfcyc) +{ + int pair_num; + + // printf("mockingboard read: %04x\n", loc); + pair_num = (loc >> 7) & 1; // 0 or 1 + return mock_6522_read(pair_num, loc & 0xf, dfcyc); +} + +void +mockingboard_write(word32 loc, word32 val, dword64 dfcyc) +{ + int pair_num; + + // printf("mockingboard write: %04x=%02x\n", loc, val); + pair_num = (loc >> 7) & 1; // 0 or 1 + mock_6522_write(pair_num, loc & 0xf, val, dfcyc); +} + +word32 +mock_6522_read(int pair_num, word32 loc, dword64 dfcyc) +{ + Mos6522 *mos6522ptr; + word32 val; + + // Read from 6522 #pair_num loc (0-15) + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + val = 0; + switch(loc) { + case 0x0: // ORB/IRB + // Connected to AY8913 { RESET, BDIR, BC1 } + val = mos6522ptr->orb; + // There are no outputs from AY8913 to the 6522 B Port + break; + case 0x1: // ORA + case 0xf: // ORA, no handshake + val = mos6522ptr->ora; + break; + case 0x2: // DDRB + val = mos6522ptr->ddrb; + break; + case 0x3: // DDRA + val = mos6522ptr->ddra; + break; + case 0x4: // T1C-L (timer[0]) + mock_update_timers(1, dfcyc); + val = (mos6522ptr->timer1_counter - 1) & 0xff; + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~0x40), mos6522ptr->ier); + // Clear Bit 6 + mock_update_timers(1, dfcyc); // Prepare another int (maybe) + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc5); + break; + case 0x5: // T1C-H + mock_update_timers(1, dfcyc); + val = ((mos6522ptr->timer1_counter - 1) >> 8) & 0xff; + break; + case 0x6: // T1L-L + val = mos6522ptr->timer1_latch & 0xff; + break; + case 0x7: // T1L-H + val = (mos6522ptr->timer1_latch >> 8) & 0xff; + break; + case 0x8: // T2C-L + mock_update_timers(1, dfcyc); + val = (mos6522ptr->timer2_counter - 1) & 0xff; + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~0x20), mos6522ptr->ier); + // Clear Bit 5 + mock_update_timers(1, dfcyc); // Prepare another int (maybe) + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc6); + break; + case 0x9: // T2C-H + mock_update_timers(1, dfcyc); + val = ((mos6522ptr->timer2_counter - 1) >> 8) & 0xff; + break; + case 0xa: // SR + val = mos6522ptr->sr; + //halt_printf("Reading SR %d %02x\n", pair_num, val); + break; + case 0xb: // ACR + val = mos6522ptr->acr; + break; + case 0xc: // PCR + val = mos6522ptr->pcr; + break; + case 0xd: // IFR + mock_update_timers(1, dfcyc); + val = mos6522ptr->ifr; + break; + case 0xe: // IER + val = mos6522ptr->ier | 0x80; // Despite MOS6522 + break; // datasheet, bit 7 = 1 + } + // printf("6522 %d loc:%x ret:%02x\n", pair_num, loc, val); + return val; +} + +void +mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc) +{ + Mos6522 *mos6522ptr; + word32 ora, orb, new_val, mask; + + // Write to 6522 #num6522 loc (0-15) + + // printf("6522 %d loc:%x write:%02x\n", pair_num, loc, val); + + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + switch(loc) { + case 0x0: // ORB + mask = mos6522ptr->ddrb; + orb = mos6522ptr->orb; + new_val = (val & mask) | (orb & (~mask)); + if(orb != new_val) { + mock_ay8913_control_update(pair_num, new_val, orb, + dfcyc); + } + mos6522ptr->orb = new_val; + break; + case 0x1: // ORA + case 0xf: // ORA, no handshake + mask = mos6522ptr->ddra; + ora = mos6522ptr->ora; + new_val = (val & mask) | (ora & (~mask)); + mos6522ptr->ora = new_val; + break; + case 0x2: // DDRB + orb = mos6522ptr->orb; + new_val = (orb & val) | (orb & (~val)); + if(orb != new_val) { + mock_ay8913_control_update(pair_num, new_val, orb, + dfcyc); + } + mos6522ptr->orb = new_val; + mos6522ptr->ddrb = val; + return; + case 0x3: // DDRA + ora = mos6522ptr->ora; + mos6522ptr->ora = (ora & val) | (ora & (~val)); + mos6522ptr->ddra = val; + return; + case 0x4: // T1C-L + mock_update_timers(0, dfcyc); + mos6522ptr->timer1_latch = + (mos6522ptr->timer1_latch & 0x1ff00) | val; + // printf("Set T1C-L, timer1_latch=%05x\n", + // mos6522ptr->timer1_latch); + break; + case 0x5: // T1C-H + mock_update_timers(1, dfcyc); + val = (mos6522ptr->timer1_latch & 0xff) | (val << 8); + mos6522ptr->timer1_latch = val; + mos6522ptr->timer1_counter = val + 2; + // The actual timer1_counter update happens next cycle, + // so we want val+1, plus another 1 + // printf("Set T1C-H, timer1_latch=%05x\n", + // mos6522ptr->timer1_latch); + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~0x40), mos6522ptr->ier); + // Clear Bit 6 + mock_update_timers(1, dfcyc); + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc7); + break; + case 0x6: // T1L-L + mock_update_timers(0, dfcyc); + mos6522ptr->timer1_latch = + (mos6522ptr->timer1_latch & 0x1ff00) | val; + break; + case 0x7: // T1L-H + mock_update_timers(1, dfcyc); + val = (mos6522ptr->timer1_latch & 0xff) | (val << 8); + mos6522ptr->timer1_latch = val; + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~0x40), mos6522ptr->ier); + // Clear Bit 6 + mock_update_timers(1, dfcyc); + // mock_show_pair(pair_num, dfcyc, "Wrote T1L-H"); + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc8); + break; + case 0x8: // T2C-L + mos6522ptr->timer2_latch = (mos6522ptr->timer2_latch & 0xff00) | + val; + break; + case 0x9: // T2C-H + mock_update_timers(1, dfcyc); + val = (mos6522ptr->timer2_latch & 0xff) | (val << 8); + mos6522ptr->timer2_latch = val; + mos6522ptr->timer2_counter = val + 2; + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~0x20), mos6522ptr->ier); + // Clear bit 5 + mock_update_timers(1, dfcyc); + break; + case 0xa: // SR + mos6522ptr->sr = val; + halt_printf("Wrote SR reg: %d %02x\n", pair_num, val); + break; + case 0xb: // ACR + mock_update_timers(0, dfcyc); + mos6522ptr->acr = val; + mock_update_timers(1, dfcyc); + break; + case 0xc: // PCR + mos6522ptr->pcr = val; + break; + case 0xd: // IFR + mock_update_timers(1, dfcyc); + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr & (~val), mos6522ptr->ier); + mock_update_timers(1, dfcyc); + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc9); + break; + case 0xe: // IER + // Recalculate effective IFR with new IER + mock_update_timers(1, dfcyc); + if(val & 0x80) { // Set EIR bits + val = mos6522ptr->ier | val; + } else { // Clear EIR bits + val = mos6522ptr->ier & (~val); + } + val = val & 0x7f; + mos6522ptr->ier = val; + mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num, + mos6522ptr->ifr, val); + mock_update_timers(1, dfcyc); + // mock_show_pair(pair_num, dfcyc, "Wrote IER"); + dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xca); + break; + } +} + +word32 +mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier) +{ + word32 irq_mask; + + // Determine if there are any interrupts pending now + irq_mask = IRQ_PENDING_MOCKINGBOARDA << pair_num; + if((ifr & ier & 0x7f) == 0) { + // No IRQ pending anymore + ifr = ifr & 0x7f; // Clear bit 7 + if(g_irq_pending & irq_mask) { + // printf("MOCK INT OFF\n"); + } + remove_irq(irq_mask); + dbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf); + } else { + // IRQ is pending + ifr = ifr | 0x80; // Set bit 7 + if(!(g_irq_pending & irq_mask)) { + // printf("MOCK INT ON\n"); + } + add_irq(irq_mask); + dbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf); + } + return ifr; +} + +void +mock_ay8913_reg_read(int pair_num) +{ + Mos6522 *mos6522ptr; + Ay8913 *ay8913ptr; + word32 reg_addr_latch, mask, val, ora; + + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913); + reg_addr_latch = ay8913ptr->reg_addr_latch; + val = 0; + if(reg_addr_latch < 16) { + val = ay8913ptr->regs[reg_addr_latch]; + } + // ORA at 6522 is merge of ORA using DDRA + mask = mos6522ptr->ddra; + ora = mos6522ptr->ora; + mos6522ptr->ora = (ora & mask) | (val & (~mask)); +} + +word32 g_mock_channel_regs[3] = { + 0x39c3, // channel A: regs 0,1,6,7,8,11,12,13 + 0x3acc, // channel B: regs 2,3,6,7,9,11,12,13 + 0x3cf0 // channel C: regs 4,5,6,7,10,11,12,13 +}; + +void +mock_ay8913_reg_write(int pair_num, dword64 dfcyc) +{ + Mos6522 *mos6522ptr; + Ay8913 *ay8913ptr; + double dsamps; + word32 reg_addr_latch, ora, mask, rmask, do_print; + int i; + + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913); + reg_addr_latch = ay8913ptr->reg_addr_latch; + ora = mos6522ptr->ora; + dsamps = dfcyc * g_dsamps_per_dfcyc; + if(reg_addr_latch < 16) { + mask = (g_mockingboard.disable_mask >> (3*pair_num)) & 7; + rmask = 0; + do_print = 0; + for(i = 0; i < 3; i++) { + if(((mask >> i) & 1) == 0) { + rmask |= g_mock_channel_regs[i]; + } + } + do_print = (rmask >> reg_addr_latch) & 1; + if((ora != ay8913ptr->regs[reg_addr_latch]) || + (reg_addr_latch == 13)) { + // New value, or writing to Envelope control + do_print = 0; + if(do_print) { + printf("%.2lf %016llx mock pair%d reg[%d]=" + "%02x. [2,3]=%02x_%02x [67]=%02x,%02x, " + "[9]=%02x, [12,11]=%02x_%02x [13]=" + "%02x\n", dsamps, dfcyc, pair_num, + reg_addr_latch, ora, + ay8913ptr->regs[3], ay8913ptr->regs[2], + ay8913ptr->regs[6], ay8913ptr->regs[7], + ay8913ptr->regs[9], ay8913ptr->regs[12], + ay8913ptr->regs[11], + ay8913ptr->regs[13]); + } + sound_play(dfcyc); + } + ay8913ptr->regs[reg_addr_latch] = ora; + if(reg_addr_latch == 13) { // Envelope control + ay8913ptr->env_dsamp &= 0x1fffffffffffULL; + // Clear "hold" in (env_val & (0x80 << 40)) + } + } +} + +void +mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val, + dword64 dfcyc) +{ + Mos6522 *mos6522ptr; + Ay8913 *ay8913ptr; + + mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522); + ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913); + // printf("ay8913 %d control now:%02x\n", pair_num, new_val); + + // new_val and prev_val are { reset_l, BDIR, BC1 } + // 4=Idle; 5=Read; 6=Write; 7=Latch_addr + // Latch new address and write data at the time the ctl changes to Idle + // Do read as soon as the ctl indicates to do a read. + + if((new_val & 4) == 0) { + mock_ay8913_reset(pair_num, dfcyc); + return; + } + new_val = new_val & 7; + prev_val = prev_val & 7; + if(prev_val == 7) { // Latch new address, latch it now + ay8913ptr->reg_addr_latch = mos6522ptr->ora; + } else if(prev_val == 6) { // Write data, do it now + mock_ay8913_reg_write(pair_num, dfcyc); + } + if(new_val == 5) { + mock_ay8913_reg_read(pair_num); + } +} + +void +mockingboard_show(int got_num, word32 disable_mask) +{ + int i, j; + + if(got_num) { + g_mockingboard.disable_mask = disable_mask; + } + printf("g_mockingboard.disable_mask:%02x\n", + g_mockingboard.disable_mask); + for(i = 0; i < 2; i++) { + for(j = 0; j < 14; j++) { + printf("Mockingboard pair[%d].reg[%d]=%02x\n", i, j, + g_mockingboard.pair[i].ay8913.regs[j]); + } + } +} + diff --git a/gsplus/src/moremem.c b/gsplus/src/moremem.c new file mode 100644 index 0000000..a8a2efa --- /dev/null +++ b/gsplus/src/moremem.c @@ -0,0 +1,2275 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +extern char g_kegs_version_str[]; + +extern byte *g_memory_ptr; +extern byte *g_dummy_memory1_ptr; +extern byte *g_slow_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; + +extern word32 g_slow_mem_changed[]; + +extern int g_num_breakpoints; +extern Break_point g_break_pts[]; + +extern Page_info page_info_rd_wr[]; + +extern int Verbose; +extern int g_rom_version; +extern int g_user_page2_shadow; +extern Iwm g_iwm; +extern int g_halt_sim; +extern int g_config_control_panel; + + +/* from iwm.c */ +int g_num_shadow_all_banks = 0; + +#define IOR(val) ( (val) ? 0x80 : 0x00 ) + + +extern int g_cur_a2_stat; + +int g_em_emubyte_cnt = 0; +int g_paddle_buttons = 0; +int g_irq_pending = 0; + +word32 g_c023_val = 0; +word32 g_c029_val_some = 0x41; +word32 g_c02b_val = 0x08; +word32 g_c02d_int_crom = 0; +word32 g_c033_data = 0; +word32 g_c034_val = 0; +word32 g_c035_shadow_reg = 0x08; // A bit set inhibits that shadowing + // [6]=Inhibit I/O and LC, [4]=Inhibit Aux hires, [3]=Inh SHR + // [2]=Inh hires pg 2, [1]=Inh hires pg 1, [0]=Inh text pages +word32 g_c036_val_speed = 0x80; // [7]=Fast, [4]=Shadow in all banks, + // [3:0]=slot 7..4 disk motor detect +word32 g_c03ef_doc_ptr = 0; +word32 g_c041_val = 0; /* C041_EN_25SEC_INTS, C041_EN_MOVE_INTS */ +word32 g_c046_val = 0; +word32 g_c05x_annuncs = 0; +word32 g_c068_statereg = 0; // [7]=ALTZP, [6]=PAGE2, [5]=RAMRD, [4]=RAMWRT + // [3]=RDROM, [2]=LCBANK2, [1]=ROMBANK, [0]=INTCXROM + // KEGS special: [8]=PREWRITE_LC, [9]=WRDEFRAM, + // [10]=INTC8ROM +word32 g_c06d_val = 0; +word32 g_c06f_val = 0; +word32 g_zipgs_unlock = 0; +word32 g_zipgs_reg_c059 = 0x5f; + // 7=LC cache dis, 6==5ms paddle del en, 5==5ms ext del en, + // 4==5ms c02e enab, 3==CPS follow enab, 2-0: 111 +word32 g_zipgs_reg_c05a = 0x0f; + // 7:4 = current ZIP speed, 0=100%, 1=93.75%, F=6.25% + // 3:0: always 1111 +word32 g_zipgs_reg_c05b = 0x40; + // 7==1ms clock, 6==cshupd: tag data at c05f updated + // 5==LC cache disable, 4==bd is disabled, 3==delay in effect, + // 2==rombank, 1-0==ram size (00:8K, 01=16K, 10=32K, 11=64K) +word32 g_zipgs_reg_c05c = 0x00; + // 7:1==slot delay enable (for 52-54ms), 0==speaker 5ms delay + +word32 g_c06c_latched_cyc = 0; + +#define SLINKY_RAM_SIZE 0x100000 /* 1MB */ +word32 g_slinky_addr = 0; +byte g_slinky_ram[SLINKY_RAM_SIZE]; + + +#define EMUSTATE(a) { #a, &a } + +Emustate_intlist g_emustate_intlist[] = { + // EMUSTATE(g_cur_a2_stat), + // EMUSTATE(g_paddle_buttons), + + // EMUSTATE(g_em_emubyte_cnt), + // EMUSTATE(g_irq_pending), + EMUSTATE(g_c023_val), + EMUSTATE(g_c029_val_some), + EMUSTATE(g_c02b_val), + EMUSTATE(g_c02d_int_crom), + EMUSTATE(g_c033_data), + EMUSTATE(g_c034_val), + EMUSTATE(g_c035_shadow_reg), + EMUSTATE(g_c036_val_speed), + EMUSTATE(g_c041_val), + EMUSTATE(g_c046_val), + EMUSTATE(g_c05x_annuncs), + EMUSTATE(g_c068_statereg), + EMUSTATE(g_zipgs_unlock), + EMUSTATE(g_zipgs_reg_c059), + EMUSTATE(g_zipgs_reg_c05a), + EMUSTATE(g_zipgs_reg_c05b), + EMUSTATE(g_zipgs_reg_c05c), + { 0, 0, } +}; + +extern dword64 g_paddle_trig_dfcyc; +extern dword64 g_last_vbl_dfcyc; + +Emustate_dword64list g_emustate_dword64list[] = { + EMUSTATE(g_paddle_trig_dfcyc), + EMUSTATE(g_last_vbl_dfcyc), + { 0, 0, } +}; + +extern word32 g_mem_size_total; + +Emustate_word32list g_emustate_word32list[] = { + EMUSTATE(g_mem_size_total), + EMUSTATE(g_c03ef_doc_ptr), + { 0, 0, } +}; + + +#define UNIMPL_READ \ + halt_printf("UNIMP READ to addr %08x\n", loc); \ + return 0; + +#define UNIMPL_WRITE \ + halt_printf("UNIMP WRITE to addr %08x, val: %04x\n", loc, val); \ + return; + +void +fixup_brks() +{ + word32 page, tmp, tmp2, end_page; + Pg_info val; + int is_wr_only, num; + int i; + + num = g_num_breakpoints; + for(i = 0; i < num; i++) { + page = (g_break_pts[i].start_addr >> 8) & 0xffff; + end_page = (g_break_pts[i].end_addr >> 8) & 0xffff; + is_wr_only = (g_break_pts[i].start_addr >> 24) & 1; + while(page <= end_page) { + if(!is_wr_only) { + val = GET_PAGE_INFO_RD(page); + tmp = PTR2WORD(val) & 0xff; + tmp2 = tmp | BANK_IO_TMP | BANK_BREAK; + SET_PAGE_INFO_RD(page, val - tmp + tmp2); + } + val = GET_PAGE_INFO_WR(page); + tmp = PTR2WORD(val) & 0xff; + tmp2 = tmp | BANK_IO_TMP | BANK_BREAK; + SET_PAGE_INFO_WR(page, val - tmp + tmp2); + page++; + } + } +} + +void +fixup_hires_on() +{ + if((g_cur_a2_stat & ALL_STAT_ST80) == 0) { + return; + } + + fixup_bank0_2000_4000(); + fixup_brks(); +} + +void +fixup_bank0_2000_4000() +{ + byte *mem0rd; + byte *mem0wr; + word32 start_page; + int i; + + // Do banks $00 and $E0 + + for(i = 0; i < 2; i++) { + mem0rd = &(g_memory_ptr[0x2000]); + start_page = 0x20; + if(i) { + start_page += 0xe000; // Bank $E0 + mem0rd = &(g_slow_memory_ptr[0x2000]); + } + mem0wr = mem0rd; + if((g_cur_a2_stat & ALL_STAT_ST80) && + (g_cur_a2_stat & ALL_STAT_HIRES)) { + if(g_cur_a2_stat & ALL_STAT_PAGE2) { + mem0rd += 0x10000; + mem0wr += 0x10000; + if(((g_c035_shadow_reg & 0x12) == 0) || + ((g_c035_shadow_reg & 0x8) == 0) || i) { + mem0wr += BANK_SHADOW2; + } + } else if(((g_c035_shadow_reg & 0x02) == 0) || i) { + mem0wr += BANK_SHADOW; + } + } else { + if(RAMRD) { + mem0rd += 0x10000; + } + if(RAMWRT) { + mem0wr += 0x10000; + if(((g_c035_shadow_reg & 0x12) == 0) || + ((g_c035_shadow_reg & 0x8) == 0) || i) { + mem0wr += BANK_SHADOW2; + } + } else if(((g_c035_shadow_reg & 0x02) == 0) || i) { + mem0wr += BANK_SHADOW; + } + } + fixup_any_bank_any_page(start_page, 0x20, mem0rd, mem0wr); + } +} + +void +fixup_bank0_0400_0800() +{ + byte *mem0rd; + byte *mem0wr; + word32 start_page, shadow; + int i; + + for(i = 0; i < 2; i++) { + mem0rd = &(g_memory_ptr[0x400]); + start_page = 4; + if(i) { + start_page += 0xe000; // Bank $E0 + mem0rd = &(g_slow_memory_ptr[0x400]); + } + mem0wr = mem0rd; + shadow = BANK_SHADOW; + if(g_cur_a2_stat & ALL_STAT_ST80) { + if(g_cur_a2_stat & ALL_STAT_PAGE2) { + shadow = BANK_SHADOW2; + mem0rd += 0x10000; + mem0wr += 0x10000; + } + } else { + if(RAMWRT) { + shadow = BANK_SHADOW2; + mem0wr += 0x10000; + } + if(RAMRD) { + mem0rd += 0x10000; + } + } + if(((g_c035_shadow_reg & 0x01) == 0) || i) { + mem0wr += shadow; + } + + fixup_any_bank_any_page(start_page, 4, mem0rd, mem0wr); + } +} + +void +fixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd, + byte *mem0wr) +{ + int i; + + for(i = 0; i < num_pages; i++) { + SET_PAGE_INFO_RD(i + start_page, mem0rd); + mem0rd += 0x100; + } + + for(i = 0; i < num_pages; i++) { + SET_PAGE_INFO_WR(i + start_page, mem0wr); + mem0wr += 0x100; + } + +} + +void +fixup_intcx() +{ + byte *rom10000; + byte *rom_inc; + word32 intcx, mask; + int no_io_shadow, off, start_k; + int j, k; + + rom10000 = &(g_rom_fc_ff_ptr[0x30000]); + + no_io_shadow = (g_c035_shadow_reg & 0x40); + + start_k = 0; + if(no_io_shadow) { + /* if not shadowing, banks 0 and 1 are not affected by intcx */ + start_k = 2; + } + + intcx = (g_c068_statereg & 1); // set means use internal rom + // printf("fixup_intcx, intcx:%d, no_io:%d, c02d:%02x\n", intcx, + // no_io_shadow, g_c02d_int_crom); + for(k = start_k; k < 4; k++) { + off = k; + if(k >= 2) { + off += (0xe0 - 2); + } + /* step off through 0x00, 0x01, 0xe0, 0xe1 */ + + off = off << 8; // Now 0x0000, 0x0100, 0xe000, 0xe100 + SET_PAGE_INFO_RD(0xc0 + off, SET_BANK_IO); + + for(j = 0xc1; j < 0xc8; j++) { + mask = 1 << (j & 0xf); + rom_inc = SET_BANK_IO; + if(((g_c02d_int_crom & mask) == 0) || intcx) { + rom_inc = rom10000 + (j << 8); + } else { + // User-slot rom + rom_inc = &(g_rom_cards_ptr[0]) + + ((j - 0xc0) << 8); + if(j == 0xc4) { // Mockingboard + rom_inc = SET_BANK_IO; + } + } + if((j == 0xc3) && !(g_c068_statereg & 0x400)) { + // If INTC8ROM not set, we need to + // watch for reads from C3xx to set it + rom_inc = SET_BANK_IO; + } + SET_PAGE_INFO_RD(j + off, rom_inc); + } + for(j = 0xc8; j < 0xd0; j++) { + /* c800 - cfff */ + if(intcx || (g_c068_statereg & 0x400)) { + // INTCXROM or INTC8ROM is set + rom_inc = rom10000 + (j << 8); + if((j == 0xcf) && (g_c068_statereg & 0x400)) { + rom_inc = SET_BANK_IO; + } + } else { // c800 space not necessarily mapped + rom_inc = SET_BANK_IO; + } + SET_PAGE_INFO_RD(j + off, rom_inc); + } + for(j = 0xc0; j < 0xd0; j++) { + SET_PAGE_INFO_WR(j + off, SET_BANK_IO); + } + } + + fixup_brks(); +} + +void +fixup_st80col(dword64 dfcyc) +{ + int cur_a2_stat; + + cur_a2_stat = g_cur_a2_stat; + + fixup_bank0_0400_0800(); + + if(cur_a2_stat & ALL_STAT_HIRES) { + /* fixup no matter what PAGE2 since PAGE2 and RAMRD/WR */ + /* can work against each other */ + fixup_bank0_2000_4000(); + } + + if(cur_a2_stat & ALL_STAT_PAGE2) { + change_display_mode(dfcyc); + } + + fixup_brks(); +} + +void +fixup_altzp() +{ + byte *mem0rd; + word32 start_page, altzp; + int i; + + altzp = ALTZP; + for(i = 0; i < 2; i++) { + start_page = 0; + mem0rd = &(g_memory_ptr[0]); + if(i) { + start_page = 0xe000; + mem0rd = &(g_slow_memory_ptr[0]); + } + if(altzp) { + mem0rd += 0x10000; + } + fixup_any_bank_any_page(start_page, 2, mem0rd, mem0rd); + } + + /* No need for fixup_brks since called from set_statereg() */ +} + +void +fixup_page2(dword64 dfcyc) +{ + if((g_cur_a2_stat & ALL_STAT_ST80)) { + fixup_bank0_0400_0800(); + if((g_cur_a2_stat & ALL_STAT_HIRES)) { + fixup_bank0_2000_4000(); + } + } else { + change_display_mode(dfcyc); + } +} + +void +fixup_ramrd() +{ + byte *mem0rd; + word32 start_page, cur_a2_stat; + int i, j; + + cur_a2_stat = g_cur_a2_stat; + + if((cur_a2_stat & ALL_STAT_ST80) == 0) { + fixup_bank0_0400_0800(); + } + if( ((cur_a2_stat & ALL_STAT_ST80) == 0) || + ((cur_a2_stat & ALL_STAT_HIRES) == 0) ) { + fixup_bank0_2000_4000(); + } + + for(i = 0; i < 2; i++) { + start_page = 0; + mem0rd = &(g_memory_ptr[0x0000]); + if(i) { + start_page = 0xe000; + mem0rd = &(g_slow_memory_ptr[0]); + } + + if(RAMRD) { + mem0rd += 0x10000; + } + + SET_PAGE_INFO_RD(start_page + 2, mem0rd + 0x200); + SET_PAGE_INFO_RD(start_page + 3, mem0rd + 0x300); + + for(j = 8; j < 0x20; j++) { + SET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100); + } + + for(j = 0x40; j < 0xc0; j++) { + SET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100); + } + } + + /* No need for fixup_brks since only called from set_statereg() */ +} + +void +fixup_ramwrt() +{ + byte *mem0wr; + word32 start_page, cur_a2_stat, shadow, ramwrt; + int i, j; + + cur_a2_stat = g_cur_a2_stat; + + if((cur_a2_stat & ALL_STAT_ST80) == 0) { + fixup_bank0_0400_0800(); + } + if( ((cur_a2_stat & ALL_STAT_ST80) == 0) || + ((cur_a2_stat & ALL_STAT_HIRES) == 0) ) { + fixup_bank0_2000_4000(); + } + + for(i = 0; i < 2; i++) { + start_page = 0; + mem0wr = &(g_memory_ptr[0x0000]); + if(i) { + start_page = 0xe000; + mem0wr = &(g_slow_memory_ptr[0]); + } + + ramwrt = RAMWRT; + if(ramwrt) { + mem0wr += 0x10000; + } + + SET_PAGE_INFO_WR(start_page + 2, mem0wr + 0x200); + SET_PAGE_INFO_WR(start_page + 3, mem0wr + 0x300); + + shadow = BANK_SHADOW; + if(ramwrt) { + shadow = BANK_SHADOW2; + } + if( ((g_c035_shadow_reg & 0x20) != 0) || + ((g_rom_version == 1) && !g_user_page2_shadow)) { + if(!i) { + shadow = 0; + } + } + for(j = 8; j < 0x0c; j++) { + SET_PAGE_INFO_WR(start_page + j, + mem0wr + j*0x100 + shadow); + } + + for(j = 0xc; j < 0x20; j++) { + SET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100); + } + + shadow = 0; + if(ramwrt) { + if(((g_c035_shadow_reg & 0x14) == 0) || + ((g_c035_shadow_reg & 0x08) == 0) || i) { + shadow = BANK_SHADOW2; + } + } else if(((g_c035_shadow_reg & 0x04) == 0) || i) { + shadow = BANK_SHADOW; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(start_page + j, + mem0wr + j*0x100 + shadow); + } + + shadow = 0; + if(ramwrt && (((g_c035_shadow_reg & 0x08) == 0) || i)) { + /* shr shadowing */ + shadow = BANK_SHADOW2; + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(start_page + j, + mem0wr + j*0x100 + shadow); + } + + for(j = 0xa0; j < 0xc0; j++) { + SET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100); + } + } + + /* No need for fixup_brks() since only called from set_statereg() */ +} + +void +fixup_lc() +{ + byte *mem0rd, *mem0wr; + word32 bank, lcbank2, c08x_wrdefram, rdrom; + int k; + + // Fixup memory from 0xd000 - 0xffff in banks 0, 1, $e0, $e1 + for(k = 0; k < 4; k++) { + bank = k; + mem0rd = &(g_memory_ptr[k << 16]); + if(k >= 2) { + bank += (0xe0 - 2); //k==2->bank=$e0, k==3->bank=$e1 + mem0rd = &(g_slow_memory_ptr[(k & 1) << 16]); + } + /* step bank through 0x00, 0x01, 0xe0, 0xe1 */ + + if(((k & 1) == 0) && ALTZP) { + mem0rd += 0x10000; + } + lcbank2 = LCBANK2; + c08x_wrdefram = g_c068_statereg & 0x200; + rdrom = RDROM; + if((k < 2) && (g_c035_shadow_reg & 0x40)) { + lcbank2 = 0; + c08x_wrdefram = 1; + rdrom = 0; + } + mem0wr = mem0rd; + if((k < 2) && !c08x_wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + if((k < 2) && rdrom) { + mem0rd = &(g_rom_fc_ff_ptr[0x30000]); + } + fixup_any_bank_any_page(bank*0x100 + 0xe0, 0x20, + mem0rd + 0xe000, mem0wr + 0xe000); + if(! lcbank2) { // lcbank1, 0xd000 is ram at 0xc000-cfff + if(!rdrom) { + mem0rd -= 0x1000; + } + mem0wr -= 0x1000; + } + fixup_any_bank_any_page(bank*0x100 + 0xd0, 0x10, + mem0rd + 0xd000, mem0wr + 0xd000); + } + + /* No need for fixup_brks() since only called from set_statereg(), */ + /* or from other routines which will handle it */ +} + +void +set_statereg(dword64 dfcyc, word32 val) +{ + word32 xor; + + dbg_log_info(dfcyc, val, g_c068_statereg, 0xc068); + + xor = val ^ g_c068_statereg; + g_c068_statereg = val; + if(xor == 0) { + return; + } + + if(xor & 0x28c) { // WRDEFRAM, ALTZP, RDROM, LCBANK2 + fixup_lc(); + if(xor & 0x80) { /* altzp */ + fixup_altzp(); + } + } + if(xor & 0x40) { // page2 + g_cur_a2_stat = (g_cur_a2_stat & ~ALL_STAT_PAGE2) | + (val & ALL_STAT_PAGE2); + fixup_page2(dfcyc); + } + + if(xor & 0x20) { // RAMRD + fixup_ramrd(); + } + + if(xor & 0x10) { // RAMWRT + fixup_ramwrt(); + } + + if(xor & 0x02) { // ROMBANK + halt_printf("Just set rombank = %d\n", ROMB); + } + + if(xor & 0x401) { // INTC8ROM, INTCX + fixup_intcx(); + } + + if(xor) { + fixup_brks(); + } +} + +void +fixup_shadow_txt1() +{ + byte *mem0wr; + int j; + + fixup_bank0_0400_0800(); + + mem0wr = &(g_memory_ptr[0x10000]); + if((g_c035_shadow_reg & 0x01) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 4; j < 8; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_txt2() +{ + byte *mem0wr; + int shadow; + int j; + + /* bank 0 */ + mem0wr = &(g_memory_ptr[0x00000]); + shadow = BANK_SHADOW; + if(RAMWRT) { + mem0wr += 0x10000; + shadow = BANK_SHADOW2; + } + if(((g_c035_shadow_reg & 0x20) == 0) && + ((g_rom_version != 1) || g_user_page2_shadow)) { + mem0wr += shadow; + } + for(j = 8; j < 0xc; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if(((g_c035_shadow_reg & 0x20) == 0) && + ((g_rom_version != 1) || g_user_page2_shadow)) { + mem0wr += BANK_SHADOW2; + } + for(j = 8; j < 0xc; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_hires1() +{ + byte *mem0wr; + int j; + + fixup_bank0_2000_4000(); + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((g_c035_shadow_reg & 0x12) == 0 || (g_c035_shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x20; j < 0x40; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_hires2() +{ + byte *mem0wr; + int j; + + /* bank 0 */ + mem0wr = &(g_memory_ptr[0x00000]); + if(RAMWRT) { + mem0wr += 0x10000; + if((g_c035_shadow_reg & 0x14) == 0 || + (g_c035_shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + } else if((g_c035_shadow_reg & 0x04) == 0) { + mem0wr += BANK_SHADOW; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((g_c035_shadow_reg & 0x14) == 0 || (g_c035_shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_shr() +{ + byte *mem0wr; + int j; + + /* bank 0, only pages 0x60 - 0xa0 */ + mem0wr = &(g_memory_ptr[0x00000]); + if(RAMWRT) { + mem0wr += 0x10000; + if((g_c035_shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1, only pages 0x60 - 0xa0 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((g_c035_shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_iolc() +{ + byte *mem0rd; + int k; + + if(g_c035_shadow_reg & 0x40) { + /* Disable language card area */ + for(k = 0; k < 2; k++) { + mem0rd = &(g_memory_ptr[k << 16]); + fixup_any_bank_any_page((k << 8) + 0xc0, 0x10, + mem0rd + 0xd000, mem0rd + 0xd000); + if(k == 0 && ALTZP) { + mem0rd += 0x10000; + } + fixup_any_bank_any_page((k << 8) + 0xd0, 0x10, + mem0rd + 0xc000, mem0rd + 0xc000); + fixup_any_bank_any_page((k << 8) + 0xe0, 0x20, + mem0rd + 0xe000, mem0rd + 0xe000); + } + } else { + /* 0xc000 area */ + fixup_intcx(); + + // Fix 0xd000-0xffff banks 0, 1, 0xe0, 0xe1 + fixup_lc(); + } +} + +void +update_shadow_reg(dword64 dfcyc, word32 val) +{ + int xor; + + dbg_log_info(dfcyc, val, g_c035_shadow_reg, 0xc035); + + if(g_c035_shadow_reg == val) { + return; + } + + xor = g_c035_shadow_reg ^ val; + g_c035_shadow_reg = val; + + if(xor & 8) { + fixup_shadow_hires1(); + fixup_shadow_hires2(); + fixup_shadow_shr(); + xor = xor & (~0x16); + } + if(xor & 0x10) { + fixup_shadow_hires1(); + fixup_shadow_hires2(); + xor = xor & (~0x6); + } + if(xor & 2) { + fixup_shadow_hires1(); + } + if(xor & 4) { + fixup_shadow_hires2(); + } + if(xor & 1) { + fixup_shadow_txt1(); + } + if((xor & 0x20) && ((g_rom_version != 1) || g_user_page2_shadow)) { + fixup_shadow_txt2(); + } + if(xor & 0x40) { + fixup_shadow_iolc(); + } + if(xor) { + fixup_brks(); + } +} + +void +fixup_shadow_all_banks() +{ + byte *mem0rd; + int shadow; + int num_banks; + int j, k; + + /* Assume Ninja Force Megademo */ + /* only do banks 3 - num_banks by 2, shadowing into e1 */ + + shadow = 0; + if((g_c036_val_speed & 0x10) && ((g_c035_shadow_reg & 0x08) == 0)) { + shadow = BANK_SHADOW2; + } + num_banks = g_mem_size_total >> 16; + for(k = 3; k < num_banks; k += 2) { + mem0rd = &(g_memory_ptr[k*0x10000 + 0x2000]) + shadow; + for(j = 0x20; j < 0xa0; j++) { + SET_PAGE_INFO_WR(k*0x100 + j, mem0rd); + mem0rd += 0x100; + } + } + + fixup_brks(); +} + +void +setup_pageinfo() +{ + byte *mem0rd; + word32 mem_size_pages; + + /* first, set all of memory to point to itself */ + mem_size_pages = g_mem_size_total >> 8; + mem0rd = &(g_memory_ptr[0]); + fixup_any_bank_any_page(0, mem_size_pages, mem0rd, mem0rd); + + /* mark unused memory as BAD_MEM */ + fixup_any_bank_any_page(mem_size_pages, 0xfc00-mem_size_pages, + BANK_BAD_MEM, BANK_BAD_MEM); + + fixup_shadow_all_banks(); + + /* ROM */ + mem0rd = &(g_rom_fc_ff_ptr[0]); + fixup_any_bank_any_page(0xfc00, 0x400, mem0rd, + mem0rd + (BANK_IO_TMP | BANK_IO2_TMP)); + + /* banks e0, e1 */ + mem0rd = &(g_slow_memory_ptr[0]); + fixup_any_bank_any_page(0xe000, 0x200, mem0rd, mem0rd); + // First, did all 128KB + + fixup_any_bank_any_page(0xe004, 0x08, mem0rd + 0x0400, + mem0rd + 0x0400 + BANK_SHADOW); + fixup_any_bank_any_page(0xe020, 0x80, mem0rd + 0x2000, + mem0rd + 0x2000 + BANK_SHADOW); + + mem0rd = &(g_slow_memory_ptr[0x10000]); + fixup_any_bank_any_page(0xe104, 0x08, mem0rd + 0x0400, + mem0rd + 0x0400 + BANK_SHADOW2); + fixup_any_bank_any_page(0xe120, 0x80, mem0rd + 0x2000, + mem0rd + 0x2000 + BANK_SHADOW2); + + fixup_intcx(); /* correct banks 0xe0,0xe1, 0xc000-0xcfff area */ + fixup_lc(); /* correct 0xd000-0xdfff area */ + + fixup_bank0_2000_4000(); + fixup_bank0_0400_0800(); + fixup_altzp(); + fixup_ramrd(); + fixup_ramwrt(); + fixup_shadow_txt1(); + fixup_shadow_txt2(); + fixup_shadow_hires1(); + fixup_shadow_hires2(); + fixup_shadow_shr(); + fixup_shadow_iolc(); + fixup_brks(); +} + +void +show_bankptrs_bank0rdwr() +{ + show_bankptrs(0); + show_bankptrs(1); + show_bankptrs(0xe0); + show_bankptrs(0xe1); + printf("statereg: %02x\n", g_c068_statereg); +} + +void +show_bankptrs(int bnk) +{ + int i; + Pg_info rd, wr; + byte *ptr_rd, *ptr_wr; + + printf("g_memory_ptr: %p, dummy_mem: %p, slow_mem_ptr: %p\n", + g_memory_ptr, g_dummy_memory1_ptr, g_slow_memory_ptr); + printf("g_rom_fc_ff_ptr: %p\n", g_rom_fc_ff_ptr); + + printf("Showing bank_info array for %02x\n", bnk); + for(i = 0; i < 256; i++) { + rd = GET_PAGE_INFO_RD(bnk*0x100 + i); + wr = GET_PAGE_INFO_WR(bnk*0x100 + i); + ptr_rd = (byte *)rd; + ptr_wr = (byte *)wr; + printf("%04x rd: ", bnk*256 + i); + show_addr(ptr_rd); + printf(" wr: "); + show_addr(ptr_wr); + printf("\n"); + } +} + +void +show_addr(byte *ptr) +{ + word32 mem_size; + + mem_size = g_mem_size_total; + if(ptr >= g_memory_ptr && ptr < &g_memory_ptr[mem_size]) { + printf("%p--memory[%06x]", ptr, + (word32)(ptr - g_memory_ptr)); + } else if(ptr >= g_rom_fc_ff_ptr && ptr < &g_rom_fc_ff_ptr[256*1024]) { + printf("%p--rom_fc_ff[%06x]", ptr, + (word32)(ptr - g_rom_fc_ff_ptr)); + } else if(ptr >= g_slow_memory_ptr && ptr<&g_slow_memory_ptr[128*1024]){ + printf("%p--slow_memory[%06x]", ptr, + (word32)(ptr - g_slow_memory_ptr)); + } else if(ptr >=g_dummy_memory1_ptr && ptr < &g_dummy_memory1_ptr[256]){ + printf("%p--dummy_memory[%06x]", ptr, + (word32)(ptr - g_dummy_memory1_ptr)); + } else { + printf("%p--unknown", ptr); + } +} + +word32 +moremem_fix_vector_pull(word32 addr) +{ + // Default vector for BRK will come from 0xfffffe. But if + // I/O shadowing is off, or we're a //e, then get from bank0 + if((g_c035_shadow_reg & 0x40) || (g_rom_version == 0)) { + // I/O shadowing off, or Apple //e: use RAM loc + addr = addr & 0xffff; + } + return addr; +} + +word32 +io_read(word32 loc, dword64 *cyc_ptr) +{ + dword64 dfcyc; + word32 val; + word32 new_lcbank2, new_wrdefram, new_prewrite, new_rdrom, tmp; + int i; + + dfcyc = *cyc_ptr; + +/* IO space */ + switch((loc >> 8) & 0xf) { + case 0: /* 0xc000 - 0xc0ff */ + switch(loc & 0xff) { + /* 0xc000 - 0xc00f */ + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + return(adb_read_c000()); + + /* 0xc010 - 0xc01f */ + case 0x10: /* c010 */ + return(adb_access_c010()); + case 0x11: /* c011 = RDLCBANK2 */ + return IOR(LCBANK2); + case 0x12: /* c012= RDLCRAM */ + return IOR(!RDROM); + case 0x13: /* c013=rdramd */ + return IOR(RAMRD); + case 0x14: /* c014=rdramwrt */ + return IOR(RAMWRT); + case 0x15: /* c015 = RDCXROM = INTCX */ + return IOR(g_c068_statereg & 1); + case 0x16: /* c016: Read ALTZP, 0 = Read Main ZP; 1 = Alt ZP */ + return IOR(ALTZP); + case 0x17: /* c017: rdc3rom */ + return IOR(g_c02d_int_crom & (1 << 3)); + case 0x18: /* c018: rd80c0l */ + return IOR((g_cur_a2_stat & ALL_STAT_ST80)); + case 0x19: /* c019: rdvblbar: MSB set if in VBL */ + tmp = in_vblank(dfcyc); + if(g_rom_version == 0) { // Apple //e + tmp = tmp ^ 1; // 1=not in VBL on //e + } + return IOR(tmp); + case 0x1a: /* c01a: rdtext */ + return IOR(g_cur_a2_stat & ALL_STAT_TEXT); + case 0x1b: /* c01b: rdmix */ + return IOR(g_cur_a2_stat & ALL_STAT_MIX_T_GR); + case 0x1c: /* c01c: rdpage2 */ + return IOR(g_cur_a2_stat & ALL_STAT_PAGE2); + case 0x1d: /* c01d: rdhires */ + return IOR(g_cur_a2_stat & ALL_STAT_HIRES); + case 0x1e: /* c01e: altcharset on? */ + return IOR(g_cur_a2_stat & ALL_STAT_ALTCHARSET); + case 0x1f: /* c01f: rd80vid */ + return IOR(g_cur_a2_stat & ALL_STAT_VID80); + + /* 0xc020 - 0xc02f */ + case 0x20: /* 0xc020 */ + /* Click cassette port */ + return float_bus(dfcyc); + case 0x21: /* 0xc021 */ + /* Not documented, but let's return COLOR_C021 */ + return IOR(g_cur_a2_stat & ALL_STAT_COLOR_C021); + case 0x22: /* 0xc022 */ + return (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff; + case 0x23: /* 0xc023 */ + return g_c023_val; + case 0x24: /* 0xc024 */ + return mouse_read_c024(dfcyc); + case 0x25: /* 0xc025 */ + return adb_read_c025(); + case 0x26: /* 0xc026 */ + return adb_read_c026(); + case 0x27: /* 0xc027 */ + return adb_read_c027(); + case 0x28: /* 0xc028 */ + return 0; + case 0x29: /* 0xc029 */ + return((g_cur_a2_stat & 0xa0) | g_c029_val_some); + case 0x2a: /* 0xc02a */ +#if 0 + printf("Reading c02a...returning 0\n"); +#endif + return 0; + case 0x2b: /* 0xc02b */ + return g_c02b_val; + case 0x2c: /* 0xc02c */ + /* printf("reading c02c, returning 0\n"); */ + if(g_c06d_val == 0x40) { // Test mode $40 + return read_video_data(dfcyc); + } + return 0; + case 0x2d: /* 0xc02d */ + return g_c02d_int_crom; + case 0x2e: /* 0xc02e */ + case 0x2f: /* 0xc02f */ + return read_vid_counters(loc, dfcyc); + + /* 0xc030 - 0xc03f */ + case 0x30: /* 0xc030 */ + /* click speaker */ + return sound_read_c030(dfcyc); + case 0x31: /* 0xc031 */ + /* 3.5" control */ + return g_iwm.state & 0xc0; + case 0x32: /* 0xc032 */ + /* scan int */ + return 0; + case 0x33: /* 0xc033 = CLOCKDATA*/ + return g_c033_data; + case 0x34: /* 0xc034 = CLOCKCTL */ + return g_c034_val; + case 0x35: /* 0xc035 */ + return g_c035_shadow_reg; + case 0x36: /* 0xc036 = CYAREG */ + return g_c036_val_speed; + case 0x37: /* 0xc037 */ + return 0; + case 0x38: /* 0xc038 */ + return scc_read_reg(dfcyc, 1); + case 0x39: /* 0xc039 */ + return scc_read_reg(dfcyc, 0); + case 0x3a: /* 0xc03a */ + return scc_read_data(dfcyc, 1); + case 0x3b: /* 0xc03b */ + return scc_read_data(dfcyc, 0); + case 0x3c: /* 0xc03c */ + /* doc control */ + return doc_read_c03c(); + case 0x3d: /* 0xc03d */ + return doc_read_c03d(dfcyc); + case 0x3e: /* 0xc03e */ + return (g_c03ef_doc_ptr & 0xff); + case 0x3f: /* 0xc03f */ + return (g_c03ef_doc_ptr >> 8); + + /* 0xc040 - 0xc04f */ + case 0x40: /* 0xc040 */ + /* cassette */ + return 0; + case 0x41: /* 0xc041 */ + return g_c041_val; + case 0x45: /* 0xc045 */ + //halt_printf("Mega II mouse read: c045\n"); + return 0; + case 0x46: /* 0xc046 */ + tmp = g_c046_val; + g_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1); + return tmp; + case 0x47: /* 0xc047 */ + remove_irq(IRQ_PENDING_C046_25SEC | + IRQ_PENDING_C046_VBL); + g_c046_val &= 0xe7; /* clear vbl_int, 1/4sec int*/ + return 0; + case 0x42: /* 0xc042 */ + case 0x43: /* 0xc043 */ + return 0; + case 0x4f: /* 0xc04f */ + /* for information on c04f, see: */ + /* www.sheppyware.net/tech/hardware/softswitches.html */ + /* write to $c04f to start. Then read $c04f to get */ + /* emulator ($16=sweet16, $fe=bernie II). */ + /* Then read again to get version: $21 == 2.1 */ + switch(g_em_emubyte_cnt) { + case 1: + g_em_emubyte_cnt = 2; + return 'K'; + case 2: + g_em_emubyte_cnt = 0; + tmp = g_kegs_version_str[0] - '0'; + i = g_kegs_version_str[2] - '0'; + return ((tmp & 0xf) << 4) + (i & 0xf); + default: + g_em_emubyte_cnt = 0; + return 0; + } + case 0x44: /* 0xc044 */ + case 0x48: /* 0xc048 */ + case 0x49: /* 0xc049 */ + case 0x4a: /* 0xc04a */ + case 0x4b: /* 0xc04b */ + case 0x4c: /* 0xc04c */ + case 0x4d: /* 0xc04d */ + case 0x4e: /* 0xc04e */ + UNIMPL_READ; + + /* 0xc050 - 0xc05f */ + case 0x50: /* 0xc050 */ + val = float_bus(dfcyc); + if(g_cur_a2_stat & ALL_STAT_TEXT) { + g_cur_a2_stat &= (~ALL_STAT_TEXT); + change_display_mode(dfcyc); + } + return val; + case 0x51: /* 0xc051 */ + val = float_bus(dfcyc); + if((g_cur_a2_stat & ALL_STAT_TEXT) == 0) { + g_cur_a2_stat |= (ALL_STAT_TEXT); + change_display_mode(dfcyc); + } + return val; + case 0x52: /* 0xc052 */ + val = float_bus(dfcyc); + if(g_cur_a2_stat & ALL_STAT_MIX_T_GR) { + g_cur_a2_stat &= (~ALL_STAT_MIX_T_GR); + change_display_mode(dfcyc); + } + return val; + case 0x53: /* 0xc053 */ + val = float_bus(dfcyc); + if((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) { + g_cur_a2_stat |= (ALL_STAT_MIX_T_GR); + change_display_mode(dfcyc); + } + return val; + case 0x54: /* 0xc054 */ + val = float_bus(dfcyc); + set_statereg(dfcyc, g_c068_statereg & (~0x40)); + return val; + return float_bus(dfcyc); + case 0x55: /* 0xc055 */ + val = float_bus(dfcyc); + set_statereg(dfcyc, g_c068_statereg | 0x40); + return val; + case 0x56: /* 0xc056 */ + val = float_bus(dfcyc); + if(g_cur_a2_stat & ALL_STAT_HIRES) { + g_cur_a2_stat &= (~ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dfcyc); + } + return val; + case 0x57: /* 0xc057 */ + val = float_bus(dfcyc); + if((g_cur_a2_stat & ALL_STAT_HIRES) == 0) { + g_cur_a2_stat |= (ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dfcyc); + } + return val; + case 0x58: /* 0xc058 */ + if(g_zipgs_unlock < 4) { + g_c05x_annuncs &= (~1); + } + return 0; + case 0x59: /* 0xc059 */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c059; + } else { + g_c05x_annuncs |= 1; + } + return 0; + case 0x5a: /* 0xc05a */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c05a; + } else { + g_c05x_annuncs &= (~2); + } + return 0; + case 0x5b: /* 0xc05b */ + if(g_zipgs_unlock >= 4) { + // Bit 7 is 1msclk, clock with 1ms period. + tmp = (dfcyc >> 25) & 1; + return (tmp << 7) + (g_zipgs_reg_c05b & 0x7f); + } else { + g_c05x_annuncs |= 2; + } + return 0; + case 0x5c: /* 0xc05c */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c05c; + } else { + g_c05x_annuncs &= (~4); + } + return 0; + case 0x5d: /* 0xc05d */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05d!\n"); + } else { + g_c05x_annuncs |= 4; + } + return 0; + case 0x5e: /* 0xc05e */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05e!\n"); + } else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) { + g_cur_a2_stat &= (~ALL_STAT_ANNUNC3); + change_display_mode(dfcyc); + } + return 0; + case 0x5f: /* 0xc05f */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05f!\n"); + } else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) { + g_cur_a2_stat |= (ALL_STAT_ANNUNC3); + change_display_mode(dfcyc); + } + return 0; + + + /* 0xc060 - 0xc06f */ + case 0x60: /* 0xc060 */ + return IOR(g_paddle_buttons & 8); + case 0x61: /* 0xc061 */ + return IOR(adb_is_cmd_key_down() || + g_paddle_buttons & 1); + case 0x62: /* 0xc062 */ + return IOR(adb_is_option_key_down() || + g_paddle_buttons & 2); + case 0x63: /* 0xc063 */ + return IOR(g_paddle_buttons & 4); + case 0x64: /* 0xc064 */ + return read_paddles(dfcyc, 0); + case 0x65: /* 0xc065 */ + return read_paddles(dfcyc, 1); + case 0x66: /* 0xc066 */ + return read_paddles(dfcyc, 2); + case 0x67: /* 0xc067 */ + return read_paddles(dfcyc, 3); + case 0x68: /* 0xc068 = STATEREG */ + return g_c068_statereg & 0xff; + case 0x69: /* 0xc069 */ + /* Reserved reg, return 0 */ + return 0; + case 0x6a: /* 0xc06a */ + case 0x6b: /* 0xc06b */ + UNIMPL_READ; + case 0x6c: /* 0xc06c */ + case 0x6d: /* 0xc06d */ + case 0x6e: /* 0xc06e */ + case 0x6f: /* 0xc06f */ + return g_c06c_latched_cyc >> (8 * (loc & 3)) & 0xff; + + /* 0xc070 - 0xc07f */ + case 0x70: /* c070 */ + paddle_trigger(dfcyc); + return float_bus(dfcyc); + case 0x71: /* 0xc071 */ + case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + return g_rom_fc_ff_ptr[3*65536 + 0xc000 + (loc & 0xff)]; + + /* 0xc080 - 0xc08f */ + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + new_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4; + new_rdrom = ((loc << 3) ^ (loc << 2)) & 8; + // new_rdrom is set if loc[0] ^ loc[1] is true + // 8=RDROM, 0=read from LC RAM + new_prewrite = (loc & 1) << 8; // Odd read set PREWRITE + new_wrdefram = g_c068_statereg & 0x200; + if((loc & 1) == 0) { + new_wrdefram = 0; // Any even access clrs + } else { + new_wrdefram |= (g_c068_statereg << 1) & 0x200; + // Odd read also makes Ram wr if PREWR was set + } + set_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) | + new_lcbank2 | new_rdrom | + new_prewrite | new_wrdefram); + // access 0-7: lcbank1, 8-f: lcbank0 + // a0!=a1: set rdrom (c081,c082,c085,c086, etc.) + // a0=1 && rd set prewrite. a0=0, or wr: clear prewrite + // wrdefram is clr if a0=0, set if prewrite and + // old_prewrite are set, otherwise stays same. + // From Apple language card schematics: + // wr_def = (wr_def_ff || (prewr && prewr_ff)) && a0; + // prewr = read && a0 + return float_bus(dfcyc); + /* 0xc090 - 0xc09f */ + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + /* UNIMPL_READ; */ + return 0; + /* 0xc0a0 - 0xc0af */ + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + return 0; + /* UNIMPL_READ; */ + + /* 0xc0b0 - 0xc0bf */ + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return voc_devsel_read(loc, dfcyc); + + /* 0xc0c0 - 0xc0cf */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + // Slot 4 has a Slinky RAM card + return slinky_devsel_read(dfcyc, loc); + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return 0; + /* 0xc0d0 - 0xc0df */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + return 0; + /* 0xc0e0 - 0xc0ef */ + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return read_iwm(loc, dfcyc); + /* 0xc0f0 - 0xc0ff */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return 0; + + default: + printf("loc: %04x bad\n", loc); + UNIMPL_READ; + } + case 1: case 2: case 5: case 6: case 7: + /* c100 - c7ff, (except c3xx, c4xx) */ + return float_bus(dfcyc); + case 3: // c300 + return c3xx_read(dfcyc, loc); + case 4: + return mockingboard_read(loc, dfcyc); + case 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe: + return float_bus(dfcyc); + // UNIMPL_READ; + case 0xf: // $CFxx + if(g_c068_statereg & 0x401) { // INTC8ROM or INTCX + val = g_rom_fc_ff_ptr[0x3cf00 + (loc & 0xff)]; + } else { + val = float_bus(dfcyc); + } + if((loc & 0xfff) == 0xfff) { + if(g_c068_statereg & 0x400) { + set_statereg(dfcyc, g_c068_statereg & (~0x400)); + } + } + return val; + // UNIMPL_READ; + } + + halt_printf("io_read: hit end, loc: %06x\n", loc); + + return 0xff; +} + +void +io_write(word32 loc, word32 val, dword64 *cyc_ptr) +{ + dword64 dfcyc; + word32 new_lcbank2, new_wrdefram, new_rdrom, new_prewrite, tmp; + word32 new_tmp; + int fixup; + + dfcyc = *cyc_ptr; + + val = val & 0xff; + switch((loc >> 8) & 0xf) { + case 0: /* 0xc000 - 0xc0ff */ + switch(loc & 0xff) { + /* 0xc000 - 0xc00f */ + case 0x00: /* 0xc000: CLR80COL */ + if(g_cur_a2_stat & ALL_STAT_ST80) { + g_cur_a2_stat &= (~ALL_STAT_ST80); + fixup_st80col(dfcyc); + } + return; + case 0x01: /* 0xc001: SET80COL, enable 80-column store */ + if((g_cur_a2_stat & ALL_STAT_ST80) == 0) { + g_cur_a2_stat |= (ALL_STAT_ST80); + fixup_st80col(dfcyc); + } + return; + case 0x02: /* 0xc002: Set RDMAINRAM */ + set_statereg(dfcyc, g_c068_statereg & ~0x20); + return; + case 0x03: /* 0xc003: Set RDCARDRAM (alt) */ + set_statereg(dfcyc, g_c068_statereg | 0x20); + return; + case 0x04: /* 0xc004: Set WRMAINRAM */ + set_statereg(dfcyc, g_c068_statereg & ~0x10); + return; + case 0x05: /* 0xc005: Set WRCARDRAM (alt) */ + set_statereg(dfcyc, g_c068_statereg | 0x10); + return; + case 0x06: /* 0xc006 = SETSLOTCXROM, use slot rom c800 */ + set_statereg(dfcyc, g_c068_statereg & ~0x01); + return; + case 0x07: /* 0xc007 = SETINTXROM, use int rom c800 */ + set_statereg(dfcyc, g_c068_statereg | 0x01); + return; + case 0x08: /* 0xc008: SETSTDZP (main ZP/LC) */ + set_statereg(dfcyc, g_c068_statereg & ~0x80); + return; + case 0x09: /* 0xc009: SETALTZP (alt ZP/LC) */ + set_statereg(dfcyc, g_c068_statereg | 0x80); + return; + case 0x0a: /* 0xc00a = SETINTC3ROM, use internal ROM slot 3*/ + tmp = 1 << 3; + if((g_c02d_int_crom & tmp) != 0) { + g_c02d_int_crom &= ~tmp; + fixup_intcx(); + } + return; + case 0x0b: /* 0xc00b = SETSLOTC3ROM, use slot rom slot 3 */ + tmp = 1 << 3; + if((g_c02d_int_crom & tmp) == 0) { + g_c02d_int_crom |= tmp; + fixup_intcx(); + } + return; + case 0x0c: /* 0xc00c = CLR80VID, disable 80 col hardware */ + if(g_cur_a2_stat & ALL_STAT_VID80) { + g_cur_a2_stat &= (~ALL_STAT_VID80); + change_display_mode(dfcyc); + } + return; + case 0x0d: /* 0xc00d: SET80VID, enable 80 col hardware */ + if((g_cur_a2_stat & ALL_STAT_VID80) == 0) { + g_cur_a2_stat |= (ALL_STAT_VID80); + change_display_mode(dfcyc); + } + return; + case 0x0e: /* 0xc00e: CLRALTCHAR */ + if(g_cur_a2_stat & ALL_STAT_ALTCHARSET) { + g_cur_a2_stat &= (~ALL_STAT_ALTCHARSET); + change_display_mode(dfcyc); + } + return; + case 0x0f: /* 0xc00f: SETALTCHAR */ + if((g_cur_a2_stat & ALL_STAT_ALTCHARSET) == 0) { + g_cur_a2_stat |= (ALL_STAT_ALTCHARSET); + change_display_mode(dfcyc); + } + return; + /* 0xc010 - 0xc01f */ + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + adb_access_c010(); + return; + /* 0xc020 - 0xc02f */ + case 0x20: /* 0xc020 */ + /* WRITE CASSETTE?? */ + return; + case 0x21: /* 0xc021 */ + new_tmp = ((val >> 7) & 1) << + (31 - BIT_ALL_STAT_COLOR_C021); + if((g_cur_a2_stat & ALL_STAT_COLOR_C021) != new_tmp) { + g_cur_a2_stat ^= new_tmp; + change_display_mode(dfcyc); + } + return; + case 0x22: /* 0xc022 */ + /* change text color */ + tmp = (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff; + if(val != tmp) { + /* change text/bg color! */ + g_cur_a2_stat &= ~(ALL_STAT_TEXT_COLOR | + ALL_STAT_BG_COLOR); + g_cur_a2_stat += (val << BIT_ALL_STAT_BG_COLOR); + change_display_mode(dfcyc); + } + return; + case 0x23: /* 0xc023 */ + if((val & 0x19) != 0) { + halt_printf("c023 write of %02x!!!\n", val); + } + tmp = (g_c023_val & 0x70) | (val & 0x0f); + if((tmp & 0x22) == 0x22) { + add_irq(IRQ_PENDING_C023_SCAN); + } + if(!(tmp & 2)) { + remove_irq(IRQ_PENDING_C023_SCAN); + } + if((tmp & 0x44) == 0x44) { + add_irq(IRQ_PENDING_C023_1SEC); + } + if(!(tmp & 0x4)) { + remove_irq(IRQ_PENDING_C023_1SEC); + } + + if(g_irq_pending & (IRQ_PENDING_C023_SCAN | + IRQ_PENDING_C023_1SEC)) { + tmp |= 0x80; + } + g_c023_val = tmp; + return; + case 0x24: /* 0xc024 */ + /* Write to mouse reg: Throw it away */ + return; + case 0x26: /* 0xc026 */ + adb_write_c026(val); + return; + case 0x27: /* 0xc027 */ + adb_write_c027(val); + return; + case 0x29: /* 0xc029 */ + g_c029_val_some = val & 0x41; + if((val & 1) == 0) { + halt_printf("c029: %02x\n", val); + } + new_tmp = val & 0xa0; + if(new_tmp != (g_cur_a2_stat & 0xa0)) { + g_cur_a2_stat = (g_cur_a2_stat & (~0xa0)) + + new_tmp; + change_display_mode(dfcyc); + } + return; + case 0x2a: /* 0xc02a */ +#if 0 + printf("Writing c02a with %02x\n", val); +#endif + return; + case 0x2b: /* 0xc02b */ + g_c02b_val = val; + if((val != 0x08) && (val != 0x00)) { + printf("Writing c02b with %02x\n", val); + } + return; + case 0x2d: /* 0xc02d = Slot Reg. Bit set means use slot rom */ + if((val & 0x9) != 0) { + halt_printf("Illegal c02d write: %02x!\n", val); + } + fixup = (val != g_c02d_int_crom); + g_c02d_int_crom = val; + if(fixup) { + vid_printf("Write c02d of %02x\n", val); + fixup_intcx(); + } + return; + case 0x28: /* 0xc028 */ + case 0x2c: /* 0xc02c */ + UNIMPL_WRITE; + case 0x25: /* 0xc025 */ + /* Space Shark writes to c025--ignore */ + case 0x2e: /* 0xc02e */ + case 0x2f: /* 0xc02f */ + /* Modulae writes to this--just ignore them */ + return; + break; + + /* 0xc030 - 0xc03f */ + case 0x30: /* 0xc030 */ +#if 0 + printf("Write speaker?\n"); +#endif + sound_write_c030(dfcyc); + return; + case 0x31: /* 0xc031 */ + tmp = val ^ g_iwm.state; + iwm_flush_cur_disk(); // In case APPLE35SEL changes + g_iwm.state = (g_iwm.state & (~0xc0)) | (val & 0xc0); + if(tmp & IWM_STATE_C031_APPLE35SEL) { + /* apple35_sel changed, maybe speed change */ + engine_recalc_events(); + } + return; + case 0x32: /* 0xc032 */ + tmp = g_c023_val & 0x7f; + if(((val & 0x40) == 0) && (tmp & 0x40)) { + /* clear 1 sec int */ + remove_irq(IRQ_PENDING_C023_1SEC); + tmp &= 0xbf; + g_c023_val = tmp; + } + if(((val & 0x20) == 0) && (tmp & 0x20)) { + /* clear scan line int */ + remove_irq(IRQ_PENDING_C023_SCAN); + g_c023_val = tmp & 0xdf; + check_for_new_scan_int(dfcyc); + } + if(g_irq_pending & (IRQ_PENDING_C023_1SEC | + IRQ_PENDING_C023_SCAN)) { + g_c023_val |= 0x80; + } + if((val & 0x9f) != 0x9f) { + irq_printf("c032: wrote %02x!\n", val); + } + return; + case 0x33: /* 0xc033 = CLOCKDATA*/ + g_c033_data = val; + return; + case 0x34: /* 0xc034 = CLOCKCTL */ + tmp = val ^ g_c034_val; + clock_write_c034(val); + if(tmp & 0xf) { + change_border_color(dfcyc, val & 0xf); + } + return; + case 0x35: /* 0xc035 */ + update_shadow_reg(dfcyc, val); + return; + case 0x36: /* 0xc036 = CYAREG */ + tmp = val ^ g_c036_val_speed; + g_c036_val_speed = (val & ~0x20); /* clr bit 5 */ + if(tmp & 0x80) { + /* to recalculate times since speed changing */ + engine_recalc_events(); + } + if(tmp & 0xf) { + /* slot_motor_detect changed */ + engine_recalc_events(); + } + + if((val & 0x60) != 0) { + /* for ROM 03, 0x40 is the power-on status */ + /* and can be read/write */ + if(((val & 0x60) != 0x40) || + (g_rom_version < 3)) { + g_c036_val_speed &= (~0x60); + halt_printf("c036: %2x\n", val); + } + } + if(tmp & 0x10) { /* shadow in all banks! */ + if(g_num_shadow_all_banks++ == 0) { + printf("Shadowing all banks...This " + "must be the NFC Megademo\n"); + } + fixup_shadow_all_banks(); + } + return; + case 0x37: /* 0xc037 */ + /* just ignore, probably someone writing c036 m=0 */ + return; + case 0x38: /* 0xc038 */ + scc_write_reg(dfcyc, 1, val); + return; + case 0x39: /* 0xc039 */ + scc_write_reg(dfcyc, 0, val); + return; + case 0x3a: /* 0xc03a */ + scc_write_data(dfcyc, 1, val); + return; + case 0x3b: /* 0xc03b */ + scc_write_data(dfcyc, 0, val); + return; + case 0x3c: /* 0xc03c */ + /* doc ctl */ + doc_write_c03c(dfcyc, val); + return; + case 0x3d: /* 0xc03d */ + /* doc data reg */ + doc_write_c03d(dfcyc, val); + return; + case 0x3e: /* 0xc03e */ + g_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff00) + val; + return; + case 0x3f: /* 0xc03f */ + g_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff) + (val << 8); + return; + + /* 0xc040 - 0xc04f */ + case 0x41: /* c041 */ + g_c041_val = val & 0x1f; + if((val & 0xe7) != 0) { + halt_printf("write c041: %02x\n", val); + } + + if(!(val & C041_EN_VBL_INTS)) { + /* no more vbl interrupt */ + remove_irq(IRQ_PENDING_C046_VBL); + } + if(!(val & C041_EN_25SEC_INTS)) { + remove_irq(IRQ_PENDING_C046_25SEC); + } + return; + case 0x46: /* c046 */ + /* ignore writes to c046 */ + return; + case 0x47: /* c047 */ + remove_irq(IRQ_PENDING_C046_VBL | + IRQ_PENDING_C046_25SEC); + g_c046_val &= 0xe7; /* clear vblint, 1/4sec int*/ + return; + case 0x48: /* c048 */ + /* diversitune writes this--ignore it */ + return; + case 0x42: /* c042 */ + case 0x43: /* c043 */ + return; + case 0x4f: /* c04f */ + g_em_emubyte_cnt = 1; + return; + case 0x45: /* c045 */ + return; + case 0x40: /* c040 */ + case 0x44: /* c044 */ + case 0x49: /* c049 */ + case 0x4a: /* c04a */ + case 0x4b: /* c04b */ + case 0x4c: /* c04c */ + case 0x4d: /* c04d */ + case 0x4e: /* c04e */ + UNIMPL_WRITE; + + /* 0xc050 - 0xc05f */ + case 0x50: /* 0xc050 */ + if(g_cur_a2_stat & ALL_STAT_TEXT) { + g_cur_a2_stat &= (~ALL_STAT_TEXT); + change_display_mode(dfcyc); + } + return; + case 0x51: /* 0xc051 */ + if((g_cur_a2_stat & ALL_STAT_TEXT) == 0) { + g_cur_a2_stat |= (ALL_STAT_TEXT); + change_display_mode(dfcyc); + } + return; + case 0x52: /* 0xc052 */ + if(g_cur_a2_stat & ALL_STAT_MIX_T_GR) { + g_cur_a2_stat &= (~ALL_STAT_MIX_T_GR); + change_display_mode(dfcyc); + } + return; + case 0x53: /* 0xc053 */ + if((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) { + g_cur_a2_stat |= (ALL_STAT_MIX_T_GR); + change_display_mode(dfcyc); + } + return; + case 0x54: /* 0xc054 */ + set_statereg(dfcyc, g_c068_statereg & (~0x40)); + return; + case 0x55: /* 0xc055 */ + set_statereg(dfcyc, g_c068_statereg | 0x40); + return; + case 0x56: /* 0xc056 */ + if(g_cur_a2_stat & ALL_STAT_HIRES) { + g_cur_a2_stat &= (~ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dfcyc); + } + return; + case 0x57: /* 0xc057 */ + if((g_cur_a2_stat & ALL_STAT_HIRES) == 0) { + g_cur_a2_stat |= (ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dfcyc); + } + return; + case 0x58: /* 0xc058 */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c059 &= 0x4; /* last reset cold */ + } else { + g_c05x_annuncs &= (~1); + } + return; + case 0x59: /* 0xc059 */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c059 = (val & 0xf8) | + (g_zipgs_reg_c059 & 0x7); + } else { + g_c05x_annuncs |= 1; + } + return; + case 0x5a: /* 0xc05a */ + g_c05x_annuncs &= (~2); + if((val & 0xf0) == 0x50) { + g_zipgs_unlock++; + } else if((val & 0xf0) == 0xa0) { + g_zipgs_unlock = 0; + } else if(g_zipgs_unlock >= 4) { + if((g_zipgs_reg_c05b & 0x10) == 0) { + /* to recalculate times */ + engine_recalc_events(); + } + g_zipgs_reg_c05b |= 0x10; // disable + } + return; + case 0x5b: /* 0xc05b */ + if(g_zipgs_unlock >= 4) { + if((g_zipgs_reg_c05b & 0x10) != 0) { + /* to recalculate times */ + engine_recalc_events(); + } + g_zipgs_reg_c05b &= (~0x10); // enable + } else { + g_c05x_annuncs |= 2; + } + return; + case 0x5c: /* 0xc05c */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c05c = val; + } else { + g_c05x_annuncs &= (~4); + } + return; + case 0x5d: /* 0xc05d */ + if(g_zipgs_unlock >= 4) { + if(((g_zipgs_reg_c05a ^ val) >= 0x10) && + ((g_zipgs_reg_c05b & 0x10) == 0)) { + engine_recalc_events(); + } + g_zipgs_reg_c05a = val | 0xf; + } else { + g_c05x_annuncs |= 4; + } + return; + case 0x5e: /* 0xc05e: SETAN3, clear AN3, double-hires on */ + if(g_zipgs_unlock >= 4) { + /* Zippy writes 0x80 and 0x00 here... */ + } else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) { + g_cur_a2_stat &= (~ALL_STAT_ANNUNC3); + change_display_mode(dfcyc); + } + return; + case 0x5f: /* 0xc05f: CLRAN3, set AN3, double-hires off */ + if(g_zipgs_unlock >= 4) { + halt_printf("Wrote ZipGS $c05f: %02x\n", val); + } else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) { + g_cur_a2_stat |= (ALL_STAT_ANNUNC3); + change_display_mode(dfcyc); + } + return; + + + /* 0xc060 - 0xc06f */ + case 0x60: /* 0xc060 */ + case 0x61: /* 0xc061 */ + case 0x62: /* 0xc062 */ + case 0x63: /* 0xc063 */ + case 0x64: /* 0xc064 */ + case 0x65: /* 0xc065 */ + case 0x66: /* 0xc066 */ + case 0x67: /* 0xc067 */ + /* all the above do nothing--return */ + return; + case 0x68: /* 0xc068 = STATEREG */ + set_statereg(dfcyc, (g_c068_statereg & ~0xff) | val); + return; + case 0x69: /* 0xc069 */ + /* just ignore, someone writing c068 with m=0 */ + return; + case 0x6a: /* 0xc06a */ + case 0x6b: /* 0xc06b */ + UNIMPL_WRITE; + case 0x6c: /* 0xc06c */ + g_c06c_latched_cyc = (word32)(dfcyc >> 16); + return; + case 0x6d: /* 0xc06d */ + // Affect what reads to $C02C can see, only $40 now + if((g_c06f_val & 0x100) == 0) { // Locked + val = 0; + } + g_c06d_val = val; // Test mode + return; + case 0x6e: /* 0xc06e */ + g_c06f_val = 0; + return; + case 0x6f: /* 0xc06f */ + if((g_c06f_val == 0xda) && (val == 0x61)) { + val |= 0x100; // Unlock + } + g_c06f_val = val; + return; + + /* 0xc070 - 0xc07f */ + case 0x70: /* 0xc070 = Trigger paddles */ + paddle_trigger(dfcyc); + return; + case 0x73: /* 0xc073 = multibank ram card bank addr? */ + return; + case 0x71: /* 0xc071 = another multibank ram card enable? */ + case 0x7e: /* 0xc07e */ + case 0x7f: /* 0xc07f */ + return; + case 0x72: /* 0xc072 */ + case 0x74: /* 0xc074 */ + case 0x75: /* 0xc075 */ + case 0x76: /* 0xc076 */ + case 0x77: /* 0xc077 */ + case 0x78: /* 0xc078 */ + case 0x79: /* 0xc079 */ + case 0x7a: /* 0xc07a */ + case 0x7b: /* 0xc07b */ + case 0x7c: /* 0xc07c */ + case 0x7d: /* 0xc07d */ + return; + + /* 0xc080 - 0xc08f */ + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + new_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4; + new_rdrom = ((loc << 3) ^ (loc << 2)) & 8; + // new_rdrom is set if loc0 ^ loc1 is set + // 8=RDROM, 0=read from LC RAM + new_prewrite = 0; // Writes clear PREWRITE + new_wrdefram = g_c068_statereg & 0x200; + if((loc & 1) == 0) { + new_wrdefram = 0; // Any even access clrs + } + set_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) | + new_lcbank2 | new_rdrom | + new_prewrite | new_wrdefram); + return; + + /* 0xc090 - 0xc09f */ + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + UNIMPL_WRITE; + + /* 0xc0a0 - 0xc0af */ + case 0xa0: case 0xa1: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + UNIMPL_WRITE; + case 0xa2: /* Burger Times writes here on error */ + case 0xa8: + /* Kurzweil SMP writes to 0xc0a8, ignore it */ + return; + + /* 0xc0b0 - 0xc0bf */ + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + voc_devsel_write(loc, val, dfcyc); + return; + + /* 0xc0c0 - 0xc0cf */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + // Slot 4 has a Slinky RAM card + slinky_devsel_write(dfcyc, loc, val); + return; + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + UNIMPL_WRITE; + + /* 0xc0d0 - 0xc0df */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + UNIMPL_WRITE; + + /* 0xc0e0 - 0xc0ef */ + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + write_iwm(loc, val, dfcyc); + return; + + /* 0xc0f0 - 0xc0ff */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + UNIMPL_WRITE; + default: + printf("Write loc: %x\n",loc); + exit(-300); + } + break; + case 1: case 2: case 5: case 6: case 7: + /* c1000 - c7ff (but not c3xx,c4xx) */ + if((loc & 0xff) == 0) { // Frogger writes $ff to $Cx00 + return; + } + UNIMPL_WRITE; + case 3: + if(((g_c02d_int_crom & 8) == 0) && + ((g_c068_statereg & 0x400) == 0)) { + // SLOTC3ROM clear, INTC8rom clear: Set INTC8ROM + set_statereg(dfcyc, g_c068_statereg | 0x400); + } + return; + case 4: + if((g_c02d_int_crom & 0x10) && !(g_c068_statereg & 1)) { + // Slot 4 is set to Your Card and not INTCX + mockingboard_write(loc, val, dfcyc); + return; + } + return; + case 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe: + // UNIMPL_WRITE; + return; + case 0xf: + if((loc & 0xfff) == 0xfff) { + /* cfff */ + if(g_c068_statereg & 0x400) { + set_statereg(dfcyc, g_c068_statereg & (~0x400)); + } + return; + } + // UNIMPL_WRITE; + // Wings of Fury writes to $cf00-$cfff when reading a 0x300 + // sector where it wants to load 0x200 to 0xd000-0xd1ff + return; + } + printf("Huh2? Write loc: %x\n", loc); + exit(-290); +} + +word32 +slinky_devsel_read(dword64 dfcyc, word32 loc) +{ + word32 val; + + loc = loc & 0xf; + val = 0; + if(loc <= 2) { + val = (g_slinky_addr >> (8*loc)) & 0xff; + } + if(loc == 3) { + val = g_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)]; + dbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c3); + g_slinky_addr++; + } + return val; +} + +void +slinky_devsel_write(dword64 dfcyc, word32 loc, word32 val) +{ + word32 mask; + + loc = loc & 0xf; + dbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c0 + loc); + if(loc <= 2) { + mask = 0xff << (8 * loc); + val = val * 0x010101; + g_slinky_addr = (g_slinky_addr & (~mask)) | (val & mask); + } + if(loc == 3) { + g_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)] = val; + g_slinky_addr++; + } +} + +word32 +c3xx_read(dword64 dfcyc, word32 loc) +{ + // We may have been marked IO so that we could set INTC8ROM, + // but we still need to return ROM + if(((g_c02d_int_crom & 8) == 0) && ((g_c068_statereg & 0x400) == 0)) { + // SLOTC3ROM is not set: Set INTC8ROM + set_statereg(dfcyc, g_c068_statereg | 0x400); + } + if(((g_c02d_int_crom & 8) == 0) || (g_c068_statereg & 1) || + (g_rom_version == 0)) { + // Access ROM for slot 3 + return g_rom_fc_ff_ptr[0x3c300 + (loc & 0xff)]; + } + return float_bus(dfcyc); +} + +// IIgs vertical line counters +// 0x7d - 0x7f: in vbl, top of screen +// 0x80 - 0xdf: not in vbl, drawing screen +// 0xe0 - 0xff: in vbl, bottom of screen +// Note: lines are then 0-0x60 effectively, for 192 lines, from 0x80-0xdf +// vertical blanking engages on line 192, even if in super hires mode +// (Last 8 lines in SHR are drawn with vbl_active set + +word32 +get_lines_since_vbl(dword64 dfcyc) +{ + double dusecs_since_last_vbl, dlines_since_vbl, dcyc_line_start; + word32 lines_since_vbl; + int offset; + + dusecs_since_last_vbl = (double)((dfcyc >> 16) - + (g_last_vbl_dfcyc >> 16)); + + dlines_since_vbl = dusecs_since_last_vbl * (1.0 / 65.0); + lines_since_vbl = (int)dlines_since_vbl; + dcyc_line_start = (double)lines_since_vbl * 65.0; + + offset = ((int)(dusecs_since_last_vbl - dcyc_line_start)) & 0xff; + + lines_since_vbl = (lines_since_vbl << 8) + offset; + + if(!g_halt_sim && !g_config_control_panel) { + dbg_log_info(dfcyc, (word32)dusecs_since_last_vbl, + lines_since_vbl, 0xc02e); + } + if(lines_since_vbl < 0x10541) { + return lines_since_vbl; + } else { + // We've entered the next frame, but update_60hz() hasn't been + // called yet. Produce the proper c02e/c02f counter values + // for the first line of the display + lines_since_vbl = lines_since_vbl - 0x10600; +#if 0 + printf("lines_since_vbl was:%08x, now:%08x\n", + lines_since_vbl + 0x10600, lines_since_vbl); + halt_printf("lines_since_vbl: %08x!\n", lines_since_vbl); + printf("du_s_l_v: %f, dfcyc: %016llx, last_vbl_cycs: %016llx\n", + dusecs_since_last_vbl, dfcyc, g_last_vbl_dfcyc); + show_dtime_array(); + show_all_events(); + /* U_STACK_TRACE(); */ +#endif + } + + return lines_since_vbl; +} + +int +in_vblank(dword64 dfcyc) +{ + word32 lines_since_vbl; + + lines_since_vbl = get_lines_since_vbl(dfcyc); + + // Testing indicates $c019 is a cycle delayed from $C02F counter + if(lines_since_vbl > 0xc000) { // Exclude line 192 first cycle! + return 1; // 1=in VBL + } + if(lines_since_vbl == 0) { + return 1; // Handle 1-cycle delay in reading c019 + } + + return 0; +} + +// horizontal video counter goes from 0x00,0x40 - 0x7f, then 0x80,0xc0-0xff +// over 2*65 cycles. The last visible screen pos is 0x7f and 0xff +// KEGS starts it's "line 0" at the start of the right border for line -1 +// See get_lines_since_vbl comment for the format of the vertical counter +int +read_vid_counters(int loc, dword64 dfcyc) +{ + word32 mask; + int lines_since_vbl; + + loc = loc & 0xf; + + lines_since_vbl = get_lines_since_vbl(dfcyc); + + lines_since_vbl += 0x10000; + if(lines_since_vbl >= 0x20000) { + lines_since_vbl = lines_since_vbl - 0x20000 + 0xfa00; + } + + if(lines_since_vbl > 0x1ffff) { + halt_printf("lines_since_vbl: %04x, dfcyc: %016llx, last_vbl:" + "%016llx\n", lines_since_vbl, dfcyc, g_last_vbl_dfcyc); + } + + if(loc == 0xe) { // c02e: Vertical count + return (lines_since_vbl >> 9) & 0xff; + } + + mask = (lines_since_vbl >> 1) & 0x80; + + lines_since_vbl = (lines_since_vbl & 0xff); + if(lines_since_vbl >= 0x01) { + lines_since_vbl = (lines_since_vbl + 0x3f) & 0x7f; + } + return (mask | (lines_since_vbl & 0xff)); +} + diff --git a/gsplus/src/op_routs.h b/gsplus/src/op_routs.h new file mode 100644 index 0000000..fa19702 --- /dev/null +++ b/gsplus/src/op_routs.h @@ -0,0 +1,143 @@ +// $KmKId: op_routs.h,v 1.47 2023-11-05 16:21:51+00 kentd Exp $ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +#define GET_DLOC_X_IND_WR() \ + CYCLES_PLUS_1; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + arg = arg + xreg + direct; \ + GET_MEMORY_DIRECT_PAGE16(arg & 0xffff, arg, 1); \ + arg = (dbank << 16) + arg; + + +#define GET_DLOC_X_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR() + +#define GET_DISP8_S_WR() \ + CYCLES_PLUS_1; \ + arg = (arg + stack) & 0xffff; \ + INC_KPC_2; + + +#define GET_DISP8_S_ADDR() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR() + +#define GET_DLOC_WR() \ + arg = (arg + direct) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; + +#define GET_DLOC_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_WR() + +#define GET_DLOC_L_IND_WR() \ + arg = (arg + direct) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY24(arg, arg, 1); + +#define GET_DLOC_L_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR() + + +#define GET_DLOC_IND_WR() \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \ + arg = (dbank << 16) + arg; + + +#define GET_DLOC_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_IND_WR(); + +#define GET_DLOC_INDEX_WR(index_reg) \ + CYCLES_PLUS_1; \ + arg = (arg & 0xff) + index_reg; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + if((psr & 0x100) && ((direct & 0xff) == 0)) { \ + arg = (arg & 0xff); \ + } \ + arg = (arg + direct) & 0xffff; + +#define GET_DLOC_X_WR() \ + GET_DLOC_INDEX_WR(xreg) +#define GET_DLOC_Y_WR() \ + GET_DLOC_INDEX_WR(yreg) + +#define GET_DLOC_X_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_INDEX_WR(xreg) + +#define GET_DLOC_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_INDEX_WR(yreg) + +#define GET_DISP8_S_IND_Y_WR() \ + arg = (stack + arg) & 0xffff; \ + GET_MEMORY16(arg,arg,1); \ + CYCLES_PLUS_2; \ + arg += (dbank << 16); \ + INC_KPC_2; \ + arg = (arg + yreg) & 0xffffff; + +#define GET_DISP8_S_IND_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR() + +#define GET_DLOC_L_IND_Y_WR() \ + arg = (direct + arg) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY24(arg,arg,1); \ + INC_KPC_2; \ + arg = (arg + yreg) & 0xffffff; + +#define GET_DLOC_L_IND_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR() + + +#define GET_ABS_ADDR() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + arg = arg + (dbank << 16); \ + INC_KPC_3; + +#define GET_LONG_ADDR() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + INC_KPC_4; + +#define GET_LONG_X_ADDR_FOR_WR() \ + GET_3BYTE_ARG; \ + INC_KPC_4; \ + arg = (arg + xreg) & 0xffffff; \ + CYCLES_PLUS_2; + diff --git a/gsplus/src/paddles.c b/gsplus/src/paddles.c new file mode 100644 index 0000000..9c018b1 --- /dev/null +++ b/gsplus/src/paddles.c @@ -0,0 +1,188 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +extern int g_mouse_raw_x; /* from adb.c */ +extern int g_mouse_raw_y; + +dword64 g_paddle_trig_dfcyc = 0; +int g_swap_paddles = 0; +int g_invert_paddles = 0; +int g_joystick_scale_factor_x = 0x100; +int g_joystick_scale_factor_y = 0x100; +int g_joystick_trim_amount_x = 0; +int g_joystick_trim_amount_y = 0; + +int g_joystick_type = 0; /* 0 = Keypad Joystick */ +int g_joystick_native_type1 = -1; +int g_joystick_native_type2 = -1; +int g_joystick_native_type = -1; + +extern int g_paddle_buttons; + +int g_paddle_val[4] = { 0, 0, 0, 0 }; + /* g_paddle_val[0]: Joystick X coord, [1]:Y coord */ + +dword64 g_paddle_dfcyc[4] = { 0, 0, 0, 0 }; + /* g_paddle_dfcyc are the dfcyc the paddle goes to 0 */ + + +void +paddle_fixup_joystick_type() +{ + /* If g_joystick_type points to an illegal value, change it */ + if(g_joystick_type == 2) { + g_joystick_native_type = g_joystick_native_type1; + if(g_joystick_native_type1 < 0) { + g_joystick_type = 0; + } + } + if(g_joystick_type == 3) { + g_joystick_native_type = g_joystick_native_type2; + if(g_joystick_native_type2 < 0) { + g_joystick_type = 0; + } + } +} + +void +paddle_trigger(dword64 dfcyc) +{ + /* Called by read/write to $c070 */ + g_paddle_trig_dfcyc = dfcyc; + + /* Determine what all the paddle values are right now */ + paddle_fixup_joystick_type(); + + switch(g_joystick_type) { + case 0: /* Keypad Joystick */ + paddle_trigger_keypad(dfcyc); + break; + case 1: /* Mouse Joystick */ + paddle_trigger_mouse(dfcyc); + break; + default: + joystick_update(dfcyc); + } +} + +void +paddle_trigger_mouse(dword64 dfcyc) +{ + int val_x, val_y; + int mouse_x, mouse_y; + + val_x = 0; + + mouse_x = g_mouse_raw_x; + mouse_y = g_mouse_raw_y; + /* mous_phys_x is 0->560, convert that to -32768 to + 32767 cyc */ + /* So subtract 280 then mult by 117 */ + val_x = (mouse_x - 280) * 117; + + /* mous_phys_y is 0->384, convert that to -32768 to + 32767 cyc */ + /* so subtract 192 then mult by 180 to overscale it a bit */ + val_y = (mouse_y - 192) * 180; + + g_paddle_val[0] = val_x; + g_paddle_val[1] = val_y; + g_paddle_val[2] = 32767; + g_paddle_val[3] = 32767; + g_paddle_buttons |= 0xc; + paddle_update_trigger_dcycs(dfcyc); +} + +void +paddle_trigger_keypad(dword64 dfcyc) +{ + int get_y, val_x, val_y; + + val_x = adb_get_keypad_xy(get_y=0); + val_y = adb_get_keypad_xy(get_y=1); + /* val_x and val_y are already scale -32768 to +32768 */ + + g_paddle_val[0] = val_x; + g_paddle_val[1] = val_y; + g_paddle_val[2] = 32767; + g_paddle_val[3] = 32767; + g_paddle_buttons |= 0xc; + paddle_update_trigger_dcycs(dfcyc); +} + +void +paddle_update_trigger_dcycs(dword64 dfcyc) +{ + dword64 trig_dfcyc; + int val, paddle_num, scale, trim; + int i; + + for(i = 0; i < 4; i++) { + paddle_num = i; + if(g_swap_paddles) { + paddle_num = i ^ 1; + } + val = g_paddle_val[paddle_num]; + if(g_invert_paddles) { + val = -val; + } + /* convert -32768 to +32768 into 0->2816.0 cycles (the */ + /* paddle delay const) */ + /* First multiply by the scale factor to adjust range */ + if(paddle_num & 1) { + scale = g_joystick_scale_factor_y; + trim = g_joystick_trim_amount_y; + } else { + scale = g_joystick_scale_factor_x; + trim = g_joystick_trim_amount_x; + } +#if 0 + if(i == 0) { + printf("val was %04x(%d) * scale %03x = %d\n", + val, val, scale, (val*scale)>>16); + } +#endif + val = (val * scale) >> 16; + /* Val is now from -128 to + 128 since scale is */ + /* 256=1.0, 128 = 0.5 */ + val = val + 128 + trim; + if(val >= 255) { + val = 280; /* increase range */ + } + trig_dfcyc = dfcyc + (dword64)((val * (2816/255.0)) * 65536); + g_paddle_dfcyc[i] = trig_dfcyc; + if(i < 2) { + dbg_log_info(dfcyc, (scale << 16) | (val & 0xffff), + (trim << 16) | i, 0x70); + } + } +} + +int +read_paddles(dword64 dfcyc, int paddle) +{ + dword64 trig_dfcyc; + + trig_dfcyc = g_paddle_dfcyc[paddle & 3]; + + if(dfcyc < trig_dfcyc) { + return 0x80; + } else { + return 0x00; + } +} + +void +paddle_update_buttons() +{ + paddle_fixup_joystick_type(); + joystick_update_buttons(); +} diff --git a/gsplus/src/protos.h b/gsplus/src/protos.h new file mode 100644 index 0000000..68ee5c4 --- /dev/null +++ b/gsplus/src/protos.h @@ -0,0 +1,14 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2019 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#ifdef INCLUDE_RCSID_C +#endif + +#include "protos_base.h" diff --git a/gsplus/src/protos_base.h b/gsplus/src/protos_base.h new file mode 100644 index 0000000..5b31153 --- /dev/null +++ b/gsplus/src/protos_base.h @@ -0,0 +1,895 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#ifdef INCLUDE_RCSID_C +#endif + +#ifdef __GNUC__ +void halt_printf(const char *fmt, ...) __attribute__ (( + __format__(printf, 1, 2))); +void cfg_err_printf(const char *pre_str, const char *fmt, ...) __attribute__ (( + __format__(printf, 2, 3))); +void dynapro_error(Disk *dsk, const char *fmt, ...) __attribute__ (( + __format__(printf, 2, 3))); +#endif + +/* xdriver.c and macdriver.c and windriver.c */ +int win_nonblock_read_stdin(int fd, char *bufptr, int len); + +/* special scc_unixdriver.c prototypes */ +void scc_serial_unix_open(int port); +void scc_serial_unix_close(int port); +void scc_serial_unix_change_params(int port); +void scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left); +void scc_serial_unix_empty_writebuf(int port); + +/* special scc_windriver.c prototypes */ +void scc_serial_win_open(int port); +void scc_serial_win_close(int port); +void scc_serial_win_change_params(int port); +void scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left); +void scc_serial_win_empty_writebuf(int port); + +/* special joystick_driver.c prototypes */ +void joystick_init(void); +void joystick_update(dword64 dfcyc); +void joystick_update_buttons(void); + +/* END_HDR */ + +/* adb.c */ +int adb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr); +int adb_get_copy_requested(void); +void adb_nonmain_check(void); +void adb_init(void); +void adb_reset(void); +void adb_log(word32 addr, int val); +void show_adb_log(void); +void adb_error(void); +void adb_add_kbd_srq(void); +void adb_clear_kbd_srq(void); +void adb_add_data_int(void); +void adb_add_mouse_int(void); +void adb_clear_data_int(void); +void adb_clear_mouse_int(void); +void adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2); +void adb_send_1byte(word32 val); +void adb_response_packet(int num_bytes, word32 val); +void adb_kbd_reg0_data(int a2code, int is_up); +void adb_kbd_talk_reg0(void); +void adb_set_config(word32 val0, word32 val1, word32 val2); +void adb_set_new_mode(word32 val); +int adb_read_c026(void); +void adb_write_c026(int val); +void do_adb_cmd(void); +int adb_read_c027(void); +void adb_write_c027(int val); +int read_adb_ram(word32 addr); +void write_adb_ram(word32 addr, int val); +int adb_get_keypad_xy(int get_y); +int adb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states, int buttons_valid); +int mouse_read_c024(dword64 dfcyc); +void mouse_compress_fifo(dword64 dfcyc); +void adb_paste_update_state(void); +int adb_paste_add_buf(word32 key); +void adb_key_event(int a2code, int is_up); +word32 adb_read_c000(void); +word32 adb_access_c010(void); +word32 adb_read_c025(void); +int adb_is_cmd_key_down(void); +int adb_is_option_key_down(void); +void adb_increment_speed(void); +void adb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask); +int adb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr); +void adb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c, int is_up); +void adb_maybe_virtual_key_update(int a2code, int is_up); +void adb_virtual_key_update(int a2code, int is_up); +void adb_kbd_repeat_off(void); +void adb_mainwin_focus(int has_focus); + + + +/* engine_c.c */ +word32 get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1); +word32 get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank); +word32 get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank); +void set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1); +void set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, dword64 dplus_1, dword64 dplus_x_m1, int in_bank); +void set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank); +word32 get_memory_c(word32 addr); +word32 get_memory16_c(word32 addr); +word32 get_memory24_c(word32 addr); +void set_memory_c(word32 addr, word32 val, int do_log); +void set_memory16_c(word32 addr, word32 val, int do_log); +void set_memory24_c(word32 addr, word32 val); +word32 do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub); +word32 do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub); +void fixed_memory_ptrs_init(void); +word32 get_itimer(void); +void engine_recalc_events(void); +void set_halt_act(int val); +void clr_halt_act(void); +word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, dword64 *dcyc_ptr, Fplus *fplus_ptr); +int enter_engine(Engine_reg *engine_ptr); + + + +/* clock.c */ +double get_dtime(void); +int micro_sleep(double dtime); +void clk_bram_zero(void); +void clk_bram_set(int bram_num, int offset, int val); +void clk_setup_bram_version(void); +void clk_write_bram(FILE *fconf); +void update_cur_time(void); +void clock_update(void); +void clock_update_if_needed(void); +void clock_write_c034(word32 val); +void do_clock_data(void); + + + +/* compile_time.c */ + + + +/* config.c */ +int config_add_argv_override(const char *str1, const char *str2); +void config_set_config_kegs_name(const char *str1); +void config_init_menus(Cfg_menu *menuptr); +void config_init(void); +void cfg_find_config_kegs_file(void); +int config_setup_kegs_file(char *outname, int maxlen, const char **name_ptr); +int config_expand_path(char *out_ptr, const char *in_ptr, int maxlen); +char *cfg_exit(int get_status); +void cfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap); +void cfg_err_printf(const char *pre_str, const char *fmt, ...); +void cfg_toggle_config_panel(void); +void cfg_set_config_panel(int panel); +char *cfg_text_screen_dump(int get_status); +char *cfg_text_screen_str(void); +char *cfg_get_serial0_status(int get_status); +char *cfg_get_serial1_status(int get_status); +char *cfg_get_current_copy_selection(void); +void config_vbl_update(int doit_3_persec); +void cfg_file_update_rom(const char *str); +void cfg_file_update_ptr(char **strptr, const char *str, int need_update); +void cfg_int_update(int *iptr, int new_val); +void cfg_load_charrom(void); +void config_load_roms(void); +void config_parse_config_kegs_file(void); +void cfg_parse_one_line(char *buf, int line); +void cfg_parse_bram(char *buf, int pos, int len); +void cfg_parse_sxdx(char *buf, int pos, int len); +void config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras); +char *config_write_config_kegs_file(int get_status); +void insert_disk(int slot, int drive, const char *name, int ejected, const char *partition_name, int part_num, word32 dynamic_blocks); +dword64 cfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot); +dword64 cfg_get_fd_size(int fd); +dword64 cfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize); +dword64 cfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize); +int cfg_partition_maybe_add_dotdot(void); +int cfg_partition_name_check(const byte *name_ptr, int name_len); +int cfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size); +int cfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr, int part_num); +int cfg_partition_make_list_from_name(const char *namestr); +int cfg_partition_make_list(int fd); +int cfg_maybe_insert_disk(int slot, int drive, const char *namestr); +void cfg_insert_disk_dynapro(int slot, int drive, const char *name); +int cfg_stat(char *path, struct stat *sb, int do_lstat); +word32 cfg_get_le16(byte *bptr); +word32 cfg_get_le32(byte *bptr); +dword64 cfg_get_le64(byte *bptr); +word32 cfg_get_be_word16(word16 *ptr); +word32 cfg_get_be_word32(word32 *ptr); +void cfg_set_le32(byte *bptr, word32 val); +void config_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr); +void cfg_htab_vtab(int x, int y); +void cfg_home(void); +void cfg_cleol(void); +void cfg_putchar(int c); +void cfg_printf(const char *fmt, ...); +void cfg_print_dnum(dword64 dnum, int max_len); +int cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras); +int cfg_get_disk_locked(int type_ext); +void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change); +void cfg_get_base_path(char *pathptr, const char *inptr, int go_up); +char *cfg_name_new_image(int get_status); +void cfg_dup_existing_image(word32 slotdrive); +void cfg_dup_image_selected(void); +void cfg_validate_image(word32 slotdrive); +void cfg_toggle_lock_disk(word32 slotdrive); +int cfg_create_new_image_act(const char *str, int type, int size_blocks); +void cfg_create_new_image(void); +void cfg_file_init(void); +void cfg_free_alldirents(Cfg_listhdr *listhdrptr); +void cfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num); +void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num); +int cfg_dirent_sortfn(const void *obj1, const void *obj2); +int cfg_str_match(const char *str1, const char *str2, int len); +int cfg_str_match_maybecase(const char *str1, const char *str2, int len, int ignorecase); +int cfgcasecmp(const char *str1, const char *str2); +int cfg_strlcat(char *dstptr, const char *srcptr, int dstsize); +char *cfg_strncpy(char *dstptr, const char *srcptr, int dstsize); +const char *cfg_str_basename(const char *str); +char *cfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize); +void cfg_file_readdir(const char *pathptr); +char *cfg_shorten_filename(const char *in_ptr, int maxlen); +void cfg_fix_topent(Cfg_listhdr *listhdrptr); +void cfg_file_draw(void); +void cfg_partition_select_all(void); +void cfg_partition_selected(void); +void cfg_file_selected(void); +void cfg_file_handle_key(int key); +void cfg_draw_menu(void); +void cfg_newdisk_pick_menu(word32 slotdrive); +int cfg_control_panel_update(void); +void cfg_edit_mode_key(int key); +int cfg_control_panel_update1(void); + + + +/* debugger.c */ +void debugger_init(void); +void check_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type); +void debug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type, int pos); +int debugger_run_16ms(void); +void dbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type); +void debugger_update_list_kpc(void); +void debugger_key_event(Kimage *kimage_ptr, int a2code, int is_up); +void debugger_page_updown(int isup); +void debugger_redraw_screen(Kimage *kimage_ptr); +void debug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line); +void debugger_help(void); +void dbg_help_show_strs(int help_depth, const char *str, const char *help_str); +const char *debug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr, int help_depth); +void do_debug_cmd(const char *in_str); +word32 dis_get_memory_ptr(word32 addr); +void show_one_toolset(FILE *toolfile, int toolnum, word32 addr); +void show_toolset_tables(word32 a2bank, word32 addr); +word32 debug_getnum(const char **str_ptr); +char *debug_get_filename(const char **str_ptr); +void debug_help(const char *str); +void debug_bp(const char *str); +void debug_bp_set(const char *str); +void debug_bp_clear(const char *str); +void debug_bp_clear_all(const char *str); +void debug_bp_setclr(const char *str, int is_set_clear); +void debug_soundfile(const char *cmd_str); +void debug_logpc(const char *str); +void debug_logpc_on(const char *str); +void debug_logpc_off(const char *str); +void debug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc); +Data_log *debug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc, dword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr, int *count_ptr); +void debug_logpc_save(const char *cmd_str); +void set_bp(word32 addr, word32 end_addr, word32 acc_type); +void show_bp(void); +void delete_bp(word32 addr, word32 end_addr); +void debug_iwm(const char *str); +void debug_iwm_check(const char *str); +int do_blank(int mode, int old_mode); +void do_go(void); +void do_step(void); +void xam_mem(int count); +void show_hex_mem(word32 startbank, word32 start, word32 end, int count); +void do_debug_list(void); +void dis_do_memmove(void); +void dis_do_pattern_search(void); +void dis_do_compare(void); +const char *do_debug_unix(const char *str, int old_mode); +void do_debug_load(void); +char *do_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr, int *size_ptr); +int debug_get_view_line(int back); +int debug_add_output_line(char *in_str); +void debug_add_output_string(char *in_str, int len); +void debug_add_output_chars(char *str); +int dbg_printf(const char *fmt, ...); +int dbg_vprintf(const char *fmt, va_list args); +void halt_printf(const char *fmt, ...); +void halt2_printf(const char *fmt, ...); + + + +/* scc.c */ +void scc_init(void); +void scc_reset(void); +void scc_hard_reset_port(int port); +void scc_reset_port(int port); +void scc_regen_clocks(int port); +void scc_port_close(int port); +void scc_port_open(dword64 dfcyc, int port); +int scc_is_port_closed(dword64 dfcyc, int port); +char *scc_get_serial_status(int get_status, int port); +void scc_config_changed(int port, int cfg_changed, int remote_changed, int serial_dev_changed); +void scc_update(dword64 dfcyc); +void scc_try_to_empty_writebuf(dword64 dfcyc, int port); +void scc_try_fill_readbuf(dword64 dfcyc, int port); +void scc_do_event(dword64 dfcyc, int type); +void show_scc_state(void); +word32 scc_read_reg(dword64 dfcyc, int port); +void scc_write_reg(dword64 dfcyc, int port, word32 val); +word32 scc_read_data(dword64 dfcyc, int port); +void scc_write_data(dword64 dfcyc, int port, word32 val); +word32 scc_do_read_rr2b(void); +void scc_maybe_br_event(dword64 dfcyc, int port); +void scc_evaluate_ints(int port); +void scc_maybe_rx_event(dword64 dfcyc, int port); +void scc_maybe_rx_int(int port); +void scc_clr_rx_int(int port); +void scc_handle_tx_event(int port); +void scc_maybe_tx_event(dword64 dfcyc, int port); +void scc_clr_tx_int(int port); +void scc_set_zerocnt_int(int port); +void scc_clr_zerocnt_int(int port); +void scc_add_to_readbuf(dword64 dfcyc, int port, word32 val); +void scc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...); +void scc_transmit(dword64 dfcyc, int port, word32 val); +void scc_add_to_writebuf(dword64 dfcyc, int port, word32 val); + + + +/* scc_socket_driver.c */ +void scc_socket_open(dword64 dfcyc, int port, int cfg); +void scc_socket_close(int port); +void scc_socket_close_extended(dword64 dfcyc, int port, int allow_retry); +void scc_socket_maybe_open(dword64 dfcyc, int port, int must); +void scc_socket_open_incoming(dword64 dfcyc, int port); +void scc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str, int remote_port); +void scc_socket_make_nonblock(dword64 dfcyc, int port); +void scc_accept_socket(dword64 dfcyc, int port); +void scc_socket_telnet_reqs(dword64 dfcyc, int port); +void scc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left); +void scc_socket_recvd_char(dword64 dfcyc, int port, int c); +void scc_socket_empty_writebuf(dword64 dfcyc, int port); +void scc_socket_modem_write(dword64 dfcyc, int port, int c); +void scc_socket_do_cmd_str(dword64 dfcyc, int port); +void scc_socket_send_modem_code(dword64 dfcyc, int port, int code); +void scc_socket_modem_connect(dword64 dfcyc, int port); +void scc_socket_modem_do_ring(dword64 dfcyc, int port); +void scc_socket_do_answer(dword64 dfcyc, int port); + + + +/* scc_windriver.c */ + + + +/* scc_unixdriver.c */ + + + +/* iwm.c */ +void iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525); +void iwm_init(void); +void iwm_reset(void); +void disk_set_num_tracks(Disk *dsk, int num_tracks); +word32 iwm_get_default_track_bits(Disk *dsk, word32 qtr_trk); +void draw_iwm_status(int line, char *buf); +void iwm_flush_cur_disk(void); +void iwm_flush_disk_to_unix(Disk *dsk); +void iwm_vbl_update(void); +void iwm_update_fast_disk_emul(int fast_disk_emul_en); +void iwm_show_stats(int slot_drive); +Disk *iwm_get_dsk(word32 state); +Disk *iwm_touch_switches(int loc, dword64 dfcyc); +void iwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc); +void iwm_move_to_qtr_track(Disk *dsk, word32 qtr_track); +void iwm525_update_phases(Disk *dsk, dword64 dfcyc); +void iwm525_update_head(Disk *dsk, dword64 dfcyc); +int iwm_read_status35(dword64 dfcyc); +void iwm_do_action35(dword64 dfcyc); +int read_iwm(int loc, dword64 dfcyc); +void write_iwm(int loc, int val, dword64 dfcyc); +int iwm_read_enable2(dword64 dfcyc); +int iwm_read_enable2_handshake(dword64 dfcyc); +void iwm_write_enable2(int val); +word32 iwm_fastemul_start_write(Disk *dsk, dword64 dfcyc); +word32 iwm_read_data_fast(Disk *dsk, dword64 dfcyc); +dword64 iwm_return_rand_data(Disk *dsk, dword64 dfcyc); +dword64 iwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr); +word32 iwm_calc_bit_diff(word32 first, word32 last, word32 track_bits); +word32 iwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits); +dword64 iwm_calc_forced_sync(dword64 dval, int forced_bit); +int iwm_calc_forced_sync_0s(dword64 sync_dval, int bits); +word32 iwm_read_data(Disk *dsk, dword64 dfcyc); +void iwm_write_data(Disk *dsk, word32 val, dword64 dfcyc); +void iwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior); +int iwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta); +void iwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc); +void iwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc); +void iwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc); +void iwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc); +void sector_to_partial_nib(byte *in, byte *nib_ptr); +int disk_unnib_4x4(Disk *dsk); +int iwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf); +int iwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf); +int iwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf); +void show_hex_data(byte *buf, int count); +void iwm_check_nibblization(dword64 dfcyc); +void disk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc); +void disk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len, int len_bits, dword64 dfcyc); +void iwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len); +void iwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc); +void iwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len, dword64 dfcyc); +void disk_4x4_nib_out(Disk *dsk, word32 val); +void disk_nib_out(Disk *dsk, word32 val, int size); +void disk_nib_end_track(Disk *dsk, dword64 dfcyc); +word32 disk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits, word32 bit_pos, dword64 dfcyc); +Disk *iwm_get_dsk_from_slot_drive(int slot, int drive); +void iwm_toggle_lock(Disk *dsk); +void iwm_eject_named_disk(int slot, int drive, const char *name, const char *partition_name); +void iwm_eject_disk_by_num(int slot, int drive); +void iwm_eject_disk(Disk *dsk); +void iwm_show_track(int slot_drive, int track, dword64 dfcyc); +void iwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc); +void dummy1(word32 psr); +void dummy2(word32 psr); + + + +/* joystick_driver.c */ +void joystick_callback_init(int native_type); +void joystick_callback_update(word32 buttons, int paddle_x, int paddle_y); + + + +/* moremem.c */ +void fixup_brks(void); +void fixup_hires_on(void); +void fixup_bank0_2000_4000(void); +void fixup_bank0_0400_0800(void); +void fixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd, byte *mem0wr); +void fixup_intcx(void); +void fixup_st80col(dword64 dfcyc); +void fixup_altzp(void); +void fixup_page2(dword64 dfcyc); +void fixup_ramrd(void); +void fixup_ramwrt(void); +void fixup_lc(void); +void set_statereg(dword64 dfcyc, word32 val); +void fixup_shadow_txt1(void); +void fixup_shadow_txt2(void); +void fixup_shadow_hires1(void); +void fixup_shadow_hires2(void); +void fixup_shadow_shr(void); +void fixup_shadow_iolc(void); +void update_shadow_reg(dword64 dfcyc, word32 val); +void fixup_shadow_all_banks(void); +void setup_pageinfo(void); +void show_bankptrs_bank0rdwr(void); +void show_bankptrs(int bnk); +void show_addr(byte *ptr); +word32 moremem_fix_vector_pull(word32 addr); +word32 io_read(word32 loc, dword64 *cyc_ptr); +void io_write(word32 loc, word32 val, dword64 *cyc_ptr); +word32 slinky_devsel_read(dword64 dfcyc, word32 loc); +void slinky_devsel_write(dword64 dfcyc, word32 loc, word32 val); +word32 c3xx_read(dword64 dfcyc, word32 loc); +word32 get_lines_since_vbl(dword64 dfcyc); +int in_vblank(dword64 dfcyc); +int read_vid_counters(int loc, dword64 dfcyc); + + + +/* paddles.c */ +void paddle_fixup_joystick_type(void); +void paddle_trigger(dword64 dfcyc); +void paddle_trigger_mouse(dword64 dfcyc); +void paddle_trigger_keypad(dword64 dfcyc); +void paddle_update_trigger_dcycs(dword64 dfcyc); +int read_paddles(dword64 dfcyc, int paddle); +void paddle_update_buttons(void); + + + +/* mockingboard.c */ +void mock_ay8913_reset(int pair_num, dword64 dfcyc); +void mockingboard_reset(dword64 dfcyc); +void mock_show_pair(int pair_num, dword64 dfcyc, const char *str); +void mock_update_timers(int doit, dword64 dfcyc); +void mockingboard_event(dword64 dfcyc); +word32 mockingboard_read(word32 loc, dword64 dfcyc); +void mockingboard_write(word32 loc, word32 val, dword64 dfcyc); +word32 mock_6522_read(int pair_num, word32 loc, dword64 dfcyc); +void mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc); +word32 mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier); +void mock_ay8913_reg_read(int pair_num); +void mock_ay8913_reg_write(int pair_num, dword64 dfcyc); +void mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val, dword64 dfcyc); +void mockingboard_show(int got_num, word32 disable_mask); + + + +/* sim65816.c */ +int sim_get_force_depth(void); +int sim_get_use_shmem(void); +void sim_set_use_shmem(int use_shmem); +word32 toolbox_debug_4byte(word32 addr); +void toolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr); +void show_toolbox_log(void); +word32 get_memory_io(word32 loc, dword64 *dcyc_ptr); +void set_memory_io(word32 loc, int val, dword64 *dcyc_ptr); +void show_regs_act(Engine_reg *eptr); +void show_regs(void); +void my_exit(int ret); +void do_reset(void); +byte *memalloc_align(int size, int skip_amt, void **alloc_ptr); +void memory_ptr_init(void); +int parse_argv(int argc, char **argv, int slashes_to_find); +int kegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window); +void load_roms_init_memory(void); +void initialize_events(void); +void check_for_one_event_type(int type, word32 mask); +void add_event_entry(dword64 dfcyc, int type); +dword64 remove_event_entry(int type, word32 mask); +void add_event_stop(dword64 dfcyc); +void add_event_doc(dword64 dfcyc, int osc); +void add_event_scc(dword64 dfcyc, int type); +void add_event_vbl(void); +void add_event_vid_upd(int line); +void add_event_mockingboard(dword64 dfcyc); +void add_event_scan_int(dword64 dfcyc, int line); +dword64 remove_event_doc(int osc); +dword64 remove_event_scc(int type); +void remove_event_mockingboard(void); +void show_all_events(void); +void show_pmhz(void); +void setup_zip_speeds(void); +int run_16ms(void); +int run_a2_one_vbl(void); +void add_irq(word32 irq_mask); +void remove_irq(word32 irq_mask); +void take_irq(void); +void show_dtime_array(void); +void update_60hz(dword64 dfcyc, double dtime_now); +void do_vbl_int(void); +void do_scan_int(dword64 dfcyc, int line); +void check_scan_line_int(int cur_video_line); +void check_for_new_scan_int(dword64 dfcyc); +void scb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val); +void init_reg(void); +void handle_action(word32 ret); +void do_break(word32 ret); +void do_cop(word32 ret); +void do_wdm(word32 arg); +void do_wai(void); +void do_stp(void); +void do_wdm_emulator_id(void); +void size_fail(int val, word32 v1, word32 v2); +int fatal_printf(const char *fmt, ...); +int kegs_vprintf(const char *fmt, va_list ap); +dword64 must_write(int fd, byte *bufptr, dword64 dsize); +void clear_fatal_logs(void); +char *kegs_malloc_str(const char *in_str); +dword64 kegs_lseek(int fd, dword64 offs, int whence); + + + +/* smartport.c */ +void smartport_error(void); +void smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list); +void do_c70d(word32 arg0); +void do_c70a(word32 arg0); +int do_read_c7(int unit_num, word32 buf, word32 blk); +int do_write_c7(int unit_num, word32 buf, word32 blk); +int smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size); +int do_format_c7(int unit_num); +void do_c700(word32 ret); + + + +/* doc.c */ +void doc_init(void); +void doc_reset(dword64 dfcyc); +int doc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps, int snd_buf_init, int *outptr_start); +void doc_handle_event(int osc, dword64 dfcyc); +void doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps); +void doc_add_sound_irq(int osc); +void doc_remove_sound_irq(int osc, int must); +void doc_start_sound2(int osc, dword64 dfcyc); +void doc_start_sound(int osc, double eff_dsamps, double dsamps); +void doc_wave_end_estimate2(int osc, dword64 dfcyc); +void doc_wave_end_estimate(int osc, double eff_dsamps, double dsamps); +void doc_remove_sound_event(int osc); +void doc_write_ctl_reg(dword64 dfcyc, int osc, int val); +void doc_recalc_sound_parms(dword64 dfcyc, int osc); +int doc_read_c03c(void); +int doc_read_c03d(dword64 dfcyc); +void doc_write_c03c(dword64 dfcyc, word32 val); +void doc_write_c03d(dword64 dfcyc, word32 val); +void doc_show_ensoniq_state(void); + + + +/* sound.c */ +void sound_init(void); +void sound_set_audio_rate(int rate); +void sound_reset(dword64 dfcyc); +void sound_shutdown(void); +void sound_update(dword64 dfcyc); +void sound_file_start(char *filename); +void sound_file_open(void); +void sound_file_close(void); +void send_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps); +void show_c030_state(dword64 dfcyc); +void show_c030_samps(dword64 dfcyc, int *outptr, int num); +int sound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps); +void sound_play(dword64 dfcyc); +void sound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr); +void sound_mock_noise(int pair, byte *noise_ptr, int num_samps); +void sound_mock_play(int pair, int channel, int *outptr, int *env_ptr, byte *noise_ptr, int *vol_ptr, int num_samps); +word32 sound_read_c030(dword64 dfcyc); +void sound_write_c030(dword64 dfcyc); + + + +/* sound_driver.c */ +void snddrv_init(void); +void sound_child_fork(int size); +void parent_sound_get_sample_rate(int read_fd); +void snddrv_shutdown(void); +void snddrv_send_sound(int real_samps, int size); +void child_sound_playit(word32 tmp); +void reliable_buf_write(word32 *shm_addr, int pos, int size); +void reliable_zero_write(int amt); +int child_send_samples(byte *ptr, int size); +void child_sound_loop(int read_fd, int write_fd, word32 *shm_addr); + + + +/* woz.c */ +void woz_crc_init(void); +word32 woz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip); +void woz_rewrite_crc(Disk *dsk, int min_write_size); +void woz_rewrite_lock(Disk *dsk); +void woz_check_file(Disk *dsk); +void woz_parse_meta(Disk *dsk, int offset, int size); +void woz_parse_info(Disk *dsk, int offset, int size); +void woz_parse_tmap(Disk *dsk, int offset, int size); +void woz_parse_trks(Disk *dsk, int offset, int size); +int woz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc); +int woz_parse_header(Disk *dsk); +Woz_info *woz_malloc(byte *wozptr, word32 woz_size); +int woz_reopen(Disk *dsk, dword64 dfcyc); +int woz_open(Disk *dsk, dword64 dfcyc); +byte *woz_append_bytes(byte *wozptr, byte *in_bptr, int len); +byte *woz_append_word32(byte *wozptr, word32 val); +int woz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length, byte *bptr); +byte *woz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr, word32 *num_blocks_ptr, dword64 *tmap_dptr); +Woz_info *woz_new_from_woz(Disk *dsk, int disk_525); +int woz_new(int fd, const char *str, int size_kb); +void woz_maybe_reparse(Disk *dsk); +void woz_set_reparse(Disk *dsk); +void woz_reparse_woz(Disk *dsk); +void woz_remove_a_track(Disk *dsk, word32 qtr_track); +word32 woz_add_a_track(Disk *dsk, word32 qtr_track); + + + +/* unshk.c */ +word32 unshk_get_long4(byte *bptr); +word32 unshk_get_word2(byte *bptr); +word32 unshk_calc_crc(byte *bptr, int size, word32 start_crc); +int unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr); +void unshk_lzw_clear(Lzw_state *lzw_ptr); +byte *unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen); +void unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr, word32 uncompr_size, word32 thread_format, byte *base_cptr); +void unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr); +void unshk(Disk *dsk, const char *name_str); +void unshk_dsk_raw_data(Disk *dsk); + + + +/* undeflate.c */ +void *undeflate_realloc(void *ptr, dword64 dsize); +void *undeflate_malloc(dword64 dsize); +void show_bits(unsigned *llptr, int nl); +void show_huftb(unsigned *tabptr, int bits); +void undeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start); +void undeflate_init_bit_rev_tab(word32 *tabptr, int num); +word32 undeflate_bit_reverse(word32 val, word32 bits); +word32 undeflate_calc_crc32(byte *bptr, word32 len); +byte *undeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len); +void undeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code, word32 entry); +word32 *undeflate_init_fixed_tabs(void); +word32 *undeflate_init_tables(void); +void undeflate_free_tables(void); +void undeflate_check_bit_reverse(void); +word32 *undeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size, word32 *bl_count_ptr, int max_bits); +word32 *undeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base); +byte *undeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base, byte *cptr_end); +byte *undeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size); +void undeflate_gzip(Disk *dsk, const char *name_str); +byte *undeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size); +int undeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off, dword64 uncompr_dsize, dword64 compr_dsize); +int undeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len, int min_size); +int undeflate_zipfile_make_list(int fd); + + + +/* dynapro.c */ +word32 dynapro_get_word32(byte *bptr); +word32 dynapro_get_word24(byte *bptr); +word32 dynapro_get_word16(byte *bptr); +void dynapro_set_word24(byte *bptr, word32 val); +void dynapro_set_word32(byte *bptr, word32 val); +void dynapro_set_word16(byte *bptr, word32 val); +void dynapro_error(Disk *dsk, const char *fmt, ...); +Dynapro_file *dynapro_alloc_file(void); +void dynapro_free_file(Dynapro_file *fileptr, int check_map); +void dynapro_free_recursive_file(Dynapro_file *fileptr, int check_map); +void dynapro_free_dynapro_info(Disk *dsk); +word32 dynapro_find_free_block_internal(Disk *dsk); +word32 dynapro_find_free_block(Disk *dsk); +byte *dynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size); +void dynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str, int path_max); +word32 dynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr, char *buf32_ptr, word32 dir_byte); +word32 dynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr); +word32 dynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte); +void dynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr); +void dynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr); +void dynapro_try_fix_damaged_disk(Disk *dsk); +void dynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str, const char *name_str); +Dynapro_file *dynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file **head_ptr_ptr, word32 dir_byte); +void dynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file *head_ptr, word32 dir_byte); +word32 dynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr); +void dynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr); +void dynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr); +word32 dynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size); +void dynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr); +void dynapro_unlink_file(Dynapro_file *fileptr); +void dynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr); +void dynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr); +void dynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr); +int dynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size); +void dynapro_debug_update(Disk *dsk); +void dynapro_debug_map(Disk *dsk, const char *str); +void dynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start); +word32 dynapro_unix_to_prodos_time(const time_t *time_ptr); +int dynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr, word32 storage_type); +Dynapro_file *dynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr, Dynapro_file *match_ptr, word32 storage_type); +int dynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr, word32 dir_byte); +word32 dynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr, word32 dir_byte, word32 inc); +word32 dynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr, word32 key_block, dword64 dsize); +word32 dynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr); +word32 dynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks); +word32 dynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num, word32 file_offset, word32 eof); +word32 dynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num, int level, word32 file_offset, word32 eof); +word32 dynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data); +word32 dynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr); +word32 dynapro_build_map(Disk *dsk, Dynapro_file *fileptr); +int dynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks); + + + +/* dyna_type.c */ +word32 dynatype_scan_extensions(const char *str); +word32 dynatype_find_prodos_type(const char *str); +const char *dynatype_find_file_type(word32 file_type); +word32 dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr, word32 storage_type); +int dynatype_get_extension(const char *str, char *out_ptr, int buf_len); +int dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr); +void dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max); + + + +/* dyna_filt.c */ + + + +/* dyna_validate.c */ +word32 dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte, word32 parent_dir_byte); +void dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks); +word32 dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block); +word32 dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof, int level_first); +word32 dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof); +word32 dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte, word32 parent_dir_byte, word32 exp_blocks_used); +int dynapro_validate_disk(Disk *dsk); +void dynapro_validate_any_image(Disk *dsk); + + + +/* applesingle.c */ +word32 applesingle_get_be32(const byte *bptr); +word32 applesingle_get_be16(const byte *bptr); +void applesingle_set_be32(byte *bptr, word32 val); +void applesingle_set_be16(byte *bptr, word32 val); +word32 applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data); +word32 applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr, dword64 dsize); +word32 applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length); + + + +/* video.c */ +void video_set_red_mask(word32 red_mask); +void video_set_green_mask(word32 green_mask); +void video_set_blue_mask(word32 blue_mask); +void video_set_alpha_mask(word32 alpha_mask); +void video_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr); +void video_set_palette(void); +void video_set_redraw_skip_amt(int amt); +Kimage *video_get_kimage(int win_id); +char *video_get_status_ptr(int line); +void video_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh); +int video_get_active(Kimage *kimage_ptr); +void video_set_active(Kimage *kimage_ptr, int active); +void video_init(int mdepth, int screen_width, int screen_height, int no_scale_window); +void video_init_kimage(Kimage *kimage_ptr, int width, int height, int screen_width, int screen_height); +void show_a2_line_stuff(void); +void video_reset(void); +void video_update(void); +word32 video_all_stat_to_filt_stat(int line, word32 new_all_stat); +void change_display_mode(dword64 dfcyc); +void video_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl); +void change_border_color(dword64 dfcyc, int val); +void update_border_info(void); +void update_border_line(int st_line_offset, int end_line_offset, int color); +void video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, int color, int st_off, int end_off); +word32 video_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse); +void video_update_edges(int line, int left, int right, const char *str); +void redraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat); +void redraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask, word32 *in_wptr, word32 bg_pixel, word32 fg_pixel, int pixels_per_line, int dbl); +void redraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat); +void video_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte, int end_byte, int pixels_per_line, word32 filt_stat); +void redraw_changed_hgr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat); +int video_rebuild_super_hires_palette(int bank, word32 scan_info, int line, int reparse); +word32 redraw_changed_super_hires_oneline(int bank, word32 *in_wptr, int pixels_per_line, int y, int scan, word32 ch_mask); +void redraw_changed_super_hires_bank(int bank, int start_line, int reparse, word32 *wptr, int pixels_per_line); +void redraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr, int pixels_per_line, word32 filt_stat); +void video_copy_changed2(void); +void video_update_event_line(int line); +void video_force_reparse(void); +void video_update_through_line(int line); +void video_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat); +void video_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat); +void prepare_a2_font(void); +void prepare_a2_romx_font(byte *font_ptr); +void video_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height); +void video_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix); +void video_form_change_rects(void); +int video_get_a2_width(Kimage *kimage_ptr); +int video_get_x_width(Kimage *kimage_ptr); +int video_get_a2_height(Kimage *kimage_ptr); +int video_get_x_height(Kimage *kimage_ptr); +int video_get_x_xpos(Kimage *kimage_ptr); +int video_get_x_ypos(Kimage *kimage_ptr); +void video_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos); +int video_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height); +void video_update_status_enable(Kimage *kimage_ptr); +int video_out_query(Kimage *kimage_ptr); +void video_out_done(Kimage *kimage_ptr); +int video_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr, int pos); +int video_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr); +int video_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr); +word32 video_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv); +void video_update_scale(Kimage *kimage_ptr, int out_width, int out_height, int must_update); +int video_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width); +int video_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height); +int video_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width); +int video_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height); +void video_update_color_raw(int bank, int col_num, int a2_color); +void video_update_status_line(int line, const char *string); +void video_draw_a2_string(int line, const byte *bptr); +void video_show_debug_info(void); +word32 read_video_data(dword64 dfcyc); +word32 float_bus(dword64 dfcyc); +word32 float_bus_lines(dword64 dfcyc, word32 lines_since_vbl); + + + +/* voc.c */ +word32 voc_devsel_read(word32 loc, dword64 dfcyc); +void voc_devsel_write(word32 loc, word32 val, dword64 dfcyc); +void voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc); +void voc_reset(void); +word32 voc_read_reg0(dword64 dfcyc); +void voc_update_interlace(dword64 dfcyc); + + diff --git a/gsplus/src/protos_macdriver.h b/gsplus/src/protos_macdriver.h new file mode 100644 index 0000000..1213dee --- /dev/null +++ b/gsplus/src/protos_macdriver.h @@ -0,0 +1,43 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2019 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +/* END_HDR */ + +/* macdriver.c */ +pascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore); +void show_simple_alert(char *str1, char *str2, char *str3, int num); +void x_dialog_create_kegs_conf(const char *str); +int x_show_alert(int is_fatal, const char *str); +pascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata); +void update_window(void); +void show_event(UInt32 event_class, UInt32 event_kind, int handled); +pascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata); +pascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore); +void mac_update_modifiers(word32 state); +void mac_warp_mouse(void); +void check_input_events(void); +void temp_run_application_event_loop(void); +int main(int argc, char *argv[]); +void x_update_color(int col_num, int red, int green, int blue, word32 rgb); +void x_update_physical_colormap(void); +void show_xcolor_array(void); +void xdriver_end(void); +void x_get_kimage(Kimage *kimage_ptr); +void dev_video_init(void); +void x_redraw_status_lines(void); +void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height); +void x_push_done(void); +void x_auto_repeat_on(int must); +void x_auto_repeat_off(int must); +void x_hide_pointer(int do_hide); +void x_full_screen(int do_full); +void update_main_window_size(void); + diff --git a/gsplus/src/protos_macsnd_driver.h b/gsplus/src/protos_macsnd_driver.h new file mode 100644 index 0000000..87748a1 --- /dev/null +++ b/gsplus/src/protos_macsnd_driver.h @@ -0,0 +1,18 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +/* END_HDR */ + +/* macsnd_driver.c */ +int mac_send_audio(byte *ptr, int in_size); +void macsnd_init(void); + + diff --git a/gsplus/src/protos_pulseaudio_driver.h b/gsplus/src/protos_pulseaudio_driver.h new file mode 100644 index 0000000..9ccc457 --- /dev/null +++ b/gsplus/src/protos_pulseaudio_driver.h @@ -0,0 +1,22 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2020 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +/* END_HDR */ + +/* pulseaudio_driver.c */ +int pulse_audio_send_audio(byte *ptr, int in_size); +void pulse_audio_main_events(void); +void pulse_audio_write_to_stream(int dbg_count, int in_sz); +int pulse_audio_start_stream(void); +int pulse_audio_do_init(void); +int pulse_audio_init(void); +void pulse_audio_shutdown(void); + diff --git a/gsplus/src/protos_windriver.h b/gsplus/src/protos_windriver.h new file mode 100644 index 0000000..51ebc3d --- /dev/null +++ b/gsplus/src/protos_windriver.h @@ -0,0 +1,55 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// $KmKId: protos_windriver.h,v 1.15 2023-05-17 22:37:57+00 kentd Exp $ + +/* END_HDR */ + +/* windriver.c */ +Window_info *win_find_win_info_ptr(HWND hwnd); +void win_hide_pointer(Window_info *win_info_ptr, int do_hide); +int win_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid); +void win_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam); +void win_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down); +void win_event_redraw(HWND hwnd); +void win_event_destroy(HWND hwnd); +void win_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam); +void win_event_minmaxinfo(HWND hwnd, LPARAM lParam); +void win_event_focus(HWND hwnd, int gain_focus); +LRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam); +int main(int argc, char **argv); +void check_input_events(void); +void win_video_init(int mdepth); +void win_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str, int mdepth); +void win_create_window(Window_info *win_info_ptr); +void xdriver_end(void); +void win_resize_window(Window_info *win_info_ptr); +void x_update_display(Window_info *win_info_ptr); +void x_hide_pointer(int do_hide); +int opendir_int(DIR *dirp, const char *in_filename); +DIR *opendir(const char *in_filename); +struct dirent *readdir(DIR *dirp); +int closedir(DIR *dirp); +int lstat(const char *path, struct stat *bufptr); +int ftruncate(int fd, word32 length); + + + +/* win32snd_driver.c */ +void win32snd_init(word32 *shmaddr); +void win32snd_shutdown(void); +void CALLBACK handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); +void check_wave_error(int res, char *str); +void child_sound_init_win32(void); +void win32snd_set_playing(int snd_playing); +void win32_send_audio2(byte *ptr, int size); +int win32_send_audio(byte *ptr, int in_size); + + diff --git a/gsplus/src/protos_xdriver.h b/gsplus/src/protos_xdriver.h new file mode 100644 index 0000000..120165e --- /dev/null +++ b/gsplus/src/protos_xdriver.h @@ -0,0 +1,49 @@ +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + +/* END_HDR */ + +/* xdriver.c */ +int main(int argc, char **argv); +int my_error_handler(Display *display, XErrorEvent *ev); +void xdriver_end(void); +void x_try_xset_r(void); +void x_badpipe(int signum); +int kegs_x_io_error_handler(Display *display); +int x_video_get_mdepth(void); +int x_try_find_visual(int depth, int screen_num); +void x_video_init(void); +void x_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str); +void x_create_window(Window_info *win_info_ptr); +int xhandle_shm_error(Display *display, XErrorEvent *event); +void x_allocate_window_data(Window_info *win_info_ptr); +void get_shm(Window_info *win_info_ptr, int width, int height); +void get_ximage(Window_info *win_info_ptr, int width, int height); +void x_set_size_hints(Window_info *win_info_ptr); +void x_resize_window(Window_info *win_info_ptr); +void x_update_display(Window_info *win_info_ptr); +Window_info *x_find_xwin(Window in_win); +void x_send_copy_data(Window_info *win_info_ptr); +void x_handle_copy(XSelectionRequestEvent *req_ev_ptr); +void x_handle_targets(XSelectionRequestEvent *req_ev_ptr); +void x_request_paste_data(Window_info *win_info_ptr); +void x_handle_paste(Window w, Atom property); +int x_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid); +void x_input_events(void); +void x_hide_pointer(Window_info *win_info_ptr, int do_hide); +void x_handle_keysym(XEvent *xev_in); +int x_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up); +void x_update_modifier_state(Window_info *win_info_ptr, int state); +void x_auto_repeat_on(int must); +void x_auto_repeat_off(int must); +void x_full_screen(int do_full); + + diff --git a/gsplus/src/pulseaudio_driver.c b/gsplus/src/pulseaudio_driver.c new file mode 100644 index 0000000..b999744 --- /dev/null +++ b/gsplus/src/pulseaudio_driver.c @@ -0,0 +1,365 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2020 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#ifdef PULSE_AUDIO +// Ignore entire file if PULSE_AUDIO is not defined! + +// Some ideas from Sample code: +// https://github.com/gavv/snippets/blob/master/pa/pa_play_async_poll.c + +#include "defc.h" +#include "sound.h" +#include "protos_pulseaudio_driver.h" + +#include +#include + +#define PULSE_REBUF_SIZE (64*1024) + +word32 g_pulseaudio_rebuf[PULSE_REBUF_SIZE]; +volatile int g_pulseaudio_rd_pos; +volatile int g_pulseaudio_wr_pos; +volatile int g_pulseaudio_playing = 0; + +extern int Verbose; + +extern int g_preferred_rate; +extern int g_audio_enable; +extern word32 *g_sound_shm_addr; +extern int g_sound_min_samples; +extern int g_sound_max_multiplier; +extern int g_sound_size; +extern word32 g_vbl_count; + +int g_call_num = 0; + +pa_mainloop *g_pa_mainloop_ptr = 0; +pa_context *g_pa_context_ptr = 0; +pa_stream *g_pa_stream_ptr = 0; +pa_sample_spec g_pa_sample_spec = { 0 }; +pa_buffer_attr g_pa_buffer_attr = { 0 }; + + +int +pulse_audio_send_audio(byte *ptr, int in_size) +{ + word32 *wptr, *pa_wptr; + int samps, sample_num; + int i; + + samps = in_size / 4; + wptr = (word32 *)ptr; + sample_num = g_pulseaudio_wr_pos; + pa_wptr = &(g_pulseaudio_rebuf[0]); + for(i = 0; i < samps; i++) { + pa_wptr[sample_num] = *wptr++; + sample_num++; + if(sample_num >= PULSE_REBUF_SIZE) { + sample_num = 0; + } + } + + g_pulseaudio_wr_pos = sample_num; + + pulse_audio_main_events(); + + return in_size; +} + +void +pulse_audio_main_events() +{ + pa_stream_state_t stream_state; + pa_context_state_t context_state; + int count, num, sz, do_write; + + count = 0; + do_write = 1; + while(1) { + // Do a few mainloop cycles to see if samples are needed + num = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0); + //printf("pa_mainloop_iterate ret:%d count:%d\n", num, count); + if(num < 0) { + return; + } + context_state = pa_context_get_state(g_pa_context_ptr); + if((context_state == PA_CONTEXT_FAILED) || + (context_state == PA_CONTEXT_TERMINATED)) { + printf("context_state is bad: %d\n", context_state); + g_audio_enable = 0; + return; + } + stream_state = pa_stream_get_state(g_pa_stream_ptr); + if((stream_state == PA_STREAM_FAILED) || + (stream_state == PA_STREAM_TERMINATED)) { + printf("stream state bad: %d\n", stream_state); + g_audio_enable = 0; + return; + } + if(do_write) { + sz = pa_stream_writable_size(g_pa_stream_ptr); + if(sz > 0) { + pulse_audio_write_to_stream(count, sz); + do_write = 0; + } + } + count++; + if(count > 50) { + printf("pulse_audio_main_events() looped %d times\n", + count); + return; + } + if(num == 0) { // Nothing else to do + return; + } + } +} + +void +pulse_audio_write_to_stream(int dbg_count, int in_sz) +{ + word32 *wptr; + void *vptr; + // const pa_timing_info *pa_timing_info_ptr; + // pa_usec_t pa_latency; + size_t sz; + int num_samps, sample_num, samps_avail, err, samps_needed; + int min_samples, max_samples; + int i; + + samps_needed = in_sz / 4; + sample_num = g_pulseaudio_rd_pos; + samps_avail = (g_pulseaudio_wr_pos - sample_num) & + (PULSE_REBUF_SIZE - 1); + min_samples = g_sound_min_samples; + max_samples = min_samples * g_sound_max_multiplier; + if(samps_needed > min_samples) { + min_samples = samps_needed; + } +#if 0 + if(samps_needed > samps_avail) { + // We don't have enough samples, must pause + g_pulseaudio_playing = 0; + sample_num = g_pulseaudio_wr_pos; // Eat remaining samps + } +#endif + if((g_pulseaudio_playing == 0) && (samps_avail > min_samples)) { + // We can unpause + g_pulseaudio_playing = 1; + } + if(g_pulseaudio_playing && (samps_avail > max_samples)) { + printf("JUMP SAMPLE_NUM by %d samples!\n", max_samples / 2); + sample_num += (max_samples / 2); + sample_num = sample_num & (PULSE_REBUF_SIZE - 1); + } + +#if 0 + if(g_call_num < 100) { + printf("call_num:%d playing:%d g_vbl_count:%d samps_needed:%d, " + "samps_avail:%d\n", g_call_num, g_pulseaudio_playing, + g_vbl_count, samps_needed, samps_avail); + } +#endif + g_call_num++; + num_samps = MIN(samps_avail, samps_needed); + if(g_pulseaudio_playing) { + vptr = 0; // Let it allocate for us + sz = num_samps * 4; + err = pa_stream_begin_write(g_pa_stream_ptr, &vptr, &sz); + wptr = vptr; + if(err) { + g_audio_enable = 0; + printf("pa_stream_begin_write failed: %s\n", + pa_strerror(err)); + return; + } + num_samps = sz / 4; + for(i = 0; i < num_samps; i++) { + wptr[i] = g_pulseaudio_rebuf[sample_num]; + sample_num++; + if(sample_num >= PULSE_REBUF_SIZE) { + sample_num = 0; + } + } + } else { + err = pa_stream_cancel_write(g_pa_stream_ptr); + // Just get out...don't let us get further behind by sending + // silence frames. + return; + } + + g_pulseaudio_rd_pos = sample_num; + +#if 0 + pa_timing_info_ptr = pa_stream_get_timing_info(g_pa_stream_ptr); + err = pa_stream_get_latency(g_pa_stream_ptr, &pa_latency, 0); + printf(" will send %d samples to the stream, write_index:%lld, " + "latency:%lld\n", num_samps * 4, + (word64)pa_timing_info_ptr->write_index, (word64)pa_latency); +#endif + err = pa_stream_write(g_pa_stream_ptr, wptr, num_samps * 4, 0, 0, + PA_SEEK_RELATIVE); + if(err) { + printf("pa_stream_write: %s\n", pa_strerror(err)); + g_audio_enable = 0; + } +} + +int +pulse_audio_start_stream() +{ + int flags, ret; + + g_pa_sample_spec.format = PA_SAMPLE_S16LE; + g_pa_sample_spec.rate = g_preferred_rate; + g_pa_sample_spec.channels = 2; + printf("Set requested rate=%d\n", g_pa_sample_spec.rate); + g_pa_stream_ptr = pa_stream_new(g_pa_context_ptr, "KEGS", + &g_pa_sample_spec, 0); + if(!g_pa_stream_ptr) { + printf("pa_stream_new failed\n"); + return 1; + } + + g_pa_buffer_attr.maxlength = -1; // Maximum server buffer + g_pa_buffer_attr.tlength = 4*g_preferred_rate/10; // 1/10th sec + //g_pa_buffer_attr.prebuf = 4*g_preferred_rate/100; // 1/100th sec + g_pa_buffer_attr.prebuf = -1; + g_pa_buffer_attr.minreq = 4*g_preferred_rate/60; // 1/60th sec + + flags = PA_STREAM_ADJUST_LATENCY; + //flags = PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | + // PA_STREAM_ADJUST_LATENCY; + // PA_STREAM_AUTO_TIMING_UPDATE and PA_STREAM_INTERPOLATE_TIMING are + // to get latency info from the server. PA_STREAM_ADJUST_LATENCY + // means the total latency including the server output sink buffer + // tries to be tlength + + ret = pa_stream_connect_playback(g_pa_stream_ptr, 0, &g_pa_buffer_attr, + flags, 0, 0); + if(ret) { + printf("pa_stream_connect_playback failed: %d\n", ret); + return ret; + } + + return 0; // Success! +} + +int +pulse_audio_do_init() +{ + pa_stream_state_t stream_state; + pa_context_state_t context_state; + int ret, count, num; + + g_pa_mainloop_ptr = pa_mainloop_new(); + g_pa_context_ptr = pa_context_new( + pa_mainloop_get_api(g_pa_mainloop_ptr), "KEGS"); + if(!g_pa_context_ptr) { + printf("Pulse Audio pa_context_new() failed\n"); + return 1; + } + + ret = pa_context_connect(g_pa_context_ptr, 0, 0, 0); + if(ret != 0) { + printf("pa_context_connect failed: %d\n", ret); + return 1; + } + + count = 0; + while(1) { + // Do a few mainloop cycles to get stream initialized + num = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0); +#if 0 + printf("pa_mainloop_iterate ret: %d, count:%d g_pa_stream_ptr:" + "%p\n", num, count, g_pa_stream_ptr); +#endif + if(num < 0) { + return 1; + } + if(num == 0) { + usleep(10*1000); + if(count++ > 50) { + // Waited more than 500ms, just give up + printf("Timed out waiting for Pulse Audio to " + "start\n"); + return 1; + } + } + // See if context is ready + context_state = pa_context_get_state(g_pa_context_ptr); + if((context_state == PA_CONTEXT_FAILED) || + (context_state == PA_CONTEXT_TERMINATED)) { + printf("context_state is bad: %d\n", context_state); + return 1; + } + if((context_state == PA_CONTEXT_READY) && !g_pa_stream_ptr) { + ret = pulse_audio_start_stream(); + if(ret) { + return ret; + } + } + if(g_pa_stream_ptr) { + stream_state = pa_stream_get_state(g_pa_stream_ptr); + if((stream_state == PA_STREAM_FAILED) || + (stream_state == PA_STREAM_TERMINATED)){ + printf("stream state bad: %d\n", stream_state); + return 1; + } + if(stream_state == PA_STREAM_READY) { + printf("Pulse Audio stream is now ready!\n"); + return 0; + } + } + } +} + +int +pulse_audio_init() +{ + int ret; + + g_pulseaudio_rd_pos = 0; + g_pulseaudio_wr_pos = 0; + + ret = pulse_audio_do_init(); + // printf("pulse_audio_init ret:%d\n", ret); + if(ret != 0) { + // Free structures, disable sound + if(g_pa_stream_ptr) { + pa_stream_disconnect(g_pa_stream_ptr); + pa_stream_unref(g_pa_stream_ptr); + g_pa_stream_ptr = 0; + } + if(g_pa_context_ptr) { + pa_context_disconnect(g_pa_context_ptr); + pa_context_unref(g_pa_context_ptr); + g_pa_context_ptr = 0; + } + if(g_pa_mainloop_ptr) { + pa_mainloop_free(g_pa_mainloop_ptr); + g_pa_mainloop_ptr = 0; + } + return ret; + } + + sound_set_audio_rate(g_preferred_rate); + + return 0; +} + +void +pulse_audio_shutdown() +{ + printf("pulse_audio_shutdown\n"); +} + +#endif /* PULSE_AUDIO */ diff --git a/gsplus/src/scc.c b/gsplus/src/scc.c new file mode 100644 index 0000000..4823ad2 --- /dev/null +++ b/gsplus/src/scc.c @@ -0,0 +1,1390 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Driver for the Zilog SCC Z8530, which implements two channels (A,B) of +// serial ports, controlled by $C038-$C03B + +#include "defc.h" + +extern int Verbose; +extern int g_code_yellow; +extern dword64 g_cur_dfcyc; +extern int g_serial_cfg[2]; +extern int g_serial_mask[2]; +extern char *g_serial_remote_ip[2]; +extern int g_serial_remote_port[2]; +extern char *g_serial_device[2]; +extern int g_serial_win_device[2]; +extern int g_irq_pending; + +/* scc port 0 == channel A = slot 1 = c039/c03b */ +/* port 1 == channel B = slot 2 = c038/c03a */ + +#include "scc.h" +#define SCC_R14_DPLL_SOURCE_BRG 0x100 +#define SCC_R14_DPLL_SOURCE_RTXC 0x200 + +#define SCC_DCYCS_PER_PCLK ((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8)) +#define SCC_DCYCS_PER_XTAL ((DCYCS_1_MHZ) / 3686400.0) + +// PCLK is 3.5795MHz + +#define SCC_BR_EVENT 1 +#define SCC_TX_EVENT 2 +#define SCC_RX_EVENT 3 +#define SCC_MAKE_EVENT(port, a) (((a) << 1) + (port)) + +Scc g_scc[2]; + +int g_baud_table[] = { + 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 +}; + +int g_scc_overflow = 0; +int g_scc_init = 0; + +// cur_state >= 0 and matches g_serial_cfg[port]: port is in that mode +// cur_state = -1: port is in no particular mode and should go to g_serial_cfg[] +// cur_state = -2: port failed to enter g_serial_cfg[], do not try again until +// something changes + +void +scc_init() +{ + Scc *scc_ptr; + int i, j; + + for(i = 0; i < 2; i++) { + scc_ptr = &(g_scc[i]); + memset(scc_ptr, 0, sizeof(*scc_ptr)); + scc_ptr->cur_state = -1; + scc_ptr->modem_state = 0; + scc_ptr->sockfd = INVALID_SOCKET; + scc_ptr->rdwrfd = INVALID_SOCKET; + scc_ptr->sockaddr_ptr = 0; + scc_ptr->sockaddr_size = 0; + scc_ptr->unix_dev_fd = -1; + scc_ptr->win_com_handle = 0; + scc_ptr->win_dcb_ptr = 0; + scc_ptr->br_event_pending = 0; + scc_ptr->rx_event_pending = 0; + scc_ptr->tx_event_pending = 0; + scc_ptr->char_size = 8; + scc_ptr->baud_rate = 9600; + scc_ptr->telnet_mode = 0; + scc_ptr->telnet_iac = 0; + scc_ptr->out_char_dfcyc = 0; + scc_ptr->socket_error = 0; + scc_ptr->socket_num_rings = 0; + scc_ptr->socket_last_ring_dfcyc = 0; + scc_ptr->modem_mode = 0; + scc_ptr->modem_plus_mode = 0; + scc_ptr->modem_s0_val = 0; + scc_ptr->modem_s2_val = '+'; + scc_ptr->modem_cmd_len = 0; + scc_ptr->modem_out_portnum = 23; + scc_ptr->modem_cmd_str[0] = 0; + for(j = 0; j < 2; j++) { + scc_ptr->telnet_local_mode[j] = 0; + scc_ptr->telnet_remote_mode[j] = 0; + scc_ptr->telnet_reqwill_mode[j] = 0; + scc_ptr->telnet_reqdo_mode[j] = 0; + } + } + + g_scc_init = 1; +} + +void +scc_reset() +{ + Scc *scc_ptr; + int i; + + if(!g_scc_init) { + halt_printf("scc_reset called before init\n"); + return; + } + for(i = 0; i < 2; i++) { + scc_ptr = &(g_scc[i]); + + scc_ptr->mode = 0; + scc_ptr->reg_ptr = 0; + scc_ptr->in_rdptr = 0; + scc_ptr->in_wrptr = 0; + scc_ptr->out_rdptr = 0; + scc_ptr->out_wrptr = 0; + scc_ptr->dcd = 1 - i; // 1 for slot 1, 0 for slot 2 + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + scc_ptr->read_called_this_vbl = 0; + scc_ptr->write_called_this_vbl = 0; + scc_evaluate_ints(i); + scc_hard_reset_port(i); + } +} + +void +scc_hard_reset_port(int port) +{ + Scc *scc_ptr; + + scc_reset_port(port); + + scc_ptr = &(g_scc[port]); + scc_ptr->reg[14] = 0; /* zero bottom two bits */ + scc_ptr->reg[13] = 0; + scc_ptr->reg[12] = 0; + scc_ptr->reg[11] = 0x08; + scc_ptr->reg[10] = 0; + scc_ptr->reg[7] = 0; + scc_ptr->reg[6] = 0; + scc_ptr->reg[5] = 0; + scc_ptr->reg[4] = 0x04; + scc_ptr->reg[3] = 0; + scc_ptr->reg[2] = 0; + scc_ptr->reg[1] = 0; + + /* HACK HACK: */ + g_scc[0].reg[9] = 0; /* Clear all interrupts */ + + scc_evaluate_ints(port); + + scc_regen_clocks(port); +} + +void +scc_reset_port(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(g_scc[port]); + scc_ptr->reg[15] = 0xf8; + scc_ptr->reg[14] &= 0x03; /* 0 most (including >= 0x100) bits */ + scc_ptr->reg[10] = 0; + scc_ptr->reg[5] &= 0x65; /* leave tx bits and sdlc/crc bits */ + scc_ptr->reg[4] |= 0x04; /* Set async mode */ + scc_ptr->reg[3] &= 0xfe; /* clear receiver enable */ + scc_ptr->reg[1] &= 0xfe; /* clear ext int enable */ + + scc_ptr->br_is_zero = 0; + scc_ptr->tx_buf_empty = 1; + + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + + scc_ptr->rx_queue_depth = 0; + + scc_evaluate_ints(port); + + scc_regen_clocks(port); + + scc_clr_tx_int(port); + scc_clr_rx_int(port); +} + +void +scc_regen_clocks(int port) +{ + Scc *scc_ptr; + double br_dcycs, tx_dcycs, rx_dcycs, rx_char_size, tx_char_size; + double clock_mult, dpll_dcycs; + word32 reg4, reg11, reg14, br_const, max_diff, diff; + int baud, cur_state, baud_entries, pos; + int i; + + /* Always do baud rate generator */ + scc_ptr = &(g_scc[port]); + br_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12]; + br_const += 2; /* counts down past 0 */ + + reg4 = scc_ptr->reg[4]; // Transmit/Receive misc params + clock_mult = 1.0; + switch((reg4 >> 6) & 3) { + case 0: /* x1 */ + clock_mult = 1.0; + break; + case 1: /* x16 */ + clock_mult = 16.0; + break; + case 2: /* x32 */ + clock_mult = 32.0; + break; + case 3: /* x64 */ + clock_mult = 64.0; + break; + } + + br_dcycs = 0.01; + reg14 = scc_ptr->reg[14]; + if(reg14 & 0x1) { + br_dcycs = SCC_DCYCS_PER_XTAL; + if(reg14 & 0x2) { + br_dcycs = SCC_DCYCS_PER_PCLK; + } + } + + br_dcycs = br_dcycs * (double)br_const * 2.0; + + dpll_dcycs = 0.1; + if(reg14 & SCC_R14_DPLL_SOURCE_BRG) { + dpll_dcycs = br_dcycs; + } else if(reg14 & SCC_R14_DPLL_SOURCE_RTXC) { + dpll_dcycs = SCC_DCYCS_PER_XTAL; + } + + tx_dcycs = 1; + reg11 = scc_ptr->reg[11]; + switch((reg11 >> 3) & 3) { + case 0: // /RTxC pin + tx_dcycs = SCC_DCYCS_PER_XTAL; + break; + case 2: // BR generator output + tx_dcycs = br_dcycs; + break; + case 3: // DPLL output + tx_dcycs = dpll_dcycs; + break; + } + + tx_dcycs = tx_dcycs * clock_mult; + + rx_dcycs = 1; + switch((reg11 >> 5) & 3) { + case 0: // /RTxC pin + rx_dcycs = SCC_DCYCS_PER_XTAL; + break; + case 2: // BR generator output + rx_dcycs = br_dcycs; + break; + case 3: // DPLL output + rx_dcycs = dpll_dcycs; + break; + } + rx_dcycs = rx_dcycs * clock_mult; + + tx_char_size = 8.0; + switch((scc_ptr->reg[5] >> 5) & 0x3) { + case 0: // 5 bits + tx_char_size = 5.0; + break; + case 1: // 7 bits + tx_char_size = 7.0; + break; + case 2: // 6 bits + tx_char_size = 6.0; + break; + } + + scc_ptr->char_size = (int)tx_char_size; + + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 1: // 1 stop bit + tx_char_size += 2.0; // 1 stop + 1 start bit + break; + case 2: // 1.5 stop bit + tx_char_size += 2.5; // 1.5 stop + 1 start bit + break; + case 3: // 2 stop bits + tx_char_size += 3.0; // 2.0 stop + 1 start bit + break; + } + + if(scc_ptr->reg[4] & 1) { + // parity enabled + tx_char_size += 1.0; + } + + if(scc_ptr->reg[14] & 0x10) { + /* loopback mode, make it go faster...*/ + rx_char_size = 1.0; + tx_char_size = 1.0; + } + + rx_char_size = tx_char_size; /* HACK */ + + baud = (int)(DCYCS_1_MHZ / tx_dcycs); + max_diff = 5000000; + pos = 0; + baud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]); + for(i = 0; i < baud_entries; i++) { + diff = abs(g_baud_table[i] - baud); + if(diff < max_diff) { + pos = i; + max_diff = diff; + } + } + + scc_ptr->baud_rate = g_baud_table[pos]; + + scc_ptr->br_dcycs = br_dcycs; + scc_ptr->tx_dcycs = tx_dcycs * tx_char_size; + scc_ptr->rx_dcycs = rx_dcycs * rx_char_size; + + cur_state = scc_ptr->cur_state; + if(cur_state == 0) { // real serial ports +#ifdef _WIN32 + scc_serial_win_change_params(port); +#else + scc_serial_unix_change_params(port); +#endif + } +} + +void +scc_port_close(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(g_scc[port]); + +#ifdef _WIN32 + scc_serial_win_close(port); +#else + scc_serial_unix_close(port); +#endif + scc_socket_close(port); + + scc_ptr->cur_state = -1; // Nothing open +} + +void +scc_port_open(dword64 dfcyc, int port) +{ + int cfg; + + cfg = g_serial_cfg[port]; + printf("scc_port_open port:%d cfg:%d\n", port, cfg); + + if(cfg == 0) { // Real host serial port +#ifdef _WIN32 + scc_serial_win_open(port); +#else + scc_serial_unix_open(port); +#endif + } else if(cfg >= 1) { + scc_socket_open(dfcyc, port, cfg); + } + printf(" open socketfd:%ld\n", (long)g_scc[port].sockfd); +} + +int +scc_is_port_closed(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + + // Returns 1 is the port is closed (not working). Returns 0 + // if the port is open. Tries to open the port if it is not in error + scc_ptr = &(g_scc[port]); + if(scc_ptr->cur_state == -1) { + scc_port_open(dfcyc, port); + } + if(scc_ptr->cur_state < 0) { + scc_ptr->cur_state = -2; + //printf("scc_is_port_closed p:%d returning 0\n", port); + return 1; // Not open + } + return 0; // Port is open! +} + +char * +scc_get_serial_status(int get_status, int port) +{ + char buf[80]; + char *str; + int cur_state; + + if(get_status == 0) { + return 0; + } + cur_state = g_scc[port].cur_state; + str = ""; + switch(cur_state) { + case -1: + str = "Not initialized yet"; + break; + case 0: + snprintf(buf, 80, "Opened %s OK", g_serial_device[port]); + str = buf; + break; + case 1: + snprintf(buf, 80, "Virtual modem, sockfd:%d", + (int)g_scc[port].sockfd); + str = buf; + break; + case 2: + snprintf(buf, 80, "Outgoing to %s:%d", + g_serial_remote_ip[port], g_serial_remote_port[port]); + str = buf; + break; + case 3: + snprintf(buf, 80, "Opened %d, sockfd:%d", 6501 + port, + (int)g_scc[port].sockfd); + str = buf; + break; + default: + str = "Open failed, port is closed"; + } + return kegs_malloc_str(str); +} + + +void +scc_config_changed(int port, int cfg_changed, int remote_changed, + int serial_dev_changed) +{ + Scc *scc_ptr; + int must_change; + + // Check if scc_init() was called, if not get out + if(!g_scc_init) { + return; + } + + // F4 may have changed the serial port config. If so, close old + // port, open new one. + + scc_ptr = &(g_scc[port]); + must_change = cfg_changed; + switch(scc_ptr->cur_state) { + case 0: // Using serial port + must_change |= serial_dev_changed; + break; + case 2: // Using remote connection + must_change |= remote_changed; + break; + } + if(must_change) { + scc_port_close(port); + } +} + +void +scc_update(dword64 dfcyc) +{ + int i; + + // called each VBL update + for(i = 0; i < 2; i++) { + g_scc[i].write_called_this_vbl = 0; + g_scc[i].read_called_this_vbl = 0; + + // These calls will try to open the port if it's closed + scc_try_to_empty_writebuf(dfcyc, i); + scc_try_fill_readbuf(dfcyc, i); + + g_scc[i].write_called_this_vbl = 0; + g_scc[i].read_called_this_vbl = 0; + } +} + +void +scc_try_to_empty_writebuf(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + int cur_state; + + scc_ptr = &(g_scc[port]); + cur_state = scc_ptr->cur_state; + if(scc_ptr->write_called_this_vbl) { + return; + } + if(scc_is_port_closed(dfcyc, port)) { + return; // Port is not open + } + + scc_ptr->write_called_this_vbl = 1; + + if(cur_state == 0) { +#if defined(_WIN32) + scc_serial_win_empty_writebuf(port); +#else + scc_serial_unix_empty_writebuf(port); +#endif + } else if(cur_state >= 1) { + scc_socket_empty_writebuf(dfcyc, port); + } +} + +void +scc_try_fill_readbuf(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + int space_used, space_left, cur_state; + + scc_ptr = &(g_scc[port]); + + space_used = scc_ptr->in_wrptr - scc_ptr->in_rdptr; + if(space_used < 0) { + space_used += SCC_INBUF_SIZE; + } + space_left = (7*SCC_INBUF_SIZE/8) - space_used; + if(space_left < 1) { + /* Buffer is pretty full, don't try to get more */ + return; + } + + if(scc_is_port_closed(dfcyc, port)) { + return; // Port is not open + } + +#if 0 + if(scc_ptr->read_called_this_vbl) { + return; + } +#endif + + scc_ptr->read_called_this_vbl = 1; + + cur_state = scc_ptr->cur_state; + if(cur_state == 0) { +#if defined(_WIN32) + scc_serial_win_fill_readbuf(dfcyc, port, space_left); +#else + scc_serial_unix_fill_readbuf(dfcyc, port, space_left); +#endif + } else if(cur_state >= 1) { + scc_socket_fill_readbuf(dfcyc, port, space_left); + } +} + +void +scc_do_event(dword64 dfcyc, int type) +{ + Scc *scc_ptr; + int port; + + port = type & 1; + type = (type >> 1); + + scc_ptr = &(g_scc[port]); + if(type == SCC_BR_EVENT) { + /* baud rate generator counted down to 0 */ + scc_ptr->br_event_pending = 0; + scc_set_zerocnt_int(port); + scc_maybe_br_event(dfcyc, port); + } else if(type == SCC_TX_EVENT) { + scc_ptr->tx_event_pending = 0; + scc_ptr->tx_buf_empty = 1; + scc_handle_tx_event(port); + } else if(type == SCC_RX_EVENT) { + scc_ptr->rx_event_pending = 0; + scc_maybe_rx_event(dfcyc, port); + scc_maybe_rx_int(port); + } else { + halt_printf("scc_do_event: %08x!\n", type); + } + return; +} + +void +show_scc_state() +{ + Scc *scc_ptr; + int i, j; + + for(i = 0; i < 2; i++) { + scc_ptr = &(g_scc[i]); + printf("SCC port: %d\n", i); + for(j = 0; j < 16; j += 4) { + printf("Reg %2d-%2d: %02x %02x %02x %02x\n", j, j+3, + scc_ptr->reg[j], scc_ptr->reg[j+1], + scc_ptr->reg[j+2], scc_ptr->reg[j+3]); + } + printf("state: %d, sockfd:%llx rdwrfd:%llx, win_com:%p, " + "win_dcb:%p\n", scc_ptr->cur_state, + (dword64)scc_ptr->sockfd, (dword64)scc_ptr->rdwrfd, + scc_ptr->win_com_handle, scc_ptr->win_dcb_ptr); + printf("in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\n", + scc_ptr->in_rdptr, scc_ptr->in_wrptr, + scc_ptr->out_rdptr, scc_ptr->out_wrptr); + printf("rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\n", + scc_ptr->rx_queue_depth, scc_ptr->rx_queue[0], + scc_ptr->rx_queue[1], scc_ptr->rx_queue[2], + scc_ptr->rx_queue[3]); + printf("want_ints: rx:%d, tx:%d, zc:%d\n", + scc_ptr->wantint_rx, scc_ptr->wantint_tx, + scc_ptr->wantint_zerocnt); + printf("ev_pendings: rx:%d, tx:%d, br:%d\n", + scc_ptr->rx_event_pending, + scc_ptr->tx_event_pending, + scc_ptr->br_event_pending); + printf("br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\n", + scc_ptr->br_dcycs, scc_ptr->tx_dcycs, + scc_ptr->rx_dcycs); + printf("char_size: %d, baud_rate: %d, mode: %d\n", + scc_ptr->char_size, scc_ptr->baud_rate, + scc_ptr->mode); + printf("modem_state: %dtelnet_mode:%d iac:%d, " + "modem_cmd_len:%d\n", scc_ptr->modem_state, + scc_ptr->telnet_mode, scc_ptr->telnet_iac, + scc_ptr->modem_cmd_len); + printf("telnet_loc_modes:%08x %08x, telnet_rem_motes:" + "%08x %08x\n", scc_ptr->telnet_local_mode[0], + scc_ptr->telnet_local_mode[1], + scc_ptr->telnet_remote_mode[0], + scc_ptr->telnet_remote_mode[1]); + printf("modem_mode:%08x plus_mode:%d, out_char_dfcyc:%016llx\n", + scc_ptr->modem_mode, scc_ptr->modem_plus_mode, + scc_ptr->out_char_dfcyc); + } + +} + +word32 +scc_read_reg(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + word32 ret; + int regnum; + + scc_ptr = &(g_scc[port]); + scc_ptr->mode = 0; + regnum = scc_ptr->reg_ptr; + + /* port 0 is channel A, port 1 is channel B */ + switch(regnum) { + case 0: + case 4: + ret = 0x60; /* 0x44 = no dcd, no cts,0x6c = dcd ok, cts ok*/ + if(scc_ptr->dcd) { + ret |= 0x08; + } + //ret |= 0x8; /* HACK HACK */ + if(scc_ptr->rx_queue_depth) { + ret |= 0x01; + } + if(scc_ptr->tx_buf_empty) { + ret |= 0x04; + } + if(scc_ptr->br_is_zero) { + ret |= 0x02; + } + //printf("Read scc[%d] stat: %016llx : %02x dcd:%d\n", port, + // dfcyc, ret, scc_ptr->dcd); + break; + case 1: + case 5: + /* HACK: residue codes not right */ + ret = 0x07; /* all sent */ + break; + case 2: + case 6: + if(port == 0) { + ret = scc_ptr->reg[2]; + } else { // Port B, read RR2B int stat + // The TELNET.SYSTEM by Colin Leroy-Mira uses RR2B + ret = scc_do_read_rr2b() << 1; + if(g_scc[0].reg[9] & 0x10) { // wr9 status high + // Map bit 3->4, 2->5, 1->6 + ret = ((ret << 1) & 0x10) | + ((ret << 3) & 0x20) | + ((ret << 5) & 0x40); + } + } + break; + case 3: + case 7: + if(port == 0) { + ret = (g_irq_pending & 0x3f); + } else { + ret = 0; + } + break; + case 8: + ret = scc_read_data(dfcyc, port); + break; + case 9: + case 13: + ret = scc_ptr->reg[13]; + break; + case 10: + case 14: + ret = 0; + break; + case 11: + case 15: + ret = scc_ptr->reg[15]; + break; + case 12: + ret = scc_ptr->reg[12]; + break; + default: + halt_printf("Tried reading c03%x with regnum: %d!\n", 8+port, + regnum); + ret = 0; + } + + scc_ptr->reg_ptr = 0; + scc_printf("Read c03%x, rr%d, ret: %02x\n", 8+port, regnum, ret); + dbg_log_info(dfcyc, 0, ret, (regnum << 20) | (0xc039 - port)); + + return ret; +} + +void +scc_write_reg(dword64 dfcyc, int port, word32 val) +{ + Scc *scc_ptr; + word32 old_val, changed_bits, irq_mask; + int regnum, mode, tmp1; + + scc_ptr = &(g_scc[port]); + regnum = scc_ptr->reg_ptr & 0xf; + mode = scc_ptr->mode; + + if(mode == 0) { + if((val & 0xf0) == 0) { + /* Set reg_ptr */ + scc_ptr->reg_ptr = val & 0xf; + regnum = 0; + scc_ptr->mode = 1; + } + } else { + scc_ptr->reg_ptr = 0; + scc_ptr->mode = 0; + } + old_val = scc_ptr->reg[regnum]; + changed_bits = (old_val ^ val) & 0xff; + + dbg_log_info(dfcyc, (mode << 16) | scc_ptr->reg_ptr, + (changed_bits << 16) | val, + (regnum << 20) | (0x1c039 - port)); + + /* Set reg reg */ + switch(regnum) { + case 0: /* wr0 */ + tmp1 = (val >> 3) & 0x7; + switch(tmp1) { + case 0x0: + case 0x1: + break; + case 0x2: /* reset ext/status ints */ + /* should clear other ext ints */ + scc_clr_zerocnt_int(port); + break; + case 0x5: /* reset tx int pending */ + scc_clr_tx_int(port); + break; + case 0x6: /* reset rr1 bits */ + break; + case 0x7: /* reset highest pri int pending */ + irq_mask = g_irq_pending; + if(port == 0) { + /* Move SCC0 ints into SCC1 positions */ + irq_mask = irq_mask >> 3; + } + if(irq_mask & IRQ_PENDING_SCC1_RX) { + scc_clr_rx_int(port); + } else if(irq_mask & IRQ_PENDING_SCC1_TX) { + scc_clr_tx_int(port); + } else if(irq_mask & IRQ_PENDING_SCC1_ZEROCNT) { + scc_clr_zerocnt_int(port); + } + break; + case 0x4: /* enable int on next rx char */ + default: + halt_printf("Wr c03%x to wr0 of %02x, bad cmd cd:%x!\n", + 9-port, val, tmp1); + } + tmp1 = (val >> 6) & 0x3; + switch(tmp1) { + case 0x0: /* null code */ + break; + case 0x1: /* reset rx crc */ + case 0x2: /* reset tx crc */ + printf("Wr c03%x to wr0 of %02x!\n", 9-port, val); + break; + case 0x3: /* reset tx underrun/eom latch */ + /* if no extern status pending, or being reset now */ + /* and tx disabled, ext int with tx underrun */ + /* ah, just do nothing */ + break; + } + return; + case 1: /* wr1 */ + /* proterm sets this == 0x10, which is int on all rx */ + scc_ptr->reg[regnum] = val; + return; + case 2: /* wr2 */ + /* All values do nothing, let 'em all through! */ + scc_ptr->reg[regnum] = val; + return; + case 3: /* wr3 */ + if((val & 0x1e) != 0x0) { + halt_printf("Wr c03%x to wr3 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 4: /* wr4 */ + if((val & 0x30) != 0x00 || (val & 0x0c) == 0) { + halt_printf("Wr c03%x to wr4 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 5: /* wr5 */ + if((val & 0x15) != 0x0) { + halt_printf("Wr c03%x to wr5 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits & 0x60) { + scc_regen_clocks(port); + } + if(changed_bits & 0x80) { + scc_printf("SCC port %d DTR:%d\n", port, val & 0x80); + } + return; + case 6: /* wr6 */ + if(val != 0) { + halt_printf("Wr c03%x to wr6 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 7: /* wr7 */ + if(val != 0) { + halt_printf("Wr c03%x to wr7 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 8: /* wr8 */ + scc_write_data(dfcyc, port, val); + return; + case 9: /* wr9 */ + if((val & 0xc0)) { + if(val & 0x80) { + scc_reset_port(0); + } + if(val & 0x40) { + scc_reset_port(1); + } + if((val & 0xc0) == 0xc0) { + scc_hard_reset_port(0); + scc_hard_reset_port(1); + } + } + // Bit 5 is software interrupt ack, which does not exist on NMOS + // Bit 2 sets IEO pin low, which doesn't exist either + old_val = g_scc[0].reg[9]; + g_scc[0].reg[9] = val; + scc_evaluate_ints(0); + scc_evaluate_ints(1); + return; + case 10: /* wr10 */ + if((val & 0xff) != 0x00) { + printf("Wr c03%x to wr10 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 11: /* wr11 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 12: /* wr12 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 13: /* wr13 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 14: /* wr14 */ + val = val | (old_val & (~0xffU)); + switch((val >> 5) & 0x7) { + case 0x0: // Null command (change nothing) + break; + case 0x1: // Enter search mode + case 0x2: // Reset missing clock + case 0x3: // Disable PLL + case 0x6: // Set FM mode + case 0x7: // Set NRZI mode + // Disable the PLL effectively + val = val & 0xff; // Clear all upper bits + break; + case 0x4: // DPLL source is BR gen + val = (val & 0xff) | SCC_R14_DPLL_SOURCE_BRG; + break; + case 0x5: // DPLL source is RTxC + val = (val & 0xff) | SCC_R14_DPLL_SOURCE_RTXC; + break; + default: + halt_printf("Wr c03%x to wr14 of %02x, bad dpll cd!\n", + 8+port, val); + } + if((val & 0x0c) != 0x0) { + halt_printf("Wr c03%x to wr14 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits || (val != old_val)) { + scc_regen_clocks(port); + } + scc_maybe_br_event(dfcyc, port); + return; + case 15: /* wr15 */ + /* ignore all accesses since IIgs self test messes with it */ + if((val & 0xff) != 0x0) { + scc_printf("Write c03%x to wr15 of %02x!\n", 8+port, + val); + } + if((g_scc[0].reg[9] & 0x8) && (val != 0)) { + printf("Write wr15:%02x and master int en = 1!\n",val); + /* set_halt(1); */ + } + scc_ptr->reg[regnum] = val; + scc_maybe_br_event(dfcyc, port); + scc_evaluate_ints(port); + return; + default: + halt_printf("Wr c03%x to wr%d of %02x!\n", 8+port, regnum, val); + return; + } +} + +// scc_read_data: Read from 0xc03b or 0xc03a +word32 +scc_read_data(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + word32 ret; + int depth; + int i; + + scc_ptr = &(g_scc[port]); + + scc_try_fill_readbuf(dfcyc, port); + + depth = scc_ptr->rx_queue_depth; + + ret = 0; + if(depth != 0) { + ret = scc_ptr->rx_queue[0]; + for(i = 1; i < depth; i++) { + scc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i]; + } + scc_ptr->rx_queue_depth = depth - 1; + scc_maybe_rx_event(dfcyc, port); + scc_maybe_rx_int(port); + } + + scc_printf("SCC read %04x: ret %02x, depth:%d\n", 0xc03b-port, ret, + depth); + + dbg_log_info(dfcyc, 0, ret, 0xc03b - port); + + return ret; +} + +void +scc_write_data(dword64 dfcyc, int port, word32 val) +{ + Scc *scc_ptr; + + scc_printf("SCC write %04x: %02x\n", 0xc03b-port, val); + dbg_log_info(dfcyc, val, 0, 0x1c03b - port); + + scc_ptr = &(g_scc[port]); + if(scc_ptr->reg[14] & 0x10) { + /* local loopback! */ + scc_add_to_readbuf(dfcyc, port, val); + } else { + scc_transmit(dfcyc, port, val); + } + scc_try_to_empty_writebuf(dfcyc, port); + + scc_maybe_tx_event(dfcyc, port); +} + +word32 +scc_do_read_rr2b() +{ + word32 val; + + val = g_irq_pending & 0x3f; + if(val == 0) { + return 3; // 011 if no interrupts pending + } + // Do Channel A first. Priority order from SCC documentation + if(val & IRQ_PENDING_SCC0_RX) { + return 6; // 110 Ch A Rx char available + } + if(val & IRQ_PENDING_SCC0_TX) { + return 4; // 100 Ch A Tx buffer empty + } + if(val & IRQ_PENDING_SCC0_ZEROCNT) { + return 5; // 101 Ch A External/Status change + } + if(val & IRQ_PENDING_SCC1_RX) { + return 2; // 010 Ch B Rx char available + } + if(val & IRQ_PENDING_SCC1_TX) { + return 0; // 000 Ch B Tx buffer empty + } + if(val & IRQ_PENDING_SCC1_ZEROCNT) { + return 1; // 001 Ch B External/Status change + } + + return 3; +} + +void +scc_maybe_br_event(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + double br_dcycs; + + scc_ptr = &(g_scc[port]); + + if(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) { + return; + } + /* also, if ext ints not enabled, don't do baud rate ints */ + if(((scc_ptr->reg[15] & 0x02) == 0) || ((scc_ptr->reg[9] & 8) == 0)) { + return; + } + + br_dcycs = scc_ptr->br_dcycs; + if(br_dcycs < 1.0) { + halt_printf("br_dcycs: %f!\n", br_dcycs); +#if 0 + printf("br_dcycs: %f!\n", br_dcycs); + dbg_log_info(dfcyc, (word32)(br_dcycs * 65536), + (scc_ptr->reg[15] << 24) | (scc_ptr->reg[14] << 16) | + (scc_ptr->reg[13] << 8) | scc_ptr->reg[12], 0xdc1c); + dbg_log_info(dfcyc, + (scc_ptr->reg[11] << 24) | (scc_ptr->reg[10] << 16) | + (scc_ptr->reg[9] << 8) | scc_ptr->reg[5], + (scc_ptr->reg[4] << 24) | (scc_ptr->reg[3] << 16) | + (scc_ptr->reg[1] << 8) | scc_ptr->reg[0], 0xdc1b); +#endif + } + + scc_ptr->br_event_pending = 1; + add_event_scc(dfcyc + (dword64)(br_dcycs * 65536.0), + SCC_MAKE_EVENT(port, SCC_BR_EVENT)); +} + +void +scc_evaluate_ints(int port) +{ + Scc *scc_ptr; + word32 irq_add_mask, irq_remove_mask; + int mie; + + scc_ptr = &(g_scc[port]); + mie = g_scc[0].reg[9] & 0x8; /* Master int en */ + + if(!mie) { + /* There can be no interrupts if MIE=0 */ + remove_irq(IRQ_PENDING_SCC1_RX | IRQ_PENDING_SCC1_TX | + IRQ_PENDING_SCC1_ZEROCNT | + IRQ_PENDING_SCC0_RX | IRQ_PENDING_SCC0_TX | + IRQ_PENDING_SCC0_ZEROCNT); + return; + } + + irq_add_mask = 0; + irq_remove_mask = 0; + if(scc_ptr->wantint_rx) { + irq_add_mask |= IRQ_PENDING_SCC1_RX; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_RX; + } + if(scc_ptr->wantint_tx) { + irq_add_mask |= IRQ_PENDING_SCC1_TX; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_TX; + } + if(scc_ptr->wantint_zerocnt) { + irq_add_mask |= IRQ_PENDING_SCC1_ZEROCNT; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_ZEROCNT; + } + if(port == 0) { + /* Port 1 is in bits 0-2 and port 0 is in bits 3-5 */ + irq_add_mask = irq_add_mask << 3; + irq_remove_mask = irq_remove_mask << 3; + } + if(irq_add_mask) { + add_irq(irq_add_mask); + } + if(irq_remove_mask) { + remove_irq(irq_remove_mask); + } +} + +void +scc_maybe_rx_event(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + double rx_dcycs; + int in_rdptr, in_wrptr, depth; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->rx_event_pending) { + /* one pending already, wait for the event to arrive */ + return; + } + + in_rdptr = scc_ptr->in_rdptr; + in_wrptr = scc_ptr->in_wrptr; + depth = scc_ptr->rx_queue_depth; + if((in_rdptr == in_wrptr) || (depth >= 3)) { + /* no more chars or no more space, just get out */ + return; + } + + if(depth < 0) { + depth = 0; + } + + /* pull char from in_rdptr into queue */ + scc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr]; + scc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1); + scc_ptr->rx_queue_depth = depth + 1; + scc_maybe_rx_int(port); + rx_dcycs = scc_ptr->rx_dcycs; + scc_ptr->rx_event_pending = 1; + add_event_scc(dfcyc + (dword64)(rx_dcycs*65536.0), + SCC_MAKE_EVENT(port, SCC_RX_EVENT)); +} + +void +scc_maybe_rx_int(int port) +{ + Scc *scc_ptr; + int depth; + int rx_int_mode; + + scc_ptr = &(g_scc[port]); + + depth = scc_ptr->rx_queue_depth; + if(depth <= 0) { + /* no more chars, just get out */ + scc_clr_rx_int(port); + return; + } + rx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3; + if((rx_int_mode == 1) || (rx_int_mode == 2)) { + scc_ptr->wantint_rx = 1; + } + scc_evaluate_ints(port); +} + +void +scc_clr_rx_int(int port) +{ + g_scc[port].wantint_rx = 0; + scc_evaluate_ints(port); +} + +void +scc_handle_tx_event(int port) +{ + Scc *scc_ptr; + int tx_int_mode; + + scc_ptr = &(g_scc[port]); + + /* nothing pending, see if ints on */ + tx_int_mode = (scc_ptr->reg[1] & 0x2); + if(tx_int_mode) { + scc_ptr->wantint_tx = 1; + } + scc_evaluate_ints(port); +} + +void +scc_maybe_tx_event(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + double tx_dcycs; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->tx_event_pending) { + /* one pending already, tx_buf is full */ + scc_ptr->tx_buf_empty = 0; + } else { + /* nothing pending, see ints on */ + scc_ptr->tx_buf_empty = 0; + scc_evaluate_ints(port); + tx_dcycs = scc_ptr->tx_dcycs; + scc_ptr->tx_event_pending = 1; + add_event_scc(dfcyc + (dword64)(tx_dcycs * 65536.0), + SCC_MAKE_EVENT(port, SCC_TX_EVENT)); + } +} + +void +scc_clr_tx_int(int port) +{ + g_scc[port].wantint_tx = 0; + scc_evaluate_ints(port); +} + +void +scc_set_zerocnt_int(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->reg[15] & 0x2) { + scc_ptr->wantint_zerocnt = 1; + } + scc_evaluate_ints(port); +} + +void +scc_clr_zerocnt_int(int port) +{ + g_scc[port].wantint_zerocnt = 0; + scc_evaluate_ints(port); +} + +void +scc_add_to_readbuf(dword64 dfcyc, int port, word32 val) +{ + Scc *scc_ptr; + int in_wrptr, in_wrptr_next, in_rdptr, safe_val; + + scc_ptr = &(g_scc[port]); + + if((scc_ptr->reg[5] & 0x60) != 0x60) { // HACK: this is tx char size! + val = val & 0x7f; + } + in_wrptr = scc_ptr->in_wrptr; + in_rdptr = scc_ptr->in_rdptr; + in_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1); + if(in_wrptr_next != in_rdptr) { + scc_ptr->in_buf[in_wrptr] = val; + scc_ptr->in_wrptr = in_wrptr_next; + safe_val = val & 0x7f; + if((safe_val < 0x20) || (safe_val >= 0x7f)) { + safe_val = '.'; + } + scc_printf("scc in port[%d] add 0x%02x (%c), %d,%d != %d\n", + port, val, safe_val, in_wrptr, in_wrptr_next, in_rdptr); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc inbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } + + scc_maybe_rx_event(dfcyc, port); + scc_maybe_rx_int(port); +} + +void +scc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...) +{ + va_list ap; + char *bufptr; + int len, c; + int i; + + va_start(ap, fmt); + bufptr = malloc(4096); + bufptr[0] = 0; + vsnprintf(bufptr, 4090, fmt, ap); + len = (int)strlen(bufptr); + for(i = 0; i < len; i++) { + c = bufptr[i]; + if(c == 0x0a) { + scc_add_to_readbuf(dfcyc, port, 0x0d); + } + scc_add_to_readbuf(dfcyc, port, c); + } + va_end(ap); +} + +void +scc_transmit(dword64 dfcyc, int port, word32 val) +{ + Scc *scc_ptr; + int out_wrptr, out_rdptr; + + scc_ptr = &(g_scc[port]); + + // printf("scc_transmit port:%d val:%02x \n", port, val); + /* See if port initialized, if not, do so now */ + if(scc_is_port_closed(dfcyc, port)) { + printf(" port %d is closed, cur_state:%d\n", port, + scc_ptr->cur_state); + return; // No working serial port, just toss it and go + } + if(!scc_ptr->tx_buf_empty) { + /* toss character! */ + printf("Tossing char\n"); + return; + } + + out_wrptr = scc_ptr->out_wrptr; + out_rdptr = scc_ptr->out_rdptr; + if(scc_ptr->tx_dcycs < 1.0) { + if(out_wrptr != out_rdptr) { + /* do just one char, then get out */ + printf("tx_dcycs < 1\n"); + return; + } + } + if(g_serial_mask[port] || (scc_ptr->reg[5] & 0x60) != 0x60) { + val = val & 0x7f; + } + + scc_add_to_writebuf(dfcyc, port, val); +} + +void +scc_add_to_writebuf(dword64 dfcyc, int port, word32 val) +{ + Scc *scc_ptr; + int out_wrptr, out_wrptr_next, out_rdptr; + + // printf("scc_add_to_writebuf p:%d, val:%02x\n", port, val); + if(scc_is_port_closed(dfcyc, port)) { + return; // Port is closed + } + scc_ptr = &(g_scc[port]); + + out_wrptr = scc_ptr->out_wrptr; + out_rdptr = scc_ptr->out_rdptr; + + out_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1); + if(out_wrptr_next != out_rdptr) { + scc_ptr->out_buf[out_wrptr] = val; + scc_ptr->out_wrptr = out_wrptr_next; + scc_printf("scc wrbuf port %d had char 0x%02x added\n", + port, val); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc outbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } +} + diff --git a/gsplus/src/scc.h b/gsplus/src/scc.h new file mode 100644 index 0000000..6f10d3b --- /dev/null +++ b/gsplus/src/scc.h @@ -0,0 +1,111 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include + +#ifdef _WIN32 +# include +#else +# include +# include +# include +#endif + +#if defined(HPUX) || defined(__linux__) || defined(SOLARIS) +# define SCC_SOCKETS +#endif +#if defined(MAC) || defined(__MACH__) || defined(_WIN32) +# define SCC_SOCKETS +#endif + + +/* my scc port 0 == channel A, port 1 = channel B */ + +#define SCC_INBUF_SIZE 512 /* must be a power of 2 */ +#define SCC_OUTBUF_SIZE 512 /* must be a power of 2 */ + +#define SCC_MODEM_MAX_CMD_STR 128 + +#ifndef _WIN32 +# define SOCKET int /* For non-Windows */ +# define INVALID_SOCKET (-1) /* For non-Windows */ +#endif + +STRUCT(Scc) { + int cur_state; + int modem_state; + SOCKET sockfd; + SOCKET rdwrfd; + void *sockaddr_ptr; // Socket: pointer to sockaddr struct + int sockaddr_size; // Socket: sizeof(sockaddr_in) + int unix_dev_fd; // Unix fd to real serial device + void *win_com_handle; // Win32 handle to COMx port + void *win_dcb_ptr; // Win32 ptr to COMx DCB + int read_called_this_vbl; + int write_called_this_vbl; + + byte dcd; + byte reg_ptr; + byte br_is_zero; + byte tx_buf_empty; + word32 mode; + byte reg[16]; + + int rx_queue_depth; + byte rx_queue[4]; + + int in_rdptr; + int in_wrptr; + byte in_buf[SCC_INBUF_SIZE]; + + int out_rdptr; + int out_wrptr; + byte out_buf[SCC_OUTBUF_SIZE]; + + int wantint_rx; + int wantint_tx; + int wantint_zerocnt; + + double br_dcycs; + double tx_dcycs; + double rx_dcycs; + + int br_event_pending; + int rx_event_pending; + int tx_event_pending; + + int char_size; + int baud_rate; + dword64 out_char_dfcyc; + + int socket_error; + int socket_num_rings; + dword64 socket_last_ring_dfcyc; + word32 modem_mode; + int modem_plus_mode; + int modem_s0_val; + int modem_s2_val; + int telnet_mode; + int telnet_iac; + word32 telnet_local_mode[2]; + word32 telnet_remote_mode[2]; + word32 telnet_reqwill_mode[2]; + word32 telnet_reqdo_mode[2]; + word32 modem_out_portnum; + int modem_cmd_len; + byte modem_cmd_str[SCC_MODEM_MAX_CMD_STR + 5]; +}; + +#define SCCMODEM_NOECHO 0x0001 +#define SCCMODEM_NOVERBOSE 0x0002 + diff --git a/gsplus/src/scc_socket_driver.c b/gsplus/src/scc_socket_driver.c new file mode 100644 index 0000000..15d8688 --- /dev/null +++ b/gsplus/src/scc_socket_driver.c @@ -0,0 +1,1236 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// This file contains the socket calls for Win32 and Mac/Linux +// Win32: see: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/ + +// Modem init string GBBS GSPORT.HST: ats0=1s2=128v0 +// Modem init string Warp6: atm0e0v0s0=0s7=25 atx4h0 at&c1&d2 + +#include "defc.h" + +#include "scc.h" +#include + +extern int Verbose; +extern Scc g_scc[2]; +extern int g_serial_cfg[2]; +extern char *g_serial_remote_ip[2]; +extern int g_serial_remote_port[2]; +extern int g_serial_modem_response_code; +extern int g_serial_modem_allow_incoming; +extern int g_serial_modem_init_telnet; + +#ifdef _WIN32 +#include +typedef int socklen_t; +#endif + +#ifndef _WIN32 +extern int h_errno; +#endif + +int g_wsastartup_called = 0; + +#ifndef _WIN32 +// SOCKET is defined in scc.h, and is an "int" for non-Windows systems +// INVALID_SOCKET is -1 for non-WINDOWS +# define closesocket(s) close(s) +#endif + +/* Usage: scc_socket_open() called to init socket mode */ +/* At all times, we try to have a listen running on the incoming socket */ +/* If we want to dial out, we close the incoming socket and create a new */ +/* outgoing socket. Any hang-up causes the socket to be closed and it will */ +/* then re-open on a subsequent call to scc_socket_open */ + +void +scc_socket_open(dword64 dfcyc, int port, int cfg) +{ + Scc *scc_ptr; + + // printf("scc_socket_open p:%d cfg:%d\n", port, cfg); +#if defined(_WIN32) && defined(SCC_SOCKETS) + WSADATA wsadata; + int ret; + + if(g_wsastartup_called == 0) { + ret = WSAStartup(MAKEWORD(2,0), &wsadata); + printf("WSAStartup ret: %d\n", ret); + g_wsastartup_called = 1; + } +#endif + scc_ptr = &(g_scc[port]); + scc_ptr->cur_state = cfg; // successful socket + scc_ptr->sockfd = INVALID_SOCKET; /* Indicate no socket open yet */ + scc_ptr->rdwrfd = INVALID_SOCKET; /* Indicate no socket open yet */ + scc_ptr->modem_state = 0; /* 0 means talk to socket */ + /* 1 means talk to modem */ + scc_ptr->socket_error = 0; + scc_ptr->socket_num_rings = 0; + scc_ptr->socket_last_ring_dfcyc = 0; + scc_ptr->dcd = 1; /* 1 means carrier */ + scc_ptr->modem_s0_val = 0; // Number of rings before answer + scc_ptr->sockaddr_size = sizeof(struct sockaddr_in); + free(scc_ptr->sockaddr_ptr); + scc_ptr->sockaddr_ptr = malloc(scc_ptr->sockaddr_size); + + // cfg==1: Virtual Modem. All set up, wait for AT commands now + // cfg==2: Outgoing connection to g_serial_remote_ip[], do it now + // cfg==3: Incoming connection on 6501/6502. Do it now + if(cfg == 2) { + scc_ptr->modem_state = 0; + // Do not open it now, it will be opened when output is + // sent to the port + } + if(cfg == 1) { + scc_ptr->modem_state = 1; + scc_ptr->dcd = 0; + scc_socket_open_incoming(dfcyc, port); + } else if(cfg == 3) { + scc_ptr->modem_state = 0; + scc_socket_open_incoming(dfcyc, port); + } +} + +void +scc_socket_close(int port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + SOCKET rdwrfd; + SOCKET sockfd; + int do_init; + int i; + + scc_ptr = &(g_scc[port]); + + // printf("In scc_socket_close, %d\n", port); + + rdwrfd = scc_ptr->rdwrfd; + do_init = 0; + if(rdwrfd != INVALID_SOCKET) { + printf("socket_close: rdwrfd=%llx, closing\n", (dword64)rdwrfd); + closesocket(rdwrfd); + do_init = 1; + } + sockfd = scc_ptr->sockfd; + if((sockfd != INVALID_SOCKET) && (rdwrfd != sockfd)) { + printf("socket_close: sockfd=%llx, closing\n", (dword64)sockfd); + closesocket(sockfd); + do_init = 1; + } + + scc_ptr->modem_state = 0; + scc_ptr->socket_num_rings = 0; + if(scc_ptr->cur_state == 1) { // Virtual modem + scc_ptr->modem_state = 1; + scc_ptr->dcd = 0; + } + if(do_init) { + scc_ptr->modem_cmd_len = 0; + scc_ptr->telnet_iac = 0; + for(i = 0; i < 2; i++) { + scc_ptr->telnet_local_mode[i] = 0; + scc_ptr->telnet_remote_mode[i] = 0; + scc_ptr->telnet_reqwill_mode[i] = 0; + scc_ptr->telnet_reqdo_mode[i] = 0; + } + scc_ptr->rdwrfd = INVALID_SOCKET; + scc_ptr->sockfd = INVALID_SOCKET; + } +#endif +} + +void +scc_socket_close_extended(dword64 dfcyc, int port, int allow_retry) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + + // printf("In scc_socket_close_extended, %d, %016llx\n", port, dfcyc); + scc_socket_close(port); + + scc_ptr = &(g_scc[port]); + if(scc_ptr->cur_state == 1) { // Virtual modem mode + scc_socket_send_modem_code(dfcyc, port, 3); + scc_ptr->modem_state = 1; // and go back to modem mode + } else if((scc_ptr->cur_state == 2) && !allow_retry) { // Remote IP + scc_ptr->cur_state = -2; // Error, give up + } +#endif +} + +void +scc_socket_maybe_open(dword64 dfcyc, int port, int must) +{ + Scc *scc_ptr; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->sockfd != INVALID_SOCKET) { + // Valid socket. See if we should hang up + if((scc_ptr->reg[5] & 0x80) && (scc_ptr->cur_state == 1)) { + // Handle DTR forcing modem hang-up now + printf("DTR is deasserted, hanging up\n"); + scc_socket_close(port); + } + return; + } + if(scc_ptr->cur_state == 2) { + if(must) { + scc_socket_open_outgoing(dfcyc, port, + g_serial_remote_ip[port], + g_serial_remote_port[port]); + } + } else { + scc_socket_open_incoming(dfcyc, port); + } +} + +void +scc_socket_open_incoming(dword64 dfcyc, int port) +{ +#ifdef SCC_SOCKETS + SOCKET sockfd; + struct sockaddr_in sa_in; + Scc *scc_ptr; + int on, ret, inc; + + inc = 0; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->sockfd != INVALID_SOCKET) { + /* it's already open, get out */ + return; + } + + //printf("scc_socket_open_incoming: scc socket close being called\n"); + scc_socket_close(port); + + scc_ptr->socket_num_rings = 0; + + memset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size); + + if(scc_ptr->cur_state == 2) { + // Outgoing connection only, never accept an incoming connect + return; + } + if(scc_ptr->cur_state == 1) { + if(!g_serial_modem_allow_incoming) { + // Virtual modem with incoming connections disallowed + return; + } + if(scc_ptr->reg[5] & 0x80) { + // DTR is forcing hang-up. Don't open incoming socket + return; + } + } + + while(1) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + //printf("sockfd ret: %llx\n", (dword64)sockfd); + if(sockfd == INVALID_SOCKET) { + printf("socket ret: -1, errno: %d\n", errno); + scc_socket_close(port); + scc_ptr->socket_error = -1; + return; + } + /* printf("socket ret: %d\n", sockfd); */ + + on = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if(ret < 0) { + printf("setsockopt REUSEADDR ret: %d, err:%d\n", + ret, errno); + scc_socket_close(port); + return; + } + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(6501 + port + inc); + sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); + printf("bind ret:%d\n", ret); + + if(ret >= 0) { + ret = listen(sockfd, 1); + break; + } + /* else ret to bind was < 0 */ + printf("bind or listen ret: %d, errno: %d\n", ret, errno); + inc++; + closesocket(sockfd); + printf("Trying next port: %d\n", 6501 + port + inc); + if(inc >= 10) { + printf("Too many retries, quitting\n"); + scc_socket_close(port); + scc_ptr->socket_error = -1; + return; + } + } + + printf("SCC port %d is at unix port %d\n", port, 6501 + port + inc); + + scc_ptr->sockfd = sockfd; + scc_ptr->socket_error = 0; + + scc_socket_make_nonblock(dfcyc, port); +#endif +} + +void +scc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str, + int remote_port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + struct sockaddr_in sa_in; + struct hostent *hostentptr; + int on; + int ret; + SOCKET sockfd; + + scc_ptr = &(g_scc[port]); + + // printf("scc socket close being called from socket_open_out\n"); + scc_socket_close(port); + + memset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + // printf("outgoing sockfd ret: %llx\n", (dword64)sockfd); + if(sockfd == INVALID_SOCKET) { + printf("socket ret: %llx, errno: %d\n", (dword64)sockfd, errno); + scc_socket_close_extended(dfcyc, port, 0); + return; + } + /* printf("socket ret: %d\n", sockfd); */ + + on = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if(ret < 0) { + printf("setsockopt REUSEADDR ret: %d, err:%d\n", + ret, errno); + scc_socket_close_extended(dfcyc, port, 0); + return; + } + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(remote_port); + while(*remote_ip_str == ' ') { + remote_ip_str++; // Skip leading blanks + } + hostentptr = gethostbyname(remote_ip_str); + printf("Connecting to %s, port:%d\n", remote_ip_str, remote_port); + if(hostentptr == 0) { +#ifdef _WIN32 + fatal_printf("Lookup host %s failed\n", + &scc_ptr->modem_cmd_str[0]); +#else + fatal_printf("Lookup host %s failed, herrno: %d\n", + &scc_ptr->modem_cmd_str[0], h_errno); +#endif + closesocket(sockfd); + scc_socket_close_extended(dfcyc, port, 0); + return; + } + memcpy(&sa_in.sin_addr.s_addr, hostentptr->h_addr, + hostentptr->h_length); + /* The above copies the 32-bit internet address into */ + /* sin_addr.s_addr. It's in correct network format */ + + ret = connect(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); + if(ret < 0) { + printf("connect ret: %d, errno: %d\n", ret, errno); + closesocket(sockfd); + scc_socket_close_extended(dfcyc, port, 0); + return; + } + scc_socket_modem_connect(dfcyc, port); + scc_ptr->dcd = 1; /* carrier on */ + scc_ptr->modem_state = 0; /* talk to socket */ + scc_ptr->socket_num_rings = 0; + + printf("SCC port %d is now outgoing to %s:%d\n", port, remote_ip_str, + remote_port); + + scc_ptr->sockfd = sockfd; + + scc_socket_make_nonblock(dfcyc, port); + scc_ptr->rdwrfd = scc_ptr->sockfd; +#endif +} + +void +scc_socket_make_nonblock(dword64 dfcyc, int port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + SOCKET sockfd; + int ret; +# ifdef _WIN32 + u_long flags; +# else + int flags; +# endif + + scc_ptr = &(g_scc[port]); + sockfd = scc_ptr->sockfd; + +# ifdef _WIN32 + flags = 1; + ret = ioctlsocket(sockfd, FIONBIO, &flags); + if(ret != 0) { + printf("ioctlsocket ret: %d\n", ret); + } +# else + flags = fcntl(sockfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags, errno); + scc_socket_close_extended(dfcyc, port, 0); + return; + } + ret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + scc_socket_close_extended(dfcyc, port, 0); + return; + } +# endif +#endif +} + +void +scc_accept_socket(dword64 dfcyc, int port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + SOCKET rdwrfd; + socklen_t address_len; + int flags, num_rings, ret; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->sockfd == INVALID_SOCKET) { + // printf("in accept_socket, call socket_open\n"); + scc_socket_open_incoming(dfcyc, port); + } + if(scc_ptr->sockfd == INVALID_SOCKET) { + return; /* just give up */ + } + if(scc_ptr->rdwrfd == INVALID_SOCKET) { + address_len = scc_ptr->sockaddr_size; + rdwrfd = accept(scc_ptr->sockfd, scc_ptr->sockaddr_ptr, + &address_len); + scc_ptr->sockaddr_size = (int)address_len; + if(rdwrfd == INVALID_SOCKET) { + return; + } + + flags = 0; + ret = 0; +#ifndef _WIN32 + /* For Linux, we need to set O_NONBLOCK on the rdwrfd */ + flags = fcntl(rdwrfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags,errno); + return; + } + ret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + return; + } +#endif + scc_ptr->rdwrfd = rdwrfd; + printf("Set port[%d].rdwrfd = %llx\n", port, (dword64)rdwrfd); + + num_rings = 4; + if(scc_ptr->modem_s0_val > 0) { + num_rings = scc_ptr->modem_s0_val; + } + scc_ptr->socket_num_rings = num_rings; + scc_ptr->socket_last_ring_dfcyc = 0; /* do ring now*/ + + /* and send some telnet codes */ + if(g_serial_modem_init_telnet) { + scc_ptr->telnet_reqwill_mode[0] = 0xa; + scc_ptr->telnet_reqdo_mode[0] = 0xa; + /* 3=GO_AH and 1=ECHO */ + scc_ptr->telnet_reqdo_mode[1] = 0x4; // 34=LINEMODE + } + printf("Telnet reqwill and reqdo's initialized: %08x_%08x," + "%08x_%08x\n", + scc_ptr->telnet_reqwill_mode[1], + scc_ptr->telnet_reqwill_mode[0], + scc_ptr->telnet_reqdo_mode[1], + scc_ptr->telnet_reqdo_mode[0]); + + scc_socket_modem_do_ring(dfcyc, port); + } +#endif +} + +void +scc_socket_telnet_reqs(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + word32 mask, willmask, domask; + int i, j; + + scc_ptr = &(g_scc[port]); + for(i = 0; i < 64; i++) { + j = i >> 5; + mask = 1 << (i & 31); + willmask = scc_ptr->telnet_reqwill_mode[j]; + if(willmask & mask) { + scc_printf("Telnet reqwill %d\n", i); + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, 0xfb); /* WILL */ + scc_add_to_writebuf(dfcyc, port, i); + } + domask = scc_ptr->telnet_reqdo_mode[j]; + if(domask & mask) { + scc_printf("Telnet reqdo %d\n", i); + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, 0xfd); /* DO */ + scc_add_to_writebuf(dfcyc, port, i); + } + } +} + +void +scc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left) +{ +#ifdef SCC_SOCKETS + byte tmp_buf[256]; + Scc *scc_ptr; + SOCKET rdwrfd; + int ret; + int i; + + scc_ptr = &(g_scc[port]); + + scc_accept_socket(dfcyc, port); + scc_socket_modem_do_ring(dfcyc, port); + + if(scc_ptr->modem_state) { + /* Just get out, this is modem mode */ + /* The transmit function stuffs any bytes in receive buf */ + return; + } + rdwrfd = scc_ptr->rdwrfd; + if(rdwrfd == INVALID_SOCKET) { + return; /* just get out */ + } + + /* Try reading some bytes */ + space_left = MY_MIN(space_left, 256); + ret = (int)recv(rdwrfd, tmp_buf, space_left, 0); + if(ret > 0) { + for(i = 0; i < ret; i++) { + if(tmp_buf[i] == 0) { + /* Skip null chars */ + continue; + } + scc_socket_recvd_char(dfcyc, port, tmp_buf[i]); + } + } else if(ret == 0) { + /* assume socket close */ + printf("recv got 0 from rdwrfd=%llx, closing\n", + (dword64)rdwrfd); + scc_socket_close_extended(dfcyc, port, 1); + } +#endif +} + +void +scc_socket_recvd_char(dword64 dfcyc, int port, int c) +{ + Scc *scc_ptr; + word32 locmask, remmask, mask, reqwillmask, reqdomask; + int telnet_mode, telnet_iac, eff_c, cpos, reply; + + scc_ptr = &(g_scc[port]); + + telnet_mode = scc_ptr->telnet_mode; + telnet_iac = scc_ptr->telnet_iac; + + eff_c = c; + if(scc_ptr->cur_state == 2) { + // Outgoing only connection, support 8-bit transfers with + // no telnet command support + telnet_mode = 0; + telnet_iac = 0; + } else if(telnet_iac == 0) { + if(c == 0xff) { + scc_ptr->telnet_iac = 0xff; + return; /* and just get out */ + } + } else { + /* last char was 0xff, see if this is 0xff */ + if(c != 0xff) { + /* this is some kind of command */ + eff_c = eff_c | 0x100; /* indicate prev char IAC */ + } + } + scc_ptr->telnet_iac = 0; + + mask = 1 << (c & 31); + cpos = (c >> 5) & 1; + locmask = scc_ptr->telnet_local_mode[cpos] & mask; + remmask = scc_ptr->telnet_remote_mode[cpos] & mask; + reqwillmask = scc_ptr->telnet_reqwill_mode[cpos] & mask; + reqdomask = scc_ptr->telnet_reqdo_mode[cpos] & mask; + switch(telnet_mode) { + case 0: /* just passing through bytes */ + switch(eff_c) { + case 0x1fe: /* DON'T */ + case 0x1fd: /* DO */ + case 0x1fc: /* WON'T */ + case 0x1fb: /* WILL */ + case 0x1fa: /* SB */ + telnet_mode = c; + break; + default: + if(eff_c < 0x100) { + scc_add_to_readbuf(dfcyc, port, c); + } + break; + } + break; + case 3: /* LINEMODE SB SLC, octet 0 */ + if(eff_c == 0x1f0) { + /* SE, the end */ + telnet_mode = 0; + } + scc_printf("LINEMODE SLC octet 0: %02x\n", c); + telnet_mode = 4; + break; + case 4: /* LINEMODE SB SLC, octet 1 */ + scc_printf("LINEMODE SLC octet 1: %02x\n", c); + telnet_mode = 5; + if(eff_c == 0x1f0) { + /* SE, the end */ + scc_printf("Got SE at octet 1...\n"); + telnet_mode = 0; + } + break; + case 5: /* LINEMODE SB SLC, octet 2 */ + scc_printf("LINEMODE SLC octet 2: %02x\n", c); + telnet_mode = 3; + if(eff_c == 0x1f0) { + /* SE, the end */ + printf("Got Telnet SE at octet 2...\n"); + telnet_mode = 0; + } + break; + case 34: /* LINEMODE SB beginning */ + switch(c) { + case 3: /* SLC */ + telnet_mode = 3; + break; + default: + telnet_mode = 0xee; /* go to SB eat */ + break; + } + break; + case 0xfa: /* in 0xfa = SB mode, eat up subcommands */ + switch(c) { + case 34: /* LINEMODE */ + telnet_mode = 34; + break; + default: + telnet_mode = 0xee; /* SB eat mode */ + break; + } + break; + case 0xee: /* in SB eat mode */ + if(eff_c == 0x1f0) { /* SE, end of sub-command */ + telnet_mode = 0; + } else { + /* just stay in eat mode */ + } + break; + case 0xfe: /* previous char was "DON'T" */ + if(locmask && (reqwillmask == 0)) { + /* it's a mode change */ + /* always OK to turn off a mode that we had on */ + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, 0xfc); /* WON'T */ + scc_add_to_writebuf(dfcyc, port, c); + } + scc_ptr->telnet_local_mode[cpos] &= ~mask; + scc_ptr->telnet_reqwill_mode[cpos] &= ~mask; + telnet_mode = 0; + break; + case 0xfd: /* previous char was "DO" */ + reply = 0xfc; + if(locmask == 0 && (reqwillmask == 0)) { + /* it's a mode change, send some response */ + reply = 0xfc; /* nack it with WON'T */ + if(c == 0x03 || c == 0x01) { + reply = 0xfb; /* Ack with WILL */ + } + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, reply); + scc_add_to_writebuf(dfcyc, port, c); + } + if(reqwillmask || (reply == 0xfb)) { + scc_ptr->telnet_local_mode[cpos] |= mask; + } + scc_ptr->telnet_reqwill_mode[cpos] &= ~mask; + telnet_mode = 0; + break; + case 0xfc: /* previous char was "WON'T" */ + if(remmask && (reqdomask == 0)) { + /* it's a mode change, ack with DON'T */ + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, 0xfe); /* DON'T */ + scc_add_to_writebuf(dfcyc, port, c); + } + scc_ptr->telnet_remote_mode[cpos] &= ~mask; + scc_ptr->telnet_reqdo_mode[cpos] &= ~mask; + telnet_mode = 0; + break; + case 0xfb: /* previous char was "WILL" */ + reply = 0xfe; /* nack it with DON'T */ + if(remmask == 0 && (reqdomask == 0)) { + /* it's a mode change, send some response */ + if(c == 0x03 || c == 0x01) { + reply = 0xfd; /* Ack with DO */ + } + scc_add_to_writebuf(dfcyc, port, 0xff); + scc_add_to_writebuf(dfcyc, port, reply); + scc_add_to_writebuf(dfcyc, port, c); + } + if(reqdomask || (reply == 0xfd)) { + scc_ptr->telnet_remote_mode[cpos] |= mask; + } + scc_ptr->telnet_reqdo_mode[cpos] &= ~mask; + telnet_mode = 0; + break; + default: + telnet_mode = 0; + break; + } + scc_ptr->telnet_mode = telnet_mode; +} + +void +scc_socket_empty_writebuf(dword64 dfcyc, int port) +{ +#ifdef SCC_SOCKETS +# ifndef _WIN32 + struct sigaction newact, oldact; +# endif + Scc *scc_ptr; + dword64 diff_dusec; + SOCKET rdwrfd; + int plus_mode, plus_char, rdptr, wrptr, done, ret, len, c; + int i; + + scc_socket_maybe_open(dfcyc, port, 0); + + scc_ptr = &(g_scc[port]); + + /* See if +++ done and we should go to command mode */ + diff_dusec = (dfcyc - scc_ptr->out_char_dfcyc) >> 16; + if((diff_dusec > 900LL*1000) && (scc_ptr->modem_plus_mode == 3) && + (scc_ptr->modem_state == 0) && + (scc_ptr->cur_state == 1)) { + scc_ptr->modem_state = 1; /* go modem mode, stay connect*/ + scc_ptr->modem_plus_mode = 0; + scc_socket_send_modem_code(dfcyc, port, 0); // "OK" + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + done = 1; + break; + } + rdwrfd = scc_ptr->rdwrfd; + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + //printf("Writing data, %d bytes, modem_state:%d\n", len, + // scc_ptr->modem_state); + if(len <= 0) { + done = 1; + break; + } + scc_socket_maybe_open(dfcyc, port, 1); + + if(scc_ptr->modem_state) { + len = 1; + scc_socket_modem_write(dfcyc, port, + scc_ptr->out_buf[rdptr]); + scc_ptr->write_called_this_vbl = 0; + ret = 1; + } else { + if(rdwrfd == INVALID_SOCKET) { + return; + } + plus_char = scc_ptr->modem_s2_val; + if((plus_char == 0) || (plus_char >= 128)) { + plus_char = 0xfff; // Invalid + scc_ptr->modem_plus_mode = 0; + } + // Look for '+++' within .8 seconds + for(i = 0; i < len; i++) { + c = scc_ptr->out_buf[rdptr + i]; + plus_mode = scc_ptr->modem_plus_mode; + diff_dusec = + (dfcyc - scc_ptr->out_char_dfcyc) >> 16; + if(c == plus_char && plus_mode == 0) { + if(diff_dusec > 500LL*1000) { + scc_ptr->modem_plus_mode = 1; + } + } else if(c == plus_char) { + if(diff_dusec < 800LL*1000) { + plus_mode++; + scc_ptr->modem_plus_mode = + plus_mode; + } + } else { + scc_ptr->modem_plus_mode = 0; + } + scc_ptr->out_char_dfcyc = dfcyc; + } +# ifndef _WIN32 + // ignore SIGPIPE around writes to the socket, so we + // can catch a closed socket and prepare to accept + // a new connection. Otherwise, SIGPIPE kills KEGS + sigemptyset(&newact.sa_mask); + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + sigaction(SIGPIPE, &newact, &oldact); +# endif + + ret = (int)send(rdwrfd, &(scc_ptr->out_buf[rdptr]), + len, 0); + +# ifndef _WIN32 + sigaction(SIGPIPE, &oldact, 0); + /* restore previous SIGPIPE behavior */ +# endif + +#if 0 + printf("sock output: %02x, len:%d\n", + scc_ptr->out_buf[rdptr], len); +#endif + + } + + if(ret == 0) { + done = 1; /* give up for now */ + break; + } else if(ret < 0) { + /* assume socket is dead */ + printf("socket write failed, resuming modem mode\n"); + scc_socket_close_extended(dfcyc, port, 1); + done = 1; + break; + } else { + rdptr = rdptr + ret; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +#endif +} + +void +scc_socket_modem_write(dword64 dfcyc, int port, int c) +{ + Scc *scc_ptr; + char *str; + word32 modem_mode; + int do_echo, got_at, len; + + scc_ptr = &(g_scc[port]); + + if(scc_ptr->sockfd == INVALID_SOCKET) { + scc_socket_open_incoming(dfcyc, port); + } + + modem_mode = scc_ptr->modem_mode; + str = (char *)&(scc_ptr->modem_cmd_str[0]); + + do_echo = ((modem_mode & SCCMODEM_NOECHO) == 0); + len = scc_ptr->modem_cmd_len; + got_at = 0; +#if 0 + printf("T:%016llx M[%d][%d]: %02x\n", dfcyc, port, len, c); +#endif + if(len >= 2 && str[0] == 'a' && str[1] == 't') { + /* we've got an 'at', do not back up past it */ + got_at = 1; + } + if(c == 0x0d) { + if(do_echo) { + scc_add_to_readbuf(dfcyc, port, c); /* echo cr */ + scc_add_to_readbuf(dfcyc, port, 0x0a); /* echo lf */ + } + do_echo = 0; /* already did the echo */ + scc_socket_do_cmd_str(dfcyc, port); + scc_ptr->modem_cmd_len = 0; + len = 0; + str[0] = 0; + } else if(c == 0x08) { + if(len <= 0) { + do_echo = 0; /* do not go past left margin */ + } else if(len == 2 && got_at) { + do_echo = 0; /* do not erase "AT" */ + } else { + /* erase a character */ + len--; + str[len] = 0; + } + } else if(c < 0x20) { + /* ignore all control characters, don't echo */ + /* includes line feeds and nulls */ + do_echo = 0; + } else { + /* other characters */ + if(len < SCC_MODEM_MAX_CMD_STR) { + str[len] = tolower(c); + str[len+1] = 0; + len++; + } + } + scc_ptr->modem_cmd_len = len; + if(do_echo) { + scc_add_to_readbuf(dfcyc, port, c); /* echo */ + } +} + +void +scc_socket_do_cmd_str(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + char *str; + int pos, len, ret_val, reg, reg_val, was_amp, out_port, c; + int i; + + scc_ptr = &(g_scc[port]); + + str = (char *)&(scc_ptr->modem_cmd_str[0]); + printf("Got modem string :%s:=%02x %02x %02x\n", str, str[0], str[1], + str[2]); + + len = scc_ptr->modem_cmd_len; + str[len] = 0; + str[len+1] = 0; + str[len+2] = 0; + pos = -1; + if(len < 2) { + /* just ignore it */ + return; + } + if(str[0] != 'a' || str[1] != 't') { + return; + } + + /* Some AT command received--make sure socket 6501/6502 is open */ + printf("Some AT command received, sockfd=%llx\n", + (dword64)scc_ptr->sockfd); + + pos = 2 - 1; + ret_val = 0; /* "OK" */ + was_amp = 0; + while(++pos < len) { + c = str[pos] + was_amp; + was_amp = 0; + switch(c) { + case '&': /* at& */ + was_amp = 0x100; + break; + case 'z': /* atz */ + scc_ptr->modem_mode = 0; + scc_ptr->modem_s0_val = 0; + pos = len; /* ignore any other commands */ + break; + case 'e': /* ate = echo */ + c = str[pos+1]; + if(c == '1') { + scc_ptr->modem_mode &= ~SCCMODEM_NOECHO; + pos++; + } else { + scc_ptr->modem_mode |= SCCMODEM_NOECHO; + pos++; + } + break; + case 'v': /* atv = verbose */ + c = str[pos+1]; + if(c == '1') { + scc_ptr->modem_mode &= ~SCCMODEM_NOVERBOSE; + pos++; + } else { + scc_ptr->modem_mode |= SCCMODEM_NOVERBOSE; + pos++; + } + break; + case 'o': /* ato = go online */ + printf("ato\n"); + if(scc_ptr->dcd && scc_ptr->modem_state && + (scc_ptr->rdwrfd != INVALID_SOCKET)) { + printf("Going back online\n"); + scc_socket_modem_connect(dfcyc, port); + scc_ptr->modem_state = 0; + // talk to socket + ret_val = -1; + } + break; + case 'h': /* ath = hang up */ + printf("ath, hanging up\n"); + scc_socket_close(port); + if(scc_ptr->rdwrfd != INVALID_SOCKET) { + scc_socket_close_extended(dfcyc, port, 0); + } + /* scc_socket_maybe_open_incoming(dfcyc, port); */ + /* reopen listen */ + break; + case 'a': /* ata */ + printf("Doing ATA\n"); + scc_socket_do_answer(dfcyc, port); + ret_val = -1; + break; + case 'd': /* atd */ + scc_ptr->modem_out_portnum = 23; + pos++; + c = str[pos]; + if(c == 't' || c == 'p') { + /* skip tone or pulse */ + pos++; + } + /* see if it is 111 */ + if(strcmp(&str[pos], "111") == 0) { + /* Do PPP! */ + } else { + /* get string to connect to */ + /* Shift string so hostname moves to str[0] */ + for(i = 0; i < len; i++) { + c = str[pos]; + if(c == ':') { + /* get port number now */ + out_port = (int)strtol( + &str[pos+1], 0, 10); + if(out_port <= 1) { + out_port = 23; + } + scc_ptr->modem_out_portnum = + out_port; + c = 0; + } + str[i] = c; + if((pos >= len) || (c == 0)) { + break; + } + pos++; + } + } + scc_socket_open_outgoing(dfcyc, port, + (char *)&scc_ptr->modem_cmd_str[0], + scc_ptr->modem_out_portnum); + ret_val = -1; + pos = len; /* always eat rest of the line */ + break; + case 's': /* atsnn=yy */ + pos++; + reg = 0; + while(1) { + c = str[pos]; + if(c < '0' || c > '9') { + break; + } + reg = (reg * 10) + c - '0'; + pos++; + } + if(c == '?') { + /* display S-register */ + if(reg == 0) { + scc_add_to_readbufv(dfcyc, port, + "S0=%d\n", + scc_ptr->modem_s0_val); + } + break; + } + if(c != '=') { + break; + } + pos++; + reg_val = 0; + while(1) { + c = str[pos]; + if(c < '0' || c >'9') { + break; + } + reg_val = (reg_val * 10) + c - '0'; + pos++; + } + printf("ats%d = %d\n", reg, reg_val); + if(reg == 0) { + scc_ptr->modem_s0_val = reg_val; + } + if(reg == 2) { + scc_ptr->modem_s2_val = reg_val; + } + pos--; + break; + default: + /* some command--peek into next chars to finish it */ + while(1) { + c = str[pos+1]; + if(c >= '0' && c <= '9') { + /* eat numbers */ + pos++; + continue; + } + if(c == '=') { + /* eat this as well */ + pos++; + continue; + } + /* else get out */ + break; + } + } + } + + if(ret_val >= 0) { + scc_socket_send_modem_code(dfcyc, port, ret_val); + } +} + +void +scc_socket_send_modem_code(dword64 dfcyc, int port, int code) +{ + Scc *scc_ptr; + char *str; + word32 modem_mode; + + scc_ptr = &(g_scc[port]); + + switch(code) { + case 0: str = "OK"; break; + case 1: str = "CONNECT"; break; + case 2: str = "RING"; break; + case 3: str = "NO CARRIER"; break; + case 4: str = "ERROR"; break; + case 5: str = "CONNECT 1200"; break; + case 10: str = "CONNECT 2400"; break; + case 12: str = "CONNECT 9600"; break; // Generic AT docs/Warp6 BBS + case 13: str = "CONNECT 9600"; break; // US Robotics Sportster/HST + case 14: str = "CONNECT 19200"; break; // Warp6 BBS + case 16: str = "CONNECT 19200"; break; // Generic AT docs + case 25: str = "CONNECT 14400"; break; // US Robotics Sportster + case 85: str = "CONNECT 19200"; break; // US Robotics Sportster + case 18: str = "CONNECT 57600"; break; // Generic AT docs/Warp6 BBS + case 28: str = "CONNECT 38400"; break; // Warp6 BBS/Hayes + default: + str = "ERROR"; + } + + printf("Sending modem code %d = %s\n", code, str); + + modem_mode = scc_ptr->modem_mode; + if(modem_mode & SCCMODEM_NOVERBOSE) { + /* just the number */ + scc_add_to_readbufv(dfcyc, port, "%d", code); + scc_add_to_readbuf(dfcyc, port, 0x0d); + } else { + scc_add_to_readbufv(dfcyc, port, "%s\n", str); + } +} + +void +scc_socket_modem_connect(dword64 dfcyc, int port) +{ + // Send code telling Term program the connect speed + if(g_scc[port].cur_state == 1) { // Virtual modem + scc_socket_send_modem_code(dfcyc, port, + g_serial_modem_response_code); /*28=38400*/ + } +} + +void +scc_socket_modem_do_ring(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + dword64 diff_dusecs; + int num_rings; + + scc_ptr = &(g_scc[port]); + num_rings = scc_ptr->socket_num_rings; + if((num_rings > 0) && scc_ptr->modem_state) { + num_rings--; + diff_dusecs = (dfcyc - scc_ptr->socket_last_ring_dfcyc) >> 16; + if(diff_dusecs < 2LL*1000*1000) { + return; /* nothing more to do */ + } + + scc_ptr->socket_num_rings = num_rings; + scc_ptr->socket_last_ring_dfcyc = dfcyc; + if(num_rings <= 0) { + /* decide on answering */ + if(scc_ptr->modem_s0_val) { + scc_socket_do_answer(dfcyc, port); + } else { + printf("No answer, closing socket\n"); + scc_socket_close(port); + } + } else { + scc_socket_send_modem_code(dfcyc, port, 2); /* RING */ + } + } +} + +void +scc_socket_do_answer(dword64 dfcyc, int port) +{ + Scc *scc_ptr; + + scc_ptr = &(g_scc[port]); + scc_accept_socket(dfcyc, port); + if(scc_ptr->rdwrfd == INVALID_SOCKET) { + printf("Answer when rdwrfd=-1, closing\n"); + scc_socket_close_extended(dfcyc, port, 0); + /* send NO CARRIER message */ + } else { + scc_socket_telnet_reqs(dfcyc, port); + printf("Send telnet reqs\n"); + if(scc_ptr->modem_state) { + scc_socket_modem_connect(dfcyc, port); + } + scc_ptr->modem_state = 0; // Talk to socket + scc_ptr->dcd = 1; // carrier on + scc_ptr->socket_num_rings = 0; + } +} + diff --git a/gsplus/src/scc_unixdriver.c b/gsplus/src/scc_unixdriver.c new file mode 100644 index 0000000..10d43eb --- /dev/null +++ b/gsplus/src/scc_unixdriver.c @@ -0,0 +1,225 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +/* This file contains the Mac/Linux calls to a real serial port */ + +#include "defc.h" +#include "scc.h" + +#ifndef _WIN32 +# include +#endif + +extern Scc g_scc[2]; +extern word32 g_c025_val; +extern char *g_serial_device[2]; + +#ifndef _WIN32 +void +scc_serial_unix_open(int port) +{ + Scc *scc_ptr; + int fd; + + scc_ptr = &(g_scc[port]); + + fd = open(&g_serial_device[port][0], O_RDWR | O_NONBLOCK); + scc_ptr->unix_dev_fd = fd; + + printf("scc_serial_unix_init %d called, fd: %d\n", port, fd); + + if(fd < 0) { + scc_ptr->unix_dev_fd = -1; + scc_ptr->cur_state = -1; // Failed to open + return; + } + + scc_serial_unix_change_params(port); + + scc_ptr->cur_state = 0; // Actual Serial device +} + +void +scc_serial_unix_close(int port) +{ + Scc *scc_ptr; + int fd; + + scc_ptr = &(g_scc[port]); + + fd = scc_ptr->unix_dev_fd; + if(fd >= 0) { + close(fd); + } + scc_ptr->unix_dev_fd = -1; +} + +void +scc_serial_unix_change_params(int port) +{ + struct termios termios_buf; + Scc *scc_ptr; + int fd, csz, ret; + + scc_ptr = &(g_scc[port]); + + fd = scc_ptr->unix_dev_fd; + printf("scc_serial_unix_change_parms port: %d, fd: %d\n", port, fd); + if(fd <= 0) { + return; + } + + ret = tcgetattr(fd, &termios_buf); + if(ret != 0) { + printf("tcgetattr port%d ret: %d\n", port, ret); + } + +#if 1 + printf("baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n", + (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag, + (int)termios_buf.c_oflag, (int)termios_buf.c_cflag, + (int)termios_buf.c_lflag); +#endif + + memset(&termios_buf, 0, sizeof(struct termios)); + cfmakeraw(&termios_buf); + cfsetspeed(&termios_buf, scc_ptr->baud_rate); + + csz = scc_ptr->char_size; + termios_buf.c_cflag = CREAD | CLOCAL; + termios_buf.c_cflag |= (csz == 5) ? CS5 : + (csz == 6) ? CS6 : + (csz == 7) ? CS7 : + CS8; + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 2: // 1.5 stop bits + termios_buf.c_cflag |= CSTOPB; /* no 1.5 stop bit setting.*/ + break; + case 3: // 2 stop bits + termios_buf.c_cflag |= CSTOPB; + break; + } + + switch((scc_ptr->reg[4]) & 0x3) { + case 1: // Odd parity + termios_buf.c_cflag |= (PARENB | PARODD); + break; + case 3: // Even parity + termios_buf.c_cflag |= PARENB; + break; + } + + /* always enabled DTR and RTS control */ +#ifdef CRTSCTS + termios_buf.c_cflag |= CRTSCTS; // Linux: CTS/RTS +#endif +#ifdef CRTS_IFLOW + termios_buf.c_cflag |= CDTR_IFLOW | CRTS_IFLOW; // Mac: CTS/RTS +#endif + + printf("fd: %d, baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n", + fd, (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag, + (int)termios_buf.c_oflag, (int)termios_buf.c_cflag, + (int)termios_buf.c_lflag); + ret = tcsetattr(fd, TCSANOW, &termios_buf); + if(ret != 0) { + printf("tcsetattr ret: %d\n", ret); + } +} + +void +scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left) +{ + byte tmp_buf[256]; + Scc *scc_ptr; + int fd, ret, flags, dcd; + int i; + + scc_ptr = &(g_scc[port]); + + fd = scc_ptr->unix_dev_fd; + if(fd <= 0) { + return; + } + + /* Try reading some bytes */ + space_left = MY_MIN(space_left, 256); + ret = read(fd, tmp_buf, space_left); + + if(ret > 0) { + for(i = 0; i < ret; i++) { + scc_add_to_readbuf(dfcyc, port, tmp_buf[i]); + } + } + flags = 0; + dcd = 0; + +#if defined(TIOCMGET) && defined(TIOCM_CAR) + ret = ioctl(fd, TIOCMGET, &flags); + if(ret == 0) { + dcd = 0; + if(flags & TIOCM_CAR) { // DCD + dcd = 1; + } + scc_ptr->dcd = dcd; + } +#endif +} + +void +scc_serial_unix_empty_writebuf(int port) +{ + Scc *scc_ptr; + int fd, rdptr, wrptr, done, ret, len; + + scc_ptr = &(g_scc[port]); + + fd = scc_ptr->unix_dev_fd; + if(fd <= 0) { + return; + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + //printf("...rdptr == wrptr\n"); + done = 1; + break; + } + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + ret = write(fd, &(scc_ptr->out_buf[rdptr]), len); + + if(ret <= 0) { + done = 1; + break; + } else { + rdptr = rdptr + ret; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +} +#endif /* !_WIN32 */ diff --git a/gsplus/src/scc_windriver.c b/gsplus/src/scc_windriver.c new file mode 100644 index 0000000..2a7677d --- /dev/null +++ b/gsplus/src/scc_windriver.c @@ -0,0 +1,258 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +/* This file contains the Win32 COM1/COM2 calls */ + +#include "defc.h" +#include "scc.h" + +extern Scc g_scc[2]; +extern word32 g_c025_val; +extern int g_serial_win_device[2]; + +#ifdef _WIN32 +void +scc_serial_win_open(int port) +{ + COMMTIMEOUTS commtimeouts; + char str_buf[32]; + Scc *scc_ptr; + HANDLE com_handle; + int ret; + + scc_ptr = &(g_scc[port]); + + snprintf(&str_buf[0], sizeof(str_buf), "COM%d", + g_serial_win_device[port]); + + com_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + + scc_ptr->win_com_handle = com_handle; + + printf("scc_serial_win_init %d called, com_handle: %p\n", port, + com_handle); + + if(com_handle == INVALID_HANDLE_VALUE) { + scc_ptr->cur_state = -1; // Failed to open + return; + } + scc_ptr->win_dcb_ptr = malloc(sizeof(DCB)); + + scc_serial_win_change_params(port); + + commtimeouts.ReadIntervalTimeout = MAXDWORD; + commtimeouts.ReadTotalTimeoutMultiplier = 0; + commtimeouts.ReadTotalTimeoutConstant = 0; + commtimeouts.WriteTotalTimeoutMultiplier = 0; + commtimeouts.WriteTotalTimeoutConstant = 10; + ret = SetCommTimeouts(com_handle, &commtimeouts); + if(ret == 0) { + printf("setcommtimeout ret: %d\n", ret); + } + scc_ptr->cur_state = 0; // COM* is open +} + +void +scc_serial_win_close(int port) +{ + Scc *scc_ptr; + HANDLE com_handle; + + scc_ptr = &(g_scc[port]); + com_handle = scc_ptr->win_com_handle; + scc_ptr->win_com_handle = INVALID_HANDLE_VALUE; + if(com_handle == INVALID_HANDLE_VALUE) { + return; + } + CloseHandle(com_handle); + free(scc_ptr->win_dcb_ptr); + scc_ptr->win_dcb_ptr = 0; +} + +void +scc_serial_win_change_params(int port) +{ + DCB *dcbptr; + HANDLE com_handle; + Scc *scc_ptr; + int ret; + + scc_ptr = &(g_scc[port]); + + com_handle = scc_ptr->win_com_handle; + dcbptr = scc_ptr->win_dcb_ptr; + if((com_handle == INVALID_HANDLE_VALUE) || (scc_ptr->cur_state != 0)) { + return; + } + + ret = GetCommState(com_handle, dcbptr); + if(ret == 0) { + printf("getcomm port%d ret: %d\n", port, ret); + } + +#if 1 + printf("dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\n", + (int)dcbptr->BaudRate, (int)dcbptr->ByteSize, + (int)dcbptr->StopBits, (int)dcbptr->Parity); + printf("dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\n", + (int)dcbptr->fBinary, + (int)dcbptr->fOutxCtsFlow, + (int)dcbptr->fOutxDsrFlow, + (int)dcbptr->fDtrControl, + (int)dcbptr->fDsrSensitivity); + printf("dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\n", + (int)dcbptr->fTXContinueOnXoff, + (int)dcbptr->fOutX, + (int)dcbptr->fInX, + (int)dcbptr->fNull, + (int)dcbptr->fRtsControl); + printf("dcb.fAbortOnErr:%d, fParity:%d\n", (int)dcbptr->fAbortOnError, + (int)dcbptr->fParity); +#endif + + dcbptr->fAbortOnError = 0; + + dcbptr->BaudRate = scc_ptr->baud_rate; + dcbptr->ByteSize = scc_ptr->char_size; + dcbptr->StopBits = ONESTOPBIT; + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 2: // 1.5 stop bits + dcbptr->StopBits = ONE5STOPBITS; + break; + case 3: // 2 stop bits + dcbptr->StopBits = TWOSTOPBITS; + break; + } + + dcbptr->Parity = NOPARITY; + switch((scc_ptr->reg[4]) & 0x3) { + case 1: // Odd parity + dcbptr->Parity = ODDPARITY; + break; + case 3: // Even parity + dcbptr->Parity = EVENPARITY; + break; + } + + dcbptr->fNull = 0; + dcbptr->fDtrControl = DTR_CONTROL_ENABLE; + dcbptr->fDsrSensitivity = 0; + dcbptr->fOutxCtsFlow = 0; + dcbptr->fOutxDsrFlow = 0; + dcbptr->fParity = 0; + dcbptr->fInX = 0; + dcbptr->fOutX = 0; + dcbptr->fRtsControl = RTS_CONTROL_ENABLE; + + ret = SetCommState(com_handle, dcbptr); + if(ret == 0) { + printf("SetCommState ret: %d, new baud: %d\n", ret, + (int)dcbptr->BaudRate); + } +} + +void +scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left) +{ + byte tmp_buf[256]; + Scc *scc_ptr; + HANDLE com_handle; + DWORD bytes_read; + int ret; + int i; + + scc_ptr = &(g_scc[port]); + + com_handle = scc_ptr->win_com_handle; + if(com_handle == INVALID_HANDLE_VALUE) { + return; + } + + /* Try reading some bytes */ + space_left = MY_MIN(256, space_left); + ret = ReadFile(com_handle, tmp_buf, space_left, &bytes_read, NULL); + + if(ret == 0) { + printf("ReadFile ret 0\n"); + } + + if(ret && (bytes_read > 0)) { + for(i = 0; i < (int)bytes_read; i++) { + scc_add_to_readbuf(dfcyc, port, tmp_buf[i]); + } + } +} + +void +scc_serial_win_empty_writebuf(int port) +{ + Scc *scc_ptr; + HANDLE com_handle; + DWORD bytes_written; + word32 err_code; + int rdptr, wrptr, done, ret, len; + + scc_ptr = &(g_scc[port]); + + //printf("win_empty_writebuf, com_h: %d\n", scc_ptr->win_com_handle); + com_handle = scc_ptr->win_com_handle; + if(com_handle == INVALID_HANDLE_VALUE) { + return; + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + //printf("...rdptr == wrptr\n"); + done = 1; + break; + } + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + bytes_written = 1; + ret = WriteFile(com_handle, &(scc_ptr->out_buf[rdptr]), len, + &bytes_written, NULL); + printf("WriteFile ret: %d, bytes_written:%d, len:%d\n", ret, + (int)bytes_written, len); + + err_code = (word32)-1; + if(ret == 0) { + err_code = (word32)GetLastError(); + printf("WriteFile ret:0, err_code: %08x\n", err_code); + } + + if(ret == 0 || (bytes_written == 0)) { + done = 1; + break; + } else { + rdptr = rdptr + bytes_written; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +} + +#endif diff --git a/gsplus/src/sim65816.c b/gsplus/src/sim65816.c new file mode 100644 index 0000000..f733d32 --- /dev/null +++ b/gsplus/src/sim65816.c @@ -0,0 +1,2104 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include + +#define INCLUDE_RCSID_C +#include "defc.h" +#undef INCLUDE_RCSID_C + +double g_dtime_sleep = 0; +double g_dtime_in_sleep = 0; +extern char *g_argv0_path; + +#define MAX_EVENTS 64 + +/* All EV_* must be less than 256, since upper bits reserved for other use */ +/* e.g., DOC_INT uses upper bits to encode oscillator */ +#define EV_60HZ 1 +#define EV_STOP 2 +#define EV_SCAN_INT 3 +#define EV_DOC_INT 4 +#define EV_VBL_INT 5 +#define EV_SCC 6 +#define EV_VID_UPD 7 +#define EV_MOCKINGBOARD 8 + +extern int g_stepping; + +extern word32 g_c068_statereg; +extern int g_cur_a2_stat; + +extern word32 g_c02d_int_crom; + +extern word32 g_c035_shadow_reg; +extern word32 g_c036_val_speed; + +extern word32 g_c023_val; +extern word32 g_c041_val; +extern word32 g_c046_val; +extern word32 g_zipgs_reg_c059; +extern word32 g_zipgs_reg_c05a; +extern word32 g_zipgs_reg_c05b; +extern word32 g_zipgs_unlock; +extern Iwm g_iwm; + +Engine_reg engine; +extern word32 table8[]; +extern word32 table16[]; + +extern byte doc_ram[]; + +extern int g_iwm_motor_on; +extern int g_fast_disk_emul; +extern int g_slow_525_emul_wr; +extern int g_config_control_panel; + +extern int g_audio_enable; +extern int g_preferred_rate; + +int g_a2_fatal_err = 0; +dword64 g_dcycles_end = 0; +int g_halt_sim = 0; +int g_rom_version = -1; +int g_user_halt_bad = 0; +int g_halt_on_bad_read = 0; +int g_ignore_bad_acc = 1; +int g_ignore_halts = 1; +int g_code_red = 0; +int g_code_yellow = 0; +int g_emul_6502_ind_page_cross_bug = 0; + +int g_config_iwm_vbl_count = 0; +const char g_kegs_version_str[] = "1.38"; + +dword64 g_last_vbl_dfcyc = 0; +dword64 g_cur_dfcyc = 1; + +double g_last_vbl_dadjcycs = 0; +double g_dadjcycs = 0; + + +int g_wait_pending = 0; +int g_stp_pending = 0; +extern int g_irq_pending; + +int g_num_irq = 0; +int g_num_brk = 0; +int g_num_cop = 0; +int g_num_enter_engine = 0; +int g_io_amt = 0; +int g_engine_action = 0; +int g_engine_recalc_event = 0; +int g_engine_scan_int = 0; +int g_engine_doc_int = 0; + +#define MAX_FATAL_LOGS 20 + +int g_debug_file_fd = -1; +int g_fatal_log = -1; +char *g_fatal_log_strs[MAX_FATAL_LOGS]; + +word32 stop_run_at; + +int g_25sec_cntr = 0; +int g_1sec_cntr = 0; + +double g_dnatcycs_1sec = 0.0; +word32 g_natcycs_lastvbl = 0; + +int Verbose = 0; +int Halt_on = 0; + +word32 g_mem_size_base = 128*1024; /* size of motherboard memory */ +word32 g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ +word32 g_mem_size_total = 128*1024; /* Total contiguous RAM from 0 */ + +extern word32 g_slow_mem_changed[]; + +byte *g_slow_memory_ptr = 0; +byte *g_memory_ptr = 0; +byte *g_dummy_memory1_ptr = 0; +byte *g_rom_fc_ff_ptr = 0; +byte *g_rom_cards_ptr = 0; + +void *g_memory_alloc_ptr = 0; /* for freeing memory area */ + +Page_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE]; + +word32 g_word32_tmp = 0; +int g_force_depth = -1; +int g_use_shmem = 1; + +extern word32 g_cycs_in_40col; +extern word32 g_cycs_in_xredraw; +extern word32 g_cycs_in_refresh_line; +extern word32 g_cycs_outside_run_16ms; +extern word32 g_refresh_bytes_xfer; + +extern int g_num_snd_plays; +extern int g_num_recalc_snd_parms; + +extern int g_doc_vol; + +extern int g_status_refresh_needed; + +int +sim_get_force_depth() +{ + return g_force_depth; +} + +int +sim_get_use_shmem() +{ + return g_use_shmem; +} + +void +sim_set_use_shmem(int use_shmem) +{ + g_use_shmem = use_shmem; +} + +#define TOOLBOX_LOG_LEN 64 + +int g_toolbox_log_pos = 0; +word32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8]; + +word32 +toolbox_debug_4byte(word32 addr) +{ + word32 part1, part2; + + /* If addr looks safe, use it */ + if(addr > 0xbffc) { + return (word32)-1; + } + + part1 = get_memory16_c(addr); + part1 = (part1 >> 8) + ((part1 & 0xff) << 8); + part2 = get_memory16_c(addr+2); + part2 = (part2 >> 8) + ((part2 & 0xff) << 8); + + return (part1 << 16) + part2; +} + +void +toolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr) +{ + int pos; + + pos = g_toolbox_log_pos; + + stack += 9; + g_toolbox_log_array[pos][0] = (word32) + ((g_last_vbl_dfcyc + *dcyc_ptr) >> 16); + g_toolbox_log_array[pos][1] = stack+1; + g_toolbox_log_array[pos][2] = xreg; + g_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1); + g_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5); + g_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9); + g_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13); + g_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17); + + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + + g_toolbox_log_pos = pos; +} + +void +show_toolbox_log() +{ + int pos; + int i; + + pos = g_toolbox_log_pos; + + for(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) { + printf("%2d:%2d: %08x %06x %04x: %08x %08x %08x %08x %08x\n", + i, pos, + g_toolbox_log_array[pos][0], + g_toolbox_log_array[pos][1], + g_toolbox_log_array[pos][2], + g_toolbox_log_array[pos][3], + g_toolbox_log_array[pos][4], + g_toolbox_log_array[pos][5], + g_toolbox_log_array[pos][6], + g_toolbox_log_array[pos][7]); + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + } +} + +word32 +get_memory_io(word32 loc, dword64 *dcyc_ptr) +{ + int tmp; + + if(loc > 0xffffff) { + halt_printf("get_memory_io:%08x out of range==halt!\n", loc); + return 0; + } + + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + return(io_read(loc & 0xfff, dcyc_ptr)); + } + + /* Else it's an illegal addr...skip if memory sizing */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { + //printf("get_io assuming mem sizing, not halting\n"); + return 0; + } + } + + /* Skip reads to f80000 and f00000, just return 0 */ + if((loc & 0xf70000) == 0xf00000) { + return 0; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + return (doc_ram[loc & 0xffff]); + } + if((loc & 0xffff00) == 0xbcff00) { + /* TWGS mapped some ROM here, we'll force in all 0's */ + /* If user has selected >= 12MB of memory, then mem will be */ + /* returned and we won't ever get here */ + return 0; + } + + g_code_yellow++; + if(g_ignore_bad_acc && !g_user_halt_bad) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return 0; + } + + printf("get_memory_io for addr: %06x\n", loc); + printf("stat for addr: %06x = %p\n", loc, + GET_PAGE_INFO_RD((loc >> 8) & 0xffff)); + set_halt(g_halt_on_bad_read | g_user_halt_bad); + + return 0; +} + +void +set_memory_io(word32 loc, int val, dword64 *dcyc_ptr) +{ + word32 tmp; + + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + io_write(loc, val, dcyc_ptr); + return; + } + + /* Else it's an illegal addr */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { + //printf("set_io assuming mem sizing, not halting\n"); + return; + } + } + + /* ignore writes to ROM */ + if((loc & 0xfc0000) == 0xfc0000) { + return; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + doc_ram[loc & 0xffff] = val; + return; + } + + if(g_ignore_bad_acc && !g_user_halt_bad) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return; + } + + if((loc & 0xffc000) == 0x00c000) { + printf("set_memory %06x = %02x, warning\n", loc, val); + return; + } + + halt_printf("set_memory %06x = %02x, stopping\n", loc, val); + + return; +} + +void +show_regs_act(Engine_reg *eptr) +{ + int tmp_acc, tmp_x, tmp_y, tmp_psw, kpc, direct_page, dbank, stack; + + kpc = eptr->kpc; + tmp_acc = eptr->acc; + direct_page = eptr->direct; + dbank = eptr->dbank; + stack = eptr->stack; + + tmp_x = eptr->xreg; + tmp_y = eptr->yreg; + + tmp_psw = eptr->psr; + + dbg_printf(" PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x", + kpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw); + dbg_printf(" S=%04x D=%04x B=%02x,cyc:%016llx\n", stack, direct_page, + dbank, g_cur_dfcyc); +} + +void +show_regs() +{ + show_regs_act(&engine); +} + +void +my_exit(int ret) +{ + g_a2_fatal_err = 0x10 + ret; + printf("exiting\n"); +} + + +void +do_reset() +{ + g_c035_shadow_reg = 0; + + g_c068_statereg = 0x200 | 0x08 | 0x04; // Set wrdefram, rdrom, lcbank2 + g_c02d_int_crom = 0xff; + if(g_rom_version != 0) { // IIgs ROM01 or ROM03 + g_c068_statereg |= 0x01; // also set intcx + g_c02d_int_crom = 0; + } + g_c023_val = 0; + g_c041_val = 0; + + engine.psr = (engine.psr | 0x134) & ~(0x08); + engine.stack = 0x100 + (engine.stack & 0xff); + engine.dbank = 0; + engine.direct = 0; + engine.xreg &= 0xff; + engine.yreg &= 0xff; + g_wait_pending = 0; + g_stp_pending = 0; + + video_reset(); + adb_reset(); + iwm_reset(); + scc_reset(); + sound_reset(g_cur_dfcyc); + setup_pageinfo(); + change_display_mode(g_cur_dfcyc); + + g_irq_pending = 0; + g_code_yellow = 0; + g_code_red = 0; + + engine.kpc = get_memory16_c(0x00fffc); + + g_stepping = 0; +} + +#define CHECK(start, var, value, var1, var2) \ + var2 = PTR2WORD(&(var)); \ + var1 = PTR2WORD((start)); \ + if((var2 - var1) != value) { \ + printf("CHECK: " #var " is 0x%x, but " #value " is 0x%x\n", \ + (var2 - var1), value); \ + exit(5); \ + } + +byte * +memalloc_align(int size, int skip_amt, void **alloc_ptr) +{ + byte *bptr; + word32 addr; + word32 offset; + + skip_amt = MY_MAX(256, skip_amt); + bptr = calloc(size + skip_amt, 1); + if(alloc_ptr) { + /* Save allocation address */ + *alloc_ptr = bptr; + } + + addr = PTR2WORD(bptr) & 0xff; + + /* must align bptr to be 256-byte aligned */ + /* this code should work even if ptrs are > 32 bits */ + + offset = ((addr + skip_amt - 1) & (~0xff)) - addr; + + return (bptr + offset); +} + +void +memory_ptr_init() +{ + word32 mem_size; + + /* This routine may be called several times--each time the ROM file */ + /* changes this will be called */ + mem_size = MY_MIN(0xdf0000, g_mem_size_base + g_mem_size_exp); + if(g_rom_version == 0) { // Apple //e + mem_size = g_mem_size_base; + } + g_mem_size_total = mem_size; + if(g_memory_alloc_ptr) { + free(g_memory_alloc_ptr); + g_memory_alloc_ptr = 0; + } + g_memory_ptr = memalloc_align(mem_size, 256, &g_memory_alloc_ptr); + + printf("RAM size is 0 - %06x (%.2fMB)\n", mem_size, + (double)mem_size/(1024.0*1024.0)); +} + +extern int g_screen_redraw_skip_amt; +extern int g_use_dhr140; +extern int g_use_bw_hires; + +char g_display_env[512]; +int g_screen_depth = 8; + +int +parse_argv(int argc, char **argv, int slashes_to_find) +{ + byte *bptr; + char *str, *arg2_str; + int skip_amt, tmp1, len; + int i; + +#if 0 + // If launched from Finder, no stdout, so send it to /tmp/out1 + fflush(stdout); + setvbuf(stdout, 0, _IONBF, 0); + close(1); + (void)open("/tmp/out1", O_WRONLY | O_CREAT | O_TRUNC, 0x1b6); +#endif + + printf("Starting KEGS v%s\n", &g_kegs_version_str[0]); + + // parse arguments + // First, Check if KEGS_BIG_ENDIAN is set correctly + g_word32_tmp = 0x01020304; + bptr = (byte *)&(g_word32_tmp); +#ifdef KEGS_BIG_ENDIAN + bptr[0] = 6; + bptr[3] = 5; +#else + bptr[0] = 5; + bptr[3] = 6; +#endif + if(g_word32_tmp != 0x06020305) { + fatal_printf("KEGS_BIG_ENDIAN is not properly set\n"); + return 1; + } + + str = kegs_malloc_str(argv[0]); + len = (int)strlen(str); + for(i = len; i > 0; i--) { + if(str[i] == '/') { + if(--slashes_to_find <= 0) { + str[i] = 0; + g_argv0_path = str; + break; + } + } + } + printf("g_argv0_path: %s\n", g_argv0_path); + + i = 0; + while(++i < argc) { + printf("argv[%d] = %s\n", i, argv[i]); + if(!strcmp("-badrd", argv[i])) { + printf("Halting on bad reads\n"); + g_halt_on_bad_read = 2; + } else if(!strcmp("-noignbadacc", argv[i])) { + printf("Not ignoring bad memory accesses\n"); + g_ignore_bad_acc = 0; + } else if(!strcmp("-noignhalt", argv[i])) { + printf("Not ignoring code red halts\n"); + g_ignore_halts = 0; + } else if(!strcmp("-24", argv[i])) { + printf("Using 24-bit visual\n"); + g_force_depth = 24; + } else if(!strcmp("-16", argv[i])) { + printf("Using 16-bit visual\n"); + g_force_depth = 16; + } else if(!strcmp("-15", argv[i])) { + printf("Using 15-bit visual\n"); + g_force_depth = 15; + } else if(!strcmp("-mem", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + return 1; + } + g_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000; + printf("Using %d as memory size\n", g_mem_size_exp); + i++; + } else if(!strcmp("-skip", argv[i])) { + if((i+1) >= argc) { + printf("Missing to skip argument\n"); + return 1; + } + skip_amt = (int)strtol(argv[i+1], 0, 0); + printf("Using %d as skip_amt\n", skip_amt); + g_screen_redraw_skip_amt = skip_amt; + i++; + } else if(!strcmp("-audio", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument to -audio\n"); + return 1; + } + tmp1 = (int)strtol(argv[i+1], 0, 0); + printf("Using %d as audio enable val\n", tmp1); + g_audio_enable = tmp1; + i++; + } else if(!strcmp("-arate", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument to -arate\n"); + return 1; + } + tmp1 = (int)strtol(argv[i+1], 0, 0); + printf("Using %d as preferred audio rate\n", tmp1); + g_preferred_rate = tmp1; + i++; + } else if(!strcmp("-v", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument to -v\n"); + return 1; + } + tmp1 = (int)strtol(argv[i+1], 0, 0); + printf("Setting Verbose = 0x%03x\n", tmp1); + Verbose = tmp1; + i++; + } else if(!strcmp("-display", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + printf("Using %s as display\n", argv[i+1]); + snprintf(g_display_env, sizeof(g_display_env), + "DISPLAY=%s", argv[i+1]); + putenv(&g_display_env[0]); + i++; + } else if(!strcmp("-noshm", argv[i])) { + printf("Not using X shared memory\n"); + g_use_shmem = 0; + } else if(!strcmp("-joystick", argv[i])) { + printf("Ignoring -joystick option\n"); + } else if(!strcmp("-dhr140", argv[i])) { + printf("Using simple dhires color map\n"); + g_use_dhr140 = 1; + } else if(!strcmp("-bw", argv[i])) { + printf("Forcing black-and-white hires modes\n"); + g_cur_a2_stat |= ALL_STAT_COLOR_C021; + g_use_bw_hires = 1; + } else if(!strncmp("-NS", argv[i], 3)) { + // Some Mac argument, just ignore it + if((i + 1) < argc) { + // If the next argument doesn't start with '-', + // then ignore it, too + if(argv[i+1][0] != '-') { + i++; + } + } + } else if(!strcmp("-logpc", argv[i])) { + printf("Force logpc enable\n"); + debug_logpc_on("on"); + } else if(!strncmp("-cfg", argv[i], 4)) { + if((i + 1) < argc) { + config_set_config_kegs_name(argv[i+1]); + } + i++; + } else if(argv[i][0] == '-') { + arg2_str = 0; + if((i + 1) < argc) { + arg2_str = argv[i+1]; + } + i += config_add_argv_override(&argv[i][1], arg2_str); + } else { + printf("Bad option: %s\n", argv[i]); + return 3; + } + } + + return 0; +} + +int +kegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window) +{ + g_config_control_panel = 0; + + woz_crc_init(); + fixed_memory_ptrs_init(); + + if(sizeof(word32) != 4) { + printf("sizeof(word32) = %d, must be 4!\n", + (int)sizeof(word32)); + return 1; + } + prepare_a2_font(); // Prepare default built-in font + scc_init(); + iwm_init(); + init_reg(); + adb_init(); + initialize_events(); + debugger_init(); + setup_pageinfo(); + + config_init(); + load_roms_init_memory(); + + clear_halt(); + + video_init(mdepth, screen_width, screen_height, no_scale_window); + + sound_init(); + joystick_init(); + + if(g_rom_version >= 3) { + g_c036_val_speed |= 0x40; /* set power-on bit */ + } + + do_reset(); + + g_stepping = 0; + clear_halt(); + cfg_set_config_panel(g_config_control_panel); + + return 0; +} + +void +load_roms_init_memory() +{ + config_load_roms(); + memory_ptr_init(); + clk_setup_bram_version(); /* Must be after config_load_roms */ + if(g_rom_version >= 3) { + g_c036_val_speed |= 0x40; /* set power-on bit */ + } else { + g_c036_val_speed &= (~0x40); /* clear the bit */ + } + // Do not call do_reset(), caller is responsible for that + + /* if user booted ROM 01, switches to ROM 03, then switches back */ + /* to ROM 01, then the reset routines call to Tool $0102 looks */ + /* at uninitialized $e1/15fe and if it is negative it will JMP */ + /* through $e1/1688 which ROM 03 left pointing to fc/0199 */ + /* So set e1/15fe = 0 */ + set_memory16_c(0xe115fe, 0, 1); +} + +Event g_event_list[MAX_EVENTS]; +Event g_event_free; +Event g_event_start; + +void +initialize_events() +{ + int i; + + for(i = 1; i < MAX_EVENTS; i++) { + g_event_list[i-1].next = &g_event_list[i]; + } + g_event_free.next = &g_event_list[0]; + g_event_list[MAX_EVENTS-1].next = 0; + + g_event_start.next = 0; + g_event_start.dfcyc = 0; + + add_event_entry(CYCLES_IN_16MS_RAW << 16, EV_60HZ); +} + +void +check_for_one_event_type(int type, word32 mask) +{ + Event *ptr; + int count, depth; + + type = type & 0xff; + count = 0; + depth = 0; + ptr = g_event_start.next; + while(ptr != 0) { + depth++; + if((ptr->type & mask) == (word32)type) { + count++; + if(count != 1) { + halt_printf("in check_for_1, type %04x found " + "at depth: %d, count: %d, at %016llx\n", + ptr->type, depth, count, ptr->dfcyc); + } + } + ptr = ptr->next; + } +} + +void +add_event_entry(dword64 dfcyc, int type) +{ + Event *this_event; + Event *ptr, *prev_ptr; + int done; + + this_event = g_event_free.next; + if(this_event == 0) { + halt_printf("Out of queue entries!\n"); + show_all_events(); + return; + } + g_event_free.next = this_event->next; + + this_event->type = type; + + if((dfcyc > (g_cur_dfcyc + (50LL*1000*1000 << 16))) || + (dfcyc < g_cur_dfcyc)) { + halt_printf("add_event bad dfcyc:%016llx, type:%05x, " + "cur_dfcyc: %016llx!\n", dfcyc, type, g_cur_dfcyc); + dfcyc = g_cur_dfcyc + (1000LL << 16); + } + + ptr = g_event_start.next; + if(ptr && (dfcyc < ptr->dfcyc)) { + /* create event before next expected event */ + /* do this by calling engine_recalc_events */ + engine_recalc_events(); + } + + prev_ptr = &g_event_start; + ptr = g_event_start.next; + + done = 0; + while(!done) { + if(ptr == 0) { + this_event->next = ptr; + this_event->dfcyc = dfcyc; + prev_ptr->next = this_event; + break; + } else { + if(ptr->dfcyc < dfcyc) { // step across this guy + prev_ptr = ptr; + ptr = ptr->next; + } else { // go before this guy */ + this_event->dfcyc = dfcyc; + this_event->next = ptr; + prev_ptr->next = this_event; + break; + } + } + } + + check_for_one_event_type(type, 0xffff); +} + +extern int g_doc_saved_ctl; + +dword64 +remove_event_entry(int type, word32 mask) +{ + Event *ptr, *prev_ptr; + Event *next_ptr; + + ptr = g_event_start.next; + prev_ptr = &g_event_start; + + while(ptr != 0) { + if((ptr->type & mask) == (word32)type) { // got it + next_ptr = ptr->next; // remove it + prev_ptr->next = next_ptr; + + /* Add ptr to free list */ + ptr->next = g_event_free.next; + g_event_free.next = ptr; + + return ptr->dfcyc; + } + prev_ptr = ptr; + ptr = ptr->next; + } + + halt_printf("remove event_entry: %08x, but not found!\n", type); + if((type & 0xff) == EV_DOC_INT) { + printf("DOC, g_doc_saved_ctl = %02x\n", g_doc_saved_ctl); + } + show_all_events(); + + return 0; +} + +void +add_event_stop(dword64 dfcyc) +{ + add_event_entry(dfcyc, EV_STOP); +} + +void +add_event_doc(dword64 dfcyc, int osc) +{ + if(dfcyc < g_cur_dfcyc) { + dfcyc = g_cur_dfcyc; + //halt_printf("add_event_doc: dfcyc:%016llx, cur_dfcyc:" + // "%016llx\n", dfcyc, g_cur_dfcyc); + } + + add_event_entry(dfcyc, EV_DOC_INT + (osc << 8)); +} + +void +add_event_scc(dword64 dfcyc, int type) +{ + if(dfcyc < g_cur_dfcyc) { + dfcyc = g_cur_dfcyc; + } + + add_event_entry(dfcyc, EV_SCC + (type << 8)); +} + +void +add_event_vbl() +{ + dword64 dfcyc; + + dfcyc = g_last_vbl_dfcyc + ((192*65LL) << 16); + add_event_entry(dfcyc, EV_VBL_INT); +} + +void +add_event_vid_upd(int line) +{ + dword64 dfcyc; + + dfcyc = g_last_vbl_dfcyc + (((line + 1) * 65LL) << 16); + add_event_entry(dfcyc, EV_VID_UPD + (line << 8)); + // Redraw line when video counters first read video data +} + +void +add_event_mockingboard(dword64 dfcyc) +{ + if(dfcyc < g_cur_dfcyc) { + dfcyc = g_cur_dfcyc; + } + add_event_entry(dfcyc, EV_MOCKINGBOARD); +} + +dword64 g_dfcyc_scan_int = 0; + +void +add_event_scan_int(dword64 dfcyc, int line) +{ + dword64 dfcyc_scan_int; + + dfcyc_scan_int = g_dfcyc_scan_int; + if(dfcyc_scan_int) { // Event is pending + if(dfcyc >= g_dfcyc_scan_int) { + // We are after (or the same) as current, do nothing + return; + } + remove_event_entry(EV_SCAN_INT, 0xff); + } + if(dfcyc < g_cur_dfcyc) { + // scan_int for line 0 is found during EV_60HZ, and some + // cycles may have passed before the EV_60HZ was handled. + // We need it to happen now, so just adjust dfcyc + dfcyc = g_cur_dfcyc; + } + add_event_entry(dfcyc, EV_SCAN_INT + (line << 8)); + g_dfcyc_scan_int = dfcyc; + + check_for_one_event_type(EV_SCAN_INT, 0xff); +} + + +dword64 +remove_event_doc(int osc) +{ + return remove_event_entry(EV_DOC_INT + (osc << 8), 0xffff); +} + +dword64 +remove_event_scc(int type) +{ + return remove_event_entry(EV_SCC + (type << 8), 0xffff); +} + +void +remove_event_mockingboard() +{ + (void)remove_event_entry(EV_MOCKINGBOARD, 0xff); +} + +void +show_all_events() +{ + Event *ptr; + dword64 dfcyc; + int count; + + count = 0; + ptr = g_event_start.next; + while(ptr != 0) { + dfcyc = ptr->dfcyc; + printf("Event: %02x: type: %05x, dfcyc: %016llx (%08llx)\n", + count, ptr->type, dfcyc, dfcyc - g_cur_dfcyc); + ptr = ptr->next; + count++; + } + +} + +word32 g_vbl_count = 0; +int g_vbl_index_count = 0; +double dtime_array[60]; +double g_dadjcycs_array[60]; +double g_dtime_diff3_array[60]; +double g_dtime_this_vbl_array[60]; +double g_dtime_exp_array[60]; +double g_dtime_pmhz_array[60]; +double g_dtime_eff_pmhz_array[60]; +double g_dtime_in_run_16ms = 0; +double g_dtime_outside_run_16ms = 0; +double g_dtime_end_16ms = 0; +int g_limit_speed = 3; +int g_zip_speed_mhz = 16; // 16MHz default +double sim_time[60]; +double g_sim_sum = 0.0; + +double g_cur_sim_dtime = 0.0; +double g_projected_pmhz = 1.0; +double g_zip_pmhz = 8.0; +double g_sim_mhz = 100.0; +int g_line_ref_amt = 1; +int g_video_line_update_interval = 0; +dword64 g_video_pixel_dcount = 0; + +Fplus g_recip_projected_pmhz_slow; +Fplus g_recip_projected_pmhz_fast; +Fplus g_recip_projected_pmhz_zip; +Fplus g_recip_projected_pmhz_unl; + +void +show_pmhz() +{ + printf("Pmhz: %f, c036:%02x, limit: %d\n", + g_projected_pmhz, g_c036_val_speed, g_limit_speed); + +} + +void +setup_zip_speeds() +{ + dword64 drecip; + double fmhz; + int mult; + + mult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf); + // 16 = full speed, 1 = 1/16th speed + fmhz = (g_zip_speed_mhz * mult) / 16.0; +#if 0 + if(mult == 16) { + /* increase full speed by 19% to make zipgs freq measuring */ + /* programs work correctly */ + fmhz = fmhz * 1.19; + } +#endif + drecip = (dword64)(65536 / fmhz); + g_zip_pmhz = fmhz; + g_recip_projected_pmhz_zip.dplus_1 = drecip; + if(fmhz <= 2.0) { + g_recip_projected_pmhz_zip.dplus_x_minus_1 = + (dword64)(1.01 * 65536); + } else { + g_recip_projected_pmhz_zip.dplus_x_minus_1 = + (dword64)(1.01 * 65536 - drecip); + } +} + +word32 g_cycs_end_16ms = 0; + +int +run_16ms() +{ + double dtime_start, dtime_end, dtime_end2, dtime_outside; + int ret; + + dtime_start = get_dtime(); + ret = 0; + fflush(stdout); + g_dtime_sleep = 1.0/61.0; // For control_panel/debugger + if(g_config_control_panel) { + ret = cfg_control_panel_update(); + if(!g_config_control_panel) { + return 0; // Was just switched off, get out + } + } else { + if(g_halt_sim) { + ret = debugger_run_16ms(); + } else { + ret = run_a2_one_vbl(); + } + } + video_update(); + g_vbl_count++; + dtime_end = get_dtime(); + g_dtime_in_run_16ms += (dtime_end - dtime_start); + + // If we are ahead, then do the sleep now + micro_sleep(g_dtime_sleep); + dtime_end2 = get_dtime(); + //printf("Did sleep for %f, dtime passed:%f\n", g_dtime_sleep, + // dtime_end2 - dtime_end); + g_dtime_sleep = 0.0; + + g_dtime_in_sleep += (dtime_end2 - dtime_end); + + dtime_outside = 0.0; + if(g_vbl_count > 1) { + dtime_outside += (dtime_start - g_dtime_end_16ms); + } + g_dtime_outside_run_16ms += dtime_outside; + g_dtime_end_16ms = dtime_end2; +#if 0 + if((g_vbl_count & 0xf) == 0) { + printf("run_16ms end at %.3lf, dtime_16ms:%1.5lf out:%1.5lf\n", + dtime_end, dtime_end - dtime_start, dtime_outside); + } +#endif + return ret | g_a2_fatal_err; +} + +int +run_a2_one_vbl() +{ + Fplus *fplus_ptr; + Event *this_event, *db1; + double now_dtime, prev_dtime, fspeed_mult; + dword64 dfcyc, dfcyc_stop, prerun_dfcyc; + word32 ret, zip_speed_0tof, zip_speed_0tof_new; + int zip_en, zip_follow_cps, type, motor_on, iwm_1, iwm_25, fast; + int limit_speed, apple35_sel, zip_speed, faster_than_28, unl_speed; + + fflush(stdout); + + g_cur_sim_dtime = 0.0; + + g_recip_projected_pmhz_slow.dplus_1 = 0x10000; + g_recip_projected_pmhz_slow.dplus_x_minus_1 = (dword64)(0.9 * 0x10000); + + g_recip_projected_pmhz_fast.dplus_1 = (dword64)(0x10000 / 2.8); + g_recip_projected_pmhz_fast.dplus_x_minus_1 = (dword64) + ((1.98 - (1.0/2.8)) * 0x10000); + + zip_speed_0tof = g_zipgs_reg_c05a & 0xf0; + setup_zip_speeds(); + + if(engine.fplus_ptr == 0) { + g_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow; + } + + while(1) { + fflush(stdout); + + if(g_irq_pending && !(engine.psr & 0x4)) { + irq_printf("taking an irq!\n"); + take_irq(); + /* Interrupt! */ + } + + motor_on = g_iwm_motor_on; + limit_speed = g_limit_speed; + apple35_sel = g_iwm.state & IWM_STATE_C031_APPLE35SEL; + zip_en = ((g_zipgs_reg_c05b & 0x10) == 0); + zip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0); + zip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0; + fast = (g_c036_val_speed & 0x80) || (zip_en && !zip_follow_cps); + + if(zip_speed_0tof_new != zip_speed_0tof) { + zip_speed_0tof = zip_speed_0tof_new; + setup_zip_speeds(); + } + + iwm_1 = motor_on && !apple35_sel && + (g_c036_val_speed & 0x4) && + (g_slow_525_emul_wr || !g_fast_disk_emul); + iwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul; + faster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en && + ((limit_speed == 0) || (limit_speed == 3)); + zip_speed = faster_than_28 && + ((zip_speed_0tof != 0) || (limit_speed == 3) || + (g_zipgs_unlock >= 4) ); + unl_speed = faster_than_28 && !zip_speed; + if(unl_speed) { + /* use unlimited speed */ + fspeed_mult = g_projected_pmhz; + fplus_ptr = &g_recip_projected_pmhz_unl; + } else if(zip_speed) { + fspeed_mult = g_zip_pmhz; + fplus_ptr = &g_recip_projected_pmhz_zip; + } else if(fast && !iwm_1 && (iwm_25 || (limit_speed != 1))) { + fspeed_mult = 2.5; + fplus_ptr = &g_recip_projected_pmhz_fast; + } else { + /* else run slow */ + fspeed_mult = 1.0; + fplus_ptr = &g_recip_projected_pmhz_slow; + } + + engine.fplus_ptr = fplus_ptr; + + prerun_dfcyc = g_cur_dfcyc; + engine.dfcyc = prerun_dfcyc; + dfcyc_stop = g_event_start.next->dfcyc + 1; + if(g_stepping) { + dfcyc_stop = prerun_dfcyc + 1; + } + g_dcycles_end = dfcyc_stop; + +#if 0 + printf("Enter engine, fcycs: %f, stop: %f\n", + prerun_fcycles, fcycles_stop); + printf("g_cur_dfcyc:%016llx, last_vbl_dfcyc:%016llx\n", + g_cur_dfcyc, g_last_vbl_dfcyc); +#endif + + g_num_enter_engine++; + prev_dtime = get_dtime(); + + ret = enter_engine(&engine); + + now_dtime = get_dtime(); + + g_cur_sim_dtime += (now_dtime - prev_dtime); + + dfcyc = engine.dfcyc; + g_cur_dfcyc = dfcyc; + + g_dadjcycs += (engine.dfcyc - prerun_dfcyc) * (1/65536.0) * + fspeed_mult; + +#if 0 + printf("...back, engine.dfcyc: %016llx, dfcyc: %016llx\n", + (double)engine.dfcyc, dfcyc); +#endif + + if(ret != 0) { + g_engine_action++; + handle_action(ret); + } + + this_event = g_event_start.next; + while(dfcyc >= this_event->dfcyc) { + /* Pop this guy off of the queue */ + g_event_start.next = this_event->next; + + type = this_event->type; + this_event->next = g_event_free.next; + g_event_free.next = this_event; + dbg_log_info(dfcyc, type, 0, 0x101); + switch(type & 0xff) { + case EV_60HZ: + update_60hz(dfcyc, now_dtime); + return 0; + break; + case EV_STOP: + printf("type: EV_STOP\n"); + printf("next: %p, dfcyc: %016llx\n", + g_event_start.next, dfcyc); + db1 = g_event_start.next; + halt_printf("next.dfcyc:%016llx\n", db1->dfcyc); + break; + case EV_SCAN_INT: + g_engine_scan_int++; + irq_printf("type: scan int\n"); + do_scan_int(dfcyc, type >> 8); + break; + case EV_DOC_INT: + g_engine_doc_int++; + doc_handle_event(type >> 8, dfcyc); + break; + case EV_VBL_INT: + do_vbl_int(); + break; + case EV_SCC: + scc_do_event(dfcyc, type >> 8); + break; + case EV_VID_UPD: + video_update_event_line(type >> 8); + break; + case EV_MOCKINGBOARD: + mockingboard_event(dfcyc); + break; + default: + printf("Unknown event: %d!\n", type); + exit(3); + } + + this_event = g_event_start.next; + + } + + if(g_event_start.next == 0) { + halt_printf("ERROR...run_prog, event_start.n=0!\n"); + } + + if(g_halt_sim) { + break; + } + if(g_stepping) { + break; + } + } + + printf("leaving run_prog, g_halt_sim:%d\n", g_halt_sim); + + return 0; +} + +void +add_irq(word32 irq_mask) +{ + if(g_irq_pending & irq_mask) { + /* Already requested, just get out */ + return; + } + g_irq_pending |= irq_mask; + engine_recalc_events(); +} + +void +remove_irq(word32 irq_mask) +{ + g_irq_pending = g_irq_pending & (~irq_mask); +} + +void +take_irq() +{ + word32 new_kpc, va; + + irq_printf("Taking irq, at: %02x/%04x, psw: %02x, dfcyc:%016llx\n", + engine.kpc >> 16, engine.kpc & 0xffff, engine.psr, + g_cur_dfcyc); + + g_num_irq++; + if(g_wait_pending) { + /* step over WAI instruction */ + engine.kpc = (engine.kpc & 0xff0000) | + ((engine.kpc + 1) & 0xffff); + g_wait_pending = 0; + } + + if(engine.psr & 0x100) { + /* Emulation */ + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, engine.kpc & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, (engine.psr & 0xef), 1); + /* Clear B bit in psr on stack */ + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + va = 0xfffffe; + } else { + /* native */ + set_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.kpc & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.psr & 0xff, 1); + engine.stack = ((engine.stack -1) & 0xffff); + + va = 0xffffee; + } + + va = moremem_fix_vector_pull(va); + new_kpc = get_memory_c(va); + new_kpc = new_kpc + (get_memory_c(va + 1) << 8); + + engine.psr = ((engine.psr & 0x1f3) | 0x4); + + engine.kpc = new_kpc; + HALT_ON(HALT_ON_IRQ, "Halting on IRQ\n"); + +} + +double g_dtime_last_vbl = 0.0; +double g_dtime_expected = (1.0/VBL_RATE); // Approximately 1.0/60.0 + +int g_scan_int_events = 0; + +void +show_dtime_array() +{ + double dfirst_time; + double first_total_cycs; + int i; + int pos; + + dfirst_time = 0.0; + first_total_cycs = 0.0; + + + for(i = 0; i < 60; i++) { + pos = (g_vbl_index_count + i) % 60; + printf("%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f " + "exp:%.5f p:%2.2f ep:%2.2f\n", + i, pos, + dtime_array[pos] - dfirst_time, + g_dadjcycs_array[pos] - first_total_cycs, + g_dtime_this_vbl_array[pos], + g_dtime_exp_array[pos] - dfirst_time, + g_dtime_pmhz_array[pos], + g_dtime_eff_pmhz_array[pos]); + dfirst_time = dtime_array[pos]; + first_total_cycs = g_dadjcycs_array[pos]; + } +} + + +void +update_60hz(dword64 dfcyc, double dtime_now) +{ + register word32 end_time; + char status_buf[1024]; + char sim_mhz_buf[128]; + char total_mhz_buf[128]; + char sp_buf[128]; + char *sim_mhz_ptr, *total_mhz_ptr, *code_str1, *code_str2, *sp_str; + dword64 planned_dcyc; + double eff_pmhz, predicted_pmhz, recip_predicted_pmhz; + double dtime_this_vbl_sim, dtime_diff_1sec, dratio, dtime_diff; + double dtime_till_expected, dtime_this_vbl, dadjcycs_this_vbl; + double dadj_cycles_1sec, dnatcycs_1sec; + int tmp, doit_3_persec, cur_vbl_index, prev_vbl_index; + + /* NOTE: this event is defined to occur before line 0 */ + /* It's actually happening at the start of the border for line (-1) */ + /* All other timings should be adjusted for this */ + + irq_printf("vbl_60hz: vbl: %d, dfcyc:%016llx, last_vbl_dfcyc:%016llx\n", + g_vbl_count, dfcyc, g_last_vbl_dfcyc); + + planned_dcyc = CYCLES_IN_16MS_RAW << 16; + + g_last_vbl_dfcyc = g_last_vbl_dfcyc + planned_dcyc; + + add_event_entry(g_last_vbl_dfcyc + planned_dcyc, EV_60HZ); + check_for_one_event_type(EV_60HZ, 0xff); + + cur_vbl_index = g_vbl_index_count; + + /* figure out dtime spent running SIM, not all the overhead */ + dtime_this_vbl_sim = g_cur_sim_dtime; + g_cur_sim_dtime = 0.0; + g_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim; + sim_time[cur_vbl_index] = dtime_this_vbl_sim; + + dadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index]; + + /* dtime_diff_1sec is dtime total spent over the last 60 ticks */ + dtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index]; + + dtime_array[cur_vbl_index] = dtime_now; + g_dadjcycs_array[cur_vbl_index] = g_dadjcycs; + + prev_vbl_index = cur_vbl_index; + cur_vbl_index = prev_vbl_index + 1; + if(cur_vbl_index >= 60) { + cur_vbl_index = 0; + } + g_vbl_index_count = cur_vbl_index; + + GET_ITIMER(end_time); + g_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl); + g_natcycs_lastvbl = end_time; + + if(prev_vbl_index == 0) { + if(g_sim_sum < (1.0/250.0)) { + sim_mhz_ptr = "???"; + g_sim_mhz = 250.0; + } else { + g_sim_mhz = (dadj_cycles_1sec / g_sim_sum) / + (1000.0*1000.0); + if(g_sim_mhz > 8000.0) { + g_sim_mhz = 8000.0; + } + snprintf(sim_mhz_buf, sizeof(sim_mhz_buf), "%6.2f", + g_sim_mhz); + sim_mhz_ptr = sim_mhz_buf; + } + if(dtime_diff_1sec < (1.0/250.0)) { + total_mhz_ptr = "???"; + } else { + snprintf(total_mhz_buf, sizeof(total_mhz_buf), "%6.2f", + (dadj_cycles_1sec / dtime_diff_1sec) / + (1000000.0)); + total_mhz_ptr = total_mhz_buf; + } + + switch(g_limit_speed) { + case 1: sp_str = "1Mhz"; break; + case 2: sp_str = "2.8Mhz"; break; + case 3: sp_str = "8.0Mhz"; break; + default: sp_str = "Unlimited"; break; + } + if(g_limit_speed == 3) { // ZipGS + snprintf(sp_buf, sizeof(sp_buf), "%1.1fMHz", + g_zip_pmhz); + sp_str = sp_buf; + } + + snprintf(status_buf, sizeof(status_buf), "dfcyc:%7.1f sim " + "MHz:%s Eff MHz:%s, sec:%1.3f vol:%02x Limit:%s", + (double)(dfcyc >> 20)/65536.0, sim_mhz_ptr, + total_mhz_ptr, dtime_diff_1sec, g_doc_vol, sp_str); + video_update_status_line(0, status_buf); + + if(g_video_line_update_interval == 0) { + if(g_sim_mhz > 12.0) { + /* just set video line_ref_amt to 1 */ + g_line_ref_amt = 1; + } else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) { + g_line_ref_amt = 8; + } + } else { + g_line_ref_amt = g_video_line_update_interval; + } + + dnatcycs_1sec = g_dnatcycs_1sec; + if(g_dnatcycs_1sec < (1000.0*1000.0)) { + /* make it so large that all %'s become 0 */ + dnatcycs_1sec = 800.0*1000.0*1000.0*1000.0; + } + dnatcycs_1sec = dnatcycs_1sec / 100.0; /* eff mult by 100 */ + + g_video_pixel_dcount = 0; + + code_str1 = ""; + code_str2 = ""; + if(g_code_yellow) { + code_str1 = "Code: Yellow"; + code_str2 = "Emulated state suspect"; + } + if(g_code_red) { + code_str1 = "Code: RED"; + code_str2 = "Emulated state corrupt?"; + } + snprintf(status_buf, sizeof(status_buf), "sleep_dtime:%8.6f, " + "out_16ms:%8.6f, in_16ms:%8.6f, snd_pl:%d", + g_dtime_in_sleep, g_dtime_outside_run_16ms, + g_dtime_in_run_16ms, g_num_snd_plays); + video_update_status_line(1, status_buf); + + draw_iwm_status(2, status_buf); + + snprintf(status_buf, sizeof(status_buf), " KEGS v%-6s " + "Press F4 for Config Menu %s %s", + g_kegs_version_str, code_str1, code_str2); + video_update_status_line(3, status_buf); + + g_status_refresh_needed = 1; + + g_num_irq = 0; + g_num_brk = 0; + g_num_cop = 0; + g_num_enter_engine = 0; + g_io_amt = 0; + g_engine_action = 0; + g_engine_recalc_event = 0; + g_engine_scan_int = 0; + g_engine_doc_int = 0; + + g_cycs_in_40col = 0; + g_cycs_in_xredraw = 0; + g_cycs_in_refresh_line = 0; + g_dnatcycs_1sec = 0.0; + g_dtime_outside_run_16ms = 0.0; + g_dtime_in_run_16ms = 0.0; + g_refresh_bytes_xfer = 0; + + g_dtime_in_sleep = 0; + g_num_snd_plays = 0; + g_num_recalc_snd_parms = 0; + } + + dtime_this_vbl = dtime_now - g_dtime_last_vbl; + if(dtime_this_vbl < 0.001) { + dtime_this_vbl = 0.001; + } + + g_dtime_last_vbl = dtime_now; + + dadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs; + g_last_vbl_dadjcycs = g_dadjcycs; + + g_dtime_expected += (1.0/VBL_RATE); // Approx. 1/60 + + eff_pmhz = (dadjcycs_this_vbl / dtime_this_vbl) / DCYCS_1_MHZ; + + /* using eff_pmhz, predict how many cycles can be run by */ + /* g_dtime_expected */ + + dtime_till_expected = g_dtime_expected - dtime_now; + + dratio = VBL_RATE * dtime_till_expected; // Approx. 60*dtime_exp + + predicted_pmhz = eff_pmhz * dratio; + + if(! (predicted_pmhz < (1.4 * g_projected_pmhz))) { + predicted_pmhz = 1.4 * g_projected_pmhz; + } + + if(! (predicted_pmhz > (0.7 * g_projected_pmhz))) { + predicted_pmhz = 0.7 * g_projected_pmhz; + } + + if(!(predicted_pmhz >= 1.0)) { + irq_printf("predicted: %f, setting to 1.0\n", predicted_pmhz); + predicted_pmhz = 1.0; + } + + if(!(predicted_pmhz < 4500.0)) { + irq_printf("predicted: %f, set to 1900.0\n", predicted_pmhz); + predicted_pmhz = 4500.0; + } + + recip_predicted_pmhz = 1.0/predicted_pmhz; + g_projected_pmhz = predicted_pmhz; + + g_recip_projected_pmhz_unl.dplus_1 = (dword64) + (65536 * recip_predicted_pmhz); + g_recip_projected_pmhz_unl.dplus_x_minus_1 = + (dword64)(65536 * (1.01 - recip_predicted_pmhz)); + + if(dtime_till_expected < -0.125) { + /* If we were way off, get back on track */ + /* this happens because our sim took much longer than */ + /* expected, so we're going to skip some VBL */ + irq_printf("adj1: dtexp:%f, dt_new:%f\n", + g_dtime_expected, dtime_now); + + dtime_diff = -dtime_till_expected; + + irq_printf("dtime_till_exp:%f, dtime_diff:%f, dfcyc:%016llx\n", + dtime_till_expected, dtime_diff, dfcyc); + + g_dtime_expected += dtime_diff; + } + + g_dtime_sleep = 0.0; + if(dtime_till_expected > (1.0/VBL_RATE)) { + /* we're running fast, usleep */ + g_dtime_sleep = dtime_till_expected - (1.0/VBL_RATE); + } +#if 0 + printf("Sleep %f, till_exp:%f, dtime_now:%f, exp:%f\n", + g_dtime_sleep, dtime_till_expected, dtime_now, + g_dtime_expected); +#endif + + g_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl; + g_dtime_exp_array[prev_vbl_index] = g_dtime_expected; + g_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz; + g_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz; + + + if(g_c041_val & C041_EN_VBL_INTS) { + add_event_vbl(); + } + + g_25sec_cntr++; + if(g_25sec_cntr >= 16) { + g_25sec_cntr = 0; + if(g_c041_val & C041_EN_25SEC_INTS) { + add_irq(IRQ_PENDING_C046_25SEC); + g_c046_val |= 0x10; + irq_printf("Setting c046 .25 sec int, g_irq_pend:%d\n", + g_irq_pending); + } + } + + g_1sec_cntr++; + if(g_1sec_cntr >= 60) { + g_1sec_cntr = 0; + tmp = g_c023_val; + tmp |= 0x40; /* set 1sec int */ + if(tmp & 0x04) { + tmp |= 0x80; + add_irq(IRQ_PENDING_C023_1SEC); + irq_printf("Setting c023 to %02x irq_pend: %d\n", + tmp, g_irq_pending); + } + g_c023_val = tmp; + } + + if(!g_scan_int_events) { + check_scan_line_int(0); + } + + doit_3_persec = 0; + if(g_config_iwm_vbl_count > 0) { + g_config_iwm_vbl_count--; + } else { + g_config_iwm_vbl_count = 20; + doit_3_persec = 1; + } + + iwm_vbl_update(); + config_vbl_update(doit_3_persec); + + sound_update(dfcyc); + clock_update(); + scc_update(dfcyc); + paddle_update_buttons(); +} + +void +do_vbl_int() +{ + if(g_c041_val & C041_EN_VBL_INTS) { + g_c046_val |= 0x08; + add_irq(IRQ_PENDING_C046_VBL); + irq_printf("Setting c046 vbl_int_status to 1, irq_pend: %d\n", + g_irq_pending); + } +} + +void +do_scan_int(dword64 dfcyc, int line) +{ + int c023_val; + + if(dfcyc) { + // Avoid unused param warning + } + + g_scan_int_events = 0; + g_dfcyc_scan_int = 0; + + c023_val = g_c023_val; + if(c023_val & 0x20) { + halt_printf("c023 scan_int and another on line %03x\n", line); + } + +#if 0 + dvbl = (dfcyc >> 16) / 17030; + dline = ((dfcyc >> 16) - (dvbl * 17030)) / 65; + printf("do_scan_int at time %lld (%lld,line %lld), line:%d, SCB:%02x, " + "a2_stat_ok:%d, c023:%02x\n", dfcyc >> 16, dvbl, dline, line, + (g_slow_memory_ptr[0x19d00 + line] & 0x40), + (g_cur_a2_stat & ALL_STAT_SUPER_HIRES) != 0, c023_val); + for(i = 0; i < 200; i++) { + if(g_slow_memory_ptr[0x19d00 + i] & 0x40) { + printf(" Line %d has SCB:%02x\n", i, + g_slow_memory_ptr[0x19d00 + i]); + } + } +#endif + + /* make sure scan int is still enabled for this line */ + if((g_slow_memory_ptr[0x19d00 + line] & 0x40) && + (g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { + /* valid interrupt, do it */ + c023_val |= 0xa0; /* vgc_int and scan_int */ + if(c023_val & 0x02) { + add_irq(IRQ_PENDING_C023_SCAN); + irq_printf("Setting c023 to %02x, irq_pend: %d\n", + c023_val, g_irq_pending); + } + g_c023_val = c023_val; + HALT_ON(HALT_ON_SCAN_INT, "In do_scan_int\n"); + } else { + /* scan int bit cleared on scan line control byte */ + /* look for next line, if any */ + check_scan_line_int(line+1); + } +} + +void +check_scan_line_int(int cur_video_line) +{ + dword64 ddelay; + int start, line; + int i; + + /* Called during VBL interrupt phase */ + + if(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { + return; + } + + if(g_c023_val & 0x20) { + /* don't check for any more */ + return; + } + + start = cur_video_line; + if(start < 0) { + halt_printf("check_scan_line_int: cur_video_line: %d\n", + cur_video_line); + start = 0; + } + + for(line = start; line < 200; line++) { + i = line; + + if(i < 0 || i >= 200) { + halt_printf("check_new_scan_int:i:%d, line:%d, st:%d\n", + i, line, start); + i = 0; + } + if(g_slow_memory_ptr[0x19d00 + i] & 0x40) { + irq_printf("Adding scan_int for line %d\n", i); + ddelay = (65ULL * line) << 16; + add_event_scan_int(g_last_vbl_dfcyc + ddelay, line); + g_scan_int_events = 1; + break; + } + } +} + +void +check_for_new_scan_int(dword64 dfcyc) +{ + int cur_video_line; + + cur_video_line = get_lines_since_vbl(dfcyc) >> 8; + check_scan_line_int(cur_video_line); +} + +void +scb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val) +{ + if(new_val & (~old_val) & 0x40) { + check_for_new_scan_int(dfcyc); + } + if(addr) { + } +} + +void +init_reg() +{ + memset(&engine, 0, sizeof(engine)); + + engine.acc = 0; + engine.xreg = 0; + engine.yreg = 0; + engine.stack = 0x1ff; + engine.direct = 0; + engine.psr = 0x134; + engine.fplus_ptr = 0; +} + +void +handle_action(word32 ret) +{ + int type, arg; + + type = ret & 0xff; + arg = ret >> 8; + switch(type) { + case RET_BREAK: + do_break(arg); + break; + case RET_COP: + do_cop(arg); + break; + case RET_IRQ: + irq_printf("Special fast IRQ response. irq_pending: %x\n", + g_irq_pending); + break; + case RET_WDM: + do_wdm(arg); + break; + case RET_STP: + do_stp(); + break; + case RET_TOOLTRACE: + dbg_log_info(g_cur_dfcyc, engine.kpc, engine.xreg, + (engine.stack << 16) | 0xe100); + break; + default: + halt_printf("Unknown special action: %08x!\n", ret); + } +} + + +void +do_break(word32 ret) +{ + printf("I think I got a break, second byte: %02x!\n", ret); + printf("kpc: %06x\n", engine.kpc); + + halt_printf("do_break, kpc: %06x\n", engine.kpc); +} + +void +do_cop(word32 ret) +{ + halt_printf("COP instr %02x!\n", ret); + fflush(stdout); +} + +void +do_wdm(word32 arg) +{ + if(arg == 0x00c7) { + // WDM, 0xc7, 0x00: WDM in Slot 7 + if(engine.psr & 0x40) { + // Overflow set: $C700 called + do_c700(arg); + } else if(engine.psr & 1) { // V=0, C=1: $C70D called + do_c70d(arg); + } else { // V=0, C=0, $C70A called + do_c70a(arg); + } + return; + } + if(arg == 0x00ea) { + // WDM, 0xea, 0x00: WDM emulator ID + do_wdm_emulator_id(); + return; + } + if((arg == 0xeaea) && ((engine.psr & 0x171) == 0x41) && + (engine.acc == 0x4d44)) { + // WDM $EA,$EA with V=1,C=1 and ACC=0x4d44 ("EM") + engine.psr = engine.psr & 0x1bf; // V=0 + // printf("WDM $EA,$EA, cleared V=0, psr:%04x\n", engine.psr); + return; + } + + switch(arg & 0xff) { + case 0x8d: /* Bouncin Ferno does WDM 8d */ + break; + case 0xea: // Detectiong feature, don't flag an error + break; + case 0xfc: // HOST.FST "head_call" for ATINIT for ProDOS 8 + case 0xfd: // HOST.FST "tail_call" for ATINIT for ProDOS 8 + case 0xff: // HOST.FST "call_host" for GS/OS driver + break; + default: + halt_printf("do_wdm: %04x!\n", arg); + } +} + +void +do_wai() +{ + halt_printf("do_wai!\n"); +} + +void +do_stp() +{ + if(!g_stp_pending) { + g_stp_pending = 1; + halt_printf("Hit STP instruction at: %06x, press RESET to " + "continue\n", engine.kpc); + } +} + +char g_emulator_name[64]; + +void +do_wdm_emulator_id() +{ + word32 addr, version, subvers; + int maxlen, len, c, got_dot; + int i; + + // WDM, $EA, $00: WDM emulator ID + // dbank.acc = address to write emulator description string + // X = size of buffer to hold string + // Y = 0 (not checked) + // Returns: X: actual length of emulator string (always <= + // value in X at call) + // ACC: emulator version as: $VVMN as BCD, so 1.32 is $0132 + // Y: Emulation feature flags. bit 0: $c06c-$c06f timer available + // Works in emulation mode + + printf("WDM EA at %06x. acc:%04x, dbank:%02x xreg:%04x\n", engine.kpc, + engine.acc, engine.dbank, engine.xreg); + maxlen = engine.xreg; + cfg_strncpy(&g_emulator_name[0], "KEGS v", 64); + cfg_strlcat(&g_emulator_name[0], &g_kegs_version_str[0], 64); + len = (int)strlen(&g_emulator_name[0]); + addr = engine.acc; + engine.xreg = 0; + for(i = 0; i < len; i++) { + if(i >= maxlen) { + break; + } + addr = (engine.dbank << 8) | (addr & 0xffff); + set_memory_c(addr, 0x80 | g_emulator_name[i], 1); + addr++; + engine.xreg = i + 1; + } + + version = 0; + subvers = 0; + len = (int)strlen(&g_kegs_version_str[0]); + got_dot = 0; + for(i = 0; i < len; i++) { + c = g_kegs_version_str[i]; + if(c == '.') { + got_dot++; + } + if(got_dot >= 3) { + break; + } + if((c >= '0') && (c <= '9')) { + c = c - '0'; + if(got_dot) { + subvers = (subvers << 4) | c; + got_dot++; + } else { + version = (version << 4) | c; + } + } + } + + engine.acc = ((version & 0xff) << 8) | (subvers & 0xff); + engine.yreg = 0x01; // $C06C timer available +} + +void +size_fail(int val, word32 v1, word32 v2) +{ + halt_printf("Size failure, val: %08x, %08x %08x\n", val, v1, v2); +} + +int +fatal_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + + if(g_fatal_log < 0) { + g_fatal_log = 0; + } + ret = kegs_vprintf(fmt, ap); + va_end(ap); + + return ret; +} + +int +kegs_vprintf(const char *fmt, va_list ap) +{ + char *bufptr, *buf2ptr; + int len, ret; + + bufptr = malloc(4096); + ret = vsnprintf(bufptr, 4090, fmt, ap); + + len = (int)strlen(bufptr); + if(g_fatal_log >= 0 && g_fatal_log < MAX_FATAL_LOGS) { + buf2ptr = malloc(len+1); + memcpy(buf2ptr, bufptr, len+1); + g_fatal_log_strs[g_fatal_log++] = buf2ptr; + } + (void)must_write(1, (byte *)bufptr, len); + if(g_debug_file_fd >= 0) { + (void)must_write(g_debug_file_fd, (byte *)bufptr, len); + } + free(bufptr); + + return ret; +} + +dword64 +must_write(int fd, byte *bufptr, dword64 dsize) +{ + dword64 dlen; + long long ret; + word32 this_len; + + dlen = dsize; + while(dlen != 0) { + // Support Windows64, which can only rd/wr 2GB max per call + this_len = (1UL << 30); + if(dlen < this_len) { + this_len = (word32)dlen; + } + ret = write(fd, bufptr, this_len); + if(ret >= 0) { + dlen -= ret; + bufptr += ret; + } else if((errno != EAGAIN) && (errno != EINTR)) { + return 0; // just get out + } + } + return dsize; +} + +void +clear_fatal_logs() +{ + int i; + + for(i = 0; i < g_fatal_log; i++) { + free(g_fatal_log_strs[i]); + g_fatal_log_strs[i] = 0; + } + g_fatal_log = -1; +} + +char * +kegs_malloc_str(const char *in_str) +{ + char *str; + int len; + + len = (int)strlen(in_str) + 1; + str = malloc(len); + memcpy(str, in_str, len); + + return str; +} + +dword64 +kegs_lseek(int fd, dword64 offs, int whence) +{ +#ifdef _WIN32 + return _lseeki64(fd, offs, whence); +#else + return lseek(fd, offs, whence); +#endif +} + diff --git a/gsplus/src/size_c.h b/gsplus/src/size_c.h new file mode 100644 index 0000000..5bc671e --- /dev/null +++ b/gsplus/src/size_c.h @@ -0,0 +1,269 @@ +// "@(#)$KmKId: size_c.h,v 1.2 2023-11-12 15:32:17+00 kentd Exp $" +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2020 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + + + 0x1, /* 00 */ /* brk */ + 0x1, /* 01 */ /* ORA (Dloc,X) */ + 0x1, /* 02 */ /* COP */ + 0x1, /* 03 */ /* ORA Disp8,S */ + 0x1, /* 04 */ /* TSB Dloc */ + 0x1, /* 05 */ /* ORA Dloc */ + 0x1, /* 06 */ /* ASL Dloc */ + 0x1, /* 07 */ /* ORA [Dloc] */ + 0x0, /* 08 */ /* PHP */ + 0x4, /* 09 */ /* ORA #imm */ + 0x0, /* 0a */ /* ASL a */ + 0x0, /* 0b */ /* PHD */ + 0x2, /* 0c */ /* TSB abs */ + 0x2, /* 0d */ /* ORA abs */ + 0x2, /* 0e */ /* ASL abs */ + 0x3, /* 0f */ /* ORA long */ + 0x1, /* 10 */ /* BPL disp8 */ + 0x1, /* 11 */ /* ORA (),y */ + 0x1, /* 12 */ /* ORA () */ + 0x1, /* 13 */ /* ORA (disp8,s),y */ + 0x1, /* 14 */ /* TRB Dloc */ + 0x1, /* 15 */ /* ORA Dloc,x */ + 0x1, /* 16 */ /* ASL Dloc,x */ + 0x1, /* 17 */ /* ORA [],y */ + 0x0, /* 18 */ /* clc */ + 0x2, /* 19 */ /* ORA abs,y */ + 0x0, /* 1a */ /* INC a */ + 0x0, /* 1b */ /* TCS */ + 0x2, /* 1c */ /* TRB Abs */ + 0x2, /* 1d */ /* ORA Abs,X */ + 0x2, /* 1e */ /* ASL abs,x */ + 0x3, /* 1f */ /* ORA Long,x */ + 0x2, /* 20 */ /* JSR abs */ + 0x1, /* 21 */ /* AND (Dloc,X) */ + 0x3, /* 22 */ /* JSL Abslong */ + 0x1, /* 23 */ /* AND Disp8,S */ + 0x1, /* 24 */ /* BIT Dloc */ + 0x1, /* 25 */ /* AND Dloc */ + 0x1, /* 26 */ /* ROL Dloc */ + 0x1, /* 27 */ /* AND [Dloc] */ + 0x0, /* 28 */ /* PLP */ + 0x4, /* 29 */ /* AND #imm */ + 0x0, /* 2a */ /* ROL a */ + 0x0, /* 2b */ /* PLD */ + 0x2, /* 2c */ /* BIT abs */ + 0x2, /* 2d */ /* AND abs */ + 0x2, /* 2e */ /* ROL abs */ + 0x3, /* 2f */ /* AND long */ + 0x1, /* 30 */ /* BMI disp8 */ + 0x1, /* 31 */ /* AND (),y */ + 0x1, /* 32 */ /* AND () */ + 0x1, /* 33 */ /* AND (disp8,s),y */ + 0x1, /* 34 */ /* BIT Dloc,X */ + 0x1, /* 35 */ /* AND Dloc,x */ + 0x1, /* 36 */ /* ROL Dloc,x */ + 0x1, /* 37 */ /* AND [],y */ + 0x0, /* 38 */ /* SEC */ + 0x2, /* 39 */ /* AND abs,y */ + 0x0, /* 3a */ /* DEC a */ + 0x0, /* 3b */ /* TSC */ + 0x2, /* 3c */ /* BIT Abs,X */ + 0x2, /* 3d */ /* AND Abs,X */ + 0x2, /* 3e */ /* ROL abs,x */ + 0x3, /* 3f */ /* AND Long,x */ + 0x0, /* 40 */ /* RTI */ + 0x1, /* 41 */ /* EOR (Dloc,X) */ + 0x2, /* 42 */ /* WDM HACK: uses 2 args */ + 0x1, /* 43 */ /* EOR Disp8,S */ + 0x2, /* 44 */ /* MVP I,J */ + 0x1, /* 45 */ /* EOR Dloc */ + 0x1, /* 46 */ /* LSR Dloc */ + 0x1, /* 47 */ /* EOR [Dloc] */ + 0x0, /* 48 */ /* PHA */ + 0x4, /* 49 */ /* EOR #imm */ + 0x0, /* 4a */ /* LSR a */ + 0x0, /* 4b */ /* PHK */ + 0x2, /* 4c */ /* JMP abs */ + 0x2, /* 4d */ /* EOR abs */ + 0x2, /* 4e */ /* LSR abs */ + 0x3, /* 4f */ /* EOR long */ + 0x1, /* 50 */ /* BVC disp8 */ + 0x1, /* 51 */ /* EOR (),y */ + 0x1, /* 52 */ /* EOR () */ + 0x1, /* 53 */ /* EOR (disp8,s),y */ + 0x2, /* 54 */ /* MVN I,J */ + 0x1, /* 55 */ /* EOR Dloc,x */ + 0x1, /* 56 */ /* LSR Dloc,x */ + 0x1, /* 57 */ /* EOR [],y */ + 0x0, /* 58 */ /* CLI */ + 0x2, /* 59 */ /* EOR abs,y */ + 0x0, /* 5a */ /* PHY */ + 0x0, /* 5b */ /* TCD */ + 0x3, /* 5c */ /* JMP Long */ + 0x2, /* 5d */ /* EOR Abs,X */ + 0x2, /* 5e */ /* LSR abs,x */ + 0x3, /* 5f */ /* EOR Long,x */ + 0x0, /* 60 */ /* RTS */ + 0x1, /* 61 */ /* ADC (Dloc,X) */ + 0x2, /* 62 */ /* PER DISP16 */ + 0x1, /* 63 */ /* ADC Disp8,S */ + 0x1, /* 64 */ /* STZ Dloc */ + 0x1, /* 65 */ /* ADC Dloc */ + 0x1, /* 66 */ /* ROR Dloc */ + 0x1, /* 67 */ /* ADC [Dloc] */ + 0x0, /* 68 */ /* PLA */ + 0x4, /* 69 */ /* ADC #imm */ + 0x0, /* 6a */ /* ROR a */ + 0x0, /* 6b */ /* RTL */ + 0x2, /* 6c */ /* JMP (abs) */ + 0x2, /* 6d */ /* ADC abs */ + 0x2, /* 6e */ /* ROR abs */ + 0x3, /* 6f */ /* ADC long */ + 0x1, /* 70 */ /* BVS disp8 */ + 0x1, /* 71 */ /* ADC (),y */ + 0x1, /* 72 */ /* ADC () */ + 0x1, /* 73 */ /* ADC (disp8,s),y */ + 0x1, /* 74 */ /* STZ Dloc,X */ + 0x1, /* 75 */ /* ADC Dloc,x */ + 0x1, /* 76 */ /* ROR Dloc,x */ + 0x1, /* 77 */ /* ADC [],y */ + 0x0, /* 78 */ /* SEI */ + 0x2, /* 79 */ /* ADC abs,y */ + 0x0, /* 7a */ /* PLY */ + 0x0, /* 7b */ /* TDC */ + 0x2, /* 7c */ /* JMP (abs,x) */ + 0x2, /* 7d */ /* ADC Abs,X */ + 0x2, /* 7e */ /* ROR abs,x */ + 0x3, /* 7f */ /* ADC Long,x */ + 0x1, /* 80 */ /* BRA Disp8 */ + 0x1, /* 81 */ /* STA (Dloc,X) */ + 0x2, /* 82 */ /* BRL DISP16 */ + 0x1, /* 83 */ /* STA Disp8,S */ + 0x1, /* 84 */ /* STY Dloc */ + 0x1, /* 85 */ /* STA Dloc */ + 0x1, /* 86 */ /* STX Dloc */ + 0x1, /* 87 */ /* STA [Dloc] */ + 0x0, /* 88 */ /* DEY */ + 0x4, /* 89 */ /* BIT #imm */ + 0x0, /* 8a */ /* TXA */ + 0x0, /* 8b */ /* PHB */ + 0x2, /* 8c */ /* STY abs */ + 0x2, /* 8d */ /* STA abs */ + 0x2, /* 8e */ /* STX abs */ + 0x3, /* 8f */ /* STA long */ + 0x1, /* 90 */ /* BCC disp8 */ + 0x1, /* 91 */ /* STA (),y */ + 0x1, /* 92 */ /* STA () */ + 0x1, /* 93 */ /* STA (disp8,s),y */ + 0x1, /* 94 */ /* STY Dloc,X */ + 0x1, /* 95 */ /* STA Dloc,x */ + 0x1, /* 96 */ /* STX Dloc,y */ + 0x1, /* 97 */ /* STA [],y */ + 0x0, /* 98 */ /* TYA */ + 0x2, /* 99 */ /* STA abs,y */ + 0x0, /* 9a */ /* TXS */ + 0x0, /* 9b */ /* TXY */ + 0x2, /* 9c */ /* STX abs */ + 0x2, /* 9d */ /* STA Abs,X */ + 0x2, /* 9e */ /* STZ abs,x */ + 0x3, /* 9f */ /* STA Long,x */ + 0x5, /* a0 */ /* LDY #imm */ + 0x1, /* a1 */ /* LDA (Dloc,X) */ + 0x5, /* a2 */ /* LDX #imm */ + 0x1, /* a3 */ /* LDA Disp8,S */ + 0x1, /* a4 */ /* LDY Dloc */ + 0x1, /* a5 */ /* LDA Dloc */ + 0x1, /* a6 */ /* LDX Dloc */ + 0x1, /* a7 */ /* LDA [Dloc] */ + 0x0, /* a8 */ /* TAY */ + 0x4, /* a9 */ /* LDA #imm */ + 0x0, /* aa */ /* TAX */ + 0x0, /* ab */ /* PLB */ + 0x2, /* ac */ /* LDY abs */ + 0x2, /* ad */ /* LDA abs */ + 0x2, /* ae */ /* LDX abs */ + 0x3, /* af */ /* LDA long */ + 0x1, /* b0 */ /* BCS disp8 */ + 0x1, /* b1 */ /* LDA (),y */ + 0x1, /* b2 */ /* LDA () */ + 0x1, /* b3 */ /* LDA (disp8,s),y */ + 0x1, /* b4 */ /* LDY Dloc,X */ + 0x1, /* b5 */ /* LDA Dloc,x */ + 0x1, /* b6 */ /* LDX Dloc,y */ + 0x1, /* b7 */ /* LDA [],y */ + 0x0, /* b8 */ /* CLV */ + 0x2, /* b9 */ /* LDA abs,y */ + 0x0, /* ba */ /* TSX */ + 0x0, /* bb */ /* TYX */ + 0x2, /* bc */ /* LDY abs,x */ + 0x2, /* bd */ /* LDA Abs,X */ + 0x2, /* be */ /* LDX abs,y */ + 0x3, /* bf */ /* LDA Long,x */ + 0x5, /* c0 */ /* CPY #Imm */ + 0x1, /* c1 */ /* CMP (Dloc,X) */ + 0x1, /* c2 */ /* REP #8bit */ + 0x1, /* c3 */ /* CMP Disp8,S */ + 0x1, /* c4 */ /* CPY Dloc */ + 0x1, /* c5 */ /* CMP Dloc */ + 0x1, /* c6 */ /* DEC Dloc */ + 0x1, /* c7 */ /* CMP [Dloc] */ + 0x0, /* c8 */ /* INY */ + 0x4, /* c9 */ /* CMP #imm */ + 0x0, /* ca */ /* DEX */ + 0x0, /* cb */ /* WAI */ + 0x2, /* cc */ /* CPY abs */ + 0x2, /* cd */ /* CMP abs */ + 0x2, /* ce */ /* DEC abs */ + 0x3, /* cf */ /* CMP long */ + 0x1, /* d0 */ /* BNE disp8 */ + 0x1, /* d1 */ /* CMP (),y */ + 0x1, /* d2 */ /* CMP () */ + 0x1, /* d3 */ /* CMP (disp8,s),y */ + 0x1, /* d4 */ /* PEI Dloc */ + 0x1, /* d5 */ /* CMP Dloc,x */ + 0x1, /* d6 */ /* DEC Dloc,x */ + 0x1, /* d7 */ /* CMP [],y */ + 0x0, /* d8 */ /* CLD */ + 0x2, /* d9 */ /* CMP abs,y */ + 0x0, /* da */ /* PHX */ + 0x0, /* db */ /* STP */ + 0x2, /* dc */ /* JML (Abs) */ + 0x2, /* dd */ /* CMP Abs,X */ + 0x2, /* de */ /* DEC abs,x */ + 0x3, /* df */ /* CMP Long,x */ + 0x5, /* e0 */ /* CPX #Imm */ + 0x1, /* e1 */ /* SBC (Dloc,X) */ + 0x1, /* e2 */ /* SEP #8bit */ + 0x1, /* e3 */ /* SBC Disp8,S */ + 0x1, /* e4 */ /* CPX Dloc */ + 0x1, /* e5 */ /* SBC Dloc */ + 0x1, /* e6 */ /* INC Dloc */ + 0x1, /* e7 */ /* SBC [Dloc] */ + 0x0, /* e8 */ /* INX */ + 0x4, /* e9 */ /* SBC #imm */ + 0x0, /* ea */ /* NOP */ + 0x0, /* eb */ /* XBA */ + 0x2, /* ec */ /* CPX abs */ + 0x2, /* ed */ /* SBC abs */ + 0x2, /* ee */ /* INC abs */ + 0x3, /* ef */ /* SBC long */ + 0x1, /* f0 */ /* BEQ disp8 */ + 0x1, /* f1 */ /* SBC (),y */ + 0x1, /* f2 */ /* SBC () */ + 0x1, /* f3 */ /* SBC (disp8,s),y */ + 0x2, /* f4 */ /* PEA Imm */ + 0x1, /* f5 */ /* SBC Dloc,x */ + 0x1, /* f6 */ /* INC Dloc,x */ + 0x1, /* f7 */ /* SBC [],y */ + 0x0, /* f8 */ /* SED */ + 0x2, /* f9 */ /* SBC abs,y */ + 0x0, /* fa */ /* PLX */ + 0x0, /* fb */ /* XCE */ + 0x2, /* fc */ /* JSR (Abs,x) */ + 0x2, /* fd */ /* SBC Abs,X */ + 0x2, /* fe */ /* INC abs,x */ + 0x3, /* ff */ /* SBC Long,x */ + diff --git a/gsplus/src/smartport.c b/gsplus/src/smartport.c new file mode 100644 index 0000000..1c7a574 --- /dev/null +++ b/gsplus/src/smartport.c @@ -0,0 +1,809 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +extern int Verbose; +extern int Halt_on; +extern int g_rom_version; +extern int g_io_amt; +extern int g_highest_smartport_unit; +extern dword64 g_cur_dfcyc; + +extern Engine_reg engine; + +extern Iwm g_iwm; + +#define LEN_SMPT_LOG 16 +STRUCT(Smpt_log) { + word32 start_addr; + int cmd; + int rts_addr; + int cmd_list; + int extras; + int unit; + int buf; + int blk; +}; + +Smpt_log g_smpt_log[LEN_SMPT_LOG]; +int g_smpt_log_pos = 0; + +void +smartport_error(void) +{ + int pos; + int i; + + pos = g_smpt_log_pos; + printf("Smartport log pos: %d\n", pos); + for(i = 0; i < LEN_SMPT_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_SMPT_LOG - 1; + } + printf("%d:%d: t:%04x, cmd:%02x, rts:%04x, " + "cmd_l:%04x, x:%d, unit:%d, buf:%04x, blk:%04x\n", + i, pos, + g_smpt_log[pos].start_addr, + g_smpt_log[pos].cmd, + g_smpt_log[pos].rts_addr, + g_smpt_log[pos].cmd_list, + g_smpt_log[pos].extras, + g_smpt_log[pos].unit, + g_smpt_log[pos].buf, + g_smpt_log[pos].blk); + } +} +void +smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list) +{ + int pos; + + pos = g_smpt_log_pos; + if(start_addr != 0) { + g_smpt_log[pos].start_addr = start_addr; + g_smpt_log[pos].cmd = cmd; + g_smpt_log[pos].rts_addr = rts_addr; + g_smpt_log[pos].cmd_list = cmd_list; + g_smpt_log[pos].extras = 0; + g_smpt_log[pos].unit = 0; + g_smpt_log[pos].buf = 0; + g_smpt_log[pos].blk = 0; + } else { + pos--; + if(pos < 0) { + pos = LEN_SMPT_LOG - 1; + } + g_smpt_log[pos].extras = 1; + g_smpt_log[pos].unit = cmd; + g_smpt_log[pos].buf = rts_addr; + g_smpt_log[pos].blk = cmd_list; + } + pos++; + if(pos >= LEN_SMPT_LOG) { + pos = 0; + } + g_smpt_log_pos = pos; +} + +void +do_c70d(word32 arg0) +{ + dword64 dsize; + word32 status_ptr, rts_addr, cmd_list, cmd_list_lo, cmd_list_mid; + word32 cmd_list_hi, status_ptr_lo, status_ptr_mid, status_ptr_hi; + word32 rts_lo, rts_hi, buf_ptr_lo, buf_ptr_hi, buf_ptr, mask, cmd; + word32 block_lo, block_mid, block_hi, block_hi2, unit, ctl_code; + word32 ctl_ptr_lo, ctl_ptr_hi, ctl_ptr, block, stat_val; + int param_cnt, ret, ext, slot; + int i; + + slot = (engine.kpc >> 8) & 7; + set_memory_c(0x7f8, 0xc0 | slot, 1); + + if((engine.psr & 0x100) == 0) { + disk_printf("c70d %02x called in native mode!\n", arg0); + if((engine.psr & 0x30) != 0x30) { + halt_printf("c70d called native, psr: %03x!\n", + engine.psr); + } + } + + engine.stack = ((engine.stack + 1) & 0xff) + 0x100; + rts_lo = get_memory_c(engine.stack); + engine.stack = ((engine.stack + 1) & 0xff) + 0x100; + rts_hi = get_memory_c(engine.stack); + rts_addr = (rts_lo + (256*rts_hi) + 1) & 0xffff; + disk_printf("rts_addr: %04x\n", rts_addr); + + cmd = get_memory_c(rts_addr); + cmd_list_lo = get_memory_c((rts_addr + 1) & 0xffff); + cmd_list_mid = get_memory_c((rts_addr + 2) & 0xffff); + cmd_list_hi = 0; + mask = 0xffff; + ext = 0; + if(cmd & 0x40) { + ext = 2; + mask = 0xffffff; + cmd_list_hi = get_memory_c((rts_addr + 3) & 0xffff); + } + + cmd_list = cmd_list_lo + (256*cmd_list_mid) + (65536*cmd_list_hi); + + disk_printf("cmd: %02x, cmd_list: %06x\n", cmd, cmd_list); + param_cnt = get_memory_c(cmd_list); + unit = get_memory_c((cmd_list + 1) & mask); + ctl_code = get_memory_c((cmd_list + 4 + ext) & mask); + + smartport_log(0xc70d, cmd, rts_addr, cmd_list); + dbg_log_info(g_cur_dfcyc, (rts_addr << 16) | (unit << 8) | cmd, + cmd_list, 0xc70d); +#if 0 + if(cmd != 0x41) { + printf("SMTPT: c70d %08x, %08x at %016llx\n", + (rts_addr << 16) | (unit << 8) | cmd, cmd_list, + g_cur_dfcyc); + } +#endif + ret = 0; + if((unit >= 1) && (unit <= MAX_C7_DISKS) && ext) { + if(g_iwm.smartport[unit-1].just_ejected) { + ret = 0x2e; // DISKSW error + } + g_iwm.smartport[unit-1].just_ejected = 0; + } + + switch(cmd & 0x3f) { + case 0x00: /* Status == 0x00 and 0x40 */ + if(param_cnt != 3) { + disk_printf("param_cnt %d is != 3!\n", param_cnt); + ret = 0x04; // BADPCNT + break; + } + status_ptr_lo = get_memory_c((cmd_list+2) & mask); + status_ptr_mid = get_memory_c((cmd_list+3) & mask); + status_ptr_hi = 0; + if(cmd & 0x40) { + status_ptr_hi = get_memory_c((cmd_list+4) & mask); + } + + status_ptr = status_ptr_lo + (256*status_ptr_mid) + + (65536*status_ptr_hi); + + smartport_log(0, unit, status_ptr, ctl_code); + dbg_log_info(g_cur_dfcyc, (ctl_code << 16) | unit, + cmd_list, 0xc700); + + disk_printf("unit: %02x, status_ptr: %06x, code: %02x\n", + unit, status_ptr, ctl_code); + if((unit == 0) && (ctl_code == 0)) { + /* Smartport driver status */ + /* see technotes/smpt/tn-smpt-002 */ + set_memory_c(status_ptr, MAX_C7_DISKS, 1); + set_memory_c(status_ptr+1, 0xff, 1); // intrpt stat + set_memory16_c(status_ptr+2, 0x004b, 1); // vendor id + set_memory16_c(status_ptr+4, 0x1000, 1); // version + set_memory16_c(status_ptr+6, 0x0000, 1); + //printf(" driver status, highest_unit:%02x\n", + // g_highest_smartport_unit+1); + + engine.xreg = 8; + engine.yreg = 0; + } else if((unit > 0) && (ctl_code == 0)) { + /* status for unit x */ + if((unit > MAX_C7_DISKS) || + (g_iwm.smartport[unit-1].fd < 0)) { + stat_val = 0x80; + dsize = 0; + ret = 0; // Not DISK_SWITCHed error + } else { + stat_val = 0xf8; + dsize = g_iwm.smartport[unit-1].dimage_size; + dsize = (dsize+511) / 512; + if(g_iwm.smartport[unit-1].write_prot) { + stat_val |= 4; // Write prot + } + } +#if 0 + printf(" status unit:%02x just_ejected:%d, " + "stat_val:%02x\n", unit, + g_iwm.smartport[unit-1].just_ejected, + stat_val); +#endif + set_memory_c(status_ptr, stat_val, 1); + set_memory24_c(status_ptr + 1, (word32)dsize); + engine.xreg = 4; + if(cmd & 0x40) { + set_memory_c(status_ptr + 4, + (dsize >> 24) & 0xff, 1); + engine.xreg = 5; + } + engine.yreg = 0; + disk_printf("just finished unit %d, stat 0\n", unit); + } else if(ctl_code == 3) { + if((unit > MAX_C7_DISKS) || + (g_iwm.smartport[unit-1].fd < 0)) { + stat_val = 0x80; + dsize = 0; + ret = 0; // Not a disk-switched error + } else { + stat_val = 0xf8; + dsize = g_iwm.smartport[unit-1].dimage_size; + dsize = (dsize + 511) / 512; + } + if(cmd & 0x40) { + disk_printf("extended for stat_code 3!\n"); + } + /* DIB for unit 1 */ + set_memory_c(status_ptr, stat_val, 1); + set_memory24_c(status_ptr + 1, (word32)dsize); + if(cmd & 0x40) { + set_memory_c(status_ptr + 4, + (dsize >> 24) & 0xff, 1); + status_ptr++; + } + set_memory_c(status_ptr + 4, 4, 1); + for(i = 5; i < 21; i++) { + set_memory_c(status_ptr + i, 0x20, 1); + } + set_memory_c(status_ptr + 5, 'K', 1); + set_memory_c(status_ptr + 6, 'E', 1); + set_memory_c(status_ptr + 7, 'G', 1); + set_memory_c(status_ptr + 8, 'S', 1); + + // Profile hard disk supporting extended calls+disk_sw + set_memory16_c(status_ptr + 21, 0xc002, 1); + set_memory16_c(status_ptr + 23, 0x0000, 1); + + if(cmd & 0x40) { + engine.xreg = 26; + } else { + engine.xreg = 25; + } +#if 0 + printf(" DIB unit:%02x just_ejected:%d, " + "stat_val:%02x\n", unit, + g_iwm.smartport[unit-1].just_ejected, + stat_val); +#endif + engine.yreg = 0; + + disk_printf("Just finished unit %d, stat 3\n", unit); + if(unit == 0 || unit > MAX_C7_DISKS) { + ret = 0x28; // NODRIVE error + } + } else { + printf("cmd: 00, unknown unit/status code %02x!\n", + ctl_code); + ret = 0x21; // BADCTL + } + break; + case 0x01: /* Read Block == 0x01 and 0x41 */ + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + ret = 0x04; // BADPCNT + break; + } + buf_ptr_lo = get_memory_c((cmd_list+2) & mask); + buf_ptr_hi = get_memory_c((cmd_list+3) & mask); + + buf_ptr = buf_ptr_lo + (256*buf_ptr_hi); + if(cmd & 0x40) { + buf_ptr_lo = get_memory_c((cmd_list+4) & mask); + buf_ptr_hi = get_memory_c((cmd_list+5) & mask); + buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536; + cmd_list += 2; + } + block_lo = get_memory_c((cmd_list+4) & mask); + block_mid = get_memory_c((cmd_list+5) & mask); + block_hi = get_memory_c((cmd_list+6) & mask); + block_hi2 = 0; + if(cmd & 0x40) { + block_hi2 = get_memory_c((cmd_list+7) & mask); + } + block = (block_hi2 << 24) | (block_hi << 16) | + (block_mid << 8) | block_lo; + disk_printf("smartport read unit %d of block %06x to %06x\n", + unit, block, buf_ptr); + if(unit < 1 || unit > MAX_C7_DISKS) { + halt_printf("Unknown unit #: %d\n", unit); + } + + smartport_log(0, unit - 1, buf_ptr, block); + + if(ret == 0) { + ret = do_read_c7(unit - 1, buf_ptr, block); + } + engine.xreg = 0; + engine.yreg = 2; + break; + case 0x02: /* Write Block == 0x02 and 0x42 */ + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + ret = 0x04; // BADPCNT + break; + } + buf_ptr_lo = get_memory_c((cmd_list+2) & mask); + buf_ptr_hi = get_memory_c((cmd_list+3) & mask); + + buf_ptr = buf_ptr_lo + (256*buf_ptr_hi); + if(cmd & 0x40) { + buf_ptr_lo = get_memory_c((cmd_list+4) & mask); + buf_ptr_hi = get_memory_c((cmd_list+5) & mask); + buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536; + cmd_list += 2; + } + block_lo = get_memory_c((cmd_list+4) & mask); + block_mid = get_memory_c((cmd_list+5) & mask); + block_hi = get_memory_c((cmd_list+6) & mask); + block_hi2 = 0; + if(cmd & 0x40) { + block_hi2 = get_memory_c((cmd_list+7) & mask); + } + block = (block_hi2 << 24) | (block_hi << 16) | + (block_mid << 8) | block_lo; + disk_printf("smartport write unit %d of block %04x from %04x\n", + unit, block, buf_ptr); + if(unit < 1 || unit > MAX_C7_DISKS) { + halt_printf("Unknown unit #: %d\n", unit); + } + + smartport_log(0, unit - 1, buf_ptr, block); + + if(ret == 0) { + ret = do_write_c7(unit - 1, buf_ptr, block); + } + engine.xreg = 0; + engine.yreg = 2; + + HALT_ON(HALT_ON_C70D_WRITES, "c70d Write done\n"); + break; + case 0x03: /* Format == 0x03 and 0x43 */ + if(param_cnt != 1) { + halt_printf("param_cnt %d is != 1!\n", param_cnt); + ret = 0x04; // BADPCNT + break; + } + if((unit < 1) || (unit > MAX_C7_DISKS)) { + halt_printf("Unknown unit #: %d\n", unit); + ret = 0x11; // BADUNIT + } + + smartport_log(0, unit - 1, 0, 0); + + if(ret == 0) { + ret = do_format_c7(unit - 1); + } + engine.xreg = 0; + engine.yreg = 2; + + HALT_ON(HALT_ON_C70D_WRITES, "c70d Format done\n"); + break; + case 0x04: /* Control == 0x04 and 0x44 */ + if(cmd == 0x44) { + halt_printf("smartport code 0x44 not supported\n"); + } + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + break; + } + ctl_ptr_lo = get_memory_c((cmd_list+2) & mask); + ctl_ptr_hi = get_memory_c((cmd_list+3) & mask); + ctl_ptr = (ctl_ptr_hi << 8) + ctl_ptr_lo; + if(cmd & 0x40) { + ctl_ptr_lo = get_memory_c((cmd_list+4) & mask); + ctl_ptr_hi = get_memory_c((cmd_list+5) & mask); + ctl_ptr += ((ctl_ptr_hi << 8) + ctl_ptr_lo) << 16; + cmd_list += 2; + } + + switch(ctl_code) { + case 0x00: + printf("Performing a reset on unit %d\n", unit); + break; + default: + halt_printf("control code: %02x ptr:%06x unknown!\n", + ctl_code, ctl_ptr); + } + // printf("CONTROL, ctl_code:%02x\n", ctl_code); + + engine.xreg = 0; + engine.yreg = 2; + break; + default: /* Unknown command! */ + /* set acc = 1, and set carry, and set kpc */ + engine.xreg = (rts_addr) & 0xff; + engine.yreg = (rts_addr >> 8) & 0xff; + ret = 0x01; // BADCMD error + if((cmd != 0x4b) && (cmd != 0x48) && (cmd != 0x4a)) { + // Finder does 0x4a before dialog for formatting disk + // Finder does 0x4b call before formatting disk + // Many things do 0x48 call to see online drives + // So: ignore those, just return BADCMD + halt_printf("Just did smtport cmd:%02x rts_addr:%04x, " + "cmdlst:%06x\n", cmd, rts_addr, cmd_list); + } + } + + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + engine.psr &= ~1; + if(ret) { + engine.psr |= 1; + printf("Smtport cmd:%02x unit:%02x ctl_code:%02x ret:%02x\n", + cmd, unit, ctl_code, ret); + } + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + // printf(" ret:%02x psr_c:%d\n", ret & 0xff, engine.psr & 1); +} + +// $C70A is the ProDOS entry point, documented in ProDOS 8 Technical Ref +// Manual, section 6.3. +void +do_c70a(word32 arg0) +{ + dword64 dsize; + word32 cmd, unit, buf_lo, buf_hi, blk_lo, blk_hi, blk, buf; + word32 prodos_unit; + int ret, slot; + + slot = (engine.kpc >> 8) & 7; + set_memory_c(0x7f8, 0xc0 | slot, 1); + + cmd = get_memory_c((engine.direct + 0x42) & 0xffff); + prodos_unit = get_memory_c((engine.direct + 0x43) & 0xffff); + buf_lo = get_memory_c((engine.direct + 0x44) & 0xffff); + buf_hi = get_memory_c((engine.direct + 0x45) & 0xffff); + blk_lo = get_memory_c((engine.direct + 0x46) & 0xffff); + blk_hi = get_memory_c((engine.direct + 0x47) & 0xffff); + + blk = (blk_hi << 8) + blk_lo; + buf = (buf_hi << 8) + buf_lo; + disk_printf("c70a %02x cmd:%02x, pro_unit:%02x, buf:%04x, blk:%04x\n", + arg0, cmd, prodos_unit, buf, blk); + + unit = 0 + (prodos_unit >> 7); // units 0,1 + if((prodos_unit & 0x7f) != (slot << 4)) { + unit += 2; // units 2,3 + } + + smartport_log(0xc70a, cmd, blk, buf); + dbg_log_info(g_cur_dfcyc, + (buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk, 0xc70a); + +#if 0 + if(cmd != 0x1ff) { + printf("SMTPT: c70a %08x %08x\n", + (buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk); + } +#endif + engine.psr &= ~1; /* clear carry */ + + ret = 0x27; /* I/O error */ + if(cmd == 0x00) { + dsize = g_iwm.smartport[unit].dimage_size; + dsize = (dsize + 511) / 512; + + smartport_log(0, unit, (word32)dsize, 0); + dbg_log_info(g_cur_dfcyc, ((unit & 0xff) << 8) | (cmd & 0xff), + (word32)dsize, 0x1c700); + + ret = 0; + engine.xreg = dsize & 0xff; + engine.yreg = (word32)(dsize >> 8); + } else if(cmd == 0x01) { + smartport_log(0, unit, buf, blk); + ret = do_read_c7(unit, buf, blk); + } else if(cmd == 0x02) { + smartport_log(0, unit, buf, blk); + ret = do_write_c7(unit, buf, blk); + } else if(cmd == 0x03) { /* format */ + smartport_log(0, unit, buf, blk); + ret = do_format_c7(unit); + } + + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + if(ret != 0) { + engine.psr |= 1; // Set carry + } + return; +} + +int +do_read_c7(int unit_num, word32 buf, word32 blk) +{ + byte local_buf[0x200]; + Disk *dsk; + byte *bptr; + dword64 dimage_start, dimage_size, dret; + word32 val; + int len, fd; + int i; + + dbg_log_info(g_cur_dfcyc, (buf << 8) | (unit_num & 0xff), blk, 0xc701); + if((unit_num < 0) || (unit_num > MAX_C7_DISKS)) { + halt_printf("do_read_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + dsk = &(g_iwm.smartport[unit_num]); + fd = dsk->fd; + dimage_start = dsk->dimage_start; + dimage_size = dsk->dimage_size; + if(fd < 0) { + printf("c7_fd == %d!\n", fd); +#if 0 + if(blk != 2 && blk != 0) { + /* don't print error if only reading directory */ + smartport_error(); + halt_printf("Read unit:%02x blk:%04x\n", unit_num, blk); + } +#endif + return 0x2f; + } + if(((blk + 1) * 0x200ULL) > (dimage_start + dimage_size)) { + halt_printf("Tried to read past %08llx on disk (blk:%04x)\n", + dimage_start + dimage_size, blk); + smartport_error(); + return 0x27; + } + + if(dsk->raw_data) { + // image was compressed and is in dsk->raw_data + bptr = dsk->raw_data + dimage_start + (blk*0x200ULL); + for(i = 0; i < 0x200; i++) { + local_buf[i] = bptr[i]; + } + } else { + dret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET); + if(dret != (dimage_start + blk*0x200ULL)) { + halt_printf("lseek ret %08llx, errno:%d\n", dret, + errno); + smartport_error(); + return 0x27; + } + + len = (int)read(fd, &local_buf[0], 0x200); + if(len != 0x200) { + printf("read returned %08x, errno:%d, blk:%04x, unit:" + "%02x\n", len, errno, blk, unit_num); + halt_printf("name: %s\n", dsk->name_ptr); + smartport_error(); + return 0x27; + } + } + + g_io_amt += 0x200; + + if(buf >= 0xfc0000) { + disk_printf("reading into ROM, just returning\n"); + return 0; + } + + for(i = 0; i < 0x200; i += 2) { + val = (local_buf[i+1] << 8) + local_buf[i]; + set_memory16_c(buf + i, val, 0); + } + + return 0; +} + +int +do_write_c7(int unit_num, word32 buf, word32 blk) +{ + byte local_buf[0x200]; + Disk *dsk; + dword64 dret, dimage_start, dimage_size; + int len, fd, ret; + int i; + + dbg_log_info(g_cur_dfcyc, (buf << 16) | (unit_num & 0xff), blk, 0xc702); + + if(unit_num < 0 || unit_num > MAX_C7_DISKS) { + halt_printf("do_write_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + dsk = &(g_iwm.smartport[unit_num]); + fd = dsk->fd; + dimage_start = dsk->dimage_start; + dimage_size = dsk->dimage_size; + if(fd < 0) { + halt_printf("c7_fd == %d!\n", fd); + smartport_error(); + return 0x28; + } + + for(i = 0; i < 0x200; i++) { + local_buf[i] = get_memory_c(buf + i); + } + + if(dsk->write_prot) { + printf("Write, but s7d%d %s is write protected!\n", + unit_num + 1, dsk->name_ptr); + return 0x2b; + } + + if(dsk->write_through_to_unix == 0) { + //halt_printf("Write to %s, but not wr_thru!\n", dsk->name_ptr); + if(dsk->raw_data) { + // Update the memory copy + ret = smartport_memory_write(dsk, &local_buf[0], + blk * 0x200ULL, 0x200); + if(ret) { + return 0x27; // I/O Error + } + } + return 0x00; + } + + if(dsk->dynapro_info_ptr) { + dynapro_write(dsk, &local_buf[0], blk*0x200UL, 0x200); + } else { + dret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET); + if(dret != (dimage_start + blk*0x200ULL)) { + halt_printf("lseek returned %08llx, errno: %d\n", dret, + errno); + smartport_error(); + return 0x27; + } + + if(dret >= (dimage_start + dimage_size)) { + halt_printf("Tried to write to %08llx\n", dret); + smartport_error(); + return 0x27; + } + + len = (int)write(fd, &local_buf[0], 0x200); + if(len != 0x200) { + halt_printf("write ret %08x bytes, errno: %d\n", len, + errno); + smartport_error(); + dsk->write_prot = 1; + return 0x2b; // Write protected + } + } + + g_io_amt += 0x200; + + return 0; +} + +int +smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size) +{ + byte *bptr; + word32 ui; + + bptr = dsk->raw_data; + if((bptr == 0) || ((doffset + size) > dsk->dimage_size)) { + printf("Write to %s failed, %08llx past end %08llx\n", + dsk->name_ptr, doffset, dsk->dimage_size); + return -1; + } + for(ui = 0; ui < size; ui++) { + bptr[doffset + ui] = bufptr[ui]; + } + + return 0; +} + +int +do_format_c7(int unit_num) +{ + byte local_buf[0x1000]; + Disk *dsk; + dword64 dimage_start, dimage_size, dret, dtotal, dsum; + int len, max, fd, ret; + int i; + + dbg_log_info(g_cur_dfcyc, (unit_num & 0xff), 0, 0xc703); + + if(unit_num < 0 || unit_num > MAX_C7_DISKS) { + halt_printf("do_format_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + dsk = &(g_iwm.smartport[unit_num]); + fd = dsk->fd; + dimage_start = dsk->dimage_start; + dimage_size = dsk->dimage_size; + if(fd < 0) { + halt_printf("c7_fd == %d!\n", fd); + smartport_error(); + return 0x28; + } + + if(dsk->write_prot || (dsk->raw_data && !dsk->dynapro_info_ptr)) { + printf("Format, but %s is write protected!\n", dsk->name_ptr); + return 0x2b; + } + + if(dsk->write_through_to_unix == 0) { + if(!dsk->raw_data) { + printf("Format of %s ignored\n", dsk->name_ptr); + return 0x00; + } + } + + for(i = 0; i < 0x1000; i++) { + local_buf[i] = 0; + } + + if(!dsk->dynapro_info_ptr) { + dret = kegs_lseek(fd, dimage_start, SEEK_SET); + if(dret != dimage_start) { + halt_printf("lseek returned %08llx, errno: %d\n", dret, + errno); + smartport_error(); + return 0x27; + } + } + + dsum = 0; + dtotal = dimage_size; + + while(dsum < dtotal) { + max = (int)MY_MIN(0x1000, dtotal - dsum); + len = max; + if(dsk->dynapro_info_ptr) { + dynapro_write(dsk, &local_buf[0], dsum, max); + } else if(dsk->raw_data) { + ret = smartport_memory_write(dsk, &local_buf[0], + dsum, max); + if(ret) { + return 0x27; // I/O Error + } + } else { + len = (int)write(fd, &local_buf[0], max); + } + if(len != max) { + halt_printf("write ret %08x, errno:%d\n", len, errno); + smartport_error(); + dsk->write_prot = 1; + return 0x2b; // Write-protected + } + dsum += len; + } + + return 0; +} + +void +do_c700(word32 ret) +{ + int slot; + + disk_printf("do_c700 called, ret: %08x\n", ret); + dbg_log_info(g_cur_dfcyc, 0, 0, 0xc700); + + slot = (engine.kpc >> 8) & 7; + ret = do_read_c7(0, 0x800, 0); // Always read unit 0, block 0 + + set_memory_c(0x7f8, slot, 1); + set_memory16_c(0x42, (slot << 12) | 1, 1); + set_memory16_c(0x44, 0x0800, 1); + set_memory16_c(0x46, 0x0000, 1); + engine.xreg = slot << 4; // 0x70 for slot 7 + engine.kpc = 0x801; + + if(ret != 0) { + printf("Failure reading boot disk in s7d1, trying slot 5!\n"); + engine.kpc = 0xc500; // Try to boot slot 5 + if((slot == 5) || (g_rom_version == 0)) { + engine.kpc = 0xc600; // Try to boot slot 6 + } + } +} + diff --git a/gsplus/src/sound.c b/gsplus/src/sound.c new file mode 100644 index 0000000..56566e8 --- /dev/null +++ b/gsplus/src/sound.c @@ -0,0 +1,1004 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include "defc.h" + +#define INCLUDE_RCSID_C +#include "sound.h" +#undef INCLUDE_RCSID_C + +#define DOC_LOG(a,b,c,d) + +extern int Verbose; +extern int g_use_shmem; +extern word32 g_vbl_count; +extern int g_preferred_rate; + +extern word32 g_c03ef_doc_ptr; + +extern int g_doc_vol; + +extern dword64 g_last_vbl_dfcyc; + +int g_queued_samps = 0; +int g_queued_nonsamps = 0; + +#if defined(HPUX) || defined(__linux__) || defined(_WIN32) || defined(MAC) +int g_audio_enable = -1; +#else +int g_audio_enable = 0; /* Not supported: default to off */ +#endif +int g_sound_min_msecs = 32; // 32 msecs +int g_sound_min_msecs_pulse = 150; // 150 msecs +int g_sound_max_multiplier = 6; // 6*32 = ~200 msecs +int g_sound_min_samples = 48000 * 32/1000; // 32 msecs + +Mockingboard g_mockingboard; + +// The AY8913 chip has non-linear amplitudes (it has 16 levels) and the +// documentation does not match measured results. But all the measurements +// should really be done at the final speaker/jack since all the stuff in +// the path affects it. But: no one's done this for Mockingboard that I +// have found, so I'm taking measurements from the AY8913 chip itself. +// AY8913 amplitudes from https://groups.google.com/forum/#!original/ +// comp.sys.sinclair/-zCR2kxMryY/XgvaDICaldUJ +// by Matthew Westcott on December 21, 2001. +double g_ay8913_ampl_factor_westcott[16] = { // NOT USED + 0.000, // level[0] + 0.010, // level[1] + 0.015, // level[2] + 0.022, // level[3] + 0.031, // level[4] + 0.046, // level[5] + 0.064, // level[6] + 0.106, // level[7] + 0.132, // level[8] + 0.216, // level[9] + 0.297, // level[10] + 0.391, // level[11] + 0.513, // level[12] + 0.637, // level[13] + 0.819, // level[14] + 1.000, // level[15] +}; +// https://sourceforge.net/p/fuse-emulator/mailman/message/34065660/ +// refers to some Russian-language measurements at: +// http://forum.tslabs.info/viewtopic.php?f=6&t=539 (translate from +// Russian), they give: +// 0000,028F,03B3,0564, 07DC,0BA9,1083,1B7C, +// 2068,347A,4ACE,5F72, 7E16,A2A4,CE3A,FFFF +double g_ay8913_ampl_factor[16] = { + 0.000, // level[0] + 0.010, // level[1] + 0.014, // level[2] + 0.021, // level[3] + 0.031, // level[4] + 0.046, // level[5] + 0.064, // level[6] + 0.107, // level[7] + 0.127, // level[8] + 0.205, // level[9] + 0.292, // level[10] + 0.373, // level[11] + 0.493, // level[12] + 0.635, // level[13] + 0.806, // level[14] + 1.000, // level[15] +}; +// MAME also appears to try to figure out how the channels get "summed" +// together. KEGS code adds them in a completely independent way, and due +// to the circuit used on the AY8913, this is certainly incorrect. +#define MAX_MOCK_ENV_SAMPLES 2000 +int g_mock_env_vol[MAX_MOCK_ENV_SAMPLES]; +byte g_mock_noise_bytes[MAX_MOCK_ENV_SAMPLES]; +int g_mock_volume[16]; // Sample for each of the 16 amplitudes +word32 g_last_mock_vbl_count = 0; + +#define VAL_MOCK_RANGE (39000) + +int g_audio_rate = 0; +double g_daudio_rate = 0.0; +double g_drecip_audio_rate = 0.0; +double g_dsamps_per_dfcyc = 0.0; +double g_fcyc_per_samp = 0.0; + +double g_last_sound_play_dsamp = 0.0; + +#define VAL_C030_POS_VAL (20400*16/15) + // C030_POS_VAL is multiplied by g_doc_vol (0-15) and then + // divided by 16. So scale this value up by 16/15 so that + // g_doc_vol==15 gives the intended value (+/-20400) + +#define MAX_C030_TIMES 18000 +float g_c030_fsamps[MAX_C030_TIMES + 2]; +int g_num_c030_fsamps = 0; +int g_c030_state = 0; +int g_c030_val = (VAL_C030_POS_VAL / 2); +dword64 g_c030_dsamp_last_toggle = 0; + +word32 *g_sound_shm_addr = 0; +int g_sound_shm_pos = 0; + +extern dword64 g_cur_dfcyc; + +#define MAX_SND_BUF 65536 + +int g_samp_buf[2*MAX_SND_BUF]; +byte g_zero_buf[4096]; + +double g_doc_dsamps_extra = 0.0; + +int g_num_snd_plays = 0; +int g_num_recalc_snd_parms = 0; + +char *g_sound_file_str = 0; +int g_sound_file_fd = -1; +int g_sound_file_bytes = 0; + +// WAV file information: +// From https://docs.fileformat.com/audio/wav/ +// left channel is first: https://web.archive.org/web/20080113195252/ +// http://www.borg.com/~jglatt/tech/wave.htm + +byte g_wav_hdr[44] = { + 'R', 'I', 'F', 'F', 0xff, 0xff, 0xff, 0xff, // 0x00-0x07 + 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', // 0x08-0x0f + 16, 0, 0, 0, 1, 0, 2, 0, // 0x10-0x17 + // 16=length of 'fmt ' chunk, 1=PCM, 2=stereo. + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 0x18-0x1f + 4, 0, 16, 0, 'd', 'a', 't', 'a', // 0x20-0x27 + 0xff, 0xff, 0xff, 0xff // 0x28-0x2b +}; + // Bytes 4-7 are the total file size-8, so [0x28:0x2b]+0x24 + // Bytes [0x18-0x1b]is the sample rate (so 44100 or so) + // [0x1c-0x1f] is bytes-per-second: [0x18-0x1b]*[0x10]*[0x16]/8 + + +void +sound_init() +{ + doc_init(); + snddrv_init(); +} + +void +sound_set_audio_rate(int rate) +{ + g_audio_rate = rate; + g_daudio_rate = (rate)*1.0; + g_drecip_audio_rate = 1.0/(rate); + g_dsamps_per_dfcyc = ((rate*1.0) / (DCYCS_1_MHZ * 65536.0)); + g_fcyc_per_samp = (DCYCS_1_MHZ * 65536.0 / (rate*1.0)); + g_sound_min_samples = rate * g_sound_min_msecs / 1000; + + printf("Set g_audio_rate = %d in main KEGS process, min_samples:%d\n", + rate, g_sound_min_samples); +} + +void +sound_reset(dword64 dfcyc) +{ + doc_reset(dfcyc); + mockingboard_reset(dfcyc); +} + +void +sound_shutdown() +{ + snddrv_shutdown(); +} + +void +sound_update(dword64 dfcyc) +{ + /* Called every VBL time to update sound status */ + + /* "play" sounds for this vbl */ + + //DOC_LOG("do_snd_pl", -1, dsamps, 0); + sound_play(dfcyc); +} + +void +sound_file_start(char *filename) +{ + sound_file_close(); + + g_sound_file_str = filename; // Can be NULL, if so, do not start + if(filename) { + printf("Set audio save file to: %s\n", filename); + } +} + +void +sound_file_open() +{ + char *filename; + word32 exp_size; + int fd; + + filename = g_sound_file_str; + if(!filename) { + return; + } + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6); + if(fd < 0) { + printf("open_sound_file open ret: %d, errno: %d\n", fd, errno); + sound_file_close(); + return; + } + + exp_size = 1024*1024; // Default to 1MB, changed at close + dynapro_set_word32(&g_wav_hdr[4], exp_size + 44 - 8); // File size + dynapro_set_word32(&g_wav_hdr[0x28], exp_size); // data size + dynapro_set_word32(&g_wav_hdr[0x18], g_audio_rate); // Sample rate + dynapro_set_word32(&g_wav_hdr[0x1c], g_audio_rate * 2 * 2); + // bytes-per-sec + + (void)cfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44); + g_sound_file_fd = fd; + g_sound_file_bytes = 0; + printf("Opened file %s for sound\n", filename); +} + +void +sound_file_close() +{ + int fd; + + fd = g_sound_file_fd; + if(fd >= 0) { + dynapro_set_word32(&g_wav_hdr[0x28], g_sound_file_bytes); + dynapro_set_word32(&g_wav_hdr[4], g_sound_file_bytes + 44 - 8); + cfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44); + // Rewrite first 44 bytes with WAV header + printf("Close sound file %s, fd:%d\n", g_sound_file_str, fd); + close(fd); + } + + free(g_sound_file_str); + g_sound_file_fd = -1; + g_sound_file_str = 0; +} + +void +send_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps) +{ + int size, this_size; + + if(!real_samps && g_sound_file_bytes) { + // Don't do anything + return; + } + if(g_sound_file_fd < 0) { + sound_file_open(); + } + + if(!wptr) { + // No real samps + size = real_samps * 4; + while(size) { + this_size = size; + if(this_size > 4096) { + this_size = 4096; + } + must_write(g_sound_file_fd, &g_zero_buf[0], this_size); + size -= this_size; + } + return; + } + + size = 0; + if((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) { + size = SOUND_SHM_SAMP_SIZE - shm_pos; + g_sound_file_bytes += (size * 4); + + must_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*size); + shm_pos = 0; + num_samps -= size; + } + + g_sound_file_bytes += (num_samps * 4); + + must_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*num_samps); +} + +void +show_c030_state(dword64 dfcyc) +{ + show_c030_samps(dfcyc, &(g_samp_buf[0]), 100); +} + +void +show_c030_samps(dword64 dfcyc, int *outptr, int num) +{ + int last; + int i; + + if(!g_num_c030_fsamps) { + return; + + } + printf("c030_fsamps[]: %d, dfcyc:%015llx\n", g_num_c030_fsamps, dfcyc); + + for(i = 0; i < g_num_c030_fsamps+2; i++) { + printf("%3d: %5.3f\n", i, g_c030_fsamps[i]); + } + + printf("Samples[] = %d\n", num); + + last = 0x0dadbeef; + for(i = 0; i < num; i++) { + if((last != outptr[0]) || (i == (num - 1))) { + printf("Samp[%4d]: %d\n", i, outptr[0]); + last = outptr[0]; + } + outptr += 2; + } +} + +int +sound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps) +{ + int *outptr; + dword64 dsamp_min; + float ftmp, fsampnum, next_fsampnum, fpercent; + int val, num, c030_state, c030_val, pos, sampnum, next_sampnum; + int doc_vol, min_i, mul; + int i, j; + + // Handle $C030 speaker clicks. Clicks for the past num_samps are + // in g_c030_fsamps[] giving the sample position when the click + // occurred. Turn this into samples, tracking multiple clicks per + // sample into an intermediate value. After 500ms of no clicks, + // transition the speakers from +/-20400 to 0, so it's idle. + // The speaker is affected by the DOC volume in g_doc_vol, like a real + // IIgs (this is used during the system beep). This code reacts + // to DOC volume changes when they happen by causing sound_play() + // to be called, so all samples with the old volume are played then + // new clicks are collected. + + num = g_num_c030_fsamps; // Number of clicks + if(!num) { + if(g_c030_val == 0) { + return 0; // Speaker is at rest + } + } + + pos = 0; + outptr = outptr_start; + c030_state = g_c030_state; + c030_val = g_c030_val; + // c030_val may be less than max due decay after 500ms. + // Always use it first, until speaker toggles, which should + // restore the full speaker range + doc_vol = g_doc_vol; + + if(!num) { + // No clicks. See if we should begin transitioning the + // speaker output to 0. I tried multiplying by .9999 but + // that seemed to take too long at the end, so just use a + // linear ramp down. Do this ramp based on the last click + // time, not VBL, since this is more consistent + + dsamp_min = g_c030_dsamp_last_toggle + (g_audio_rate >> 4); + if(dsamp >= dsamp_min) { + min_i = 0; + } else { + min_i = (int)(dsamp_min - dsamp); + } + mul = (2 * c030_state - 1) * doc_vol; + val = (c030_val * mul) >> 4; + for(i = 0; i < num_samps; i++) { + if(i >= min_i) { + if(c030_val > 4) { + c030_val -= 4; + } else { + c030_val = 0; + } + val = (c030_val * mul) >> 4; + } + outptr[0] = val; + outptr[1] = val; + outptr += 2; + } +#if 0 + printf("at %015llx, num_samps:%d val at start:%d, at end:%d, " + "min_i:%d\n", dfcyc, num_samps, g_c030_val, c030_val, + min_i); +#endif + g_c030_val = c030_val; + if(c030_val == 0) { + //printf("Speaker at rest\n"); + } + + return 1; + } + + g_c030_fsamps[num] = (float)(num_samps); + + num++; + fsampnum = g_c030_fsamps[0]; + sampnum = (int)fsampnum; + fpercent = (float)0.0; + i = 0; + + while(i < num) { + if(sampnum < 0 || sampnum > num_samps) { + halt_printf("play c030: [%d]:%f is %d, > %d\n", + i, fsampnum, sampnum, num_samps); + break; + } + + /* write in samples to all samps < me */ + val = ((2 * c030_state) - 1) * ((c030_val * doc_vol) >> 4); + if(num <= 1) { + printf("num:%d i:%d pos:%d, sampnum:%d c030_state:%d " + " at %015llx\n", num, i, pos, sampnum, + c030_state, dfcyc); + } + for(j = pos; j < sampnum; j++) { + outptr[0] = val; + outptr[1] = val; + outptr += 2; + pos++; + } + + if((sampnum >= num_samps) || ((i + 1) >= num)) { + break; // All done + } + + /* now, calculate me */ + fpercent = (float)0.0; + if(c030_state) { + fpercent = (fsampnum - (float)sampnum); + } + + c030_state = !c030_state; + c030_val = VAL_C030_POS_VAL; + g_c030_dsamp_last_toggle = dsamp + i; + + next_fsampnum = g_c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + // Handle all the changes during this one sample + while(next_sampnum == sampnum) { + if(c030_state) { + fpercent += (next_fsampnum - fsampnum); + } + i++; + fsampnum = next_fsampnum; + + if(i > num) { + break; // This should not happen! + } + next_fsampnum = g_c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + c030_state = !c030_state; + } + + if(c030_state) { + // add in time until next sample + ftmp = (float)(int)(fsampnum + (float)1.0); + fpercent += (ftmp - fsampnum); + } + + if((fpercent < (float)0.0) || (fpercent > (float)1.0)) { + halt_printf("fpercent: %d = %f\n", i, fpercent); + show_c030_samps(dfcyc, outptr_start, num_samps); + break; + } + + val = (int)((2*fpercent - 1) * ((c030_val * doc_vol) >> 4)); + outptr[0] = val; + outptr[1] = val; + outptr += 2; + pos++; + i++; + + sampnum = next_sampnum; + fsampnum = next_fsampnum; + } + + g_c030_state = c030_state; + g_c030_val = c030_val; + +#if 0 + if(g_sound_file_str) { + show_c030_samps(dfcyc, outptr_start, num_samps); + } +#endif + + // See if there are any entries >= fsampnum, copy them back down + // to the beginning of the array + pos = 0; + num--; + fsampnum = (float)num_samps; + while(i < num) { + g_c030_fsamps[pos] = g_c030_fsamps[i] - fsampnum; +#if 0 + printf("Copied [%d] %f to [%d] as %f\n", i, g_c030_fsamps[i], + pos, g_c030_fsamps[pos]); +#endif + i++; + pos++; + } + g_num_c030_fsamps = pos; + + return 1; +} + + +int g_sound_play_depth = 0; + +// sound_play(): forms the samples from the last sample time to the current +// time. Can be called anytime from anywhere. This is how KEGS handles +// dynamic sound changes (say, disabling an Ensoniq oscillator manually): +// when it's turned off, call sound_play() to play up to this moment, then +// the next time sound_play() is called, it will just know this osc is off +// So, on any sound-related state change, call sound_play(). + +void +sound_play(dword64 dfcyc) +{ + Ay8913 *ay8913ptr; + int *outptr, *outptr_start; + word32 *sndptr; + double last_dsamp, dsamp_now, dvolume, dsamps; + word32 uval1, uval0; + int val, val0, val1, pos, snd_buf_init, num_samps, num_pairs; + int sound_mask, ivol; + int i, j; + + g_num_snd_plays++; + if(g_sound_play_depth) { + halt_printf("Nested sound_play!\n"); + } + + g_sound_play_depth++; + + /* calc sample num */ + + dsamps = dfcyc * g_dsamps_per_dfcyc; + last_dsamp = g_last_sound_play_dsamp; + num_samps = (int)(dsamps - g_last_sound_play_dsamp); + + dsamp_now = last_dsamp + (double)num_samps; + + if(num_samps < 1) { + /* just say no */ + g_sound_play_depth--; + return; + } + + dbg_log_info(dfcyc, (word32)(dword64)dsamp_now, num_samps, 0x200); + + if(num_samps > MAX_SND_BUF) { + printf("num_samps: %d, too big!\n", num_samps); + g_sound_play_depth--; + return; + } + + outptr_start = &(g_samp_buf[0]); + outptr = outptr_start; + + snd_buf_init = sound_play_c030(dfcyc, (dword64)dsamp_now, outptr_start, + num_samps); + + snd_buf_init = doc_play(dfcyc, last_dsamp, dsamp_now, num_samps, + snd_buf_init, outptr_start); + + num_pairs = 0; + // Do Mockinboard channels + for(i = 0; i < 2; i++) { // Pair: 0 or 1 + ay8913ptr = &(g_mockingboard.pair[i].ay8913); + for(j = 0; j < 3; j++) { // Channels: A, B, or C + if((ay8913ptr->regs[8 + j] & 0x1f) == 0) { + continue; + } + num_pairs = 2; + g_last_mock_vbl_count = g_vbl_count; + break; + } + } + if((g_vbl_count - g_last_mock_vbl_count) < 120) { + // Keep playing for 2 seconds, to avoid some static issues + num_pairs = 2; + } + if(num_pairs) { + sound_mask = -1; + if(snd_buf_init == 0) { + sound_mask = 0; + snd_buf_init++; + } + outptr = outptr_start; + ivol = -((VAL_MOCK_RANGE * 3 / (8 * 15)) * g_doc_vol); + // Do 3/8 of range below 0, leaving 5/8 above 0 + for(i = 0; i < num_samps; i++) { + outptr[0] = (outptr[0] & sound_mask) + ivol; + outptr[1] = (outptr[1] & sound_mask) + ivol; + outptr += 2; + } + for(i = 0; i < 16; i++) { + dvolume = (g_doc_vol * VAL_MOCK_RANGE) / (15.0 * 3.0); + ivol = (int)(g_ay8913_ampl_factor[i] * dvolume); + g_mock_volume[i] = ivol; + } + } + for(i = 0; i < num_pairs; i++) { + if(g_mockingboard.disable_mask) { + printf("dsamp:%lf\n", dsamps); + } + + sound_mock_envelope(i, &(g_mock_env_vol[0]), num_samps, + &(g_mock_volume[0])); + sound_mock_noise(i, &(g_mock_noise_bytes[0]), num_samps); + for(j = 0; j < 3; j++) { + sound_mock_play(i, j, outptr_start, + &(g_mock_env_vol[0]), &(g_mock_noise_bytes[0]), + &(g_mock_volume[0]), num_samps); + } + } + + g_last_sound_play_dsamp = dsamp_now; + + outptr = outptr_start; + pos = g_sound_shm_pos; + sndptr = g_sound_shm_addr; + +#if 0 + printf("samps_left: %d, num_samps: %d\n", samps_left, num_samps); +#endif + + if(g_audio_enable != 0) { + if(snd_buf_init) { + /* convert sound buf */ + for(i = 0; i < num_samps; i++) { + val0 = outptr[0]; + val1 = outptr[1]; + val = val0; + if(val0 > 32767) { + val = 32767; + } + if(val0 < -32768) { + val = -32768; + } + uval0 = val & 0xffffU; + val = val1; + if(val1 > 32767) { + val = 32767; + } + if(val1 < -32768) { + val = -32768; + } + uval1 = val & 0xffffU; + outptr += 2; + +#if defined(__linux__) || defined(OSS) + /* Linux seems to expect little-endian */ + /* samples always, even on PowerPC */ +# ifdef KEGS_BIG_ENDIAN + sndptr[pos] = ((uval1 & 0xff) << 24) + + ((uval1 & 0xff00) << 8) + + ((uval0 & 0xff) << 8) + + ((uval0 >> 8) & 0xff); +# else + sndptr[pos] = (uval1 << 16) + (uval0 & 0xffff); +# endif +#else +# ifdef KEGS_BIG_ENDIAN + sndptr[pos] = (uval0 << 16) + uval1; +# else + sndptr[pos] = (uval1 << 16) + uval0; +# endif +#endif + pos++; + if(pos >= SOUND_SHM_SAMP_SIZE) { + pos = 0; + } + } + if(g_queued_nonsamps) { + /* force out old 0 samps */ + snddrv_send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + if(g_sound_file_str) { + send_sound_to_file(g_sound_shm_addr, + g_sound_shm_pos, num_samps, 1); + } + g_queued_samps += num_samps; + } else { + /* move pos */ + pos += num_samps; + while(pos >= SOUND_SHM_SAMP_SIZE) { + pos -= SOUND_SHM_SAMP_SIZE; + } + if(g_sound_file_str) { + send_sound_to_file(0, g_sound_shm_pos, + num_samps, 0); + } + if(g_queued_samps) { + /* force out old non-0 samps */ + snddrv_send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + g_queued_nonsamps += num_samps; + } + } + + g_sound_shm_pos = pos; + + if(g_audio_enable != 0) { + if(g_queued_samps >= (g_audio_rate/60)) { + snddrv_send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + + if(g_queued_nonsamps >= (g_audio_rate/60)) { + snddrv_send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + } + + g_last_sound_play_dsamp = dsamp_now; + + g_sound_play_depth--; +} + +void +sound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr) +{ + Ay8913 *ay8913ptr; + double dmul, denv_period, dusecs_per_samp; + dword64 env_dsamp, dsamp_inc; + word32 ampl, eff_ampl, reg13, env_val, env_period; + int i; + + // This routine calculates a fixed-point increment to apply + // to env_dsamp, where the envelope value is in bits 44:40 (bit + // 44 is to track the alternating waveform, 43:40 is the env_ampl). + // This algorithm does not properly handle dynamically changing the + // envelope period in the middle of a step. In the AY8913, the + // part counts up to the env_period, and if the period is changed + // to a value smaller than the current count, it steps immediately + // to the next step. This routine will wait for enough fraction + // to accumulate before stepping. At most, this can delay the step + // by almost the new count time (if the new period is smaller), but + // no more. I suspect this is not noticeable. + if(num_samps > MAX_MOCK_ENV_SAMPLES) { + halt_printf("envelope overflow!: %d\n", num_samps); + return; + } + + ay8913ptr = &(g_mockingboard.pair[pair].ay8913); + ampl = ay8913ptr->regs[8] | ay8913ptr->regs[9] | ay8913ptr->regs[10]; + if((ampl & 0x10) == 0) { + // No one uses the envelope + return; + } + + env_dsamp = ay8913ptr->env_dsamp; + env_period = ay8913ptr->regs[11] + (256 * ay8913ptr->regs[12]); + if(env_period == 0) { + denv_period = 0.5; // To match MAME + } else { + denv_period = (double)env_period; + } + dmul = (1.0 / 16.0) * (1 << 20) * (1 << 20); // (1ULL << 40) / 16.0 + // Calculate amount counter will count in one sample. + // inc_per_tick 62.5KHz tick: (1/env_period) + // inc_per_dfcyc: (1/(16*env_period)) + // inc_per_samp = inc_per_dfcyc * g_fcyc_per_samp + dusecs_per_samp = g_fcyc_per_samp / 65536.0; + dsamp_inc = (dword64)((dmul * dusecs_per_samp / denv_period)); + // Amount to inc per sample, fixed point, 40 bit frac + + reg13 = ay8913ptr->regs[13]; // "reg15", env ctrl + eff_ampl = 0; + for(i = 0; i < num_samps; i++) { + env_dsamp = (env_dsamp + dsamp_inc) & 0x9fffffffffffULL; + env_val = (env_dsamp >> 40) & 0xff; + eff_ampl = env_val & 0xf; + if((reg13 & 4) == 0) { + eff_ampl = 15 - eff_ampl; // not attack + } + if((reg13 & 8) && (reg13 & 2)) { + // continue and alternate + if(env_val & 0x10) { + eff_ampl = 15 - eff_ampl; + } + } + if(((reg13 & 8) == 0) && (env_val >= 0x10)) { + eff_ampl = 0; + ampl = 0; // Turn off envelope + env_dsamp |= (0x80ULL << 40); + } else if((reg13 & 1) && (env_val >= 0x10)) { + eff_ampl = ((reg13 >> 1) ^ (reg13 >> 2)) & 1; + eff_ampl = eff_ampl * 15; + ampl = eff_ampl; // Turn off envelope + env_dsamp |= (0x80ULL << 40); + } + *env_ptr++ = vol_ptr[eff_ampl & 0xf]; + } + ay8913ptr->env_dsamp = env_dsamp; +} + +void +sound_mock_noise(int pair, byte *noise_ptr, int num_samps) +{ + Ay8913 *ay8913ptr; + word32 ampl, mix, noise_val, noise_samp, noise_period, xor, samp_inc; + int doit; + int i; + + if(num_samps > MAX_MOCK_ENV_SAMPLES) { + halt_printf("noise overflow!: %d\n", num_samps); + return; + } + + ay8913ptr = &(g_mockingboard.pair[pair].ay8913); + doit = 0; + for(i = 0; i < 3; i++) { + ampl = ay8913ptr->regs[8 + i]; + mix = ay8913ptr->regs[7] >> i; + if((ampl != 0) && ((mix & 8) == 0)) { + doit = 1; + break; + } + } + if(!doit) { + // No channel looks at noise, don't bother + return; + } + + noise_val = ay8913ptr->noise_val; + noise_samp = ay8913ptr->noise_samp; + noise_period = (ay8913ptr->regs[6] & 0x1f); + noise_period = noise_period << 16; + samp_inc = (word32)(g_fcyc_per_samp / 16.0); + // Amount to inc per sample + if(noise_samp >= noise_period) { + // Period changed during sound, reset + noise_samp = noise_period; + } + for(i = 0; i < num_samps; i++) { + noise_samp += samp_inc; + if(noise_samp >= noise_period) { + // HACK: handle fraction + // 17-bit LFSR, algorithm from MAME:sound/ay8910.cpp + // val = val ^ (((val & 1) ^ ((val >> 3) & 1)) << 17) + xor = 0; + xor = (noise_val ^ (noise_val >> 3)) & 1; + noise_val = (noise_val ^ (xor << 17)) >> 1; + noise_samp -= noise_period; + } + noise_ptr[i] = noise_val & 1; + } + ay8913ptr->noise_samp = noise_samp; + ay8913ptr->noise_val = noise_val; +} + +int g_did_mock_print = 100; + +void +sound_mock_play(int pair, int channel, int *outptr, int *env_ptr, + byte *noise_ptr, int *vol_ptr, int num_samps) +{ + Ay8913 *ay8913ptr; + word32 ampl, mix, tone_samp, tone_period, toggle_tone; + word32 samp_inc, noise_val; + int out, ival, do_print; + int i; + + if((g_mockingboard.disable_mask >> ((pair * 3) + channel)) & 1) { + // This channel is disabled + return; + } + + ay8913ptr = &(g_mockingboard.pair[pair].ay8913); + ampl = ay8913ptr->regs[8 + channel] & 0x1f; + if(ampl == 0) { + return; + } + toggle_tone = ay8913ptr->toggle_tone[channel]; // 0 or 1 + mix = (ay8913ptr->regs[7] >> channel) & 9; + if(mix == 9) { + // constant tone: output will be ampl for this channel. + if(ampl & 0x10) { // Envelope! + // The envelope can make the tone, so must calculate it + } else { + // HACK: do nothing for now + return; + } + } + outptr += pair; // pair[1] is right + tone_samp = ay8913ptr->tone_samp[channel]; + tone_period = ay8913ptr->regs[2*channel] + + (256 * ay8913ptr->regs[2*channel + 1]); + tone_period = tone_period << 16; + samp_inc = (word32)(g_fcyc_per_samp / 8.0); + // Amount to inc per sample + do_print = 0; + if(g_mockingboard.disable_mask) { + printf("Doing %d samps, mix:%d, ampl:%02x\n", num_samps, mix, + ampl); + do_print = 1; + g_did_mock_print = 0; + } + if((num_samps > 500) && (g_did_mock_print == 0)) { + do_print = 1; + g_did_mock_print = 1; + printf("Start of %d sample, channel %d mix:%02x ampl:%02x " + "toggle_tone:%02x\n", num_samps, channel, mix, ampl, + toggle_tone); + printf(" tone_period:%08x, tone_samp:%08x, samp_inc:%08x\n", + tone_period, tone_samp, samp_inc); + } + if(tone_samp >= tone_period) { + // Period changed during sound, reset it + tone_samp = tone_period; + } + for(i = 0; i < num_samps; i++) { + tone_samp += samp_inc; + if(tone_samp >= tone_period) { + // HACK: handle toggling mid-sample... + toggle_tone ^= 1; + if(do_print) { + printf("i:%d tone_toggled to %d, tone_period:" + "%04x, pre tone_samp:%08x\n", i, + toggle_tone, tone_period, tone_samp); + } + tone_samp -= tone_period; + if(do_print) { + printf("post tone_samp:%08x\n", tone_samp); + } + } + noise_val = noise_ptr[i] & 1; + out = (toggle_tone || (mix & 1)) & + ((noise_val & 1) || (mix & 8)); + // Careful mix of || and & above... + ival = vol_ptr[ampl & 0xf]; + if(ampl & 0x10) { // Envelope + ival = env_ptr[i]; + } + *outptr += ival*out; + outptr += 2; + } + ay8913ptr->tone_samp[channel] = tone_samp; + ay8913ptr->toggle_tone[channel] = toggle_tone; +} + +word32 +sound_read_c030(dword64 dfcyc) +{ + sound_write_c030(dfcyc); + return float_bus(dfcyc); +} + +void +sound_write_c030(dword64 dfcyc) +{ + int num; + + num = g_num_c030_fsamps; + if(num >= MAX_C030_TIMES) { + halt_printf("Too many clicks per vbl: %d\n", num); + return; + } + + g_c030_fsamps[num] = (float)(dfcyc * g_dsamps_per_dfcyc - + g_last_sound_play_dsamp); + g_num_c030_fsamps = num + 1; + dbg_log_info(dfcyc, num, 0, 0xc030); + + doc_printf("touch c030, num this vbl: %04x\n", num); +} + diff --git a/gsplus/src/sound.h b/gsplus/src/sound.h new file mode 100644 index 0000000..01925e1 --- /dev/null +++ b/gsplus/src/sound.h @@ -0,0 +1,101 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#if !defined(_WIN32) && !defined(__CYGWIN__) +# include +# include +#endif + +#define SOUND_SHM_SAMP_SIZE (32*1024) + +#define SAMPLE_SIZE 2 +#define NUM_CHANNELS 2 +#define SAMPLE_CHAN_SIZE (SAMPLE_SIZE * NUM_CHANNELS) + +STRUCT(Doc_reg) { + double dsamp_ev; + double dsamp_ev2; + double complete_dsamp; + int samps_left; + word32 cur_acc; + word32 cur_inc; + word32 cur_start; + word32 cur_end; + word32 cur_mask; + int size_bytes; + int event; + int running; + int has_irq_pending; + word32 freq; + word32 vol; + word32 waveptr; + word32 ctl; + word32 wavesize; + word32 last_samp_val; +}; + +// Mockingboard contains two pairs. Each pair is a 6522 interfacing +// to an AY-8913 to generate sounds. Eacho AY-8913 contains 3 channels of +// sound. Model each pair separately. + +STRUCT(Mos6522) { + byte orb; + byte ora; + byte ddrb; + byte ddra; + word32 timer1_latch; + word32 timer1_counter; + word32 timer2_latch; + word32 timer2_counter; + byte sr; + byte acr; + byte pcr; + byte ifr; + byte ier; +}; + +STRUCT(Ay8913) { + byte regs[16]; + byte reg_addr_latch; + byte toggle_tone[3]; // Channel A,B,C: 0 = low, 1 = high + word32 tone_samp[3]; + word32 noise_val; + word32 noise_samp; + dword64 env_dsamp; +}; + +STRUCT(Mock_pair) { + Mos6522 mos6522; + Ay8913 ay8913; +}; + +STRUCT(Mockingboard) { + Mock_pair pair[2]; + word32 disable_mask; +}; + +/* prototypes for win32snd_driver.c functions */ +void win32snd_init(word32 *); +void win32snd_shutdown(void); +void child_sound_init_win32(void); +int win32_send_audio(byte *ptr, int size); + +/* Prototypes for macsnd_driver.c functions */ +int mac_send_audio(byte *ptr, int in_size); +void macsnd_init(); + +/* Prototypes for pulseaudio_driver.c functions */ +int pulse_audio_init(); +int pulse_audio_send_audio(byte *ptr, int in_size); +void pulse_audio_shutdown(void); + diff --git a/gsplus/src/sound_driver.c b/gsplus/src/sound_driver.c new file mode 100644 index 0000000..7fc708d --- /dev/null +++ b/gsplus/src/sound_driver.c @@ -0,0 +1,547 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Routines for managing sending sound samples to the hardware. The +// primary routines are snddrv_init() for initializing the sound hardware, +// and snddrv_send_sound() which calls the driver for the sound hardware +// in use to play samples. +// Linux forks a child process to manage /dev/dsp (so KEGS will not block) +// so the lowerlevel routines for all sound hardware start with child_(). + +#include "defc.h" +#include "sound.h" + +#if defined(__linux__) || defined(OSS) +# include +#endif + +#ifndef _WIN32 +# include +# include +#endif +#include + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(MAC) +# define KEGS_CAN_FORK 0 +#else + // Linux, or other Unix, we may fork and run sound in the child +# define KEGS_CAN_FORK 1 +#endif + +extern int Verbose; + +extern int g_audio_rate; +extern int g_audio_enable; +extern double g_last_sound_play_dsamp; +extern word32 *g_sound_shm_addr; + +int g_preferred_rate = 48000; +int g_audio_socket = -1; +int g_bytes_written = 0; +int g_pulse_audio = 0; +int g_pipe_fd[2] = { -1, -1 }; +int g_pipe2_fd[2] = { -1, -1 }; + +#define ZERO_BUF_SIZE 2048 + +word32 g_snd_zero_buf[ZERO_BUF_SIZE]; + +#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5) +#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate) + +int g_zeroes_buffered = 0; +int g_zeroes_seen = 0; +int g_sound_paused = 0; +int g_childsnd_vbl = 0; +int g_childsnd_pos = 0; + +int child_sound_init_linux(void); + +void +snddrv_init() +{ + word32 *shmaddr; + int size, ret, use_shm; + + ret = 0; + if(ret) { // Avoid unused var warning + } + + g_zeroes_buffered = 0; + g_zeroes_seen = 0; + g_sound_paused = 0; + + g_childsnd_pos = 0; + g_childsnd_vbl = 0; + + if(g_audio_enable == 0) { + sound_set_audio_rate(g_preferred_rate); + return; + } + printf("snddrv_init, g_audio_enable:%d\n", g_audio_enable); + + size = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE; + + use_shm = KEGS_CAN_FORK; +#ifdef PULSE_AUDIO + use_shm = 0; +#endif + if(!use_shm) { + /* windows and mac, and Linux Pulse Audio */ + shmaddr = malloc(size); + memset(shmaddr, 0, size); + g_sound_shm_addr = shmaddr; +#ifdef MAC + macsnd_init(); + return; +#endif +#ifdef _WIN32 + win32snd_init(shmaddr); + return; +#endif +#ifdef PULSE_AUDIO + ret = pulse_audio_init(shmaddr); + if(ret == 0) { + g_pulse_audio = 1; + return; // Success! + } + free(shmaddr); + g_sound_shm_addr = 0; + use_shm = 1; + // Otherwise, fall back on /dev/dsp +#endif + } + + if(use_shm) { + sound_child_fork(size); + } +} + +void +sound_child_fork(int size) +{ +#if KEGS_CAN_FORK + word32 *shmaddr; + int shmid, pid, tmp, ret; + int i; + + doc_printf("In sound_child_fork, size:%d\n", size); + shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + if(shmid < 0) { + printf("sound_init: shmget ret: %d, errno: %d\n", shmid, + errno); + exit(2); + } + + shmaddr = shmat(shmid, 0, 0); + tmp = (int)PTR2WORD(shmaddr); + if(tmp == -1) { + printf("sound_init: shmat ret: %p, errno: %d\n", shmaddr, + errno); + exit(3); + } + + ret = shmctl(shmid, IPC_RMID, 0); + if(ret < 0) { + printf("sound_init: shmctl ret: %d, errno: %d\n", ret, errno); + exit(4); + } + + g_sound_shm_addr = shmaddr; + printf("shmaddr: %p\n", shmaddr); + + fflush(stdout); + + /* prepare pipe so parent can signal child each other */ + /* pipe[0] = read side, pipe[1] = write end */ + ret = pipe(&g_pipe_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + ret = pipe(&g_pipe2_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + + + doc_printf("pipes: pipe_fd = %d, %d pipe2_fd: %d,%d\n", + g_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]); + fflush(stdout); + + pid = fork(); + switch(pid) { + case 0: + /* child */ + /* close stdin and write-side of pipe */ + close(0); + /* Close other fds to make sure X window fd is closed */ + for(i = 3; i < 100; i++) { + if((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) { + close(i); + } + } + close(g_pipe_fd[1]); /*make sure write pipe closed*/ + close(g_pipe2_fd[0]); /*make sure read pipe closed*/ + child_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr); + printf("Child sound loop returned\n"); + exit(0); + case -1: + /* error */ + printf("sound_init: fork ret: -1, errno: %d\n", errno); + exit(6); + default: + /* parent */ + /* close read-side of pipe1, and the write side of pipe2 */ + close(g_pipe_fd[0]); + close(g_pipe2_fd[1]); + doc_printf("Child is pid: %d\n", pid); + } + + parent_sound_get_sample_rate(g_pipe2_fd[0]); +#endif + if(size) { + // Avoid unused param warning + } +} + +void +parent_sound_get_sample_rate(int read_fd) +{ + word32 audio_rate, tmp; + int ret; + + ret = (int)read(read_fd, &audio_rate, 4); + if(ret != 4) { + printf("parent dying, could not get sample rate from child\n"); + printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno); + exit(1); + } + ret = (int)read(read_fd, &tmp, 4); + if(ret != 4) { + printf("parent dying, could not get audio status from child\n"); + printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno); + exit(1); + } + if(tmp == 0) { + g_audio_enable = 0; + printf("Failed to init Sound, turning off audio\n"); + } + close(read_fd); + + sound_set_audio_rate(audio_rate); +} + +void +snddrv_shutdown() +{ +#ifdef _WIN32 + win32snd_shutdown(); +#else + if((g_audio_enable != 0) && (g_pipe_fd[1] >= 0)) { + close(g_pipe_fd[1]); + } +#endif +#ifdef PULSE_AUDIO + if(g_pulse_audio) { + pulse_audio_shutdown(); + } +#endif +} + +void +snddrv_send_sound(int real_samps, int size) +{ + word32 tmp; + int ret, call_playit; + + if(g_audio_enable == 0) { + printf("Entered send_sound but audio off!\n"); + exit(2); + } + + if(real_samps) { + tmp = size + 0xa2000000; + } else { + tmp = size + 0xa1000000; + } + //doc_log_rout("send_sound", -1, g_last_sound_play_dsamp, + // (real_samps << 30) + size); + + call_playit = 0; +#if defined(MAC) || defined(_WIN32) + call_playit = 1; // Never fork child mac/windows +#endif + if(call_playit || g_pulse_audio) { + child_sound_playit(tmp); + return; + } + + /* Although this looks like a big/little-endian issue, since the */ + /* child is also reading an int, it just works with no byte swap */ + ret = (int)write(g_pipe_fd[1], &tmp, 4); + if(ret != 4) { + halt_printf("send_sound, wr ret: %d, errno: %d\n", ret, errno); + } +} + +void +child_sound_playit(word32 tmp) +{ + int size; + + size = tmp & 0xffffff; + + //printf("child_sound_playit: %08x\n", tmp); + + if((tmp >> 24) == 0xa2) { // play sound + if(g_zeroes_buffered) { + reliable_zero_write(g_zeroes_buffered); + } + + g_zeroes_buffered = 0; + g_zeroes_seen = 0; + + if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) { + reliable_buf_write(g_sound_shm_addr, g_childsnd_pos, + SOUND_SHM_SAMP_SIZE - g_childsnd_pos); + size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE; + g_childsnd_pos = 0; + } + + reliable_buf_write(g_sound_shm_addr, g_childsnd_pos, size); + + if(g_sound_paused) { + printf("Unpausing sound, zb: %d\n", g_zeroes_buffered); + g_sound_paused = 0; + } + + } else if((tmp >> 24) == 0xa1) { // play zeroes + if(g_sound_paused) { + if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) { + g_zeroes_buffered += size; + } + } else { + /* not paused, send it through */ + g_zeroes_seen += size; + + reliable_zero_write(size); + + if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) { + printf("Pausing sound\n"); + g_sound_paused = 1; + } + } + } else { + printf("tmp received bad: %08x\n", tmp); + exit(3); + } + + g_childsnd_pos += size; + while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) { + g_childsnd_pos -= SOUND_SHM_SAMP_SIZE; + } + + g_childsnd_vbl++; + if(g_childsnd_vbl >= 60) { + g_childsnd_vbl = 0; +#if 0 + printf("sound bytes written: %06x\n", g_bytes_written); +#endif + g_bytes_written = 0; + } +} + +void +reliable_buf_write(word32 *shm_addr, int pos, int size) +{ + byte *ptr; + int ret; + + if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE || + size > SOUND_SHM_SAMP_SIZE || + (pos + size) > SOUND_SHM_SAMP_SIZE) { + printf("reliable_buf_write: pos: %04x, size: %04x\n", + pos, size); + exit(1); + } + + ptr = (byte *)&(shm_addr[pos]); + size = size * 4; + + while(size > 0) { + ret = child_send_samples(ptr, size); + + if(ret < 0) { + printf("audio write, errno: %d %s\n", errno, + strerror(errno)); + exit(1); + } + size = size - ret; + ptr += ret; + g_bytes_written += ret; + } + +} + +void +reliable_zero_write(int amt) +{ + int len; + + while(amt > 0) { + len = MY_MIN(amt, ZERO_BUF_SIZE); + reliable_buf_write(g_snd_zero_buf, 0, len); + amt -= len; + } +} + + +int +child_send_samples(byte *ptr, int size) +{ +#ifdef _WIN32 + return win32_send_audio(ptr, size); +#else +# ifdef MAC + return mac_send_audio(ptr, size); +# else +# ifdef PULSE_AUDIO + if(g_pulse_audio) { + return pulse_audio_send_audio(ptr, size); + } +# endif + return (int)write(g_audio_socket, ptr, size); +# endif +#endif +} + +// child_sound_loop(): used by Linux child process as the main loop, to read +// from pipe to get sample info every VBL, and use shm_addr to get samples +void +child_sound_loop(int read_fd, int write_fd, word32 *shm_addr) +{ + word32 tmp, did_init; + int ret, ret1, ret2; + + doc_printf("Child pipe fd: %d, shm_addr:%p\n", read_fd, shm_addr); + + g_audio_rate = g_preferred_rate; + + did_init = 0; +#if defined(__linux__) || defined(OSS) + did_init = child_sound_init_linux(); +#endif + + tmp = g_audio_rate; + ret1 = (int)write(write_fd, &tmp, 4); + tmp = did_init; + ret2 = (int)write(write_fd, &tmp, 4); + if((ret1) != 4 || (ret2 != 4)) { + printf("Unable to send back audio rate to parent\n"); + printf("ret1: %d,%d fd: %d, errno:%d\n", ret1, ret2, write_fd, + errno); + exit(1); + } + doc_printf("Wrote to fd %d the audio rate\n", write_fd); + + close(write_fd); + + while(1) { + errno = 0; + ret = (int)read(read_fd, &tmp, 4); + if(ret <= 0) { + printf("child dying from ret: %d, errno: %d\n", + ret, errno); + break; + } + + child_sound_playit(tmp); + } + + close(g_audio_socket); + + exit(0); +} + + +#if defined(__linux__) || defined(OSS) +int +child_sound_init_linux() +{ + int stereo, sample_size, rate, fmt, ret; + + g_audio_socket = open("/dev/dsp", O_WRONLY, 0); + if(g_audio_socket < 0) { + printf("open /dev/dsp failed, ret: %d, errno:%d\n", + g_audio_socket, errno); + return 0; + } + +#if 0 + fragment = 0x00200009; + ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment); + if(ret < 0) { + printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n", + ret, errno); + return 0; + } +#endif + + sample_size = 16; + ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n", + ret, errno); + return 0; + } + +#ifdef KEGS_BIG_ENDIAN + fmt = AFMT_S16_BE; +#else + fmt = AFMT_S16_LE; +#endif + ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n", + ret, errno); + return 0; + } + + stereo = 1; + ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n", + ret, errno); + return 0; + } + + rate = g_audio_rate; + ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n", + ret, errno); + return 0; + } + if(ret > 0) { + rate = ret; /* rate is returned value */ + } + if(rate < 8000) { + printf("Audio rate of %d which is < 8000!\n", rate); + return 0; + } + g_audio_rate = rate; + + printf("Sound initialized\n"); + return 1; +} +#endif diff --git a/gsplus/src/style_check b/gsplus/src/style_check new file mode 100644 index 0000000..1b6b554 --- /dev/null +++ b/gsplus/src/style_check @@ -0,0 +1,147 @@ +#!/usr/bin/perl -w +# $KmKId: style_check,v 1.1 2020-06-14 02:52:13+00 kentd Exp $ + +# Perl script to check for coding conventions +use English; + +$some_bad = 0; + +while($#ARGV >= 0) { + $file = shift; + $check_spaces = 0; + if($file =~ /\.c$/) { + $check_spaces = 1; + } elsif($file =~ /\.s$/) { + $check_spaces = 1; + } elsif($file =~ /\.k$/) { + $check_spaces = 1; + } elsif($file =~ /\.h$/) { + $check_spaces = 1; + if($file =~ /^protos.*.h$/) { + next; # skip global_names.h + } + if($file =~ /^global_names.h$/) { + next; # skip global_names.h + } + if($file =~ /^knobs.h$/) { + next; # skip global_names.h + } + } else { + next; # skip this file + } + print "Style check: $file\n"; + if(-x $file) { + print "File mode is executable\n"; + $some_bad++; + } + open(FILE, "<$file") || die "Open: $file: $1\n"; + $line_num = 1; + foreach $line () { + chomp($line); + $ign_tab_space = 0; + if($line =~ m:^[/\t]+{.*}:) { + $ign_tab_space = 1; + } + $len = 0; + $prev = 0; + $pprev = 0; + $bad = 0; + $last_tab = -10; + if($check_spaces) { + if($line =~ / $/ || $line =~ / $/) { + print "Line ends in tab or space\n"; + $bad++; + } + } + if($line =~ /\r$/) { # Line ends with Ctrl-M + print "Windows linebreak detected\n"; + $bad = 101; + } + while($line =~ m/^([^"]*)("[^"]*")(.*)$/) { + # Convert text in strings to '-' to avoid space checks + $prev = $1; + $post = $3; + $quot = &dotify($2); + $line = $prev . $quot . $post; + } + while($line =~ m:^(.*)//(.*)$:) { + # Convert text in comments to '-' to avoid space checks + $prev = $1; + $quot = &dotify($2); + $line = $prev . "::" . $quot; + } + @chars = split(//, $line); + foreach $char (@chars) { + $len++; + if(!$check_spaces) { + # do nothing + } elsif($char eq "\t") { + $len = (($len + 7) >> 3) * 8; + if($prev eq ' ') { + print "Space followed by tab\n"; + $bad++; + } + $last_tab = $len; + } elsif($char eq " ") { + if($prev eq "\t" && !$ign_tab_space) { + print "Tab followed by space\n"; + $bad++; + } + if($prev eq " " && $pprev eq " " && + (($len - $last_tab) > 4)) { + print "Too many spaces\n"; + $bad++; + last; + } + } + $pprev = $prev; + $prev = $char + } + if($check_spaces) { + if(($len > 80) && ($line_num > 2)) { + print "Line more than 80 columns\n"; + $bad++; + } + #print "line $line has len $len\n"; + } + if($bad) { + $some_bad++; + print "...at line $line_num in file $file\n"; + if($some_bad > 20) { + die "Too many style errors\n"; + } + if($bad >= 100) { + next; # Skip to next file + } + } + $line_num++; + } +} + +if($some_bad) { + die "Style errors\n"; +} + +exit 0; + +sub +dotify +{ + my ($str) = @_; + my @chars = (); + my @outchars = (); + my ($char, $result); + + @chars = split(//, $str); + @outchars = (); + foreach $char (@chars) { + if($char ne "\t") { + $char = "-"; + } + push(@outchars, $char); + } + $result = join('', @outchars); + #print "Old quote :$str:\n"; + #print "New quote :$result:\n"; + return $result; +} diff --git a/gsplus/src/undeflate.c b/gsplus/src/undeflate.c new file mode 100644 index 0000000..9260fe2 --- /dev/null +++ b/gsplus/src/undeflate.c @@ -0,0 +1,1451 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// This file has routines for the undeflate uncompression algorithm for +// gzip/zip, and routines for reading .zip files. + +// Based on https://www.ietf.org/rfc/rfc1951.txt for Deflate algorithm, +// and https://www.ietf.org/rfc/rfc1952.txt for gzip file format. + +// .zip file format from: +// https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT + +#include "defc.h" + +// FILE *g_outf = 0; + +#define LENGTH_ENCODED 0xffffff444449ULL + // LENGTH_ENCODED encodes the first table of section 3.2.5 for + // fixed Huffman: 257-264 = 0 extra bits, length=3-10 (so 8 entries) + // then 265-268 = 1 extra bit, length=11... (so 4 entries), etc. + // which is encoded in each nibble of this word. Code 285 (entry 29) + // has extra_bits=0, indicated by the encoded nibble being 0xf. +#define DIST_ENCODED 0xff22222222222224ULL + // DIST_ENCODED encodes the second table of section 3.2.5 for the + // fixed Huffman table: Codes 0-3 have 0 extra bits, dist=1,2,3,4 + // (so 4 entries), codes 4,5 have 1 extra bit, dest=5... (so 2 + // entries), etc. 0xf indicates an invalid entry + +word32 g_undeflate_fixed_len_tab[512+1]; + // extrabits << 20 | bits << 16 | len/literal +word32 g_undeflate_fixed_dist_tab[32+1]; + // extrabits << 20 | bits << 16 | distance +word32 g_undeflate_length_tab[32+1]; // extra_bits << 20 | len +word32 g_undeflate_dist_tab[32+1]; // extra_bits << 20 | dist +word32 g_undeflate_bit_rev[512]; +word32 g_undeflate_lencode_positions[19] = +{ 0x200310, 0x300311, 0x700b12, + 0x100, 0x108, 0x107, 0x109, 0x106, 0x10a, 0x105, 0x10b, + 0x104, 0x10c, 0x103, 0x10d, 0x102, 0x10e, 0x101, 0x10f }; +word32 g_undeflate_lencode_tab[128 + 1]; +word32 *g_undeflate_dynamic_tabptr = 0; +word32 g_undeflate_dynamic_bits = 0; +word32 *g_undeflate_dynamic_dist_tabptr = 0; +word32 g_undeflate_dynamic_dist_bits = 0; + +void * +undeflate_realloc(void *ptr, dword64 dsize) +{ + if((size_t)dsize != dsize) { + return 0; + } + return realloc(ptr, (size_t)dsize); +} + +void * +undeflate_malloc(dword64 dsize) +{ + if((size_t)dsize != dsize) { + return 0; + } + return malloc((size_t)dsize); +} + +void +show_bits(unsigned *llptr, int nl) +{ + int i; + + fprintf(stdout, "Showing %03x bits entries\n", nl); + for(i = 0; i < nl; i++) { + fprintf(stdout, "%03x: %03x\n", i, (llptr[i] >> 16) & 0xf); + } +} + +void +show_huftb(unsigned *tabptr, int bits) +{ + unsigned char seen[512]; + word32 entry, code, val; + int i, j; + + printf("Showing hufftab of %d bits\n", bits); + + for(i = 0; i < 256+32; i++) { + seen[i] = 0; + } + for(i = 0; i < (1 << bits); i++) { + entry = tabptr[i]; + code = entry & 0xff; + if((entry >> 24) & 1) { + val = entry & 0xfff0ffffUL; + for(j = 0; j < 32; j++) { + if(val == g_undeflate_length_tab[j]) { + code = 256 + j; + break; + } + } + if(code < 256) { + printf("entry %08x (%08x) not found, [0]=%08x " + "[1]=%08x\n", entry, val, + g_undeflate_length_tab[0], + g_undeflate_length_tab[1]); + code = 256 + 31; + } + } + if(!seen[code]) { + printf("code %03x has bits:%d huffcode:%04x\n", code, + (entry >> 16) & 0xf, i); + seen[code] = 1; + } + } +} + +void +undeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start) +{ + word32 pos, repeats, extra_bits; + int i; + + // Initializes g_undeflate_length_tab[] and g_underflate_dist_tab[] + // printf("undeflate len_dist_tab repeats:%016llx\n", drepeats); + pos = 0; + extra_bits = 0; + while(pos < 30) { + repeats = drepeats & 0xf; + drepeats = drepeats >> 4; + if(repeats == 0xf) { + // Special handling for code=285 (pos=29) + extra_bits = 0; + start--; // 258, not 259 + repeats = 1; + } + for(i = 0; i < (int)repeats; i++) { + tabptr[pos] = start | (extra_bits << 20) | (1 << 24); + //printf("Set table[%d]=%08x (%d) i:%d out of %d\n", + // pos, tabptr[pos], start, i, repeats); + pos++; + start += (1 << extra_bits); + } + extra_bits++; + } +} + +void +undeflate_init_bit_rev_tab(word32 *tabptr, int num) +{ + word32 pos, val; + int i, j; + + // Initializes g_undeflate_bit_rev[] + for(i = 0; i < num; i++) { + pos = i; + val = 0; + for(j = 0; j < 9; j++) { + val = (val << 1) | (pos & 1); + pos = pos >> 1; + } + tabptr[i] = val; + // printf("Bit reverse[%03x]=%03x\n", i, val); + } +} + +word32 +undeflate_bit_reverse(word32 val, word32 bits) +{ + word32 new_val, val2, shift; + + new_val = g_undeflate_bit_rev[val & 0x1ff]; // at most 9 bits + shift = 9 - bits; + if(bits <= 9) { + return new_val >> shift; + } else if(bits <= 18) { + shift += 9; // bits:10->shift=8, bits:11->shift=7,.. + val2 = g_undeflate_bit_rev[(val >> 9) & 0x1ff] >> shift; + shift = bits - 9; + return (new_val << shift) | val2; + } + printf("Cannot reverse %08x bits:%d!\n", val, bits); + return 0; +} + +word32 +undeflate_calc_crc32(byte *bptr, word32 len) +{ + word32 crc, c, xor; + int i; + + // Old version, don't use other than for testing purposes. + // Use woz_calc_crc32() instead. + + // Generate CCITT-32 CRC, with remainder initialized to -1 and return + // the complement of the CRC value + // This is slow--but it doesn't matter for KEGS, where the images + // are generally only 800KB max. + crc = (word32)-1; + while(len != 0) { + c = *bptr++; + len--; + for(i = 0; i < 8; i++) { + xor = 0; + if((crc ^ c) & 1) { + xor = 0xedb88320UL; + } + crc = (crc >> 1) ^ xor; + c = c >> 1; + } + } + return (~crc); +} + +byte * +undeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len) +{ + byte *raw_ptr; + dword64 raw_dsize, dimage_size; + word32 new_image_size; + + raw_dsize = dsk->raw_dsize; + dimage_size = dsk->dimage_size; + if(ucptr) { + new_image_size = (word32)(ucptr - dsk->raw_data); + if(new_image_size < dimage_size) { + printf("ucptr moved backwards!\n"); + return 0; + } + if((new_image_size >> 31) != 0) { + printf("Output file > 2GB, failing\n"); + return 0; + } + dimage_size = new_image_size; + dsk->dimage_size = dimage_size; + } + if(dimage_size > raw_dsize) { + printf("dimage_size %08llx overflowed raw_dsize %08llx\n", + dimage_size, raw_dsize); + return 0; + } + if((dimage_size + len) > raw_dsize) { + raw_dsize = ((dimage_size + len) * 3ULL) / 2; + raw_ptr = undeflate_realloc(dsk->raw_data, raw_dsize); + //printf("Did realloc to %08x, new new_data:%p, was %p\n", + // raw_size, raw_ptr, dsk->raw_data); + if(raw_ptr == 0) { + printf("undeflate realloc failed\n"); + free(dsk->raw_data); + dsk->raw_data = 0; + return 0; + } + dsk->raw_data = raw_ptr; + dsk->raw_dsize = raw_dsize; + } +#if 0 + printf("undeflate_ensure_dest_len will ret %p, dsk->raw_data:%p, " + "image_size:%08llx, raw_dsize:%08llx\n", + dsk->raw_data + dimage_size, dsk->raw_data, dimage_size, + raw_dsize); +#endif + return dsk->raw_data + dimage_size; +} + +void +undeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code, + word32 entry) +{ + word32 rev_code, bits, pos, tab_size; + int num; + int i; + + if(tabsz_lg2 > 15) { + printf("tabsz_lg2: %04x is not supported\n", tabsz_lg2); + return; + } + tab_size = 1 << tabsz_lg2; + rev_code = 0; + bits = (entry >> 16) & 0xf; + rev_code = undeflate_bit_reverse(code, bits); + if(rev_code >= tab_size) { + printf("rev_code:%04x out of range for entry %08x\n", rev_code, + entry); + tabptr[tab_size] = 1; + return; + } + num = 1 << (tabsz_lg2 - bits); + if(num < 0) { + printf("num %d out of range for entry %08x\n", num, entry); + tabptr[tab_size] = 1; + return; + } + for(i = 0; i < num; i++) { + pos = rev_code | (i << bits); + if(tabptr[pos] != 0) { + printf("Overwriting old [%04x]=%08x with %08x\n", pos, + tabptr[pos], entry); + tabptr[tab_size] = 1; + } +#if 0 + if(i >= 0) { + printf("Set code tab[%04x]=%08x (code:%04x)\n", pos, + entry, save_code); + } +#endif + tabptr[pos] = entry; + } +} + +word32 * +undeflate_init_fixed_tabs() +{ + word32 *tabptr; + int i; + + tabptr = &(g_undeflate_fixed_len_tab[0]); + // Init g_undeflate_fixed_len_tab[] for the fixed Huffman code + for(i = 0; i < 513; i++) { + tabptr[i] = 0; + } + // printf("Add fixed_len_tab for literals 0 - 143\n"); + for(i = 0; i < 144; i++) { + undeflate_add_tab_code(tabptr, 9, 0x30 + i, (8 << 16) | i); + } + // printf("Add fixed_len_tab for literals 144 - 255\n"); + for(i = 144; i < 256; i++) { + undeflate_add_tab_code(tabptr, 9, 0x190 + i - 144, + (9 << 16) | i); + } + // printf("Add fixed_len_tab for length codes 256 - 279\n"); + for(i = 256; i < 280; i++) { + // printf("code: %03x fixed_len_tab[%03x]=%08x\n", i, i - 256, + // g_undeflate_length_tab[i - 256]); + undeflate_add_tab_code(tabptr, 9, 0 + i - 256, + (7 << 16) | g_undeflate_length_tab[i - 256]); + } + // printf("Add fixed_len_tab for length codes 280 - 287\n"); + for(i = 280; i < 288; i++) { + undeflate_add_tab_code(tabptr, 9, 0xc0 + i - 280, + (8 << 16) | g_undeflate_length_tab[i - 256]); + } + if(tabptr[512]) { + return 0; + } + + // And init g_undeflate_fixed_dist_tab[] + tabptr = &(g_undeflate_fixed_dist_tab[0]); + for(i = 0; i < 33; i++) { + tabptr[i] = 0; + } + // printf("Add fixed_dist_tab for codes 0 - 29\n"); + for(i = 0; i < 30; i++) { + undeflate_add_tab_code(tabptr, 5, i, + (5 << 16) | g_undeflate_dist_tab[i]); + } + + if(tabptr[32]) { + return 0; + } + return tabptr; +} + +word32 * +undeflate_init_tables() +{ + undeflate_init_len_dist_tab(&(g_undeflate_length_tab[0]), + LENGTH_ENCODED, 2); + // code=257 has length 3, but the first entry is really code=256 + // so set 256 to length=2 + undeflate_init_len_dist_tab(&(g_undeflate_dist_tab[0]), DIST_ENCODED, + 1); + undeflate_init_bit_rev_tab(&(g_undeflate_bit_rev[0]), 512); + // undeflate_check_bit_reverse(); + return undeflate_init_fixed_tabs(); +} + +void +undeflate_free_tables() +{ + free(g_undeflate_dynamic_tabptr); + g_undeflate_dynamic_tabptr = 0; + free(g_undeflate_dynamic_dist_tabptr); + g_undeflate_dynamic_dist_tabptr = 0; +} + +void +undeflate_check_bit_reverse() +{ + word32 rev, tmp, checked; + int i, j, bits; + + // Check bit-reverse function. Reverse all values from 0-32767 + checked = 0; + for(bits = 1; bits <= 16; bits++) { + // printf("Checking bit reverse bits=%d\n", bits); + for(i = 0; i < 65536; i++) { + if(i >= (1 << bits)) { + break; + } + tmp = i; + rev = 0; + for(j = 0; j < bits; j++) { + rev = (rev << 1) | (tmp & 1); + tmp = tmp >> 1; + } + tmp = undeflate_bit_reverse(i, bits); + if(tmp != rev) { + printf("Reverse %04x bits:%d ret:%04x, " + "exp:%04x\n", i, bits, tmp, rev); + exit(2); + } + checked++; + } + } + printf("Checked %08x values\n", checked); +} + +word32 * +undeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size, + word32 *bl_count_ptr, int max_bits) +{ + word32 next_code[16]; + word32 code, tab_size, bits, entry; + int i; + + tab_size = (1 << max_bits); + if(max_bits > 15) { + printf("max_bits: %d out of range\n", max_bits); + return 0; + } + next_code[0] = 0; + bl_count_ptr[0] = 0; // Force number of 0-bit lengths to 0 + code = 0; + // printf("build_huff_tab, max_bits:%d, tab_size:%08x\n", max_bits, + // tab_size); + for(i = 1; i <= max_bits; i++) { + // printf("bl_count[%d] = %03x\n", i - 1, bl_count_ptr[i-1]); + code = (code + bl_count_ptr[i - 1]) << 1; + next_code[i] = code; + // printf("Set next_code[%d] = %03x\n", i, code); + } + for(i = 0; i < (int)tab_size; i++) { + tabptr[i] = 0; + } + tabptr[tab_size] = 0; + + for(i = 0; i < (int)len_size; i++) { + entry = entry_ptr[i]; + bits = (entry >> 16) & 0xf; + //printf("i:%03x, bits:%d, entry:%08x\n", i, bits, entry); + if(!bits) { + continue; + } + code = next_code[bits]++; + //printf("Set tab code:%03x = %08x\n", code, entry); + undeflate_add_tab_code(tabptr, max_bits, code, entry); + } + + // printf("All done, returning tabptr\n"); + return tabptr; +} + +word32 * +undeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base) +{ + word32 code_list[256+32+32+1]; + word32 len_codes[19]; + word32 bl_count[19], bl_count_dist[16]; + word32 *tabptr, *tabptr_dist; + byte *cptr_start; + word32 bit_pos, val, hlit, hdist, hclen, pos, max_bits, code_pos; + word32 total_codes_needed, repeat, mask, entry, bits; + word32 max_length_bits, max_distance_bits, extra_bits; + int i; + + // This is compressed compressed huffman lengths. First + // get the length codes, then get the actual lengths + // Get 14 bits, which always fits in 3 bytes + bit_pos = *bit_pos_ptr; + cptr_start = cptr; + val = (cptr[0] + (cptr[1] << 8) + (cptr[2] << 16)) >> bit_pos; + hlit = (val & 0x1f) + 257; // 257 - 288 + hdist = ((val >> 5) & 0x1f) + 1; + hclen = (val >> 10) & 0xf; +#if 0 + printf("At +%06x, bit:%d, hlit:%02x hdist:%02x, hclen:%02x\n", + (word32)(cptr - cptr_base), bit_pos, hlit, hdist, hclen); +#endif + if(cptr_base) { + // Avoid unused parameter warning + } + bit_pos += 14; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + for(i = 0; i < 19; i++) { + len_codes[i] = 0; + bl_count[i] = 0; + } + hclen += 4; // 19*3 = 57 bits, at most + max_bits = 0; + for(i = 0; i < (int)hclen; i++) { + val = ((cptr[0] + (cptr[1] << 8)) >> bit_pos) & 7; + entry = g_undeflate_lencode_positions[i]; + entry = entry & (~0xf0000); // clear bits from entry + pos = entry & 0x1f; + len_codes[pos] = entry | (val << 16); + // printf("len_codes[%d]=%08x\n", pos, len_codes[pos]); + bl_count[val]++; + if(val > max_bits) { + max_bits = val; + } + // printf("Num bits for len code %02x = %d\n", pos, val); + bit_pos += 3; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + } + // Build huffman table + tabptr = undeflate_build_huff_tab(&(g_undeflate_lencode_tab[0]), + &(len_codes[0]), 19, &(bl_count[0]), max_bits); + if(tabptr == 0) { + printf("Bad table\n"); + return 0; + } + + // Now we've made the table in tabptr. Read the length codes now + total_codes_needed = hlit + hdist; + // printf("Getting %04x total codes\n", total_codes_needed); + code_pos = 0; + mask = (1 << max_bits) - 1; + if(total_codes_needed > (256+32+32)) { + printf("total_codes_needed high: %04x\n", total_codes_needed); + return 0; + } + for(i = 0; i < 16; i++) { + bl_count[i] = 0; + bl_count_dist[i] = 0; + } + while(code_pos < total_codes_needed) { + pos = (cptr[0] | (cptr[1] << 8)) >> bit_pos; + pos = pos & mask; + entry = tabptr[pos & mask]; +#if 0 + printf("At +%06x, bit:%d: Raw code: %02x, entry:%08x\n", + (word32)(cptr - cptr_base), bit_pos, pos, entry); +#endif + val = entry & 0x1f; + + bits = (entry >> 16) & 7; + extra_bits = (entry >> 20) & 7; + repeat = (entry >> 8) & 0xf; + entry = (val << 16); // Set bits + bit_pos += bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + pos = (cptr[0] | (cptr[1] << 8)) >> bit_pos; +#if 0 + printf("At +%06x, bit:%d: Raw pos:%04x\n", + (word32)(cptr - cptr_base), bit_pos, pos); +#endif + pos = pos & ((1 << extra_bits) - 1); + repeat = repeat + pos; + bit_pos += extra_bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + if(!repeat) { + printf("Bad repeat value\n"); + return 0; + } + if(val >= 0x10) { + entry = 0; + if(val == 0x10) { // Repeat prev entry + entry = code_list[code_pos - 1]; + if(!code_pos) { + printf("Got repeat code 0x10 at 0!\n"); + return 0; + } + } + } + for(i = 0; i < (int)repeat; i++) { + code_list[code_pos] = entry; + // printf("Added code_list[%03x] = %08x\n", code_pos, + // entry); + code_pos++; + } + } + + // Fix lengths and literals + max_length_bits = 0; + for(i = 0; i < (int)hlit; i++) { + entry = code_list[i]; + bits = (entry >> 16) & 0xf; + bl_count[bits]++; + if(i >= 256) { + entry |= g_undeflate_length_tab[i - 256]; + } else { + entry |= i; + } + code_list[i] = entry; + if(bits > max_length_bits) { + max_length_bits = bits; + } + } + + // Fix distances + max_distance_bits = 0; + for(i = 0; i < (int)hdist; i++) { + entry = code_list[i + hlit]; + bits = (entry >> 16) & 0xf; + bl_count_dist[bits]++; + entry |= g_undeflate_dist_tab[i]; + code_list[i + hlit] = entry; + if(bits > max_distance_bits) { + max_distance_bits = bits; + } + } + if(code_pos != total_codes_needed) { + printf("Got %03x codes, needed %03x codes\n", code_pos, + total_codes_needed); + return 0; + } + // printf("max_length_bits: %d, max_distance_bits: %d\n", + // max_length_bits, max_distance_bits); + tabptr = g_undeflate_dynamic_tabptr; + if(!tabptr) { + tabptr = malloc(sizeof(word32)*((1 << 15) + 1)); + g_undeflate_dynamic_tabptr = tabptr; + // printf("malloc literal table\n"); + } + g_undeflate_dynamic_bits = max_length_bits; + //printf("Building literal/length table, %d entries, %d bits\n", hlit, + // max_length_bits); + //show_bits(&(code_list[0]), hlit); + + tabptr = undeflate_build_huff_tab(tabptr, &(code_list[0]), + hlit, &(bl_count[0]), max_length_bits); + if(tabptr == 0) { + printf("Building literal table failed\n"); + return 0; + } + //show_huftb(tabptr, max_length_bits); + + tabptr_dist = g_undeflate_dynamic_dist_tabptr; + if(!tabptr_dist) { + tabptr_dist = malloc(sizeof(word32) * ((1 << 15) + 1)); + g_undeflate_dynamic_dist_tabptr = tabptr_dist; + // printf("malloc dist table\n"); + } + g_undeflate_dynamic_dist_bits = max_distance_bits; + tabptr_dist = undeflate_build_huff_tab(tabptr_dist, &(code_list[hlit]), + hdist, &(bl_count_dist[0]), max_distance_bits); + if(tabptr_dist == 0) { + printf("Building dist table failed\n"); + return 0; + } + + // Update *bit_pos_ptr to skip over the table + *bit_pos_ptr = bit_pos + (int)(8*(cptr - cptr_start)); + return tabptr; +} + +byte * +undeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base, + byte *cptr_end) +{ + word32 *lit_tabptr, *dist_tabptr; + byte *ucptr, *ucptr_end; + word32 bfinal, btype, bit_pos, len, pos, extra_bits, entry, dist_entry; + word32 bits, is_len, dist, lit_mask, dist_mask, tmp; + int i; + + bit_pos = *bit_pos_ptr; + + // printf("At file offset %08x,bit %d cptr[0]:%02x %02x\n", + // (word32)(cptr - cptr_base), bit_pos, cptr[0], cptr[1]); + bfinal = (cptr[0] >> bit_pos) & 1; + bit_pos++; + btype = (((cptr[1] << 8) | cptr[0]) >> bit_pos) & 3; + bit_pos += 2; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + // printf("bfinal:%d, btype:%d\n", bfinal, btype); + + if(bfinal) { + dsk->fd = 0; // Last block + } + if(btype == 3) { // Reserved: error + return 0; + } else if(btype == 0) { // uncompressed + // Align cptr to next byte + bit_pos += 7; + cptr += (bit_pos >> 3); + *bit_pos_ptr = 0; + len = cptr[0] + (cptr[1] << 8); + ucptr = undeflate_ensure_dest_len(dsk, 0, len); + if(!ucptr) { + return 0; + } + cptr += 4; + for(i = 0; i < (int)len; i++) { + *ucptr++ = *cptr++; + } + dsk->dimage_size += len; + return cptr; + } + + if(btype == 1) { // Fixed Huffman codes + lit_tabptr = &(g_undeflate_fixed_len_tab[0]); + dist_tabptr = &(g_undeflate_fixed_dist_tab[0]); + lit_mask = 0x1ff; + dist_mask = 0x1f; + } else { // Dynamic Huffman codes + *bit_pos_ptr = bit_pos; + lit_tabptr = undeflate_dynamic_table(cptr, bit_pos_ptr, + cptr_base); + dist_tabptr = g_undeflate_dynamic_dist_tabptr; + // printf("dynamic table used %d bits\n", + // *bit_pos_ptr - bit_pos); + lit_mask = (1 << g_undeflate_dynamic_bits) - 1; + dist_mask = (1 << g_undeflate_dynamic_dist_bits) - 1; + bit_pos = *bit_pos_ptr; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + } + if(!lit_tabptr || !dist_tabptr) { + printf("Code table failure\n"); + return 0; + } + + ucptr = undeflate_ensure_dest_len(dsk, 0, 65536); // Just a guess + if(!ucptr) { + return 0; + } + ucptr_end = dsk->raw_data + dsk->raw_dsize - 500; + + while(cptr < cptr_end) { +#if 0 + printf("Top of loop, cptr:%p, lit_tabptr:%p, dsk->raw:%p\n", + cptr, lit_tabptr, dsk->raw_data); +#endif + + if(ucptr > ucptr_end) { + ucptr = undeflate_ensure_dest_len(dsk, ucptr, 65536); + ucptr_end = dsk->raw_data + dsk->raw_dsize - 500; + // printf("Update ucptr to %p\n", ucptr); + if(!ucptr) { + return 0; + } + } + pos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16); + pos = pos >> bit_pos; + entry = lit_tabptr[pos & lit_mask]; + bits = (entry >> 16) & 0xf; + is_len = (entry >> 24) & 1; + len = entry & 0xffff; +#if 0 + printf("At offset +%08x bit:%d, huffcode=%04x, is %d bits, " + "entry=%08x\n", (int)(cptr - cptr_base), bit_pos, + pos & lit_mask, bits, entry); +#endif + if(bits == 0) { + printf("bits=0, %08x bad table\n", lit_mask); + return 0; + } + bit_pos += bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + if(!is_len) { // Literal + // literal byte + // printf(" Out +%06x: %02x\n", + // (int)(ucptr - dsk->raw_data), len & 0xff); + //putc(len, g_outf); + *ucptr++ = len; + } else { + if(len == 2) { // Code=0x100, end block + // All done + // printf("Got the 0x100 code! All done!\n"); + *bit_pos_ptr = bit_pos; + dsk->dimage_size = ucptr - dsk->raw_data; + // printf("Set dsk->image_size = %08x\n", + // dsk->image_size); + return cptr; + } + extra_bits = (entry >> 20) & 7; + if(extra_bits) { + pos = cptr[0] | (cptr[1] << 8); + pos = pos >> bit_pos; + pos = pos & ((1 << extra_bits) - 1); + len += pos; + } +#if 0 + printf("At offset +%08x, bit:%d got extra_bits=%d, " + "len=%08x\n", (int)(cptr - cptr_base), bit_pos, + extra_bits, len); +#endif + bit_pos += extra_bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + + // Get distance code + pos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16); + pos = pos >> bit_pos; +#if 0 + printf("At offset +%08x, bit:%d raw distance code: " + "%02x\n", (int)(cptr - cptr_base), bit_pos, + pos & dist_mask); +#endif + dist_entry = dist_tabptr[pos & dist_mask]; + bits = (dist_entry >> 16) & 0xf; + if(bits == 0) { + printf("bits=0 for dist_entry:%08x %08x\n", + dist_entry, pos); + } + extra_bits = (dist_entry >> 20) & 0xf; + dist = dist_entry & 0xffff; + //printf("dist_entry:%08x, extra_bits:%d, dist:%05x\n", + // dist_entry, extra_bits, dist); + bit_pos += bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + if(extra_bits) { + pos = (cptr[0] | (cptr[1] << 8) | + (cptr[2] << 16)) >> bit_pos; +#if 0 + printf(" At offset +%08x, bit:%d, raw ex:" + "%08x\n", (int)(cptr - cptr_base), + bit_pos, pos); +#endif + tmp = pos & ((1 << extra_bits) - 1); + dist += tmp; +#if 0 + printf("at offset +%08x, got %d extra dist " + "for total dist=%d (%05x)\n", + (int)(cptr - cptr_base), extra_bits, + dist, pos); +#endif + bit_pos += extra_bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + } + //printf("Repeating %d bytes from dist:%05x\n", len, + // dist); + if(ucptr < (dsk->raw_data + dist)) { + printf("Dist out of bounds:%04x %p %p\n", + dist, ucptr, dsk->raw_data); + return 0; + } + for(i = 0; i < (int)len; i++) { + ucptr[0] = ucptr[0-(int)dist]; +#if 0 + putc(ucptr[0], g_outf); + printf(" Out +%06x: %02x\n", + (int)(ucptr - dsk->raw_data), + ucptr[0]); +#endif + ucptr++; + } + } + } + + printf("Ran out of compressed data, bad gzip file\n"); + + return 0; +} + +byte * +undeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size) +{ + word32 *wptr; + byte *cptr_base, *cptr_end; + word32 flg, xfl, xlen, bit_offset, exp_crc, len, crc; + + cptr_base = cptr; + cptr_end = cptr + compr_size; + + if((cptr[0] != 0x1f) || (cptr[1] != 0x8b) || (cptr[2] != 0x08)) { + printf("Not gzip file, exiting\n"); + return 0; + } + + flg = cptr[3]; + xfl = cptr[8]; + printf("flg:%02x and xflags:%02x\n", flg, xfl); + cptr += 10; + + if(flg & 4) { // FEXTRA set + xlen = cptr[0] + (cptr[1] * 256); + printf("FEXTRA XLEN is %d, skipping that many bytes\n", xlen); + cptr += 2 + xlen; + } + + if(flg & 8) { // FNAME set + cptr += strlen((char *)cptr) + 1; + } + if(flg & 0x10) { // FCOMMENT set + cptr += strlen((char *)cptr) + 1; + } + if(flg & 2) { // FHCRC set + cptr += 2; + } + printf("gzip header was %02x bytes long\n", (int)(cptr - cptr_base)); + + dsk->raw_dsize = 140*1024; // Just a guess, alloc size + dsk->raw_data = undeflate_malloc(dsk->raw_dsize); + if(dsk->raw_data == 0) { + return 0; + } + printf("Initial malloc (not realloc) set raw_data=%p\n", dsk->raw_data); + + dsk->dimage_size = 0; // Used size + + wptr = undeflate_init_tables(); + if(wptr == 0) { + return 0; // Some sort of error, get out + } + + bit_offset = 0; + while(cptr < cptr_end) { + cptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base, + cptr_end); + if(cptr == 0) { + // Failed + break; + } + if(dsk->fd == 0) { + printf("undeflate_block set fd=0, success\n"); + // Done, success! + // Check crc + if(bit_offset) { + cptr++; + } + if((cptr + 8) > cptr_end) { + printf("No CRC or LEN fields at end\n"); + break; + } + exp_crc = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16) | + (cptr[3] << 24); + len = cptr[4] | (cptr[5] << 8) | (cptr[6] << 16) | + (cptr[7] << 24); + if(len != dsk->dimage_size) { + printf("Len mismatch: exp %08x != %08llx\n", + len, dsk->dimage_size); + break; + } + crc = woz_calc_crc32(dsk->raw_data, len, 0); + if(crc != exp_crc) { + printf("CRC mismatch: %08x != exp %08x\n", + crc, exp_crc); + break; + } + // Real success, set raw_dsize + dsk->raw_data = undeflate_realloc(dsk->raw_data, + dsk->dimage_size); + dsk->raw_dsize = dsk->dimage_size; + return cptr; + } + } + + printf("Failed\n"); + // Disk image thread not found, get out + free(dsk->raw_data); + dsk->fd = -1; + dsk->dimage_size = 0; + dsk->raw_data = 0; + dsk->raw_dsize = 0; + return 0; +} + +void +undeflate_gzip(Disk *dsk, const char *name_str) +{ + byte *cptr; + dword64 compr_dsize, dret; + word32 compr_size; + int fd; + int i; + + // On success, set dsk->fd=0 and dsk->raw_data,raw_dsize properly. + printf("undeflate_gzip on file %s\n", name_str); + fd = open(name_str, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + return; + } + compr_dsize = cfg_get_fd_size(fd); + printf("size: %lld\n", compr_dsize); + if((compr_dsize >> 31) != 0) { + // > 2GB...too big for this code + printf("gzip file is too large\n"); + dsk->fd = -1; + return; + } + compr_size = (word32)compr_dsize; + + cptr = malloc(compr_size + 0x1000); + for(i = 0; i < 0x1000; i++) { + cptr[compr_size + i] = 0; + } + dret = cfg_read_from_fd(fd, cptr, 0, compr_size); + if(dret != compr_size) { + compr_size = 0; // Make header searching fail + } + //g_outf = fopen("out.dbg", "w"); + undeflate_gzip_header(dsk, cptr, compr_size); + + free(cptr); + undeflate_free_tables(); +} + +byte * +undeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size) +{ + word32 *wptr; + byte *cptr_base, *cptr_end; + word32 bit_offset; + + cptr_base = cptr; + cptr_end = cptr + dcompr_size; + + dsk->raw_data = undeflate_malloc(dsk->raw_dsize); + if(dsk->raw_data == 0) { + return 0; + } + printf("Initial malloc (not realloc) set raw_data=%p\n", dsk->raw_data); + + dsk->dimage_size = 0; // Used size + + wptr = undeflate_init_tables(); + if(wptr == 0) { + return 0; // Some sort of error, get out + } + + bit_offset = 0; + while(cptr < cptr_end) { + cptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base, + cptr_end); + if(cptr == 0) { + // Failed + break; + } + if(dsk->fd == 0) { + printf("undeflate_block set fd=0, success\n"); + // Done, success! + // Check crc + if(bit_offset) { + cptr++; + } + // Real success, set raw_dsize + dsk->raw_data = undeflate_realloc(dsk->raw_data, + dsk->dimage_size); + dsk->raw_dsize = dsk->dimage_size; + return cptr; + } + } + + printf("Failed\n"); + // Disk image thread not found, get out + free(dsk->raw_data); + dsk->fd = -1; + dsk->dimage_size = 0; + dsk->raw_data = 0; + dsk->raw_dsize = 0; + return 0; +} + +byte g_zip_local_file_header[] = { 0x50, 0x4b, 0x03, 0x04 }; +byte g_zip_central_file_header[] = { 0x50, 0x4b, 0x01, 0x02 }; +byte g_zip_end_central_dir_header[] = { 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0 }; +byte g_zip64_end_central_dir_locator[] = { 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0 }; +byte g_zip64_end_central_dir_header[] = { 0x50, 0x4b, 0x06, 0x06 }; + +extern Cfg_listhdr g_cfg_partitionlist; + + +int +undeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off, + dword64 uncompr_dsize, dword64 compr_dsize) +{ + byte buf[64]; + byte *cptr, *cptr2; + dword64 dret, compr_doffset; + word32 compr_method, name_len, extra_len; + word32 bit_flags; + int ret; + int i; + + // return -1 on failure, >= 0 on success + + printf("undeflate_zipfile called, fd:%d, offset:%08llx, unc:%lld " + "compr:%lld\n", fd, dlocal_header_off, uncompr_dsize, + compr_dsize); + + dret = cfg_read_from_fd(fd, &buf[0], dlocal_header_off, 64); + if(dret != 64) { + printf("read dret:%08llx != 64\n", dret); + return -1; + } + + for(i = 0; i < 4; i++) { + if(buf[i] != g_zip_local_file_header[i]) { + printf("hdr[%d]=%02x\n", i, buf[i]); + return -1; + } + } + + if(((uncompr_dsize | compr_dsize) >> 31) != 0) { + printf("Size >2GB, not supported\n"); + return -1; + } + bit_flags = cfg_get_le16(&(buf[6])); + compr_method = cfg_get_le16(&(buf[8])); + // compr_size = cfg_get_le32(&(buf[18])); // Probably 0 + // uncompr_size = cfg_get_le32(&(buf[22])); // Probably 0 + name_len = cfg_get_le16(&(buf[26])); + extra_len = cfg_get_le16(&(buf[28])); + + // The ZIP file format is annoying, the local header doesn't have + // compr_size and uncompr_size generally (if bit_flags bit 3 is set). + // Even if it does, it's fine to always use the central directory + printf("bit_flags: %04x\n", bit_flags); + + compr_doffset = dlocal_header_off + 30 + name_len + extra_len; + + cptr = undeflate_malloc(compr_dsize + 0x1000); + for(i = 0; i < 0x1000; i++) { + cptr[compr_dsize + i] = 0; + } + + dret = cfg_read_from_fd(fd, cptr, compr_doffset, compr_dsize); + if(dret != compr_dsize) { + return -1; + } + dsk->raw_dsize = uncompr_dsize; + dsk->dimage_size = uncompr_dsize; + + ret = -1; + if(compr_method == 0) { // Stored, just use cptr + dsk->raw_data = cptr; + dsk->raw_dsize = uncompr_dsize; + dsk->dimage_start = 0; + close(fd); + dsk->fd = 0; + cptr = 0; // So free(cptr) does nothing + ret = 0; + } else if(compr_method == 8) { // Deflate + cptr2 = undeflate_zipfile_blocks(dsk, cptr, compr_dsize); + printf("undeflate_zipfile_blocks ret:%p\n", cptr2); + if(cptr2 != 0) { + ret = 0; + } + } else { + printf("Unknown compr_method:%04x\n", compr_method); + } + free(cptr); + undeflate_free_tables(); + + return ret; +} + +int +undeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len, + int min_size) +{ + int pos, good; + int i; + + // Search for cmp_ptr in the bptr buffer (basically, look for "PKxx" + // header strings). + pos = size - min_size; + good = 0; + while(pos >= 0) { + good = 1; + for(i = 0; i < cmp_len; i++) { + if(bptr[pos + i] != cmp_ptr[i]) { + good = 0; + break; + } + } + if(good) { + break; + } + pos--; + } + + if(!good) { + return -1; + } + return pos; +} + +int +undeflate_zipfile_make_list(int fd) +{ + byte buf[1024]; + dword64 dret, dsize, dir_doff, dir_dsize, unc_dsize, compr_dsize; + dword64 local_dheader, dneg1, dval, doff, dpos, dlen; + byte *dirptr, *name_ptr, *bptr, *bptr2; + char *str; + word32 extra_len, comment_len, ent, entries, part_len, inc; + word32 tmp_off, ex_off, this_size, this_id; + int pos, good, add_it, need_compr, need_unc, need_dheader; + int name_len; + int i; + + dret = cfg_read_from_fd(fd, &buf[0], 0, 64); + if(dret != 64) { + return 0; // Not a ZIP file + } + + // See if it's a PKZIP file, starting 0x50, 0x4b, 0x03, 0x04 + for(i = 0; i < 4; i++) { + if(buf[i] != g_zip_local_file_header[i]) { + return 0; + } + } + + printf("This looks like a .zip file\n"); + + // Find end of central directory record in last 1024 bytes. If it's + // not there, this is too complex of a ZIP file for us, give up + dsize = cfg_get_fd_size(fd); + + for(i = 0; i < 1024; i++) { + buf[i] = 0; + } + dpos = 0; + dlen = dsize; + if(dsize > 1024) { + dpos = dsize - 1024; + dlen = 1024; + } + dret = cfg_read_from_fd(fd, &buf[0], dpos, dlen); + if(dret != dlen) { + return 0; // Unknown problem + } + + pos = undeflate_zipfile_search(&buf[0], + &g_zip_end_central_dir_header[0], 1024, 8, 22); + // End of Central Directory is at least 22 bytes + if(pos < 0) { + printf("Cannot parse this .zip file\n"); + return 0; + } + + entries = cfg_get_le16(&(buf[pos + 8])); + dir_dsize = cfg_get_le32(&(buf[pos + 12])); + dir_doff = cfg_get_le32(&(buf[pos + 16])); + +#if 0 + printf(".zip entries:%04x, dir_dsize:%06llx, dir_doff:%08llx\n", + entries, dir_dsize, dir_doff); +#endif + dneg1 = 0xffffffffULL; + if(dir_doff == dneg1) { + printf("We must look for the ZIP64 end dir locator\n"); + pos = undeflate_zipfile_search(&buf[0], + &g_zip64_end_central_dir_locator[0], 1024, 8, 20); + if(pos < 0) { + printf("Cannot parse this ZIP64 file\n"); + return 0; + } + doff = cfg_get_le64(&(buf[pos + 8])); + printf("ZIP64 end of central dir record at 0x%08llx\n", doff); + if((doff + 64) > dsize) { + printf("End Central Dir record out of bounds\n"); + return 0; + } + // Now read end of central directory record. Just read 64 bytes + // It has to be at least 56 bytes, and the locator had to be + // after, so it must fit + dret = cfg_read_from_fd(fd, &buf[0], doff, 64); + if(dret != 64) { + return 0; // Unknown problem + } + pos = undeflate_zipfile_search(&buf[0], + &g_zip64_end_central_dir_header[0], 64, 4, 64); + if(pos != 0) { + printf("ZIP64 end of central dir record not found\n"); + return 0; + } + + entries = cfg_get_le32(&(buf[32])); + dir_dsize = cfg_get_le64(&(buf[40])); + dir_doff = cfg_get_le64(&(buf[48])); + } + + if((entries < 1) || (dir_dsize > dsize) || (dir_dsize > (1L << 20)) || + ((dir_doff + dir_dsize) > dsize)) { + printf("Malformed zip file\n"); + return 0; + } + + dirptr = undeflate_malloc(dir_dsize); + dret = cfg_read_from_fd(fd, dirptr, dir_doff, dir_dsize); + if(dret != dir_dsize) { + printf("Couldn't read central dir\n"); + return 0; + } + + part_len = cfg_partition_maybe_add_dotdot(); + // part_len is strlen(g_cfg_part_path[]); + + pos = 0; + ent = 0; + while(pos < (int)dir_dsize) { +#if 0 + printf("Working on ent %d at pos %d\n", ent, pos); +#endif + if(ent >= entries) { + break; // all done + } + good = 1; + for(i = 0; i < 4; i++) { + if(dirptr[pos + i] != g_zip_central_file_header[i]) { + // corrupt index, get out + printf("At pos %04x, i:%d bad hdr\n", pos, i); + good = 0; + break; + } + } + if(!good) { + break; + } + compr_dsize = cfg_get_le32(&dirptr[pos + 20]); + unc_dsize = cfg_get_le32(&dirptr[pos + 24]); + name_len = cfg_get_le16(&dirptr[pos + 28]); + extra_len = cfg_get_le16(&dirptr[pos + 30]); + comment_len = cfg_get_le16(&dirptr[pos + 32]); + local_dheader = cfg_get_le32(&dirptr[pos + 42]); + if((pos + 46UL + name_len) > dir_dsize) { + printf("Corrupt entry: pos:%04x, name_len:%04x, " + "dir_dsize:%05llx\n", pos, name_len, dir_dsize); + break; + } + + need_unc = (unc_dsize == dneg1); + need_compr = (compr_dsize == dneg1); + need_dheader = (local_dheader == dneg1); + + // Walk extras to update unc/compr size and file offset, if + // the standard fields are 0xffffffff. + bptr = &(dirptr[pos + 46 + name_len]); + ex_off = 0; + add_it = 1; + while(ex_off < extra_len) { +#if 0 + printf("Working on ex_off:%d out of %d\n", ex_off, + extra_len); +#endif + this_id = cfg_get_le16(&bptr[ex_off]); + this_size = cfg_get_le16(&bptr[ex_off + 2]); + if((this_size + ex_off + pos + 46UL + name_len) > + dir_dsize) { + printf("Corrupt ZIP64 extra info entry\n"); + add_it = 0; + break; + } + ex_off += 4; + if(this_id == 0x0001) { + tmp_off = 0; + bptr2 = &(bptr[ex_off]); + while(tmp_off < this_size) { + dval = cfg_get_le64(bptr2); +#if 0 + printf("tmp_off %d of %d, dval:" + "%016llx\n", tmp_off, + this_size, dval); +#endif + if(need_compr) { + compr_dsize = dval; + need_compr = 0; + } else if(need_unc) { + unc_dsize = dval; + need_unc = 0; + } else if(need_dheader) { + local_dheader = dval; + need_dheader = 0; + } else { + printf("Corrupt ZIP64\n"); + add_it = 0; + } + tmp_off += 8; + bptr += 8; + } + } + ex_off += this_size; + } + + if(need_unc || need_compr || need_dheader) { + printf("Bad ZIP64 overrides\n"); + add_it = 0; + } + + // See if filename is at the proper depth + name_ptr = &(dirptr[pos + 46]); + if(add_it) { + add_it = cfg_partition_name_check(name_ptr, name_len); + } + + //printf("ent:%d name:%s len:%d had add_it:%d, part_len:%d\n", + // ent, name_ptr, name_len, add_it, part_len); + + inc = 46 + name_len + extra_len + comment_len; + if(add_it) { + // Handle directories either explicitly listed, as + // foo/, foo/bar/, foo/bar/1, foo/bar/2 ; or + // implied: foo/bar/1 and foo/bar/2 as entries + // implies foo/ and foo/bar/ are directories. + // Add any name at the current part_len level, but + // make sure it's unique (don't add lots of "foo"s). + name_ptr += part_len; + name_len -= part_len; + if(name_len <= 0) { + add_it = 0; + } + for(i = 0; i < name_len; i++) { + if(name_ptr[i] == '/') { + // This ends this name at this level + if(i > 0) { + add_it = 2; + name_len = i + 1; + } else { + add_it = 0; + } + break; + } + } + } + if((add_it < 2) && (unc_dsize < 140*1024)) { + add_it = 0; + } + if(add_it) { + str = malloc(name_len + 1); + cfg_strncpy(str, (char *)&name_ptr[0], name_len + 1); + cfg_file_add_dirent_unique(&g_cfg_partitionlist, str, + add_it - 1, unc_dsize, local_dheader, + compr_dsize, ent); + free(str); + } + pos += inc; + ent++; + } + free(dirptr); + + printf("Returning %d, pos:%05x, dir_dsize:%05llx\n", ent, pos, + dir_dsize); + return g_cfg_partitionlist.last; +} + diff --git a/gsplus/src/unshk.c b/gsplus/src/unshk.c new file mode 100644 index 0000000..12737e9 --- /dev/null +++ b/gsplus/src/unshk.c @@ -0,0 +1,549 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2021 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// This code is based on the official NuFX documentation in Apple II FTN.e08002 +// available at: http://nulib.com/library/FTN.e08002.htm. Andy McFadden has +// reverse-engineered GSHK (and its quirks) in Nulib, at: http://nulib.com/, +// and that code was very helpful in getting the basic algorithms correct. + +#include "defc.h" + +word32 +unshk_get_long4(byte *bptr) +{ + word32 val; + int i; + + // Get 4 bytes in little-endian form + val = 0; + for(i = 3; i >=0 ; i--) { + val = (val << 8) | bptr[i]; + } + return val; +} + +word32 +unshk_get_word2(byte *bptr) +{ + // Get 2 bytes in little-endian form + return (bptr[1] << 8) | bptr[0]; +} + +word32 +unshk_calc_crc(byte *bptr, int size, word32 start_crc) +{ + word32 crc; + int i, j; + + // No table used: do basic CRC operation on size bytes. For CCITT-16 + // (the one used in ShrinkIt), xor the byte into the upper 8 bits of + // the current crc, then xor in 0x1021 for each '1' bit shifted out + // the top. Use a 32-bit crc variable to get the bit shifted out. + crc = start_crc & 0xffff; + for(i = 0; i < size; i++) { + crc = crc ^ (bptr[i] << 8); + for(j = 0; j < 8; j++) { + crc = crc << 1; + if(crc & 0x10000) { + crc = crc ^ 0x11021; + // XOR in 0x1021, and clear bit 16 as well. + } + } + // printf("CRC after [%04x]=%02x is %04x\n", i, bptr[i], crc); + } + return crc & 0xffff; +} + +int +unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr) +{ + byte *start_ucptr; + word32 c; + int outlen, count; + int i; + + // RLE is 3 bytes: { 0xdb, char, count}, where count==0 means output + // one char. + start_ucptr = ucptr; + while(len > 0) { + c = *cptr++; + len--; + if(c == rle_delim) { + c = *cptr++; + count = *cptr++; + len -= 2; + for(i = 0; i <= count; i++) { + *ucptr++ = c; + } + } else { + *ucptr++ = c; + } + } + outlen = (int)(ucptr - start_ucptr); + if(outlen != 0x1000) { + printf("RLE failed, output %d bytes\n", outlen); + return 1; + } + return 0; +} + +void +unshk_lzw_clear(Lzw_state *lzw_ptr) +{ + int i; + + lzw_ptr->entry = 0x100; // First expected table pos + lzw_ptr->bits = 9; + for(i = 0; i < 256; i++) { + lzw_ptr->table[i] = i << 12; // Encodes depth==0 as well + } + lzw_ptr->table[0x100] = 0; +} + +// LZW Table format in 32-bit word: { depth[11:0], finalc[7:0], code[11:0] } + +byte * +unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen) +{ + byte *end_ucptr, *bptr; + word32 mask, val, entry, newcode, finalc_code; + int bit_pos, depth, bits; + + // This routine handles ShrinkIt LZW/1 and LZW/2 streams. It expects + // the caller has set entry=0x100 and bits=9 at the start of each + // LZW/1 chunk + + entry = lzw_ptr->entry; + bits = lzw_ptr->bits; + end_ucptr = ucptr + uclen; + //printf("unlzw block: format:%d, uclen:%04x\n", thread_format, uclen); + mask = (1 << bits) - 1; + bit_pos = 0; + + while(ucptr < end_ucptr) { + newcode = (cptr[2] << 16) | (cptr[1] << 8) | cptr[0]; + newcode = (newcode >> bit_pos) & mask; + bit_pos += bits; + cptr += (bit_pos >> 3); + bit_pos = bit_pos & 7; + // printf("At entry:%04x, bits:%d newcode:%04x\n", entry, bits, + // newcode); + if((entry + 1) >= mask) { + bits++; + mask = (mask << 1) | 1; + // Note this is one too early, but this is needed to + // match ShrinkIt + } + + // Newcode is up to 12-bits, where <= 0xff means just this + // char, and >= 0x101 means chase down that code, output the + // character in that table entry, and repeat + if(newcode == 0x100) { + // printf("Got clear code\n"); + entry = 0x100; + bits = 9; + mask = 0x1ff; + continue; + } + if(newcode > entry) { + printf("Bad code: %04x, entry:%04x\n", newcode, entry); + return 0; + } +#if 0 + if(newcode == entry) { + // KwKwK case: operate on oldcode + printf("KwKwK case!\n"); + } +#endif + finalc_code = newcode; + depth = lzw_ptr->table[newcode & 0xfff] >> 20; + // depth will be 0 for 1 character, 1 for 2 characters, etc. + bptr = ucptr + depth; + while(bptr >= ucptr) { + finalc_code = lzw_ptr->table[finalc_code & 0xfff]; + *bptr-- = (finalc_code >> 12) & 0xff; + } + val = lzw_ptr->table[entry]; + lzw_ptr->table[entry] = (val & (~0xff000)) | + (finalc_code & 0xff000); + // [entry] has code from last iteration (which stuck in + // the last finalc char, which we need to toss now), + // and update it with the correct finalc character. + // printf("Table[%04x]=%08x\n", entry, lzw_ptr->table[entry]); + + depth++; + ucptr += depth; +#if 0 + bptr = ucptr - depth; + printf("src:%04x, out+%06x: ", (int)(cptr - cptr_start), + (int)(ucptr - depth - (end_ucptr - uclen))); + for(i = 0; i < depth; i++) { + printf(" %02x", *bptr++); + } + printf("\n"); +#endif + lzw_ptr->table[entry + 1] = (depth << 20) | + (finalc_code & 0xff000) | newcode; + // Set tab[entry+1] for KwKwK case, with this newcode, + // and this finalc character. This also saves this + // newcode when the next code is received. + entry++; + } + lzw_ptr->entry = entry; + lzw_ptr->bits = bits; + if(bit_pos) { // We used part of this byte, use it + cptr++; + } + return cptr; +} + +void +unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr, + word32 uncompr_size, word32 thread_format, byte *base_cptr) +{ + Lzw_state lzw_state; + byte *end_cptr, *end_ucptr, *rle_inptr; + word32 rle_delim, len, use_lzw, lzw_len, crc, chunk_crc; + int ret; + int i; + + printf("Uncompress %d compress bytes into %d bytes, source offset:" + "%08x\n", compr_size, uncompr_size, (word32)(cptr - base_cptr)); + + // LZW/1 format: crc_lo, crc_hi, vol, rle_delim then start the chunk + // each chunk: rle_len_lo, rle_len_hi, lzw_used + // LZW/2 format: vol, rle_delim then start the chunk + // each chunk: rle_len_lo, rle_len_hi, lzw_len_lo, lzw_len_hi + // where rle_len_hi[7]==1 means LZW was used + end_cptr = cptr + compr_size; + end_ucptr = ucptr + uncompr_size; + + chunk_crc = 0; + if(thread_format != 3) { // LZW/1 + chunk_crc = (cptr[1] << 8) | cptr[0]; + cptr += 2; // Skip over CRC bytes + } + dsk->vol_num = cptr[0]; // LZW/1 + rle_delim = cptr[1]; + cptr += 2; + unshk_lzw_clear(&lzw_state); + + // printf("vol_num:%02x, rle_delim:%02x\n", dsk->vol_num, rle_delim); + + // LZW/1 format for each chunk: len_lo, len_hi, use_lzw + // LZW/2 format for each chunk: len_lo, len_hi. If len_hi[7]=1, then + // two more bytes: lzw_len_lo, lzw_len_hi + while(cptr < (end_cptr - 4)) { + if(ucptr >= end_ucptr) { + break; + } + len = (cptr[1] << 8) | cptr[0]; +#if 0 + printf("chunk at +%08x, len:%04x, dest offset:%08x\n", + (word32)(cptr - base_cptr), len, + (word32)(ucptr - (end_ucptr - uncompr_size))); +#endif + cptr += 2; + use_lzw = (len >> 15) & 1; + if(len & 0x6000) { + printf("Illegal length: %04x\n", len); + return; // Ilegal length + } + len = len & 0x1fff; + lzw_len = 0; + if(thread_format == 3) { // LZW/2 + if(use_lzw) { + lzw_len = (cptr[1] << 8) | cptr[0]; + if(lzw_len > 0x1004) { + printf("Bad lzw_len: %04x\n", lzw_len); + return; // Illegal + } + cptr += 2; + lzw_len -= 4; // Counts from [-4] + } + } else { // LZW/1 + use_lzw = *cptr++; + if(use_lzw >= 2) { + printf("Bad use_lzw:%02x\n", use_lzw); + return; // Bad format + } + } + rle_inptr = cptr; + if(use_lzw) { + //printf("lzw on %02x.%02x.%02x.., %d bytes (rle:%d)\n", + // cptr[0], cptr[1], cptr[2], lzw_len, len); + rle_inptr = ucptr; + if(len != 0x1000) { + // RLE pass is needed: Write to ucptr+0x1000, + // and then UnRLE down to ucptr; + rle_inptr = ucptr + 0x1000; + } + cptr = unshk_unlzw(cptr, &lzw_state, rle_inptr, len); + if(cptr == 0) { + printf("Bad LZW stream\n"); + return; + } + if(thread_format != 3) { // LZW/1 + lzw_state.entry = 0x100; // Reset table + lzw_state.bits = 9; + } + } else { + lzw_state.entry = 0x100; // Reset table + lzw_state.bits = 9; + } + if(len != 0x1000) { + // printf("RLE on %02x.%02x.%02x... %d bytes\n", + // cptr[0], cptr[1], cptr[2], len); + ret = unshk_unrle(rle_inptr, len, rle_delim, ucptr); + if(ret) { + printf("unRLE failed\n"); + return; + } + if(!use_lzw) { + cptr += len; + } + } else if(!use_lzw) { + // Uncompressed + // printf("Uncompressed %02x.%02x.%02x....%d bytes\n", + // cptr[0], cptr[1], cptr[2], len); + for(i = 0; i < 0x1000; i++) { + ucptr[i] = *cptr++; + } + } + // write(g_out_fd, ucptr, 0x1000); + ucptr += 0x1000; + } + + printf("cptr:%p, end_cptr:%p, uncompr_size:%08x\n", cptr, end_cptr, + uncompr_size); + if(thread_format != 3) { // LZW/1 + crc = unshk_calc_crc(ucptr - uncompr_size, uncompr_size, 0); + //printf("LZW/1 calc CRC %04x vs CRC %04x\n", crc, chunk_crc); + if(crc != chunk_crc) { + printf("Bad LZW/1 CRC: %04x != %04x\n", crc, chunk_crc); + return; + } + } + dsk->fd = 0; +} + +void +unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr) +{ + byte *cptr_end, *dptr, *ucptr; + word32 total_records, attrib_count, total_threads, thread_class; + word32 thread_format, thread_kind, thread_eof, comp_thread_eof; + word32 thread_crc, crc, version, disk_size, block_size, num_blocks; + word32 filename_length; + int i; + + cptr_end = cptr + compr_size; + if(compr_size < 0xa0) { + printf("Didn't read everything\n"); + return; + } + + // Parse NuFX format: "NuFile" with alternating high bits + if((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) || + (cptr[3] != 0xe9) || (cptr[4] != 0x6c) || (cptr[5] != 0xe5)) { + printf("Not NuFile, exiting\n"); + return; + } + total_records = unshk_get_long4(&cptr[8]); + if(total_records < 1) { + return; + } + + // Master Header to NuFile is apparently 48 bytes. Look for "NuFX" + // Header to describe threads + cptr += 0x30; + if((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) || + (cptr[3] != 0xd8)) { + return; + } + attrib_count = unshk_get_word2(&cptr[6]); + version = unshk_get_word2(&cptr[8]); // >= 3 means File CRC + total_threads = unshk_get_long4(&cptr[10]); + num_blocks = unshk_get_long4(&cptr[26]); // extra_type + block_size = unshk_get_word2(&cptr[30]); // storage_type + // P8 ShrinkIt is riddled with bugs. Disk archives have incorrect + // thread_eof for the uncompressed total size. So we need to do + // num_blocks * block_size to get the real size. But this can be + // buggy too! These fixes are from NuFxLib::Thread.c actualThreadEOF + // comments + // First, fix block_size. SHK v3.0.1 stored it as a small value + if(block_size < 256) { + block_size = 512; + } + disk_size = block_size * num_blocks; + if(disk_size == (70*1024)) { + // Old GSHK apparently set block_size==256 but blocks=280 for + // 5.25" DOS 3.3 disks...block size must be 512 to equal 140K. + disk_size = 140*1024; + } + if(disk_size < 140*1024) { + printf("disk_size %dK is invalid\n", disk_size >> 10); + return; + } + cptr += attrib_count; + filename_length = unshk_get_word2(&cptr[-2]); // filename_length + cptr += filename_length; + dptr = cptr + 16*total_threads; + // Each thread is 16 bytes, so the data is at +16*total_threads + // The data is in the same order as the header for the threads + // We ignore anything other than a data thread for SDK + for(i = 0; i < (int)total_threads; i++) { + if((dptr >= cptr_end) || (cptr >= cptr_end)) { + return; + } + thread_class = unshk_get_word2(&cptr[0]); + thread_format = unshk_get_word2(&cptr[2]); + thread_kind = unshk_get_word2(&cptr[4]); + thread_crc = unshk_get_word2(&cptr[6]); + //thread_eof = unshk_get_long4(&cptr[8]); + // thread_eof is wrong in P8 ShrinkIt, so just use disk_size + thread_eof = disk_size; + comp_thread_eof = unshk_get_long4(&cptr[12]); + if((dptr + comp_thread_eof) > cptr_end) { + return; // Corrupt + } + if((thread_class == 2) && (thread_kind == 1)) { + // Disk image! + ucptr = malloc(thread_eof + 0x1000); + unshk_data(dsk, dptr, comp_thread_eof, ucptr, + thread_eof, thread_format, base_cptr); + if(dsk->fd == 0) { + // Success, so far. Check CRC + printf("Version:%d, thread_crc:%04x\n", + version, thread_crc); + if(version >= 3) { // CRC is valid + crc = unshk_calc_crc(ucptr, thread_eof, + 0xffff); +#if 0 + printf("Thread CRC:%04x, exp:%04x\n", + crc, thread_crc); +#endif + if(crc != thread_crc) { + printf("Bad CRC: %04x != exp " + "%04x\n", crc, + thread_crc); + dsk->fd = -1; + } + } + } + if(dsk->fd < 0) { + free(ucptr); + } else { + // Real success, set raw_size + dsk->raw_dsize = thread_eof; + dsk->raw_data = ucptr; + return; + } + } else { + dptr += comp_thread_eof; + cptr += 16; + } + } + + // Disk image thread not found, get out +} + +void +unshk(Disk *dsk, const char *name_str) +{ + byte *cptr; + int compr_size, fd, pos, ret; + int i; + + printf("unshk %s\n", name_str); + // Handle .sdk inside a .zip + if(dsk->raw_data) { + unshk_dsk_raw_data(dsk); + return; + } + + // File is not opened yet, try to open it + fd = open(name_str, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + return; + } + compr_size = (int)cfg_get_fd_size(fd); + printf("size: %d\n", compr_size); + + cptr = malloc(compr_size + 0x1000); + pos = 0; + for(i = 0; i < 0x1000; i++) { + cptr[compr_size + i] = 0; + } + while(1) { + if(pos >= compr_size) { + break; + } + ret = read(fd, cptr + pos, compr_size - pos); + if(ret <= 0) { + break; + } + pos += ret; + } + close(fd); + + if(pos != compr_size) { + compr_size = 0; // Make header searching fail + } + unshk_parse_header(dsk, cptr, compr_size, cptr); + + free(cptr); +} + +void +unshk_dsk_raw_data(Disk *dsk) +{ + byte *save_raw_data, *cptr; + dword64 save_raw_dsize; + int save_fd, compr_size; + int i; + // This code handles the case of .sdk inside a .zip (for example). + // Since unshk() code uses dsk->fd, dsk->raw_data, and dsk->raw_dsize + // to communicate success in unshk'ing the disk, we need to copy + // those, and restore them, if the unshk fails + save_fd = dsk->fd; + save_raw_data = dsk->raw_data; + save_raw_dsize = dsk->raw_dsize; + if(save_raw_dsize >= (1ULL << 30)) { + return; // Too large + } + + dsk->fd = -1; + dsk->raw_data = 0; + dsk->raw_dsize = 0; + + compr_size = (int)save_raw_dsize; + cptr = malloc(compr_size + 0x1000); + for(i = 0; i < 0x1000; i++) { + cptr[compr_size + i] = 0; + } + for(i = 0; i < compr_size; i++) { + cptr[i] = save_raw_data[i]; + } + + unshk_parse_header(dsk, cptr, compr_size, cptr); + free(cptr); + + if(dsk->raw_data) { + // Success, free the old raw data + free(save_raw_data); + return; + } + dsk->fd = save_fd; + dsk->raw_data = save_raw_data; + dsk->raw_dsize = save_raw_dsize; +} + diff --git a/gsplus/src/vars b/gsplus/src/vars new file mode 100644 index 0000000..75cf7de --- /dev/null +++ b/gsplus/src/vars @@ -0,0 +1,9 @@ + +TARGET = gsplus +OBJECTS1 = macsnd_driver.o +CCOPTS = -Wall -O2 -DMAC +SUFFIX = +NAME = gsplus + +XOPTS = + diff --git a/gsplus/src/vars_mac b/gsplus/src/vars_mac new file mode 100644 index 0000000..75cf7de --- /dev/null +++ b/gsplus/src/vars_mac @@ -0,0 +1,9 @@ + +TARGET = gsplus +OBJECTS1 = macsnd_driver.o +CCOPTS = -Wall -O2 -DMAC +SUFFIX = +NAME = gsplus + +XOPTS = + diff --git a/gsplus/src/vars_mac_x b/gsplus/src/vars_mac_x new file mode 100644 index 0000000..5fc8fed --- /dev/null +++ b/gsplus/src/vars_mac_x @@ -0,0 +1,10 @@ + +TARGET = gsplus +OBJECTS1 = macsnd_driver.o xdriver.o +CCOPTS = -O2 -DMAC -Wall -I/usr/X11/include +SUFFIX = +NAME = gsplus + +XOPTS = +LDOPTS = -Wl,-framework,CoreAudio -Wl,-framework,CoreFoundation -Wl,-framework,AudioToolbox + diff --git a/gsplus/src/vars_x86linux b/gsplus/src/vars_x86linux new file mode 100644 index 0000000..00d1927 --- /dev/null +++ b/gsplus/src/vars_x86linux @@ -0,0 +1,11 @@ + +TARGET = gsplus +OBJECTS1 = pulseaudio_driver.o xdriver.o +CCOPTS = -O2 -Wall -fomit-frame-pointer -DPULSE_AUDIO +NAME = gsplus +LD = $(CC) +EXTRA_LIBS = -lXext -lpulse +EXTRA_SPECIALS = + +XOPTS = -I/usr/X11R6/include + diff --git a/gsplus/src/video.c b/gsplus/src/video.c new file mode 100644 index 0000000..66ecdbf --- /dev/null +++ b/gsplus/src/video.c @@ -0,0 +1,2927 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +#include + +#include "defc.h" + +extern int Verbose; + +word32 g_a2_filt_stat[200]; +int g_a2_line_left_edge[200]; +int g_a2_line_right_edge[200]; + +byte g_cur_border_colors[270]; + +word32 g_a2_screen_buffer_changed = (word32)-1; +word32 g_full_refresh_needed = (word32)-1; + +word32 g_cycs_in_40col = 0; +word32 g_cycs_in_xredraw = 0; +word32 g_refresh_bytes_xfer = 0; + +extern byte *g_slow_memory_ptr; +extern int g_fatal_log; + +extern dword64 g_cur_dfcyc; + +extern int g_line_ref_amt; + +extern word32 g_c034_val; +extern int g_config_control_panel; +extern int g_halt_sim; + +word32 g_slow_mem_changed[SLOW_MEM_CH_SIZE]; +word32 g_slow_mem_ch2[SLOW_MEM_CH_SIZE]; + +word32 g_a2font_bits[0x100][8]; + +word32 g_superhires_scan_save[2][256]; + +Kimage g_mainwin_kimage = { 0 }; +Kimage g_debugwin_kimage = { 0 }; +int g_debugwin_last_total = 0; + +extern int g_debug_lines_total; + +extern dword64 g_last_vbl_dfcyc; +extern dword64 g_video_pixel_dcount; + +dword64 g_video_dfcyc_check_input = 0; +int g_video_act_margin_left = BASE_MARGIN_LEFT; +int g_video_act_margin_right = BASE_MARGIN_RIGHT; +int g_video_act_margin_top = BASE_MARGIN_TOP; +int g_video_act_margin_bottom = BASE_MARGIN_BOTTOM; +int g_video_act_width = X_A2_WINDOW_WIDTH; +int g_video_act_height = X_A2_WINDOW_HEIGHT; +int g_mainwin_width = X_A2_WINDOW_WIDTH; +int g_mainwin_height = X_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2; +int g_mainwin_xpos = 100; +int g_mainwin_ypos = 300; +int g_video_no_scale_window = 0; + +word32 g_palette_change_cnt[2][16]; +int g_border_sides_refresh_needed = 1; +int g_border_special_refresh_needed = 1; +int g_border_line24_refresh_needed = 1; +int g_status_refresh_needed = 1; + +int g_vbl_border_color = 0; +int g_border_last_vbl_changes = 0; +int g_border_reparse = 0; + +int g_use_dhr140 = 0; +int g_use_bw_hires = 0; + +int g_vid_update_last_line = 0; +int g_video_save_all_stat_pos = 0; + +int g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR); + +word32 g_palette_8to1624[2][256]; +word32 g_a2palette_1624[16]; + +word32 g_saved_line_palettes[2][200][8]; + +word32 g_cycs_in_refresh_line = 0; +word32 g_cycs_in_refresh_ximage = 0; +word32 g_cycs_in_run_16ms = 0; + +int g_num_lines_superhires = 0; +int g_num_lines_superhires640 = 0; +int g_num_lines_prev_superhires = 0; +int g_num_lines_prev_superhires640 = 0; + +int g_screen_redraw_skip_count = 0; +int g_screen_redraw_skip_amt = -1; + +word32 g_alpha_mask = 0; +word32 g_red_mask = 0xff; +word32 g_green_mask = 0xff; +word32 g_blue_mask = 0xff; +int g_red_left_shift = 16; +int g_green_left_shift = 8; +int g_blue_left_shift = 0; +int g_red_right_shift = 0; +int g_green_right_shift = 0; +int g_blue_right_shift = 0; + +int g_status_enable = 1; +int g_status_enable_previous = 1; +char g_status_buf[MAX_STATUS_LINES][STATUS_LINE_LENGTH + 1]; +char *g_status_ptrs[MAX_STATUS_LINES] = { 0 }; +word16 g_pixels_widened[128]; + +int g_video_scale_algorithm = 0; + +STRUCT(Video_all_stat) { + word32 lines_since_vbl; + word32 cur_all_stat; +}; + +#define MAX_VIDEO_ALL_STAT ((200*42) + 40) +int g_video_all_stat_pos = 0; +Video_all_stat g_video_all_stat[MAX_VIDEO_ALL_STAT]; + +STRUCT(Video_filt_stat) { + word32 line_bytes; + word32 filt_stat; +}; + +#define MAX_VIDEO_FILT_STAT 10000 +int g_video_filt_stat_pos = 0; +Video_filt_stat g_video_filt_stat[MAX_VIDEO_FILT_STAT]; + +int g_video_stat_old_pos = 0; +Video_filt_stat g_video_filt_stat_old[MAX_VIDEO_FILT_STAT]; + + +word16 g_dhires_convert[4096]; /* look up { next4, this4, prev 4 } */ + +const byte g_dhires_colors_16[] = { // Convert dhires to lores color + 0x00, /* 0x0 black */ + 0x02, /* 0x1 dark blue */ + 0x04, /* 0x2 dark green */ + 0x06, /* 0x3 medium blue */ + 0x08, /* 0x4 brown */ + 0x0a, /* 0x5 light gray */ + 0x0c, /* 0x6 green */ + 0x0e, /* 0x7 aquamarine */ + 0x01, /* 0x8 deep red */ + 0x03, /* 0x9 purple */ + 0x05, /* 0xa dark gray */ + 0x07, /* 0xb light blue */ + 0x09, /* 0xc orange */ + 0x0b, /* 0xd pink */ + 0x0d, /* 0xe yellow */ + 0x0f /* 0xf white */ +}; + +const int g_lores_colors[] = { // From IIgs Technote #63 + /* rgb */ + 0x000, /* 0x0 black */ + 0xd03, /* 0x1 deep red */ + 0x009, /* 0x2 dark blue */ + 0xd2d, /* 0x3 purple */ + 0x072, /* 0x4 dark green */ + 0x555, /* 0x5 dark gray */ + 0x22f, /* 0x6 medium blue */ + 0x6af, /* 0x7 light blue */ + 0x850, /* 0x8 brown */ + 0xf60, /* 0x9 orange */ + 0xaaa, /* 0xa light gray */ + 0xf98, /* 0xb pink */ + 0x1d0, /* 0xc green */ + 0xff0, /* 0xd yellow */ + 0x4f9, /* 0xe aquamarine */ + 0xfff /* 0xf white */ +}; + +const byte g_hires_lookup[64] = { +// Indexed by { next_bit, this_bit, prev_bit, hibit, odd_byte, odd_col }. +// Return lores colors: 0, 3, 6, 9, 0xc, 0xf + 0x00, // 00,0000 // black: this and next are 0 + 0x00, // 00,0001 + 0x00, // 00,0010 + 0x00, // 00,0011 + 0x00, // 00,0100 + 0x00, // 00,0101 + 0x00, // 00,0110 + 0x00, // 00,0111 + 0x00, // 00,1000 + 0x00, // 00,1001 + 0x00, // 00,1010 + 0x00, // 00,1011 + 0x00, // 00,1100 + 0x00, // 00,1101 + 0x00, // 00,1110 + 0x00, // 00,1111 + + 0x03, // 01,0000 // purple + 0x03, // 01,0001 // purple + 0x0c, // 01,0010 // green (odd column) + 0x0c, // 01,0011 // green + 0x06, // 01,0100 // blue + 0x06, // 01,0101 // blue + 0x09, // 01,0110 // orange + 0x09, // 01,0111 // orange + 0x0f, // 01,1000 // white: this and prev are 1 + 0x0f, // 01,1001 + 0x0f, // 01,1010 + 0x0f, // 01,1011 + 0x0f, // 01,1100 + 0x0f, // 01,1101 + 0x0f, // 01,1110 + 0x0f, // 01,1111 + + 0x00, // 10,0000 // black + 0x00, // 10,0001 // black + 0x00, // 10,0010 // black + 0x00, // 10,0011 // black + 0x00, // 10,0100 // black + 0x00, // 10,0101 // black + 0x00, // 10,0110 // black + 0x00, // 10,0111 // black + 0x0c, // 10,1000 // green + 0x0c, // 10,1001 // green + 0x03, // 10,1010 // purple + 0x03, // 10,1011 // purple + 0x09, // 10,1100 // orange + 0x09, // 10,1101 // orange + 0x06, // 10,1110 // blue + 0x06, // 10,1111 // blue + + 0x0f, // 11,0000 // white + 0x0f, // 11,0001 + 0x0f, // 11,0010 + 0x0f, // 11,0011 + 0x0f, // 11,0100 + 0x0f, // 11,0101 + 0x0f, // 11,0110 + 0x0f, // 11,0111 + 0x0f, // 11,1000 + 0x0f, // 11,1001 + 0x0f, // 11,1010 + 0x0f, // 11,1011 + 0x0f, // 11,1100 + 0x0f, // 11,1101 + 0x0f, // 11,1110 + 0x0f // 11,1111 +}; + +const int g_screen_index[] = { + 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, + 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8, + 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0, + 0x078, 0x0f8, 0x178, 0x1f8, 0x278, 0x2f8, 0x378, 0x3f8 + // Last row is for float_bus() during VBL +}; + +byte g_font_array[256][8] = { +#include "kegsfont.h" +}; + +void +video_set_red_mask(word32 red_mask) +{ + video_set_mask_and_shift(red_mask, &g_red_mask, &g_red_left_shift, + &g_red_right_shift); +} + +void +video_set_green_mask(word32 green_mask) +{ + video_set_mask_and_shift(green_mask, &g_green_mask, &g_green_left_shift, + &g_green_right_shift); +} + +void +video_set_blue_mask(word32 blue_mask) +{ + video_set_mask_and_shift(blue_mask, &g_blue_mask, &g_blue_left_shift, + &g_blue_right_shift); +} + +void +video_set_alpha_mask(word32 alpha_mask) +{ + g_alpha_mask = alpha_mask; + printf("Set g_alpha_mask=%08x\n", alpha_mask); +} + +void +video_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, + int *shift_right_ptr) +{ + int shift; + int i; + + /* Shift until we find first set bit in mask, then remember mask,shift*/ + + shift = 0; + for(i = 0; i < 32; i++) { + if(x_mask & 1) { + /* we're done! */ + break; + } + x_mask = x_mask >> 1; + shift++; + } + *mask_ptr = x_mask; + *shift_left_ptr = shift; + /* Now, calculate shift_right_ptr */ + shift = 0; + x_mask |= 1; // make sure at least one bit is set + for(i = 0; i < 32; i++) { + if(x_mask >= 0x80) { + break; + } + shift++; + x_mask = x_mask << 1; + } + + *shift_right_ptr = shift; +} + +void +video_set_palette() +{ + int i; + + for(i = 0; i < 16; i++) { + video_update_color_raw(0, i, g_lores_colors[i]); + g_a2palette_1624[i] = g_palette_8to1624[0][i]; + } +} + +void +video_set_redraw_skip_amt(int amt) +{ + if(g_screen_redraw_skip_amt < amt) { + g_screen_redraw_skip_amt = amt; + printf("Set g_screen_redraw_skip_amt = %d\n", amt); + } +} + +Kimage * +video_get_kimage(int win_id) +{ + if(win_id == 0) { + return &g_mainwin_kimage; + } + if(win_id == 1) { + return &g_debugwin_kimage; + } + printf("win_id: %d not supported\n", win_id); + exit(1); +} + +char * +video_get_status_ptr(int line) +{ + if(line < MAX_STATUS_LINES) { + return g_status_ptrs[line]; + } + return 0; +} + +#if 0 +int +video_get_x_refresh_needed(Kimage *kimage_ptr) +{ + int ret; + + ret = kimage_ptr->x_refresh_needed; + kimage_ptr->x_refresh_needed = 0; + + return ret; +} +#endif + +void +video_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh) +{ + kimage_ptr->x_refresh_needed = do_refresh; +} + +int +video_get_active(Kimage *kimage_ptr) +{ + return kimage_ptr->active; +} + +void +video_set_active(Kimage *kimage_ptr, int active) +{ + kimage_ptr->active = active; + if(kimage_ptr != &g_mainwin_kimage) { + adb_nonmain_check(); + } +} + +void +video_init(int mdepth, int screen_width, int screen_height, int no_scale_window) +{ + word32 col[4]; + word32 val0, val1, val2, val3, next_col, next2_col; + word32 val, cur_col; + int i, j; + +// Initialize video system (called one-time only) + + g_video_no_scale_window = no_scale_window; + + for(i = 0; i < 200; i++) { + g_a2_line_left_edge[i] = 0; + g_a2_line_right_edge[i] = 0; + } + for(i = 0; i < 200; i++) { + g_a2_filt_stat[i] = -1; + for(j = 0; j < 8; j++) { + g_saved_line_palettes[0][i][j] = (word32)-1; + g_saved_line_palettes[1][i][j] = (word32)-1; + } + } + for(i = 0; i < 262; i++) { + g_cur_border_colors[i] = -1; + } + for(i = 0; i < 128; i++) { + val0 = i; + val1 = 0; + for(j = 0; j < 7; j++) { + val1 = val1 << 2; + if(val0 & 0x40) { + val1 |= 3; + } + val0 = val0 << 1; + } + g_pixels_widened[i] = val1; + } + + vid_printf("Zeroing out video memory, mdepth:%d\n", mdepth); + + for(i = 0; i < SLOW_MEM_CH_SIZE; i++) { + g_slow_mem_changed[i] = (word32)-1; + g_slow_mem_ch2[i] = 0; + } + + // create g_dhires_convert[] array + // TODO: Look at patent #4786893 for details on VGC and dhr + for(i = 0; i < 4096; i++) { + /* Convert index bits 11:0 where 3:0 is the previous color */ + /* and 7:4 is the current color to translate */ + /* Bit 4 will be the first pixel displayed on the screen */ + for(j = 0; j < 4; j++) { + cur_col = (i >> (1 + j)) & 0xf; + next_col = (i >> (2 + j)) & 0xf; + next2_col = (i >> (3 + j)) & 0xf; + cur_col = (((cur_col << 4) + cur_col) >> (3 - j)) & 0xf; + + if((cur_col == 0xf) || (next_col == 0xf) || + (next2_col == 0xf)) { + cur_col = 0xf; + col[j] = cur_col; + } else if((cur_col == 0) || (next_col == 0) || + (next2_col == 0)) { + cur_col = 0; + col[j] = cur_col; + } else { + col[j] = cur_col; + } + } + if(g_use_dhr140) { + for(j = 0; j < 4; j++) { + col[j] = (i >> 4) & 0xf; + } + } + val0 = g_dhires_colors_16[col[0] & 0xf]; + val1 = g_dhires_colors_16[col[1] & 0xf]; + val2 = g_dhires_colors_16[col[2] & 0xf]; + val3 = g_dhires_colors_16[col[3] & 0xf]; + val = val0 | (val1 << 4) | (val2 << 8) | (val3 << 12); + g_dhires_convert[i] = val; + if((i == 0x7bc) || (i == 0xfff)) { + //printf("g_dhires_convert[%03x] = %04x\n", i, val); + } + } + + video_init_kimage(&g_mainwin_kimage, X_A2_WINDOW_WIDTH, + X_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2, + screen_width, screen_height); + + video_init_kimage(&g_debugwin_kimage, 80*8 + 8 + 8, 25*16 + 8 + 8, + screen_width, screen_height); + + change_display_mode(g_cur_dfcyc); + video_reset(); + g_vid_update_last_line = 0; + g_video_all_stat_pos = 1; + g_video_all_stat[0].cur_all_stat = 0; + g_video_all_stat[0].lines_since_vbl = 0; + g_video_save_all_stat_pos = 0; + g_video_filt_stat_pos = 0; + video_update_status_enable(&g_mainwin_kimage); + video_update_through_line(262); + + printf("g_mainwin_kimage created and init'ed\n"); + fflush(stdout); +} + +int +video_clamp(int value, int min, int max) +{ + // Ensure value is >= min and <= max. If max <= min, return min + if(value > max) { + value = max; + } + if(value <= min) { + value = min; + } + return value; +} + +void +video_init_kimage(Kimage *kimage_ptr, int width, int height, + int screen_width, int screen_height) +{ + int x_width, x_height, a2_height, x_xpos, x_ypos; + int i; + + if(screen_width < width) { + screen_width = width; + } + if(screen_height < height) { + screen_width = height; + } + x_width = width; + x_height = height; + a2_height = height; + x_xpos = 100; + x_ypos = 300; + if(kimage_ptr == &g_mainwin_kimage) { + x_width = g_mainwin_width; + x_height = g_mainwin_height; + x_xpos = g_mainwin_xpos; + x_ypos = g_mainwin_ypos; + + // Handle status lines now + if(!g_status_enable) { + a2_height = g_video_act_margin_top + A2_WINDOW_HEIGHT + + g_video_act_margin_bottom; + } + } + + x_width = video_clamp(x_width, width, screen_width); + x_height = video_clamp(x_height, height, screen_height); + x_xpos = video_clamp(x_xpos, 0, screen_width - 640); + x_ypos = video_clamp(x_ypos, 0, screen_height - 420); + + kimage_ptr->wptr = (word32 *)calloc(1, width * (height + 2) * 4); + // Scaling routines read from line+1, expect it to be 0 + kimage_ptr->a2_width_full = width; + kimage_ptr->a2_height_full = height; + kimage_ptr->a2_width = width; + kimage_ptr->a2_height = a2_height; + kimage_ptr->x_width = x_width; + kimage_ptr->x_height = x_height; + kimage_ptr->x_refresh_needed = 1; + kimage_ptr->x_max_width = screen_width; + kimage_ptr->x_max_height = screen_height; + kimage_ptr->x_xpos = x_xpos; + kimage_ptr->x_ypos = x_ypos; + kimage_ptr->active = 0; + kimage_ptr->vbl_of_last_resize = 0; + kimage_ptr->c025_val = 0; + //printf("Created window, width:%d x_width:%d height:%d x_height:%d\n", + // width, x_width, height, x_height); + + kimage_ptr->scale_width_to_a2 = 0x10000; + kimage_ptr->scale_width_a2_to_x = 0x10000; + kimage_ptr->scale_height_to_a2 = 0x10000; + kimage_ptr->scale_height_a2_to_x = 0x10000; + kimage_ptr->num_change_rects = 0; + for(i = 0; i <= MAX_SCALE_SIZE; i++) { + kimage_ptr->scale_width[i] = i; + kimage_ptr->scale_height[i] = i; + } + + video_update_scale(kimage_ptr, x_width, x_height, 1); +} + +void +show_a2_line_stuff() +{ + int num, num_filt; + int i; + + for(i = 0; i < 200; i++) { + printf("line: %d: stat: %07x, " + "left_edge:%d, right_edge:%d\n", + i, g_a2_filt_stat[i], + g_a2_line_left_edge[i], + g_a2_line_right_edge[i]); + } + + num = g_video_all_stat_pos; + num_filt = g_video_stat_old_pos; + printf("cur_a2_stat:%04x, all_stat_pos:%d, num_filt:%d\n", + g_cur_a2_stat, num, num_filt); + for(i = 0; i < num; i++) { + printf("all_stat[%3d]=%08x stat:%08x\n", i, + g_video_all_stat[i].lines_since_vbl, + g_video_all_stat[i].cur_all_stat); + } + for(i = 0; i < num_filt; i++) { + printf("filt[%3d]=%08x filt_stat:%08x\n", i, + g_video_filt_stat_old[i].line_bytes, + g_video_filt_stat_old[i].filt_stat); + } +} + +int g_flash_count = 0; + +void +video_reset() +{ + int stat; + int i; + + voc_reset(); + + stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR); + if(g_use_bw_hires) { + stat |= ALL_STAT_COLOR_C021; + } + if(g_config_control_panel) { + /* Don't update cur_a2_stat when in configuration panel */ + //g_save_cur_a2_stat = stat; + } else { + g_cur_a2_stat = stat; + } + + for(i = 0; i < 16; i++) { + g_palette_change_cnt[0][i] = 0; + g_palette_change_cnt[1][i] = 0; + } +} + +word32 g_cycs_in_check_input = 0; + +void +video_update() +{ + int did_video; + + if(g_fatal_log > 0) { + // NOT IMPLEMENTED YET + //adb_all_keys_up(); + clear_fatal_logs(); + } + if(g_status_enable != g_status_enable_previous) { + g_status_enable_previous = g_status_enable; + video_update_status_enable(&g_mainwin_kimage); + } + + debugger_redraw_screen(&g_debugwin_kimage); + if(g_config_control_panel) { + return; // Nothing else to do + } + + g_screen_redraw_skip_count--; + did_video = 0; + if(g_screen_redraw_skip_count < 0) { + did_video = 1; + video_copy_changed2(); + video_update_event_line(262); + update_border_info(); + g_screen_redraw_skip_count = g_screen_redraw_skip_amt; + } + + /* update flash */ + g_flash_count++; + if(g_flash_count >= 16) { + g_flash_count = 0; + g_cur_a2_stat ^= ALL_STAT_FLASH_STATE; + change_display_mode(g_cur_dfcyc); + } + + if(did_video) { + g_vid_update_last_line = 0; + g_video_all_stat_pos = 1; + g_video_all_stat[0].cur_all_stat = g_cur_a2_stat; + g_video_all_stat[0].lines_since_vbl = 0; + g_video_save_all_stat_pos = 0; + g_video_filt_stat_pos = 0; + } +} + +word32 +video_all_stat_to_filt_stat(int line, word32 new_all_stat) +{ + word32 filt_stat, merge_mask, mix_t_gr; + + filt_stat = new_all_stat & ALL_STAT_TEXT; + merge_mask = 0; + if((new_all_stat & ALL_STAT_ST80) == 0) { + merge_mask = ALL_STAT_PAGE2; + } + mix_t_gr = new_all_stat & ALL_STAT_MIX_T_GR; + if(new_all_stat & ALL_STAT_SUPER_HIRES) { + filt_stat = ALL_STAT_SUPER_HIRES; + merge_mask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN; + } else if(line >= 192) { + filt_stat = ALL_STAT_BORDER; + } else if(filt_stat || (line >= 160 && mix_t_gr)) { + // text mode + filt_stat |= ALL_STAT_TEXT; + merge_mask |= ALL_STAT_ALTCHARSET | ALL_STAT_BG_COLOR | + ALL_STAT_TEXT_COLOR | ALL_STAT_VID80; + if((new_all_stat & ALL_STAT_ALTCHARSET) == 0) { + merge_mask |= ALL_STAT_FLASH_STATE; + } + } else { + // GR or Hires + merge_mask |= ALL_STAT_ANNUNC3 | ALL_STAT_HIRES; + if((new_all_stat & ALL_STAT_ANNUNC3) == 0) { + // AN3 must be 0 to enable dbl-lores or dbl-hires + merge_mask |= ALL_STAT_VID80; + } + if(new_all_stat & ALL_STAT_HIRES) { + merge_mask |= ALL_STAT_COLOR_C021 | + ALL_STAT_DIS_COLOR_DHIRES; + } + } + filt_stat = filt_stat | (new_all_stat & merge_mask); + return filt_stat; +} + +void +change_display_mode(dword64 dfcyc) +{ + word32 lines_since_vbl; + + lines_since_vbl = get_lines_since_vbl(dfcyc); + video_add_new_all_stat(dfcyc, lines_since_vbl); + +} + +void +video_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl) +{ + word32 my_start, first_start, prev_lines_since_vbl; + int pos, prev; + + pos = g_video_all_stat_pos; + my_start = lines_since_vbl & 0x1ff00; + first_start = my_start + 24; + if(lines_since_vbl >= (200 << 8)) { + return; // In VBL, don't log this + } + if(pos && (lines_since_vbl < first_start)) { + prev = pos - 1; + prev_lines_since_vbl = g_video_all_stat[prev].lines_since_vbl; + // If the previous toggle has the same line, and it is before + // offset 24, then ignore it and overwrite it + if((my_start <= prev_lines_since_vbl) && + (prev_lines_since_vbl < first_start)) { + // needless toggling during HBL, just toss earlier + pos = prev; + } + } + g_video_all_stat[pos].lines_since_vbl = lines_since_vbl; + g_video_all_stat[pos].cur_all_stat = g_cur_a2_stat; + if(!g_halt_sim || g_config_control_panel) { + dbg_log_info(dfcyc, g_cur_a2_stat, lines_since_vbl, + (pos << 16) | 0x102); + } + pos++; + if(pos >= MAX_VIDEO_ALL_STAT) { + pos--; + } + g_video_all_stat_pos = pos; +} + +#define MAX_BORDER_CHANGES 16384 + +STRUCT(Border_changes) { + word32 usec; + int val; +}; + +Border_changes g_border_changes[MAX_BORDER_CHANGES]; +int g_num_border_changes = 0; + +void +change_border_color(dword64 dfcyc, int val) +{ + int pos; + + pos = g_num_border_changes; + g_border_changes[pos].usec = (word32)((dfcyc - g_last_vbl_dfcyc) >> 16); + g_border_changes[pos].val = val; + + pos++; + g_num_border_changes = pos; + + if(pos >= MAX_BORDER_CHANGES) { + halt_printf("num border changes: %d\n", pos); + g_num_border_changes = 0; + } +} + +void +update_border_info() +{ + dword64 drecip_usec, dline; + word32 usec; + int offset, new_line_offset, last_line_offset, new_line, new_val; + int limit, color_now; + int i; + + /* to get this routine to redraw the border, change */ + /* g_vbl_border_color, set g_border_last_vbl_changes = 1 */ + /* and change the cur_border_colors[] array */ + + color_now = g_vbl_border_color; + + drecip_usec = (65536LL * 65536LL) / 65; + limit = g_num_border_changes; + if(g_border_last_vbl_changes || limit || g_border_reparse) { + /* add a dummy entry */ + g_border_changes[limit].usec = CYCLES_IN_16MS_RAW + 21; + g_border_changes[limit].val = (g_c034_val & 0xf); + limit++; + } + last_line_offset = (((word32)-1L) << 8) + 44; + for(i = 0; i < limit; i++) { + usec = g_border_changes[i].usec; + dline = usec * drecip_usec; + new_line = dline >> 32; + offset = ((dword64)(word32)dline * 65ULL) >> 32; + + /* here comes the tricky part */ + /* offset is from 0 to 65, where 0-3 is the right border of */ + /* the previous line, 4-20 is horiz blanking, 21-24 is the */ + /* left border and 25-64 is the main window */ + /* Convert this to a new notation which is 0-3 is the left */ + /* border, 4-43 is the main window, and 44-47 is the right */ + /* basically, add -21 to offset, and wrap < 0 to previous ln */ + /* note this makes line -1 offset 44-47 the left hand border */ + /* for true line 261 on the screen */ + offset -= 21; + if(offset < 0) { + new_line--; + offset += 64; + } + new_val = g_border_changes[i].val; + new_line_offset = (new_line << 8) + offset; + + if((new_line_offset < -256) || + (new_line_offset > (262*256 + 0x80))) { + printf("new_line_offset: %05x\n", new_line_offset); + new_line_offset = last_line_offset; + } + while(last_line_offset < new_line_offset) { + /* see if this will finish it */ + if((last_line_offset & -256)==(new_line_offset & -256)){ + update_border_line(last_line_offset, + new_line_offset, color_now); + last_line_offset = new_line_offset; + } else { + update_border_line(last_line_offset, + (last_line_offset & -256) + 65, + color_now); + last_line_offset =(last_line_offset & -256)+256; + } + } + + color_now = new_val; + } + +#if 0 + if(g_num_border_changes) { + printf("Border changes: %d\n", g_num_border_changes); + } +#endif + + if(limit > 1) { + g_border_last_vbl_changes = 1; + } else { + g_border_last_vbl_changes = 0; + } + + g_num_border_changes = 0; + g_border_reparse = 0; + g_vbl_border_color = (g_c034_val & 0xf); +} + +void +update_border_line(int st_line_offset, int end_line_offset, int color) +{ + word32 filt_stat; + int st_offset, end_offset, left, right, width, line; + + line = st_line_offset >> 8; + if(line != (end_line_offset >> 8)) { + halt_printf("ubl, %04x %04x %02x!\n", st_line_offset, + end_line_offset, color); + } + if(line < -1 || line >= 262) { + halt_printf("ubl-b, mod line is %d\n", line); + line = 0; + } + if(line < 0 || line >= 262) { + line = 0; + } + + st_offset = st_line_offset & 0xff; + end_offset = end_line_offset & 0xff; + + if((st_offset == 0) && (end_offset >= 0x41) && !g_border_reparse) { + /* might be the same as last time, save some work */ + if(g_cur_border_colors[line] == color) { + return; + } + g_cur_border_colors[line] = color; + } else { + g_cur_border_colors[line] = -1; + } + + /* 0-3: left border, 4-43: main window, 44-47: right border */ + /* 48-65: horiz blanking */ + /* first, do the sides from line 0 to line 199 */ + if((line < 200) || (line >= 262)) { + if(line >= 262) { + line = 0; + } + if(st_offset < 4) { + /* left side */ + left = st_offset; + right = MY_MIN(4, end_offset); + video_border_pixel_write(&g_mainwin_kimage, + g_video_act_margin_top + 2*line, 2, color, + (left * BORDER_WIDTH)/4, + (right * BORDER_WIDTH) / 4); + + g_border_sides_refresh_needed = 1; + } + if((st_offset < 48) && (end_offset >= 44)) { + /* right side */ + filt_stat = g_a2_filt_stat[line]; + width = BORDER_WIDTH; + if((filt_stat & ALL_STAT_SUPER_HIRES) == 0) { + width += 80; + } + left = MY_MAX(0, st_offset - 44); + right = MY_MIN(4, end_offset - 44); + video_border_pixel_write(&g_mainwin_kimage, + g_video_act_margin_top + 2*line, 2, color, + X_A2_WINDOW_WIDTH - width + + (left * width/4), + X_A2_WINDOW_WIDTH - width + + (right * width/4)); + g_border_sides_refresh_needed = 1; + } + } + + if((line >= 192) && (line < 200)) { + filt_stat = g_a2_filt_stat[line]; + if((filt_stat & ALL_STAT_BORDER) && (st_offset < 44) && + (end_offset > 4)) { + left = MY_MAX(0, st_offset - 4); + right = MY_MIN(40, end_offset - 4); + video_border_pixel_write(&g_mainwin_kimage, + g_video_act_margin_top + 2*line, 2, color, + g_video_act_margin_left + (left * 640 / 40), + g_video_act_margin_left + (right * 640 / 40)); + g_border_line24_refresh_needed = 1; + } + } + + /* now do the bottom, lines 200 to 215 */ + if((line >= 200) && (line < (200 + BASE_MARGIN_BOTTOM/2)) ) { + line -= 200; + left = st_offset; + right = MY_MIN(48, end_offset); + video_border_pixel_write(&g_mainwin_kimage, + g_video_act_margin_top + 200*2 + 2*line, 2, + color, + (left * X_A2_WINDOW_WIDTH / 48), + (right * X_A2_WINDOW_WIDTH / 48)); + g_border_special_refresh_needed = 1; + } + + /* and top, lines 236 to 262 */ + if((line >= (262 - BASE_MARGIN_TOP/2)) && (line < 262)) { + line -= (262 - BASE_MARGIN_TOP/2); + left = st_offset; + right = MY_MIN(48, end_offset); + video_border_pixel_write(&g_mainwin_kimage, 2*line, 2, color, + (left * X_A2_WINDOW_WIDTH / 48), + (right * X_A2_WINDOW_WIDTH / 48)); + g_border_special_refresh_needed = 1; + } +} + +void +video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, + int color, int st_off, int end_off) +{ + word32 *wptr, *wptr0; + word32 pixel; + int width, width_full, offset; + int i, j; + + if(end_off <= st_off) { + return; + } + + width = end_off - st_off; + width_full = kimage_ptr->a2_width_full; + + if(width > width_full) { + halt_printf("border write but width %d > act %d\n", width, + width_full); + return; + } + if((starty + num_lines) > kimage_ptr->a2_height) { + halt_printf("border write line %d, > act %d\n", + starty+num_lines, kimage_ptr->a2_height); + return; + } + pixel = g_a2palette_1624[color & 0xf]; + + offset = starty * width_full; + wptr0 = kimage_ptr->wptr; + wptr0 += offset; + for(i = 0; i < num_lines; i++) { + wptr = wptr0 + st_off; + for(j = 0; j < width; j++) { + *wptr++ = pixel; + } + wptr0 += width_full; + } +} + +word32 +video_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse) +{ + word32 ch_mask, mask; + int shift; + + if(reparse) { + return (word32)-1; + } + shift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f; + mask = (1 << (40 >> SHIFT_PER_CHANGE)) - 1; + ch_mask = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] | + g_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT]; + if(filt_stat & ALL_STAT_VID80) { + mem_ptr += 0x10000; + ch_mask |= (g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT]); + ch_mask |= (g_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT]); + } + ch_mask = (ch_mask >> shift) & mask; + + return ch_mask; +} + +void +video_update_edges(int line, int left, int right, const char *str) +{ + g_a2_line_left_edge[line] = MY_MIN(left, g_a2_line_left_edge[line]); + g_a2_line_right_edge[line] = MY_MAX(right, g_a2_line_right_edge[line]); + + if((left < 0) || (right < 0) || (left > 640) || (right > 640)) { + printf("video_update_edges: %s: line %d: %d (left) >= %d " + "(right)\n", str, line, left, right); + } +} + +void +redraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr, + int pixels_per_line, word32 filt_stat) +{ + byte str_buf[81]; + byte *slow_mem_ptr; + word32 ch_mask, line_mask, mem_ptr, val0, val1, bg_pixel, fg_pixel; + int flash_state, y, bg_color, fg_color, start_line; + int x1, x2; + + // Redraws a single line, will be called over 8 lines to finish a byte. + + start_line = line_bytes >> 16; + bg_color = (filt_stat >> BIT_ALL_STAT_BG_COLOR) & 0xf; + fg_color = (filt_stat >> BIT_ALL_STAT_TEXT_COLOR) & 0xf; + bg_pixel = g_a2palette_1624[bg_color]; + fg_pixel = g_a2palette_1624[fg_color]; + + y = start_line >> 3; + line_mask = 1 << y; + mem_ptr = 0x400 + g_screen_index[y]; + if(filt_stat & ALL_STAT_PAGE2) { + mem_ptr += 0x400; + } + if((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) { + halt_printf("redraw_changed_text: mem_ptr: %08x, y:%d\n", + mem_ptr, y); + return; + } + + ch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse); + if(!ch_mask) { + return; + } + + g_a2_screen_buffer_changed |= line_mask; + x2 = 0; + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]); + flash_state = -0x40; + if(g_cur_a2_stat & ALL_STAT_FLASH_STATE) { + flash_state = 0x40; + } + + for(x1 = 0; x1 < 40; x1++) { + val0 = slow_mem_ptr[0x10000]; + val1 = *slow_mem_ptr++; + if(!(filt_stat & ALL_STAT_ALTCHARSET)) { + if((val0 >= 0x40) && (val0 < 0x80)) { + val0 += flash_state; + } + if((val1 >= 0x40) && (val1 < 0x80)) { + val1 += flash_state; + } + } + if(filt_stat & ALL_STAT_VID80) { + str_buf[x2++] = val0; // aux mem + } + str_buf[x2++] = val1; // main mem + } + str_buf[x2] = 0; // null terminate + + redraw_changed_string(&str_buf[0], line_bytes, ch_mask, in_wptr, + bg_pixel, fg_pixel, pixels_per_line, + (filt_stat & ALL_STAT_VID80)); +} + +void +redraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask, + word32 *in_wptr, word32 bg_pixel, + word32 fg_pixel, int pixels_per_line, int dbl) +{ + register word32 start_time, end_time; + word32 *wptr; + word32 val0, val1, val2, val3, pixel; + int left, right, st_line_mod8, offset, pos, shift, start_line; + int start_byte, end_byte; + int x1, j; + + left = 40; + right = 0; + + GET_ITIMER(start_time); + + start_line = line_bytes >> 16; + start_byte = line_bytes & 0x3f; + end_byte = (line_bytes >> 8) & 0x3f; + st_line_mod8 = start_line & 7; + + for(x1 = start_byte; x1 < end_byte; x1++) { + shift = x1 >> SHIFT_PER_CHANGE; + if(((ch_mask >> shift) & 1) == 0) { + continue; + } + + left = MY_MIN(x1, left); + right = MY_MAX(x1 + 1, right); + offset = (start_line * 2 * pixels_per_line) + x1*14; + pos = x1; + if(dbl) { + pos = pos * 2; + } + + wptr = in_wptr + offset; + + val0 = bptr[pos]; + if(dbl) { + pos++; + } + val1 = bptr[pos++]; + val2 = g_a2font_bits[val0][st_line_mod8]; + val3 = g_a2font_bits[val1][st_line_mod8]; + // val2, [6:0] is 80-column character bits, and + // [21:8] are the 40-column char bits (double-wide) + if(dbl) { + val2 = (val3 << 7) | (val2 & 0x7f); + } else { + val2 = val3 >> 8; // 40-column format + } + for(j = 0; j < 14; j++) { + pixel = bg_pixel; + if(val2 & 1) { // LSB is first pixel + pixel = fg_pixel; + } + wptr[pixels_per_line] = pixel; + *wptr++ = pixel; + val2 = val2 >> 1; + } + } + GET_ITIMER(end_time); + if(start_line < 200) { + video_update_edges(start_line, left * 14, right * 14, "text"); + } + + if((left >= right) || (left < 0) || (right < 0)) { + printf("str line %d, 40: left >= right: %d >= %d\n", + start_line, left, right); + printf(" line_bytes:%08x ch_mask:%08x\n", line_bytes, ch_mask); + } + + g_cycs_in_40col += (end_time - start_time); +} + +// gr with an3=0: +// 0=0 +// 1,0=3 (purple). 1,1=0 +// 2,0=c (green). 2,1=0 +// 3,0=f (white). 3,1=0 +// 4,0=0. 4,1=c (green) +// 5,0=3 (purple). 5,1=c (green) +// 6,0=c (green). 6,1=c (green) +// 7,0=f (white). 7,1=c (green) +// 8,0=0 (black). 7,1=3 (purple) +// 9,0=3 (purple). 9,1=3 (purple) +// a,0=c (green). a,1=3 (purple) +// b,0=f (white). b,1=3 (purple) +// c,0=0 (black). c,1=f (white) +// d,0=3 (purple). d,1=f (white) +// e,0=c (green). e,1=f (white) +// f,0=f (white). e,1=f (white) + +void +redraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr, + int pixels_per_line, word32 filt_stat) +{ + word32 *wptr; + byte *slow_mem_ptr; + word32 line_mask, mem_ptr, val0, val1, pixel0, pixel1, ch_mask; + int y, shift, left, right, st_line_mod8, start_line, offset; + int start_byte, end_byte; + int x1, i; + + start_line = line_bytes >> 16; + st_line_mod8 = start_line & 7; + + y = start_line >> 3; + line_mask = 1 << (y); + mem_ptr = 0x400 + g_screen_index[y]; + if(filt_stat & ALL_STAT_PAGE2) { + mem_ptr += 0x400; + } + if((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) { + halt_printf("redraw_changed_gr: mem_ptr: %08x, y:%d\n", + mem_ptr, y); + return; + } + + ch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse); + if(!ch_mask) { + return; + } + + g_a2_screen_buffer_changed |= line_mask; + + left = 40; + right = 0; + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]); + offset = (start_line * 2 * pixels_per_line); + start_byte = line_bytes & 0x3f; + end_byte = (line_bytes >> 8) & 0x3f; + for(x1 = start_byte; x1 < end_byte; x1++) { + shift = x1 >> SHIFT_PER_CHANGE; + if(((ch_mask >> shift) & 1) == 0) { + continue; + } + + left = MY_MIN(x1, left); + right = MY_MAX(x1 + 1, right); + + wptr = in_wptr + offset + x1*14; + + val0 = slow_mem_ptr[0x10000 + x1]; + val1 = slow_mem_ptr[x1]; + + if(st_line_mod8 >= 4) { + val0 = val0 >> 4; + val1 = val1 >> 4; + } + if(filt_stat & ALL_STAT_VID80) { + // aux pixel is { [2:0],[3] } + val0 = (val0 << 1) | ((val0 >> 3) & 1); + } else if((filt_stat & ALL_STAT_ANNUNC3) == 0) { + if(x1 & 1) { // odd cols + val0 = ((val1 >> 1) & 2) | ((val1 >> 3) & 1); + } else { + val0 = val1 & 3; // even cols + } + // map val0: 0->0, 1->3, 2->c, 3->f + val1 = 0; + if(val0 & 1) { + val1 |= 3; + } + if(val0 & 2) { + val1 |= 0xc; + } + val0 = val1; + } else { + val0 = val1; + } + pixel0 = g_a2palette_1624[val0 & 0xf]; + pixel1 = g_a2palette_1624[val1 & 0xf]; + for(i = 0; i < 7; i++) { + wptr[pixels_per_line] = pixel0; + wptr[pixels_per_line + 7] = pixel1; + wptr[0] = pixel0; + wptr[7] = pixel1; + wptr++; + } + } + + video_update_edges(start_line, left * 14, right * 14, "gr"); +} + +void +video_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte, + int end_byte, int pixels_per_line, word32 filt_stat) +{ + word32 val0, val1, val2, prev_bits, val1_hi, dbl_step, pixel, color; + word32 monochrome; + int shift; + int x2, i; + + monochrome = filt_stat & (ALL_STAT_COLOR_C021 | + ALL_STAT_DIS_COLOR_DHIRES); + + prev_bits = 0; + if(start_byte) { + prev_bits = (slow_mem_ptr[-1] >> 3) & 0xf; + if(!(filt_stat & ALL_STAT_VID80)) { + // prev_bits is 4 bits, widen to 8 for std HGR + prev_bits = g_pixels_widened[prev_bits] >> 4; + } + prev_bits = prev_bits & 0xf; + } + for(x2 = start_byte; x2 < end_byte; x2++) { + val0 = slow_mem_ptr[0x10000]; + val1 = *slow_mem_ptr++; + val2 = slow_mem_ptr[0x10000]; // next pixel, aux mem + if(x2 >= 39) { + val2 = 0; + } + val1_hi = ((val1 >> 5) & 4) | ((x2 & 1) << 1); + // Hi-order bit in bit 2, odd pixel is in bit 0 + + dbl_step = 3; + if(filt_stat & ALL_STAT_VID80) { + // aux+1[6:0], main[6:0], aux[6:0], prev[3:0] + val0 = (val2 << 18) | ((val1 & 0x7f) << 11) | + ((val0 & 0x7f) << 4); + if(!monochrome && (x2 & 1)) { // Get 6 bits from prev + val0 = (val0 << 2); + dbl_step = 1; + } + val0 = val0 | prev_bits; + } else if(monochrome) { + val0 = g_pixels_widened[val1 & 0x7f] << 4; + } else { // color, normal hgr + val2 = g_pixels_widened[*slow_mem_ptr & 0x7f]; + if(x2 >= 39) { + val2 = 0; + } + val0 = ((val1 & 0x7f) << 4) | prev_bits | (val2 << 11); + if((filt_stat & ALL_STAT_ANNUNC3) == 0) { + val1_hi = val1_hi & 3; + } + } +#if 0 + if(st_line < 8) { + printf("hgrl %d c:%d,d:%d, off:%03x val0:%02x 1:%02x\n", + st_line, monochrome, dbl, x1 + x2, val0, val1); + } +#endif + for(i = 0; i < 14; i++) { + color = 0; // black + if(monochrome) { + if(val0 & 0x10) { + color = 0xf; // white + } + val0 = val0 >> 1; + } else { // color + if(filt_stat & ALL_STAT_VID80) { + color = g_dhires_convert[val0 & 0xfff]; + shift = (x2 + x2 + i) & 3; + color = color >> (4 * shift); + if((i & 3) == dbl_step) { + val0 = val0 >> 4; + } + } else { + val2 = (val0 & 0x38) ^ val1_hi ^(i & 3); + color = g_hires_lookup[val2 & 0x7f]; + if(i & 1) { + val0 = val0 >> 1; + } + } + } + pixel = g_a2palette_1624[color & 0xf]; + wptr[pixels_per_line] = pixel; + *wptr++ = pixel; + } + if((filt_stat & ALL_STAT_VID80) && ((x2 & 1) == 0)) { + prev_bits = val0 & 0x3f; + } else { + prev_bits = val0 & 0xf; + } + } +} + +void +redraw_changed_hgr(word32 line_bytes, int reparse, + word32 *in_wptr, int pixels_per_line, word32 filt_stat) +{ + word32 *wptr; + byte *slow_mem_ptr; + word32 ch_mask, line_mask, mem_ptr; + int y, shift, st_line_mod8, start_line, offset, start_byte; + int end_byte; + int x1; + + start_line = line_bytes >> 16; + start_byte = line_bytes & 0x3f; + end_byte = (line_bytes >> 8) & 0x3f; // Usually '40' + + y = start_line >> 3; + st_line_mod8 = start_line & 7; + line_mask = 1 << y; + mem_ptr = 0x2000 + g_screen_index[y] + (st_line_mod8 * 0x400); + if(filt_stat & ALL_STAT_PAGE2) { + mem_ptr += 0x2000; + } + if((mem_ptr < 0x2000) || (mem_ptr >= 0x6000)) { + halt_printf("redraw_changed_hgr: mem_ptr: %08x, y:%d\n", + mem_ptr, y); + return; + } + + ch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse); + if(ch_mask == 0) { + return; + } + + // Hires depends on adjacent bits, so also reparse adjacent regions + // to handle redrawing of pixels on the boundaries + ch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1); + + g_a2_screen_buffer_changed |= line_mask; + + for(x1 = start_byte; x1 < end_byte; x1++) { + shift = x1 >> SHIFT_PER_CHANGE; + if(((ch_mask >> shift) & 1) == 0) { + continue; + } + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + offset = (start_line * 2 * pixels_per_line) + x1*14; + + wptr = in_wptr + offset; + video_hgr_line_segment(slow_mem_ptr, wptr, x1, end_byte, + pixels_per_line, filt_stat); + + video_update_edges(start_line, x1 * 14, end_byte * 14, "hgr"); + break; + } +} + +int +video_rebuild_super_hires_palette(int bank, word32 scan_info, int line, + int reparse) +{ + word32 *word_ptr; + byte *byte_ptr; + word32 ch_mask, mem_ptr, scan, old_scan, val0, val1; + int diffs, palette; + int j; + + palette = scan_info & 0xf; + + mem_ptr = (bank << 16) + 0x9e00 + (palette * 0x20); + ch_mask = video_get_ch_mask(mem_ptr, 0, 0); + + old_scan = g_superhires_scan_save[bank][line]; + scan = (scan_info & 0xfaf) + + (g_palette_change_cnt[bank][palette] << 12); + g_superhires_scan_save[bank][line] = scan; + +#if 0 + if(line == 1) { + word_ptr = (word32 *)&(g_slow_memory_ptr[0x19e00+palette*0x20]); + printf("y1vrshp, ch:%08x, s:%08x,os:%08x %d = %08x %08x %08x " + "%08x %08x %08x %08x %08x\n", + ch_mask, scan, old_scan, reparse, + word_ptr[0], word_ptr[1], word_ptr[2], word_ptr[3], + word_ptr[4], word_ptr[5], word_ptr[6], word_ptr[7]); + } +#endif + + diffs = reparse | ((scan ^ old_scan) & 0xf0f); + /* we must do full reparse if palette changed for this line */ + + if(!diffs && (ch_mask == 0) && (((scan ^ old_scan) & (~0xf0)) == 0)) { + /* nothing changed, get out fast */ + return 0; + } + + if(ch_mask) { + /* indicates the palette has changed, and other scan lines */ + /* using this palette need to do a full 32-byte compare to */ + /* decide if they need to update or not */ + g_palette_change_cnt[bank][palette]++; + } + + word_ptr = (word32 *)&(g_slow_memory_ptr[(bank << 16) + 0x9e00 + + palette*0x20]); + for(j = 0; j < 8; j++) { + if(word_ptr[j] != g_saved_line_palettes[bank][line][j]) { + diffs = 1; + break; + } + } + + if(diffs == 0) { + return 0; + } + + /* first, save this word_ptr into saved_line_palettes */ + byte_ptr = (byte *)word_ptr; + for(j = 0; j < 8; j++) { + g_saved_line_palettes[bank][line][j] = word_ptr[j]; + } + + byte_ptr = (byte *)word_ptr; + /* this palette has changed */ + for(j = 0; j < 16; j++) { + val0 = *byte_ptr++; + val1 = *byte_ptr++; + video_update_color_raw(bank, palette*16 + j, (val1<<8) + val0); + } + + return 1; +} + +word32 +redraw_changed_super_hires_oneline(int bank, word32 *in_wptr, + int pixels_per_line, int y, int scan, word32 ch_mask) +{ + word32 *palptr, *wptr; + byte *slow_mem_ptr; + word32 mem_ptr, val0, pal, pix0, pix1, pix2, pix3, save_pix; + int offset, shift_per, left, right, shift; + int x1, x2; + + mem_ptr = (bank << 16) + 0x2000 + (0xa0 * y); + + shift_per = (1 << SHIFT_PER_CHANGE); + pal = (scan & 0xf); + + save_pix = 0; + if(scan & 0x20) { // Fill mode + ch_mask = (word32)-1; + } + + palptr = &(g_palette_8to1624[bank][pal * 16]); + left = 160; + right = 0; + + for(x1 = 0; x1 < 0xa0; x1 += shift_per) { + shift = x1 >> SHIFT_PER_CHANGE; + if(((ch_mask >> shift) & 1) == 0) { + continue; + } + + left = MY_MIN(x1, left); + right = MY_MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + offset = x1*4; + + wptr = in_wptr + offset; + + for(x2 = 0; x2 < shift_per; x2++) { + val0 = *slow_mem_ptr++; + + if(scan & 0x80) { // 640 mode + pix0 = (val0 >> 6) & 3; + pix1 = (val0 >> 4) & 3; + pix2 = (val0 >> 2) & 3; + pix3 = val0 & 3; + pix0 = palptr[pix0 + 8]; + pix1 = palptr[pix1 + 12]; + pix2 = palptr[pix2 + 0]; + pix3 = palptr[pix3 + 4]; + } else { /* 320 mode */ + pix0 = (val0 >> 4); + pix2 = (val0 & 0xf); + if(scan & 0x20) { // Fill mode + if(!pix0) { // 0 = repeat last color + pix0 = save_pix; + } + if(!pix2) { + pix2 = pix0; + } + save_pix = pix2; + } + pix0 = palptr[pix0]; + pix1 = pix0; + pix2 = palptr[pix2]; + pix3 = pix2; + } + wptr[pixels_per_line] = pix0; + *wptr++ = pix0; + wptr[pixels_per_line] = pix1; + *wptr++ = pix1; + wptr[pixels_per_line] = pix2; + *wptr++ = pix2; + wptr[pixels_per_line] = pix3; + *wptr++ = pix3; + } + } + + return (left << 16) | (right & 0xffff); +} + +void +redraw_changed_super_hires_bank(int bank, int start_line, int reparse, + word32 *wptr, int pixels_per_line) +{ + dword64 dval, dval1; + word32 this_check, mask, tmp, scan, old_scan, mem_ptr; + int left, right, ret, shift; + + mem_ptr = (bank << 16) + 0x2000 + (160 * start_line); + dval1 = g_slow_mem_changed[(mem_ptr >> CHANGE_SHIFT) + 1] | + g_slow_mem_ch2[(mem_ptr >> CHANGE_SHIFT) + 1]; + dval = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] | + g_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT] | (dval1 << 32); + shift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f; + mask = (1 << (160 >> SHIFT_PER_CHANGE)) - 1; + this_check = (dval >> shift) & mask; + + scan = g_slow_memory_ptr[(bank << 16) + 0x9d00 + start_line]; + + old_scan = g_superhires_scan_save[bank][start_line]; + + ret = video_rebuild_super_hires_palette(bank, scan, start_line, + reparse); + if(ret || reparse || ((scan ^ old_scan) & 0xa0)) { + /* 0x80 == mode640, 0x20 = fill */ + this_check = (word32)-1; + } + + if(!this_check) { + return; // Nothing to do, get out + } + + if(scan & 0x80) { // 640 mode + g_num_lines_superhires640++; + } + + if((scan >> 5) & 1) { // fill mode--redraw whole line + this_check = (word32)-1; + } + + g_a2_screen_buffer_changed |= (1 << (start_line >> 3)); + tmp = redraw_changed_super_hires_oneline(bank, wptr, pixels_per_line, + start_line, scan, this_check); + left = tmp >> 16; + right = tmp & 0xffff; + + video_update_edges(start_line, left * 4, right * 4, "shr"); +} + +void +redraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr, + int pixels_per_line, word32 filt_stat) +{ + int bank, start_line; + + start_line = line_bytes >> 16; + wptr += start_line * 2 * pixels_per_line; + + if(filt_stat & ALL_STAT_VOC_INTERLACE) { + // Do 400 interlaced lines. Do aux first, then main mem + redraw_changed_super_hires_bank(1, start_line, reparse, wptr, + 0); + redraw_changed_super_hires_bank(0, start_line, reparse, + wptr + pixels_per_line, 0); + } else { + bank = 1; + if(filt_stat & ALL_STAT_VOC_MAIN) { + bank = 0; // VOC SHR in main memory + } + redraw_changed_super_hires_bank(bank, start_line, reparse, wptr, + pixels_per_line); + } +} + +void +video_copy_changed2() +{ + word32 *ch_ptr, *ch2_ptr; + int bank1_off; + int i; + + // Copy entries from g_slow_mem_changed[] to g_slow_mem_ch2[] and + // clear g_slow_mem_changed[] + ch_ptr = &g_slow_mem_changed[0]; + ch2_ptr = &g_slow_mem_ch2[0]; + bank1_off = 0x10000 >> CHANGE_SHIFT; + for(i = 4; i < 0xa0; i++) { // Pages 0x0400 through 0x9fff + ch2_ptr[i] = ch_ptr[i]; + ch2_ptr[i + bank1_off] = ch_ptr[i + bank1_off]; + ch_ptr[i] = 0; + ch_ptr[i + bank1_off] = 0; + } +} + +void +video_update_event_line(int line) +{ + int new_line; + + video_update_through_line(line); + + new_line = line + g_line_ref_amt; + if(new_line < 200) { + if(!g_config_control_panel && !g_halt_sim) { + add_event_vid_upd(new_line); + } + } else if(line >= 262) { + if(!g_config_control_panel && !g_halt_sim) { + add_event_vid_upd(0); /* add event for new screen */ + } + } +} + +void +video_force_reparse() +{ + word32 *wptr; + int height, width_full; + int i, j; + + g_video_stat_old_pos = 1; + g_video_filt_stat_old[0].filt_stat = (word32)-1; + height = g_video_act_margin_top + A2_WINDOW_HEIGHT + + g_video_act_margin_bottom; + height = MY_MIN(height, g_mainwin_kimage.a2_height); + width_full = g_mainwin_kimage.a2_width_full; + wptr = g_mainwin_kimage.wptr; + for(i = 0; i < height; i++) { + for(j = 0; j < width_full; j++) { + *wptr++ = 0; + } + } + g_border_reparse = 1; +} + +void +video_update_through_line(int line) +{ + register word32 start_time; + register word32 end_time; + word32 my_start_lines, my_end_lines, prev_all_stat, next_all_stat; + word32 prev_lines_since_vbl, next_lines_since_vbl; + int last_line, pos, last_pos, end, num; + int i; + +#if 0 + vid_printf("\nvideo_upd for line %d, lines: %06x\n", line, + get_lines_since_vbl(g_cur_dfcyc)); +#endif + + GET_ITIMER(start_time); + + last_line = MY_MIN(200, line+1); /* go through line, but not past 200 */ + + pos = g_video_save_all_stat_pos; + last_pos = g_video_all_stat_pos; + prev_all_stat = g_video_all_stat[pos].cur_all_stat; + prev_lines_since_vbl = g_video_all_stat[pos].lines_since_vbl; + g_video_all_stat[last_pos].cur_all_stat = g_cur_a2_stat; + g_video_all_stat[last_pos].lines_since_vbl = (line + 1) << 8; + next_all_stat = g_video_all_stat[pos+1].cur_all_stat; + next_lines_since_vbl = g_video_all_stat[pos+1].lines_since_vbl; + for(i = g_vid_update_last_line; i < last_line; i++) { + // We need to step through pos in g_video_all_stat[] and find + // the start/end pairs for each line + g_a2_line_left_edge[i] = 640; + g_a2_line_right_edge[i] = 0; + my_start_lines = (i << 8) + 25; + my_end_lines = (i << 8) + 65; + if(prev_lines_since_vbl > my_start_lines) { + printf("prev:%08x > %08x start at i:%d\n", + prev_lines_since_vbl, my_start_lines, i); + } + while(my_start_lines < my_end_lines) { + while(next_lines_since_vbl <= my_start_lines) { + // Step into next entry + prev_all_stat = next_all_stat; + prev_lines_since_vbl = next_lines_since_vbl; + pos++; + g_video_save_all_stat_pos = pos; + next_all_stat = + g_video_all_stat[pos+1].cur_all_stat; + next_lines_since_vbl = + g_video_all_stat[pos+1].lines_since_vbl; + if(pos >= last_pos) { + printf("FELL OFF %d %d!\n", pos, + last_pos); + pos--; + break; + } + } + end = 65; + if(next_lines_since_vbl < my_end_lines) { + end = (next_lines_since_vbl & 0xff); + if(end < 25) { + printf("i:%d next_lines_since_vbl:" + "%08x!\n", i, + next_lines_since_vbl); + end = 25; + } + } + video_do_partial_line(my_start_lines, end, + prev_all_stat); + my_start_lines = (i << 8) + end; + } + } + + + g_vid_update_last_line = last_line; + g_video_save_all_stat_pos = pos; + + /* deal with border and forming rects for xdriver.c to use */ + if(line >= 262) { + if(g_num_lines_prev_superhires != g_num_lines_superhires) { + /* switched in/out from superhires--refresh borders */ + g_border_sides_refresh_needed = 1; + } + + video_form_change_rects(); + g_num_lines_prev_superhires = g_num_lines_superhires; + g_num_lines_prev_superhires640 = g_num_lines_superhires640; + g_num_lines_superhires = 0; + g_num_lines_superhires640 = 0; + + num = g_video_filt_stat_pos; + g_video_stat_old_pos = num; + for(i = 0; i < num; i++) { + g_video_filt_stat_old[i] = g_video_filt_stat[i]; + } + g_video_filt_stat_pos = 0; + } + GET_ITIMER(end_time); + g_cycs_in_refresh_line += (end_time - start_time); +} + +extern word32 g_vbl_count; + +void +video_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat) +{ + word32 filt_stat, old_filt_stat, line_bytes, old_line_bytes; + int pos, old_pos, reparse, line; + + pos = g_video_filt_stat_pos; + old_pos = g_video_stat_old_pos; + filt_stat = video_all_stat_to_filt_stat(lines_since_vbl >> 8, + cur_all_stat); + line_bytes = ((lines_since_vbl & 0x1ff00) << 8) | + ((end - 25) << 8) | ((lines_since_vbl - 25) & 0x3f); + g_video_filt_stat[pos].line_bytes = line_bytes; + g_video_filt_stat[pos].filt_stat = filt_stat; + reparse = 1; + old_filt_stat = (word32)-1; + old_line_bytes = (word32)-1; + if(pos < old_pos) { + old_filt_stat = g_video_filt_stat_old[pos].filt_stat; + old_line_bytes = g_video_filt_stat_old[pos].line_bytes; + } + if((old_filt_stat == filt_stat) && (line_bytes == old_line_bytes)) { + reparse = 0; + } else if((old_filt_stat ^ filt_stat) & ALL_STAT_SUPER_HIRES) { + g_border_reparse = 1; + } + video_refresh_line(line_bytes, reparse, filt_stat); + line = lines_since_vbl >> 8; + if(line < 200) { + g_a2_filt_stat[line] = filt_stat; + } else { + printf("partial_line %08x %d %08x out of range!\n", + lines_since_vbl, end, cur_all_stat); + } + if((end <= 25) || (end < (int)(lines_since_vbl & 0xff))) { + printf("Bad lsv:%08x, end:%d, stat:%08x\n", lines_since_vbl, + end, filt_stat); + } + pos++; + if(pos >= MAX_VIDEO_FILT_STAT) { + pos--; + } + g_video_filt_stat_pos = pos; +} + +void +video_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat) +{ + word32 *wptr; + int pixels_per_line, offset, line; + + line = line_bytes >> 16; + if((word32)line >= 200) { + printf("video_refresh %08x %d %08x!\n", line_bytes, + must_reparse, filt_stat); + return; + } + wptr = g_mainwin_kimage.wptr; + pixels_per_line = g_mainwin_kimage.a2_width_full; + offset = (pixels_per_line * g_video_act_margin_top) + + g_video_act_margin_left; + wptr = wptr + offset; + + if(filt_stat & ALL_STAT_SUPER_HIRES) { + g_num_lines_superhires++; + redraw_changed_super_hires(line_bytes, must_reparse, wptr, + pixels_per_line, filt_stat); + } else if(filt_stat & ALL_STAT_BORDER) { + if(line < 192) { + halt_printf("Border line not 192: %d\n", line); + } + g_a2_line_left_edge[line] = 0; + g_a2_line_right_edge[line] = 560; + if(g_border_line24_refresh_needed) { + g_border_line24_refresh_needed = 0; + g_a2_screen_buffer_changed |= (1 << 24); + } + } else if(filt_stat & ALL_STAT_TEXT) { + redraw_changed_text(line_bytes, must_reparse, wptr, + pixels_per_line, filt_stat); + } else if(filt_stat & ALL_STAT_HIRES) { + redraw_changed_hgr(line_bytes, must_reparse, wptr, + pixels_per_line, filt_stat); + } else { + redraw_changed_gr(line_bytes, must_reparse, wptr, + pixels_per_line, filt_stat); + } +} + +void +prepare_a2_font() +{ + word32 val0, val1, val2; + int i, j, k; + + // Prepare g_a2font_bits[char][line] where each entry indicates the + // set pixels in this line of the character. Bits 6:0 are an + // 80-column character, and bits 21:8 are the 14 expanded bits of a + // 40-columns character, both with the first visible bit at the + // rightmost bit address. But g_font_array[] is in big-endian bit + // order, which is less useful + for(i = 0; i < 256; i++) { + for(j = 0; j < 8; j++) { + val0 = g_font_array[i][j] >> 1; + val1 = 0; // 80-column bits + val2 = 0; // 40-column bits (doubled) + for(k = 0; k < 7; k++) { + val1 = val1 << 1; + val2 = val2 << 2; + if(val0 & 1) { + val1 |= 1; + val2 |= 3; + } + val0 = val0 >> 1; + } + g_a2font_bits[i][j] = (val2 << 8) | val1; + } + } +} + +void +prepare_a2_romx_font(byte *font_ptr) +{ + word32 val0, val1, val2; + int i, j, k; + + // ROMX file + for(i = 0; i < 256; i++) { + for(j = 0; j < 8; j++) { + val0 = font_ptr[i*8 + j]; + val1 = 0; // 80-column bits + val2 = 0; // 40-column bits (doubled) + for(k = 0; k < 7; k++) { + val1 = val1 << 1; + val2 = val2 << 2; + if((val0 & 0x40) == 0) { + val1 |= 1; + val2 |= 3; + } + val0 = val0 << 1; + } + g_a2font_bits[i][j] = (val2 << 8) | val1; + } + } +} + +void +video_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height) +{ + int pos; + + pos = kimage_ptr->num_change_rects++; + if(pos >= MAX_CHANGE_RECTS) { + return; // This will be handled later + } + kimage_ptr->change_rect[pos].x = x; + kimage_ptr->change_rect[pos].y = y; + kimage_ptr->change_rect[pos].width = width; + kimage_ptr->change_rect[pos].height = height; + + g_video_pixel_dcount += (width * height); +#if 0 + printf("Add rect %d, x:%d y:%d, w:%d h:%d\n", pos, x, y, width, height); +#endif +} + +void +video_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix) +{ + int srcy; + + if((left_pix >= right_pix) || (left_pix < 0) || (right_pix <= 0)) { + halt_printf("video_push_lines: lines %d to %d, pix %d to %d\n", + start_line, end_line, left_pix, right_pix); + printf("a2_screen_buf_ch:%08x, g_full_refr:%08x\n", + g_a2_screen_buffer_changed, g_full_refresh_needed); + return; + } + + srcy = 2*start_line; + + video_add_rect(&g_mainwin_kimage, g_video_act_margin_left + left_pix, + g_video_act_margin_top + srcy, + (right_pix - left_pix), 2*(end_line - start_line)); +} + +void +video_form_change_rects() +{ + Kimage *kimage_ptr; + register word32 start_time; + register word32 end_time; + dword64 save_pixel_dcount; + word32 mask; + int start, line, left_pix, right_pix, left, right, line_div8; + int x, y, width, height; + + kimage_ptr = &g_mainwin_kimage; + if(g_border_sides_refresh_needed) { + g_border_sides_refresh_needed = 0; + // Add left side border + video_add_rect(kimage_ptr, 0, g_video_act_margin_top, + BORDER_WIDTH, A2_WINDOW_HEIGHT); + + // Add right-side border. Resend x from 560 through + // X_A2_WINDOW_WIDTH + x = g_video_act_margin_left + 560; + width = X_A2_WINDOW_WIDTH - x; + video_add_rect(kimage_ptr, x, g_video_act_margin_top, width, + A2_WINDOW_HEIGHT); + } + if(g_border_special_refresh_needed) { + g_border_special_refresh_needed = 0; + + // Do top border + width = g_video_act_width; + height = g_video_act_margin_top; + video_add_rect(kimage_ptr, 0, 0, width, height); + + // Do bottom border + height = g_video_act_margin_bottom; + y = g_video_act_margin_top + A2_WINDOW_HEIGHT; + video_add_rect(kimage_ptr, 0, y, width, height); + } + if(g_status_refresh_needed) { + g_status_refresh_needed = 0; + width = g_mainwin_kimage.a2_width; + y = g_video_act_margin_top + A2_WINDOW_HEIGHT + + g_video_act_margin_bottom; + height = kimage_ptr->a2_height - y; + if(height > 0) { + save_pixel_dcount = g_video_pixel_dcount; + video_add_rect(kimage_ptr, 0, y, width, height); + g_video_pixel_dcount = save_pixel_dcount; + } + } + + if(g_a2_screen_buffer_changed == 0) { + return; + } + + GET_ITIMER(start_time); + + start = -1; + + left_pix = 640; + right_pix = 0; + + for(line = 0; line < 200; line++) { + line_div8 = line >> 3; + mask = 1 << (line_div8); + if((g_full_refresh_needed & mask) != 0) { + left = 0; + right = 640; + } else { + left = g_a2_line_left_edge[line]; + right = g_a2_line_right_edge[line]; + } + + if(!(g_a2_screen_buffer_changed & mask) || (left >= right)) { + /* No need to update this line */ + /* Refresh previous chunks of lines, if any */ + if(start >= 0) { + video_add_a2_rect(start, line, left_pix, + right_pix); + start = -1; + left_pix = 640; + right_pix = 0; + } + } else { + /* Need to update this line */ + if(start < 0) { + start = line; + } + left_pix = MY_MIN(left, left_pix); + right_pix = MY_MAX(right, right_pix); + } + } + + if(start >= 0) { + video_add_a2_rect(start, 200, left_pix, right_pix); + } + + g_a2_screen_buffer_changed = 0; + g_full_refresh_needed = 0; + + GET_ITIMER(end_time); + + g_cycs_in_xredraw += (end_time - start_time); +} + +int +video_get_a2_width(Kimage *kimage_ptr) +{ + return kimage_ptr->a2_width; +} + +int +video_get_x_width(Kimage *kimage_ptr) +{ + return kimage_ptr->x_width; +} + +int +video_get_a2_height(Kimage *kimage_ptr) +{ + return kimage_ptr->a2_height; +} + +int +video_get_x_height(Kimage *kimage_ptr) +{ + return kimage_ptr->x_height; +} + +int +video_get_x_xpos(Kimage *kimage_ptr) +{ + return kimage_ptr->x_xpos; +} + +int +video_get_x_ypos(Kimage *kimage_ptr) +{ + return kimage_ptr->x_ypos; +} + +void +video_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos) +{ + x_xpos = video_clamp(x_xpos, 0, kimage_ptr->x_max_width - 640); + x_ypos = video_clamp(x_ypos, 0, kimage_ptr->x_max_height - 420); + kimage_ptr->x_xpos = x_xpos; + kimage_ptr->x_ypos = x_ypos; + if(kimage_ptr == &g_mainwin_kimage) { + g_mainwin_xpos = x_xpos; + g_mainwin_ypos = x_ypos; + // printf("Set g_mainwin_xpos:%d, ypos:%d\n", x_xpos, x_ypos); + } +} + +int +video_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height) +{ + // Return 1 if the passed in height, width do not match the kimage + // aspect-corrected version, and at least 2 VBL periods have passed + + if((kimage_ptr->vbl_of_last_resize + 6) > g_vbl_count) { + return 0; + } + if((kimage_ptr->x_height != x_height) || + (kimage_ptr->x_width != x_width)) { +#if 0 + printf("change_aspect_needed, vbl:%d kimage width:%d height:%d " + "but x width:%d height:%d\n", g_vbl_count, + kimage_ptr->x_width, kimage_ptr->x_height, + x_width, x_height); +#endif + return 1; + } + return 0; +} + +void +video_update_status_enable(Kimage *kimage_ptr) +{ + int height, a2_height; + + height = g_video_act_margin_top + A2_WINDOW_HEIGHT + + g_video_act_margin_bottom; + a2_height = height; + if(g_status_enable) { + a2_height = kimage_ptr->a2_height_full; + } + kimage_ptr->a2_height = a2_height; + height = (a2_height * kimage_ptr->scale_width_a2_to_x) >> 16; + if(height > kimage_ptr->x_max_height) { + height = kimage_ptr->x_max_height; + } + kimage_ptr->x_height = height; +#if 0 + printf("new a2_height:%d, x_height:%d\n", kimage_ptr->a2_height, + kimage_ptr->x_height); +#endif + //printf("Calling video_update_scale from video_update_status_en\n"); + video_update_scale(kimage_ptr, kimage_ptr->x_width, height, 0); +} + +// video_out_query: return 0 if no screen drawing at all is needed. +// returns 1 or the number of change_rects if any drawing is needed +int +video_out_query(Kimage *kimage_ptr) +{ + int num_change_rects, x_refresh_needed; + + num_change_rects = kimage_ptr->num_change_rects; + x_refresh_needed = kimage_ptr->x_refresh_needed; + if(x_refresh_needed) { + return 1; + } + return num_change_rects; +} + +// video_out_done: used by specialize xdriver platform code which needs to +// clear the num_change_rects=0. +void +video_out_done(Kimage *kimage_ptr) +{ + kimage_ptr->num_change_rects = 0; + kimage_ptr->x_refresh_needed = 0; +} + +// Called by xdriver.c to copy KEGS's kimage data to the vptr buffer +int +video_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act, + Change_rect *rectptr, int pos) +{ + word32 *out_wptr, *wptr; + int a2_width, a2_width_full, width, a2_height, height, x, eff_y; + int x_width, x_height, num_change_rects, x_refresh_needed; + int i, j; + + // Copy from kimage_ptr->wptr to vptr + num_change_rects = kimage_ptr->num_change_rects; + x_refresh_needed = kimage_ptr->x_refresh_needed; + if(((pos >= num_change_rects) || (pos >= MAX_CHANGE_RECTS)) && + !x_refresh_needed) { + kimage_ptr->num_change_rects = 0; + return 0; + } + a2_width = kimage_ptr->a2_width; + a2_width_full = kimage_ptr->a2_width_full; + a2_height = kimage_ptr->a2_height; + if((num_change_rects >= MAX_CHANGE_RECTS) || x_refresh_needed) { + // Table overflow, just copy everything in one go + kimage_ptr->x_refresh_needed = 0; + if(pos >= 1) { + kimage_ptr->num_change_rects = 0; + return 0; // No more to do + } + // Force full update + rectptr->x = 0; + rectptr->y = 0; + rectptr->width = a2_width; + rectptr->height = a2_height; + } else { + *rectptr = kimage_ptr->change_rect[pos]; // Struct copy + } +#if 0 + printf("video_out_data, %p rectptr:%p, pos:%d, x:%d y:%d w:%d h:%d, " + "wptr:%p\n", vptr, rectptr, pos, rectptr->x, + rectptr->y, rectptr->width, rectptr->height, + kimage_ptr->wptr); +#endif + + width = rectptr->width; + height = rectptr->height; + x = rectptr->x; + x_width = kimage_ptr->x_width; + x_height = kimage_ptr->x_height; + if(!g_video_no_scale_window && + ((a2_width != x_width) || (a2_height != x_height))) { +#if 0 + printf("a2_width:%d, x_width:%d, a2_height:%d, x_height:" + "%d\n", a2_width, x_width, a2_height, x_height); +#endif + return video_out_data_scaled(vptr, kimage_ptr, out_width_act, + rectptr); + } else { + out_wptr = (word32 *)vptr; + for(i = 0; i < height; i++) { + eff_y = rectptr->y + i; + wptr = kimage_ptr->wptr + (eff_y * a2_width_full) + x; + out_wptr = ((word32 *)vptr) + + (eff_y * out_width_act) + x; + for(j = 0; j < width; j++) { + *out_wptr++ = *wptr++; + } + } + } + + return 1; +} + + +int +video_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act, + Change_rect *rectptr) +{ + word32 *out_wptr, *wptr; + word32 pos_scale, alpha_mask; + int a2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y; + int out_width, out_height, max_x, max_y, out_max_x, out_max_y, pos; + int i, j; + + // Faster scaling routine which does simple pixel replication rather + // than blending. Intended for scales >= 3.0 (or so) since at + // these scales, replication looks fine. + x = rectptr->x; + y = rectptr->y; + max_x = rectptr->width + x; + max_y = rectptr->height + y; + max_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1); + max_y = MY_MIN(kimage_ptr->a2_height, max_y + 1); + x = MY_MAX(0, x - 1); + y = MY_MAX(0, y - 1); + a2_width_full = kimage_ptr->a2_width_full; + + out_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16; + out_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16; + out_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16; + out_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16; + out_max_x = MY_MIN(out_max_x, out_width_act); + out_max_y = MY_MIN(out_max_y, kimage_ptr->x_height); + out_width = out_max_x - out_x; + out_height = out_max_y - out_y; + out_wptr = (word32 *)vptr; + rectptr->x = out_x; + rectptr->y = out_y; + rectptr->width = out_width; + rectptr->height = out_height; + alpha_mask = g_alpha_mask; + for(i = 0; i < out_height; i++) { + eff_y = out_y + i; + pos_scale = kimage_ptr->scale_height[eff_y]; + src_y = pos_scale >> 16; + wptr = kimage_ptr->wptr + (src_y * a2_width_full); + out_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x; + for(j = 0; j < out_width; j++) { + new_x = j + out_x; + pos_scale = kimage_ptr->scale_width[new_x]; + pos = pos_scale >> 16; + *out_wptr++ = wptr[pos] | alpha_mask; + } + } + rectptr->width = kimage_ptr->x_width - rectptr->x; + + return 1; +} + +int +video_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act, + Change_rect *rectptr) +{ + word32 *out_wptr, *wptr; + dword64 dval0a, dval0b, dval1a, dval1b, dscale, dscale_y, dval; + word32 new_val, pos_scale, alpha_mask; + int a2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y; + int out_width, out_height, max_x, max_y, out_max_x, out_max_y, pos; + int i, j; + + if((kimage_ptr->scale_width_a2_to_x >= 0x34000) || + (kimage_ptr->scale_height_a2_to_x >= 0x34000)) { + return video_out_data_intscaled(vptr, kimage_ptr, + out_width_act, rectptr); + } + x = rectptr->x; + y = rectptr->y; + max_x = rectptr->width + x; + max_y = rectptr->height + y; + max_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1); + max_y = MY_MIN(kimage_ptr->a2_height, max_y + 1); + x = MY_MAX(0, x - 1); + y = MY_MAX(0, y - 1); + a2_width_full = kimage_ptr->a2_width_full; + + out_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16; + out_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16; + out_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16; + out_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16; + out_max_x = MY_MIN(out_max_x, out_width_act); + out_max_y = MY_MIN(out_max_y, kimage_ptr->x_height); + out_width = out_max_x - out_x; + out_height = out_max_y - out_y; +#if 0 + printf("scaled: in %d,%d %d,%d becomes %d,%d %d,%d\n", x, y, width, + height, out_x, out_y, out_width, out_height); +#endif + out_wptr = (word32 *)vptr; + rectptr->x = out_x; + rectptr->y = out_y; + rectptr->width = out_width; + rectptr->height = out_height; + alpha_mask = g_alpha_mask; + for(i = 0; i < out_height; i++) { + eff_y = out_y + i; + pos_scale = kimage_ptr->scale_height[eff_y]; + src_y = pos_scale >> 16; + dscale_y = (pos_scale & 0xffff) >> 8; + wptr = kimage_ptr->wptr + (src_y * a2_width_full); + out_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x; + for(j = 0; j < out_width; j++) { + new_x = j + out_x; + pos_scale = kimage_ptr->scale_width[new_x]; + pos = pos_scale >> 16; + dscale = (pos_scale & 0xffff) >> 8; + dval0a = wptr[pos]; + dval0a = (dval0a & 0x00ff00ffULL) | + ((dval0a & 0xff00ff00ULL) << 24); + dval0b = wptr[pos + 1]; + dval0b = (dval0b & 0x00ff00ffULL) | + ((dval0b & 0xff00ff00ULL) << 24); + dval1a = wptr[pos + a2_width_full]; + dval1a = (dval1a & 0x00ff00ffULL) | + ((dval1a & 0xff00ff00ULL) << 24); + dval1b = wptr[pos + 1 + a2_width_full]; + dval1b = (dval1b & 0x00ff00ffULL) | + ((dval1b & 0xff00ff00ULL) << 24); + dval0a = ((0x100 - dscale) * dval0a) + + (dscale * dval0b); + dval1a = ((0x100 - dscale) * dval1a) + + (dscale * dval1b); + dval0a = (dval0a >> 8) & 0x00ff00ff00ff00ffULL; + dval1a = (dval1a >> 8) & 0x00ff00ff00ff00ffULL; + dval = ((0x100 - dscale_y) * dval0a) + + (dscale_y * dval1a); + new_val = ((dval >> 8) & 0x00ff00ffULL) | + ((dval >> 32) & 0xff00ff00ULL); + *out_wptr++ = new_val | alpha_mask; +#if 0 + if((pos == 300) && (eff_y == 100)) { + printf("x:%d pos:%d %08x. %016llx,%016llx " + "pos_sc:%08x, %08x\n", new_x, pos, + new_val, dval0a, dval0b, pos_scale, + wptr[pos]); + } +#endif + } + } + rectptr->width = kimage_ptr->x_width - rectptr->x; +#if 0 + for(i = 0; i < kimage_ptr->x_height; i++) { + out_wptr = ((word32 *)vptr) + (i * out_width_act) + + kimage_ptr->x_width - 1; + *out_wptr = 0x00ff00ff; +# if 0 + for(j = 0; j < 10; j++) { + if(*out_wptr != 0) { + printf("out_wptr:%p is %08x at %d,%d\n", + out_wptr, *out_wptr, + out_width_act - 1 - j, i); + } + out_wptr--; + } +# endif + } +#endif + + return 1; +} + +word32 +video_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv) +{ + word32 frac, frac_to_next, new_frac; + + frac = pos * frac_inc; + if(frac >= max) { + return max; // Clear frac bits + } + if(g_video_scale_algorithm == 2) { + return frac & -65536; // nearest neighbor + } + if(g_video_scale_algorithm == 1) { + return frac; // bilinear interp + } + // Do proper scaling. fraction=0 means 100% this pixel, fraction=ffff + // means 99.99% the next pixel + frac_to_next = frac_inc + (frac & 0xffff); + if(frac_to_next < 65536) { + frac_to_next = 0; + } + frac_to_next = (frac_to_next & 0xffff) * frac_inc_inv; + frac_to_next = frac_to_next >> 16; + new_frac = (frac & -65536) | (frac_to_next & 0xffff); +#if 0 + if((frac >= (30 << 16)) && (frac < (38 << 16))) { + printf("scale %d (%02x) -> %08x (was %08x) %08x %08x\n", + pos, pos, new_frac, frac, frac_inc, frac_inc_inv); + } +#endif + return new_frac; +} + +void +video_update_scale(Kimage *kimage_ptr, int out_width, int out_height, + int must_update) +{ + word32 frac_inc, frac_inc_inv, new_frac, max; + int a2_width, a2_height, exp_width, exp_height; + int i; + + out_width = video_clamp(out_width, 1, kimage_ptr->x_max_width); + out_width = video_clamp(out_width, 1, MAX_SCALE_SIZE); + + out_height = video_clamp(out_height, 1, kimage_ptr->x_max_height); + out_height = video_clamp(out_height, 1, MAX_SCALE_SIZE); + + a2_width = kimage_ptr->a2_width; + a2_height = kimage_ptr->a2_height; + kimage_ptr->vbl_of_last_resize = g_vbl_count; + + // Handle aspect ratio. Calculate height/width based on the other's + // aspect ratio, and pick the smaller value + exp_width = (a2_width * out_height) / a2_height; + exp_height = (a2_height * out_width) / a2_width; + + if(exp_width < a2_width) { + exp_width = a2_width; + } + if(exp_height < a2_height) { + exp_height = a2_height; + } + if(exp_width < out_width) { + // Allow off-by-one to be OK, so window doesn't keep resizing + if((exp_width + 1) != out_width) { + out_width = exp_width; + } + } + if(exp_height < out_height) { + if((exp_height + 1) != out_height) { + out_height = exp_height; + } + } + if(out_width <= 0) { + out_width = 1; + } + if(out_height <= 0) { + out_height = 1; + } + + // See if anything changed. If it's unchanged, don't do anything + if((kimage_ptr->x_width == out_width) && !must_update && + (kimage_ptr->x_height == out_height)) { + return; + } + kimage_ptr->x_width = out_width; + kimage_ptr->x_height = out_height; + kimage_ptr->x_refresh_needed = 1; + if(kimage_ptr == &g_mainwin_kimage) { + g_mainwin_width = out_width; + g_mainwin_height = out_height; + //printf("Set g_mainwin_width=%d, g_mainwin_height=%d\n", + // out_width, out_height); + } + + // the per-pixel inc = a2_width / out_width. Scale by 65536 + frac_inc = (a2_width * 65536UL) / out_width; + kimage_ptr->scale_width_to_a2 = frac_inc; + frac_inc_inv = (out_width * 65536UL) / a2_width; + kimage_ptr->scale_width_a2_to_x = frac_inc_inv; +#if 0 + printf("scale_width_to_a2: %08x, a2_to_x:%08x, is_debugwin:%d\n", + kimage_ptr->scale_width_to_a2, kimage_ptr->scale_width_a2_to_x, + (kimage_ptr == &g_debugwin_kimage)); +#endif + max = (a2_width - 1) << 16; + for(i = 0; i < out_width + 1; i++) { + new_frac = video_scale_calc_frac(i, max, frac_inc, + frac_inc_inv); + kimage_ptr->scale_width[i] = new_frac; + } + + frac_inc = (a2_height * 65536UL) / out_height; + kimage_ptr->scale_height_to_a2 = frac_inc; + frac_inc_inv = (out_height * 65536UL) / a2_height; + kimage_ptr->scale_height_a2_to_x = frac_inc_inv; +#if 0 + printf("scale_height_to_a2: %08x, a2_to_x:%08x. w:%d h:%d\n", + kimage_ptr->scale_height_to_a2, + kimage_ptr->scale_height_a2_to_x, out_width, out_height); +#endif + max = (a2_height - 1) << 16; + for(i = 0; i < out_height + 1; i++) { + new_frac = video_scale_calc_frac(i, max, frac_inc, + frac_inc_inv); + kimage_ptr->scale_height[i] = new_frac; + } +} + +int +video_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width) +{ + int x; + + // raw_x is in output coordinates. Scale down to a2 coordinates + if(x_width == 0) { + x = (kimage_ptr->scale_width_to_a2 * raw_x) / 65536; + } else { + // Scale raw_x using x_width + x = (raw_x * kimage_ptr->a2_width_full) / x_width; + } + x = x - BASE_MARGIN_LEFT; + return x; +} + +int +video_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height) +{ + int y; + + // raw_y is in output coordinates. Scale down to a2 coordinates + if(y_height == 0) { + y = (kimage_ptr->scale_height_to_a2 * raw_y) / 65536; + } else { + // Scale raw_y using y_height + y = (raw_y * kimage_ptr->a2_height) / y_height; + } + y = y - BASE_MARGIN_TOP; + return y; +} + +int +video_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width) +{ + int x; + + // Convert a2_x to output coordinates + x = a2_x + BASE_MARGIN_LEFT; + if(x_width == 0) { + x = (kimage_ptr->scale_width_a2_to_x * x) / 65536; + } else { + // Scale a2_x using x_width + x = (x * x_width) / kimage_ptr->a2_width_full; + } + return x; +} + +int +video_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height) +{ + int y; + + // Convert a2_y to output coordinates + y = a2_y + BASE_MARGIN_TOP; + if(y_height == 0) { + y = (kimage_ptr->scale_height_a2_to_x * y) / 65536; + } else { + // Scale a2_y using y_height + y = (y * y_height) / kimage_ptr->a2_height; + } + return y; +} + +void +video_update_color_raw(int bank, int col_num, int a2_color) +{ + word32 tmp; + int red, green, blue, newred, newgreen, newblue; + + if(col_num >= 256 || col_num < 0) { + halt_printf("video_update_color_raw: col: %03x\n", col_num); + return; + } + + red = (a2_color >> 8) & 0xf; + green = (a2_color >> 4) & 0xf; + blue = (a2_color) & 0xf; + red = ((red << 4) + red); + green = ((green << 4) + green); + blue = ((blue << 4) + blue); + + newred = red >> g_red_right_shift; + newgreen = green >> g_green_right_shift; + newblue = blue >> g_blue_right_shift; + + tmp = ((newred & g_red_mask) << g_red_left_shift) + + ((newgreen & g_green_mask) << g_green_left_shift) + + ((newblue & g_blue_mask) << g_blue_left_shift); + g_palette_8to1624[bank][col_num] = tmp; +} + +void +video_update_status_line(int line, const char *string) +{ + byte a2_str_buf[STATUS_LINE_LENGTH+1]; + word32 *wptr; + char *buf; + const char *ptr; + word32 line_bytes; + int start_line, c, pixels_per_line, offset; + int i; + + if(line >= MAX_STATUS_LINES || line < 0) { + printf("update_status_line: line: %d!\n", line); + exit(1); + } + + ptr = string; + buf = &(g_status_buf[line][0]); + g_status_ptrs[line] = buf; + for(i = 0; i < STATUS_LINE_LENGTH; i++) { + if(*ptr) { + c = *ptr++; + } else { + c = ' '; + } + buf[i] = c; + a2_str_buf[i] = c | 0x80; + } + + buf[STATUS_LINE_LENGTH] = 0; + a2_str_buf[STATUS_LINE_LENGTH] = 0; + start_line = (200 + 2*8) + line*8; + pixels_per_line = g_mainwin_kimage.a2_width_full; + offset = (pixels_per_line * g_video_act_margin_top); + wptr = g_mainwin_kimage.wptr; + wptr += offset; + for(i = 0; i < 8; i++) { + line_bytes = ((start_line + i) << 16) | (40 << 8) | 0; + redraw_changed_string(&(a2_str_buf[0]), line_bytes, -1L, + wptr, 0, 0x00ffffff, pixels_per_line, 1); + } + + // Don't add rectangle here, video_form_change_rects will do it + //video_add_a2_rect(start_line, start_line + 8, 0, 640); +} + +void +video_draw_a2_string(int line, const byte *bptr) +{ + word32 *wptr; + word32 line_bytes; + int start_line, pixels_per_line, offset; + int i; + + start_line = line*8; + pixels_per_line = g_mainwin_kimage.a2_width_full; + offset = (pixels_per_line * g_video_act_margin_top) + + g_video_act_margin_left; + wptr = g_mainwin_kimage.wptr; + wptr += offset; + for(i = 0; i < 8; i++) { + line_bytes = ((start_line + i) << 16) | (40 << 8) | 0; + redraw_changed_string(bptr, line_bytes, -1L, + wptr, 0, 0x00ffffff, pixels_per_line, 1); + } + g_mainwin_kimage.x_refresh_needed = 1; +} + +void +video_show_debug_info() +{ + word32 tmp1; + + printf("g_cur_dfcyc: %016llx, last_vbl: %016llx\n", g_cur_dfcyc, + g_last_vbl_dfcyc); + tmp1 = get_lines_since_vbl(g_cur_dfcyc); + printf("lines since vbl: %06x\n", tmp1); + printf("Last line updated: %d\n", g_vid_update_last_line); +} + +word32 +read_video_data(dword64 dfcyc) +{ + word32 val, val2; + int lines_since_vbl, line; + + // Return Charrom data at $C02C for SuperConvert 4 TDM mode + lines_since_vbl = get_lines_since_vbl(dfcyc); + val = float_bus_lines(dfcyc, lines_since_vbl); + line = lines_since_vbl >> 8; + if(line < 192) { + // Always do the character ROM + val2 = g_a2font_bits[val & 0xff][line & 7]; + dbg_log_info(dfcyc, val, + (lines_since_vbl << 8) | (val2 & 0xff), 0xc02c); + val = ~val2; // Invert it, maybe + } + return val & 0xff; +} + +word32 +float_bus(dword64 dfcyc) +{ + word32 lines_since_vbl; + + lines_since_vbl = get_lines_since_vbl(dfcyc); + return float_bus_lines(dfcyc, lines_since_vbl); +} + +word32 +float_bus_lines(dword64 dfcyc, word32 lines_since_vbl) +{ + word32 val; + int line, eff_line, line24, all_stat, byte_offset; + int hires, page2, addr; + +/* For floating bus, model hires style: Visible lines 0-191 are simply the */ +/* data being displayed at that time. Lines 192-255 are lines 0 - 63 again */ +/* and lines 256-261 are lines 58-63 again */ +/* For each line, figure out starting byte at -25 mod 128 bytes from this */ +/* line's start */ +/* This emulates an Apple II style floating bus. A real IIgs does not */ +/* drive anything meaningful during the 25 horizontal blanking cycles, */ +/* nor during veritical blanking. The data seems to be 0 or related to */ +/* the instruction fetches on a real IIgs during blankings */ + + line = lines_since_vbl >> 8; + byte_offset = lines_since_vbl & 0xff; + // byte offset is from 0 through 64, where the visible screen is drawn + // from 25 through 64 + + eff_line = line; + if(eff_line >= 0x100) { + eff_line = (eff_line - 6) & 0xff; + } + if(byte_offset == 0) { + byte_offset = 1; + } + all_stat = g_cur_a2_stat; + hires = (all_stat & ALL_STAT_HIRES) && !(all_stat & ALL_STAT_TEXT); + if((all_stat & ALL_STAT_MIX_T_GR) && (line >= 160)) { + hires = 0; + } + page2 = EXTRU(all_stat, 31 - BIT_ALL_STAT_PAGE2, 1); + if(all_stat & ALL_STAT_ST80) { + page2 = 0; + } + + line24 = (eff_line >> 3) & 0x1f; + addr = g_screen_index[line24] & 0x3ff; + addr = (addr & 0x380) + (((addr & 0x7f) - 25 + byte_offset) & 0x7f); + if(hires) { + addr = 0x2000 + addr + ((eff_line & 7) << 10) + (page2 << 13); + } else { + addr = 0x400 + addr + (page2 << 10); + } + + val = g_slow_memory_ptr[addr]; +#if 0 + printf("For %04x (%d) addr=%04x, val=%02x, dfcyc:%016llx\n", + lines_since_vbl, eff_line, addr, val, dfcyc - g_last_vbl_dfcyc); +#endif + dbg_log_info(dfcyc, ((lines_since_vbl >> 11) << 24) | + (lines_since_vbl - 25), (addr << 8) | val, 0xff); + + return val; +} diff --git a/gsplus/src/voc.c b/gsplus/src/voc.c new file mode 100644 index 0000000..5edde5d --- /dev/null +++ b/gsplus/src/voc.c @@ -0,0 +1,221 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// This file provides emulation of the Apple Video Overlay Card, which +// will appear to be in slot 3 if g_voc_enable=1 (there's a config.c +// setting to control enabling VOC). The only currently supported VOC +// feature is the SHR interlaced display using both Main and Aux memory +// to provide a 640x400 (or 320x400) pixel display. + +#include "defc.h" + +extern word32 g_c02b_val; +extern int g_cur_a2_stat; +extern word32 g_vbl_count; + +int g_voc_enable = 0; // Default to disabled for now +word32 g_voc_reg1 = 0x09; +word32 g_voc_reg3 = 0; +word32 g_voc_reg4 = 0; +word32 g_voc_reg5 = 0; +word32 g_voc_reg6 = 0; + +word32 +voc_devsel_read(word32 loc, dword64 dfcyc) +{ + // Reads to $c0b0-$c0bf. + loc = loc & 0xf; + switch(loc) { + case 0: // 0xc0b0 + return voc_read_reg0(dfcyc); + break; + case 1: // 0xc0b1 + return g_voc_reg1; + break; + case 3: // 0xc0b3 + return g_voc_reg3; + break; + case 4: // 0xc0b4 + return g_voc_reg4; + break; + case 5: // 0xc0b5 + return g_voc_reg5; + break; + case 6: // 0xc0b6 + return g_voc_reg6; + break; + case 7: // 0xc0b7, possible Uthernet 2 detection + return 0x00; + break; + case 8: // 0xc0b8, Second Sight detection by jpeGS program + return 0x00; // Second Sight returns 0x01 + break; + case 0xd: // 0xc0bd, A2OSX Uthernet 1 detection code + return 0x00; + break; + } + + halt_printf("Tried to read: %04x\n", 0xc0b0 + loc); + + return 0; +} + +void +voc_devsel_write(word32 loc, word32 val, dword64 dfcyc) +{ + // Writes to $c0b0-$c0bf. + loc = loc & 0xf; + switch(loc) { + case 0: // 0xc0b0 + // Write 0 to clear VBL interrupts + if(val != 0) { + halt_printf("VOC write %04x = %02x\n", loc, val); + } + return; + break; + case 1: // 0xc0b1 + // bit 0: R/W: 1=GG Bus Enable + // When 0, I think VOC ignores writes to $c023,etc. + // bit 2: R/W: 0=OutChromaFilter enabled, 1=ChromaFilter disab + // bit 2 is also TextMonoOver somehow using bit[5]==1 + // bit 3: R/W: 1=MainPageLin + // bits 5:4: R/W: 00=Aux mem; 01=Main Memory; 11=Interlaced + // bit 6: R/W: 1=Enable VBL Interrupt + // bit 7: R/W: 1=Enable Line interrupts + if(!g_voc_enable) { + val = 0; + } + if(val & 0xc0) { + halt_printf("VOC write %04x = %02x\n", loc, val); + } +#if 0 + if(val != g_voc_reg1) { + printf("$c0b1:%02x (was %02x)\n", val, g_voc_reg1); + } +#endif + g_voc_reg1 = val; + voc_update_interlace(dfcyc); + return; + break; + case 3: // 0xc0b3 + // bits 2:0: R/W: Key Dissolve, 0=100% graphics, 7=100% video + // bit 3: R/W: 1=Enhanced Dissolve enabled + // bits 6:4: R/W: Non-Key Dissolve, 0=100% graphics, 7=100% vid + // bit 7: R/W: 0=Output Setup Enabled, 1=Output Setup Disabled + g_voc_reg3 = val; + return; + break; + case 4: // 0xc0b4 + // bits 3:0: R/W: KeyColor Blue + // bits 7:4: R/W: KeyColor Green + g_voc_reg4 = val; + return; + break; + case 5: // 0xc0b5 + // bits 3:0: R/W: KeyColor Red + // bit 4: R/W: OutExtBlank: 0=Graphics, 1=External + // bit 5: R/W: 0=GenLock enabled, 1=GenLock disabled + // bit 6: R/W: 0=KeyColor enabled, 1=KeyColor disabled + // bit 7: R/W: 1=Interlace mode enabled + g_voc_reg5 = val; + voc_update_interlace(dfcyc); + return; + break; + case 6: // 0xc0b6 + // Write 0 to cause AdjSave to occur + // Write 8, then 9, then 8 again to cause AdjInc for Hue + // Write a, then b, then a again to cause AdjDec for Hue + // Write 4, then 5, then 4 again to cause AdjInc for Saturation + // Write 6, then 7, then 6 again to cause AdjDec for Saturation + // bit 3: hue, bit 2: saturation + g_voc_reg6 = val; + return; + break; + case 7: // 0xc0b7 + // Written by System Disk 1.1 Desktop.sys to 0xfd, ignore + if(val == 0xfd) { + return; + } + break; + case 0xa: + case 0xb: // 0xc0ba,0xc0bb written to 0 by A2OSX Uthernet1 detect + if(val == 0) { + return; + } + break; + } + halt_printf("Unknown Write %04x = %02x %016llx\n", 0xc0b0 + loc, val, + dfcyc); +} + +void +voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc) +{ + // Writes to $c300-$c3ff + halt_printf("Wrote VOC %04x = %02x %016llx\n", 0xc300 + (loc & 0xff), + val, dfcyc); +} + +void +voc_reset() +{ + g_voc_reg1 = 0x0d; // [0]: GG Bus enable, [3]:MainPageLin + g_voc_reg3 = 0x07; + g_voc_reg4 = 0; + g_voc_reg5 = 0x40; + g_voc_reg6 = 0; +} + +double g_voc_last_pal_vbl = 0; + +word32 +voc_read_reg0(dword64 dfcyc) +{ + word32 frame, in_vbl; + + if(!g_voc_enable) { + return 0; + } + // Reading $c0b0. + // c0b0: bit 2: R/O: 1=In VBL + // c0b0: bit 3: R/O: 0=No Video Detected, 1=Video Detected + // c0b0: bit 4: R/O: 1=Video Genlocked + // c0b0: bit 5: R/O: 0=showing Field 0, 1=showing Field 1 + // c0b0: bit 6: R/O: 1=VBL Int Request pending + // c0b0: bit 7: R/O: 1=Line Int Request pending + in_vbl = in_vblank(dfcyc); + dbg_log_info(dfcyc, 0, in_vbl, 0x1c0b0); + frame = g_vbl_count & 1; + return (frame << 5) | (in_vbl << 2); +} + +void +voc_update_interlace(dword64 dfcyc) +{ + word32 new_stat, mask; + + new_stat = 0; + if(((g_voc_reg1 & 0x30) == 0x30) && (g_voc_reg5 & 0x80)) { + new_stat = ALL_STAT_VOC_INTERLACE; + } + if((g_voc_reg1 & 0x30) == 0x10) { // Draw SHR from mainmem + new_stat = ALL_STAT_VOC_MAIN; + } + mask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN; + if((g_cur_a2_stat ^ new_stat) & mask) { + // Interlace mode has changed + g_cur_a2_stat &= (~mask); + g_cur_a2_stat |= new_stat; + printf("Change VOC interlace mode: %08x\n", new_stat); + change_display_mode(dfcyc); + } +} + diff --git a/gsplus/src/win32snd_driver.c b/gsplus/src/win32snd_driver.c new file mode 100644 index 0000000..2c5d139 --- /dev/null +++ b/gsplus/src/win32snd_driver.c @@ -0,0 +1,307 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2023 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Audio is sent every 1/60th of a second to win32_send_audio(). Play it +// using waveOutWrite() as long as we've got at least 2/60th of a second +// buffered--otherwise waveOutPause(). If we get more than 10 buffers +// queued, drop buffers until we get down to 6 buffers queued again. + +// Track headers completed in g_wavehdr_rd_pos using callback function. + +#include "defc.h" +#include "sound.h" + +#include +#include + +extern int Verbose; + +extern int g_audio_rate; + +unsigned int __stdcall child_sound_loop_win32(void *param); +void check_wave_error(int res, char *str); + +#define NUM_WAVE_HEADERS 32 + +HWAVEOUT g_wave_handle; +WAVEHDR g_wavehdr[NUM_WAVE_HEADERS]; +// Each header is for 1/60th of a second of sound (generally). Pause +// until 2 headers are available, then unpause. Experimentally it appears +// we keep about 5 headers (5/60th of a second = 80msec) ahead, which is +// excellent latency + +extern int g_audio_enable; +extern word32 *g_sound_shm_addr; +extern int g_preferred_rate; + +int g_win32snd_buflen = 0x1000; +int g_win32_snd_playing = 0; +int g_win32_snd_to_drop = 0; +word32 g_win32_snd_dropped = 0; +volatile int g_wavehdr_rd_pos = 0; +volatile int g_wavehdr_wr_pos = 0; + +void +win32snd_init(word32 *shmaddr) +{ + printf("win32snd_init\n"); + child_sound_init_win32(); +} + +void +win32snd_shutdown() +{ + /* hmm */ + +} + +void CALLBACK +handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + LPWAVEHDR lpwavehdr; + int pos; + + /* Only service "buffer done playing messages */ + if(uMsg == WOM_DONE) { + lpwavehdr = (LPWAVEHDR)dwParam1; + if(lpwavehdr->dwFlags == (WHDR_DONE | WHDR_PREPARED)) { + lpwavehdr->dwUser = FALSE; + } + pos = (int)(lpwavehdr - &g_wavehdr[0]); + // printf("At %.3f, pos %d is done\n", get_dtime(), pos); + if(pos == g_wavehdr_rd_pos) { + pos = (pos + 1) % NUM_WAVE_HEADERS; + g_wavehdr_rd_pos = pos; + } else { + printf("wavehdr %d finished, exp %d\n", pos, + g_wavehdr_rd_pos); + } + } + + return; +} + + +void +check_wave_error(int res, char *str) +{ + char buf[256]; + + if(res == MMSYSERR_NOERROR) { + return; + } + + waveOutGetErrorText(res, &buf[0], sizeof(buf)); + printf("%s: %s\n", str, buf); + exit(1); +} + +void +child_sound_init_win32() +{ + WAVEFORMATEX wavefmt; + WAVEOUTCAPS caps; + byte *bptr; + UINT wave_id; + int bits_per_sample, channels, block_align, blen, res; + int i; + + memset(&wavefmt, 0, sizeof(WAVEFORMATEX)); + + wavefmt.wFormatTag = WAVE_FORMAT_PCM; + bits_per_sample = 16; + channels = 2; + wavefmt.wBitsPerSample = bits_per_sample; + wavefmt.nChannels = channels; + wavefmt.nSamplesPerSec = g_preferred_rate; + block_align = channels * (bits_per_sample / 8); + wavefmt.nBlockAlign = block_align; + wavefmt.nAvgBytesPerSec = block_align * g_audio_rate; + + res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, 0, 0, + WAVE_FORMAT_QUERY); + + if(res != MMSYSERR_NOERROR) { + printf("Cannot open audio device, res:%d, g_audio_rate:%d\n", + res, g_preferred_rate); + g_audio_enable = 0; + return; + } + + res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, + (DWORD_PTR)handle_wav_snd, 0, + CALLBACK_FUNCTION | WAVE_ALLOWSYNC); + + if(res != MMSYSERR_NOERROR) { + printf("Cannot register audio\n"); + g_audio_enable = 0; + return; + } + + g_audio_rate = wavefmt.nSamplesPerSec; + blen = (((g_audio_rate * block_align) / 60) * 5) / 4; + // Size buffer 25% larger than expected, to add some margin + blen = (blen + 15) & -16L; + + g_win32snd_buflen = blen; + bptr = malloc(blen * NUM_WAVE_HEADERS); + if(bptr == NULL) { + printf("Unabled to allocate sound buffer\n"); + exit(1); + } + + for(i = 0; i < NUM_WAVE_HEADERS; i++) { + memset(&g_wavehdr[i], 0, sizeof(WAVEHDR)); + g_wavehdr[i].dwUser = FALSE; + g_wavehdr[i].lpData = (char *)&(bptr[i * blen]); + g_wavehdr[i].dwBufferLength = blen; + g_wavehdr[i].dwFlags = 0; + g_wavehdr[i].dwLoops = 0; + res = waveOutPrepareHeader(g_wave_handle, &g_wavehdr[i], + sizeof(WAVEHDR)); + check_wave_error(res, "waveOutPrepareHeader"); + } + + res = waveOutGetID(g_wave_handle, &wave_id); + res = waveOutGetDevCaps(wave_id, &caps, sizeof(caps)); + check_wave_error(res, "waveOutGetDevCaps"); + printf("Using %s, buflen:%d\n", caps.szPname, g_win32snd_buflen); + printf(" Bits per Sample = %d. Channels = %d\n", + wavefmt.wBitsPerSample, wavefmt.nChannels); + printf(" Sampling rate = %d, avg_bytes_per_sec = %d\n", + (int)wavefmt.nSamplesPerSec, (int)wavefmt.nAvgBytesPerSec); + + sound_set_audio_rate(g_audio_rate); +} + +void +win32snd_set_playing(int snd_playing) +{ + g_win32_snd_playing = snd_playing; + if(snd_playing) { + waveOutRestart(g_wave_handle); +#if 0 + printf("win32 restarted sound wr:%d rd:%d\n", g_wavehdr_wr_pos, + g_wavehdr_rd_pos); +#endif + } else { + waveOutPause(g_wave_handle); +#if 0 + printf("win32 paused sound wr:%d rd:%d\n", g_wavehdr_wr_pos, + g_wavehdr_rd_pos); +#endif + } +} + +void +win32_send_audio2(byte *ptr, int size) +{ + int res, wr_pos, rd_pos, new_pos, bufs_in_use; + + wr_pos = g_wavehdr_wr_pos; + rd_pos = g_wavehdr_rd_pos; +#if 0 + if(wr_pos == 0) { + printf("send_audio2 wr:%d rd:%d sz:%d at %.3f\n", wr_pos, + rd_pos, size, get_dtime()); + } +#endif + if(g_wavehdr[wr_pos].dwUser != FALSE) { + // Audio buffer busy...should not happen! + printf("Audio buffer %d is busy!\n", wr_pos); + return; + } + + bufs_in_use = (NUM_WAVE_HEADERS + wr_pos - rd_pos) % NUM_WAVE_HEADERS; + if(g_win32_snd_to_drop) { + g_win32_snd_to_drop--; + g_win32_snd_dropped += size; + if((bufs_in_use < 4) && (g_win32_snd_to_drop != 0)) { +#if 0 + printf("bufs_in_use:%d, snd_to_drop:%d\n", bufs_in_use, + g_win32_snd_to_drop); +#endif + g_win32_snd_to_drop = 0; + } + if(g_win32_snd_to_drop == 0) { + printf("Dropped %d bytes of sound\n", + g_win32_snd_dropped); + } + return; + } +#if 0 + if(g_win32_snd_playing && (bufs_in_use <= 2)) { + printf("bufs_in_use:%d, wr:%d, rd:%d\n", bufs_in_use, wr_pos, + rd_pos); + } +#endif + if(bufs_in_use == 0) { +#if 0 + printf("bufs_in_use:%d, wr_pos:%d rd_pos:%d\n", bufs_in_use, + wr_pos, rd_pos); +#endif + // We've underflowed, so pause sound until we get some buffered + win32snd_set_playing(0); + } else if(g_win32_snd_playing == 0) { + if(bufs_in_use >= 2) { + //printf("bufs_in_use:%d, will start\n", bufs_in_use); + win32snd_set_playing(1); + } + } else { + if(bufs_in_use >= 14) { // About 230msec + // Drop 6 buffers to get us back down to 100msec delay + printf("bufs_in_use:%d, wr:%d will drop 6\n", + bufs_in_use, wr_pos); + g_win32_snd_to_drop = 6; + g_win32_snd_dropped = 0; + } + } + memcpy(g_wavehdr[wr_pos].lpData, ptr, size); + g_wavehdr[wr_pos].dwBufferLength = size; + g_wavehdr[wr_pos].dwUser = TRUE; + + new_pos = (wr_pos + 1) % NUM_WAVE_HEADERS; + g_wavehdr_wr_pos = new_pos; + res = waveOutWrite(g_wave_handle, &g_wavehdr[wr_pos], + sizeof(g_wavehdr)); + check_wave_error(res, "waveOutWrite"); + + return; +} + +int +win32_send_audio(byte *ptr, int in_size) +{ + int size; + int tmpsize; + + // printf("send_audio %d bytes at %.3f\n", in_size, get_dtime()); + size = in_size; + while(size > 0) { + tmpsize = size; + if(size > g_win32snd_buflen) { + tmpsize = g_win32snd_buflen; + } + win32_send_audio2(ptr, tmpsize); + ptr += tmpsize; + if(size != tmpsize) { +#if 0 + printf("Orig size:%d, reduced to %d\n", in_size, + tmpsize); +#endif + } + size = size - tmpsize; + } + + return in_size; +} + diff --git a/gsplus/src/win_dirent.h b/gsplus/src/win_dirent.h new file mode 100644 index 0000000..21ffdbc --- /dev/null +++ b/gsplus/src/win_dirent.h @@ -0,0 +1,32 @@ +#ifdef INCLUDE_RCSID_C +#endif + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2022 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Hacky defines to get something to compile for now +typedef unsigned short mode_t; + +struct dirent { + char d_name[1024]; +}; + +struct DIR_t { + int find_data_valid; + void *win_handle; + void *find_data_ptr; + struct dirent dirent; +}; +typedef struct DIR_t DIR; + +DIR *opendir(const char *filename); +struct dirent *readdir(DIR *dirp); +int closedir(DIR *dirp); + diff --git a/gsplus/src/windriver.c b/gsplus/src/windriver.c new file mode 100644 index 0000000..4e0c241 --- /dev/null +++ b/gsplus/src/windriver.c @@ -0,0 +1,1073 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Based on code from Chea Chee Keong from KEGS32, which was available at +// http://www.geocities.com/akilgard/kegs32 (geocities is gone now) + +#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ +#define STRICT /* Tell Windows we want compile type checks */ + +#include +#include +#include +#include +#include +#include /* For _get_osfhandle */ + +#include "defc.h" +#include "win_dirent.h" + +extern int Verbose; + +typedef struct windowinfo { + HWND win_hwnd; + HDC win_dc; + HDC win_cdc; + BITMAPINFO *win_bmapinfo_ptr; + BITMAPINFOHEADER *win_bmaphdr_ptr; + HBITMAP win_dev_handle; + + Kimage *kimage_ptr; // KEGS Image pointer for window content + char *name_str; + byte *data_ptr; + int motion; + int mdepth; + int active; + int pixels_per_line; + int x_xpos; + int x_ypos; + int width; + int height; + int extra_width; + int extra_height; +} Window_info; + +#include "protos_windriver.h" + +Window_info g_mainwin_info = { 0 }; +Window_info g_debugwin_info = { 0 }; + +int g_win_max_width = 0; +int g_win_max_height = 0; +int g_num_a2_keycodes = 0; + +int g_win_button_states = 0; +int g_win_hide_pointer = 0; +int g_win_warp_pointer = 0; +int g_win_warp_x = 0; +int g_win_warp_y = 0; + + +/* this table is used to search for the Windows VK_* in col 1 or 2 */ +/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */ +/* regardless of numlock */ +int g_a2_key_to_wsym[][3] = { + { 0x35, VK_ESCAPE, 0 }, + { 0x7a, VK_F1, 0 }, + { 0x78, VK_F2, 0 }, + { 0x63, VK_F3, 0 }, + { 0x76, VK_F4, 0 }, + { 0x60, VK_F5, 0 }, + { 0x61, VK_F6, 0 }, + { 0x62, VK_F7, 0 }, + { 0x64, VK_F8, 0 }, + { 0x65, VK_F9, 0 }, + { 0x6d, VK_F10, 0 }, + { 0x67, VK_F11, 0 }, + { 0x6f, VK_F12, 0 }, + { 0x69, VK_F13, 0 }, + { 0x6b, VK_F14, 0 }, + { 0x71, VK_F15, 0 }, + { 0x7f, VK_PAUSE, VK_CANCEL+0x100 }, // Reset + + { 0x12, '1', 0 }, + { 0x13, '2', 0 }, + { 0x14, '3', 0 }, + { 0x15, '4', 0 }, + { 0x17, '5', 0 }, + { 0x16, '6', 0 }, + { 0x1a, '7', 0 }, + { 0x1c, '8', 0 }, + { 0x19, '9', 0 }, + { 0x1d, '0', 0 }, + { 0x1b, 0xbd, 0 }, /* '-' */ + { 0x18, 0xbb, 0 }, /* '=' */ + { 0x33, VK_BACK, 0 }, /* backspace */ + { 0x72, VK_INSERT+0x100, 0 }, /* Insert key */ + { 0x74, VK_PRIOR+0x100, 0 }, /* pageup */ + { 0x47, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ + { 0x51, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ + { 0x4b, VK_DIVIDE, VK_DIVIDE+0x100 }, // KP / + { 0x43, VK_MULTIPLY, VK_MULTIPLY+0x100 }, // KP * + + { 0x30, VK_TAB, 0 }, + { 0x32, 0xc0, 0 }, /* '`' */ + { 0x0c, 'Q', 0 }, + { 0x0d, 'W', 0 }, + { 0x0e, 'E', 0 }, + { 0x0f, 'R', 0 }, + { 0x11, 'T', 0 }, + { 0x10, 'Y', 0 }, + { 0x20, 'U', 0 }, + { 0x22, 'I', 0 }, + { 0x1f, 'O', 0 }, + { 0x23, 'P', 0 }, + { 0x21, 0xdb, 0 }, /* [ */ + { 0x1e, 0xdd, 0 }, /* ] */ + { 0x2a, 0xdc, 0 }, /* backslash, bar */ + { 0x75, VK_DELETE+0x100, 0 }, + { 0x77, VK_END+0x100, VK_END }, + { 0x79, VK_NEXT+0x100, 0 }, + { 0x59, VK_NUMPAD7, VK_HOME }, + { 0x5b, VK_NUMPAD8, VK_UP }, + { 0x5c, VK_NUMPAD9, VK_PRIOR }, + { 0x4e, VK_SUBTRACT, VK_SUBTRACT+0x100 }, + + { 0x39, VK_CAPITAL, 0 }, // Capslock + { 0x00, 'A', 0 }, + { 0x01, 'S', 0 }, + { 0x02, 'D', 0 }, + { 0x03, 'F', 0 }, + { 0x05, 'G', 0 }, + { 0x04, 'H', 0 }, + { 0x26, 'J', 0 }, + { 0x28, 'K', 0 }, + { 0x25, 'L', 0 }, + { 0x29, 0xba, 0 }, /* ; */ + { 0x27, 0xde, 0 }, /* single quote */ + { 0x24, VK_RETURN, 0 }, + { 0x56, VK_NUMPAD4, VK_LEFT }, + { 0x57, VK_NUMPAD5, VK_CLEAR }, + { 0x58, VK_NUMPAD6, VK_RIGHT }, + { 0x45, VK_ADD, 0 }, + + { 0x38, VK_SHIFT, 0 }, + { 0x06, 'Z', 0 }, + { 0x07, 'X', 0 }, + { 0x08, 'C', 0 }, + { 0x09, 'V', 0 }, + { 0x0b, 'B', 0 }, + { 0x2d, 'N', 0 }, + { 0x2e, 'M', 0 }, + { 0x2b, 0xbc, 0 }, /* , */ + { 0x2f, 0xbe, 0 }, /* . */ + { 0x2c, 0xbf, 0 }, /* / */ + { 0x3e, VK_UP+0x100, 0 }, + { 0x53, VK_NUMPAD1, VK_END }, + { 0x54, VK_NUMPAD2, VK_DOWN }, + { 0x55, VK_NUMPAD3, VK_NEXT }, + + { 0x36, VK_CONTROL, VK_CONTROL+0x100 }, + { 0x37, VK_SCROLL, VK_MENU+0x100 }, // Command=scr_lock or alt-r + { 0x3a, VK_SNAPSHOT+0x100, VK_MENU }, // Opt=prntscrn or alt-l + { 0x31, ' ', 0 }, + { 0x3b, VK_LEFT+0x100, 0 }, + { 0x3d, VK_DOWN+0x100, 0 }, + { 0x3c, VK_RIGHT+0x100, 0 }, + { 0x52, VK_NUMPAD0, VK_INSERT }, + { 0x41, VK_DECIMAL, VK_DELETE }, + { 0x4c, VK_RETURN+0x100, 0 }, + { -1, -1, -1 } +}; + +#if 0 +int +win_nonblock_read_stdin(int fd, char *bufptr, int len) +{ + HANDLE oshandle; + DWORD dwret; + int ret; + + errno = EAGAIN; + oshandle = (HANDLE)_get_osfhandle(fd); // get stdin handle + dwret = WaitForSingleObject(oshandle, 1); // wait 1msec for data + ret = -1; + if(dwret == WAIT_OBJECT_0) { + ret = read(fd, bufptr, len); + } + return ret; +} +#endif + +Window_info * +win_find_win_info_ptr(HWND hwnd) +{ + if(hwnd == g_mainwin_info.win_hwnd) { + return &g_mainwin_info; + } + if(hwnd == g_debugwin_info.win_hwnd) { + return &g_debugwin_info; + } + return 0; +} + +void +win_hide_pointer(Window_info *win_info_ptr, int do_hide) +{ + ShowCursor(!do_hide); + // printf("Doing ShowCursor(%d)\n", !do_hide); +} + +int +win_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, + int button_states, int buttons_valid) +{ + Kimage *kimage_ptr; + int buttons_changed, x, y; + + kimage_ptr = win_info_ptr->kimage_ptr; + x = video_scale_mouse_x(kimage_ptr, raw_x, 0); + y = video_scale_mouse_y(kimage_ptr, raw_y, 0); + + // printf("wum: %d,%d -> %d,%d\n", raw_x, raw_y, x, y); + + buttons_changed = ((g_win_button_states & buttons_valid) != + button_states); + g_win_button_states = (g_win_button_states & ~buttons_valid) | + (button_states & buttons_valid); + if(g_win_warp_pointer && (raw_x == g_win_warp_x) && + (raw_y == g_win_warp_y) && (!buttons_changed) ) { + /* tell adb routs to recenter but ignore this motion */ + adb_update_mouse(kimage_ptr, x, y, 0, -1); + return 0; + } + return adb_update_mouse(kimage_ptr, x, y, button_states, + buttons_valid & 7); +} + +void +win_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + Window_info *win_info_ptr; + word32 flags; + int buttons, x, y, hide, warp; + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + + flags = (word32)wParam; + x = LOWORD(lParam); + y = HIWORD(lParam); + + buttons = (flags & 1) | (((flags >> 1) & 1) << 2) | + (((flags >> 4) & 1) << 1); +#if 0 + printf("Mouse at %d, %d fl: %08x, but: %d\n", x, y, flags, buttons); +#endif + win_info_ptr->motion |= win_update_mouse(win_info_ptr, x, y, buttons, + 7); + + hide = 0; + warp = 0; + hide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp); + if(warp != g_win_warp_pointer) { + win_info_ptr->motion = 1; + } + g_win_warp_pointer = warp; + if(g_win_hide_pointer != hide) { + win_hide_pointer(win_info_ptr, hide); + } + g_win_hide_pointer = hide; +} + +void +win_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down) +{ + Window_info *win_info_ptr; + Kimage *kimage_ptr; + word32 vk, raw_vk, flags, capslock_state; + int a2code, is_up; + int i; + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + kimage_ptr = win_info_ptr->kimage_ptr; + raw_vk = (word32)wParam; + flags = HIWORD(lParam); +#if 0 + printf("win_event_key: raw:%04x lParam:%08x d:%d flags:%08x\n", + raw_vk, (word32)lParam, down, flags); +#endif + + if((flags & 0x4000) && down) { + /* auto-repeating, just ignore it */ + return; + } + + vk = raw_vk + (flags & 0x100); +#if 0 + printf("Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\n", + vk, down, repeat, flags); +#endif + + /* remap a few keys here.. sigh */ + if((vk & 0xff) == VK_APPS) { + /* remap to command */ + vk = VK_MENU; + } + + if((vk & 0xff) == VK_CAPITAL) { + // Fix up capslock info: Windows gives us a down, then up event + // when the capslock key itself is pressed and released. We + // need to ask for the true toggle state instead + capslock_state = (GetKeyState(VK_CAPITAL) & 1); + down = capslock_state; + } + + /* search a2key_to_wsym to find wsym in col 1 or 2 */ + i = 0; + is_up = !down; + for(i = g_num_a2_keycodes-1; i >= 0; i--) { + a2code = g_a2_key_to_wsym[i][0]; + if((vk == g_a2_key_to_wsym[i][1]) || + (vk == g_a2_key_to_wsym[i][2])) { + vid_printf("Found vk:%04x = %02x\n", vk, a2code); + adb_physical_key_update(kimage_ptr, a2code, 0, is_up); + return; + } + } + printf("VK: %04x unknown\n", vk); +} + +void +win_event_redraw(HWND hwnd) +{ + Window_info *win_info_ptr; + + win_info_ptr = win_find_win_info_ptr(hwnd); + + if(win_info_ptr) { + video_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1); + } +} + +void +win_event_destroy(HWND hwnd) +{ + Window_info *win_info_ptr; + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(win_info_ptr == 0) { + return; + } + video_set_active(win_info_ptr->kimage_ptr, 0); + win_info_ptr->active = 0; + if(win_info_ptr == &g_mainwin_info) { + my_exit(0); + } else { + ShowWindow(win_info_ptr->win_hwnd, SW_HIDE); + ReleaseDC(hwnd, win_info_ptr->win_dc); + DeleteDC(win_info_ptr->win_cdc); + DeleteObject(win_info_ptr->win_dev_handle); + GlobalFree(win_info_ptr->win_bmapinfo_ptr); + win_info_ptr->win_hwnd = 0; + win_info_ptr->data_ptr = 0; + } +} + +void +win_event_move(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + Window_info *win_info_ptr; + int x_xpos, x_ypos; + + // These WM_MOVE events indicate the window is being moved + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + // printf("WM_MOVE: %04x %08x\n", (word32)wParam, (word32)lParam); + x_xpos = lParam & 0xffff; + x_ypos = (lParam >> 16) & 0xffff; + video_update_xpos_ypos(win_info_ptr->kimage_ptr, x_xpos, x_ypos); +} + +void +win_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + Window_info *win_info_ptr; + int width, height; + + // These WM_SIZE events indicate the window is being resized + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + // printf("WM_SIZE: %04x %08x\n", (word32)wParam, (word32)lParam); + width = lParam & 0xffff; + height = (lParam >> 16) & 0xffff; + video_update_scale(win_info_ptr->kimage_ptr, width, height, 0); +#if 0 + printf("Frac width: %f\n", + win_info_ptr->kimage_ptr->scale_width_a2_to_x / 65536.0); +#endif + + // The following try to do "live updating" of the resize + win_info_ptr->kimage_ptr->x_refresh_needed = 1; + x_update_display(win_info_ptr); +} + +void +win_event_minmaxinfo(HWND hwnd, LPARAM lParam) +{ + Window_info *win_info_ptr; + MINMAXINFO *minmax_ptr; + int a2_width, a2_height; + + // Windows sends WM_GETMINMAXINFO events when resizing is occurring, + // and we can modify the *lParam MINMAXINFO structure to set the + // minimum and maximum Track size (the size of the window) + // This code forces the minimum to be the A2 window size, and the + // maximum to be the screen size + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + minmax_ptr = (MINMAXINFO *)lParam; +#if 0 + printf("MinMax: mintrack.x:%d, mintrack.y:%d\n", + minmax_ptr->ptMinTrackSize.x, + minmax_ptr->ptMinTrackSize.y); +#endif + a2_width = video_get_a2_width(win_info_ptr->kimage_ptr); + a2_height = video_get_a2_height(win_info_ptr->kimage_ptr); + minmax_ptr->ptMinTrackSize.x = a2_width + win_info_ptr->extra_width; + minmax_ptr->ptMinTrackSize.y = a2_height + win_info_ptr->extra_height; + minmax_ptr->ptMaxTrackSize.x = g_win_max_width + + win_info_ptr->extra_width; + minmax_ptr->ptMaxTrackSize.y = g_win_max_height + + win_info_ptr->extra_height; +} + +void +win_event_focus(HWND hwnd, int gain_focus) +{ + Window_info *win_info_ptr; + word32 c025_val, info; + + win_info_ptr = win_find_win_info_ptr(hwnd); + if(!win_info_ptr) { + return; + } + + if(gain_focus) { + // printf("Got focus on %p\n", hwnd); + // Get shift, ctrl, capslock state + c025_val = 0; + info = GetKeyState(VK_SHIFT); // left or right + if(info & 0x8000) { + c025_val |= 1; // Shift key is down + } + info = GetKeyState(VK_CONTROL); // left or right + if(info & 0x8000) { + c025_val |= 2; + } + info = GetKeyState(VK_CAPITAL); // Capslock? + if(info & 1) { + c025_val |= 4; // Capslock key is down + } + //printf("Calling update_c025 with %03x\n", c025_val); + adb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7); + } else { + // printf("Lost focus on %p\n", hwnd); + } + if(win_info_ptr == &g_mainwin_info) { + adb_kbd_repeat_off(); + adb_mainwin_focus(gain_focus); + } +} + +LRESULT CALLBACK +win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) +{ + +#if 0 + printf("Message: umsg: %04x, wparam:%04x lParam:%08x\n", umsg, + (word32)wParam, (word32)lParam); +#endif + + switch(umsg) { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + win_event_mouse(hwnd, wParam, lParam); + return 0; + case WM_KEYUP: + case WM_SYSKEYUP: + win_event_key(hwnd, wParam, lParam, 0); + return 0; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + win_event_key(hwnd, wParam, lParam, 1); + return 0; + case WM_SYSCOMMAND: + // Alt key press can cause this. Return 0 for SC_KEYMENU + if(wParam == SC_KEYMENU) { + return 0; + } + break; + case WM_KILLFOCUS: + win_event_focus(hwnd, 0); + break; + case WM_SETFOCUS: + win_event_focus(hwnd, 1); + break; + case WM_DESTROY: + win_event_destroy(hwnd); + return 0; + case WM_PAINT: + win_event_redraw(hwnd); + break; + case WM_MOVE: + win_event_move(hwnd, wParam, lParam); + break; + case WM_SIZE: + win_event_size(hwnd, wParam, lParam); + break; + case WM_GETMINMAXINFO: + win_event_minmaxinfo(hwnd, lParam); + break; + } +#if 0 + switch(umsg) { + HANDLE_MSG(hwnd, WM_KEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_DESTROY, win_event_destroy); + } +#endif + +#if 0 + switch(umsg) { + case WM_NCACTIVATE: + case WM_NCHITTEST: + case WM_NCMOUSEMOVE: + case WM_SETCURSOR: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_PAINT: + + break; + default: + printf("Got umsg2: %d\n", umsg); + } +#endif + + return DefWindowProc(hwnd, umsg, wParam, lParam); +} + + +int +main(int argc, char **argv) +{ + int ret, mdepth; + + ret = parse_argv(argc, argv, 1); + if(ret) { + printf("parse_argv ret: %d, stopping\n", ret); + exit(1); + } + + mdepth = 32; + + video_set_blue_mask(0x0000ff); + video_set_green_mask(0x00ff00); + video_set_red_mask(0xff0000); + + g_win_max_width = GetSystemMetrics(SM_CXSCREEN); + g_win_max_height = GetSystemMetrics(SM_CYSCREEN); + vid_printf("g_win_max_width:%d, g_win_max_height:%d\n", + g_win_max_width, g_win_max_height); + + ret = kegs_init(mdepth, g_win_max_width, g_win_max_height, 0); + printf("kegs_init done\n"); + if(ret) { + printf("kegs_init ret: %d, stopping\n", ret); + exit(1); + } + + win_video_init(mdepth); + + printf("Entering main loop!\n"); + fflush(stdout); + while(1) { + ret = run_16ms(); + if(ret != 0) { + printf("run_16ms returned: %d\n", ret); + break; + } + x_update_display(&g_mainwin_info); + x_update_display(&g_debugwin_info); + check_input_events(); + } + xdriver_end(); + exit(0); +} + +void +check_input_events() +{ + MSG msg; + POINT pt; + BOOL ret; + Window_info *win_info_ptr; + + while(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) { + if(GetMessage(&msg, 0, 0, 0) > 0) { + //TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + printf("GetMessage returned <= 0\n"); + my_exit(2); + } + } + + win_info_ptr = &g_mainwin_info; + if(win_info_ptr->motion == 0) { + return; + } + + // ONLY look at g_mainwin_info! + win_info_ptr->motion = 0; + + if(g_win_warp_pointer) { + /* move mouse to center of screen */ + g_win_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr, + BASE_MARGIN_LEFT + (A2_WINDOW_WIDTH/2), 0); + g_win_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr, + BASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0); + pt.x = g_win_warp_x; + pt.y = g_win_warp_y; + ClientToScreen(win_info_ptr->win_hwnd, &pt); + ret = SetCursorPos(pt.x, pt.y); +#if 0 + printf("Did SetCursorPos(%d, %d) warp_x:%d,y:%d, ret:%d\n", + pt.x, pt.y, g_win_warp_x, g_win_warp_y, ret); +#endif + } + + return; +} + +void +win_video_init(int mdepth) +{ + WNDCLASS wndclass; + int a2code; + int i; + + video_set_palette(); + + g_num_a2_keycodes = 0; + for(i = 0; i < 0x7f; i++) { + a2code = g_a2_key_to_wsym[i][0]; + if(a2code < 0) { + g_num_a2_keycodes = i; + break; + } + } + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)win_event_handler; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = "kegswin"; + + // Register the window + if(!RegisterClass(&wndclass)) { + printf("Registering window failed\n"); + exit(1); + } + + win_init_window(&g_mainwin_info, video_get_kimage(0), "KEGS", mdepth); + win_init_window(&g_debugwin_info, video_get_kimage(1), + "KEGS Debugger", mdepth); + + win_create_window(&g_mainwin_info); +} + +void +win_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str, + int mdepth) +{ + int height, width, x_xpos, x_ypos; + + height = video_get_x_height(kimage_ptr); + width = video_get_x_width(kimage_ptr); + + x_xpos = video_get_x_xpos(kimage_ptr); + x_ypos = video_get_x_ypos(kimage_ptr); + + win_info_ptr->win_hwnd = 0; + win_info_ptr->win_dc = 0; + win_info_ptr->win_cdc = 0; + win_info_ptr->win_bmapinfo_ptr = 0; + win_info_ptr->win_bmaphdr_ptr = 0; + win_info_ptr->win_dev_handle = 0; + win_info_ptr->kimage_ptr = kimage_ptr; + win_info_ptr->name_str = name_str; + win_info_ptr->data_ptr = 0; + win_info_ptr->motion = 0; + win_info_ptr->mdepth = mdepth; + win_info_ptr->active = 0; + win_info_ptr->pixels_per_line = width; + win_info_ptr->x_xpos = x_xpos; + win_info_ptr->x_ypos = x_ypos; + win_info_ptr->width = width; + win_info_ptr->height = height; +} + +void +win_create_window(Window_info *win_info_ptr) +{ + HWND win_hwnd; + RECT rect; + BITMAPINFO *bmapinfo_ptr; + BITMAPINFOHEADER *bmaphdr_ptr; + HBITMAP win_dev_handle; + Kimage *kimage_ptr; + int height, width, extra_width, extra_height; + int extra_size, w_flags; + + kimage_ptr = win_info_ptr->kimage_ptr; + + height = win_info_ptr->height; + width = win_info_ptr->width; + + printf("Got height: %d, width:%d\n", height, width); + + // We must call CreateWindow with a width,height that accounts for + // the title bar and any other stuff. Use AdjustWindowRect() to + // calculate this info for us + w_flags = WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_SIZEBOX; + rect.left = 0; + rect.top = 0; + rect.right = width; + rect.bottom = height; + (void)AdjustWindowRect(&rect, w_flags, 0); + extra_width = rect.right - rect.left - width; + extra_height = rect.bottom - rect.top - height; + win_info_ptr->extra_width = extra_width; + win_info_ptr->extra_height = extra_height; + win_hwnd = CreateWindow("kegswin", win_info_ptr->name_str, w_flags, + win_info_ptr->x_xpos, win_info_ptr->x_ypos, width + extra_width, + height + extra_height, NULL, NULL, GetModuleHandle(NULL), NULL); + win_info_ptr->win_hwnd = win_hwnd; + win_info_ptr->active = 0; + + video_set_active(kimage_ptr, 1); + video_update_scale(kimage_ptr, win_info_ptr->width, + win_info_ptr->height, 1); + + printf("win_hwnd = %p, height = %d\n", win_hwnd, height); + GetWindowRect(win_hwnd, &rect); + printf("...rect is: %ld, %ld, %ld, %ld\n", rect.left, rect.top, + rect.right, rect.bottom); + + win_info_ptr->win_dc = GetDC(win_hwnd); + + SetTextColor(win_info_ptr->win_dc, 0); + SetBkColor(win_info_ptr->win_dc, 0xffffff); + + win_info_ptr->win_cdc = CreateCompatibleDC(win_info_ptr->win_dc); + printf("win_cdc: %p, win_dc:%p\n", win_info_ptr->win_cdc, + win_info_ptr->win_dc); + + + printf("Getting height, kimage_ptr:%p\n", kimage_ptr); + fflush(stdout); + + win_info_ptr->data_ptr = 0; + + extra_size = sizeof(RGBQUAD); + bmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR, + sizeof(BITMAPINFOHEADER) + extra_size); + win_info_ptr->win_bmapinfo_ptr = bmapinfo_ptr; + + bmaphdr_ptr = (BITMAPINFOHEADER *)bmapinfo_ptr; + win_info_ptr->win_bmaphdr_ptr = bmaphdr_ptr; + bmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER); + bmaphdr_ptr->biWidth = g_win_max_width; + bmaphdr_ptr->biHeight = -g_win_max_height; + bmaphdr_ptr->biPlanes = 1; + bmaphdr_ptr->biBitCount = win_info_ptr->mdepth; + bmaphdr_ptr->biCompression = BI_RGB; + bmaphdr_ptr->biClrUsed = 0; + + /* Use g_bmapinfo_ptr, adjusting width, height */ + printf("bmaphdr_ptr:%p\n", bmaphdr_ptr); + + printf("About to call CreateDIBSection, win_dc:%p\n", + win_info_ptr->win_dc); + fflush(stdout); + win_dev_handle = CreateDIBSection(win_info_ptr->win_dc, + win_info_ptr->win_bmapinfo_ptr, DIB_RGB_COLORS, + (VOID **)&(win_info_ptr->data_ptr), NULL, 0); + win_info_ptr->win_dev_handle = win_dev_handle; + + win_info_ptr->pixels_per_line = g_win_max_width; + printf("kim: %p, dev:%p data: %p\n", kimage_ptr, + win_dev_handle, win_info_ptr->data_ptr); + fflush(stdout); +} + +void +xdriver_end() +{ + printf("xdriver_end\n"); +} + +void +win_resize_window(Window_info *win_info_ptr) +{ + RECT rect1, rect2; + BOOL ret; + Kimage *kimage_ptr; + int x_width, x_height; + + kimage_ptr = win_info_ptr->kimage_ptr; + x_width = video_get_x_width(kimage_ptr); + x_height = video_get_x_height(kimage_ptr); + // printf("win_resize_window, x_w:%d, x_h:%d\n", x_width, x_height); + + ret = GetWindowRect(win_info_ptr->win_hwnd, &rect1); + ret = GetClientRect(win_info_ptr->win_hwnd, &rect2); +#if 0 + printf("window_rect: l:%d, t:%d, r:%d, b:%d\n", + rect1.left, rect1.top, rect1.right, rect1.bottom); + printf("client_rect: l:%d, t:%d, r:%d, b:%d\n", + rect2.left, rect2.top, rect2.right, rect2.bottom); +#endif + ret = MoveWindow(win_info_ptr->win_hwnd, rect1.left, rect1.top, + x_width + win_info_ptr->extra_width, + x_height + win_info_ptr->extra_height, TRUE); + // printf("MoveWindow ret:%d\n", ret); + win_info_ptr->width = x_width; + win_info_ptr->height = x_height; +} + +void +x_update_display(Window_info *win_info_ptr) +{ + Change_rect rect; + void *bitm_old; + //POINT point; + int valid, a2_active, x_active; + int i; + + a2_active = video_get_active(win_info_ptr->kimage_ptr); + x_active = win_info_ptr->active; + + if(x_active && !a2_active) { + // We need to SW_HIDE this window + ShowWindow(win_info_ptr->win_hwnd, SW_HIDE); + x_active = 0; + win_info_ptr->active = x_active; + } + if(!x_active && a2_active) { + // We need to SW_SHOWDEFAULT this window (and maybe create it) + if(win_info_ptr->win_hwnd == 0) { + win_create_window(win_info_ptr); + } + ShowWindow(win_info_ptr->win_hwnd, SW_SHOWDEFAULT); + UpdateWindow(win_info_ptr->win_hwnd); + x_active = 1; + win_info_ptr->active = x_active; + } + if(x_active == 0) { + return; + } + + if(video_change_aspect_needed(win_info_ptr->kimage_ptr, + win_info_ptr->width, win_info_ptr->height)) { + win_resize_window(win_info_ptr); + } + for(i = 0; i < MAX_CHANGE_RECTS; i++) { + valid = video_out_data(win_info_ptr->data_ptr, + win_info_ptr->kimage_ptr, win_info_ptr->pixels_per_line, + &rect, i); + if(!valid) { + break; + } +#if 0 + point.x = 0; + point.y = 0; + ClientToScreen(win_info_ptr->win_hwnd, &point); +#endif + bitm_old = SelectObject(win_info_ptr->win_cdc, + win_info_ptr->win_dev_handle); + + BitBlt(win_info_ptr->win_dc, rect.x, rect.y, rect.width, + rect.height, win_info_ptr->win_cdc, rect.x, rect.y, + SRCCOPY); + + SelectObject(win_info_ptr->win_cdc, bitm_old); + } +} + +void +x_hide_pointer(int do_hide) +{ + if(do_hide) { + ShowCursor(0); + } else { + ShowCursor(1); + } +} + +int +opendir_int(DIR *dirp, const char *in_filename) +{ + HANDLE handle1; + wchar_t *wcstr; + char *filename; + size_t ret_val; + int buflen, len; + int i; + + printf("opendir on %s\n", in_filename); + len = (int)strlen(in_filename); + buflen = len + 8; + if(buflen >= sizeof(dirp->dirent.d_name)) { + printf("buflen %d >= d_name %d\n", buflen, + (int)sizeof(dirp->dirent.d_name)); + return 1; + } + filename = &dirp->dirent.d_name[0]; + memcpy(filename, in_filename, len + 1); + while(len && (filename[len-1] == '/')) { + filename[len - 1] = 0; + len--; + } + cfg_strlcat(filename, "/*.*", buflen); + for(i = 0; i < len; i++) { + if(filename[i] == '/') { + filename[i] = '\\'; + } + } + len = (int)strlen(filename); + wcstr = malloc(buflen * 2); + (void)mbstowcs_s(&ret_val, wcstr, buflen, filename, _TRUNCATE); + handle1 = FindFirstFileW(wcstr, dirp->find_data_ptr); + dirp->win_handle = handle1; + free(wcstr); + if(handle1) { + dirp->find_data_valid = 1; + return 0; + } + return 1; +} + +DIR * +opendir(const char *in_filename) +{ + DIR *dirp; + int ret; + + dirp = calloc(1, sizeof(DIR)); + if(!dirp) { + return 0; + } + dirp->find_data_valid = 1; + dirp->find_data_ptr = calloc(1, sizeof(WIN32_FIND_DATAW)); + ret = 1; + if(dirp->find_data_ptr) { + ret = opendir_int(dirp, in_filename); + } + if(ret) { // Bad + free(dirp->find_data_ptr); // free(0) is OK + free(dirp); + return 0; + } + return dirp; +} + +struct dirent * +readdir(DIR *dirp) +{ + WIN32_FIND_DATAW *find_data_ptr; + HANDLE handle1; + size_t ret_val; + BOOL ret; + + handle1 = dirp->win_handle; + find_data_ptr = dirp->find_data_ptr; + if(!handle1 || !find_data_ptr) { + return 0; + } + ret = 1; + if(!dirp->find_data_valid) { + if(handle1) { + find_data_ptr->cFileName[MAX_PATH-1] = 0; + ret = FindNextFileW(handle1, find_data_ptr); + } + } + dirp->find_data_valid = 0; + if(!ret) { + return 0; + } + (void)wcstombs_s(&ret_val, &(dirp->dirent.d_name[0]), + (int)sizeof(dirp->dirent.d_name), + &(find_data_ptr->cFileName[0]), _TRUNCATE); + printf("Returning file %s\n", &(dirp->dirent.d_name[0])); + + return &(dirp->dirent);; +} + +int +closedir(DIR *dirp) +{ + FindClose(dirp->win_handle); + free(dirp->find_data_ptr); + free(dirp); + + return 0; +} + +int +lstat(const char *path, struct stat *bufptr) +{ + return stat(path, bufptr); +} + +int +ftruncate(int fd, word32 length) +{ + HANDLE handle1; + + handle1 = (HANDLE)_get_osfhandle(fd); + SetFilePointer(handle1, length, 0, FILE_BEGIN); + SetEndOfFile(handle1); + + return 0; +} + diff --git a/gsplus/src/woz.c b/gsplus/src/woz.c new file mode 100644 index 0000000..5ce482a --- /dev/null +++ b/gsplus/src/woz.c @@ -0,0 +1,1139 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +// Based on WOZ 2.0/1.0 spec at https://applesaucefdc.com/woz/reference2/ + +#include "defc.h" + +extern const char g_kegs_version_str[]; + +byte g_woz_hdr_bytes[12] = { 0x57, 0x4f, 0x5a, 0x32, // "WOZ2" + 0xff, 0x0a, 0x0d, 0x0a, 0, 0, 0, 0 }; + +word32 g_woz_crc32_tab[256]; + +void +woz_crc_init() +{ + word32 crc, val, xor; + int i, j; + + for(i = 0; i < 256; i++) { + crc = 0; + val = i; + for(j = 0; j < 8; j++) { + xor = 0; + if((val ^ crc) & 1) { + xor = 0xedb88320UL; + } + crc = (crc >> 1) ^ xor; + val = val >> 1; + } + g_woz_crc32_tab[i] = crc; + // printf("crc32_tab[%d] = %08x\n", i, crc); + } +} + +word32 +woz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip) +{ + word32 crc, c; + + crc = (~0U); + if(bytes_to_skip > dlen) { + dlen = 0; + } else { + bptr += bytes_to_skip; + dlen -= bytes_to_skip; + } + while(dlen != 0) { + c = *bptr++; + dlen--; + crc = g_woz_crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8); + } + return (~crc); +} + +void +woz_rewrite_crc(Disk *dsk, int min_write_size) +{ + Woz_info *wozinfo_ptr; + byte *wozptr; + word32 crc; + + // Recalculate WOZ image CRC and write it to disk + + wozinfo_ptr = dsk->wozinfo_ptr; + if(!wozinfo_ptr || (dsk->fd < 0)) { + return; + } + wozptr = wozinfo_ptr->wozptr; + crc = woz_calc_crc32(&wozptr[0], wozinfo_ptr->woz_size, 12); + cfg_set_le32(&wozptr[8], crc); + if(min_write_size < 12) { + min_write_size = 12; + } + cfg_write_to_fd(dsk->fd, wozptr, 0, min_write_size); +} + +void +woz_rewrite_lock(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + byte *wozptr; + int offset; + + wozinfo_ptr = dsk->wozinfo_ptr; + if(!wozinfo_ptr || (dsk->fd < 0)) { + return; + } + wozptr = wozinfo_ptr->wozptr; + offset = wozinfo_ptr->info_offset; + + wozptr[offset + 2] = (dsk->write_prot != 0); // Update locked + woz_rewrite_crc(dsk, offset + 2 + 1); +} + +void +woz_check_file(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + byte *wozptr, *newwozptr, *bptr; + word32 fdval, memval, crc, mem_crc, crcnew, file_size; + int woz_size; + int i; + + wozinfo_ptr = dsk->wozinfo_ptr; + if(!wozinfo_ptr) { + return; + } + woz_size = wozinfo_ptr->woz_size; + wozptr = wozinfo_ptr->wozptr; + crc = woz_calc_crc32(wozptr, woz_size, 12); + mem_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) | + (wozptr[11] << 24); + if(crc != mem_crc) { + halt_printf("WOZ CRC calc:%08x, from mem:%08x\n", crc, mem_crc); + } + if((dsk->fd < 0) || dsk->raw_data || !dsk->write_through_to_unix) { + printf("woz_check_file: CRC check done, cannot check fd\n"); + return; + } + + file_size = (word32)cfg_get_fd_size(dsk->fd); + if(file_size != (word32)woz_size) { + halt_printf("woz_size:%08x != file_size %08x\n", woz_size, + file_size); + if((word32)woz_size > file_size) { + woz_size = file_size; + } + } + newwozptr = malloc(woz_size); + cfg_read_from_fd(dsk->fd, newwozptr, 0, woz_size); + + for(i = 0; i < woz_size; i++) { + fdval = newwozptr[i]; + memval = wozptr[i]; + if(fdval == memval) { + continue; + } + halt_printf("byte %07x of %07x: mem %02x != %02x fd\n", i, + woz_size, memval, fdval); + } + + crcnew = woz_calc_crc32(newwozptr, woz_size, 12); + free(newwozptr); + printf("Woz check file complete. mem %08x vs fd %08x, freed %p\n", + crc, crcnew, newwozptr); + + bptr = dsk->cur_trk_ptr->raw_bptr; + printf("dsk->cur_trk_ptr->raw_bptr = %p. offset:%d\n", bptr, + (int)(bptr - wozptr)); +} + +void +woz_parse_meta(Disk *dsk, int offset, int size) +{ + Woz_info *wozinfo_ptr; + byte *wozptr, *bptr; + int c; + int i; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + if(wozinfo_ptr->meta_offset) { + printf("Bad WOZ file, 2 META chunks\n"); + wozinfo_ptr->woz_size = 0; + return; + } + wozinfo_ptr->meta_offset = offset; + wozinfo_ptr->meta_size = size; + printf("META field, %d bytes:\n", size); + bptr = &(wozptr[offset]); + for(i = 0; i < size; i++) { + c = bptr[i]; + if(c == 0) { + break; + } + putchar(c); + } + putchar('\n'); +} + +void +woz_parse_info(Disk *dsk, int offset, int size) +{ + byte new_buf[36]; + Woz_info *wozinfo_ptr; + byte *wozptr, *bptr; + int info_version, disk_type, write_protect, synchronized; + int cleaned, ram, largest_track; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + if(wozinfo_ptr->info_offset) { + printf("Two INFO chunks, bad WOZ file\n"); + wozinfo_ptr->woz_size = 0; + return; + } + wozinfo_ptr->info_offset = offset; + bptr = &(wozptr[offset]); + if(size < 60) { + printf("INFO field is %d, too short\n", size); + wozinfo_ptr->woz_size = 0; + return; + } + info_version = bptr[0]; // Only "1" or "2" is supported + disk_type = bptr[1]; // 1==5.25", 2=3.5" + write_protect = bptr[2]; // 1==write protected + synchronized = bptr[3]; // 1==cross track sync during imaging + cleaned = bptr[4]; // 1==MC3470 fake bits have been removed + memcpy(&new_buf[0], &(bptr[5]), 32); + new_buf[32] = 0; // Null terminate + printf("INFO, %d bytes. info_version:%d, disk_type:%d, wp:%d, sync:" + "%d, cleaned:%d\n", size, info_version, disk_type, + write_protect, synchronized, cleaned); + printf("Creator: %s\n", (char *)&new_buf[0]); + if(info_version >= 2) { + ram = bptr[42] + (bptr[43] << 8); + largest_track = (bptr[44] + (bptr[45] << 8)) * 512; + printf("Disk sides:%d, boot_format:%d bit_timing:%d, hw:" + "%02x%02x, ram:%d, largest_track:0x%07x\n", bptr[37], + bptr[38], bptr[39], bptr[41], bptr[40], ram, + largest_track); + } + + if(write_protect) { + printf("Write protected\n"); + dsk->write_prot = 1; + } +} + +void +woz_parse_tmap(Disk *dsk, int offset, int size) +{ + Woz_info *wozinfo_ptr; + byte *bptr, *wozptr; + int i; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + if(wozinfo_ptr->tmap_offset) { + printf("Second TMAP chunk, bad WOZ file!\n"); + wozinfo_ptr->woz_size = 0; + return; + } + wozinfo_ptr->tmap_offset = offset; + printf("TMAP field, %d bytes\n", size); + bptr = &(wozptr[offset]); + for(i = 0; i < 40; i++) { + printf("Track %2d.00: %02x, %2d.25:%02x %2d.50:%02x %2d.75:" + "%02x\n", i, bptr[0], i, bptr[1], + i, bptr[2], i, bptr[3]); + bptr += 4; + } +} + +void +woz_parse_trks(Disk *dsk, int offset, int size) +{ + Woz_info *wozinfo_ptr; + + printf("TRKS field, %d bytes, offset: %d\n", size, offset); + wozinfo_ptr = dsk->wozinfo_ptr; + if(wozinfo_ptr->trks_offset) { + printf("Second TRKS chunk, illegal Woz file\n"); + wozinfo_ptr->woz_size = 0; + return; + } + wozinfo_ptr->trks_offset = offset; + wozinfo_ptr->trks_size = size; +} + +int +woz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc) +{ + Woz_info *wozinfo_ptr; + Trk *trk; + byte *wozptr, *sync_ptr, *bptr; + word32 raw_bytes, num_bytes, trks_size, len_bits, offset, num_blocks; + word32 block; + int trks_offset; + int i; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + trks_offset = wozinfo_ptr->trks_offset; + trks_size = wozinfo_ptr->trks_size; + if(wozinfo_ptr->version == 1) { + offset = tmap * 6656; + if((offset + 6656) > trks_size) { + printf("Trk %d is out of range!\n", tmap); + return 0; + } + + offset = trks_offset + offset; + bptr = &(wozptr[offset]); + len_bits = bptr[6648] | (bptr[6649] << 8); + if(len_bits > (6656*8)) { + printf("Trk bits: %d too big\n", len_bits); + return 0; + } + } else { + bptr = &(wozptr[trks_offset + (tmap * 8)]); + // This is a TRK 8-byte structure + block = cfg_get_le16(&bptr[0]); // Starting Block + num_blocks = cfg_get_le16(&bptr[2]); // Block Count + len_bits = cfg_get_le32(&bptr[4]); // Bits Count +#if 0 + printf("qtr_track:%02x, block:%04x, num_blocks:%04x, " + "len_bits:%06x\n", qtr_track, block, num_blocks, + len_bits); + printf("File offset: %05lx\n", bptr - wozinfo_ptr->wozptr); +#endif + + if(block < 3) { + printf("block %04x is < 3\n", block); + return 0; + } + offset = (block * 512); // Offset from wozptr + if((offset + (num_blocks * 512)) > (trks_size + trks_offset)) { + printf("Trk %d is out of range!\n", tmap); + return 0; + } + bptr = &(wozptr[offset]); +#if 0 + printf("Qtr_track %03x offset:%06x, bptr:%p, trks_bptr:%p\n", + qtr_track, offset, bptr, trks_bptr); + printf(" len_bits:%d %06x bptr-wozptr: %07lx\n", len_bits, + len_bits, bptr - wozinfo_ptr->wozptr); +#endif + if(len_bits > (num_blocks * 512 * 8)) { + printf("Trk bits: %d too big\n", len_bits); + return 0; + } + } + + dsk->raw_bptr_malloc = 0; + + raw_bytes = (len_bits + 7) >> 3; + num_bytes = raw_bytes + 8; + trk = &(dsk->trks[qtr_track]); + trk->raw_bptr = bptr; + trk->dunix_pos = offset; + trk->unix_len = raw_bytes; + trk->dirty = 0; + trk->track_bits = len_bits; +#if 0 + printf("track %d.%d dunix_pos:%08llx\n", qtr_track >> 2, + (qtr_track & 3) * 25, trk->dunix_pos); +#endif + trk->sync_ptr = (byte *)malloc(num_bytes); + + dsk->cur_trk_ptr = 0; + iwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc); + sync_ptr = &(trk->sync_ptr[0]); + for(i = 0; i < (int)raw_bytes; i++) { + sync_ptr[i] = 0xff; + } + + iwm_recalc_sync_from(dsk, qtr_track, 0, dfcyc); + + if(qtr_track == 0) { + printf("Track 0 data begins: %02x %02x %02x, offset:%d\n", + bptr[0], bptr[1], bptr[2], offset); + } + for(i = 0; i < qtr_track; i++) { + trk = &(dsk->trks[i]); + if(trk->track_bits && (trk->raw_bptr == bptr)) { + // Multiple tracks point to the same woz track + // This is not allowed, reparse + wozinfo_ptr->reparse_needed = 1; + printf("Track %04x matchs track %04x, reparse needed\n", + qtr_track, i); + break; + } + } + + return 1; +} + +int +woz_parse_header(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + byte *wozptr, *bptr; + word32 chunk_id, size, woz_size; + int pos, version; + int i; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + woz_size = wozinfo_ptr->woz_size; + version = 2; + if(woz_size < 8) { + return 0; + } + for(i = 0; i < 8; i++) { + if(wozptr[i] != g_woz_hdr_bytes[i]) { + if(i == 3) { // Check for WOZ1 + if(wozptr[i] == 0x31) { // WOZ1 + version = 1; + continue; + } + } + printf("WOZ header[%d]=%02x, invalid\n", i, wozptr[i]); + return 0; + } + } + wozinfo_ptr->version = version; + + printf("WOZ version: %d\n", version); + pos = 12; + while(pos < (int)woz_size) { + bptr = &(wozptr[pos]); + chunk_id = bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) | + (bptr[3] << 24); + size = bptr[4] | (bptr[5] << 8) | (bptr[6] << 16) | + (bptr[7] << 24); + pos += 8; + printf("chunk_id: %08x, size:%08x\n", chunk_id, size); + if(((pos + size) > woz_size) || (size < 8) || + ((size >> 30) != 0)) { + return 0; + } + bptr = &(wozptr[pos]); + if(chunk_id == 0x4f464e49) { // "INFO" + woz_parse_info(dsk, pos, size); + } else if(chunk_id == 0x50414d54) { // "TMAP" + woz_parse_tmap(dsk, pos, size); + } else if(chunk_id == 0x534b5254) { // "TRKS" + woz_parse_trks(dsk, pos, size); + } else if(chunk_id == 0x4154454d) { // "META" + woz_parse_meta(dsk, pos, size); + } else { + printf("Chunk header %08x is unknown\n", chunk_id); + } + pos += size; + } + + return 1; // Good so far +} + +Woz_info * +woz_malloc(byte *wozptr, word32 woz_size) +{ + Woz_info *wozinfo_ptr; + + wozinfo_ptr = malloc(sizeof(Woz_info)); + printf("malloc wozinfo_ptr:%p\n", wozinfo_ptr); + if(!wozinfo_ptr) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + + wozinfo_ptr->wozptr = wozptr; + wozinfo_ptr->woz_size = woz_size; + wozinfo_ptr->version = 0; + wozinfo_ptr->reparse_needed = 0; + wozinfo_ptr->max_trk_blocks = 0; + wozinfo_ptr->meta_size = 0; + wozinfo_ptr->trks_size = 0; + wozinfo_ptr->tmap_offset = 0; + wozinfo_ptr->trks_offset = 0; + wozinfo_ptr->info_offset = 0; + wozinfo_ptr->meta_offset = 0; + + if(woz_size < 12) { + if(wozptr) { + free(wozptr); + } + wozptr = 0; + } + if(!wozptr) { + wozptr = malloc(12); + woz_append_bytes(wozptr, &g_woz_hdr_bytes[0], 12); + wozinfo_ptr->wozptr = wozptr; + wozinfo_ptr->woz_size = 12; + } + + return wozinfo_ptr; +} + +int +woz_reopen(Disk *dsk, dword64 dfcyc) +{ + byte act_tmap[160]; + Woz_info *wozinfo_ptr; + byte *wozptr, *tmap_bptr; + word32 tmap, prev_tmap, last_act; + int ret, num_tracks, num_match; + int i, j; + + wozinfo_ptr = dsk->wozinfo_ptr; + wozptr = wozinfo_ptr->wozptr; + + if(!wozinfo_ptr->tmap_offset) { + printf("No TMAP found\n"); + return 0; + } + if(!wozinfo_ptr->trks_offset) { + printf("No TRKS found\n"); + return 0; + } + if(!wozinfo_ptr->info_offset) { + printf("No INFO found\n"); + return 0; + } + if(wozinfo_ptr->woz_size == 0) { + printf("woz_size is 0!\n"); + return 0; + } + + tmap_bptr = wozptr + wozinfo_ptr->tmap_offset; + dsk->cur_fbit_pos = 0; + num_tracks = 4*35; + for(i = num_tracks; i < 160; i++) { + // See what the largest track is, go to track 39.50 + if(tmap_bptr[i] != 0xff) { + num_tracks = i + 1; + //printf("Will set num_tracks=%d\n", num_tracks); + } + } + dsk->fbit_mult = 128; // 5.25" multipler value + if(!dsk->disk_525) { + num_tracks = 160; + dsk->fbit_mult = 256; // 3.5" multipler value + } + disk_set_num_tracks(dsk, num_tracks); + for(i = 0; i < 160; i++) { + act_tmap[i] = 0xff; + } + for(i = 0; i < 160; i++) { + tmap = tmap_bptr[i]; + if(tmap >= 0xff) { + continue; // Skip + } + // WOZ format adds dup entries for adjacent qtr tracks, so + // track 2.0 has entries at 1.75 and 2.25 pointing to 2.0. + // KEGS doesn't want these, it handles this itself, so remove + // them. + if(dsk->disk_525) { + num_match = 1; + for(j = i + 1; j < 160; j++) { + if(tmap_bptr[j] != tmap) { + break; + } + num_match++; + } + // From i, num_match is the number of time tmap repeats + prev_tmap = 0xff; + last_act = 0xff; + if(i) { + prev_tmap = tmap_bptr[i - 1]; + last_act = act_tmap[i - 1]; + } else if(num_match == 3) { + // Weird case where WOZ starts with 0,0,0, we + // should treat track 0.25 as the real track + continue; + } + if(num_match >= 3) { + // long run of repeats, add a track every other + // one. + if(last_act == tmap) { // Just did trk + continue; + } + if(prev_tmap != tmap) { // Start of run + continue; + } + } else if(num_match == 2) { + // Handle A, B, [B], B, C and A, B, B, [B], B, C + // ALWAYS add this track + } else { // num_match==1 + if(prev_tmap == tmap) { + continue; + } + // Otherwise, a lone track, always add it + } + } + ret = woz_add_track(dsk, i, tmap, dfcyc); + if(ret == 0) { + printf("woz_add_track i:%04x tmap:%04x ret 0\n", i, + tmap); + return ret; + } + } + + return 1; // WOZ file is good! +} + +int +woz_open(Disk *dsk, dword64 dfcyc) +{ + Woz_info *wozinfo_ptr; + byte *wozptr; + dword64 doff; + word32 woz_size, crc, file_crc; + int fd, ret; + + // return 0 for bad WOZ file, 1 for success + // We set dsk->wozinfo_ptr, and caller will free it if we return 0 + printf("woz_open on file %s, write_prot:%d\n", dsk->name_ptr, + dsk->write_prot); + if(dsk->trks == 0) { + return 0; // Smartport? + } + if(dsk->raw_data) { + wozptr = dsk->raw_data; + woz_size = (word32)dsk->raw_dsize; + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } else { + fd = dsk->fd; + if(fd < 0) { + return 0; + } + woz_size = (word32)cfg_get_fd_size(fd); + printf("size: %d\n", woz_size); + + wozptr = malloc(woz_size); + doff = cfg_read_from_fd(fd, wozptr, 0, woz_size); + if(doff != woz_size) { + close(fd); + return 0; + } + } + + wozinfo_ptr = woz_malloc(wozptr, woz_size); + dsk->wozinfo_ptr = wozinfo_ptr; + + if(woz_size < 16) { + return 0; + } + crc = woz_calc_crc32(wozptr, woz_size, 12); + file_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) | + (wozptr[11] << 24); + if((crc != file_crc) && (file_crc != 0)) { + printf("Bad Woz CRC:%08x in file, calc:%08x\n", file_crc, crc); + return 0; + } + + ret = woz_parse_header(dsk); + printf("woz_parse_header ret:%d, write_prot:%d\n", ret, + dsk->write_prot); + if(ret == 0) { + return ret; + } + + ret = woz_reopen(dsk, dfcyc); + printf("woz_reopen ret:%d\n", ret); + + woz_maybe_reparse(dsk); + return ret; +} + +byte * +woz_append_bytes(byte *wozptr, byte *in_bptr, int len) +{ + int i; + + for(i = 0; i < len; i++) { + *wozptr++ = *in_bptr++; + } + + return wozptr; +} + +byte * +woz_append_word32(byte *wozptr, word32 val) +{ + int i; + + for(i = 0; i < 4; i++) { + *wozptr++ = val & 0xff; + val = val >> 8; + } + + return wozptr; +} + +int +woz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length, + byte *bptr) +{ + byte *wozptr, *new_wozptr, *save_wozptr; + word32 woz_size, new_size; + int offset; + + wozptr = wozinfo_ptr->wozptr; + woz_size = wozinfo_ptr->woz_size; + + new_size = woz_size + 4 + 4 + length; + new_wozptr = realloc(wozptr, new_size); + if(new_wozptr == 0) { + return 0; + } + wozptr = new_wozptr + woz_size; + wozptr = woz_append_word32(wozptr, chunk_id); + save_wozptr = woz_append_word32(wozptr, length); + wozptr = woz_append_bytes(save_wozptr, bptr, length); + + wozinfo_ptr->wozptr = new_wozptr; + wozinfo_ptr->woz_size = new_size; + offset = (int)(save_wozptr - new_wozptr); + switch(chunk_id) { + case 0x4f464e49: // "INFO" + wozinfo_ptr->info_offset = offset; + break; + case 0x50414d54: // "TMAP" + wozinfo_ptr->tmap_offset = offset; + break; + case 0x534b5254: // "TRKS" + wozinfo_ptr->trks_offset = offset; + wozinfo_ptr->trks_size = new_size; + break; + } + if(wozptr != (new_wozptr + new_size)) { + halt_printf("wozptr:%p != %p + %08x\n", wozptr, new_wozptr, + new_size); + return 0; + } + + return 1; +} + +byte * +woz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr, + word32 *num_blocks_ptr, dword64 *tmap_dptr) +{ + byte *new_bptr; + word32 num_blocks, blocks_start, this_blocks, size_bytes, track_bits; + int i; + + // Align trks_buf_size to 512 bytes + blocks_start = *num_blocks_ptr; + + track_bits = dsk->trks[trk_num].track_bits; + size_bytes = (dsk->trks[trk_num].track_bits + 7) >> 3; + this_blocks = (size_bytes + 511) >> 9; + if(wozinfo_ptr->max_trk_blocks < this_blocks) { + wozinfo_ptr->max_trk_blocks = this_blocks; + printf("max_trk_blocks=%d from trk %03x\n", this_blocks, + trk_num); + } + + num_blocks = blocks_start + this_blocks; + *num_blocks_ptr = num_blocks; + + *tmap_dptr = (((dword64)track_bits) << 32) | (this_blocks << 16) | + blocks_start; + + new_bptr = realloc(bptr, num_blocks << 9); + if(new_bptr == 0) { + return 0; + } + // Zero out last 512 byte block, to ensure a partial track has all 0's + // in the last block + for(i = 0; i < 512; i++) { + new_bptr[(num_blocks << 9) - 512 + i] = 0; + } + woz_append_bytes(new_bptr + (blocks_start << 9), + dsk->trks[trk_num].raw_bptr, size_bytes); + + return new_bptr; +} + +Woz_info * +woz_new_from_woz(Disk *dsk, int disk_525) +{ + dword64 tmap_dvals[160]; + int tmap_qtrk[160]; + byte buf[160]; + Woz_info *wozinfo_ptr, *in_wozinfo_ptr; + Trk *trkptr; + byte *in_wozptr, *wozptr, *trks_bufptr; + dword64 dval; + word32 type, woz_size, num_blocks, crc, track_bits; + int c, num_valid_tmap, pos, offset, raw_bytes; + int i, j; + + wozinfo_ptr = woz_malloc(0, 0); // New wozinfo + in_wozptr = 0; + in_wozinfo_ptr = 0; + if(dsk) { + in_wozinfo_ptr = dsk->wozinfo_ptr; + if(!in_wozinfo_ptr) { + halt_printf("Changing to WOZ format!\n"); + } + } + if(in_wozinfo_ptr) { + in_wozptr = in_wozinfo_ptr->wozptr; + } + printf(" START woz_new_from_woz, in_wozinfo_ptr:%p, in_wozptr:%p\n", + in_wozinfo_ptr, in_wozptr); + for(i = 0; i < 60; i++) { + buf[i] = 0; + } + if(in_wozptr) { + // Output the INFO chunk + memcpy(&buf[0], &in_wozptr[20], 60); + } else { + // Output an INFO chunk for KEGS + buf[3] = 1; // 1=synchronized tracks + buf[4] = 1; // 1=MC3470 fake bits removed + for(i = 0; i < 32; i++) { + buf[5 + i] = ' '; // Creator field + } + memcpy(&buf[5], "KEGS", 4); + // And put the revision number after it + for(i = 0; i < 20; i++) { + c = g_kegs_version_str[i]; + if(c == 0) { + break; + } + buf[5 + 5 + i] = c; + } + } + buf[0] = 2; // WOZ2 version + type = 2 - disk_525; // 1=5.25, 2=3.5 + buf[1] = type; + buf[2] = 0; // write_prot=0 + if(buf[37] == 0) { + buf[37] = type; // sides, re-use type + } + if(buf[39] == 0) { + buf[39] = 16 + 16 * (type & 1); // 5.25=32, 3.5=16 + } + woz_append_chunk(wozinfo_ptr, 0x4f464e49, 60, &buf[0]); // INFO + + // TMAP + for(i = 0; i < 160; i++) { + buf[i] = 0xff; + } + num_blocks = 6; // 1280 + 256 = 6x512 + trks_bufptr = malloc(num_blocks << 9); + printf("trk_bufptr = %p\n", trks_bufptr); + for(i = 0; i < (int)(num_blocks << 9); i++) { + trks_bufptr[i] = 0; + } + num_valid_tmap = 0; + if((dsk != 0) && (dsk->trks != 0)) { + // Output all valid tracks, and set the TMAP for adjacent .25 + for(i = 0; i < 160; i++) { + trkptr = &(dsk->trks[i]); + if(trkptr->track_bits >= 10000) { + buf[i] = num_valid_tmap; + if(i && (buf[i-1] == 0xff)) { + buf[i-1] = num_valid_tmap; + } + if((i < 159) && (trkptr[1].track_bits < 10000)){ + buf[i+1] = num_valid_tmap; + } + trks_bufptr = woz_append_a_trk(wozinfo_ptr, dsk, + i, trks_bufptr, &num_blocks, + &tmap_dvals[num_valid_tmap]); + tmap_qtrk[num_valid_tmap] = i; + printf("Did append, tmap:%04x qtrk:%04x dval:" + "%016llx\n", num_valid_tmap, i, + tmap_dvals[num_valid_tmap]); + num_valid_tmap++; + } + } + } + woz_append_chunk(wozinfo_ptr, 0x50414d54, 160, &buf[0]); // TMAP + + // TRKS. Already all 0 if there are no tracks. Fill in tmap_dvals[] + for(i = 0; i < num_valid_tmap; i++) { + pos = 256 + i*8; + dval = tmap_dvals[i]; + for(j = 0; j < 8; j++) { + trks_bufptr[pos + j] = dval & 0xff; + dval = dval >> 8; + } + } + woz_append_chunk(wozinfo_ptr, 0x534b5254, (num_blocks << 9) - 256, + trks_bufptr + 256); + // TRKS, 1280 minimum size + free(trks_bufptr); + + // Final META chunk...just remove, we've added or removed tracks from + // an original WOZ image, so the Meta data is no longer accurate + + wozptr = wozinfo_ptr->wozptr; + woz_size = wozinfo_ptr->woz_size; + + printf(" new wozptr:%p, woz_size:%08x\n", wozptr, woz_size); + wozptr[64] = wozinfo_ptr->max_trk_blocks; // largest track + + crc = woz_calc_crc32(wozptr, woz_size, 12); + cfg_set_le32(&wozptr[8], crc); + if(dsk) { + pos = 0; + for(i = 0; i < 160; i++) { + // Go through and fix up trks structure to match new WOZ + trkptr = &(dsk->trks[i]); + if((pos < num_valid_tmap) && (tmap_qtrk[pos] == i)) { + // This is a valid track + dval = tmap_dvals[pos]; + track_bits = dval >> 32; + offset = (dval & 0xffff) << 9; + + raw_bytes = (track_bits + 7) >> 3; + if(trkptr->unix_len == 0) { + free(trkptr->raw_bptr); + } + trkptr->raw_bptr = &wozptr[offset]; + trkptr->dunix_pos = offset; + trkptr->unix_len = raw_bytes; + trkptr->dirty = 0; + trkptr->track_bits = track_bits; + if(trkptr->sync_ptr == 0) { + halt_printf("sync_ptr 0 qtrk:%04x\n", + i); + } + pos++; + } else { + // No longer a valid track, free any ptrs + if(dsk->raw_bptr_malloc) { + free(trkptr->raw_bptr); + } + free(trkptr->sync_ptr); + trkptr->raw_bptr = 0; + trkptr->sync_ptr = 0; + trkptr->dunix_pos = 0; + trkptr->unix_len = 0; + trkptr->dirty = 0; + trkptr->track_bits = 0; + } + } + dsk->raw_bptr_malloc = 0; + + if(dsk->raw_data) { + free(dsk->raw_data); + dsk->raw_data = wozptr; + dsk->raw_dsize = woz_size; + dsk->dimage_start = 0; + dsk->dimage_size = woz_size; + in_wozptr = 0; + } + free(in_wozptr); + free(in_wozinfo_ptr); + if(in_wozinfo_ptr == 0) { + dsk->write_through_to_unix = 0; + halt_printf("Force write_through_to_unix since image " + "changed to WOZ format\n"); + } + } + + printf(" END woz_new_from_woz, wozinfo_ptr:%p wozptr:%p\n", + wozinfo_ptr, wozinfo_ptr->wozptr); + return wozinfo_ptr; +} + +int +woz_new(int fd, const char *str, int size_kb) +{ + Woz_info *wozinfo_ptr; + byte *wozptr; + word32 size, woz_size; + int disk_525; + + disk_525 = 0; + if(size_kb <= 140) { + disk_525 = 1; + } + wozinfo_ptr = woz_new_from_woz(0, disk_525); + + wozptr = wozinfo_ptr->wozptr; + woz_size = wozinfo_ptr->woz_size; + + + size = (word32)must_write(fd, wozptr, woz_size); + free(wozptr); + free(wozinfo_ptr); + if(size != woz_size) { + return -1; + } + if(str) { + // Avoid unused var warning + } + + return 0; +} + +void +woz_maybe_reparse(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + + wozinfo_ptr = dsk->wozinfo_ptr; + if(wozinfo_ptr) { + if(wozinfo_ptr->reparse_needed) { + woz_reparse_woz(dsk); + } + } +} + +void +woz_set_reparse(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + + wozinfo_ptr = dsk->wozinfo_ptr; + if(wozinfo_ptr) { + wozinfo_ptr->reparse_needed = 1; + } else { + woz_reparse_woz(dsk); + } +} + +void +woz_reparse_woz(Disk *dsk) +{ + Woz_info *wozinfo_ptr; + +#if 0 + printf("In woz_reparse_woz, showing track 0\n"); + + iwm_show_a_track(dsk, &(dsk->trks[0]), 0.0); + iwm_show_stats(3); +#endif + + wozinfo_ptr = woz_new_from_woz(dsk, dsk->disk_525); + // This wozinfo_ptr has reparse_needed==0 + + dsk->wozinfo_ptr = wozinfo_ptr; + if(!dsk->raw_data && dsk->write_through_to_unix) { + (void)!ftruncate(dsk->fd, wozinfo_ptr->woz_size); + (void)cfg_write_to_fd(dsk->fd, wozinfo_ptr->wozptr, 0, + wozinfo_ptr->woz_size); + printf("did ftruncate and write of WOZ to %s\n", dsk->name_ptr); + } + + // Need to recalculate dsk->cur_track_bits, cur_trk_ptr + dsk->cur_trk_ptr = 0; + iwm_move_to_ftrack(dsk, dsk->cur_frac_track, 0, 0); + +#if 0 + printf("End of woz_reparse_woz, showing track 0\n"); + iwm_show_a_track(dsk, &(dsk->trks[0]), 0.0); + iwm_show_stats(3); +#endif + + woz_check_file(dsk); + printf("woz_reparse_woz complete!\n"); +} + +void +woz_remove_a_track(Disk *dsk, word32 qtr_track) +{ + Trk *trkptr; + + printf("woz_remove_track: %s qtr_track:%03x\n", dsk->name_ptr, + qtr_track); + trkptr = &(dsk->trks[qtr_track]); + trkptr->track_bits = 0; // Track invalid + + woz_set_reparse(dsk); +} + +word32 +woz_add_a_track(Disk *dsk, word32 qtr_track) +{ + Trk *trkptr, *other_trkptr; + byte *bptr, *other_bptr, *sync_ptr, *other_sync_ptr; + word32 track_bits, val; + int raw_bytes; + int i; + + // Return track_bits for the new track + + trkptr = &(dsk->trks[qtr_track]); + + other_trkptr = 0; + if((qtr_track > 0) && (trkptr[-1].track_bits > 0)) { + // Copy this track + other_trkptr = trkptr - 1; + } else if((qtr_track < 159) && (trkptr[1].track_bits > 0)) { + other_trkptr = trkptr + 1; + } + other_trkptr = 0; // HACK + if(dsk->disk_525 && other_trkptr) { + // We're .25 tracks away from a valid track, copy it's data + track_bits = other_trkptr->track_bits; + raw_bytes = (track_bits + 7) >> 3; + trkptr->track_bits = track_bits; + trkptr->raw_bptr = malloc(raw_bytes + 8); + trkptr->sync_ptr = malloc(raw_bytes + 8); + printf(" add a track, copy bptr:%p sync_ptr:%p size:%08x\n", + trkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8); + bptr = trkptr->raw_bptr; + sync_ptr = trkptr->sync_ptr; + other_bptr = other_trkptr->raw_bptr; + other_sync_ptr = other_trkptr->sync_ptr; + for(i = 0; i < raw_bytes; i++) { + bptr[i] = other_bptr[i]; + sync_ptr[i] = other_sync_ptr[i]; + } + } else { + track_bits = iwm_get_default_track_bits(dsk, qtr_track); + raw_bytes = (track_bits + 7) >> 3; + trkptr->track_bits = track_bits; + trkptr->raw_bptr = malloc(raw_bytes + 8); + trkptr->sync_ptr = malloc(raw_bytes + 8); + printf(" add a track, raw_bptr:%p sync_ptr:%p size:%08x\n", + trkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8); + + bptr = trkptr->raw_bptr; + sync_ptr = trkptr->sync_ptr; + for(i = 0; i < raw_bytes; i++) { + val = ((i >> 6) ^ i) & 0x7f; + if(((val & 0xf0) == 0) || ((val & 0x0f) == 0)) { + val |= 0x21; + } + bptr[i] = val; + sync_ptr[i] = 0xff; + } + bptr[raw_bytes - 1] = 0; + + iwm_recalc_sync_from(dsk, qtr_track, 0, 0); + } + trkptr->dunix_pos = 0; + trkptr->unix_len = 0; // Mark as a newly created trk + trkptr->dirty = 0; + + printf("woz_add_new_track: %s qtr_track:%03x\n", dsk->name_ptr, + qtr_track); + woz_set_reparse(dsk); + + return track_bits; +} + diff --git a/gsplus/src/xdriver.c b/gsplus/src/xdriver.c new file mode 100644 index 0000000..4cfc084 --- /dev/null +++ b/gsplus/src/xdriver.c @@ -0,0 +1,1512 @@ + +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2025 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ + +# if !defined(__CYGWIN__) && !defined(__POWERPC__) +/* No shared memory on Cygwin */ +# define X_SHARED_MEM +#endif /* CYGWIN */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef X_SHARED_MEM +# include +# include +# include +#endif + +int XShmQueryExtension(Display *display); +void _XInitImageFuncPtrs(XImage *xim); + +#include "defc.h" + +extern int g_video_scale_algorithm; +extern int g_audio_enable; + +typedef struct windowinfo { + XShmSegmentInfo *seginfo; + XImage *xim; + Kimage *kimage_ptr; // KEGS Image pointer for window content + char *name_str; + Window x_win; + GC x_winGC; + Atom delete_atom; + int x_use_shmem; + int active; + int width_req; + int pixels_per_line; + int main_height; + int full_min_width; + int full_min_height; +} Window_info; + +#include "protos_xdriver.h" + +int g_x_warp_x = 0; +int g_x_warp_y = 0; +int g_x_warp_pointer = 0; +int g_x_hide_pointer = 0; + +extern int Verbose; +int g_x_screen_depth = 24; +int g_x_screen_mdepth = 32; +int g_x_max_width = 0; +int g_x_max_height = 0; +int g_screen_num = 0; + +extern int _Xdebug; + +int g_auto_repeat_on = -1; + +Display *g_display = 0; +Visual *g_vis = 0; +Colormap g_default_colormap = 0; + +Window_info g_mainwin_info = { 0 }; +Window_info g_debugwin_info = { 0 }; + +Cursor g_cursor; +Pixmap g_cursor_shape; +Pixmap g_cursor_mask; + +XColor g_xcolor_black = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue, 0 }; +XColor g_xcolor_white = { 0, 0xffff, 0xffff, 0xffff, DoRed|DoGreen|DoBlue, 0 }; + +const char *g_x_selection_strings[3] = { + // If we get SelectionRequests with target=Atom("TARGETS"), then + // send XA_STRING plus this list to say what we can provide for copy + "UTF8_STRING", "text/plain", "text/plain;charset=utf-8" +}; + +int g_x_num_targets = 0; +Atom g_x_targets_array[5] = { 0 }; + +Atom g_x_atom_targets = None; // Will be set to "TARGETS" + +int g_depth_attempt_list[] = { 24, 16, 15 }; + +#define X_EVENT_LIST_ALL_WIN \ + (ExposureMask | ButtonPressMask | ButtonReleaseMask | \ + OwnerGrabButtonMask | KeyPressMask | KeyReleaseMask | \ + KeymapStateMask | FocusChangeMask) + +#define X_BASE_WIN_EVENT_LIST \ + (X_EVENT_LIST_ALL_WIN | PointerMotionMask | ButtonMotionMask | \ + StructureNotifyMask) + +#define X_A2_WIN_EVENT_LIST \ + (X_BASE_WIN_EVENT_LIST) + +int g_num_a2_keycodes = 0; + +int g_x_a2_key_to_xsym[][3] = { + { 0x35, XK_Escape, 0 }, + { 0x7a, XK_F1, 0 }, + { 0x78, XK_F2, 0 }, + { 0x63, XK_F3, 0 }, + { 0x76, XK_F4, 0 }, + { 0x60, XK_F5, 0 }, + { 0x61, XK_F6, 0 }, + { 0x62, XK_F7, 0 }, + { 0x64, XK_F8, 0 }, + { 0x65, XK_F9, 0 }, + { 0x6d, XK_F10, 0 }, + { 0x67, XK_F11, 0 }, + { 0x6f, XK_F12, 0 }, + { 0x69, XK_F13, 0 }, + { 0x6b, XK_F14, 0 }, + { 0x71, XK_F15, 0 }, + { 0x7f, XK_Pause, XK_Break }, + { 0x32, '`', '~' }, /* Key number 18? */ + { 0x12, '1', '!' }, + { 0x13, '2', '@' }, + { 0x14, '3', '#' }, + { 0x15, '4', '$' }, + { 0x17, '5', '%' }, + { 0x16, '6', '^' }, + { 0x1a, '7', '&' }, + { 0x1c, '8', '*' }, + { 0x19, '9', '(' }, + { 0x1d, '0', ')' }, + { 0x1b, '-', '_' }, + { 0x18, '=', '+' }, + { 0x33, XK_BackSpace, 0 }, + { 0x72, XK_Insert, XK_Help }, /* Help? */ +/* { 0x73, XK_Home, 0 }, alias XK_Home to be XK_KP_Equal! */ + { 0x74, XK_Page_Up, 0 }, + { 0x47, XK_Num_Lock, XK_Clear }, /* Clear */ + { 0x51, XK_KP_Equal, XK_Home }, /* Note XK_Home alias! */ + { 0x4b, XK_KP_Divide, 0 }, + { 0x43, XK_KP_Multiply, 0 }, + + { 0x30, XK_Tab, 0 }, + { 0x0c, 'q', 'Q' }, + { 0x0d, 'w', 'W' }, + { 0x0e, 'e', 'E' }, + { 0x0f, 'r', 'R' }, + { 0x11, 't', 'T' }, + { 0x10, 'y', 'Y' }, + { 0x20, 'u', 'U' }, + { 0x22, 'i', 'I' }, + { 0x1f, 'o', 'O' }, + { 0x23, 'p', 'P' }, + { 0x21, '[', '{' }, + { 0x1e, ']', '}' }, + { 0x2a, 0x5c, '|' }, /* backslash, bar */ + { 0x75, XK_Delete, 0 }, + { 0x77, XK_End, 0 }, + { 0x79, XK_Page_Down, 0 }, + { 0x59, XK_KP_7, XK_KP_Home }, + { 0x5b, XK_KP_8, XK_KP_Up }, + { 0x5c, XK_KP_9, XK_KP_Page_Up }, + { 0x4e, XK_KP_Subtract, 0 }, + + { 0x39, XK_Caps_Lock, 0 }, + { 0x00, 'a', 'A' }, + { 0x01, 's', 'S' }, + { 0x02, 'd', 'D' }, + { 0x03, 'f', 'F' }, + { 0x05, 'g', 'G' }, + { 0x04, 'h', 'H' }, + { 0x26, 'j', 'J' }, + { 0x28, 'k', 'K' }, + { 0x25, 'l', 'L' }, + { 0x29, ';', ':' }, + { 0x27, 0x27, '"' }, /* single quote */ + { 0x24, XK_Return, 0 }, + { 0x56, XK_KP_4, XK_KP_Left }, + { 0x57, XK_KP_5, XK_KP_Begin }, + { 0x58, XK_KP_6, XK_KP_Right }, + { 0x45, XK_KP_Add, 0 }, + + { 0x38, XK_Shift_L, XK_Shift_R }, + { 0x06, 'z', 'Z' }, + { 0x07, 'x', 'X' }, + { 0x08, 'c', 'C' }, + { 0x09, 'v', 'V' }, + { 0x0b, 'b', 'B' }, + { 0x2d, 'n', 'N' }, + { 0x2e, 'm', 'M' }, + { 0x2b, ',', '<' }, + { 0x2f, '.', '>' }, + { 0x2c, '/', '?' }, + { 0x3e, XK_Up, 0 }, + { 0x53, XK_KP_1, XK_KP_End }, + { 0x54, XK_KP_2, XK_KP_Down }, + { 0x55, XK_KP_3, XK_KP_Page_Down }, + + { 0x36, XK_Control_L, XK_Control_R }, + { 0x3a, XK_Print, XK_Sys_Req }, /* Option */ + { 0x37, XK_Scroll_Lock, 0 }, /* Command */ + { 0x31, ' ', 0 }, + { 0x3b, XK_Left, 0 }, + { 0x3d, XK_Down, 0 }, + { 0x3c, XK_Right, 0 }, + { 0x52, XK_KP_0, XK_KP_Insert }, + { 0x41, XK_KP_Decimal, XK_KP_Separator }, + { 0x4c, XK_KP_Enter, 0 }, + { -1, -1, -1 } +}; + +int +main(int argc, char **argv) +{ + int ret, mdepth; + + ret = parse_argv(argc, argv, 1); + if(ret) { + printf("kegsmain ret: %d, stopping\n", ret); + exit(1); + } + + mdepth = x_video_get_mdepth(); + + ret = kegs_init(mdepth, g_x_max_width, g_x_max_height, 0); + printf("kegs_init done\n"); + if(ret) { + printf("kegs_init ret: %d, stopping\n", ret); + exit(1); + } + x_video_init(); + + // This is the main loop of KEGS, when this exits, KEGS exits + // run_16ms() does one video frame worth of instructions and video + // updates: 17030 1MHz clock cycles. + while(1) { + ret = run_16ms(); + if(ret != 0) { + printf("run_16ms returned: %d\n", ret); + break; + } + x_input_events(); + x_update_display(&g_mainwin_info); + x_update_display(&g_debugwin_info); + } + xdriver_end(); + exit(0); +} + +int +my_error_handler(Display *display, XErrorEvent *ev) +{ + char msg[1024]; + XGetErrorText(display, ev->error_code, msg, 1000); + printf("X Error code %s\n", msg); + fflush(stdout); + + return 0; +} + +void +xdriver_end() +{ + printf("xdriver_end\n"); + if(g_display) { + x_auto_repeat_on(1); + XFlush(g_display); + } +} + +void +x_try_xset_r() +{ + /* attempt "xset r" */ + (void)!system("xset r"); + xdriver_end(); + exit(5); +} + +void +x_badpipe(int signum) +{ + /* restore normal sigpipe handling */ + signal(SIGPIPE, SIG_DFL); + + x_try_xset_r(); +} + +int +kegs_x_io_error_handler(Display *display) +{ + printf("kegs_x_io_error_handler called (likely window closed)\n"); + g_display = 0; + x_try_xset_r(); + return 0; +} + +int +x_video_get_mdepth() +{ + XWindowAttributes get_attr; + int depth, len, ret, force_depth; + int i; + + printf("Preparing X Windows graphics system\n"); + ret = 0; + + signal(SIGPIPE, x_badpipe); + signal(SIGPIPE, x_badpipe); + +#if 0 + printf("Setting _Xdebug = 1, makes X synchronous\n"); + _Xdebug = 1; +#endif + + g_display = XOpenDisplay(NULL); + if(g_display == NULL) { + fprintf(stderr, "Can't open display\n"); + exit(1); + } + + vid_printf("Just opened display = %p\n", g_display); + fflush(stdout); + + g_screen_num = DefaultScreen(g_display); + + get_attr.width = 0; + get_attr.height = 0; + ret = XGetWindowAttributes(g_display, DefaultRootWindow(g_display), + &get_attr); + printf("XGetWindowAttributes ret: %d\n", ret); + g_x_max_width = get_attr.width; + g_x_max_height = get_attr.height; + printf("get_attr.width:%d, height:%d\n", g_x_max_width, g_x_max_height); + + len = sizeof(g_depth_attempt_list)/sizeof(int); + force_depth = sim_get_force_depth(); + if(force_depth > 0) { + /* Only use the requested user depth */ + len = 1; + g_depth_attempt_list[0] = force_depth; + } + + for(i = 0; i < len; i++) { + depth = g_depth_attempt_list[i]; + + g_x_screen_mdepth = x_try_find_visual(depth, g_screen_num); + if(g_x_screen_mdepth != 0) { + break; + } + } + if(g_x_screen_mdepth == 0) { + fprintf(stderr, "Couldn't find any visuals at any depth!\n"); + exit(2); + } + + return g_x_screen_mdepth; +} + +int +x_try_find_visual(int depth, int screen_num) +{ + XVisualInfo *visualList; + XVisualInfo *v_chosen; + XVisualInfo vTemplate; + int visualsMatched, visual_chosen, mdepth; + int i; + + vTemplate.screen = screen_num; + vTemplate.depth = depth; + + visualList = XGetVisualInfo(g_display, + (VisualScreenMask | VisualDepthMask), + &vTemplate, &visualsMatched); + + vid_printf("visuals matched: %d\n", visualsMatched); + if(visualsMatched == 0) { + return 0; + } + + visual_chosen = -1; + for(i = 0; i < visualsMatched; i++) { + printf("Visual %d\n", i); + printf(" id: %08x, screen: %d, depth: %d, class: %d\n", + (word32)visualList[i].visualid, + visualList[i].screen, + visualList[i].depth, + visualList[i].class); + printf(" red: %08lx, green: %08lx, blue: %08lx\n", + visualList[i].red_mask, + visualList[i].green_mask, + visualList[i].blue_mask); + printf(" cmap size: %d, bits_per_rgb: %d\n", + visualList[i].colormap_size, + visualList[i].bits_per_rgb); + if((depth != 8) && (visualList[i].class == TrueColor)) { + visual_chosen = i; + break; + } + } + + if(visual_chosen < 0) { + printf("Couldn't find any good visuals at depth %d!\n", + depth); + return 0; + } + + printf("Chose visual: %d\n", visual_chosen); + + v_chosen = &(visualList[visual_chosen]); + video_set_red_mask((word32)v_chosen->red_mask); + video_set_green_mask((word32)v_chosen->green_mask); + video_set_blue_mask((word32)v_chosen->blue_mask); + + video_set_palette(); // Uses above masks to initialize palettes + + g_x_screen_depth = depth; + mdepth = depth; + if(depth > 8) { + mdepth = 16; + } + if(depth > 16) { + mdepth = 32; + } + + // XFree(visualList); -- Cannot free, still using g_vis... + + g_vis = v_chosen->visual; + + return mdepth; +} + +void +x_video_init() +{ + int tmp_array[0x80]; + Atom target; + char cursor_data; + int keycode, num_targets, max_targets, num; + int i; + + printf("Opening X Window now\n"); + + g_num_a2_keycodes = 0; + for(i = 0; i <= 0x7f; i++) { + tmp_array[i] = 0; + } + for(i = 0; i < 0x7f; i++) { + keycode = g_x_a2_key_to_xsym[i][0]; + if(keycode < 0) { + g_num_a2_keycodes = i; + break; + } else if(keycode > 0x7f) { + printf("a2_key_to_xsym[%d] = %02x!\n", i, keycode); + exit(2); + } else { + if(tmp_array[keycode]) { + printf("a2_key_to_x[%d] = %02x used by %d\n", + i, keycode, tmp_array[keycode] - 1); + } + tmp_array[keycode] = i + 1; + } + } + + + g_default_colormap = XDefaultColormap(g_display, g_screen_num); + if(!g_default_colormap) { + printf("g_default_colormap == 0!\n"); + exit(4); + } + + /* and define cursor */ + cursor_data = 0; + g_cursor_shape = XCreatePixmapFromBitmapData(g_display, + RootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1); + g_cursor_mask = XCreatePixmapFromBitmapData(g_display, + RootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1); + + g_cursor = XCreatePixmapCursor(g_display, g_cursor_shape, + g_cursor_mask, &g_xcolor_black, &g_xcolor_white, 0, 0); + + XFreePixmap(g_display, g_cursor_shape); + XFreePixmap(g_display, g_cursor_mask); + + XFlush(g_display); + g_x_atom_targets = XInternAtom(g_display, "TARGETS", 0); + num = sizeof(g_x_selection_strings)/sizeof(g_x_selection_strings[0]); + g_x_targets_array[0] = XA_STRING; + num_targets = 1; + for(i = 0; i < num; i++) { + target = XInternAtom(g_display, g_x_selection_strings[i], 0); + if(target != None) { + g_x_targets_array[num_targets++] = target; + } + } + g_x_num_targets = num_targets; + max_targets = sizeof(g_x_targets_array)/sizeof(g_x_targets_array[0]); + if(num_targets > max_targets) { + printf("Overflowed g_x_targets_array: %d out of %d\n", + num_targets, max_targets); + exit(-1); + } + + x_init_window(&g_mainwin_info, video_get_kimage(0), "KEGS"); + x_init_window(&g_debugwin_info, video_get_kimage(1), "KEGS Debugger"); + + x_create_window(&g_mainwin_info); + + vid_printf("Set error handler to my_x_handler\n"); + XSetIOErrorHandler(kegs_x_io_error_handler); +} + +void +x_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str) +{ + int width, height, x_use_shmem, ret; + + height = video_get_x_height(kimage_ptr); + width = video_get_x_width(kimage_ptr); + + win_info_ptr->seginfo = 0; + win_info_ptr->xim = 0; + win_info_ptr->kimage_ptr = kimage_ptr; + win_info_ptr->name_str = name_str; + win_info_ptr->x_win = 0; + win_info_ptr->x_winGC = 0; + win_info_ptr->delete_atom = 0; + win_info_ptr->active = 0; + win_info_ptr->x_use_shmem = 0; + win_info_ptr->width_req = width; + win_info_ptr->pixels_per_line = width; + win_info_ptr->main_height = height; + win_info_ptr->full_min_width = width; + win_info_ptr->full_min_height = height; + vid_printf("init win %p (main:%p) width:%d, height:%d\n", win_info_ptr, + &g_mainwin_info, width, height); + + x_use_shmem = 0; + // Allow cmd-line args to force shmem off +/* Check for XShm */ +#ifdef X_SHARED_MEM + if(sim_get_use_shmem()) { + ret = XShmQueryExtension(g_display); + if(ret == 0) { + printf("XShmQueryExt ret: %d, not using shared mem\n", + ret); + } else { + vid_printf("Will use shared memory for X\n"); + x_use_shmem = 1; + } + } +#endif + win_info_ptr->x_use_shmem = x_use_shmem; + + video_update_scale(kimage_ptr, win_info_ptr->width_req, + win_info_ptr->main_height, 1); +} + +void +x_create_window(Window_info *win_info_ptr) +{ + XGCValues new_gc; + XSetWindowAttributes win_attr; + Window x_win; + GC x_winGC; + word32 create_win_list; + int width, height, x_xpos, x_ypos; + + win_attr.event_mask = X_A2_WIN_EVENT_LIST; + win_attr.colormap = g_default_colormap; + win_attr.backing_store = WhenMapped; + win_attr.border_pixel = 1; + win_attr.background_pixel = 0; + if(g_x_warp_pointer) { + win_attr.cursor = g_cursor; + } else { + win_attr.cursor = None; + } + + vid_printf("About to win, depth: %d\n", g_x_screen_depth); + fflush(stdout); + + create_win_list = CWEventMask | CWBackingStore | CWCursor; + create_win_list |= CWColormap | CWBorderPixel | CWBackPixel; + + x_xpos = video_get_x_xpos(win_info_ptr->kimage_ptr); + x_ypos = video_get_x_ypos(win_info_ptr->kimage_ptr); + width = win_info_ptr->width_req; + height = win_info_ptr->main_height; + + x_win = XCreateWindow(g_display, RootWindow(g_display, g_screen_num), + x_xpos, x_ypos, width, height, 0, g_x_screen_depth, + InputOutput, g_vis, create_win_list, &win_attr); + vid_printf("x_win = %d, width:%d, height:%d\n", (int)x_win, width, + height); + + XFlush(g_display); + + win_info_ptr->x_win = x_win; + win_info_ptr->active = 0; + video_set_active(win_info_ptr->kimage_ptr, 1); + + x_allocate_window_data(win_info_ptr); + + if(!win_info_ptr->x_use_shmem) { + printf("Not using shared memory, setting skip_amt = 2, " + "g_audio_enable=0\n"); + video_set_redraw_skip_amt(2); + g_audio_enable = 0; + } + + x_set_size_hints(win_info_ptr); + + XMapRaised(g_display, x_win); + + if(win_info_ptr != &g_mainwin_info) { + // Debugger window + win_info_ptr->delete_atom = XInternAtom(g_display, + "WM_DELETE_WINDOW", False); + XSetWMProtocols(g_display, x_win, &(win_info_ptr->delete_atom), + 1); + } + + XSync(g_display, False); + + x_winGC = XCreateGC(g_display, x_win, 0, (XGCValues *) 0); + win_info_ptr->x_winGC = x_winGC; + win_info_ptr->active = 1; + + new_gc.fill_style = FillSolid; + XChangeGC(g_display, x_winGC, GCFillStyle, &new_gc); + + /* XSync(g_display, False); */ + + XFlush(g_display); + fflush(stdout); +} + +int g_xshm_error = 0; + +int +xhandle_shm_error(Display *display, XErrorEvent *event) +{ + g_xshm_error = 1; + return 0; +} + +void +x_allocate_window_data(Window_info *win_info_ptr) +{ + int width, height; + + width = g_x_max_width; + height = g_x_max_height; + if(win_info_ptr->x_use_shmem) { + win_info_ptr->x_use_shmem = 0; // Default to no shmem + get_shm(win_info_ptr, width, height); + } + if(!win_info_ptr->x_use_shmem) { + get_ximage(win_info_ptr, width, height); + } +} + +void +get_shm(Window_info *win_info_ptr, int width, int height) +{ +#ifdef X_SHARED_MEM + XShmSegmentInfo *seginfo; + XImage *xim; + int (*old_x_handler)(Display *, XErrorEvent *); + int depth, size; + + depth = g_x_screen_depth; // 24, actual bits per pixel + + seginfo = (XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo)); + xim = XShmCreateImage(g_display, g_vis, depth, ZPixmap, + (char *)0, seginfo, width, height); + + /* check mdepth, which should be 32 */ + if(xim->bits_per_pixel != g_x_screen_mdepth) { + printf("get_shm bits_per_pix: %d != %d\n", + xim->bits_per_pixel, g_x_screen_mdepth); + } + vid_printf("xim: %p, DO_VERBOSE:%d, Verbose:%d, VERBOSE_VIDEO:%d\n", + xim, DO_VERBOSE, Verbose, VERBOSE_VIDEO); + + vid_printf("xim: %p\n", xim); + win_info_ptr->seginfo = seginfo; + if(xim == 0) { + return; + } + vid_printf("bytes_per_line:%d, height:%d\n", xim->bytes_per_line, + xim->height); + size = xim->bytes_per_line * xim->height; + vid_printf("size: %d\n", size); + + /* It worked, we got it */ + seginfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + vid_printf("seginfo->shmid = %d, errno:%d, %s\n", seginfo->shmid, + errno, strerror(errno)); + if(seginfo->shmid < 0) { + XDestroyImage(xim); + return; + } + + /* Still working */ + seginfo->shmaddr = (char *)shmat(seginfo->shmid, 0, 0); + vid_printf("seginfo->shmaddr: %p\n", seginfo->shmaddr); + if(seginfo->shmaddr == ((char *) -1)) { + XDestroyImage(xim); + return; + } + + /* Still working */ + xim->data = seginfo->shmaddr; + seginfo->readOnly = False; + vid_printf("xim->data is %p, size:%08x\n", xim->data, size); + + /* XShmAttach will trigger X error if server is remote, so catch it */ + g_xshm_error = 0; + old_x_handler = XSetErrorHandler(xhandle_shm_error); + + XShmAttach(g_display, seginfo); + XSync(g_display, False); + + + vid_printf("about to RMID the shmid\n"); + shmctl(seginfo->shmid, IPC_RMID, 0); + + XFlush(g_display); + XSetErrorHandler(old_x_handler); + + if(g_xshm_error) { + XDestroyImage(xim); + /* We could release the shared mem segment, but by doing the */ + /* RMID, it will go away when we die now, so just leave it */ + printf("Not using shared memory\n"); + return; + } + + width = xim->bytes_per_line; + win_info_ptr->pixels_per_line = width / 4; + win_info_ptr->xim = xim; + win_info_ptr->x_use_shmem = 1; + + vid_printf("Sharing memory. xim: %p, xim->data: %p, width:%d\n", xim, + xim->data, win_info_ptr->pixels_per_line); +#endif /* X_SHARED_MEM */ +} + +void +get_ximage(Window_info *win_info_ptr, int width, int height) +{ + XImage *xim; + byte *ptr; + int depth, mdepth, size; + + depth = g_x_screen_depth; + mdepth = g_x_screen_mdepth; + + size = (width * height * mdepth) >> 3; + printf("Get_ximage, w:%d, h:%d, mdepth:%d, size:%08x\n", width, + height, mdepth, size); + ptr = (byte *)malloc(size); + + vid_printf("ptr: %p\n", ptr); + + if(ptr == 0) { + printf("malloc for data failed, mdepth: %d\n", mdepth); + exit(2); + } + + win_info_ptr->pixels_per_line = width; + + xim = XCreateImage(g_display, g_vis, depth, ZPixmap, 0, + (char *)ptr, width, height, 8, 0); + +#ifdef KEGS_BIG_ENDIAN + xim->byte_order = MSBFirst; +#else + xim->byte_order = LSBFirst; +#endif + _XInitImageFuncPtrs(xim); /* adjust to new byte order */ + + /* check mdepth! */ + if(xim->bits_per_pixel != mdepth) { + printf("shm_ximage bits_per_pix: %d != %d\n", + xim->bits_per_pixel, mdepth); + } + + vid_printf("xim: %p\n", xim); + + win_info_ptr->xim = xim; + win_info_ptr->x_use_shmem = 0; + + return; +} + +void +x_set_size_hints(Window_info *win_info_ptr) +{ + XSizeHints my_winSizeHints; + XClassHint my_winClassHint; + XTextProperty my_winText; + Kimage *kimage_ptr; + int width, height, a2_width, a2_height; + + width = win_info_ptr->width_req; + height = win_info_ptr->main_height; + kimage_ptr = win_info_ptr->kimage_ptr; + video_update_scale(kimage_ptr, width, height, 0); + + a2_width = video_get_a2_width(kimage_ptr); + a2_height = video_get_a2_height(kimage_ptr); + + XStringListToTextProperty(&(win_info_ptr->name_str), 1, &my_winText); + + my_winSizeHints.flags = PSize | PMinSize | PMaxSize | PAspect; + my_winSizeHints.width = width; + my_winSizeHints.height = height; + my_winSizeHints.min_width = a2_width; + my_winSizeHints.min_height = a2_height; + my_winSizeHints.max_width = g_x_max_width; + my_winSizeHints.max_height = g_x_max_height; + my_winSizeHints.min_aspect.x = a2_width - 1; + my_winSizeHints.min_aspect.y = a2_height; + my_winSizeHints.max_aspect.x = a2_width + 1; + my_winSizeHints.max_aspect.y = a2_height; + my_winClassHint.res_name = win_info_ptr->name_str; + my_winClassHint.res_class = win_info_ptr->name_str; + + XSetWMProperties(g_display, win_info_ptr->x_win, &my_winText, + &my_winText, 0, 0, &my_winSizeHints, 0, &my_winClassHint); + // printf("Did XSetWMProperties w:%d h:%d\n", width, height); +} + +void +x_resize_window(Window_info *win_info_ptr) +{ + Kimage *kimage_ptr; + int x_width, x_height, ret; + + kimage_ptr = win_info_ptr->kimage_ptr; + x_width = video_get_x_width(kimage_ptr); + x_height = video_get_x_height(kimage_ptr); + + win_info_ptr->main_height = MY_MIN(x_height, g_x_max_height); + win_info_ptr->width_req = MY_MIN(x_width, g_x_max_width); + + ret = XResizeWindow(g_display, win_info_ptr->x_win, x_width, x_height); + if(0) { + printf("XResizeWindow ret:%d, w:%d, h:%d\n", ret, x_width, + x_height); + } +} + +void +x_update_display(Window_info *win_info_ptr) +{ + Change_rect rect; + int did_copy, valid, x_active, a2_active; + int i; + + // Update active state + a2_active = video_get_active(win_info_ptr->kimage_ptr); + x_active = win_info_ptr->active; + if(x_active && !a2_active) { + // We need to unmap this window + XUnmapWindow(g_display, win_info_ptr->x_win); + x_active = 0; + win_info_ptr->active = x_active; + } + if(!x_active && a2_active) { + // We need to map this window (and maybe create it) + if(win_info_ptr->xim == 0) { + x_create_window(win_info_ptr); + } + XMapWindow(g_display, win_info_ptr->x_win); + x_active = 1; + win_info_ptr->active = x_active; + } + if(x_active == 0) { + return; + } + + if(video_change_aspect_needed(win_info_ptr->kimage_ptr, + win_info_ptr->width_req, win_info_ptr->main_height)) { + x_resize_window(win_info_ptr); + } + did_copy = 0; + for(i = 0; i < MAX_CHANGE_RECTS; i++) { + valid = video_out_data(win_info_ptr->xim->data, + win_info_ptr->kimage_ptr, + win_info_ptr->pixels_per_line, &rect, i); + if(!valid) { + break; + } +#if 0 + if(win_info_ptr == &g_debugwin_info) { + printf(" i:%d valid:%d, w:%d h:%d\n", i, valid, + rect.width, rect.height); + } +#endif + did_copy = 1; +#ifdef X_SHARED_MEM + if(win_info_ptr->x_use_shmem) { + XShmPutImage(g_display, win_info_ptr->x_win, + win_info_ptr->x_winGC, + win_info_ptr->xim, rect.x, rect.y, + rect.x, rect.y, + rect.width, rect.height, False); + } +#endif + if(!win_info_ptr->x_use_shmem) { + XPutImage(g_display, win_info_ptr->x_win, + win_info_ptr->x_winGC, + win_info_ptr->xim, rect.x, rect.y, + rect.x, rect.y, + rect.width, rect.height); + } + } + + if(did_copy) { + XFlush(g_display); + } +} + +Window_info * +x_find_xwin(Window in_win) +{ + if(g_mainwin_info.kimage_ptr) { + if(g_mainwin_info.x_win == in_win) { + return &g_mainwin_info; + } + } + if(g_debugwin_info.kimage_ptr) { + if(g_debugwin_info.x_win == in_win) { + return &g_debugwin_info; + } + } + printf("in_win:%d not found\n", (int)in_win); + exit(1); +} + +#define KEYBUFLEN 128 + +int g_num_check_input_calls = 0; +int g_check_input_flush_rate = 2; + +// https://stackoverflow.com/questions/72236711/trouble-with-xsetselectionowner +// See answer from "n.m" on May 17th. +void +x_send_copy_data(Window_info *win_info_ptr) +{ + printf("x_send_copy_data!\n"); + XSetSelectionOwner(g_display, XA_PRIMARY, win_info_ptr->x_win, + CurrentTime); + (void)cfg_text_screen_str(); +} + +void +x_handle_copy(XSelectionRequestEvent *req_ev_ptr) +{ + byte *bptr; + int ret; + + bptr = (byte *)cfg_get_current_copy_selection(); + ret = XChangeProperty(g_display, req_ev_ptr->requestor, + req_ev_ptr->property, req_ev_ptr->target, 8, + PropModeReplace, bptr, strlen((char *)bptr)); + // req_ev_ptr->target is either XA_STRING, or equivalent + if(0) { + // Seems to return 1, BadRequest always, but it works + printf("XChangeProperty ret: %d\n", ret); + } +} + +void +x_handle_targets(XSelectionRequestEvent *req_ev_ptr) +{ + int ret; + + // Tell the other client what targets we can supply from + // g_x_targets_array[] + ret = XChangeProperty(g_display, req_ev_ptr->requestor, + req_ev_ptr->property, XA_ATOM, 32, + PropModeReplace, (byte *)&g_x_targets_array[0], + g_x_num_targets); + if(0) { + // Seems to return 1, BadRequest always, but it works + printf("XChangeProperty TARGETS ret: %d\n", ret); + } +} + +void +x_request_paste_data(Window_info *win_info_ptr) +{ + printf("Pasting selection\n"); + // printf("Calling XConvertSelection\n"); + XConvertSelection(g_display, XA_PRIMARY, XA_STRING, XA_STRING, + win_info_ptr->x_win, CurrentTime); + // This will cause a SelectionNotify event, and we get the data + // by using XGetWindowProperty on our own window. This will eventually + // call x_handle_paste(). +} + +void +x_handle_paste(Window w, Atom property) +{ + byte *bptr; + Atom sel_type; + unsigned long sel_nitems, sel_bytes_after; + long sel_length; + int sel_format, ret, ret2, c; + int i; + + sel_length = 16384; + sel_type = 0; + sel_format = 0; + sel_nitems = 0; + sel_bytes_after = 0; + bptr = 0; + ret = XGetWindowProperty(g_display, w, property, 0, sel_length, 1, + AnyPropertyType, &sel_type, &sel_format, &sel_nitems, + &sel_bytes_after, &bptr); +#if 0 + printf("XGetWindowProperty ret:%d, sel_type:%ld, sel_format:%d, " + "sel_nitems:%ld, sel_bytes_after:%ld, bptr:%p\n", + ret, sel_type, sel_format, sel_nitems, sel_bytes_after, bptr); +#endif + if(bptr && (sel_type == property) && sel_nitems && (sel_format == 8)) { + //printf("bptr: %s\n", (char *)bptr); + for(i = 0; i < sel_nitems; i++) { + c = bptr[i]; + ret2 = adb_paste_add_buf(c); + if(ret2) { + printf("Paste buffer full!\n"); + break; + } + } + } + if(ret == 0) { + XFree(bptr); + } +} + +int +x_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, + int button_states, int buttons_valid) +{ + Kimage *kimage_ptr; + int x, y, ret; + + if((button_states & buttons_valid & 2) == 2) { + // Middle button: Paste request + x_request_paste_data(win_info_ptr); + button_states = button_states & (~2); + } + kimage_ptr = win_info_ptr->kimage_ptr; + x = video_scale_mouse_x(kimage_ptr, raw_x, 0); + y = video_scale_mouse_y(kimage_ptr, raw_y, 0); + + if(g_x_warp_pointer && (raw_x == g_x_warp_x) && + (raw_y == g_x_warp_y) && (buttons_valid == 0) ) { + /* tell adb routs to recenter but ignore this motion */ + adb_update_mouse(kimage_ptr, x, y, 0, -1); + return 0; + } + ret = adb_update_mouse(kimage_ptr, x, y, button_states, + buttons_valid & 7); + return ret; +} + +void +x_input_events() +{ + XEvent ev, response; + XSelectionRequestEvent *req_ev_ptr; + Window_info *win_info_ptr; + char *str; + int len, motion, key_or_mouse, refresh_needed, buttons, hide, warp; + int width, height, resp_property, match, x_xpos, x_ypos; + int i; + + str = 0; + if(str) { + // Use str + } + if(adb_get_copy_requested()) { + x_send_copy_data(&g_mainwin_info); + } + g_num_check_input_calls--; + if(g_num_check_input_calls < 0) { + len = XPending(g_display); + g_num_check_input_calls = g_check_input_flush_rate; + } else { + len = QLength(g_display); + } + + motion = 0; + win_info_ptr = 0; + refresh_needed = 0; + key_or_mouse = 0; + while(len > 0) { + XNextEvent(g_display, &ev); + len--; + if(len == 0) { + /* Xaccel on linux only buffers one X event */ + /* must look for more now */ + len = XPending(g_display); + } + switch(ev.type) { + case FocusIn: + case FocusOut: + win_info_ptr = x_find_xwin(ev.xfocus.window); + if(ev.xfocus.type == FocusOut) { + /* Allow keyrepeat again! */ + vid_printf("Left window, auto repeat on\n"); + x_auto_repeat_on(0); + } else if(ev.xfocus.type == FocusIn && + (win_info_ptr == &g_mainwin_info)) { + /* Allow keyrepeat again! */ + vid_printf("Enter window, auto repeat off\n"); + x_auto_repeat_off(0); + } + break; + case EnterNotify: + case LeaveNotify: + /* These events are disabled now */ + printf("Enter/Leave event for winow %08x, sub: %08x\n", + (word32)ev.xcrossing.window, + (word32)ev.xcrossing.subwindow); + printf("Enter/L mode: %08x, detail: %08x, type:%02x\n", + ev.xcrossing.mode, ev.xcrossing.detail, + ev.xcrossing.type); + break; + case ButtonPress: + win_info_ptr = x_find_xwin(ev.xbutton.window); + vid_printf("Got button press of button %d!\n", + ev.xbutton.button); + buttons = (1 << ev.xbutton.button) >> 1; + motion |= x_update_mouse(win_info_ptr, ev.xbutton.x, + ev.xbutton.y, buttons, buttons & 7); + key_or_mouse = 1; + break; + case ButtonRelease: + win_info_ptr = x_find_xwin(ev.xbutton.window); + buttons = (1 << ev.xbutton.button) >> 1; + motion |= x_update_mouse(win_info_ptr, ev.xbutton.x, + ev.xbutton.y, 0, buttons & 7); + key_or_mouse = 1; + break; + case MotionNotify: + win_info_ptr = x_find_xwin(ev.xmotion.window); + motion |= x_update_mouse(win_info_ptr, ev.xmotion.x, + ev.xmotion.y, 0, 0); + key_or_mouse = 1; + break; + case Expose: + win_info_ptr = x_find_xwin(ev.xexpose.window); + refresh_needed = -1; + //printf("Got an Expose event\n"); + break; + case NoExpose: + /* do nothing */ + break; + case KeyPress: + case KeyRelease: + x_handle_keysym(&ev); + key_or_mouse = 1; + break; + case KeymapNotify: + break; + case DestroyNotify: + win_info_ptr = x_find_xwin(ev.xdestroywindow.window); + video_set_active(win_info_ptr->kimage_ptr, 0); + win_info_ptr->active = 0; + printf("Destroy %s\n", win_info_ptr->name_str); + if(win_info_ptr == &g_mainwin_info) { + x_try_xset_r(); // Mainwin: quit BURST + } + break; + case ReparentNotify: + case UnmapNotify: + case MapNotify: + break; + case ClientMessage: + win_info_ptr = x_find_xwin(ev.xclient.window); + if(ev.xclient.data.l[0] == win_info_ptr->delete_atom) { + // This is a WM_DELETE_WINDOW event + // Just unmap the window + win_info_ptr->kimage_ptr->active = 0; + } else { + printf("unknown ClientMessage\n"); + } + break; + case ConfigureNotify: + win_info_ptr = x_find_xwin(ev.xconfigure.window); + width = ev.xconfigure.width; + height = ev.xconfigure.height; +#if 0 + printf("ConfigureNotify, width:%d, height:%d\n", + width, height); +#endif + video_update_scale(win_info_ptr->kimage_ptr, width, + height, 0); + x_xpos = ev.xconfigure.x; + x_ypos = ev.xconfigure.y; + video_update_xpos_ypos(win_info_ptr->kimage_ptr, + x_xpos, x_ypos); + break; + case SelectionRequest: + //printf("SelectionRequest received\n"); + req_ev_ptr = &(ev.xselectionrequest); + // This is part of the dance for copy: Another client + // is asking us what format we can supply (TARGETS), + // or is doing to tell us one at a time what types + // it would like. +#if 0 + printf("req:%ld, property:%ld, target:%ld, " + "selection:%ld\n", req_ev_ptr->requestor, + req_ev_ptr->property, req_ev_ptr->target, + req_ev_ptr->selection); + str = XGetAtomName(g_display, req_ev_ptr->target); + printf("XAtom target str: %s\n", str); + XFree(str); + str = XGetAtomName(g_display, req_ev_ptr->property); + printf("XAtom property str: %s\n", str); + XFree(str); +#endif + resp_property = None; + match = 0; + for(i = 0; i < g_x_num_targets; i++) { + if(req_ev_ptr->target == g_x_targets_array[i]) { + match = 1; + break; + } + } + if(match) { + x_handle_copy(req_ev_ptr); + resp_property = req_ev_ptr->property; + } else if(req_ev_ptr->target == g_x_atom_targets) { + // Some other agent is asking us "TARGETS", + // so send our list of targets + x_handle_targets(req_ev_ptr); + } + // But no matter what the request target was, respond + // so it will send an eventual request for XA_STRING + response.xselection.type = SelectionNotify; + response.xselection.display = req_ev_ptr->display; + response.xselection.requestor = req_ev_ptr->requestor; + response.xselection.selection = req_ev_ptr->selection; + response.xselection.target = req_ev_ptr->target; + response.xselection.property = resp_property; + response.xselection.time = req_ev_ptr->time; + XSendEvent(g_display, req_ev_ptr->requestor, 0, 0, + &response); + XFlush(g_display); // Speed up getting more resp + break; + case SelectionNotify: + // We get this event after we requested the PRIMARY + // selection, so paste this to adb(). + vid_printf("SelectionNotify received\n"); + vid_printf("req:%ld, selection:%ld, target:%ld, " + "property:%ld\n", ev.xselection.requestor, + ev.xselection.selection, + ev.xselection.target, + ev.xselection.property); + if(ev.xselection.property == None) { + printf("No selection\n"); + break; + } + x_handle_paste(ev.xselection.requestor, + ev.xselection.property); + + break; + default: + printf("X event 0x%08x is unknown!\n", + ev.type); + break; + } + } + + if(key_or_mouse && (win_info_ptr == &g_mainwin_info)) { + hide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp); + if(warp != g_x_warp_pointer) { + motion = 1; + } + g_x_warp_pointer = warp; + if(g_x_hide_pointer != hide) { + x_hide_pointer(&g_mainwin_info, hide); + } + g_x_hide_pointer = hide; + } + + if(motion && g_x_warp_pointer && (win_info_ptr == &g_mainwin_info)) { + // Calculate where to warp to + g_x_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr, + BASE_MARGIN_LEFT + (BASE_WINDOW_WIDTH/2), 0); + g_x_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr, + BASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0); + + XWarpPointer(g_display, None, win_info_ptr->x_win, 0, 0, 0, 0, + g_x_warp_x, g_x_warp_y); + } + + if(refresh_needed && win_info_ptr) { + //printf("...at end, refresh_needed:%d\n", refresh_needed); + video_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1); + } + +} + +void +x_hide_pointer(Window_info *win_info_ptr, int do_hide) +{ + if(do_hide) { // invisible + XDefineCursor(g_display, win_info_ptr->x_win, g_cursor); + } else { // Default cursor + XDefineCursor(g_display, win_info_ptr->x_win, None); + } +} + +void +x_handle_keysym(XEvent *xev_in) +{ + Window_info *win_info_ptr; + KeySym keysym; + word32 state; + int keycode, a2code, type, is_up; + + win_info_ptr = x_find_xwin(xev_in->xkey.window); + + keycode = xev_in->xkey.keycode; + type = xev_in->xkey.type; + + keysym = XLookupKeysym(&(xev_in->xkey), 0); + + state = xev_in->xkey.state; + + vid_printf("keycode: %d, type: %d, state:%d, sym: %08x\n", + keycode, type, state, (word32)keysym); + + x_update_modifier_state(win_info_ptr, state); + + is_up = 0; + if(type == KeyRelease) { + is_up = 1; + } + + /* first, do conversions */ + switch(keysym) { + case XK_Alt_L: + case XK_Meta_R: // Windows key on right side + case XK_Super_R: + case XK_Mode_switch: + case XK_Cancel: + keysym = XK_Print; /* option */ + break; + case XK_Meta_L: // Windows key on left side + case XK_Alt_R: + case XK_Super_L: + case XK_Menu: + keysym = XK_Scroll_Lock; /* cmd */ + break; + case XK_F5: + break; + case XK_F10: + if(!is_up) { + g_video_scale_algorithm++; + if(g_video_scale_algorithm >= 3) { + g_video_scale_algorithm = 0; + } + printf("g_video_scale_algorithm = %d\n", + g_video_scale_algorithm); + video_update_scale(win_info_ptr->kimage_ptr, + win_info_ptr->width_req, + win_info_ptr->main_height, 1); + } + break; + case 0x1000003: + if(keycode == 0x3c) { + /* enter key on Mac OS X laptop--make it option */ + keysym = XK_Print; + } + break; + case NoSymbol: + switch(keycode) { + /* 94-95 are for my PC101 kbd + windows keys on HPUX */ + case 0x0095: + /* left windows key = option */ + keysym = XK_Print; + break; + case 0x0096: + case 0x0094: + /* right windows key = cmd */ + keysym = XK_Scroll_Lock; + break; + /* 0072 is for cra@WPI.EDU who says it's Break under XFree86 */ + case 0x0072: + /* 006e is break according to mic@research.nj.nec.com */ + case 0x006e: + keysym = XK_Break; + break; + + /* 0x0042, 0x0046, and 0x0048 are the windows keys according */ + /* to Geoff Weiss on Solaris x86 */ + case 0x0042: + case 0x0046: + /* flying windows == open apple */ + keysym = XK_Scroll_Lock; + break; + case 0x0048: + case 0x0076: /* Windows menu key on Mac OS X */ + /* menu windows == option */ + keysym = XK_Print; + break; + } + } + + a2code = x_keysym_to_a2code(win_info_ptr, (int)keysym, is_up); + if(a2code >= 0) { + adb_physical_key_update(win_info_ptr->kimage_ptr, a2code, + 0, is_up); + } else if(a2code != -2) { + printf("Keysym: %04x of keycode: %02x unknown\n", + (word32)keysym, keycode); + } +} + +int +x_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up) +{ + int i; + + if(keysym == 0) { + return -1; + } + + /* Look up Apple 2 keycode */ + for(i = g_num_a2_keycodes - 1; i >= 0; i--) { + if((keysym == g_x_a2_key_to_xsym[i][1]) || + (keysym == g_x_a2_key_to_xsym[i][2])) { + + vid_printf("Found keysym:%04x = a[%d] = %04x or %04x\n", + (int)keysym, i, g_x_a2_key_to_xsym[i][1], + g_x_a2_key_to_xsym[i][2]); + + return g_x_a2_key_to_xsym[i][0]; + } + } + + return -1; +} + +void +x_update_modifier_state(Window_info *win_info_ptr, int state) +{ + word32 c025_val; + + c025_val = 0; + if(state & ShiftMask) { + c025_val |= 1; + } + if(state & ControlMask) { + c025_val |= 2; + } + if(state & LockMask) { + c025_val |= 4; + } + adb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7); +} + +void +x_auto_repeat_on(int must) +{ + if((g_auto_repeat_on <= 0) || must) { + g_auto_repeat_on = 1; + XAutoRepeatOn(g_display); + XFlush(g_display); + adb_kbd_repeat_off(); + } +} + +void +x_auto_repeat_off(int must) +{ + if((g_auto_repeat_on != 0) || must) { + XAutoRepeatOff(g_display); + XFlush(g_display); + g_auto_repeat_on = 0; + adb_kbd_repeat_off(); + } +} + +void +x_full_screen(int do_full) +{ + return; +} From 496066acd13972134519ef4edea3a82c7e4e1ada Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Fri, 27 Feb 2026 19:58:06 -0600 Subject: [PATCH 2/5] initial blank lines removed --- gsplus/src/adb.c | 1 - gsplus/src/applesingle.c | 1 - gsplus/src/clock.c | 1 - gsplus/src/compile_time.c | 2 -- gsplus/src/config.c | 1 - gsplus/src/debugger.c | 1 - gsplus/src/disas.h | 1 - gsplus/src/doc.c | 1 - gsplus/src/dyna_filt.c | 1 - gsplus/src/dyna_type.c | 1 - gsplus/src/dyna_validate.c | 1 - gsplus/src/dynapro.c | 1 - gsplus/src/engine_c.c | 1 - gsplus/src/iwm.c | 1 - gsplus/src/joystick_driver.c | 1 - gsplus/src/macsnd_driver.c | 1 - gsplus/src/mockingboard.c | 1 - gsplus/src/moremem.c | 1 - gsplus/src/paddles.c | 1 - gsplus/src/pulseaudio_driver.c | 1 - gsplus/src/scc.c | 1 - gsplus/src/scc_socket_driver.c | 1 - gsplus/src/scc_unixdriver.c | 1 - gsplus/src/scc_windriver.c | 1 - gsplus/src/sim65816.c | 1 - gsplus/src/smartport.c | 1 - gsplus/src/sound.c | 1 - gsplus/src/sound_driver.c | 1 - gsplus/src/undeflate.c | 1 - gsplus/src/unshk.c | 1 - gsplus/src/vars | 1 - gsplus/src/vars_mac | 1 - gsplus/src/vars_mac_x | 1 - gsplus/src/vars_x86linux | 1 - gsplus/src/video.c | 1 - gsplus/src/voc.c | 1 - gsplus/src/win32snd_driver.c | 1 - gsplus/src/windriver.c | 1 - gsplus/src/woz.c | 1 - gsplus/src/xdriver.c | 1 - 40 files changed, 41 deletions(-) diff --git a/gsplus/src/adb.c b/gsplus/src/adb.c index 9da98aa..d9bf395 100644 --- a/gsplus/src/adb.c +++ b/gsplus/src/adb.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/applesingle.c b/gsplus/src/applesingle.c index f0cebc0..5bc7139 100644 --- a/gsplus/src/applesingle.c +++ b/gsplus/src/applesingle.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/clock.c b/gsplus/src/clock.c index 1899bd8..28701c4 100644 --- a/gsplus/src/clock.c +++ b/gsplus/src/clock.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/compile_time.c b/gsplus/src/compile_time.c index 7b4a63d..f576f92 100644 --- a/gsplus/src/compile_time.c +++ b/gsplus/src/compile_time.c @@ -1,4 +1,2 @@ - - char g_compile_time[] = "Compiled: " __DATE__ " " __TIME__ ; diff --git a/gsplus/src/config.c b/gsplus/src/config.c index f5c894b..57c9e74 100644 --- a/gsplus/src/config.c +++ b/gsplus/src/config.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/debugger.c b/gsplus/src/debugger.c index 5d7c703..793d2ca 100644 --- a/gsplus/src/debugger.c +++ b/gsplus/src/debugger.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/disas.h b/gsplus/src/disas.h index 727aa89..b7aaf4a 100644 --- a/gsplus/src/disas.h +++ b/gsplus/src/disas.h @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/doc.c b/gsplus/src/doc.c index 7109642..8375bdb 100644 --- a/gsplus/src/doc.c +++ b/gsplus/src/doc.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/dyna_filt.c b/gsplus/src/dyna_filt.c index b8acfe5..1044392 100644 --- a/gsplus/src/dyna_filt.c +++ b/gsplus/src/dyna_filt.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/dyna_type.c b/gsplus/src/dyna_type.c index 517fd96..3328d5b 100644 --- a/gsplus/src/dyna_type.c +++ b/gsplus/src/dyna_type.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/dyna_validate.c b/gsplus/src/dyna_validate.c index 2cbf303..fcf5091 100644 --- a/gsplus/src/dyna_validate.c +++ b/gsplus/src/dyna_validate.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/dynapro.c b/gsplus/src/dynapro.c index fe24a78..d2e9b87 100644 --- a/gsplus/src/dynapro.c +++ b/gsplus/src/dynapro.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/engine_c.c b/gsplus/src/engine_c.c index 02224fd..04df05e 100644 --- a/gsplus/src/engine_c.c +++ b/gsplus/src/engine_c.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/iwm.c b/gsplus/src/iwm.c index 8bd1232..cab465e 100644 --- a/gsplus/src/iwm.c +++ b/gsplus/src/iwm.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/joystick_driver.c b/gsplus/src/joystick_driver.c index e5206dc..72e63cf 100644 --- a/gsplus/src/joystick_driver.c +++ b/gsplus/src/joystick_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/macsnd_driver.c b/gsplus/src/macsnd_driver.c index 34e134d..c1bae00 100644 --- a/gsplus/src/macsnd_driver.c +++ b/gsplus/src/macsnd_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/mockingboard.c b/gsplus/src/mockingboard.c index cee0456..78e3758 100644 --- a/gsplus/src/mockingboard.c +++ b/gsplus/src/mockingboard.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/moremem.c b/gsplus/src/moremem.c index a8a2efa..c4c51e7 100644 --- a/gsplus/src/moremem.c +++ b/gsplus/src/moremem.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/paddles.c b/gsplus/src/paddles.c index 9c018b1..66c90f0 100644 --- a/gsplus/src/paddles.c +++ b/gsplus/src/paddles.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/pulseaudio_driver.c b/gsplus/src/pulseaudio_driver.c index b999744..f8d7474 100644 --- a/gsplus/src/pulseaudio_driver.c +++ b/gsplus/src/pulseaudio_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/scc.c b/gsplus/src/scc.c index 4823ad2..ed4ec26 100644 --- a/gsplus/src/scc.c +++ b/gsplus/src/scc.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/scc_socket_driver.c b/gsplus/src/scc_socket_driver.c index 15d8688..ef27281 100644 --- a/gsplus/src/scc_socket_driver.c +++ b/gsplus/src/scc_socket_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/scc_unixdriver.c b/gsplus/src/scc_unixdriver.c index 10d43eb..53a5f03 100644 --- a/gsplus/src/scc_unixdriver.c +++ b/gsplus/src/scc_unixdriver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/scc_windriver.c b/gsplus/src/scc_windriver.c index 2a7677d..2b32263 100644 --- a/gsplus/src/scc_windriver.c +++ b/gsplus/src/scc_windriver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/sim65816.c b/gsplus/src/sim65816.c index f733d32..2228c3e 100644 --- a/gsplus/src/sim65816.c +++ b/gsplus/src/sim65816.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/smartport.c b/gsplus/src/smartport.c index 1c7a574..b3dd1ea 100644 --- a/gsplus/src/smartport.c +++ b/gsplus/src/smartport.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/sound.c b/gsplus/src/sound.c index 56566e8..8d5df3c 100644 --- a/gsplus/src/sound.c +++ b/gsplus/src/sound.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/sound_driver.c b/gsplus/src/sound_driver.c index 7fc708d..58bbc23 100644 --- a/gsplus/src/sound_driver.c +++ b/gsplus/src/sound_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/undeflate.c b/gsplus/src/undeflate.c index 9260fe2..8e0591d 100644 --- a/gsplus/src/undeflate.c +++ b/gsplus/src/undeflate.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/unshk.c b/gsplus/src/unshk.c index 12737e9..85e0fec 100644 --- a/gsplus/src/unshk.c +++ b/gsplus/src/unshk.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/vars b/gsplus/src/vars index 75cf7de..b6642a6 100644 --- a/gsplus/src/vars +++ b/gsplus/src/vars @@ -1,4 +1,3 @@ - TARGET = gsplus OBJECTS1 = macsnd_driver.o CCOPTS = -Wall -O2 -DMAC diff --git a/gsplus/src/vars_mac b/gsplus/src/vars_mac index 75cf7de..b6642a6 100644 --- a/gsplus/src/vars_mac +++ b/gsplus/src/vars_mac @@ -1,4 +1,3 @@ - TARGET = gsplus OBJECTS1 = macsnd_driver.o CCOPTS = -Wall -O2 -DMAC diff --git a/gsplus/src/vars_mac_x b/gsplus/src/vars_mac_x index 5fc8fed..bbca383 100644 --- a/gsplus/src/vars_mac_x +++ b/gsplus/src/vars_mac_x @@ -1,4 +1,3 @@ - TARGET = gsplus OBJECTS1 = macsnd_driver.o xdriver.o CCOPTS = -O2 -DMAC -Wall -I/usr/X11/include diff --git a/gsplus/src/vars_x86linux b/gsplus/src/vars_x86linux index 00d1927..ac985a5 100644 --- a/gsplus/src/vars_x86linux +++ b/gsplus/src/vars_x86linux @@ -1,4 +1,3 @@ - TARGET = gsplus OBJECTS1 = pulseaudio_driver.o xdriver.o CCOPTS = -O2 -Wall -fomit-frame-pointer -DPULSE_AUDIO diff --git a/gsplus/src/video.c b/gsplus/src/video.c index 66ecdbf..9be841a 100644 --- a/gsplus/src/video.c +++ b/gsplus/src/video.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/voc.c b/gsplus/src/voc.c index 5edde5d..a72c692 100644 --- a/gsplus/src/voc.c +++ b/gsplus/src/voc.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/win32snd_driver.c b/gsplus/src/win32snd_driver.c index 2c5d139..c783b4d 100644 --- a/gsplus/src/win32snd_driver.c +++ b/gsplus/src/win32snd_driver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/windriver.c b/gsplus/src/windriver.c index 4e0c241..3857952 100644 --- a/gsplus/src/windriver.c +++ b/gsplus/src/windriver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/woz.c b/gsplus/src/woz.c index 5ce482a..2b26230 100644 --- a/gsplus/src/woz.c +++ b/gsplus/src/woz.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ diff --git a/gsplus/src/xdriver.c b/gsplus/src/xdriver.c index 4cfc084..d91c9e4 100644 --- a/gsplus/src/xdriver.c +++ b/gsplus/src/xdriver.c @@ -1,4 +1,3 @@ - /**********************************************************************/ /* GSplus - Apple //gs Emulator */ /* Based on KEGS by Kent Dickey */ From f5ca7aca989ecba95f453d3b8a7a8b36cf3faad5 Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Fri, 27 Feb 2026 20:50:05 -0600 Subject: [PATCH 3/5] license consistency --- gsplus/src/AppDelegate.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gsplus/src/AppDelegate.swift b/gsplus/src/AppDelegate.swift index 6cecdf0..fd130a2 100644 --- a/gsplus/src/AppDelegate.swift +++ b/gsplus/src/AppDelegate.swift @@ -1,12 +1,12 @@ -// $KmKId: AppDelegate.swift,v 1.32 2024-09-15 13:55:35+00 kentd Exp $ - -// Copyright 2019-2024 by Kent Dickey -// This code is covered by the GNU GPL v3 -// See the file COPYING.txt or https://www.gnu.org/licenses/ -// -// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/ -// Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreAudio.framework/ -// Versions/A/Headers +/**********************************************************************/ +/* GSplus - Apple //gs Emulator */ +/* Based on KEGS by Kent Dickey */ +/* Copyright 2002-2024 Kent Dickey */ +/* Copyright 2025-2026 GSplus Contributors */ +/* */ +/* This code is covered by the GNU GPL v3 */ +/* See the file COPYING.txt or https://www.gnu.org/licenses/ */ +/**********************************************************************/ import Cocoa From cb2b316461ec520c216ad1b529b35b2873d027fb Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Fri, 27 Feb 2026 21:46:51 -0600 Subject: [PATCH 4/5] mac title --- TODO.md | 2 ++ gsplus/src/AppDelegate.swift | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 0092fa0..915d0e5 100644 --- a/TODO.md +++ b/TODO.md @@ -3,3 +3,5 @@ ## Backlog - [ ] Remove `#ifdef INCLUDE_RCSID_C` / `#endif` guards from header files and `#define`/`#undef INCLUDE_RCSID_C` from `sim65816.c` and `sound.c` in `gsplus/src/`. The `const char rcsid_` lines were already removed; these are the leftover scaffolding. + +- [ ] Update Menubar titles for X/Win to match.. Mac build already updated to "GS+" \ No newline at end of file diff --git a/gsplus/src/AppDelegate.swift b/gsplus/src/AppDelegate.swift index fd130a2..a719071 100644 --- a/gsplus/src/AppDelegate.swift +++ b/gsplus/src/AppDelegate.swift @@ -338,7 +338,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { } video_set_palette() kimage_ptr = video_get_kimage(Int32(0)) - mainwin_info.set_kimage(kimage_ptr, title: "GSplus", + mainwin_info.set_kimage(kimage_ptr, title: "GS+", delegate: self) kimage_ptr = video_get_kimage(Int32(1)) debugwin_info.set_kimage(kimage_ptr, title: "Debugger", From 7ffd246186cd1fdfe1639dabeae40472df0597e7 Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Fri, 27 Feb 2026 22:30:57 -0600 Subject: [PATCH 5/5] mac about title --- gsplus/src/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsplus/src/AppDelegate.swift b/gsplus/src/AppDelegate.swift index a719071..a61a590 100644 --- a/gsplus/src/AppDelegate.swift +++ b/gsplus/src/AppDelegate.swift @@ -168,7 +168,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { "CFBundleShortVersionString"] as? String { NSApplication.shared.orderFrontStandardAboutPanel( options: [.applicationVersion: ver_str, - .version: "", .applicationName: "GSplus"]) + .version: "", .applicationName: "GS+"]) } } func applicationDidFinishLaunching(_ aNotification: Notification) {

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%0DqAD{O=pI=^mX{>U;p&4U;OrqKmF-XdfeRu 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�+qP>$h;iy;> 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