From 8fbeb6eecc918835dae7593494c0f7369729dd5b Mon Sep 17 00:00:00 2001 From: Wayne Warthen Date: Sat, 24 Oct 2015 12:52:27 -0700 Subject: [PATCH] Continued cleanup of SD and IDE driver code --- Doc/Source/RomWBW Architecture.docx | Bin 257859 -> 274319 bytes Doc/Source/WBW.vsdx | Bin 0 -> 14492 bytes Source/HBIOS/Config/mk4_dsd.asm | 6 - Source/HBIOS/Config/mk4_std.asm | 12 +- Source/HBIOS/hdsk.asm | 2 +- Source/HBIOS/ide.asm | 1276 +++++++++++++--------- Source/HBIOS/sd.asm | 1514 ++++++++++++++------------- 7 files changed, 1566 insertions(+), 1244 deletions(-) create mode 100644 Doc/Source/WBW.vsdx diff --git a/Doc/Source/RomWBW Architecture.docx b/Doc/Source/RomWBW Architecture.docx index 026bf8c51f1257b884a80f3dc24cf2a3e82f03c3..3071afbc6583b1818d9bb6fa2e6b09bdbac188c1 100644 GIT binary patch delta 56222 zcmV)GK)%1j-Vcx85EoEO0|XQR1^@^E001EXA#h#j!T|sPZLt?40)JLZkJB&^z9aD; zB41JBE(e5A(>=iM5msW6xVrJAweU-x>FeJ!PTL4jx~b^q5-FMa9`jkrWPZPQw%9^K z6MQZ1s+lOjH$fY}s>P4z$MT~nkd)WT1`oA30E+v?+c)#)Lj)`s?XebXqWDQlY}Viu zRw07tN*j_>#J6cBqkn3CsTIigv)KpP1W(|pq|6}}^DoeEj7eZ|MDp*s%F|30BfB14n~3kY;etZ0 zTo$;D<1F?g&K#Cq9+ggN$7h$BzVWdpqoH_GiM}ewFu4m!OMe}jjbp0nWkUlKhqg5h z^!UscQ)mE@HJz>cic`j)lNj>RY>8vya?kRmYvZf*h%}EAESgFjiBH!kpCf6kMfg8zNYoHYVK*%Qy~URK22ORPUX zbnp^y+cZkRofR7mCOlJuDUG$!kh>*>ifE9EpTvTKZU}pxp_u+3h-nNWDT5D(7CYHP zoW>M=#>&4-SssiuHVgsIN(`YWQ!>~VKG#1%gaL%EK^~XBXAep^>wj$%7T*=_jRhJ^ zCuLsbi#x}6IHjOlFGop}rWNqr!GifcJc>uB2(14QAu;yDuEQ6z$nnN{YssFz0nU zNTZjc6e%IZ#s{l^V6`5C(u>Me03B^i;z(@xV=whmG?Jv5jGt*`>`Z%`l3@q+R|w&_ zeJ>;fF`7nFl4AqFJyvZ9xagWF@{|B8baJgS>kGr9t-0QUp7>hOuHIR<_EcDXp7mfQ z0({&ftl^`mZ<1a_e|7Av#L{}765;Wea35vf%dx_CFAs0mij( z**Y!R)Anm!uJY@uwx8{)?w*}RgA&sg%M__0DIdL9VE)5?THw7uH@{@#$Rv~EA<6Pp zwt5C-n@473+z}bf_~GAf$BuC={J{3yfBjNrt6VaKJM=8uz1%N-IX`^gDj7j&x|Zp9 zuGlZ#iJgxS;_GN7(Y%Rp;pTgEH^-Pc7Vy{YQ*{Ad(%lgn!Va+NA6WL7E1OK zV@V?GOAA&5=4@n6Kmy0Zy`xLl^UZ++5CO>yz`B8KT{-}bGw`fC{25rG{Bh#rFTVV7 z=(%BF-0TT62-|^eo&zK>&e(<_KepY#E^Xs;j_F=%Pwkh4`~J%r-rJ7Gf3huj;@WgT zCsyjU-CDD`v&(OT+B-4*@Obb|`|E@0IKsbE?{qy-sm6(U3EEPB>5FUsV=8zeeVlD= zG4M;@wN89`5B})FqChL)U*W)!KloAjWpd)fq>i`<4}EWZE^b4BxYaDdMc=+0rLT@W z|Btpigdczy)4xOs;gNnlf13zUy7Zd5_8VUtcwy*`c`0!D7q|;zS*_F>^x)kDusj)t z(E}YIF4z?M%bO^yAfX+BZv=nrmufro+r%8g%Vqq1=s5`0Zl}{}?9#wUzb;=jheNJ6jBCGS2)gRlgUUZy1^M}SjMMyYf5$nf8z4}N=3tWbU#8y z?g&KE{2>@T9iV~p$@8uh8p`!Hje24GL3rxnDEOyi%D>{9u=ME|{RnkmU7$x??_&o% ziRiDd{8uH0A!6$fzHQ;Jm+%wDVA$*B-A4SZdL%>v0RP20g@MbxUZc}FB=(0(=RRJm z?3CNRCTWl-&>W^?f15ZIq3+xcfq|H}VvSO3G-{Q)(kEtUc5D|tE_h(#iwpbqvpJc- z-=I`kzq&zr|1a~cAh6~Y=p1SML}=pJ+e6P6??Kg0gdf@>_>0upZcvHFoJ@U(P5E$J zI0C(pV7sza+5QV+@)mrn(SB(gpxDE~PNK2?XXSsv)yiIof6IhFNI*h(t$e)i6Q zpU`z<9rD7_uc25i$M?L8zK`mG(HBfW#hryk#3F$|}^NFV^|EhR$9e^(?^{3qY#(U-9mk13DUX0_ex zKY%>O*sYh-*yU?!(a14_%M3PS=+34CG6LDQt|9%a*I?^{>EIji;Uii*#(csyWby62 z2^Svx-1^%8Y{1%ZC9~p`B|$R~)OQ>0ofRM$;2Mh+n69TEa_W0((FX+~N5_5TG@Mc0 zngK33e|xW1Z&z1AK(kPRK8vP=P_hE1KwZ)_Wxvv_^?DDgDXXnCmiSs)Q}RE{j{pIY zV|7O+qEY^KPM8=747oM-zyAynt%g1Z_c{D@*8bY>wa@#8@%JB%(r5V3ur`f5_^$+) z{)h3ofBK<+czpV~eQw~e(RdF5T+Z?XF)@8JeM!QEhD+)pDgVD_Re|K*1^_U$*cE=W!AeAelNt0+M%fB;ean!HHQ1;Y%3kE<^Ej z^I=NDyTa5wDRBe+0TuL{=pp|8!RvteW*n<$LDxo4%E4YUk}~JZcNp(IU&OD^;`|Tz zf8TCEKPg|PK&pwOe_@`3&e{q-pY21pEabY`-fSE3Ev(5Le_;-SY{xrUM zsGRRGD?YX*Wv}Er`a>meeUEo4? zihRK-b8pY_hF8WljsXV5vf)t^{Lf+N%F{+{8S$vjpLV5O1@_~y|D-dm4>NIgDR;PGbe^aMQ(@8&n z5bHj9C81)K*PYOt8A%v*ZMluH_qBSXRj;(9B3@))>>M}t4DZ5_NWTB>m_~-!Kh!jT zewsC5aQo$2qY~v|5^)FL(X4AS$T&aleiSBVG9-R4FW4{b)*8ymQm@YPbQb4Ez5q>{iFX|fOGQBQf*Dw> z@psQCRo>T1#@|QA-~TZFJ}nGemWV(`aK{I`iCbQ#+#LP=+#A+wmCA0V(d3;|83XiHpHU+p>h4 z9N8S`Bv^I0g=xL&MDzIMr2V0P+CMZBW55g6`=@>l`*q5$xhGC+(yju%(bN96hb!{!w&m+_= zN?SjHP@hiS&>jopvlzon#wYX6nVW`TOYsl$ zrMUJ)Qrw}}(#Z3)jf!%CLn z+FBz))-t}9fANQv@xmIl2bA%ToulJ3;}k30g*!}sjJq_yyb571EH7JP_7^A2mGXBl z&EIP>?5S$Pj@)aHb75;iZoS%?u(cpRn;<_`QP|fTza zi2|=ys@KI~&n4AQRvacw$2i9l?k;`Kzj1orUCT7!|JBO$x>W4BWcsN}#o|4zjsE8^ zYiS0|zglVDT}$&bN%K>cit#Qqc;6GpyffOCIl=IH<7=VO#(+nvR1z&FfmFg$czrR~zQ zf7~;n>y5`#*ADNDjX(%P_LP`aVM!j62rB}9W`NijM`xW@7T9>M?d(z4UWjv^fb9T0?>q zAY+56?O296f$=B44Tci$qmt*92U9?fi7VckMe@VRV-fJ@F@9NSrkD`9u|Yr(P!l-c zG=eb-43dRpL*58x2@tU&&fnXuly|yC9FGz;J3_(ZRCB!1F#~&eU&`wnt`A6me-1t0 z85gqt3iJ;cCwLuy7U8JTt=2ldA{-4pOCVDPWEc=*EI`KK&Hz;&Viz6LHLyXANq@-O z7rr+J!dZ531%h&ByF;OAuoLe35=kLE8A~3)u(>>n^A@&xvz7U1(l!4WMG92}v9m8m z0=$)LK%93G>V^RDa^!;I8HSsNe;pcjV1QKGz5q1@;Z|Uy-UrP4Q-y zQf3`)>~LhbUO2)#gDF#LF`<@=%Mj}1ofl(dBZCS|+AvnO9bU^Qnk$|8}tO(zD4f8PcQ8bn;? zB$-(HzE|2d2Bb<^VQgDsFueq^0l&ucU(qu8Q>{| zJ}X<5S0)SKriJgp*`c*wf7p(YIFP;$Vg=cQWyXw;4++T2n}*nS-j?nxKA1u#J~mQZ zI1qSe3Q@=7VUgFCt;CPW-sxT`SURv@=p=xHMLIEjI0ox)u}1{Yycf5?Nz{E1!W(b`K6@bXuyzRazzN1Bf7{r=!h&~FM4W*! z;ic57m3pm4s1FYSyMYZ@xnt*EMwmlyLOSIVPj3Z2B4$%Ug`jA7k9b9CC?doSc$A=; zE^QZmcGP3mbOW&f zc@`>86C(t&51Lijq{u79B%4VqpiA}w{BhVV!#|mZpx3CC=_{iMDe__6$T_J^l#J1f z)avZ(Jnqk{e~n7gnK^Ry^NOyK92Y8Oeg1TB4Sh_CMzmp zro`iMa*A?OsL3mzJD8a|myBa0Gr%-5n>94uC02_1WNae5Q63VMi~?mE5YHLF+9PV3 zV<04;dsZXIz;v&S0QA=oV_95^2kbtwLAR1Pq!I&?e+v+-2&;0rsQ7KfxP^WK=u*!Z zj;8Ka03j7j6QuU(bx?cla-~+5l-tCXRua`Uz_b96vf%)TjW!s4JQm$VRcy{Qi=LUL zQ)yH>Ejf+340WXO0b2a)pZ{-hF3cWH*F?-**~_!Z`nZOC(L#>+*FXPrY)=05&;J7L z64}IAe_Al&lUk+#6*+4*e)Fct#mEZ4C(68&R=oj}@7>4@Av^-yYg*r?WDfyK+>20V zK5|b|9ODvs2UtCw2DT8gBbL%3*xER3in+-egSO2m8=*sF?^8K4e8 z4Un_!3;T8^{#t&Gh&s?PCgLw*=AtooTa}$1c2>dz=K2*uAc1oQ9-lQFp%a6iZPAzs z&3eAt>7d{S_#a=Ef2bLHJ~sNKaf}wiAMj*WLpzk;E%orRD zg3xqBo17ln&mdP6Jq3US001%Xh@lDQM~ibOytPA-jtk~E$`F{wHFja-eV%L6w*?2# z3u_7$wk*L;v_6DnARu5u#tD5f#vK-WjIIDI50{LI5<%&K1W(|Wyyr|QF91&!e*xMr z*AJU2_o78-%dkG;aGdf0b7#4=$I~%Kjflo5sIvqea7KK9ZYZV^)V`7FES7_r;qJL* zC~20L5h0DdS|)%JKIu#V^x#cbR zBJR{k9`e3%FtW$SMy3p4EPPw&c_fC*tf z!7{;vufjMJFhYb&)UbsDsj*u&Fd%(4s$Pnf0f7+(<+c!S2Vr0tU=B4>f7}T5!kQ5r z9*fT}MYMz0vl`6I5wq5YxFu<4RaV{+v_8D)i+@Z-5N3FQb3`DO&&0sSH3?S~*Pt5r zhQldXG7H%d`yfXaiQeUfe@Dp{#)AdyO1=q6-pR=zaz5Th%JYT~Zf{(=UJ!xPFE*cXL(GZHn=c-w1b&b6UzW9P(^I&#h0-`jrFH=7{N&%o0l@@ z7kP+eQvyul!Uz~rxiJRWb|$f?5S6gL3rLDoBlne*FRo)>Vad_BkjeC9z&(;E{aW>~ zQ&pN2mYOWwP|)Oze~gLrWVY7`hFE;f$ucbPK{*A40tqK1(_G9r^HOA((qs<<;an&S zkN>C2M^1?c98> z%5b+XTbd!Bz&J1Sn8aL~EMtEbr*ycC(JZ7);gr4FPG^qTfAcnn1EjnXV#31Dw&7MP zDyg8xrVF7jg?PH{xH0c|Up`l1>6I2M8MvfCFBu?UEidlZVGKJ9rp%IzOkka;ibU<~ z6(P9XuXpRpreDkyUCkltj0kRst;?-Vjx%b~P+}T91u9jw<0dw{YFxs>33DVtG89p{ zDuggIJoF|sHN_bb539{pdWObWfPo73Sex}${Ka36-p5WPQj<{u zwJe>n%+MqpYH<PJGP3dOe}ih6Tm_Xu90Qu4YqXl9p-n}3o@zVlX8%vY2TjkR#XbAbTxB#r$D9<4azkxh(#|5yw zIt&uRsFIS3DBlrUBwVsggM0&UhlnAgwAyqKU?cb5M4N`fNmMl+`{IW>L*K~qJm9u5(Ke7`^1k7Aa_%iw(LV&zPK+b#~N6D{6 zhu&?#f|KIXZ#R0KrjFZhLxorT{7d*rtkOT;fYO;ad)BRa?Aw?48ElnKMz-$cg;uSM z0pImrFfu2=P=KM|Z$UE4zuT2^b*F@T$3TTYf60qsvrz)J1fuSjZfq+Y?bUZ$lkl%2 zfgj)Q)hqQ$STejl4diAVe?P6Zzc?T{Wqu;ugzKX`k`pZdoPHh=He$9l37 zR^4A7_4Z3&?ZEc7dcJvsJM1{p8NP|Pl5pcAZ3?LNOY+melEEX>!<9z4zFVu+>&=n| zf0?SPVazRKF6ZSLGhE+?%QMnB0cm-W-%3Z8Gb7J9zsG7?gc=1Rw8Zi?IEw6byU-D+ zRCn;(YF^A3CbQo!;R{SgxZ{o56${rW!%E*WfZq1*C{?4p@7OXAV+e=RdE(ze`;Y~Bz;cmm)E4(wEMPQpo9$tUbq zs*PUKCtQ%A#A`)JnV;o5A-YxA_+?ig0$_Do{Ma~VmZZQ#B!ALP@(ysQT3tk0L&04ha>MYf4&;k zT*!0-aR+$~kwXm3wvE07yN>t`8>|#eE4#+NfT!70N;<5?n@qhmD?IRp+#-nl z6MTIHCPb@c@q8?TMcleJZsg&<9zv}7ev3M$wN|a(%&dr%U_B=nS7m@FMOL80rz&}2 zskH6Mq{!9)NIdrDWlT^UV@q5Ve}J{AHj^=LEmKSp4P=ETvcz&y=N2MMhw6a2LL!93 z$no$*kA>xS0hTyy*X!lX9MvK$!PF=E6#=CiryOCgDP4_AMCp^H4}>FkUebABj5qWz zj^1HFH&3z21xxT(gL)q8C+7OJ=v)Ux*{+@7NCa>A8)cdNUm#VBOcg!2^i+ysL6px3|rE%ns^x!W^ z6ZVt5D)oVBI@cw)QKGQrUsVxcD1Qr}W6IE{8d z3O*{27|5Pm(SOAys0^0vq9vz@EoOH7A~(71WV$j}VGb~fOuJ9$TWVP48cZIEJdP=C zMPz(!%@YhM>4F(*_fC2RBp00Jz+o^mcdT}VbbRBEhnzH?Q?e3^e>B`th~f+nen+Du zH6^9(AefGoZjhDJMqr0i#`FzkG~=1@qoT}ZDWxtlK80ckP+>?@J4?X1Uw@?@xb)3& z-WTNwvA07kxn5|#=!3D$*|NVkpU-WEcjL}D6nxqPo(iQBd66%(fJ%}^Bts=79lvE% z5_!6u@*RfEyy1-pf7HGse_xT!j8qD6q*U=2wjYG-B8~C$xP|Q>RbDMwbBMnDCiuhQ z$g^lG6DvqDTtpH1*FXQO(3~#dpKvhZW4Ah5IIdF(hg>=riQpIhYBwVB1-SeK4aI;9 zTH?wK024xv2^H!nm}wCX;W?^lgun~lolu;M-m%YhfM)EUe=NHw$~ zPh)als%ol0e+8d96Q#6o4t(c8&}`jBgiY26xEx*LGOmq#3sUrf;g?HQGK zMjdF`0H9d$)J=8mr6>Un_+A558pr+G(E((#k0;-c25)ZxE! zVJEdWe@%-u7795seMIO(D9q&JvkFsgG~v$l&%mh!I{xtqJPk$#GCW!XKn|G*SPm!C z%!^Rq+`L|h4jp*sQ+&V(m8ZWXqT2lLy-2LFu)gd1x)zsfhUCuS$8IOrxFp!{4WZ_P zNJo9Ia`^|HyYzQLZ=L!Of&X9Q?6ns1ImZ$@dpyIEW@Ds*h ze^~3ymOQa4^D+;i-V7tASTGtnr)xWh<-?jh=UC!=Xu9Fq#IZ5#z&R8thWxMkOCpq@ ziEwJ&Qlk!hO+NU1K@#$c6o+~85(kQvj1&6RwW+#*-*h=qK&UvCj%Qw0Qne6OhHQni z41D=#@!()B_Oq4aGRh=Z@2XG7a?f^2e+@-&Al@XleVW4T*jLEY+||*3SSKK zW#)|PswGqMMA=gj`tr;)_3X z3rBtnZuhq`DEks5GmFRY2fG`O10Mmi&L!ggm*8~H2*d01sI|1=$@13W)kl?C=IHe? z!8&c2LHf6rHaz)qzRoJW*{o8kUHID}{W`0Zdzpt=XO-4jrKPe;iz|~CmsOJgM1%GB z=j0w<3fF2<_SQ2`3Hp$FAvm5%e|x=*+|Uy2u|#etTY7)-ZTf(yC|i_B5Y4d0qP=$d zhWU`K4-w$3>{W*BzXa*c;&S})y=A8kd)1~qJ7}GqT4$$p%dxI>TM{*Ir_${g&(H05 zuufUM1rAtOy1fZ9uXB=bHYeGB!}7h(N#LR{@&#vi)|GDgL%rfkf43#V_NV5^ zXRCDUtxMe^9KINR_`IcVHxQ@BrEa~asdanl*+i*32$*#?5%0eQw{u1sUY}3Bu61KX zyy;rEb>c9C^y^x;H50s@ywY3F8?W<9d6#*JH02k|wf2WA*_fHSk;VV4T^WDal1GyN7LQ@Z0qqRW@jqfb%>1)4&9nBZt^_V43s4A5K6See_JIHCRyF0l%|u< zWOa)og_-UaB^#GXe5>=AXK7{Qa-0sK(AkEH`GzyCkVmc zgztf~g@H!0gFEs`f2y(WJK3oM*GVwS!<`MwhWk;!*dX+$+&P&V3t(4R-=9vf6GeOr zPt;Cy28uemnAEcJ=f$ExU{)4txf1W%Hc{2wUDMcPjNP!Y1$J5H>m~UF+}@b!DKQ*QzZ;4z!78 z*s>$oru?PKf9LP(=ur(b%>mr;@XU0jvPt|9v9h^19i-2F2(aDAweWAi5etQ@`s~ae z0+e8Juz9P5q%f^!#7{{7<9}Fw~YAj8n}77Fj2IXwPKl z$R|wOIklI@n4NQuecIm9M+up0&u=r9ArNT~=NIm2e}KoiCxjA@jK|){n_%@HncX8dW5KfN zckY5e*+F<>`X=bn$%tWhr(=H7cH)8{f0?v}dq)T%=YJ%K@`V^*%n&&rw_#sZ(EP}k z)De(c0k-8Ig#yd#W+alK9$8Qg9b@ajb#t*zP(7*xFqK3)Hzg@B;+%1sXdyP%ko=hEffAkKR8F6?HXb{?fXd`pRo8 zlz@>D-y^9oK7)Cl&HkoU_HM4qond0n~LUSgJ?;bPrSTvF3XQmpLYVs zrN}OT_yCMbf~bo5(41g6h|N~PB%7e%FSx-YUB&*U#GZ`7n0l#|0{3VO1r>*mE_QKs zxWPLSM(gX2(Y~AtB1_wf(isdHHYQED1j?%jZkUS@{dxIw?g^=df0Of|Uw|&%!)Cui zp5-iEl6P|Ga-=Vg1%@y*2BuS~U(fL18NR!zD71-~g;2$T=p{BOava1A$yz)XsEk03 z7`5ZvP>*2a-YMKaJv%qbEzI&LD}$cdwP#zDb|PWZ1Vtv4$ydR&wNwR3kL1A(Pov!2 zIqW`?;i+&XSIY9pfBswrmM7bJ#?3?%bb)Vd8~riac2GRq#jWle&2Bf&DP$PAuOJv+ z(GW!_Er8ZIgP-^e${7U=_mh%5J*N^Xdsg105c=ZG1}a7YM>dr85dLZQiV$iV^Gpe*-Ji)n_uH0 zLT{VWNC1Qle`H22gX1Nd!Ygc1K}&)c77#T?#3T??%X^c9D?Ve_y zi;#n1+(ePCRI~*4;m~E;R{Kl}B}nZ@fC&bl|Nqc}9Yi z9+sdWw``K7BBzUt7w(>#lDCVN(d<AtohXaoA7a%KXd6`GOB>bc=K>v7yZ{QfgW7e&C46gltsnx7Ba9rz7U8qq7$Ms$?G9e&B zQ~3KW7Et_dR{)L@#Yy|6Kgo+>6J8I{71=M{*j6~&tJhkS@UJ5QE^oM3ua+la$?*0( ze1OKKtU1Y;Plnb%vT)JfUTBB(5P~hIKls7he>?uvKW4DGW~o0O2&-hA_}=AFZ@=`_ z4s36$=bOk3Ta`-5IKwybRuXP}q>aiGFp5=#fTNPZBh$l`M!CLQtJUk^(#DWc@^o8a zu8@5FO1-g@nFLMV$>!>8=K5J~!OOU2Hu#|6K!%P|-9+Z`*I$cMm*sM&-6>-1SpJvL ze;OEp3k?3~liZ;IcYVfQ%H5gRZ#>#Qj@&X{5!y*X*JTJyz7Q@fuI8MK?gH|Oq}_@< zbxa~abQ{%|p!SWdYOKn>v`^15#Au&&k1!r+2bfE|0pD!kU4tdCIdFL_(<7nA7=p?= zIF>dQ+{lmj_G|83J~|9p1DI@%Dy5QUqIaNq?= zlZ=s8W1&Q$&l2bX{e7p}rL!Shln&YB&6vSuK#4GcS)JH(2Y!M9u?X)1I0iEfL@yOs z%A6O%&;9`-RqbO^+7f9K;xebMNtp>5)XU0+S@zyg4C8{G0|5h+BG*0!ayGz!e|VRT zq8J;|#(9*a*Y`S9*fJ`DtlX;Ac4eFR16cUAX**Pv5v`pq>zu%1zFmDB#jLsJn&6<` zTa6s(z0E;>`{YPZW?~*bLZ0L=_Tsgyde0-f;YA55EC8ddA4e&es3kRvnGVdV<3(K(0PiqUeNIw!D$S}=nUAZL&B_Ml8yJ9{%4VVy*$~iHUoulm)Z)guwPMxeSr+O_B08?2S z4^aAAmU+aoyUL>UXZ1*a!N92CS*bHeS@b^fx&`=DTe-;&GR9%$7 zG^RQEH--9d9(uUt%=rgDr7B zsgg!X)&78EiyOmA5uZSh zuxLd7R7~66nYz<}RAIDpWT(&ZO<+y)b`MZdE+hU36UeGkqJL|Z!9@hHT-WIgcpDf^tDfq$h9QO(n<%6mVni+F^|N63HRFeJ(nUmEN*e{uqdTq z36U7^KJB9uCz}nAe_Y<#Fddm!m7YU{P^`_vf{~~euM@E>qB)MVG_`<|D@&^^aY5D1 zat}H(nQD9(<5Wq1TPqZz_W{@P#IOh)036l~u;30nZ(9Qt`(^bigkDH?m}xphCvQ`3 zTT0D>phaU932>+0tT)P1ktnF|EmpPNT4$G1c(asC-pLWGe={uGi3LU_k*FqA7HO#6 zXx_C}1z5?eGJh`>JG(a8{x`Y7RJ~iP{zO|TsO_l=PT#*)(X$+g7(TEsT`C}^90KLK zFaf03;es!8%LT!3*Y~cmewgbhOsvz!ybA8{qJTg!V|i1Ilen5!1~qDJ^U+cB@R5Wm!BiPPJcYG^8NsxI^2=g(K*Y9CSVR z!oHlMTL+q3uD5&mA)lr32czxPE1h0OS^jhDo8A-Xnl?Xm_F#)Lkxf53`gwsDL%CTWi{4*mV>^*@d&bxy`7y@5&|JLG!`)^_o_Cvv)JQVOMiyeYQ|`2icfV#| zUmD1RIU?C_H+L&7dNyAqbwH`6Cl*Hde`{74A7qA8fpz9`hSPpwU32aX3t>x|qGhO% zcwH-YKR{=r%XQ_PE`A9daMu0)uy-P>@?Qu7Prf4rMk&5_82a`AtB+p<65EBo$g?6b z31|X|U9a$1(ggZ}Z(jzx7G_Ic4!d$vEyndrK;AE>$#a!oEbI0`I9GTu2RHMlM9-spazEeZ$=TtIzFd1D`lm2I{&H~kleimr zrf+Fc^RpxGr{a+P1tRaznFgbmfxE-|X`<)Do!X>&e$>ruk!`<>gvujs$Et3A8)v_r zJ$lBX+}k*g#Bw74U*gvKEb|gLIJ5 zjxTR!+%x^!zn{^b%jNQ7f-3)?y=!Z3+(y=ag>^O$o+`(qyX391t8~q{e=@hVJxS#$ zMM?CS8;Mj%%8v82|KU8JKexZ+pwS=+P#{H8q;5oNcQgZ%xOO+bZZx_fp%5h@{~(EU zmXrY%?Y9#NX*{E!#fV-QLW(NlyAxAyI4eoH28)}OL=yJbEA3n*Cdn*jRtT-6oD|ZV zQzNSKr;iX-&gpwEK?d+6e@X9CYjm&s(r{bi6BYv^Xp;6PTQ-_er#)!Ovr-68&~qG% zXeDg3&g;rd&l)ydnXg$8O8Q;QVw{e*X*hJwTiTB$A-NvgsJA8q*-|vytpVN*3s)Uq z=7tr{i#%qM#vJO|ZIz9YYvy<2CM%g*-i{us+w_4*5^EKH?Mk`pBsUW+JKc#YC6TwHMQ&5e5ACPBaJs&;b`} zgA2rDT0lPJE1fRFe{AhIERF`DrI+$9$OZ&sB)1_bh4SO`q-KJ=k5}7QUF5wjTsa)q zdfg3WgJpdSB_bm_SbR3MNrN49>O|pTgN5raK?FjaE|+pHy?rAmZh6$9O5D`wv}6}G znr#g17rnpH#2k=Vam@XVUVAhM{1JSGR48N$J(kfLVie(ee|u9A&X}?>MRx?{UwA@E zmJ^TCJ$*5Qc(K$Tx z$@)gPJ$|a-2VX3UZh@zce9F_zu6J9@L2y)b2V%SfUhfd)s(J)K^6&`r1r zxos&!^4D&(f3IW<%*+L>2iv(|Qg3ty%yzsx7o=#GpE~h%v|@(+DVhuFWphDf^jQa* zo(mv~TiIN2_@&&P3r=G%kWAgKdjU>eqiljNU+kwfb$oT~)y5T|U5p&R{3PmgC2-i` zAp_3c=l2Oijp3|Gw>cSyDY*Bwzock(FG8tVHfKc!f69$Xe3E}FNzGZh1F{zw?6lY` zBdw&G*L|_qX>44QDJw=ICESTDhP~p)B)K23i@p-IWR^hZr?id0kK`DrS#NjyQs68; zLDOTSHJWTcn+c8|#Px^^G@)JmP^-yp<0{gNUxnOKD6SE^@rY&kGPVpcjUHADlUa!U z;y4z_f8+#|iGI4B>js{J7NB8rhHy3@_iw+ZI-61V@1YwlTl)nNLw^w*Ub0!hqdS2b zxt1fN*k~7xyIb0jxx@@^et;yk!Kp#Y4#=t+-k_=TeXsQ^xtf9JwOUWH-WB9o+BsL3^7#?o3vqlVYNrM2=qfTyBv@zs0_9Lg!lhRjc5yG!O zLCju=+>Xu1wtFR6*W~#^24eG)X%hW42}lNhjs#bT!KS`^djw=rSI8o+7^_gof|IIe ze~K*XWh0Ah^^9-gK4$YDe$MfC^c(s(#>fGw9yj-KWPT>U6qTMH{t*@se(>W{$1FWk zeUr-gf-wv4b?2v1n=@}v+{(9p>i7KnxGx7LVoqQDL zPl$|M;75X2>ZA5hj)lZ0#oIwP;#dM|e`zSx5?72>$We>9R>=FTEI;pO+7GhPkwPu- zFb=gePS755e(Qf=Jn&ELAEMVVnle}<_4wtad`+t=UqTdt4l)tWF6cmxnD0aLgtcBy zG2a}YHkZ%)1t7wA+~-##(VtrIkw?$_=<5mn6QE3hNFd#Y;KTn6+2J5Jb=WCCe_@tB zn;9!F90DCLOKDkygeY~$YVR22A4@~088=ueWKv;>hBhYxtULASH@vxKS@x7lD8>k;$-AAC56X zQ1J*TGQ~dB5!b=^3%+G3qWJB8X0I*C_ehY5uGKr*6fV+>-?F5(>IxnP1}@evw=!1}UMW^o9Rzj}8Q1Lxep7s(#V5!& zSQN`QNwBM=yV96~B-O|1JRBJc98P`$6xdQIFs>M@kfT76b`F68Gtu=)z=l1cz*c$` z_^>hzF`9)`EEXza()l!wZzhhuB!5ref7Kn+dy{mMNk>I(#{g1mHxEL#O2~ArJrNMH zj$9cR){cq{T2=YNrwEK%3YAFd|Fi9BZ-xh%CKoh8rIE&Lw7>p67#;#I7eok!V^7Gi zepn6e1kod?Y{{od9Y(@&&`S6h#*r8Nx}lvF6$oMdG`Ri+?T!Bk0qJXhg#fZbmd>!$;1PgW{7&WhV`0)b{5^#?G*B z%GsHF;-ivalifB?>4F>0;nlsxHHu7>uwC{W^=oDfA*(eSN8iNM-wLJu ziTMmjGk;JWShDCGp7~_u3aV2e-DbM(eV8dvGrKv1>VW7ti_w2FmbUW-*~b1j0mdV0 zO0ArrGBi9%O}o+VzkeiW<_xgZ5$c@bjtYjO9%Q>Wn{jL3e{$F_!Cv z4o-{DM1Y_xEP1+>z2HaU`P+~*(=H7R^45*-s(|z$4GHc>c!dW;8h8%SnjUb?bZfE+Ed0#tr!taY-g!w z_gIyVUVjFe_QAS?dTUgZ-VsTj^~hwgY8Z3fjeh_9<+FIrVo%JKdpy|nJf5?=lm3pK z{%o_I*0fwtUo50ImK9C@{%I8Tggp(S%;NxhaH1qrt&JQr$0`evma)jkEg}$eyf>iQQB_&EQqhuS$|+fX=qWHm2lGXq&mw&p1Y5cn{?Lc zJT7;c$g_-2e*hB!dfR*)^s`5FuY|z-a1dQB)5>lNo;+@32bY$L98-|vWYlfsQ5FtO zk$F6!%B09iqh9OLs1@;@mY>cvuiF3p&;KRf`{xPlmaNy$TdyHho22WlJOb;`B%R0Y zXMfKm&5DX9>H1Zxwf$;MwQ0K9IYlv@$5rPz#dQ0sk&URyg5{ReVmA|Ldf2Z`#>kLG z(6qGOOtZ;#Xgf{UONSmA2PC0sen8mwJbo>iXu8-{z)t(*Tfb^lP}1`I)>f)a;&84a zvTfD#STqkUlJmHF92#ca_KQ4PR4P0{uH7Zw z7*%xdAx{Z}MIPOKxgTj4Kh#yi_1I#pLhhCz^FZJe+c?;AiBXYvbaL6nA?T!|BG;3$ zs|VjfablKJ;mr>pZyp_eCVGZNUmD#2e%zIiZao}G}*>HxHp%LQTIw6o6`$A3F# zCM`vH6cN?96uOY^Msy=l=Vh~u9J<}aS z*DhQmF&bpE-)%Fhi}Qh&(GFqSyDtxe{<|y58Xs)#CNv9t=x>SPA}=-DBJ=Zc(AV_& z95RxSxrIa+L0m~l_5s02N!d(y$$tkQK4PMImW71Q__O)_nI$4v9g}bi19HFK1CUes?PH6v3I*gip?D5JE-9IP z@EsH@Ag6#FPdp096$eIeCyGUKPmaA*NKPR+h2%C@pCUOfVJ%Q7>#Zs0|fq+ST0!`rgi6%w=v6st1ZcB z%+&PHmSoi9QYTHIA&s>!k8Gr-P|h)RVXNu6^h&; zUA*~dA#ZPQ&lB^L;b{Xj9QM&l=17xXya@JbwcqCt_)+a>tu8;+$fF2SekM?#T6ZxLrM zvQE1V4qB8B>z1zljxz_8lVQ@RvsBt9Z((jTRnF$wr+SClhk;5g?Zd}Z1fD^FH3zLm zeYl;#r8}cc&clyH7Z^+$lTJ726uavw7IHlmpU|GNB7d%@TzuxJh6B?IEJwC1g9uRr zwoD3q^(R8D0D|dj_S##JZ$7>Nnx*4y;sdtLI1?MhD*4QvnI@5zOH$KP|F`XY=v%G{ z6&eI+_gz!xAC6fWJb#84rC-imk1HOSa}(R1;-5ntiHU!*f6eTL4Wvf<+M4w0gAv{D zprRo>BY&F2Y)1A1AJ+@I8%L(X0-WE7W7&&piZXUQyYb)RxhVk$Yik2lR-Fud%Roga zVf@SR^P{4HO^Z%7)?dtWQmLiCRVKT*Vyr@LvP%KiHp9T?QrR;ZefEkXGoYsOOnT{C z&S4XZtz7Q+g-`l%CPCyax^!y2Q>?e#-gl@4Pk(C}3@Sw`i3OPgHBd1?KRJ+;DZ((g zJ29qJwplMkYZCg2xtgxcV6uJhFYdk=wW1BKB+GDEWjqh%`oouz5&XykF(Hwp9hLYx zIzNSErc4aV#E?_3sGf=8&O+}`0tvEiXUfoUW`>5l&#{gc%FuA`>p!UO(knwlY%x|5 zbAK&tJ7_zEp<%CBG6Qz1lA&Rc^@v|2^-Q?R-}=FG|s>#k@CVI6&Uo31IeR8 zZmEgAO(wfttPgTv^C-BDn9R+Yy61R~X}o~%OQ0EO`jqIx5FsdXRXvOi#Qwc7UGGZM zeg)4IaB(u`m!!t;q||Vu8gXVVsZJ(UEzGQ8c zYvOoNWH#I=h|JEWw8k=DVnShkiOSn1yb*%~*Ut!8J~mQJVe1l`@RDAOX64u26y zy40ONh_H4%@~R2gk;*nphl%_B(Vn%M_4;+aea(`Bqf~+DuM6$bTpHSt29&zEe;h`o zyI=d*num3>_6r*=>klCS2#qfE$(L`gwsPzj@H*r?FkwwYpST(Q0KCH-V?mwZKHJuh zzu4sOCYi+h{-+f3VYeZ~32H<0V1G#AU|_0$di+FtOaA)s_~{+#^`C3gfhcZC(y!lY zj@!&jHgIv>!&=u}GvitYVc6T^rcVXnvB}-9@OJ zZvZhJBSneX!RK*q&-XHl%S@DzkcdlB6D9aGKWIGjRRd)!z6@ z&w*UV`aKvk+nT3eKlqUuvBa*WQl}|SF5QYE4P_wLoQZ-?;~Y`51b7q(vq7qGZ2M{K z0R7^6eRuRFHOGkN7NYWxA0O{NMLrMc9v5yj?hI=aB4y(w<>$+fDyb+N@*MH*|srXXWlUx`PonOG{9uFLV3NI)kYlOaDBVR3BQJD+t9 zw@TPS$h`t`Zoly)&W{}T8BgM$TJ7dxO^xA~n(aLs&a3Gf+LLZB){dCRLs&a1Q)pG? zOQ#|ZTuY%6DHqVhcz^Uzfkl)epRA1_=4Wuy;Ci-$0Xaj%Iy=+!Q?1x=MW3F_PT$g9 zA(!L1POUSN-YbtEyFZn|O2bbRp`Ir0tCv5;u z61y6P2ET2I5phz$MI}#*EygO8VQ7PZDh*s>bFcZVb7;fR@MbR#{t8s(A;8?A>hN?m zzWD`kN<3Y`558Nzs60U{Rgm@Olk|=flcGR%@OswXVvr=*F@I=i3qK=}6n6!PEbhcrBY$A1 z4f+Tk=wU7)c3kV(D?eG!@as1&vyYxC6h_6(bh|?`u+yCtBx@^37FUc_C`h(JK$V7M zZ3W2`BvX*=XsuycFj;VBx+^A26e|60o6%}qm>#*LsXER}5xH!)RvGcDU}mWasrYq` z31&uPKYwlpGxH<)BZG{WYhY%=i5KHf-%^Az>-sHkjI-aU55xTE{5ZuY(Q&e#fZ06k zO&IeFl5@UluHkuP+}e7@0vEbOI?XjB?7n9>OViR98Z68^O(#o9Bpv)x0i!$Li4wDV zesj$kdC8CBNJu8AgNUj-&rc=0vR&6C03GDjQhzrw=puR3Ggk{V{jUu22N7stA6&94 z87{4KZh|y~$B`y6zkB!%m5elFCYQXH#%9AF>onJGq>>ckYuv82htiAe;uE?WtY{n6 zNwr3fgedx?<0G_m_gTC6K@6esDoXX%pw%BRsU8VZ6jZKMtGHsULathgOMD2`Diir} zgnv+V)oNFUi3{exx7$6L7G7yItPL(`^iArZ(c_!pv+n3Ka4-gJ_#F&f9@Y{-fWL%U;<;Pg7+t09pV;88xwFb z5ptEkLc@R~o?zoQ7H_NgZyJ@ z=oUQ!a_H?`Qeg;fHb(&_oO<*d-X^p0$(4k>6d>Bgup=U(FBuWh#+z8I5K&w)R-q6P zM^u$UL~@2vh^X*|-!CFU>zBV1)0GT2ulU8I>`UuXkVx34Vw~ecoD`X2AAb;u>tOr^ z-$F1sUBneLdu>7XI0Aumt=_?VsN%OQ^C$VX!$UDRM*`0nH1N2F<$*1S&vE){uNT3| zRJ}$`JshlC*{;c4Z4x_=y^E78!dVj%BdH;aY>7KoV>nRg%Pv5W4A`L`PuK2S6!8TQ z14|iems_kW3HzJ@@T#L=k_#|<2|ob4Zsg)#FG5`PiUvnC896&kmM z>*M53+O{O;l=8L^7ZLz0oOVXuO?eUZDt)gv-pzCYHO2U~dp}O#kVc7tfYf-=d7pFc z(j}*c*q}e-z&^{|=zZzQfpz-v8g!bA+RfwRZ*ZF5|NLr2Q(t1u_0 zXa&1iJcGaweptS+Jr%@`^N0p!H%LxmS_Jzp5ftcCdyO{|2tqes_=9$rP&C|VjFWjM z15M?vu-RC=F;lTI-?dQ@^vYXB^Rk%KG0*s7fV8|((*kV0bAOxFfaZCwTGMU_wTwFL zL6gdeXf3r~vw0n4(x$brmt(m*!-Z`M1vW2iQ{5nBFmrC$+Q@WQ3;j*&&-InWGhQ;9 zMB+&Lop2;7tWY*w(5sn?nHZ#**1CJ?#x55c_pZW7l zJwR)4`+n`FkZi11CrDe4@#h+%vUtxroJf|{Aa1}(wSPB8wPxS1o$l0G>IY*Jf2#l% zdZ_uyGFS-Z^=d`-V#}Pu;qC?gtEEj%&wUp$O0k#-_}RjK4L(adf|zcKtxz~}0ihv6 z^Z`~c>9I(Z7o~LA9M=a8>24)H!9!v!qLqX@>}P@-Wv~%uBJAcI?wFFP2AO0BaVQAO z{Zrb>N`KgZ>a8B#lOx5wmeexTLA-($%Pm8Z)kI|0BN~3UZ6@C3Dhlme zFqK=(0x8-RNDJixPg;#`eK_dzq?JeG+p-lTbu)7Z7Y*25JJ>&>^4B!3z^Ow)# zL-0<*yp+W|Vl0$>@y-4RDwmO;v#1jh3Q}BGFxMr$DWK+GM&ByJoni!m^X1Ol@o=D# zW!0$0s@SYdHXBxleZ(uPXU;w}Qm0~~{V-9t(a3|GPLYYk)BiJPqV?AiEUjEZgi-ixx*+FW=)g5}!QlGdE@6#Pg|Drz}T$mmhzS#|a|A+?9WY(@h!vVln?a~WW zy5~9ObPd6em-<^0zjW&LRtHxl&f-V*e}7U51YwR=l=KyxDXdp;dy5Yx;CEb`+XlvU zW-q~0R~#%I+*_h5&G-HmPO~!h(EM)Mj*%*6K$cZyMI5OooedzwnkFt2?`3oAXT-#_ z&6#bvp0fr|VL}N&IE0~TZ0h@;26x)Uy)l3PlVcdx`vLjp{T<{Fa82*+9X-2Yr++lh z=J#jq$zD6O&?ht6iZ(aUIRf(#O!e6x-yM5RsEiUEr8js0QNhC+#E+iHEb||U6CN3B ztN97x?Sw@nl!%!vr<;ZD zL3sFBMjs%wqvg)?;l_$FR;SX81^SQTDaBL8QdPUsJVh-Fim4P+6-!om?AhnTRQHkl zQ^&+o{_v`%nN0!0pX`6!!qt z9c3w-y_JeUhnW86rA$}I6(wQR2BAnXm#kT(L1d5nyfg}%YXHDttlzK?s9#T zyG~5H?K(%INdL`lYcv{(VxGO2!1B_ikVfRmF7Q)ylV=R^b;=m*OFzD}q2y4@Iz5&|xz^XvXS4>H~BI9buI<0StPw0HEQ zEoN0~;`Q6}9Vht@R`%FOyQxy-8SP*}_S+mo-RsstUVVnRW`DSWFAUv@exiY!IZQdmfwO z>25toKiXnewO*aAr(T_{C((izJJ4g@czV3lPBZT%PO&hP@PkvR_K8q_<~g}N*?pp3 z5f!CTnSG*OA%7KR%Ry=EP>)wdfxSZQ6eqG%flWTG~T6WJ*8Tr)l0jiMqdMY}p1 zMTJy~Px^Pm^7I^oJ?4Xo-l;1U;@Lb4lGmm1CsGd`@xl8-+BQ_E~Nec>=AS?nY4&rJ`G%jiN$I#jnq%cTVP~KK)Pa6eqG% z#qzl{1ol zWwx3sosr;HbADGkV7pP9jbC`qo6X?Z8MV;OrXqUHpgNmPh4h-=42YqaW30!k)Tn)? zD*Mdv*m<>g+X@JF7Xr zYbl`EsO_d2+s*jcS+&URrYh$(%Cc&yro@wUSyUlY2d>4{SJh$Ob$<+R z?O4WKo4#qf_J{E{wRLAsiuJkX*_vy9x6G$E&9bc@g}HeRcWda(q~7U_uaWj|`wzpr zyXf2Wk@nYe;UCSe9K$u77vq0!p;^~+>^}^*V3TY2y17Y+^o7$ix<-n7Em>)^dEE+| z+tEVX?>EOywx}m7&|bIL9b?`ADu4V1jt$OK{OvF;Kl|L8V`_kE6~RPjg>r34)54he z$O#61@NGdXD&WfdL%B8Sk9{bNpvSOa)IXEkhjD?Zpvwx&mQOb&? z8#PN|#XM&BlVrs_%X4s6{9rE?BW7j9(od2~VZ=OVohQkNd6wtkj5xLu!bvJVl=5Ne zr}w4sVIHTGljOrZ%X2tBTsgM=Gjt6}RhV@ndZhTt_LyP;E@U%ZO zij|XDxpYsg$Vm6X0D1fiPk)lr^Q_)3oL=gn(hGvQF|uDRB?^ia1*LoC9zqo4@ijk5 zQIKc#PF@szTlF0$5;{>yf}JHn=@&E(Aqn!NlR8OBkZ1KyUJ?KxP_m!|S&)Zqj$Rhz zN&0h=vLMguoxCjgXgqmJ7L*_h^03VjxQFC%n>a~s&$B+qZ}_{1aDN-6@N>01Q#biJ zPeP)CZe}&EyTf)sG|@o?tXsBg*Rd?cB+pXp?#3CLgFvx^JW0mQvwxZgXWU=)99I&m&bN7D{+GwM{GievS30KUJ|jrlf%B~DE(~1apcE5tXW~4u zsZWxL`>O=K>bzJu&zhbP3&T%oVKe&O#%LT|apr5O^~fBFmw%tx%dtcIVCWG0*N>tg zF4HgO+_1Hg>8=*~o7SJ}D=%VjdllniaIM7!zqqqZ&(s&@--h`fBOf~Upew>hy2%Quvf*CoTM(uB6kHeao(|%Tiu>4 zREJ-TRmekih<}vLHZ^Wq9c+1~6OCrIFj?dmzc@y0(1wHQ+afmNc}skfinq4OLcbLh z!^3f{*F~EQ8<6|AUsIjUsQdTOjh3zb0@tKzL9h*fPHyk4)mps%?EevMq(S4PZ>ezV zuzv>i5Z&ZvFU&@ZW2DN()7qMOrfta??{I63V;SCzjDHt%X1xDqNh4lIdN>$#$9-wE zeDO(vJsjVlu>WMw4eer1d#kxCV`e^?vlNqhfG&Udcr$#~9eqYNC2e4OOMRtX*ejp{ zeUT74QArB9r1-M-N*5LVn+H$#*6u&G+RejyHZxrJQnS5h!+AAbLwnNA#oEbkr7UIR zxNdhTB7YOe0e3aBN>(rz`KYAXsu>1w+6v|cQ<=;DeT+_|m| zaK3IY97E)w$ji-k=ejXwFPlE3(}DM71QaK>?I)U#g2@?UuDwtpgyQE)(;$qSOKq2$ z>1s4H33-X&5&BsWzHPdGdLlA_yz;bIyU)h_Dt~#PR3IhwOS_A9EO|201Jx9byX)1; zc05;G5>hc&Wc`yWY>aBnK5JZFA+~%>yZXmcZUYf45p(4r?n(6g0KQb;p;viAI$a1d`Yghl6ha zwKfO~6li!Eb-Lqg%+*C|zyB?5PvZ2r#D3+}BtN#DrO@Az> zlw--+;6GYifF-BXYBc*tZOQ4iTh01d)Pus;zYs`vv>|(rJ3H2r`Vl@(E3;bYR<)epk zZFf8Hk#uVBX;uQFx`MF|)meRNuYbK{|P}`*{`>HJ*GUw zS$;m-9?|BXb(jpIgp>bmQ@*)ceQ6t!=CchZ%ib|E8PTy`YGC!+y?cP|cYo|1>Dqhj zm1eD%Q^OJ0A&pGI{gkz}2PU|C0+S3`UzkJ?DVLh*p^1hwZ@FvJBkI@3&gMziZHRQKbD=zqIO!r~W3>uKnZz(jnU*Q!u1EB|$6?&-QvTM>haeji_tDrS@zu5&jVL z=Gx9BROi_afkTA<34b!uJp6U9lnPnz!{GBeoS_!mHC&kIDg7sj&JBklD<(sr7tPw zR!}u8J{G8&mP({#fA{2jWMbOj&;=GBM2Yk$`O?^;s7*gxn9w&o8luq)2U^!XhN-nUH4drtb|>OewHy2$s&chiNQ zjlsOjwl?acM86kVyNIkc4ZZ@%2(OyxG;eOO0q8zCHVm;8=6H zS{O^i@`_g~b{c!15-)VjYGJ}np*_sKBtbc5m40b_@oh1R)j0c zALDs@6w1MtTuqAZEqsRs4?Rww`f9r-J!&Y+8}MQHHR)a6MW|$NKmpn&zR4dvEA=q8 z7^_g;fE@KGZ@|3#M~f?Que081k2@1t55*@%J3bv{&wQ6BN#`hI)3=l~P!~1B;)6JQ z?Z5yrs(;4HM_wG%X23htReN+>JMe!Vu5 z-+~aH6srizi26&tOm)q_a-C?FQ#^`U{~= z06M8VcN?^vp;5XLFc;x17F{?j@inu^8Gj2qaD=m!_poq5+mZV;Qo*hCn#1ZfL+~$5 zB&!4JG{Y}D_N)}0M$8_Z&?%+U#1>-}N~htdN9i=Fn2i=Iou)LMhWeW;Ek6=+tZV9)I(q zz5NwvIaDDM>29yegV@V^G4EOSo|U3JbklZ9DG#y5ScOs^IOyY#g$MV`mN5S z*_QTDcv8-Rh@^j%UFeI7$CU0679T`+*ntP6q>qqDH2#Z`$CG51uPS_;T zB|y>e2-v3gVW|66aE%8;1)6ia~e$N-Q#sv?dlT$X5yI#|bPjjqnW78?sCm)1J(YL4`s4 z(K9X8m6uc0JBHoPq#=#}Ei5=1Qr-C#ZT@&!ln40Lb-9pSbv2a<%-LN zBA3exw+ED)iIjHD!{@hh4!7ESnSkbArx`n$5ojKuhsi+m@co0}qcM?DnV42Qcy=hb z6p>s63XV_-u3xj;X-q~1la7oTZVg+lfi$>;_@uxdj&2_8wKFrcRCjLo zKZWxBXYBVnN@J4QWGfMOarf{Td8wi=bb z5ueqKl93viR{&-{4yElVdLtB4-m_@;bTO)ERnmF&e+4 z5u)U(57Qx{3K0tkwO-A^1&O^x#ErRJFWG#OSBUHZ-HXgIC@P-)o(|Z4F`R|IT2bZ2 zF^Iad+qhX@!tiFGt$#^p;FhXDG|hl6y*$2KM^dO;F%_oQz%uqIi&7S@)YN#hna0av z53|?#gb&hbcM|XBOJrFZ8Ieb?^5swM#-KH6OKsuelYc^8rI<)3D8d5+vv%WE7bE;JWr&RAh`4`ZGrDh0H0Rl3kqml85V?AT(gLV;|KdKAc(api_aOUo6=R+ivWh*(*IeJU9Z z8;$Nb7k`yViJyRBJ&KV5+JcjTYfzxenQhn3cjkkXwpTb$4t!{ed(v{!S3DftH~73CCB9oI_)L!}Q+G;X%GhG8LSagddK9LVVag2!j+QG- zsW9bnVamwtcUtX1cOaX6@kyc76`7iibV=@tRDZOYXX$}RQm{d$`kbX(iWzjbQ>PTV zIcWY#frB399W=o|E?Mc_2VEOC8x3g+Jn;z~G+X=~2R+_l&{6i`ha#L;J^qqC8=dodlLhcH=soVss%WWU%_Z*aPEslDn%(Z)EhWPtFF z%Y6Rn%Ir2`nC{GCBx%jF3XDi~g5q=~RD_~sA3JP%mW7KanWqm2=jPaNw-5~(#F_>- zfK_$$*CXm$aOElxH2=@u)%LcHBjLXyEPoD%98h#E`7Q1u0sJDj!M071WZS*{a*%Ds z(Z#k5Np70`^~)hCS(Hh=*|KGc`yuK|^pGWPthR%1l-S=#+_D(A$F9p@sh(VOOPIH$tx4fTN-PGocIIe_n4DFSARTk z>?iEHT-q~;XN}FH+|)I#sS}tIR0up^oOYkBClC5faQV-*3#mg;Gu{;Qr{&@!T_m>% zWL5FQ7_dkj`^C@EM_EB^`NzEs_rrm8g1$o0{rcr^*HfvL_eQ$u6zDxdA?3ucL& z@{6M*mRNh2cuv85J*Z$ZFo*eQ?ealG(6dr%?o#@W{$;cl=*=pTn6^u- zzwmMpQg;c#<_O|22eAf6SQuX7NeQ4JgXxrZ$;yuU3@g$iCUA0B!(stz#ys#^3dBkKg(FV(OYTlY4YKW*rGMeP;PH!!TCfv@ zHE(5r;Qse$Pg_k@?Wyfvo3+r9?P4yKi--R{us-=MQ5{5rTU~ zcI9ce#_k;Rhh=YEWLD8EC5at|2O<7_@x%EL8v%u=25+9Z?t=skNJ<3OAr&Nhrgj+e z_L5?2Q4c`zzMnb>8NQ~YeEH+y5#qC~)erjxZbgGtG`?1BVSmH=0apP;H|sSj-qd~% zvaTXq6)nKF1lVHlIo%yEMw)r5)98%w%#vQVepdyW1P_iSduH~*$B2FKF|ZF*X&;DQ z6B3oM50W&nPWFKsAGSk>zwD$_&ir_?lGFVj(>*z&NM`XV$^0jn#JTu?+S#?IwHw;j zVMr<*b=rd_?tg(4Q!b?-lwBcHZm3f4`ir@=owp!mH_W;uXZKi>Q?BSWA;F(=S*19| zlp9gYup^QAf;Dh(*%~%eY-!bD%GJ^?$w&It$aFnxg)cc)jM)sf9d~~I!)=D zKS|GN-H|>3T1P=gt_p#YLx0!@T2~#9TRbgczwB=x;Csk} zT_&8$mgd8P=!MPVMOl&w32Pswm4Q2`hO~`l4R{IU!Agak|I2d#_1f(F{iNT zSQ;y9y;&GI2#Oi-2w)Y{aIFROiQ{b6o-%bT4@MRK;rO$#kLNubLH-9RQ zIfsBQ2ZtRfYr`>?kVl5~(9$!sE~H0?-`S2eN9w)UMx`N2G|9IIxNboLc+hAshT=k!x zxtbv$<}6|Hbd1)pO}T~@y(T0ov4-t-TAjfj$A{QqaNcX!X6iL;+&~LS)VpTZx;$T} z_{Y3f?i|w+*GyN;N810EYu`LdSGTnLH6PSf0dm%PWXLz;7T9(U=>yZT<6j0WV1;==71+sa+VF$#NkbkzZ zX9C)U+Xy$K;;stZ87ov8RfwAqc4{Qc_@I*|7qECE79Jeaa;$3M4$om)QVPNr7Ghe4 zDisic;Dm2TFQ7BQ%#5R^cC^0cCu>06p0?)ekNV>`{T@u$xSud~a3?i!X$*^A6B2x5 zm{n*p6NSZEyfGXZQsL3Vd`Y!1tACR*oPP}A*8=y(@!~Qvb$k+RN{cjOvXY}ppEP}> zU(qQ>tXddcaPi_{>JSDq*B}u4I`@!m3M-nLfk6s~PlKD=2L*0kfv?2!qvkRD5{$s$ zXp^;4MsTysCvfAhtO=y2M)uvvKWdIo%=iS}Vb4FWl(h<%!g~VSV!B^!ZGU*v=isgw zuvl4oLBYw!TFiVNPNd!x7>J6ak7VZ!rfvKo&mi}Els5?A!LdPAt<@a#BP!QayK1A$ zFbScu^DdhrRLO(xT5lz<(aX@^3O7Re}R%7Wtd&xLH(%{CT2M%n1#Z6sw+)PSr#x zpN4}p65)>18i@%OyLDBZ>%rS@W(uhzy9*(@*;ZRUL0BTyBrK4Rx-dAW`k*0IAsI0e z{F;?kkV~l`VR1-jwCFVWr&f_J2#()h*}>9S%w3 zr8sAG`kHh2F@?nWZf){Kh%dWTvN^WnNQJT#@LTl_$L!s#X68@j0v2DlPfxDt74AB( zv_|s1=!L2zsaQ|1W7lyCA?|&6kdi0Dy+gRXkE(oJXJ3n(ZV--JI+jV}YKiZANOP|Z z7LZ-;2Oh38hQh+5R)3g#fjFHZqMiH-Uw6*w*)ohwo{qtT<2*iC`*35V6Bxdi!RU+( zhRRvg(g`f^(I|J~xiV=~)<{#NzgH?qyn;x7@8Ir_N9pKGp(=HD^!22IgvB8peWKTd zgmm;tM_(pKpAcWl@93j{0%H1Af;*@zS{oW=b$13b{e$5Jw}0bA=v5APT?X~Y;I0!K z$#*P#&?(M=$N20bs*&~%0$nkpTD8yp1JGceLyoa^tlvvUKM)RA0X#7gT) zLeyyO)Fdftw13jdAXOm+Vk9_|E3F`XsUTr-NN2L>H6bBLVUWCxJcTnk>*|&%UUs*f zj2ab_Ds9y0{u-IV-*Rk?JXzG}=wxA|#{<5DWJ)+D9-cOA^c5|Zw_&5BG;yORnJsuq z1`l51t3zQ2Zj5vT!}l_nosrQ{Ih$I#fpf-<%H?@-+<&O__(}zl9^d4+QK>4@)h88% z5r=g3iCz;DHFNcq*s&txM#ZFxi5rDHna2nmt+a=_jDe%lVRsORUFI0k5k!0L5u;gS z=UgP&=x$!o5KHB;(ipp}*=dh^*p%VfW&Z1jXJdxQ4#qy>1Y?AQLFAo}0}%zCK+R;} zt&Mqr7=I$&8aB`$o`CgHvoq*1-K1)rPFoAhe9gN&z&FKc8E%eO%sI{R`e;vEO;zow z?Oum4OGg^LeCo6DX2*MrdhJ%H8+*ZkGBQ2GT11HWP19F1zhM-2IbQ;{c7)g(cGTWB z;W8NjgLb>q)p(L+i(|A7+_qQ~vb>~n5k`=@i3(o;l|xt5f?m2;g|{L zXmiPBtJ&&qt^EQdEEFcKn#c`qG|Pir`$GJ=^S>{0mxg~e7ytX?u~-1@Mt9T;R;vdQ z0O57bFdC9eQX|RF=YN}y9VJ0Ny*J!t4u2``57b9hBf=|Q4*eAs1;a2rNNEYRdk?kh zKy6O`CDfiD)J_;iLvl$6wQPL_R2)&VHtsIL-3jhaaCdhP!QEwWcX!v|?(Po3HMqOO z1o+ABd%OGg{C%dUr~7n$Gxy%=TT^wvQ`LOI%@J7!dctEw-tjry33|ZOM;}d1xK=@B)$MRywPL)r zBjii1#0g+K)qW7)oXGm40p$|0D~o~nm&wl`vvWS0Wqmk~z7wP+w3V3x?A5!s(l>#;cU}9MKwM{VUnb^rW{d9;CODdou-g(^=(cZ-LF{d8 zDJ|O8{N_=bcE$Ly2SUgY_Gnc#v0h!Zm1bgxh z>;Uj==OliN`*kq%y|tuYEWwIPqyP&xs^wPXb*3=eM_ie)HwB34e0q^#OSqlO=gT?M zPPtzMY11sh)$Ev|>5z}o4AYR4qZcCC#8eZY=h#7h^jk29Wd2$BGOTmmNxCF|quR2t zFuv05qj=4H9ZAABI|vecuF}u_{1ZMX$QEEJX^1>IqQVdU;pWd0J@x2pnohV_s-GvT zb+~7`Myp*S_IiSRWwbSce$Tga9!R*|d4*#aLaWd$f*!ZQ{W!x}2i6zZxkqsoW{)`o z;u8$~s>Lxf>i10)jhxqzUWPQp&o2FZ`+f{b_{b=d#e#(Z6sd{oEzUc63`v^gcoN{q z7itP!jI!WC&PyFyHch@NL=%+Ey9z4DFJ^$roLbAS7{dUq?KYXrq{G(}6x?5=hTcX% zvMNOJGUK;#aQFd3WzP!dhd$m^0|HYsC-;Gkza4%$IEEx&BObK;XAJ2SscDFcw~YJ) z&2k3W(j<|9T;!rWkst}u>PjkQE*;=lr)8t-l;ZPLdYM+Si0|ZdYk)I3HS(clY-`;g zXgbO7&>vX;?NSJhaDl)jQo&tO>iuhAyM&~>;+KVJIrq5!U;H-x;1q#H2OvMYnP>6gRJ?De{J>(n?io8b0*2NtO5rO+Iy>s3lCd`}X=UK>^Z6vj z2Mh=Z$j8UI-llCK7q$;(QY1i09am0D+IKBoU0Sl9;G!`)i^E z1PlR^TbX1S3;PlW=W}pUtQL#C>;?p&2Zf z8kBh3{x}NXX>rHyll*SJWU&0X_M>^dD$hNRO@|Fzl&3HF+Fe?6s%;t2%00)l>bv`A`l*>gKn@N zrJ={+kGW0Tja$3&p52d(3@lLkCRVPqE*emTvm|ib9Ww*6)dLjcfxBG}%`K_{1Jk^^ z#q&ds7{|i8Baavx+!a7b^)ITTXKWlkdVF_JLygOApa2LuhsRMXx|myL!kMl3pZ;T8 zjz3q*Zazpqf>H;Z-3~GvJT@RE5~N(Iz7hon7l^ohpGl>j{e7`=;;*M!Zfm+=;aFkI zN#<*$s*x(;LhuhURV>^u-yG2CWGENkn#aIHQwTNZq|FU@0mvR@N!sQOyI5l8ysOto zkwRlFigl_(cTO9B7KRasJI0VZja5f=*kqQD;SN$V(dLM#hc{qplCaIc-!8d!3W)^h1jtUJ61!;y+Q?|TNBG(TsE6$+z>RGI*BZ=*_{vYEwv6@ zPIn|Z5HNfRv7#}7klO~L(75Ok0aUxs_BvwWdJju2>9f^08W3$A2Khf5Kj@!kXyN022HZv$N9#EX?n8^#18N!5tp&!p&b=efk-D&>^ULS?`5BmE;^sJ_QA zcp3(~{rGNlTh^pgS{g1>f}fL&Uhy5!S^i>I3?KfX$xMs*Zf%f5$jBUw)>{kpb?pkI zPnsO=gnTzl^gXvU6&GMm9xEx@Q&Igp)(g1VcORGOC3InV5ti(83EAy#u?au66)<}g zXWf;*uvb&@@ifyjZV1FXJ6l(7`x4_uPyRSSzAS7q`%8zXV`@Yp5?ZyR6sQ5H4tA4u zwj{iBqlXbzMXiPuDjPr7-Uh{_j4d5_EOR?P-L}z)Hd>5o0n>#S-W61jk`C+vSr#sb z9lf-R)8oE<;)DtBQAQHt=_T+YZ%%ssrT5w^{UM)m4)-fbSBPa7OI)aa2TMXIVK<4! z#p(bTe!X&^+{SHO&^SHrIXE7WXpK5C7@wGWrQ7x3R$XEw05bHgw``D{AQdv`01d5& z+BkukP9w)S;X3274uYw&FJ)8TKQA6HVx;3;93mHjXP z6?1NWd+)LKN91Mx4n(=S53C^hSLDRontKzdj!kcOeu97+UTrZk4|^GUG=5o2oR)O{s^?UFx2>ViQ;njkHhA z)rbz(Ory24L&t@RSmJ2Z1;bdoTgbMs!#29sGebbIoo8tI{;x!K;!5gYD&M|sDDTZ2 z@1aV^%Ev_B(6$AbMmqp#BLXl`_L>%+S%=;E^@UtTna8ajQY0{xRu9NNs0)NYJ5Zor z4Am#~Hs0V~c1(=i>RwVww8-x`ePj=?ew_YC1p!(>nM3_*>o~oWO^Qs`$a&ag?`t`Zo`qYV$G{`| zcA=OpYwJa>ujrJ`O}WCgV+Ow;m`eert?DiJ`Ip+Fylerco3t~ZKal7N3M$Zn>OC{I z>j=UKvjl0N5W0z5t+i*h(pGRhEme`Yua@lEm1uSXFJnA9g{!8hB1EHC>ZyN_;C1mp z6KA~;X(P^Gz35ciQZ6>Sij582I5{e!iB5dYoLbIvMmkcPn zC=&bd3N50eTBW=m)?&ko%);@#n+ha*nLh$XK;jj!Ve*O1S>Qni9TntxCl~zw(y#;q zxn`(uNXaEN2R?UvcYTyAV3Sy^1e$fovpUWvR?N%-E0UdB)c@L8(1xZ>H)X-_6QD?! z%0{)LUPcb&5^Y2hFY=q9(~-*{p1~SH7Sxw^(haWId{P81m}(i?C!raH%!rHgW|mVzjll4J9rtrkWI(>bvk2zhq=7`#8no)5FdG$8{`JC9j8rhqZ(d&& zOF{}x9-x5VGq;~EfkqZQ#gb#Jc&tdrz8hjn*TQdqHLMqhAFo_2oD?bc;1zsrx(@l* z^x)!(^&s_xE6w5}*c>b?luShtXBbWEfm>Qv2$X8{8DFXJ9~z{VV5m&PmjRQSavQM? zV+#YH>6JFppv8Ng1GOT)3u6~3ev;t(wXlu&d75Zw43gM-I?9<$ur#%aUTZLh^gfFg zLl4L69RK`ox>vaChfBV+Ch@9p(sp1Vt|&P1rIpqA^0&JH(4QbM;i_%+mdOc%(>(CVmdH zXyl9=Oy%O{NWeZ^Gl@dIL!ck;>5LLcAJ2pp(U_-1p%jAVgSJAqF#uREkyU~j4QEf| ze65)yBDSF#4ne|O#)pz+KrCmVqIvD$yo`b_yvbvagw9rfqRVWCdJ|g0H7oS>Cd?k$ zqt2@R^)_Paj8Nx=2jbTUJ49Cvyj5>VAjMzHfM*{%O#q_ z{_alyld+Id-L!Mp>=yuKS0c|EXztdOkIb%w7Z4&;a+`pgkl944(0>zbDZShB;f4Kc zTjy-jC&zkJSW~8{zDC^5Kx0Ec9fPKwHaeLZj1^YX;dy0U$Y;TLa%UK7#`9FqL-{@I z2whx8Jxazpd$A(eYFo!x1-R6xR2{9|+XcvfI$RxMg9-QzfEBDRS;m@Qty$Js?Wf4& zeo;cp4Nw+#ja(8Vbe(16 z+!Mt=p+Pf`9sMEK=t_+j* zJR^r4Qon?=+vo~x!u|1#2{Lfc?i7c*kCg!u*kE#f>qn-WIk-PS?Aao!WBpUOO*lsYDMrheTON8PxLe`kn z8v>q`*mdMO$|2J#0{_$tf8+%aq_o;%(&q*G_dTkYId%CHDRzbl<|3KHuUiM2+}Kce zmxpPdA64Fy-G!~=416$_U?wl;P@&HAy;di^p8wi0@h8I81;b&G(%4)&>Wi@_0|q|r z@Zk=DUWqi$n6YlmTXuC5KU%PMyLuZ4y1l@jSZTbjC3imp2lx8PCxCzhj#i0_?HphC z5(5E?-XHk6ba**htk%T)i5Vo$UN=%x)W;LzDptpGRUlzAA%rVGEN{GjEm$|?d3_hT znx@iao&IHiD}`N}xsy2M-Gd}BY|}DN#2c@G@v3t!4Lph{M7Uj!TMQcu%zn$EC*+)dP`p$qOE&#jd%qh4!?Jaz{ zw%e7Hn?yyEqu{umi2qEEwUemp?Ynyb*Pd-{ z0PM8$rGK!!IGdeY>%BuK^b@upzQdWj8bY_iQO-UW8yeI82I~XuDZdW2?p)Rq?FsiH z{rMVKjwp|m<`<*AU$D)(p$K<*lj?t*br1*eet}L6w06Nq7DEc~A$_$p>HvDial_== zz4KF!<7`|_;jj;kF@Pg(j(xdrl2w@iSDN1bZSLtLuJW}6&Xai&BqiGO=XbhdD{;o@ zZ4RelEnN9&lsc|fK9e(U6Rz7R->pCdubjRadZlTY9FumZ5OW?aJZh?JYo4~0a`Ici zwrt&n|al!15R~M>{_!?a_VX&MBloFv+*MwGRr5&uIXjmI&_+9K zu5r@{=+(fmU9x?&6Xd%gW%|0V>jVq1Z2n~`g7FT}Rt#XpI>0T$-Kt;cIkZCO_lw0+ zyu-c2kx3tD2se9!+fbxGGq6GOkPw)k1;>lKvwh+tO1kY^-HK;ENavq zti0~o($=1ggLbxmWu||`7Vfp}^3!(RJgCuc*er9T?TzT~U&)r}7_hHUSa6?_q`w;p z$i%O36<09>P!+C8HK2$k(-6BIWvQ+lc~F+3^)V^2o%WQ*b@nesPa>h5-11= zJVYHf2^gR#aYBB82}SHl@=xFay>wRnHyHMdfDyEIY9hnc#3fSS(Y^bdC)orJG&P#& z6;bJPubBq|bDrO;88*ZPH`V)zo!bamw`;9xV}hx6t7PVBMf3HKNYdTV`**7sQ_gM3 zeo+fZGTrhte6aqOi0a0d;9T0}-i<7CR=VNDi>Uxz|5nBb==@HZZRHegWP!}?Zxoq4 zb;|@kmx-cf@eN15CKI%62>Be+l+v^u^0|uj;3QOLEzrx9+XO?~yL9<_F9i0pK8oyf zKMdh<<~tfOM640SFxD!Df05iy2-qk_YL$+;B19RIH8V6Y4;I*Wm6sWG=SjD9)C&AU(<_x zcMg#X<#8&2bN1fCtw(*)QfN#>E@3zKHr`JlDo4beqNk4isI-ary( z5_U~viXj}76z{XAnw%0_Hhkt5?k}j$C4O6sn+h(`)S;MP6OgRq9^UoV$y{5)@uq0F z!+wr{-1cYIY-~t_(=pa4LB-4FF{PkSe^Q>#GpU4Y4muN!{6LRZfG5<_nZiY&$U1zv zOase^1@SOPGqY@C**CfxkCCCidu4H>cFF|<%I%gu=;qe@;@5T8zo4$KGj~kx+nG9? za+CE9FB&>r#ixab`&K0nv`ln$}$C2)UXmL4-UvaRYQjmV|pK5124 z8jH7bl$+o&a(JJY3(D-40t|c3_&E%*0S)sn2cRZ*v={zFW*5}Zd0S^61(lY8V({Es zQRkQ-d4CCR;O1I8E(LTx`s2o&S#FXT;lSj{^zNlMW=}n}>cPM-*pyF*F`-)3y<6m3jrD3% zvgT3ONbyd{l6zey`X)_E9gwi`STD!e!Fr+n3<@C?2O*@m2E1`VZ6p-UdRA)veQD#o zPLC>I04OKgx@yrlIiFg0JsSz^`LMYPM2=Hp)1ntmYxesp`?!9`ruRO3LY~;B={`2x zpF608HuWo7s_|H~AzctP8`|{T+SMyFJNPKnW(X)^?I0n88nB^-!Pv>R8nz3R71gKx zj9I!Gqq@`l$eesLBQ1W5qw5M^v12re4|^7B1Ry^;rqoHQlu7?;4^Pq9;`hr7AY;B$ z7xE+{oOgJ|=pCrq-AN~cJKL!vf>;_zOctEVUsv3hszvi~fWCU<8NTXrIHrd`y_{aZ zc-J3sr$m*AEr+T5nh;oJ14$f9Zi=!6O?vVTGglZH_Ye7FsYJdTR@jMqSuK8R*)>e-HM-0C-%lTAvMdAg%7Y*6&qxZ)j)&Hvhh`xoY&> zd{_Utu&r6PdeAns8)(B483H9|?y8y+5a_<`rv}|yw|;Qd=B<)@aqDP;{0J52LhAI| zUfH;insQ|SMrIMd*9H=PFLCBfUd20qM`|f$*x{0apdp1z&mvqyUs3=25S0nm55SBp zhSJFJxISKQujn_mZ4dNwK1T=l1201JGnoQ)M;R0~tdnR3ODhjzgCAprKkNo^J#5JY$hCOLDK z%JuOeb`{Ib?mq95lf`6auE49A1F)mnqrS*gj!n%g(^x6iCFbQrMk#4E&c#Y56)rAG zyH-~m4Bwq-&@ND;hBr$6jj#YcX-n79v zU_F0m!Ey^~^bVslqA+F5Q>RcDVnysC1tyc?$*ISU+#Ofw4X39SAL%1d0xagM9bnqR zYPs4aLzUoELL*;=2FsB@bArC}vsT(K@J7)Z1a@^e;F^gTnS{pBn-ab-)9Ka{u3^%e z^0vRzWq7|FE6le|MHD@m%I6o8UhsLMMA7e)55vT&^l4LRQ4@Xv6Ba5yw!F=)2x zUT2LsG{yz1TeyHTlq;TbGm3%5d}mb*NSCfNXF)ZBLAh3=9mJO~Dvq1CrzP}wx2i+v zUEdBT>Rd^ku9oW#W4QA5xC&OehT^t##{`nRcH=vwL19)pvJr_#0s31eXjZZ_W30Gw z6Z9ybi${cIuR6!DGQ+A_1VsS%0gM`TY2nze6tZM}+il@*K(F|jlq!>xl{~Xbu-F)D zY+1Sd+4@eN+TPtqi6y^r0XcEQs=XhWPSX0K=I?={d`=||y-U62f7G%fpL5Fh#2`dS zGV1hT!DWL~7;E?)0F%?bjQj*ra$?ViuCj~CXQK+cCsr5=$pT3cYHbJ(E#%9zH2s>z zhPqLm3lpz%o4bBw!`s6I{NFNTE(46Vv6hqq-<`b)<*YCj10ML4$?owvf(4ek)vvu! z>5S&?snDeG?6B!`dx=@yl9i`BsZetxhxEQzBt4#ZVurZ~0la!tx%+fNa$nj)s-GG2 z4Q($pHW*H+HiUi^{8pY4&`_qh!gl>#=^(97rFPq%lu*gA8{VQxea1TJ2$N`!;HA3g zRl@2(4J5~q;`k#=8x@KjfUMP$C^Xn%G^-h(%S5KuPAY?ZMEm_~;TOJIogs^3fm?J< zs2sS0IiRI$8vt}h-ZZ(8-#Nn&-(L~2uT-kLwgZLi2gkj8h?8Jiz^Uuk(UV>2L3n>7 zs9{zX&?tZ@SoJgH)f&QiFX5D{Z|s`{Gr^(UkNKK_V=G9UN00J|)mF>!pJu?rdN4x+ z%pq{h7V{5X`jk#Eh;0U9EYKLn`DsxIkgSU+7cQhf-T+*($1K>mDf>bC`TD})3BNlu zc6u@1S{)|VUDg|7uuM;SG|fGGtvUN1{qd@zt|PPWAmrm z*xYkO(*shWOgGGWjX-RfhbbLFDFH&$t8%v>OCDkIXpdFhO5ITVVA3GP#P6IMepR4g zj<5(IBV}v;N6OAzu-B^Y#uD6W%v5DC7;mH;mkgwdm)(a}d`+Wp%HPEdjn0OeZr zGIG-{Ni3yNkPQr!iWUos#Z=Pwu_Iwy4HWN)v7Elzj$Dmx_=z?U zUi}d^FFljFc_xGuc0d1kS%>f-;)2R59l};fM8mSpw$$NwlV~o*8d!$)I8OiFk`CCI z-+=Lxl!_HolM8ucy9fe=hH4ukj65-%ZII-mDA@tkbs@-yhJ)2V^)$>jf5Ab z{-H=Yn;27Vya{+*>cm}K7E_$uDwn55e4Hq9af>tu?Ux>42mCDEH*;XB5hed?kz9qu z0Q6^sV%WIyKB+JlZvvFKRGv|wz3ggscT9?j%6Xv`sK{{O+<1=HqLQ9fG}qk zB9WD;B{w>Y>GE&!(C}Px%E|11*u;jyNBA;Wkq&z$0z`hjJuV_vRemR!=I7#8%doj` zwu#f7LqHiCMP(lz1m6j({#jgv23Dv7>GoBrbygM3G;bwetgc-$`0ShGkego&u`>pQ zFUtr@5qP3lBC{v`ku6#&+4{EtQUE9q;daF5Jj%2cY~J^B3Fr0a`t}w9saDTl^FIB! zQ8${c#GG?MV8|+9ieHHHOY)-8Y<1)Dd?bKKW5Eu?%fN{8JzBSxmz1?ghgn;iEM(sP z^7ji3RAl{0FFn3hG>0jnJlnaBDSj1k2-BRoDx#*H0`t)Ebm+=}N>xBHD?rapu1If% zs$mhK{%jP7-nnBwqrq2kE_yr$y8;qsp4RX(at8*Rv=7>Ac}^q!(WFrG$9e_5>bNoI=fdI|1h-Stw$_g5DEIrazs&f>aTYAR#P%CWfZYya+DPcq_+&GDZ z_un|y#sj!yQ+M;~yVEI^l!)4~*iMTZ+RM(()f&i3nbSAEA2geVTWuQ}el#kZep8SK|BHmtU zoU>AC&BFg{L%0f`mlLB3bmNG0ry92$z4)iY6c6HC&UZ7i`gDM-axNu(u)S6}rD1^` zt2=ky%;pbAGV-@?2?xvA)y*(++h&XnpbD#V*1gI>Sh$8#8N{rU<42&*U+R#+sOy}; zzDdy*E%E8i+7sJ5E)@0>bN_-&*Um@z%#j*9&L46kDAALaPd0vw7mN_%HU&hupnqy|pV z4PIwzK|JK*_?<}L4fNW7$59t!{9{2C@VuCuX1w`XNCP}+3*pF|RrAyi-Emc+$`Qoq zhJk@|yLspUs3&T6TCtw`d%2g}zxpx6qs!CNnI5R(;VBLfL^YSHwtt*VcYCUEGQW(G z?%p<7X#>K)(hp;& z?-xE6dq-H_v>?6>c}ga0lz%IfRGpVn8q9E78L(#{lETZ71&_v^t3s!yNnyZwwqb{fr> zB~>3lRa!plawHCh*Jov5A>R~@ZrjP;N~;ls66d!xAiekuf>5B#xMzRKfsLu>6K8x<; zaxO)s=4`y|LPwRdZB&bd$U9}^Xe{7qc76lQ(JON7DLEh}fJ*z{e+ExN^dn9ECLbgN zNX_*IN>=@CB~(LwCI>COH?`zO(@YW29&TO4&5>9{qZW@FH6BOHxRg#)uD-C01i`hOpKUo}Q7lCa$($|mx`&hoJnWuEF4A>-oDtT3||qja~q zZT#6Uykbh7&2~U2&_>QFytE-dyNfw5re2}WR!V5zB&Uj5z1<|F;%qA4hU2^@T_w(f zAs!&tgn(J<(dOaSev2y?se1`9gf>7cpTMOkH%sdtt`aI_u5r~#o!}vKR-F{9ddsSQ=W~3e~!x@@AE617fwWqaJ6r&HMYjn9DE4v-il*-0X zRaMfVg@C~qET;)xO5bD5YxbXZ8y8hoK|a4*a*Dm7)9TEC4E7>*`@Z%fNLhhn**om5 zhALSGShl0V^N&2bJe+yC#* z+vlAKZqXN}2+hZ*IQq*gvi*kUwyVSk>&qRyygK5Bfjwt7eTIhzS6(`jnBz=#sXD|K zVR0H+UF-7&Sc{3tKfsbm8sPHwkldASucAnA&@-FIUgJj~)9{rarc}QJ|-s3fjxTMVp#L@nz>#jD0*A8+~8#>)$M24}CvRkttVMXgzF)51Aj6fhpq z>uJ-MeLTu7-CEau2Bl8uzw`8D*&0M^!m~Y6XV=D$>X~{H0CyGZgpp=|mu7T8%DZ ziHdvAAl7A~2$5f(82IAXtsfEvX>m|^9u>QbmqVFnFyx79o%;YJgVfX`;r%N?^6N@k z!5ZR=diR!WIU@&g+0Rr;(;(vxT{}I^y8wQI?l#5+s`iurAzY=-h zg?rDY=#ljQgcptu$|F5@eNx3?m8#@%wZzkWJ+tC9f(27MNRrC&Fh9)xtZt?5&*pv+xYb;p^<2V1uo4Enb&TL=g|${-ZF`$4CNodm&mR;e3751e!x5r^il9`(uH`c7 zRy7<8?D=uq+Wt^Y(0q@21)shh??`~^b&}z-JL53*Vq!K&y`{lA=jF+x+ZlxT3;+QM zhhl?;;de0lI${|XBOt~=Z)0Iv8YJ*g_S+}>Z2pniBrCU&y)*sk2VVxr(T_Rjn?M^m z3wVlW6?@Jwfsw%ZE2PlWW6sZdRD5{QZ}5-Z6W9mfuSa&_xKmhoe31XF@d1Li7FKU~#)ldXjQiCO=D~&U0o9fa7 z=6X8Bp8dJWh6~sgSW~cFibhb1zjKj;YFUYTNoKign^~3o zbymYR)VKIjIok)noMfF+vsj05yGQ#2zi0#i`j<5T$Oq{<=Z6FV!Q%b@>k0Ee!lgPo zD(hSrzQ5DokO^OjS1#A;4`liWoxwoYa14J)<^zv;q<8!3Seo-^4SEvJxs-Qxskdy+ zE119sdET9!fzHm;N=KXXY)ga=8Diu&{^cThJLKd8)Hp!EHVM(&BG_6-;cvj1F0_fa zZ~{J+N6B1j!X$JeUoHWz_!s$Ww$XbFj1qFPLS5vs1>!;_4C6iKg4qyAxrsC=C9nuL z5F$5|MH_=}fappWyBNU>sLC7lF^pO75@w;53<@KxN8D{twOqq}*k9~AQIPyfy zT&Ekd0XWlu@t(EN9OO8}#=g6#h#QtXJyF>1v0Rf^2+#|&I05EpbI_-4fi&=B%RH=4t7V;iM&2He9$jL|L^Aq!T3Y)%mxgTEZT`Rvk5Pwli-lC%=E^P5~p?ozCbRpR1 zmOpVW7%++mw$`1QH^iR9ams)&FxFjw5hDqyRV!1tC9bKxEbvyAC*S}VWmBinX#9b@ zpLv(b6oCo%e3hmAv!E|X}ae?^Rl*8b&C&mmBxS$HLhf5bH6 z?jGj{kpOEIR>OTntvq$y(6BCQiSp)MChUtgkPr5|%Z#9a=#CE~qV$6nh0m=T-g92; zp3H3+bdll&#=_5Z+kRD6YQM04)$BseuhhRf0V}AG7XkhLoF~RLd6MWQvu0l-8sOoe z8JwBN{RL04y-TG3NTe4Cz3Y&2kWUj!E1H>~l~K&?PrM+X>LZq`(RK2EkVFiWDc=wk zLJk4G-53lFkw(7Ty~M8JM&IPNS$|aJ_fVvGbHCBtDWn6&|LjRHG!Js5>yH0f|MA|0 z3n&t7BzvPdt8vC>a+1mRT|8;2Z(TTPv0t+$y%$&Lxe!0G8>?1XB62#RJ|cSP=~9{& zJTzz}9e)`SfwJ067hHHT=fwF_>nsLX3^ygjRlXk~1BrNl{;6@VLFny^_G)!|ZS^ZG zh-}!`s;b8fIiFrSJl>#`348qaDCR*B7l3I48@ibsBatIjoW~UguED(kE@F~f7g9`B z(G)fB9W#K`HF0EjGYpkMC)O&E6s=Tf!7>2H!V8mC+5*)&!)_`_X|gQsX4zOg^iYTc zn_+9nR9A|2BN-=ostgU*r#DQO0*&DoZ<&xkIH=82UL}5CF5Yy+G7g8Hp#LJ&0#MeX zpDVExjWzQ3T!#_{~_i?#6RO?zVCpu zZ*f1k>7EeW8fW}{+qHatF6@xVd>wM;*e1Y5SvJ2z4EIk2Fz^dJ&T@?u#WsQeE_Mbog$A@wrDw(wDi5aezS!Sw@h3CG5dXHr-tFDG76>#0A*93=YODQXgGT z{^dK}mJ3arWii58ftNftlI&H!!_lmD6_v1}UO$*dJbd(gq^;W#L>`e){>5+S7nrf?7o$l2Qnr2%z^#ggK zbnqlUyiiD%9m{~VB_p!yO*YHHHkqzHNmp;}gzK%#vzgq{T00$=?1-lHQg^oR((=1kD~rY&HyaBq90)kNz@aP_Lu=xOVP;rw9P>WKTU== zWNAsXssVB|oQkTL^pd0zDX7&X;$_H@>K1+HhosQ*p6~J>>h%~yec9UIfx|1&0d_ZC zYRqyIDBk3@S>z0kHd&r^7sTe)UV2x3;^0Bo*dyq6Nxone+;k>}H(BY&6$i!{mEy5l zuWUqSC*Eu_xzW$^K$p9c#waj%9|4h%u&bP#J@b^MgHAImQWGghxpDNYN7519eel!W z9JfY89?(wI?k8{o`V+yqO;(?u-$iA#vq)=#%L-|0Fu4!;Q#fTd1X)?fM_m>omP9l4 zgW~trMjLL3opgy@e4u*>qjfo|r+No;_}-F^|3Luo^IzIr%?HeLDK+K^vy`Y=qbF0Hb9b5M5}Q5E z)yGWnh}{_L58@cda%EM5$-Bj`^{>=$2b@5vI}VaPmvL4dXLW!LbvER&dK=l=3CN%C~YaHb;#WPL_o z(g^Q?9m4X({{Zju>Dju5|;%93HWj_TS*dpnqu@w(Cp? zAy?q9NFkRo*3c(BWDfpS#}<%qY+@msY~e1AYYD{6PH&IJ2P<+*NGoy}0AF%|h0ohB zjxnNZA)N-J7g7^gY>Q-$hY)^SyZNsv5E$a&!?-K8gkT-j3<6cfUyC2?ud2XfafOA+ z(WOr~tYeR*rvo=czV-)T074QJmRi7Q?%Cah1tM|X+$kCpCz0m@oSF!(0&DeJFCb(1|X92?l>S*eo;v8 zyE8xLJyXkjZ3qK_3%Fk+>DreR>>zd_1KXOL7qCz?0QTT@VIjPQWO&se2KGSE=$=SxE2n2}=!O6G5kA4f z_@h6Z3-sr+2c)o#51$||%~4pCbx=9-J?~ehlRXjdfEU_JulJh@jnA3-;{zN-Q3e!@ z9_0VWTT1HK`7d$6|M1HG zuSxp1Gzf?^IS9yq8K}S3_g@JA1MsgI^wXxH{HHv~CFOqr7}?wZA8C{S5&zc{{we;~ z+W&iNr1=kV6FWzBUcdxAjBM$Axoq z!OoX61AW2irzxgcD8tN$>S;eOl<1iH2es;vJ}vV_>m4fp=jCE~qNIdcWzuRrCD0j@k$ z%zW(*?Q-a=zDk+$Re)QooIz+_KZ-u!3=+6{i+B_uxcL^5(r|ixA~U`CA@0{hTx$TB zk&2mBxR5h=lHlbgFlor@=~{R7@oAu}DB9CSk!avFP3dvnJlu8YwtA&FIrKdIxw`nA z1@UW>A;j|s%;wc+ElNYc-h_(^de_K{o`Kyc&x`_+mHL#;TP@+%GG}D|`kV}rmGSyD z`q+Wb=JG`T308-v)Pivf!KA zvFDk~P5^az>L}!qG`(YVJa2Nx!c#1@@}!e5FiN9#y5eaUtPjeUeS2}sMcXGgd%)>1lm~Y+Q+dZ zQknHe^F-MAbXac1hSX!{EXk2j(61Tb_2|7l*Ai(LxQBrO0s?=VeKuFW|+UX`5(jEP=R7=*JGmv-` zzsJcJy!p}nn{*O35Or^UY(}40H>{Gm0DJ^!zt@@lEudc){(?>%V;(_eUX2THUqyHRFRPljzZOA5wG9I z&t;pSMi<7W+H{DLL?yP1vO369LoL88p-fYq^-NzN713g)NDF>7YF9=b_e06O7Rc~I zx`?(&W>3OvU{MDnjcN0Hav=RKJEak067E?-n&N*XCKPF|e*`B(B^`d@C#Gg}W~PJ{ zNKanpJiQBGM8(8BoZbL&F zp{@QJS`~rd8l=wiU(S!;C2F;i>D!50_Zy6$Gk;wRknX{O@K`2A$qf@K* z>IzGyqy-WeP`7;VA9BiyMc4RUL$D(_5^ z(cGGirGXojA^3$=ESEyGt~$J_{?Sq5ap(+KgRh~EXEekFgTGcqeTM95z$xPTxH7(jcdl-4ZR zq7DQ!5l|!UvFF2mnV^@PHpD;|!U;bR7YO|d9lt7SL&I_eX2omurLN0N1#By{fO@*L zK7qSGQCi)il6x#VN=33U&Pup`5D24Bmqg8NQOrzQJoHXh%<;vub|G257A{WJt%7FI zWJ=14W%_$UWoln5S$0Be3Gv`wyEN1Zc%wexr;1rGzUUObI2r-0Q(VxXhtL-WxP`2O zte9nLG#B=|;bSll6%#e^193^mI4iNo5Gw((kP9PG<&YB?pO`pO!zTG08wa_3>*NQB zexLMjs&Hvm=B?8G*gT{i33GlIPDgUpovkr=**%mmkKtdouVBy0Sf&~fg4uRmZxJw|oI|N%oaDvMM zL4&&mcMrM*cf#Us!R>8wtG@5Pd*ACnrh3ktIz3a}HGR5&-KQCeEFE-H!^%@*vW#=b zeuYTr*vNkkUM?)dK=-P2XL>vQvEVouw$|AHLe`s>IZ0311TK^S0e*X%9@v!_qcjK? z(YDBD9(HM>k1BEF9L4zg0c%51|BQGXP(H0_hIMH5T#HQl8FfikcIk6kSKC%~UPuEL zb`urCJIM0 zoa@e1aY9nYb;ylT<%wd+puXzYNdgI9A6qWX2_QOJQ*dV^*M^~8Fp+vrvNG`HY7vMMFD^kqaccCOcOk-i1s`T8|rJ zQTFpR)VZc$3awGm$Xh!2r5$&0XUImh_P9u)(#3>&W5J3BIwo_!H(Xq*I;A1EPD+X~ zdnz*X8cImwO$W>{%5T#ek`@CgO+33eS;U`Zq7AegpY}}e^*3@1n~Uu1XP=|z6Fnr-J(ayC zry7?@li$uFxdkiFukw*_#cLPPq2 zTKwfbTMM|@xsOA}qzlf#;J6qR%w*E3kI$UOE%72j`2`-a79X!FQ6z-#@pEX`BwuZm zvVH83`Z0+$=}AjC;`qk&LY*EbF6++Yb*|JR9Xd+1OLJh{r;bq0L5XxJBLaOrsb{D7 z<0n9DHiyX?9A_k1D*KVXc=DLH7mr)tu7|#DxvEN5zzCj z#TU!1`J@(Fb;q(=5gZHASPkkZFAO*$fbaTj&pOw2`0BhUnmOiY83xCwTPAV5D8VqF zA8%lCu`ZuxXr9Sv3#5D?>s3v{dz4G9=D~6iBgTX=s2V>5cSBrgfg)Drrj=vMPZ?&n zIO63bmN__49{a=(qI+CsnXY;kMKLKWv8ve!>Xw<#dssbEKws@MYIk&pxDxZO74-VG>1YzndXlj2Sq}h-p{gIC9*R665dfD#xMnzC5VX7A5-u zxLXi2VMD+7m8QSl7g)xqAMpb2Xaqjgdo}tV*llQJn+Mz+8T7X+;KM)uR;wHo;BWy1 z?_f!t?g+Zjs$2)>#W$qYvC zy*6oW+*!DRFL^FqTl=i#KjO|G5Vu>|H@98m=?E>Rbq)irytTV&zg+4*3>CRJKlCIxy?aLOOIGDBcsFT3 z3cJMNjewjL+bxOuA`9DUi9+^qoV^HPYd#GQJRz_1M5jW$lS4ed9Bi;gA0z)_esWV+e2Wvp%Ik3#cmn8)o?Vr1B?C)2WQquhu5DQu zwa|^idd@gM_`S-^EIQ*nzf;J}+&P)MpD5p%Ie=VrpYNabpIqD38+t^K>{7t4c%!4l z`4$}?y0tRE6%~60hsIaC8!t##mVFgXG;q$Z4|la9aCja9QcmJ7t7*OUh3xDzj_lh7 zoZIX#yT8s&`3k$<6W=Z%zqfANO>)p$>pfdLTVZ-VP}#*&Hg=*qAII9Nbi<*NRn!vI zsfT%;8DEnAc-iyi5#={Y!C-78%?rapLQ<=D?FRs`Ai&0|7V6SD*5Uin>*1O_n2Gtk zfJQ>RR!#w<*1<029*i-mtxKduv0+W5*iFz3++ET3w(1^A6SZ=CI^$~bV5KlEv^={! zpxq)^SO=js`ec&;mTWaeCDRb3Zm+uy(Ks$KJq?)6VQ+k@M{*-##YR-GZi@*Lf26 zo>NMi@)V~o2cD$e`c3a8K90x2N6-e0%(}z?L&U8E9ct3(no$cA9bPKO7MaCR2{ku1 zI28JEY8?YuRWe|5LvPL|ee$eml&g5~rTl9^0L4naFCK zgMG)FZ-4T!R(4RbGOvrEk!-!xbO?D&u8D}eERcFLfbs*U!X2HU6Td2S_lVPx7bl9s z@)nF<~2PT1k(V28W$ga2g;e2L4b~n-+zTxU-pt0OHlS{9rSo}tBXnZ;Te(S=N z-Tl<*okV>IrOFDf=>{*N4<_QVBI;h}@enc8a{RIpeA9SH$tQ7fdH0yu_T@eNaLw%G zO5LF9CPzb&u}o<~am#^ZFm*Y5%sF-g;@U=5a$R|98TrtwZDb{Y(T@u{P%xK4Y!Q|I&Jr!`p47aH^+yG2c)L688NJ>G7Zu4BCUU{J@P;#ly7Ux2cZh4xa2ve0P$ zzQ(}O44=hQG6USOU(wf`<_IzxA>^DQm<+UGS;6&B3#q$eZAY0R>hh6pw_ec?Vyfty z=r!A56J#YA(%b5aV9g3>8zFf80W>6b?-+5% z?UB$vdES(9E#JQP^YPr!AFg(B|1b855tKw1D2w3Z-=Xv~01yPu18l39^bm{n;>V{8 z?07p=`p&#Cf4;o)1UhxEDbej!8b)joWjN^+x>wE&ktT{QkYQUe{go~)4!GG z!{Kvvl^8erEDYC$hpSZYoh$CyHC9dU*wvnyGbfvXG3p!^-ssraY}VM7=i?ceVwyZ+ z2Z+&FNzJakT?PR_ESYlXj}118@J}5FH-#MRf8rNr-KE68gYib zrPaCiE+QBv;cZ@lmc(fHymUWiMHLg~vN?o@UX4<8B+N?mm!n*+^yzm2j$KgVbYH72 zzaxnh$_?blR;=4%<#ewTge ze&_z+B6CXxsymD_+z)f8^hPy{3TPSk<(r|Y@-tFgFR#PtkfaXNZxcpi5>q=)V+A%O zkz+@&_%toE>U)5{`|*`sHl#6^bX#o~Ixo&F z_bGyQDcx_pQK^WVOh&+Ei@QQO${OjsYkV4M$!VpU9C3=At=jXKE02KYqo{CY;1iW8 zBsYL)B*q&ZT=_ST4+lm|TmqEH(;45%@khTxl+k#9RRL3scw7f%O$6ec7Kl5TR0)Ja zI5>#4160+({=b&;GUKGFy4}{CVm$ z6-(9Pi?#pqEE@Rz7UT|JTVpU?+PisbFi4YSQZ!qW<(qSMBBq&KnLeL9OXb^Fm3K4j z+~0DDPw$(F$rj$5Q}EP8C%fT%*%&?IEV-Mout^FOS%n#`RxoqZ4Lm~^@$6B zD`d38d}VYvG;Rr?0>OQI6m<7#u9-?ubDm2ZJIp|$Jny3+!3R4`rU4meONRT)!CwjF z-n!-?>Abd1S7Yy;CAJc|$*SF+VUSqqnR#JD>x1`Sp1euc{5<{g$@^91a`N!$BfS@a zIMb{ASxRv6-Vf#cKAmUP90OpiT~kEB;zx8nt2j-iyIJ*wOiG_Xk6kDf5B{ z`NrbCLffMrE*}(T%$p~*+s#@DqQ8j-ba)xCfqLVx2&u1K0ni~sR2&e%sedK*3Oja1 zP0*el|vb&JJoGd`HG+Ww5hFv(EQ`dy=$9`uhZ5%oKXB*x>2lk zpA530N8imsxX*%qqP*%`_2t64vq~!I&~@=PKaD-ZZI%wVZLg=6l!OE8Ib-+X`?%h3 ztR@8=8AtI})jTI4Xa#Jb?xYL3M-{!Sv?`Zfq(qfMOTwgkQyRLwAMw>IA?~@JrdkkJ^XU>w6^liNJOWy z$wAY#edD*I?=lBG3Ru=bjl4zTP9Gocy{}kdh{=Smh?Yk{xPJJMB(o1BU}sfgR;x(k zNamm%cHm4Vucv=wgbS1%^#wEviG)0&IV<|>eYs@anlgEpAMwKzR4UCDS7WWhVR*U` z4C@WR?DJEaeo+h-^xVc&h&87peLLqhW_&dgqK? zf<&n^jCrS!!+Z+S#iX@i%OfhjiY4tTjKNmcKBs-{Q35xRn&b1%VQooap?SB`!uv)B z`(zcga}x+d?r}3;XG8P_&jra}@n!*tW6A7*Gi?GCdMuqkETIuy!p(^@4T*i_&uoZ1 zu-kM7=D{l2I6O3VL=2s!POr{DSINFoi3DVH4NYpNmSZ*+F=SXkQizVt`yEvjpYJp% zoLoy2(Sud_&rqTTYq_e&F^ePDP{s8a>7HJ`dqD=NCiy9HcX^D|YfL><KIi52r)O-q@DvIN}a0L@+USoj?PEBxH zILnc_#Fzr%EH{Hg+j@!D{8SjFB8MnpKtGJdysa>GhZ`3voEVo=woHn!VKHK?3Hwjq zx49uyQ`AMi<#ZgJq^}#EE3k|aA$?Slq#cvd`=M;!d%#bY;t4N-ME2rrAG6IZ1}RzT zGYbUSw_Hb)m`UYn>pErAY-%8aVv0M_l*x4^b~R2v4~U0K#s(Z)xhqG}d|!}cw_bRu zj{MZ?<6EK^=Y>zHb3kF2k>Rf}(lRJdtQA}6qI&{1V@}X;FOgmXCB$MM?&Dr=ZN8i8pK`Y`vQuyVvWU(C1S^Kq^MH-V75Be zXqTEG5;n;9YMK;IPf0d3pqBm1&)$6-FIMDrwWoG5Vb=pMGJo=Ol_x!gdwbVp00~J) z(OF^4g90-Oag{bt=ih2R?L61u1=^Sj}u|NG;CrF z141N2fh1TndY56TnQg@P)viR^# zuI7yVi4()mCO-0p^p<1YS?Et7Sr*q#Wnvy9>Kl zjuQmz`@gs+O~fC{>@3m`!uPKrN?!iQq- z0dS$2zXh+7<5xcep|D+qfP4=j;7Ns!C7^IZ?e+i!P}_Zkq>&k#u?L`o1|S|2m^-5& zc{n(mvf7!MS{bui*&#qAC$wr00Q?;oi3H%khywkQfWiyaM}$ct`?ml|e^%4}{dGtv z?aL6s z-+5PUA)}3<6?e@{R1kDDHmq9l=p6Pf4q`O>0fFE^6hN)PU;v40@YMJ#A5ylI<;|BectiwBgqzx zs6HP1N>nRUO3rFUT$bBlnyuwdAqUmqyMNTym-Jm-D#Mx#4o4#YK39D@Q^U$b53X&( zH{5U`p;RshoOIv#GO{&LJ*Z4ymFF(`6tj^g>Wyb*^SsRfXY4wwf1%eGx0Is>M80M3 zn!e(d4QKu#4!uE&{-<}+68MK46W-m7)0-AQ$Yi0P;l){mjbB1$`j;O@Z#@cuw0D3n zPjK`dj|U0+c1X^PwEtw$jsn28w9&R5L=^6mf0!6`t# zBA@fr(!mr2C+krNFybWy$LH;nIM_TF$(@G}AuqN3AICoLkfN^BRbRk9k*#k&AC&cE zzzk%|!-`-029q%g6tf8gAOi|;U@48k0RRBj2(vQ>Dgl4Ymceh+Fcik`Nc<0x@3rF$ z#z0GV8Av-!NYEi}m1DoOrm-E_ZoB^XyiqmDV6`|{h@-#Q-}lS&dr_`$9!*#5zz6MY zO*kz{0aiMt?W!gZi%+vFQiLe15}mV9lLG{DGk^W+`U`X-;-RqCeJIeyhMKHn>_0FT zFj z)_{#KM6fkF`A*vnnhL*yIM~{&9hg~YB<-8M^P51WGpAkmP9ea@#|YkvjS;IvyVugY;M%zSrg3_fXmByBN?tB+z1ZWHitd6sN17GXZV}64qY_tN{gddQ84W}h_^Wz_RxHV7@vA} zB$+;l$!cUJiFkHWjbF>7nCuH){zpU2Q$zhG>j(IcOFJp4V!i)2?aNc`%d^&@bv7~CcL6O%Ct6bsO|ZAqwp0033u1(Q$;7=LST+eo(H_XYMp5WWM9vq)l5 zZ|9DETQ8@*(@A`7C+VJ@#ekA%n;nVNB`G`Z#RBsm_R|7;|J?kNJ&&ql6lyMNK%za9VA|MI81H`8FUoV$K(!4Q7*=58@L+PjJ4#hZhJXmaDuo#^%4n}k6W zT*a>^!Tcb&y7DIO!Ceqe59&3$M!zk>VB$s*d~e|Rw@$PthMQ+c3l^>qH?M+l?!@qC zczrN;!Y|9kD;Ry@#NODOdGS4rU2Dr>f}_1<=zqTvL%dP|;VW+#An`-K5mxywzcLgU zpf4VT?hIfE{OHD8B-424XmImJ4su&IiQD;1K6kgUo13k{Fm&$VM>1d)@TdIJe8!NM zk7(B_tOyRF-lzhC`ArGQ-0}S6!w*7@hFrU~-T?LV0E_F*7UyjkEEmaW-e#kn`d`#o z$bZ>uj3pLXpIWpTFc&w@0yuC!d2@R02ca{b0YtE57GT{%vfeuei8BtS_xLlK#^T3W zh`)s5$4TJFk#+aRb)vWzdCnz30^`g*81l!SA9;HR_}t9#ueGO+_FVtf#|yl7pvLm1 z@Wib%1DR+y8;6Zfr+vt8gV;NB!uZ4ZSAXrVx6W+lhWGNFK>#AvI&-c;TIw%-^u7OB zy8I!1oC9q#@XN@bo`v)t{4sz@fmFc1;_*!U;78$?#aRd+ow-->Nf^v8-LEk~-0kea z#n8LH$zHt)!ryxS1bzTwobVbsgh%@MV&Q_&rPt)OU-()aEO;Jp?K9j0o~+xA7Jof; zzW@|3Cb4>;4=W1_h5iZ_5+%6GGZ){8emmN0wCT5nGl7?D`1>T7AxMY)e!q1{1LNWi z_^LCR016uXiE$9e!F>DUdO#pk_s4Aye2_uPcs6invv&@!A))9Fzd*w>KBr$DL94K` zeBZV;e5?Bk8HFRzdKM4C;OPL3XMaBh!54{!TC+!^UU^{@p9eS!{yB5RU&&3F`f`p& zguE{=&?A2EV;{^2_17o<%T8cWH2pU8rugeM{Dd(W_Gay{l{_mSiIMujc$uEVz_sD9 z)$gAW?Zc(Z5U&k-b-Uju@$n7BhBNaV9EwnP>BT@mOk0UKX|!4myD5c<6Ms8>&qs3$ z9$19#mG||Xvsl32AW>Pk`cduZkE>N6kmeT5B`Xl{0fHE&C%WgK=B3#J5yu*5Bq<@Rr^hh(}X{W zL1K91GdZSm}PPg}}-|KhkhxTE;*|Yz*kQ*SQ z7?T-6n733;H1RySPR_4#opKftZ;r$@l7IMM?wgZ{I9OcndsDIQ(Qk6?I(2zvpcYwP z%TUL!&a3}^rc(#cO-(5!+9&+4V1kK|vKm7>%Q%n>ARa@>Q9%6XLx1N2l#qK0thAu5N zKgClMi!Vt@kq`gLcX_yV#NuO;-buFx;?5ww=CIN1)z>Ax1f9)V7M*-8O9cfIf1SZn zGW}vXCV7`H)?31q_H=IeDo$@#+ z1wr$$)oX76!I(2BxEjm!_wNdj!xcw04y3@i7&1LLwx*U5(i^&w(Bpgdd+U8`D)vDXMde(wEmQ4%b5QiMD&w~S)2amB!|l%NLEa%@L= zZM`tot46ccZQ5P23RR+5ml%e#f-6fP`QfoFZ5a|?*MFA1*e^1eXjE&o>`|@`;X4`= znF{0b!{A5PL3>Q#M<=xSSWeK89!zE_5IW7eJsdm`1PXS*7o0H%f*eI5D(E%?C|zKy zX+VE20eBj(_x~Q`a{~c2F>HZMe#7z3GQm(aBlw z?dW`TVj0Va1XDyRn*sFL2wZ9VJZSBY>)R%$ihst9Vzjk|q44|Zjqgktu97YkLVp_R z(sw3buESvIPj~X=;qs-mlP^2@@_gkBB^sYbzPxpPH*{v!(2cxn-@1sA)~@e&!sg+^ zro9t3DI#m^gw692He3?&B*Nx=>BrvOwcfdN__+0xa~~|t82vWL?Vm{0batX9MPiv~ z@PAI!Je{cNKAot!xQ}9YZuNs8#$+JY9PR|o!v#%uCunwp=J^U5D#m;oLDRFqmT|4A z8#~_2om%eK#mw<(R{-Ww;v3*AE4~Y?F^ZxEiy}9&yvQP+^J92v?9PI_*Ov85u(T$Q zZ-wra8(MLIuUnBjNk)U$)FzAx=5BUzp??j;+Tl*DrO3JUdlYLTd-}bJwHDRdJdvyR z;T;)IJL&RZ>C&q0q)Upb+B@m;e54B%@H~;xv{=l%2^&c}DYKcBv3FADpFzrer>VKa`%DfL^cNg{9K(y3%qGcyqp08-3O-4^6T85#A+r8ffQ+KwLFb|h7jh%!^QGb@X zkLU5g-$|H1t(GuPSXPWHIT*Y)*MfE*3BleXIFH*lReK^ z_B>(H@sIt}4;R)sCZSw^Vs1*`2BG~E37K8xaazb6{vL(Q_ZkC!!ph@Q|2E(d@J_xw zT)ymzjd$|p8OxU^EH*w_`dl}&+kc?BxtQ4%8mGmKUE9f-=ObsHu+lgQ)SUW@Wqj~q z8N&~2e{lNY#ZKfrT;%LZjx!?1-ie&&BXXXw=6L8uUsBR$xRW;OiHAs=T_tcv+Wane zZGNZHhTA+pQ7N$N%&be?w>g*vlP}i(`Q>0IYaT9Z+Pk{ojI7xe20tTN^M8be!AYij zfAsF-PReW`Wp;JJ87b45t91kh`Am?BV<8@f|GG_)VO&KJ5}*il}7X@BJ72@m`o0ZIlN z=JcvhTyVNLyJ!&xx8N#AmSf>@%QH82tt&5_-#MXciBqbqE1(c(HWST`tiNSKt7trVy%^sG;g<+c#x)=_#hY4z z7q2aALawPntBz&>z}haIBsUs^4@{^!00k7KJU z99gU{+zsv`3k(MAH8lzREAM)V3k4K5UV5{s#PNrtCBWw32VbYB zvu$e3U*F4QdQ6>5jN#mQK#2&b10?2H(Htogu|h;BZbWI?F!b2)(5|&vj}?3xNjE)0 z$>X${V!!W1-o#AWvs3uOx)PO&z-J&CU4Ymr2eCD%H~K>c@puwUU0lA11Q0b0V`p=G zPd&%*Ksv{_@PDi~hyD<^ufkvsD4KfF7odm>&!4y&Q=ahwYKq+8$%MpciZ3M=gQq@_?j(L`c`^1v1H$q`7YG^b_{c*U0aFT>xQJIv zSY?GIoB@L~QR3n>+>0vAMF%Y{Y6wvS{5 zHnFoXV;SG~OncUtSd`i0VCs&S*Fejln1b+kG)>;ywg00NPKjvo&liG+NjfYSivR=( z3VGzDTYoTAEHV;NLxVPA<~u$3Ks+A!)ecemj>tfnIvJ6?Im|WxmQ1@)J2@8D^EQqDA@el}DMp|o0(+G+-ZM9TVKoB18z)}Ip zaDVOjXo{g2nJy>T&=kfcRsm~t?1qsXg@wmNTKpAsw3tTDYMXcRSNj;8Un_LyK(T=M zYztMg7q8$ifRKF>mXA2Yq=PG&6PCE8jL2N+NEEUJmty{E>fX9DSTe-(l>MXx1qz*u znG@sTzY*F*)F4(GQ*?P>la7l+lPk+I}03#R7CP9|Me$Qnupx-g98W0n_M>Z0e zSMUT_V=gRy1GA?s1Mgh!nVBNIU30ZLhSTEVdj6_sbL5h3`+z+WFh z229jx-#`Vp&@*oiSEetmcJt4cGo6A|imcG_rJYRdiIhOJD<2>k29gN~(3v1d1%FeN zTMk;yB%U>){F2Q443WGvg|N=q73kw+0IQIDzG-XNYSgGcS|LR|tm~LrX?R+Dp`wQ` z#(lrpsKlMQBj-Oa*H!UxsZiFxf1J2hi<|ohz1h^_5|hJ{llmd|3q^>-f{KXAcsz0W z$>T>!ZoliJzkewR$3tR(&xjbo#DDSEm?_GWxr6YkIF9I0ji4+eqB#pl`-YAQ744}j zqa**LW5@quMIgT>=-=T~0wDJr4`eH`!^;9HK(G{6#dJ~ddx-HVs%em=fi<~V`d=dO zXy7wZX=ZlVtJ#g3SUF%th3W=i+69pE;Q)xO9w>c0UCBXF42)LxnZDh!`+r^W8E2i< z5y}VF;vawiztyQQe>7bZF>&Qj&m--F4a@1pDN@8g{{H{w&f*_`|4)!EN++h*f)Q^@ znG#eK%vm)|NALfOQtu>H??B}TcOviuj{x;L(_fdQ4*^OHuu3(r%##eo__VxZtb5J^ zTMF4JbLj+hZQ?a4A;E?nXn$|uX#7I5cqz2QY|tte)&n+;Uxteys!T%#;O*nr7(=C0 zznW4=x}e<)x}|_f!~`>w+w8-o6uue--li<#M99;~(FN1K^1haL=dUnFL>)*B2l1zf zvuw=6uHA04@ev<06>kUvF_7e;JMaaTGg#?2!>eF?%vCQS$>N006Lg z-<>#1JikQwtqaZzkmurqdQ~33W8LC8@|+~NbwbZ&hq$t&kbiE-8stRtLrBIhI6z1^ zG5GqJMeydR+F^P)Wej|{qi#Nj++(G`B> zt2bZH*$YH8Dkp8~QsY0O12ii!EFg0u&a7HYB}LsUbLCl*7akssyqRZDW#C!QzP;MB zuXc24*3GB#)qiis^=KTi`EOkZ04<8W0EF3uoOR*brDJSUcY(D5c*YM!8#NZj;x2gH zr{EBBYoEh*DQW0auqen-yfh|QalA(7g!q;-rDfEqExAZKMmOHv+Rx)Sf*YZ(EE&PznR35jL_1hLo59Q!k=8auEvBB8LGGDL z^TV5=3!Z%x=U9L%SRfOhM90Q=2v_9SpcoG(lO<@fDUxMGfz~h}jtSj(8KSK%V62R~ zU=1)8gnyMs&scILqlf#H6_i8xEeq+@6Dg%c3O<7a!~s;P463 z*d3aLxkN~DD=+3SS7o#N#HIbjfU<4z1?p%u)p0W^0r$iW(g%QxD@)HcQ?liF|5oKuK z3V)m;#K=QA<)c6IzF=9+yp+iFV88={D5FOGq+gek6sBs{K}cu{M#jLn8NIcl308=5 z@C)l@kWT@ju!IZZX|6d-y`&UVcG=^|on1){kN>9vH4bq{0g+2!xqgJH7WN6zZBGw_ zVFlNTJfH)L5#nw@PMM*tg&NNbSE+^7n1A+s;4;j<<#RLm6X@ry?1yQr$vXO{`bx*^ zD9zH9DgETI(eAHcdrn)-U@5=2?t+=0b;DO7sDyx;J3hF+d?==-JMNr&&a)Q^EWL1H zBL!zN^qLL=mRw@~4Sm=&m?BDY;{fTXq70d-D?@N?)EqRWPQOakFs)eB6btUVuYa#! zzhpn7ArvKswKJenl`kfx*;M0-2o8Yb5}2-tj9uX}h>AfN4OI;3dQxeab%)VFcFS0X z@1nkS;6)WZ;(WXJTk6o&984lS>=uC!zr^T(fQq~Z0nifT?O?34PDSw0n$W})M??aw z$(7+58lwUMmE=r2&F)a`VJMLMcz>dikYrTnDk@Q?PV5j4HNOaLdNHBtlH`}LCPj1& zw6bM=zVWacI~yd5j9Ow*iizK&Ww;BLAg5U}$_g0_AaN2vyfj8{fe4DT7X&2MT8sKm z`Y4-KAPC~2-6R9Tg1))X2CCVmW(GPQ??f?rjKK5RAYWXlf^-D?f|9UZs7P9$D& zkBS?bSfzi0F@-bl-b}wbAAdsc8ut*^X=A|jUR>xlYUuFYzKL#}g^L?fEX2~^z5>c? ze~UY2H%EK(ADTb3 zX7G<~|8U6vw7$A~SammZ;Y(M0SOXM%;ok`o`qTO~z`bceKDduj%zy9p4FM}-u ztsm;}I4x=Fdcz_#@CWH*s~-B%rQMx500BrjFSiT6+$sMujoB5?m=n!;zX&M1rjKx7 zA|6*3)*K_!;9&-S@PCEY@j3+l#*#rKCA=0RWq|VnYAxoyP8d$Y0fRTxZR-}tINEck z9z5!h`-@sFz*qURiYyh4>h$T^wK|X=kNqz?(F)4BL-f39w!4e?UvFrtZ<=;`wl378haJw+3xgP0hPW0BdST|r>XkR8F%5cKEqbu$ZD z3VyG4STFVd$O}P>DtB7o9EzZzFn{iDJkD7nJP}X?#~x%om|;h6s{_$zv~D)rXW4R$k~deyfDPtg@~HDNqH(KRsefpA{<@r_a~Ys%i#1^&XFPw& zfi2e4Ynp5VeBsocAL~<0ClhLC;py)&M7KkCqUnmDCxvI*7L`00JgA7~c zvjMRkpt;N54eo}6nS%Al-W!-AU~T94RONZlVc+hau!$8PgC)aREM-FD>o=f5wDKvi zi?C=BaDVH2m}|z=GeWGnCslo?Mz_)ImmjrSk^_KL>7<3R4FshOOY=D zaPipT6mDyI7)aov1gw{`-T_l9A_}V3KokWdNvy|pekrnasE(N`1VV_7W&w8WsLZz& zNaCc|Y@(h0Fp}War}ZlXN>@$=%-)c)8mEZdCx4bca%Unrm`zonORVoPdx0^BRf0`S zSb)D7)T>C}_|{KVzjaKDoDr2p0~bqAm(7`8j5HYC%-f|oKc@SZUeDrni8mZ=8Q3=L4UbWG$Ska+;A9-6MvBTNgcJAe-j@~zA31Nxfkw8oN&AmcWFtc%rImx0(V@B0a2H4 zMP9sQq~1|{J^2!DS<9W4l;3X7MmvT8WrNVF@M$Wu5AOVwTIKEu*uqFgT$M(zz<-!R zl;$v|Y5uIkm}Kv9Ytit#vm{QIeOMTI4LhAX)-~Y2zsZ`L?Y?4zHjwUXtMRT!Nv+BqN6pY)euz#`@rM}#} ztDzNX8nZA$iczUBSeCgcr2+?_yBWBS(|fhBW-*7)UZ&s`?Nf4od|7SzVz=~aiNg7f zWxudWQE8|&7xyuOCweEU01&(lv1O3gm^@z8>(`+(Cw;;0K28c4_Fs_|>n~&_fG8Uu zyYU^Cis(bBt-@EcD#f}dbZzcc5a)DLBWSeVX@8;|+UT7|*YMLk z`h%J&YCx{)kx2a!&)us=%WlGdwaO>vd|O03=e5Z8Wn5QcBs~^)y#hDs6-eYzj9_u6 zAZ)a5r&W#ms7O?(x;rGGiSQi@A1TxWVhHx*2eAt*>`#lnoo(@FyLYjp!2EMbJ30>3qK z1eS3y2i3|Ir@VuaopvqpGrmuT!M$QLWqBAr=8AB~aAfyHOVRikyGvSsal^pcCvVQ& zDS>p7DPVme!fjlU40=H)qe;BOon3R)(JQJAx}@_u<$KC#KZZ^!Wfzgc!}i}p;E(M2XI9Op~Aw} z+O@t->GSl$ns*8oRyn#_nnJ~!l8q>WQgfg+iq1t^^QHKu&fyasJ+d&(8N;1`_K*va zP3(t=6-E3M2jMUh#!PSB@GkV?7(5b@EJ*v|8;SbLTz^m?kqseK!9mmF!e#|N<04pw z6XCOh>yB#!1ohg9=9ySfm3ea6#1rW`3aDrGoG>m0W>tF6&1OJ*sRyN4zt$kjvnyC_QDz z5(9-zOZV0bmeDMU9AzScq#G#f5QFNsYc)V{l7AyopV4u|ou-qxmfB@C#^f~&NF9Yx zSi)EJ{6d9Uq#(~p0B9Qy7fvJsUmornnc@%4aLF~wu$bAKIVvV0g(N3T65`+)E8J2o z^P?~Qg{V|#&5dAi^|)9XYu~6kc_GajX^oI#EG(eORh1cwMvYOXHENCaVYl4d(JU@u zsececIi{@90a`nT(Ic|EZ0MBQ&y99ZMBz#bkoe zJ!@crPryMCT%Z{Qx?z?!$56lew^%@IVFM(KHjhywNP2AB?qQbtM=&OCn2`Fq$)XXh(bvttyv-tE{Z1f zYBUhm@?-<@3g8mFOZ$;lQ=%va%~^w-Ej~}U1|ls?{)N%?Iq~DEti%_aXT=6$cz>Q6 z4O!L&cCL^wMmMq`KxO`@13#6ylr5cJJA0gQDEKnj1DHl-RqCVhi4l@}%#$Xfr1EDf ztYWd~c$TY3+XrJLeA+~~m^s)ZMu^q{=@G7j;Oy+&@x29|v;9ABNw2Iaj z8QFz6%mvaA)dXYJf~Zm$rroXEc7J-SPqD{hJ}$OVsY1gIWi>_qva*`6u91)dBO`vu zTgu;odQL}wvm%?)ZdH&`K5uc!Tz;^80DkNqEh10A!>J9Nw1-x73r_8c=Sm2)N)wfg zr5>=XatpNBTTcULHrxWCFeZ5SCqh7CRwkE}WlBQO zP^lNWr}(VCt^eAMmHnw2L&@~E*zYf6uEZ7D7Gjt$XRwv6k57uGCvMVc3EQ4?Rvi;M zb2rB63!lNm)Z{gAJX`}<$>-w!C2W|bmL^#|+?|TGgk16laK!-?Ad-(QdTdMl&p7ws ze#NaZiF31RkMsJ>xh_4bVt;i4$0yHDc6`$pRT3Yn6S*^=wik_;S6rVXPYq)7Om~3E z%oM1{=t578k+D8)rfrJViqp~C7oSQxJ|r|n#a+5dAgu~&;e%_>eVFnoiAs7(`qw-l zYBxCPjBK(j)9Yg1DOi_NeR9k&gdy6Zt-6|U&vxGDD^}4wdRZWF`S?CcgNnGOQ;)_eG)>W;EkqSUS-3A^ulJl9_^!9yfLktd}M1g6MY+doR zYMu6pnGD>X;<57&32Vti!iqIdzVVF7L}!9$ARSmE+=fVU>Y%({zttHGl9)n{lB1Kd z%Z4aJX$iE}1^mQ5c7H21r`pW7by0QdtCrYAed-tlEWbku%pN+C<_!o^j2VjR(uqR0 zM+Z7_2=t|F8GzbW9tLDK=VKj;$`hGi^>e_c)B$DJK(LpgTifGYSR=;AnHP5rx|C-{ zQJV$icuq#h^KW?e`BO}Ex7aIp`r+p?czcKSZclC)$~;*zjeno@FERI%{ldGPL^CD@ zqUbFEx}MHaxTLzJ#LVCmq+Fi@#@Jh*R`!~^`tWN!L>wGY7zu#zV3|?M;CM!5yTlgt zelTdE0;@417LmK02KieYkGa94?4F)evsgIn;5^f9w*Nki|b>OWzSw=1iJ*uY#YI#YTii|GN@s!q#XuE70 zonifC@bIE90}G#qJ!_vNs5NBPwtCE*YxO%!=y;z`-2)?W8AiARCWuIU0WH6@q6ruq zGy2tyC$C>TzO6^TM}S<3%ry1Tznx?6N)Hc?W6PQKP=9peEmSRKOx&rRo&(Z`1ZOE? z2hB#iG$bp8sE9HGMEUGh;c&KMwA4HHNji$Qovstzj!hMtTGt7x;3(M9->$HuzoGBw zuh%N<=*NxtkGrM6y~dXQuD+#T=2)w2>9?D$cK-Ht<4!(R7wv806uj_ja<_*F*HJcD zmg@26pMT3km$h2I*DoXNg#Xu&8t8$$==@=>%$>N{dCb(8B0H1Vjd-M+G|PBKs3%>z zE?i);g>Yh`@exR4wF5%vEoG_GevvDBk?KoO`YNrOh_a8p^GkFwdKZIJ^apwo#uD#* z+^ckpJCxaRc`Vf*Jc_jIwuy#!J9s` z!)#zcEW*12ior+&t(SBxMa&D~XZrw=D)X2Wwp88XQrith}s7p)!bRLu5c_?>($2*%=)I& zgmLn=wy(DwXuYjKe(&s54`yN3%^u<=Z7F8bxq&ET;puz$$iu$paqWV^1qywYs zn25o-fhPcNTa5jmC5pMQ7OP==Z6b4<}GC94c&KCUD$X%k4*(K9NU zB#{C3^uy={Nn;JjUQkvnM56%%;;NjJ8`I=Udr2GG!xU2|t;?Za*=#tW0SaHM^CGGo z7PD$n`m?$xU(qotc~(eK)SN^dfQp~IFYcWe3Fq&T)s=CO)G=@4fJAY>) zM4;A5LnwV-S1@)kWQsa{H4hdi>CHRDgIZ>L(|}|C8Hz2r^{E7F6)Iio?S8!_mgF3F zW}!WkdLt*eSA68+V_>O5p{AXG9z{!@N5L~Z1zDHdt2bwg#m7xNbc(B&%2Z0S_6H1G zObkmwtQr$(IV?Kx_oJl}SR_=El7C1ryf|{Nm-rh2GXB15KOg~9BU-Q?ORC-y>7Av& zj7SvfeO}8*O}BQBQBbbK{sQSdeFsSxSo ziY*H`V~V~4x<$ws%M=m8WUJnDKHtIJ7wo`^ZM;;PK}B8XR)oGrvk$-L@--t{W ztb|Wfp=SSZZx8C!%^e_wb}s0h z;`J5DBAOFVOA`yoxuUdc>VIBQb+gDpE0rn7hcV72_xH3yA$mW?_5c{CO~3)bVa@;x zX5cw(4Nx4_Ibt`}=Yk4$I-{U4J~^1yd;~?WqdR zIKV)7&tf1Vcs#*~3WzC&K(Q_i0O@tO3p##+s-@@fJ9Z=zbrcTPX=7XkGrY(kc%)6R zWwc$+XTy^!G}&s_nmt*__~90f zRrhYSjbU_bj>j|cgMa4%zZkQq z(LTKM;7%OPJ#)lg$<0A9Th4v$zP!MR@8HKiP>TBN6aOXcK^=Gm6@R^kpD+f)+U#`2 z9^u@}JcN8R&cm^|bQz}cKH-1G_a_25G_u0a^s6I3BOWw`L4U2;>gKn3H$G0(dD5-5 zTI^;E%EN}|*q|9VRwwi*B!w2RrNrz9PR#Cu+ie;JOb-O&w&vAq*BYH~GcOPk{|7?} zr|(PorDL}T?01Yq9yNbVfu-#Z%7B7=h?kKX`l9s6({%wh2mkQE!Ob=74C>;P!BwRs zWgn}L>{hF^Gk+!~4$4N*Avqod{*`yVL<0(Bq?qsEl0^JtzmD7#U{ML;{m89ClFUBq#xqsPgce>KBemlDCe>~ZEfeHYr z3LYgJ#Tk9C{=3Jp-^g_%m+N9Fn`h$`VQs>g9o9xe4;0ZoxVKeumGuw zGe}34Vz^)Hcgr(6>DSi=@^}SHj(VL#n-7R9W=S1Ts#8gr5&lXu<5s?K?qcn<_`><9 zv{tw93ze`XRM9%CV7#ssa370c?`}TX4`yoa=4(~SkFZM zl*Y%O56=GV-j4$(oN8Y4lOylXbYd%x7J+)|niX6Rn;fOFu&o6;(lAksdcQA? zsIOoWF)rolv~e*~zm0K8!;3LDQ@vuq(N|ITsgL~gLxqT zG5yI6@knn1zic!eq+^(8)h}bt^wa2JN_!=hi;Kpp{5a?{3CTnf01MZUioPunXUo!- z()w!!q8F!7VTfXI3dT*aa`y3CV5O9V<$6`c)$!*bEbWfBIaYvHDk24-`QC`?@zDoF z^+5C;f+Kz)+nS?`3EVY}6DzB{zBk4S?0x>LX^p)&dXd^>$eW(^&42pbv;VCh?bO5&wIgwKM-av@E&C{PCjlUp|11^Rhh4kfRvki%D)jSg#?<;8 zcG~b?-ujq#^4BMyhY=HHz;CBFfn(izA?-((-GOKeJJNyHs6lJvats0arJ+< zHSDX*zjXbKi^6s0YcxWE%W1c=E+>O@v2c8eN~yQGupgTXQnO!oqSd(odw@MQ7d-t^ zZq5bYhc3uL-KM&LQCEL#g8yb>|11m^rys0OZ%rrb5y<}O^*GP?{Xxl83&{PX(>NK? z33}gD=vVmcH<1*L$0qmGM724YcQ%&qXHJOn4ypcI(EOSUSbu4<--_nH4-ub(vh9e8 zl6`vHq50xD<W_YH-9s!GrQ}*KYmu*PoM;-<$w?N22(0zjpND&zPtig<+6AuwV^B z7#*cDo5jRHQi0xjeED+N>QKgSlj!|gWOp*G)x}22Qh&0`Ra>7Ok56ZGEM7`2{mX5> zlGekb9bJ+J7FjuNB}mQIC@H9|iZ#OXV`Yab$|>IA7qQLt74}BO4%z(q!tcJ$S=VxW zF#`#_tc66mCW>V6$1|}j%vM7mK3<|_Vec%9qRTwQ&a%*?>hILDupis9SZ|!sQPIl+ z|H=Pbx3JkhDO+hRM37UGdnorh7FL`WXF;Ex=8#U8c> z`L>XGXZvxGOcz76TIxHirRXvbk#Dt_YsI`Tu75q8_t(+~>(SB9YQdXDR!jXmjEDUG z*8hp^fj8E_nDw=>EWRZf6QHnPBsun&cc9+HIzjJMtAJ=J9@Y#^UCWSvfYFno{0^$R++!J zR7k?>#e6c|n$d`A?2M?Q%REHB5oIn`w!q`@ji{{>((^T<9?F`<7E`0dVp8uB1AR-I zXszYkZoTF=wTZU7o{0e@_M-_MOR6!^DSyR_M@&g$JwAJH!vb21Oa^UxFygK?+qZyH zD3+#pEbkeY}#f%ScVtup%>rh04e0qj@q7sIe%C?x-ib)!?Sy>D^T~#)HY^g?MP(MP>(NswqXrE z3YFy2|N9`e?%f#A&YRJZdv28g&9{T^!j0L)iBN=);d8|dD_cEixw;tapSgbU;~>={ zf(Fqa7^NPNW8-%g|DU~QTW;J&_E%VE^Wdp+Ji5zHm0hK4#+A9P?MXIIDSt|WZSF{< zLQ-~|r~QZXe12}fRkevAN>WUnTJ?%M_kp#uvIn_q z;-nsWX2L|(qeGTT>W;jr`4UrRWKA6&shdse@bG#l3gVKVAaM!BQv|7;q9RLjp%qPKd2$$#(r-JGve3@fFSORwXK z3h-=uMRQfa$->wBpFNu>+!M>#xM&XEe8#+~fb~{0V#wA}eoXjgh&6b-aM?u`K287t zm|F?J4P$vWSZ1z24sEWWf=%@q!(FbLKPRKOZ~FtSwL|v)qzwIu^C(Pdufz&9>#k+p zMyXl1^U(Qvi+{{(oY{`Fo0;oYAI;91o%hPl%>!fSLA_lWgqKWgaiU+pCedUPlEtj_rjLUbcwD0>yFVRWc3+KvDwz$~2YS5OOKiZ|{4_>BOvx#PtWRzW&n@xoB z&YD*=uOy@Fs?@x4fBWf!@u7eJ%dz-O#i4@vOh2~4cYgt_klA8tLekzdT*-~8p!Dfl z_JSWpI(1~~$+P55?^k-cY_u=i%9KU<3ETQ&O#-gcrz}pO+$Np-j7;F}KDxMNPgkrq zE-~FN(tN4RVr;ZnQai+^4T1jba7}C3o^MX4%CoVW zCja=EG_0q+S@-ZcJk60+wy^ zvD?XBrMnaY^TS?zwM;9!DtPjwQSDt?YI00Lj>CStmd9DxH$~>j1S*muhqY?u66+(x z1AnbfEU8~K|NEc+OFZ{364(u0uV2=t;g_4FtBpJYYu_ZDC+%g&B+Z+)P14niMq~Za zl5*2@y>*OYI!~&~VT$SIMJ*dq(*?^dr)4k`Xu8*_3yxc*CXIMP?j{aoHyP>UyW(->u~cK<*;W0w`*ZV^PGI-fWUFI^#h;Kav;Dw2-`5%3 zeD=>n%ezH+n$ukJ{`AVt(xHOsD9+MVo8NqH9up24VLv^_6vmRW z35xj9NzkELRfmVnO9l+o`#@VU+*Tl$raTyN!6TPLganhnu*{PeI~>IgT82Hyb&QqK zU;YEdxKJqWdUxEu=3AP-U=e|y4LAF*xwvNv|#EqR2!fxeNL5@tx5@gSc52XarKnawC*AtFac zXftXvgKe1pV=3OtIV-LCppm?Uu5EEqTw1O*2>l28<@c)7$PMERO{}#`qhm}%kS?LGdFY_QA zgBF1 zoO9fH;{>d26V<92D0b&sq`pKrUdP{N3`4Ov9Rt5}qSEN74OgFH(lhMGfzPDQu4>#_ z{2|R9H^vHRfI@(X3KRMWfF~%^+~sLR&S|}7nBBvkV5GA$3Nm+MKv&5FWaBD}N9hV8 zTY7QZej1Y(m*i$cn>j`ZA{&vT56HO0~@2ZhVD>yr&SVt+m`m1p*RU;tZ>gRo4`X$jOI-2q3I6k5i8Vt<;nHUCRTKkgz2c@-e(%m?E~P%I zR&Gy8WHQ0KeKF8A4#f`*uG}r3QnP}#XSiqQuWngITNn%_OF%`I@He!8DI8d z_Ks4a)=y;|FmKp230A$O<)~-&EwX{qwDRvqRWbt8?sNNojjDwJY#ANI31Athltx1j z{N-9;Y0(S`vFO2ZTFxiBwJn)mf%ph+#095+a)32)Gq{COVQLzyNOU*$cuqFe?lzUH z>L~!tbUzK7&lEzSW}Z1r#vpJ^vlZShUH0`a+i>B}!b>I^&X<|dp49je;c6f&alIHy zzj!k|+SoIZ%gn6vATIqNhRb^MzJXH zd$U&8M2~*Kx%AcvsUJbSfj?005w@?T@IC7NNcJx99q+@fGUlB|PyWRzk^RvoON*?Y zH%kcomNehq*R+k|B*=vm8#1P35!wm5VRo9i&z0+mwLc&TkT>7#8dHPKeD6Ko6sua( zmfw+@DviIpF1UtlA6TnhMDpJ==Q|VwReFIH9?yV~2>v&zhz$J)j#gY1Gfz0tZ(MX> z*}HH7PK>epi1v-@{b(Ovqk(gq+pBjmipN>7a&D%x&Q#HYtAKB665zQE{e7YNhd3|9 zb)-S&X4kI;=({s_SA02KxeGq3W~*CSA$>HDnHn?#Zh`2mGVsEX+W#P!Eztvk^FpBc zLIQS789mIo=a?JXsEU@~e1-$9S6)e}(MUqZC*nFs(QseCULEy>9F&krE?FYubJNp{ z(0s3@XbJuq!?vSRc~Q?SaYw7!QZTnkO23Xa9XOCNVLF3;zgmWo)@MmgUzp;1fX|e- zGQH@be=bQ$Pd4ffpCVV|7V)4q^7`1cV?i2dSP~^=6fF~utAMaW=@rks4ufDT7iE!U zgA|M}Yvf^+ZHQ|u=ocahRWa;Vq6!W$@jYFspp`1($1dK#X znsG>xG&E6HX=;CGBAw(6a7bN9&5+NtjutU5*g9oP`kR&&r~Xz`6oaDglThuQZ4rC)0Lxz{i+T^>>pX2-MV<4aG zb9d9iu8o9D4)P~H}0$j*FrayVC2yhyQv&solUvcjLh_x%BIkAC>LV8B1Xes7plAE*@7?;kV;_K zfoS@8c;8D@E1_x9_i*xeLTDb{r1}#*-g=HJ>q0kxk4N_Ck>L#BLkH`pR712YR<)dQ z-slK^%E_d(DQ4`dA ze!kW;`5@0Gty5<(-PlgkxN_j<4F8e_9*i}D&3AOu?+{7p0P1;Q9Bi9B(hSAik&x6K zh%iA8&~@g`++I7=gW&EA=quM6V2K5E`_cRd>c9zUAXF?#i61|zS?g`EKx4KZbj%j71Ef|%4ct$pmyG_yJJ(voxk`9?E;jBK1JZbU zaP0v^kG41Yx2*^@+biENgAXyXizaM5>PU|4C59>8SrtlMZLD6U;k*ss6vDiH-pI!< zm{DB~{f2aCOAu>w;cfh?P!!4#YlpuT{;Q=Wf&`o;WYWPNjy5IDpL!w7>HCa7`)sJB z@F*L7_gpU!{z21-_{Hq_w{@Ba+r7L@^R|u_K0RdEpM|HD%Lsb(%jds<-;{i1N~f>A zg{_LH*tU~PISm?Ed1<{7y!|F|_z zJFV=Cnnf83(cdTiE4e9jC%#vGfV5lw*oO9~(=T1TXnyd!^tI34H0^pOZY|>#LIOQ` zlJj*W*sya>DHappdsoTiLsd#4E=h472t<@?3$)HTUb<)hM0Ac-XPGw|+Wwo-&6ZOi z`?Omz8EJ z`&O{(4MIJTqpE7XB8_BUM55&68WL$mDTQkvD5x!-F}1}zfg&B znz)47hSx)_CucpdPZboAxOT=df2(L5`X$0aDYMpJ^UnJq`;llM;8oVy_WNiUqT&z+ zC+NI1w3c(&4;M@1WcolI8~Z#=98h>O`8S*wM(`nA*E#mf*qZ5F-BQ-HZN*&&N*YZG z>FtMivX2H%6cISf{zOZtjH{T;ySaUbyB zHc8nt3t*z7oftK(muT@A(wuJCGhg(o!8i09gYJY-SledIrUtEREvzWMKyF?Yj(@3? z=~O9>n7C|n_jSi6^v8&j;qFi~Hqx$k{Eb!UMx?GqhVYWo`G-Lx#QBi*AiW-nLT6O? z4kSMXE;Q{j5dLg1!#PV2z)r!h_9_2;25Ko8(+e>#$`6fw-Y#}U4^};R+v8cvS+In(Z?dWxXRN4&e z0Ox*sUtTSDJH<$V!5dp_{JFK?NcYFeF@y|%H3mB4guGJO+5!_}_TAuzqz(The!H&u zEnCf6Qw4QNgEV=PMFPWrhavmRL6cd$ul;HzN19I$cme|&0bH&{f*ni9slHw_lq`)?YjG=gxVXyf)r*-noqW5 zl^=fKBn4WQ3euS_u5mo z1g4^mDdVPHz8HFCkzLm*#%(+U;TEirYVtDsCDKq06oX9vk~wnH?ZnKDrZLJ+B38*z z#pj;(__a~J#781HvB&2({M)*ue3E>dRtbJA3wj1v^=2e%D9xb#cDG}mCqsvW9xmHu zbOW-m+U5G#GgvYVt6|i}z|+EN?_R>3u$bw%_p~ zr5%d)*slvTPv*;}rx<*e+YfvV!A=+sF~r68J`z|f~6^Qei3kW`G(Hb|fDw%`eo-Wils3B6(_6WCEUhUjfF5nMcaDBV2x;%5d2su3C=fXM@eXgj@86}&1o~Xd zf$2r;5wu~CRX>|Op{Y)c&S-9g!$4Y8uQPuYmZH*}>Ur!*3gS5=eJh#B;t14R&YiH# z9VuU}lwdzg3iUZ)DWII@*PvErnYB$V)vY+J%lsV|ez5t8W}ZK^%uj2Bas}+x8BZw; zb?7)+-ksDd7to&HU!Q!OmV4ru76r3C070Wo#$;7o$5hXX=W1d{sk{W;$Tz-4DMCfF zrrsDAKYhOnOtqdE6B}~-fID&#TU$?0#j!Pc!+773-uGc5t|w@^L(J?4mOoxYB%Hif zvuO!F?54dW4>D9)Hq9DUpaNJo1^;qf!Bk@l2*`aPz2G9b@N-aSZlV3~&_Zkb$KS}^ zPsb95#=6Mc=Sc23YBQ3)37?I*>cpS^@l94elm#oJyg+0`vr250F-o42wO=GzU&rTZ zKJI`YMK4YS2@=7qoAZtZ|39_l@Q2ks^6Xnn~@d6}1`s`QA+ z>|&KuZ)CE?mqFmYRkUk~?vKI#fC@Ui2^u|*-WXaEgMeH)qI!SCASGgmAJl=z%%21p zVVt>H7vw{s@+39#d~*%WFkWIGkT^5L@%}I zF(A``eh>g5KWW|@sB8M~kXw}4Fd0F))Md{BQs8{*;e70v6sRi!5fpoT6Wu65!B80# z5nICKQipP#l7ej*tylUar14i_ND@R$^c5LO42A|1dP-%Tyn^@UeBQ9UWAvp=#Hh@`)AVjK z#1MiB+9Df4A2?cVUd+~nx;$VwI-KTn*canuz=1!MB%0$-JtCaq6J+ynRBvi2S`}|V zMpekZ?{|Dry)I}6Ar}9Dz71S}m$y;4ClLsjOx1GhchZ7eon$fkzYsHe5Df9r*CaZ+y%#L6E4zmBF^%0cFiF zz!2642=iDFm0sAtHq@t@r88*esa*D~C){K+yP8_%9m&GYs#wZxmLWDGE0j8)A@AO2 zEW>BxuhI4;nDtt$ba{n^Oqn}$`HUN3-aO&;m@mF7{8Y5M+A;6QQmq;tv6FYpz8BC~ z70J0MmyR=U-lSS=;VN!dUKG;z328Tz@s|$=PQ(P5dX-ZHl>I-6f6bVPg{=HlW(}fn zJ6#M@ms!_}UKX5X#G=2BTtP=~m(s$0HnalMw_FT7coWY~-McBz4$Qf5Rcq2BTvVou zIhBms8IrqKz?doe(KNr)C5SGa8blN#Tt%6D%Q7>4F?$+rDXEjFWpOXsBuYP?9zC}L zlo3`(g0%}T42yqJ2(8Jxw%whHJ6s|ynr+RW_dLg5H3H@5K~RHsW){(APqVqR*~UYn$efnaLT?YtNCaJ zRiwyqxL(86mOzIhG2c${i`P__h4=s3hFh`FewnJi(5tYo^ARLi*d3ZAfxZ2(6`pVo z#jz6(E7aqx`qGBy3^Iz%$^IhPcB`80ryTwp{c+Pz&&;B06h*KO+-(l6P@)-wx`JluhW57`8V{romE1$Vlt2 zXIz!&Q>=mI_(shyqG|TRC8|+;h9eK595^D*EGLMWVsJ&`^jv8qj#9oAos->NX*$>&{ZZ|JHA3P!)V5+!p>3oBn&_RFnbECI5`-*!z zyyY;~>b1OED(t!c22z*w3&p5fb@?u8Ahj3ELXFP3Z>t8bZ}{Fm##W$e9K^p&#_Ss3t7iSUL) zyf--oCPTav4Q^`Z2~?8fnsb$A|%|*|o2LV!cuJ;VKV}uf19Nw+3dTT^`9` zMx*Sm=>n6%|E$FDS6~mx*odfKYOgn6g`XMa=f)Io4ga$t@@2~zZD}(bimKFbr%*L1 z<{{qUeeo9`EAvpZLzCMaD(p^2o0IR{(QBxWPlBI`*o3Az9(&-IQk!n@44od|J|b8K zWXokrxirVdf{E-`pec{rexLl{;+i=n;HC@|Uh2#YO}UGG@4Iu%mbUvHpK@2Zm3Mb5 ze*4!-Z+c+DWAp=L4)50bm599MNHeDky81E~SCrqER^9A?THvS+#ax;)&{F+Ek45MH zZ=vuK4R_ODg=C{tb1HUu(kSyTp@PLKAjC!2Xz94(PD*FW1*eUEX6k##VS9c<7*fdD zE$z@&+oi9R#A9@Ny_0UX7i#mMOT-uG*(>k%a66c(XVlAHT;bV&H#?HBVBpz4EW^>q z-=?z0Uao(Cb(qdL`W^prOFOoeR&$2x^zI=0tn9QM|9YGFhyIpR`&P-0alYmVASuHY zJU5An_rI$=r9jZxDP%>fG2i2D3Xh2o=edLEIcn74oJlAA20tKgXo|*tp#`82I$(H} z=_uikh3dad)G5CT%9Ot2X6C1)oTys~;}_U@+?BnUIa3^M#6WcF9k#>t|8f{YwzZjP znJjzYVEXu?dSy>5x2&H$5tmkhsTvTHy%*A7zrW#e z+J|+s8IfkU98i|ITV>HAh#4BSa+K;cd&mTJ?YtA*s~loKd|Z0WHvjws>}a~vLEw@0 zj!jkZ>5q=+2MXi|F4W3BrXz{df10l#Kgd?Q^;@stld4V=Vm+?3;A>C0ziJz^r);_8 zbZQvieq>Zty5sxTuG=7Uk6WJHCeR6Uwwm>SB(M+2Z5l_(91XOhtK$jwNQjaa@RH2PiIPfNh3K$)P@iTU z%)fY$n8=fbF#77rzN!kTl-;wVNpfcBpL*3C+UiZ9z$%;!H<~*j|5MPxXx(G*6{aO zrW<0Q(qdwsB5XrDTSokDI@>H}i7J&`x}SweR_{_>1JLSjqxR{Tr5QNr(mW$49S1Xz zH#?UwzgT~~Vv~LYsFH0EqF7>{6?m$Q6qE|2L(xCBT;C2OuTYl@<}27kI*;?J@~lP4Q0I*-WQXO({El<+Kb6$nI1=C&T7Ao}kD*&9}Tl zry^vrPxiSTiFG|ne>e2h?}1$D&HlUW*B*RK`jZ9jBct`VAs=pYLj)DIcCs_z%BqoX zB|r4h-|}ad9sE%zO+QE^sjxe52>cLNK#ToGA@Ycc3M|>+4`` z-21$ad>El^fAfQF!)C8+Hy!v&Px1J|UoLna$oWeEw7ZW@`N%?OvT?Z6LIh?1aD2|L zo>Y-kvbh)etit^)Z)If@%DYhXfcgvF%Aueq@imuF>n8 zF3kELj#uzU%lwSKvTUJ2kN_mgrKlA`$T4_t3nZu~XPFr8rQ#O9Ui^nH-Q&dpkWK89 z)eZ>&w_e#sN6+z?nEv)|^`?yD(;AGq6ix24W%>ov6S2@nG(AQ=m6pc<9cQ*J8xQ4S z4WGC6f+Og6z+<+M=}P2kSnDl!VH}lA&TXA*FGNcWN_^fKyaYs?qpb!%{4C5aot@#! z!low+@0negbbYEE3Wyn5N9xRpfcrf(iNFzHBCH7h4L^cXOa}C(0=Gkru>F%6MkhR8%+U_wAS0jeGoRJr4LXRH53V~3n~0YM zR}`>;4MezX1gl|R@x@Y7JZ413b(8<%a15(b=tQfdqB&r{$fK=miQAVG_gH?Sx<;5m z9kfe;VdKj!+H(OPsXsE3E=Z7qZqSIazulYpTo~@s1C23G(X| zYy+HfC0KSWLd`#q5#yPtG(Map=!Ou925FN8UQ!r*Ix2xKL$pP&TYMU9ikAQuagFL) zmuqb}c}fV<$by*Sf2$ntCzxVZfMO%@fVJVo5eQ<{yT}SO&%nbra;a;&HV}6rB+S)1 z9D`m`(EnU(tp|Y!q+bla7;_k71l`TN3b0dQNnr1Fw7!R=_Bb>(USx*a;_1Ba#$Zz z?)iGj1iKM?$L?!5!Is9-7Zg8TB4KJ1^C+K{^$BMBt3vYYL9e{@6SgpvgW#s25@ps0 zIfkOmF9?Uf^f=A{d#)fKHy$Iyr=b(ow2PYdl#o?vfP!!R#b8Nb5UqrV5=dxNw4H@T zF0v(k##8)x6Das#EACzIEvk%-tvdrB*RltOV`Q(-fVf>PoAFGQZZSq9p)Cth(p#)@Vf6jGm{; z1fzVGSfOKI?H+Qxi#x>R=O-1k&u zDf^(Pe@}i3{%*_Bc@^Oetb$XK+H*hiCO;CqiCvvX_>^amS@BQ^ejY>xO`R2bBDTB|!q|W1^@h!30Pyd}OE}rKNRkz>oijy7TLKVPzPcD)kdR zyTGu?Q)5jjRSjby^!Q>YT1HRCONK(>F(mETJ|sl@btkb`dT_X1VUQe6?IRN)LLY$T zhIMKJ$;?H|$p7ZGx9AC$0wN%1;I9~DUb-C%skz}*uSU^SnLeX5pp?bMCDv20YzrV- zjf0)vq6MXK3^9xTMmrVJFmR>zaew_LSEO%A!|@;4)a-b8XhfpknrZ{BW=o;8c2Cl2 zkf_Y5=9@P5XM-voCl&|sD^Qhc{p*s5zw`q~IXyAbVk(f*91@EpzQqtyyfmmvziYD` z@l~6G0E4B}rtaa<(SQ@GnRqQ)1IR+FVz&|-lotDoMZ_>d;(Uc*8E|~L=$U08M*xIB9pAn6x$Tu}9n)c(#8!c4JpHJN%NNV>< z&H4N3U(9iY@1okhoK0_(nsdSOFXyC1f&)tJpjyD^igtsENr}L7lMin3A7G9mNKnKC z$^IfWn3cIH5LfyZG^#@15p9+vPj0(!WcFdYkUrI4Lp}R3+)mik^KIX5aH0jDUD*`? zm!)9sRh~d}5jQ8=HXYk1>gsFzDx06igvOQeSLulg-+UTmOcS_9l4dQ_CAt(5B_Pp@ zvtY^*ghK_wBk-)6QyskL0QJ&y(Cx@QY7Fv)t}dMKR2x;PNb_C>IK<<7iFE3gqFeZvH@AB|Z!R~(kmPHdg9A5k zn#p2f2R9X9T<8&!&s=R$6<*DqKZ~R`pDyFKFj^?#<&sdz1XMFE0ROo%r7x`jd=@pi zLX2F}UX4_lk~($nL>#s18GDGVDXorg{h|sL{ahKwu87|%=PR`I^U{;-=|WkcT-KT& zWnyJ74rr9Zi(l{fZ+IRQVmq`tz7){Q?9^mLHY$ooA0*v~-*E0K4A0&8;of;?^WHi{+D3{t-+k$1oH{j4c8+3}isP+PSL@vTs?)_=413I3;PVBU5usilZ#8os59MDru&;xz8hBv{X}nn7f6~)@)ot zH|kWME?$n?`=DEkrFU@)l`?pd&G>&#qL~U3IGtrL8ZXvy*s0ZBPh2bJrOjXtcvmY} zwQ*BjNy)KJYQHR#2r@9jy9X}Y4J{i-;Y;N#uxQSV_+`OwXh*kl%nZb!^S%--EgGV+4_fu#^ha6!q5xlq` zfR;mye5T35Ki!p?4s7*t|HA>x>JN73PjNxY_~-n9IbYGb5Rw53WFue{%)xSHGOST& z-mbO9!L~DfVah~R?gV0&AjZ6Nf4oo+rF*+T3k}k(-i{u9vI`!z23^*ExNkpyGq;A} z8kgWKHxoR zL&!n60G4aeiF3fvn#R|8LpV+b@fy$hZTW=|eREGaZys*iE_F*@dzjx&D)@aF$f4y{ zKShfdU(?N4RicreE81B781v(y9juZ?0VJ%Qache0_r~=mqh2gaK|cgVX#X}et(nhc zdJT)4zY%zuK$|Q*&g)zpvp^DCaYQfLx@Vg#WuI2?y^DPwb$W)e{yj;Pp@g!jTXt2` zzNln*aB^1By?`NccMjp=uJ{oP1k)yLEFRsmRUwuZ9_Xh!8j!@PzGTbot||Uu8I2C% z%ITQiXhNBJ>RQ*PFfX+;;0sv=>$54L$p6isp|xEWbx2QT7sxc2-InODbOZwi1Nx;A zE+{%jp?*}v+5L7-AM8GT0E%>$q*sBb6@DK2PzmR&=YrP+8WNK4Ga+9KTuD{=EJ~vA z^FHIOlnQ@mKP&A=*|2sEMS>{M2bjCQw3`k*1@%7vKXoST8bM*oCpZtfR4Z#Wb^3pegbn zGL$=*VZ-X)4Zs`jeG}XH)xgJMROND2K zVdwKNM_$NrdF%{2A)XD14O^)R1R4j>O@%dw^#ZqOS|^&TNk*hT)miDb?c7uKlunK5 z{sJ=$f$eVBzF63s*S%E_JfTKm36UamTW&w0&LhG~k>3(8tZMRMiM7i|4m2P&ZUOmd z3)ke;z|G0}JNWsz_9Og*66Iv*07~BiXaQlUpB&a5mrs}8D|zLzxQ*&51|3hVeuo0B z8#AuQRJ>ZzqWDxiVN0J1MY$P}TS9*)x=a7`Eut6rqQ((GhqBsUX zJN9#t@R#@q$TK!1`56|8RI?$`!rJ0)ZC*a=%h=H)=#~NAC9~l-Q9Vh><}s}KtKldr zMgyc|9bvYW9>HKkDS^8i@gQGNO6A~b4G-^Kt1`Dvr3j2vbUi%9Kr$sL*FP6LSu9HBL;8)bLTwTFx^fZWPP41dUTb$eA|A_QI3FKkK>e~g6pPESd z|4UB*GVutv1{m3OZ(CE1Y(r%d-Fa~fj5R3RQN<^7U4#zCP;AVO0dx*joVBR4;k6Q!)|9bMtR>Jf=I=2vMBJ{-s*iQ3o<_i9TzyI9Ms}rIx z1z+JWd*&bT=@tvjX1jZ65CI46kVS$1kD9mmDP<%Gzxbw(lu`OV8Vr?@ir*i)yFVKt zPSRYtf}1D-MbN9ej2V{5^~!h`hLH}rJ|}{=Lo_d8$h!XEpDr>!EA5Yl{~aPhLjxVDi_py1u^;`NBQz5b;; zQxHRiL}0b~Y649?jcUjTlrcbbKPRo?-0Z8G)i-pGLm}Nh0Z6Avw*WngBfWqpSg1eW zH*S8r()TR)DA1m5{drjJ+f(MPVP!Tw@(ir%o0r@FHhQM72>$Smp<}pR{a3o&AMFIX ziPiqQG;nAJY^UV_Cjj<0sEffiBM6h3E)RAZNGgh0t1@y5HG=udy$$#GfbURjOZ)B> z<4RPuD){gMaPy8DRpId21C0lKZvtM+&Fk#bLHa-c4v}E}+`lu7gu1SyYqp3Yy8!b# zUWp)OZAQ?vNY}gId9!;K7>j_W$(K15Ohc7r#!@6vEIgXO-UsYQahP%x!Ypts5*1-r zGOzjmz+W@_r=_WAcgL%C{pYO;WjYl(0az~fv;tdoE z7sUO5t8h2wTq?@%3T=(IFe*-9n%oZEvB(zmenz74xUp!opqW2I2f%5;CZvWKgBGvKmhz7?7zBZEs>y^lB||G9<)*@ z7&P(6BjH6N@<1m7@*75_EMjj*XrjQ^ETFj_!)WFf?0KP=cmlvxS4*I`nU37VukOt4f$h zq4yNe!OgM2p#~3d7HF0zVmO;Shggs~zB?B;S)kcn^ryn#D;#HT#;e~>+xl1UT*fZx z4{}vBv`p@fn5fCQSryR8_;OW}=NR*pl}jXp{R5PJLcJdO?UElE&Jbj}t1qwIni)1+ zsvE`Ty8a5QqO$RhRU2bT!u9#S?)XWm4SJ2(d!&?mtC;IYW-ni^J@SnVzgos!c?F^6 zBN7RXx8&4z?D3%^oi|Nxz>l6FxHcAa?1VZ}*{OwOOU^Ud=W@oxJbt~a6orPZlhXG$ z$_0Qrq_y0K;BQ9LkQAjivz{Q1E?`yqw*Sr!7hm~(_%&HaBVDd zEs(}^{Ii?!XYy(5t{-%@IxY>i_XOmz(|)0D93&FbiR2WX&~ql9LFN3$+r(teVOEue z|0c?2f0p48c-5rA<&_}-cCT8(3Q9hOn7ts+7C>dg&^R99bklN1;Jq~Y7bb;6!-7xp z2Fj|0!on*rNe!w14hNfb=x1eP076uN=q@9@(UOGRo)jVSr zeyUn(t?SPa8*^w+k#%&XHN(9SlBjcgJ>A^l?5<*cK;l?Dm-n{OqBve~6ZCKSK z8AX}kf~%UBMyYJn6SsOkUVZwUp=-YtR$CeS zK^8K75l&XT>y~S};M8)bD#1m&iMtq}&+13HZ)Uzisi)jEd(w!fIN8!@7jYiVrxSAO zXXO`B2Y$Jv+t{marS*>VhA+8dcg=CqJ*mH80;u$Co;suT@EAqg0GGVO)OoG1j1J1x z=O>pagG;5#S3O6D;sAlcimh9_!X#(Ci9^`=FNF*_7_^uO>o+U91_zCi;hn&|=5&JJgLe5-k*iU5s zSQW&K2>wQSKyRQGX`pIfv2_-|;Mc|~BQk_J!D1v1PAi=mXax{U&#>HtX0>_o>-umZ1D|*^cE0k8C79q+6eII?FEC;pu*;c z1N16ZPrJ3wz);Kr!ZXBL{gE+l8Pydd1YBMsnOj(Im)i~)rbnzn)|xI`iKf|Oz(C?% zq+xLT$S;UzFMx{e=HuZC!IZ5jH!CFcNHmu+%OI2B`7~fp0R=1&oZ6Z>-gf)LfS!KPjxR)pS_m#s|vE||PN&`rZ<_PK4`5Ic_p^$@$4 ztxw5YIjaJoANuXW))eYU3N(HzqaOK zaK35*uOwceKP!j3z&rS%zQ zwroeYuHPv=N@MyfQL4auQ-T2ZFi{@|z|F$O_33eTiZsneM`l zufGYQ;|Y!&BE#ieHb0voLz#J-B2^r1=rczm{4M4f?0Zm5VssPUFSrV_=zO;CzsRU| zj8fWu`X5QyBSr3)v+mth;K7Fo1(R2!34z za*7W2U&{)1edu>8`d>ei2LK@|Vd*asje<0R*T~6gpahrmIk&Xs3{+OQ+@?CQbWf+Fu0y*5ah7+X6lTEn(7JBDQu4xbaIw^5m;o?Nm z#t6ihdr^(Rj{$yJuV6As`t01|uCXEIMP1+dcq8N}bK+}_Wlm6j0+NolMkv1A$7=XN z4Dg490HD@R1A?9T^~IlEZI<`U4KvwVkPQ^l6JdH$8wUl7ROG*~AdS`J=_x@$8K$4~o< z*WBu%j{EQ3*u|_O#E%;<74!Eh&-@22{VyS%?V8ss3itF0E+fMEmB>lXzLevw#^%&lkB9f(sN zDayX4?kVyg2?v{Rks}Z)%=Cone)+XS##tII`9sE<2Mol49UE6UZa2>mnjD{dOcobX zt?oLY5t%p}Y{#qRYEkmKzN+rYUPBuAGs*D17M0lQy(PiwQGIsJ25U?<@=nEc#K9dA zq*Ibwjo|>*#ys6xgmUXdfKeU#W%|60zEk^u;~Pj2Tj-bN*v;g{_9?-PKMU=bv&4o< z>{BxSU+baVS|M6MUSyv#e2Jdiuj|B25GKD%-}FXaXwN|Z>CkW%PY)veEU~K5QAV{P z8L>266`R@5cOj`ruxRFz0x6lf$Ok(P{Bm_YTmE-5qpP6*eRoMjG2M>QDai+D+q)Gj@nU*R>eANhgR z3{(E-91~Hmy+Vk*CZ>N(Y&=1XU}6bHjc91aU7*%z9wK-@(o11fP$qHCmxJL1mhY=s zBrdr{F*s*mg(#yUw$2vBsuJVa>RmG07jV0A(c;qa6R6U!qDnIlsx%zmi_F+F=b z=9NT%kn`WGe9Pq&k-ziNmqsjYEufP0mnvvcE7`PGk|hLn(zTq2X+dSqrlA59nd8NL zyMK-Ep(|5^NT_}2Y_=x^J;z!B$ObpY1e*!UVThq8VZ5ZfSZ693DFMTE)1nvwsJ z-U@{dD%EKv_$_zb9`OelMvIjndMT@?l^plH(9-OCs~V9l?yg#Zm7wGc5E&oGdYiI? z8;c9#3;1775^w3}7QTgWp)30B!M?ee$A%ms^nYzugnr5%ZUo_pEYmDHefGPi21e(2 z_5ah=S4PFP1Pu-_xVyW%yAvQtunBH~;66A6ncyCr3GM`f1`qBM+--1z1PJc7y!Y+y z+p~Soxu?6jx_)%ux~1Kn-Dfie0tH&_hjPcHiFwq zc24>nEeSna47uUxC`_8(l-0La4NRFu8Nbp?+q1Ri8L-mYc;Ym%#sMI^4S?h+uIsPm zj-U6EpIp?g$;UbB33pDBR|u1_#QlDA>fK8G7>8~=-u>qG@3r7e@m>Yh;{xQwc-?!U z`bB#ACb1k_=vKmN@|esPg6uzVx;~eT{eqeKa9`hnNhQ`?f3ZiEfMa6N94`*Ww3p=lG6n^f9K(+c2 z2+_7G9i)FC001Kx$%08tkf~l5^WR~QXl^L>3|!Zf*Ii2W{o!<|NsY-wPbZWhY~~fG zjkvl+Q#v=Qb^k0Fqc9A!dY{Z2@>vjUopdtTrDnzsv1%405= z8XS(7_7~|RFO)dl6_Xm`kPYMIyAH@EEb6!K5+?TYPm4Nf&u{Dzq_KF@9 zl>f$PX|ffq`D7u!EIpSonU^_ zMm9<))wPG>gZ9)0abq)hy|ki_M zP}KoB!+7Lr*X>Gv*75Wxdf30$?BKG!2J3wssH&;wz05_8{^ZQ)iWK{%bsUH+wBgf;BvBjAu-gUm;-?eyT6(_fz-~H#TZNbASex`R$QfC z;09|IcS8g*2o1V1+#lF%u2@&=fN0-WEk{@5>G@e6Q1R@B6MZF6z~y=qym)*)xV@P- zy|cK%`uq9{4!>+DTx!jX)g0SISCC>s)=f(|3&yHDmOsiIyNLV!>6Gu1`_JDUF~_(M z>9m?X$Zd>Q`!q;Kbb(9+2Qq~!1sII?J#_{?p$jESkOb_ZAAkr@Glq4=Ck8U>a=V~<65#!rA<5(d$pF+OJ>Xkum-ibF<+{Z6{*xxR~t=h z^iTbZv#5mlOUWq~=T2u=&N%-#Gc^@F3Owx;v$2=brZ2I6yeg@+SD!ksY+TQ761ZJM za(B7OfmDPwXd68^H(Rf{t!SWvJd8-#Er47cHCv~ur9t=4OHJ8p7_`m28Ksi4$%OTF zHw7psqsiws5+8V$Yy5T1$SX`Ma~XVK_?gR1C2?*}vt(TT(D=j3>GH5W-_EC9zbmEFsgr$WVNl6;%{D-a;fN`yRC3S@|tUR77}>9gj^O@2AFNo|*Ndn!PVKKtrs|y9LrX) z0-B^cn~t8BFr!8rnaim3VB8Ox57rC2^qWta3#fTPR;$ z(OLI?x>;ddy2xEU`twS1_5&4%^W&*YZPrD3>|Eb|HN7AGee7iJj(}n_l!#tvCpouW zCN!nrtWQ4Z26rA=I7mM3XWD>xtgW+1iM752`Hrf5MC5`9Yx`g%A3g;R9?w>>GBdqz z5U}5|Co)P*hM~;z2ZSiei}qt*>E|-5DvR#OGDLa>v>**bZ4fD~al&7qVoGd+FjwsL zD9C;h??Twz>)kr_O{WsU zkH6%eQ221s%TT+&hd$GXy235PA$E+?EXb+G(8l zegjL&R)QyZTp+1$zxb9LZu+yOPm7EwS)(^P3^(-OMI2@G+q!_jVZWKeR0uDaQf)L> zKx9JQLG>xqv;|*t@=6GoP!wZ{C&`N&S;wiAjmdhMN)1x^>k;hYN=aNn z<`YTkI!N@03OL(LIHZzkwi_H4QAp*S^Ik_j}T$Cam(c%GWY9}V8piB*hzcR)Sx|Y4emuq^K1GXC5J7PGZ-q0La=t~8;l}Cm!6l}>lS0Eo)vK(AD(7gJKK!MXCMxn$)Jl1 z@g3LZ1gkj@=1}Jhg4X&3a~UN!>{t05Tp{*hL?R&l{(_*|#+1n)SD@7m^jLy<7X18n zTnOLPi@t?_f7_B43}2~_pFnkV)8Mlw-zLdNd`}Mj`6$2miDd15-xX^uSIUodcyNXV zoFEHqrapyHPpf* zh)M8CQc%*GTJLDQ>PG#ARt+N4>d#yH*{_b zk7lR{GxN}zR1N;2%_`Ry9cyD8sx3i{nf423U%DHeIPVx(4b169(r~o+>HD5Z1f^DK z)HClkT#&e7AURCt%Cm1T-#_}BLJ(vps!}+1#^URkoDieTVVtr1V~ko#^tq&bijD|$ zQWo<%w={0hRe-FjMqOD1@n(OjjDgq1^bdw0aoOK~SLW~^>LDm|p3MufQ$!VEO81*S zxuYuGd_!(MTdb@ou&@ET_x;BBoLPbvsVSPcNzIcY!XOegk(b!WfmvX~7fpz>a;fnd znLYVgTQpACVYa1DkE;Ob@Y#;QQkelWE#ZwdK~iB~WRsXoc1ago6(>I89qIuP4f(6^ zpQW0KVi`|?`VfNRe)orG%ATW3G+lbKV;n)id z(IGho3GCq{jZ1U`P$#-)Mls|{B-*N&0Gxt^%hEC z??#Z*NW{ui@}Qs+Wu;kUwfHH955IGQkWKTn?R$dBwvS1yrq8Yk#ZQF#sja@glu8j! z!QHNrsGks2vF%nMBS{qVH^#*%1GW6F5OE$hEX#hka@f|LdfD_QTMN}LJI?0Ek3HHy z&qZ3a?Jt?GaZ}Xuh`vKMMrBvZdcYETOblDX{gAl1E}yvhdEd4Lu3Vnv z9^|a;Z_Zfx-SjdWZda}}zWA+69(~!jSU>s#C7!4TPY>c6IYm(GWnNr7^&XG;&NUw7 zN`+80j^s*RBa1>LWArCK4&4NspMp1_h1m#+SS3zZA27OjxSLH_5t!a^J@@1&Pg#@{oC zhr4BdBpp7!eFIJ2ACmNi-ne%--S=;W#C4ikx23h~CrYkKI63FK7;AXDMNqFxskpTf~Lb`qBHex%tF+wyq+h} zU7laH?w4euG+b2G7^!y=*Z_ch6Am&E0RvJBL!sz~O=W2a8i}!%6jZvu5j7HQDLEhO zRkLT(8`JyYfpxv<)FXMN#W~CBM;3U+7=7Hy;QF`*?JN<$(6Ff8g&Vk5AK0>zZFpk8 zTQe&3XG3G4-zZ%sy=z$I-BA(wGcLKUouvnd`G=?}5o=CBXyQx=TLZQr&iE&_*BMf# z<313wno}lVH?F-gdxk~2wJ311XzWSybQjQp3jTY%g^5y6ZWPn?8RL%(Tk7>%$#>rVHd+A>n`5@QT!nr}%jMU5o3XhU*?T7AKfGBMshbCMvx zc48&WV(pb8)BYr1zDr^G;rH|Vb|!`NXidyz!-(sgBk3mQm>y{Wll_KN>R&*c`| zgX}yroW;y%H0Lj|!qQ$3U%7z%)*p*V{m_fgQ9!wI`}YFvJwcliJyh=G7Xwq}Sb8GO5dw8dvlncz9y%N zQs<}S$>oFEVwWHzj3=F@ZI0h5SBX1ylYhZ!70%C_ z9@(cZkT2!$3>gW`ztmkp_9;)n<;osb2H1^sut$pLyvZ_2LWA{51z>Y+HKje0DYeYZT7T@jQRQ;k^)e7a_X%GVD`ll)RH{cx_k;QIBb+i) zk|u9O3VX=$#hw4)-eNAs$r_}vs7{ftD=QA;kcfn`PeN+YQ53@0(e};-Ns&1rVT^VY zBQBN`E%xT7^ZDGF-;y?)HlN9h00}*ue4U(>o%Jc-4rvWdiv$KbLZvzlYr7H=y!#j= zY3bbjHB+cY=5m-A_C_`jOhseV+oiN&iyQY4S=Nn6OrUb5!=Z-OzcAUUwJ(+86ONi@ z6ov^S2#=4|tbv%ryC#bksfGV|qnvCQ$#yuv6c0mZ{m5rHMG(nCErPDQxISeYU4o>? zI&@DGCCGTt7Uv?mcidh0nSAaDP~I#ZAIB0UDgDJZ2u-|zmM?~li=rz1lN_yeYZ!T$ zHFvXaHPz>A{%R9&Y~3 zUmyW8n%B}g*{>J+JC}%H6GSkXO#NnA^!8eUnv^D^2VO$Y!a-@)b$2oT%Wrjz_wkbn zN{?`rxV}sGEZKF-hhi26D#z$pu zJ?R5uW^aMEB#+&0$||idrl-7xPv{-u2S)QDGcl5OpSsIO0?ck!T?hDM+|Gil$>t1{ zP*mt3DKY!;Col73iTF1U)+|ZZztS;0cHptueQ$kLZL;J`EC`!IuttlxCJ?m~mAmLt zqCdj*5x;-(7sn$t73TV;-BTXqyOH0eIbg;?^6D_tVel@-l~G7O97p`GEb_ zFZ}8`Ss8x}M;cepX3L5kRwBwlipRY=58yc|;|6R!QK^3$I@`e~L|nI0`|yW$Vuq3b zOwA1wmrsVs%l3zoXu2bqcbP7FyTP^Pb1 zQ7PDYW7dHxV@LGXKc-m(RkOMI*&kc2ZHU6e1!GojKR|AvKRDrupiP`WtR{JK;4K^? zq#!VD6Dmsy)Pi7ypMZt8XPI zbWqO?V)N%`p391l(5_p4PD!nr@!B|jz7AHEp+#GX8aC+f784PQ$Mz(~%8sOy-Ys6nHq_FSsl;iqtc@Xrqw>Q4py4Gbgs3}~{V1~S7zOk7rI2*OX8kEQit_)>lq;wiwx7&vMdo+A|Bo!kJ8eyv?6&pUjS6U%%QY4ANvsMwh!`uaEmd913hl zEbrmKZkz%RBPweI0`JSee8xm*&A+{^H6X3B(Vm+4aA0IKKq#$TG zcSU#38(=so(xI)Ix&MnPKpe4fYDBZ(=!w4eqvTFVhP2CRsY_{ zZp7kFrbCp}K}v}$M72a~<{TzTrmM#b=pQ^Q>cLBb3k3xYp!B`zoHY9JkCPX2Y4et@ zqx!9Pm^Hsuw^~Z|&+* zfeekwFXA4;Pmc$UB+QC?T%RwN;|MeJ1SaZSNyO6J6ify!joP}Hm9gAttKCf8{ zkCw*nLO!w<4SAb~x>6rI9SL$VDFe4Zy!zCtF3$aJe*)hYny~SCgW0QG*<($t=6U{7 z_}3++<`&3NJ1H6SgMtmV-z9l(SX`5)>VmU2&eeyLWvjDgJp?ozA3ZDMid9ihX_En? zlO7tuYHIJPXs^sj>ggZwOXv;NNuG|^ zz-uD>z=>|%AmG5ISXv9|f)CRLCP@H6TFU)1kw#OR*8Rtf6j44~l;pK;$74dJVxh>6zDQ;qHP)}#id_~$ z(E$VtC7jY1kP!x2*3+LgYH2YCZKE@=n^r_MCF)NS@Hqp{004hpAA++%DDbBM|3mZ5 zsc__egQ#rPSNsQZ;ivy$Zd7g{PE#oZP!J9khHZp)4F>=`G_9T^(7x`=cUj@VXvbcJ z@OZfF6x|u-mF|NuBpn@Un#T;15A?rsqT=BTJX{qX_q)$$m&|O_D@Y}#H2YPS?*M7Z zY!)0PR1DyCTVad68Im@+&S@M`rH)l>Hs)jFI7_douj-B=7dVqL?^wd(9CYdK3HP+1 zn&l#NO^h0wa+L|A;0;I>OKdpuw|GJQon(P*cOQis(H-FqjiHvj6B9>5yFk}<5B8S0K-J{~84`D)8*S|muzVW38(pz;C zus3zWohzTzuo}%>YGA&_cIq<+{*5Gm`@5LE`sCC@{vyxeZzbjselLMvZ9pF4=5?VQ zD{1$D7<{oQOi^z&B>vJ^_+oiZ{HiOzydVHHRpH<{0R#X%Bml}Qh!$|t)pT9=`d!eY zK>cqKgrRDzKr*-^==d!H6*<_9HLm-$Z~hgP;uZV9f&tBE1#**5HaRJm1_A&&TZsQg zu>M29h90s487TgXYy$u||MkAsb|azeY(P3_EE|vvzCRXP$_8YBs{PZL`vSFN1JXlB z|7mO`K(k=*{7`asAmKmZH5`ops8}&T-?0Okp(Y$aG6cvMc<3h%AO*Db4gn3?$o>kd zPl1PK!{CLXXdFNsD9=A2A#VgIbteK5RP7bS@c#$k{v*BJitxX~aZny^AUWm#brYlk z0I%MH|EHJ?B0%*xfi#r=zh&|t4(wGm|;<3-0W; z-CcXV&$DX3tE&24s}!Wcz|jDZ0B8UJKn#FNdbZI80RRNR0RU70G^q9`dpj3XI~RQw zPX|+HU3w2&8=^dLP^ugN===5mJ^l~(K!Nh0TsI?98|futLIsuMFbMHXEmSJ$jRJpX zHD#G->%g|1|2U^5sa8tjXXyfyU#Xts=_781W9&!@JlfTcOh(umhspKBiN5q}SK_IL zXlfBlnpBGf@uP(e97(-vU}hop8r7=NuMy749~nX6_JLCcPQcF+6^4Ut@%lzEzx-Ie zHl`M#+4qU1sLf6)*6X9VfWb%|_VJO4vWy&OPx@nhsU6;~BUEaoX7% zJr{U^1OVRNzyJ#W5|u?7U3KK|QK<(80KmORrM{D?jWYxNAMgK-#{a`1_)o5vCJp}) zjiUDwzk+vtw+nbf6q4+al-!|15wT7rjgSRQIH+iG7mJb7MrYLZpLdYM=4ct`u_>gt#2RY6I`85Q}ONcQ$6x{JIj zex~AKMPk{XEX?SG085U|MJL#Ss-;Tx^keAKN6?F5T@*{GRHrz#^WP z9Pz3i3XFS*S}r#N#2R5l&w;v1_IpSDdsNAVH)sP<0Dvzp0KoePz|GRx(w@P@-q_Xl zea8D^syoqLbzWvi@$X1~CS;w4x+O4@ewE>N?~bs0G)wp;8i8l3VF` zbpr{~K!QuO@|Ik6W}a4AS*OS71l6F$*a61rV<_$m6=;6x3158`t5`<9Bufvwd@*!S z8P{t)ar5(ex$nSlg#%%hq9_UdeEq?pchS&2Yk!!Koug(@hc?K@7+G$LOxoCs9E|LwY3DfY7oH z0X+-#X}ArxOpl>^dZbJNV&3zUU`@1W)+g2KG280a4`(3Y=y(yDm62E4m*j-|amc(Y z^n_XARSpDMA&NV^yt<*v2&;i4^7$zfy={K3m6k93SpC52g0s{1l|_Mt+APVQ%QT&y z1@y(^Q+x=bI5HEYb#apA5E}kI5Y`7v9J#pd4SJO6`IlhXy+feu*Bzt0qpxje_%=(3 zQct`ZuuJEu=WpIHeST_x-m!9g|NPAls??1WQOSZIASvVTZB0n159OE`2xIU1)bee0EcI8* z^kSHQF138#1_5XN-IEea8Kec-sV6TcQNrs%X3P#2^-Ze`H_F0krfIioDrHpA#Q9J$ zo|`AQ3^^lN7)c~|TDY!hHfL?;mBU2)C+@pah)zQ_xK89_kk&w;M`A(nF0)tim8w1Z zRn@^gNo)3gWc}pvz1x%b-Z=j( zSjpAAUHph=##9%9BIyJzNa-|#p3B~p*%sMg4cD+)jc${wp06wX^eNRC=sYkuTAdPU zqS`N?MY#5K%S`s4wCs4Z{R)N4P!c4TS4dD?d1t9zuO=iH^}uDKp|`b7qNm9cE6i$S zb_iFR)sYB~k=zv@vQy$~d=PASVMsvox#X21s=ab)Jht7lEfJ$dE%TTC;fH2shZ z3=-*2A;aV2pruRWT#|PfT_1j&i+l}N$Q43cW9fz!Z|nbYBSo+hb6tQ1i`(L2Lwrpo zWB3@XI$Nedy*z!pKe#jf?C^D5SBIU*A~ zaexwvZyYiSQNB^~s*}KrA2Ih zsp7NwXy3yOY9CD54mYLHJVO7-+|>NMD~lu#EZX^T>59{LAmrR*NHAMDH0hJf2-~46 z2R!@bqBA@hzrqY`;X^j^!j$1}5^-sK#D4Nc@@2o88XGE3<)Aio^@-0~3i#_pckxFomglLC#}E=?a_ znodapv~>z-5+Q`A!cBfhFNk0K$gu zQ>kzM1_y~&S01(;St9!yqb7fl5XKqJ_e`ibk~XBMmM%5w)9z6kltXCu_$H^I20DRj zV=&!IeIek=QbnWh3}PeVchQc5ufC< zJYjisXT9rL9Lb)^Ff^Hd+0aCXqX}b)HY5r4v=~lkNPb}=Rc$622JF+Aeay$;t<>(d zJmiOE(SRh7&YOL@Dr-*IuI zn+okTfY`=f1oR11gUtU5ayJeSH;CKg?x5VnWWqn9a5qhf&;0>j%_m4a+~qM zcGSzu-L-(SgToN>p(ha8QL-0-w8o?VNG}_T?NID!>9rcd_f;Bi&ysauY-=|X8#_3h zEYm1Z{3kR&6I=zWh`jksdgKmn1bIW#g?7%_@)U1rVDV1^*Ge3mE?jX4pa#^{Iz<|z z7wq!VfxV_SJs4GENkBd{o_Vya1qi#pF}v`0;&L5L!39ylOuSa>w^ zVGf$~q1g;XXmMBQk!6$WG@t>3LT>iK(-0l17uPrR1^ARNEqKKio5Jhyn_qmopoHKF zloFndazR#orJuHIjM&;IZ|V?6=#&MiGRsg?^rfE47N*~ELjV>fpJ3CrJbOa4S7yRh zZp+Hltk+=hfpr^b!eLg)_M=9Cf5#s@HK*~iFALdAUyuM9xm44r`n`G|=G9cg9d9m~RT(xoLB zt*Y5PuokXx91i)^5Bb!AbI=-A*%p7P44vFK2OeumAoxoh>b0@*Kwg;Eu>@@2Ec&Ad zsTr1S$dPAPFJ$L^aBe z!wY#ain3yFFFT!4IC57bkWr!JY=g98c@u+e>v~|6x~ng2i>s^eI|Q7<_2i9<6zzHy zu<A-JmfnHCXsFDE2zgKCjnlvRDk*m;;`oH6KX5n!&pwaD`}JB-mtl=( zVbRhQf^|5QvMt5?a!yv;CpgvyVRiLnD%KhJdWS_wxfAyg5a6PhW*$CSlbm+(rI(q< z>xz$AD&ZI+ReE2XwV6MQ3G+-hZXNcg7NcpNb*U2WZ_G63uKA40sDMDY=g?o z&Q~5nX~ziPD7fpI*Z&c@w2+HaA)Z{j1*@jrW@07k$PC-)!G5#gV~l|*Rdf76N013y z7IWAOSgH86%dS&QB06}6lt^`Rc1w=41jZ+*AU=sCggs4dC{3&$Q)}&4BnoAbtznPE zu;EsS0-JnHwQ0wF#*B&mcv}@LaI#CR;YP^rM)}~~6=Q71^Xw4n{XhVO`-9y!KA+c19Q%lqVa8~_ef1)W- zB+XrPGh=mv*ETi^=S$;VV)7UnpgE;l0ktxmhAPt)v**^kK*=~~nly5a`NK{j zeA%?wZ40|pLKsEyB)t7_;G-9t@(1A{xL*RYruVzOk;`oSCOAyIxTXaI*N1`Kig0y%@oN>6YHoIBYZhn{-7s&a?SB| z1zJ)d-sG_^tb|S&*JF0r2B_@Hs$H498H%BiNHG9kbL0v5CYl)xkSfgtC?vxEtdqyA z#e>eo8>On0$Z`=XRHFhHt{cj~8YeQ<>|07s(@(E(D4ZGE&UAHfPcCAN^PsM=5U`u^{HHoYJsOp;X#zv1vjtHi(Of{;)dq2vhbZx6)0Gt z)V+gTl|{SfcjgaiW9&-4PTj878A{o0MoT#3dXDqwcKPW7;k;ZGMJ}x!wgcCTAHz<9 zE4I+p^M_wU(=|QkFNmI~FEc(*y`gR$YgWxoJb%i&m{@0S?CiggIhkWSc)An)1AQCWJszFL)|v$*VeF$Eh{ zV6$)zjKdbs$6$YPKK%kzDJNusrhEwn4%7kLV@GlxtOwy0B1?4CYvETRe_$yfbiSVo zuDFV3v(I`5^Xu$heL;jI_~{!n3FSiH$Rs!rXq&6&=v?X4mVg<4#tOO{HrL@!LNXXr z@09>hdFc&PQ|VVL7kGjgGUKJM5Mmajj@Jt>?VYerfRAq?kW|8XUsZv8-pB^qV$*stnY7pWu%%{~ zNyGS*HM}Wt_G{?Ms8xS{7sGeEQdEXpMZtU})I^J|aPBluYyr7cy|$U-Zs3Q$_Lb#w zbl7^QD(n7T9ZniwwM%$^Jn%Nm2&_~{mB}H9d+soqTOJiGh-7=DEvnw*Ddj$f_VefxlDNsXUDlN|u+vmArvsm1G|w|cv&qfdtIAW^MA7}dAoXl9a$E?^|!xuwupIA z)>Smt@>PtCC0?Oxzj>Ozrk1_6GW!qD`Rk>3m>vr}rYtto2>W%aZx6*iZL04`kU;TP zjP5ubbg-ptTcvb=T;BZR3v4SxT_=Ll_w|H^xK1eny1<^NJ@oiG9pE!QSmbz(;+{&N zp-vej8K|}|wfQ*X2clIErLAlpU%AkS_`MMaVyZrRLRhBlww&rbd4R4@()J+HU_j=c zA2sMmh=9w%-bU>ZwRk7+=}-i#ewd91%5536!)<)xp(&J|&e}8@Gy1tUMs93;VJLW# zzV*x{r-npDCP1#$`rUH+sr}&i4fbyZuek2?lg9TJxOXX;=ug4R!O+~)nc;sV<$s+2 zp?v*ATh8@2JyC|`;JO~tz`09T^02} z(LQLj+bPa2Z#eCjtxB2)Io)oCz?n83)d|AC^*UBTaA^F!CG_2E{6D?^AAkQ@?E8ny z1@Yrn-Hh=QiDc@c4e(3 zk=fK-hm8tzg9+A7vcuelj<3_QAsOxdeO}`Mj=ikU2DxY5kRzK~Gryv~SZEm`z>6{bUKv}}}+x;%?x?QeUcEnm*= z_PrNi-;;ajfA+-x-2XrCe6*5o+!7;_PsPmJ28Tcb(u8isXNRLeg#@Hb==)n0Z1W@y z*OM7`hnx9%H(|+;@}vB=8kQCoo_2o*F7ALSnMf>gAwp6ll|q>vrnQVx_wdP=o$I1Y z(m^58C=BktP^MWbcs9BmGHxntRi)t(aFx{D5oR%BUdCbU4^NXsv@ocRXZSx$>@))_ zThmp@nYiO=?5WHW&!mz15^wB3L1bd_e5cp=&bKU~9wHq55dvohRM0(FuXOyF7%nO~jnU-<4uN~b1*$^YF1i-2_0Y!0k zLQ}IHN(2eF3~Yp>{1uBh2KPi?cpfl0Bq4#qp&`Xbn)2>z`@lKRM9E~@=L zNE?6)O_R#p0)h!5Wo?;km+a@ctjfbitU4r!Wu_!(CEbrLbZLpND}ueJuB?ED<8H4u zn{f}Zu{(70mp<0C}lJFUCVrwGa95T zhAxTrB#QxdEvtbKCU8nZMjV#q{kAxB5Z+gBO^bc)ZTU&mc6U|TG^6gU(Kh|C1^LJ5M@zDAaiGYQvz{P%&J)0@%;y%Lmv;lo*`738fbEUMiqQ2Mwm%5DKmbLp6L!P z!N-Z0+q5xR#^8M_AR{nc*iMR>aD+)O%SsDlY?EYQMii}(>XUM5m_=^xAuDX|9U8L5 z=nR6gHn<|_mACwKUCSg21-N_UlxP48`u7^pytuA@2+P!fgYwia&d#0%8v;6(Ft@LC z!AMUA>0R)J@<=W`4c7)RHCq)|KhMF2A%`AJC5x+=b9BUDT6-Conv6h9v3ebEZQ-*W zAIp}x!W~O>o;N6@+5x*`P|a4JA0|Q){WT(TOTwtpR>e_y8O~6o`MWIG`yXwKHC!Tu z%b6m7!25RQ_|2<=2vmG+C@-;7Wa}@VKP9ih+N-~qL)^B|bSd>~>fB|BGr;f-SGTB! ziGbfm`tFb_qruX#s2|Mel771#7hD4D-Cz6RKl0jktV=++3So&x_JG$*RTn2G_YiiF zxdM*Wom`5;d<3a4QFKm%K!L1@6-@Sk)7>iitwIF(P7Q|`Zv*9+zw6p4bRuGJ(jsYO zqSc!Fd8UQEnUiPkjZDBu|Da9#nOQgv#&_c?U9oHlmS!2hNJu#Kmlc(rp#&j#GcyB` zmSE<)=hK*0u64{6$oL_6>E|PCd6gOE>tmA*G4n|VO@~kGaf%NU^1)p`O9Rlp%6! z+H1yopv6>W5`W`59Fa7SJtLc!;C7~+T0Y8ZFJqavir*_OW2OAYgI z=mF=ENQ{U`jPMWZ30XCvZAp2tdJ4clnYRjhk;o={h9LoKPQE{qGSN~iH2p|CGT!O; z0U41!HFb5w&$iGZshm)m_-cn9O#f$V(ojo^(mnpzJzkCERf%Yk&xv*t!Sz};7CPKy zITAyMmdAbjkrB->zc*D~ukzhbIERn6mzqfYBTB_(rp2rINdoXf`XjX;WcFk^_DcXo zlPsu>uRUQ z_~s#(i-BzIq?|y1iXkhex=0J8pO=kTyVY@HuJ?0~ZYijA`%j~phehO&*)|F&nS0vl zTH4h+mthj9sGUmt?$6lu*30E34Ytg`Fzip_VixF`8b0aCSdL|{)qihQdnGO9Z_1wl z8@H;6=Na1LaNda}aZkSTBQ-2Qz|ec^u}E89JvCw>vd1EiGdCMKma!Z7CCP^Namz)H#8 zOfsfk?|FF05RTiC&Q>4K)~=nM9+gtYSR{JE)M@1<$E~-&}JgfZ?XuMt)k3Ktu?&NO8gxQ&t`4@>FFFsVJb`||>?H9lfOBu&&SiY#3uY~A{wgC`EQr|v z@{X|-q#aUUKL5i|zy30jF(JrfqVL3TR3H|-gjwp7747jrKO=d(`s z2~$j26Bdc!Plh*ouzjxT*B|6Dc0rsbaC%i2$u7sX$o6Z`swXXYqaC05%|6=cu{s#5 zri6AE=0Ghber*3J1oLMmaOb!5xi;gr&d7<;0iD>{s`BdOlg$n!ysRRET7veRGzcA824jk7uix)h7 z{(SuluBKGgTWQBSlhq|uu$h#kUp4`vcK^|#cueE!*j_OZ)DzSJ@Ph|M5gA*24KmN| zR-82j4n(5?#0RnRjKEX^#j~(A(#zI?i_G1uf?0dtt;FM_K(3h9W4DwEN}aP!5POzJ znm}HGs(-`M+>ks~Nebfe`sXnR!jbq_7#$wSOb`c+s{ja2z^UK@pmd##)-ZuV8m`2{F`vd7&pSmRAQgk1OTT^2$?pJ?RO1KZZR0@;ck5 z%EvD-#6s@Gw3il(u-+y+cf4In-vcK*_4<&b>ydqs302$A=4!Q{&*=gf04W`J*;^{6 zyx-J6UPK-e2f3D-9VSwUA093|}b?3)`Ao&hrS zdPP0hhZbUQ{&1)G6}2pGv%q%^$sXjUx4ZcMVOiqa%Y^Z#1{lFx-zOKcA@6ap8%t=< zIDd(3T|OV-<-kPAc|-ofJTZtXluGRWPwYK0wTRi>o+goCejhgXgQUwn>5qp`=9kRd zvb^cDZkmb+wRtvB+@E@_<)K#c2k0DJ(H-K{F|y3xHg4~Me3NV|EHHH7<;w}?H=KD0 zUE9`t*)y^hhd|2fRNM+J&74t6Rp}60%$zr>7L)?UY?ZPgE_=4<=0!%>fH!>(wro~G zwYG->Lz8WY>6rOIIUiv?;xW)D!3V!Od>xjE)zlWZKV0p;%ZOtBCb^;Wgq3z|e zK}vr}pQ*UAVS;RU_EaXwK1#gtwg)m?_sebn4EcpVNKMymRLlfX_Ok#MJW0@$1eA$b zc^cBT#{%ZW7)s+-Cv*NLXu=(ybRA)M!fXqH({TAGPxB9J{oy41iqb`|=a-V6`;ZT0 zQ2c{|@=nD3gSWBrD>}fDJsdGl^KJ>yl*u4XY}>rnj?>`LSDd0L6aH@eWT}nEZa%7|RS6?oD!Oi|e$bC78egVCmWFM9)Uz>f`O_{+1 zNf`MU>m{&}G-`VsjkDrY6gw!yz@I*D@In@Rnj!oJ-@_pSBNDyEDk52CjTB)U%9N0dqp%5b|4Mwek$n zP$6o*`~bUFqf@NgOH?hvHJ1tgIDl;)p2~6%_2Lj_s$!5K-Pv~MOUP@kVZHWNbFiPf zd{1ufv7TvTr){3h*64~9Z9k@c<6)keyU+A(8=m8=pb9LBzCY82xjwl#dIzK|I8zZw zAj7ftlbnXMSADq_ND6VVh0P zlk9zE015tOX5L|?kC}GjYlU43+1G>9TKHNRz8>9s5rf+=M$8$TyY-#p zYTY+_WT<7IVE1@(`Hj!l*ERhKAitLW^jPTs`hf29=-O$0;G1QJr8(O!|79ivN!AOU z+n?|Xc8m~eL0yW1t_f^ChlK3K_p5#oLIRNSe(8``7{Q&Of8iCQvoLP++*D%CnBP>q zkddS6R6n3aart%mi||T<{H$yl*sabt@gxM_5=SI-SN-}@`6fr7=LOG`ZZt$+*zt5G za^E8`iS?o*3g3ZxfjXP#Yuoyl*#WdQ=|>f=!k2^fi=S|fYn~e&JJeU*)_{J+2kk?L zN4QKLGWE-f>9!8GIrI79HqOqfZddTwZ;qXi9@&b!*`E}Z1jDe&a1CDnUQ)Wo&+LnT z7g9Lh>oJi4&>(-#wT2E3f2{Z3i!wppOQheg_`iK*Dg$Gu|9^?_dD${LkqVcdkh%q8 z>5tmQH7N5bEzbH~rpeR_^TY`X=)H-Dn-XKu?$F`L^2&dGS2eT~-|&?!uU zmo-M<2fwZn&E8!#yB6BA7SlIwi{ubuqB(2+)sg4Q*e1ZOmNpPpS6mtMf!T_j@_|~+ z9Kl3SS3olfG?W5Ud{~o;i*mLGB-~qV27yzUyeP~|zG+?b$6GbU1Ndq-)X&q_F>!@sfDXumOd`i)Q5zTSMBSpsnk z#6}3<3K0BMYd~Ju0%$dH_621ncGlWs%clZ}lXbGTc3i7xvVE404k;x20o|!L)W-s^ zc!E6GD;V?ht4ef|PI2NnyqapGAk0UtpdYNX(Bfi~u!Eo1bapy5$9V?UrBX{dn^2EW z7N}Zs-{|~iXhWal3)}RPTRM@SB?Bv?(WP2YFUUm}*SUc*{Ygdc%?op-v;O(x$ z|9V8y?_jZLXD;OGvn}TF4CSM><)6pH=SxhGu+zvXzQ(K;uBrz&vwJ#}K~8Wx0*~r~ zU)RN!Sd6JP;o@>|G)Kl_JC5xgd%c9+WTTV8Bxis5OSy~TZ%NuRHsw?BPEyf#k|O^L zN$s6X|4Y$7848F_1llb#!iSxM-F*l+bxfDM@nFDnZ_nPNq2J#IW#pj?E!kqcn<(~D53YbT6RaTXDqn^N3q}uYLc^ z^yGa9%D-2_|8D<#D)BG-RqX$@|IdlV-_d?g(fx%sjq|@~{~=-bJJjzvuD_sGasL%UeCzXSa1xc$!n3C#Zt@c&0~1!;(P-T?sc P?+=`J>e{gV@$LTsEW8+O literal 0 HcmV?d00001 diff --git a/Source/HBIOS/Config/mk4_dsd.asm b/Source/HBIOS/Config/mk4_dsd.asm index 4b0f4f2b..71a35c91 100644 --- a/Source/HBIOS/Config/mk4_dsd.asm +++ b/Source/HBIOS/Config/mk4_dsd.asm @@ -50,38 +50,32 @@ IDEMODE .EQU IDEMODE_MK4 ; IDEMODE_DIO, IDEMODE_DIDE, IDEMODE_MK4 IDECNT .EQU 1 ; NUMBER OF IDE UNITS IDETRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) IDE8BIT .EQU TRUE ; USE IDE 8BIT TRANSFERS (PROBABLY ONLY WORKS FOR CF CARDS!) -IDECAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; PPIDEENABLE .EQU FALSE ; TRUE FOR PPIDE SUPPORT (DO NOT COMBINE WITH DSKYENABLE) PPIDEIOB .EQU $60 ; PPIDE IOBASE PPIDECNT .EQU 1 ; NUMBER OF PPIDE UNITS PPIDETRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPIDEENABLE = TRUE) PPIDE8BIT .EQU FALSE ; USE IDE 8BIT TRANSFERS (PROBABLY ONLY WORKS FOR CF CARDS!) -PPIDECAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) PPIDESLOW .EQU FALSE ; ADD DELAYS TO HELP PROBLEMATIC HARDWARE (TRY THIS IF PPIDE IS UNRELIABLE) ; SDENABLE .EQU TRUE ; TRUE FOR SD SUPPORT SDMODE .EQU SDMODE_DSD ; SDMODE_JUHA, SDMODE_CSIO, SDMODE_UART, SDMODE_PPI, SDMODE_DSD, SDMODE_MK4 SDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) -SDCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) SDCSIOFAST .EQU TRUE ; TABLE-DRIVEN BIT INVERTER ; PRPENABLE .EQU FALSE ; TRUE FOR PROPIO SD SUPPORT (FOR N8VEM PROPIO ONLY!) PRPIOB .EQU $A8 ; PORT IO ADDRESS BASE PRPSDENABLE .EQU TRUE ; TRUE FOR PROPIO SD SUPPORT (FOR N8VEM PROPIO ONLY!) PRPSDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PRPSDENABLE = TRUE) -PRPSDCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) PRPCONENABLE .EQU TRUE ; TRUE FOR PROPIO CONSOLE SUPPORT (PS/2 KBD & VGA VIDEO) ; PPPENABLE .EQU FALSE ; TRUE FOR PARPORTPROP SUPPORT PPPSDENABLE .EQU TRUE ; TRUE FOR PARPORTPROP SD SUPPORT PPPSDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPPENABLE = TRUE) -PPPSDCAPACITY .EQU 64 ; CAPACITY OF PPP SD DEVICE (IN MB) PPPCONENABLE .EQU TRUE ; TRUE FOR PROPIO CONSOLE SUPPORT (PS/2 KBD & VGA VIDEO) ; HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) -HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD PPKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPKENABLE = TRUE) diff --git a/Source/HBIOS/Config/mk4_std.asm b/Source/HBIOS/Config/mk4_std.asm index 4cb4cf3c..49ceab34 100644 --- a/Source/HBIOS/Config/mk4_std.asm +++ b/Source/HBIOS/Config/mk4_std.asm @@ -47,41 +47,35 @@ RFCNT .EQU 1 ; NUMBER OF RAM FLOPPY UNITS ; IDEENABLE .EQU TRUE ; TRUE FOR IDE SUPPORT IDEMODE .EQU IDEMODE_MK4 ; IDEMODE_DIO, IDEMODE_DIDE, IDEMODE_MK4 -IDECNT .EQU 1 ; NUMBER OF IDE UNITS -IDETRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) +IDECNT .EQU 2 ; NUMBER OF IDE UNITS +IDETRACE .EQU 2 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) IDE8BIT .EQU TRUE ; USE IDE 8BIT TRANSFERS (PROBABLY ONLY WORKS FOR CF CARDS!) -IDECAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; PPIDEENABLE .EQU FALSE ; TRUE FOR PPIDE SUPPORT (DO NOT COMBINE WITH DSKYENABLE) PPIDEIOB .EQU $60 ; PPIDE IOBASE PPIDECNT .EQU 1 ; NUMBER OF PPIDE UNITS PPIDETRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPIDEENABLE = TRUE) PPIDE8BIT .EQU FALSE ; USE IDE 8BIT TRANSFERS (PROBABLY ONLY WORKS FOR CF CARDS!) -PPIDECAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) PPIDESLOW .EQU FALSE ; ADD DELAYS TO HELP PROBLEMATIC HARDWARE (TRY THIS IF PPIDE IS UNRELIABLE) ; SDENABLE .EQU TRUE ; TRUE FOR SD SUPPORT SDMODE .EQU SDMODE_MK4 ; SDMODE_JUHA, SDMODE_CSIO, SDMODE_UART, SDMODE_PPI, SDMODE_DSD, SDMODE_MK4 -SDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) -SDCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) +SDTRACE .EQU 2 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) SDCSIOFAST .EQU TRUE ; TABLE-DRIVEN BIT INVERTER ; PRPENABLE .EQU FALSE ; TRUE FOR PROPIO SD SUPPORT (FOR N8VEM PROPIO ONLY!) PRPIOB .EQU $A8 ; PORT IO ADDRESS BASE PRPSDENABLE .EQU TRUE ; TRUE FOR PROPIO SD SUPPORT (FOR N8VEM PROPIO ONLY!) PRPSDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PRPSDENABLE = TRUE) -PRPSDCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) PRPCONENABLE .EQU TRUE ; TRUE FOR PROPIO CONSOLE SUPPORT (PS/2 KBD & VGA VIDEO) ; PPPENABLE .EQU FALSE ; TRUE FOR PARPORTPROP SUPPORT PPPSDENABLE .EQU TRUE ; TRUE FOR PARPORTPROP SD SUPPORT PPPSDTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPPENABLE = TRUE) -PPPSDCAPACITY .EQU 64 ; CAPACITY OF PPP SD DEVICE (IN MB) PPPCONENABLE .EQU TRUE ; TRUE FOR PROPIO CONSOLE SUPPORT (PS/2 KBD & VGA VIDEO) ; HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) -HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD PPKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF PPKENABLE = TRUE) diff --git a/Source/HBIOS/hdsk.asm b/Source/HBIOS/hdsk.asm index ce70779f..a5fa3efe 100644 --- a/Source/HBIOS/hdsk.asm +++ b/Source/HBIOS/hdsk.asm @@ -79,7 +79,7 @@ HDSK_CAP: XOR A ; SIGNAL SUCCESS RET ; -SD_GEOM: +HDSK_GEOM: ; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS ; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE CALL SD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC diff --git a/Source/HBIOS/ide.asm b/Source/HBIOS/ide.asm index f43eb982..4c7ec737 100644 --- a/Source/HBIOS/ide.asm +++ b/Source/HBIOS/ide.asm @@ -1,72 +1,205 @@ ; -;================================================================================================== +;============================================================================= ; IDE DISK DRIVER -;================================================================================================== -; -#IF (IDETRACE >= 2) +;============================================================================= +; +; TODO: +; - IMPLEMENT IDE_INITDEVICE +; +; +-----------------------------------------------------------------------+ +; | CONTROL BLOCK REGISTERS | +; +-----------------------+-------+-------+-------------------------------+ +; | REGISTER | PORT | DIR | DESCRIPTION | +; +-----------------------+-------+-------+-------------------------------+ +; | IDE_IO_ALTSTAT | 0x0E | R | ALTERNATE STATUS REGISTER | +; | IDE_IO_CTRL | 0x0E | W | DEVICE CONTROL REGISTER | +; | IDE_IO_DRVADR | 0x0F | R | DRIVE ADDRESS REGISTER | +; +-----------------------+-------+-------+-------------------------------+ +; +; +-----------------------+-------+-------+-------------------------------+ +; | COMMAND BLOCK REGISTERS | +; +-----------------------+-------+-------+-------------------------------+ +; | REGISTER | PORT | DIR | DESCRIPTION | +; +-----------------------+-------+-------+-------------------------------+ +; | IDE_IO_DATA | 0x00 | R/W | DATA INPUT/OUTPUT | +; | IDE_IO_ERR | 0x01 | R | ERROR REGISTER | +; | IDE_IO_FEAT | 0x01 | W | FEATURES REGISTER | +; | IDE_IO_COUNT | 0x02 | R/W | SECTOR COUNT REGISTER | +; | IDE_IO_SECT | 0x03 | R/W | SECTOR NUMBER REGISTER | +; | IDE_IO_CYLLO | 0x04 | R/W | CYLINDER NUM REGISTER (LSB) | +; | IDE_IO_CYLHI | 0x05 | R/W | CYLINDER NUM REGISTER (MSB) | +; | IDE_IO_DRVHD | 0x06 | R/W | DRIVE/HEAD REGISTER | +; | IDE_IO_LBA0* | 0x03 | R/W | LBA BYTE 0 (BITS 0-7) | +; | IDE_IO_LBA1* | 0x04 | R/W | LBA BYTE 1 (BITS 8-15) | +; | IDE_IO_LBA2* | 0x05 | R/W | LBA BYTE 2 (BITS 16-23) | +; | IDE_IO_LBA3* | 0x06 | R/W | LBA BYTE 3 (BITS 24-27) | +; | IDE_IO_STAT | 0x07 | R | STATUS REGISTER | +; | IDE_IO_CMD | 0x07 | W | COMMAND REGISTER (EXECUTE) | +; +-----------------------+-------+-------+-------------------------------+ +; * LBA0-4 ARE ALTERNATE DEFINITIONS OF SECT, CYL, AND DRVHD PORTS +; +; === STATUS REGISTER === +; +; 7 6 5 4 3 2 1 0 +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; | BSY | DRDY | DWF | DSC | DRQ | CORR | IDX | ERR | +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; +; BSY: BUSY +; DRDY: DRIVE READY +; DWF: DRIVE WRITE FAULT +; DSC: DRIVE SEEK COMPLETE +; DRQ: DATA REQUEST +; CORR: CORRECTED DATA +; IDX: INDEX +; ERR: ERROR +; +; === ERROR REGISTER === +; +; 7 6 5 4 3 2 1 0 +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; | BBK | UNC | MC | IDNF | MCR | ABRT | TK0NF | AMNF | +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; (VALID WHEN ERR BIT IS SET IN STATUS REGISTER) +; +; BBK: BAD BLOCK DETECTED +; UNC: UNCORRECTABLE DATA ERROR +; MC: MEDIA CHANGED +; IDNF: ID NOT FOUND +; MCR: MEDIA CHANGE REQUESTED +; ABRT: ABORTED COMMAND +; TK0NF: TRACK 0 NOT FOUND +; AMNF: ADDRESS MARK NOT FOUND +; +; === DRIVE/HEAD / LBA3 REGISTER === +; +; 7 6 5 4 3 2 1 0 +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; | 1 | L | 1 | DRV | HS3 | HS2 | HS1 | HS0 | +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; +; L: 0 = CHS ADDRESSING, 1 = LBA ADDRESSING +; DRV: 0 = DRIVE 0 (PRIMARY) SELECTED, 1 = DRIVE 1 (SLAVE) SELECTED +; HS: CHS = HEAD ADDRESS (0-15), LBA = BITS 24-27 OF LBA +; +; === DEVICE CONTROL REGISTER === +; +; 7 6 5 4 3 2 1 0 +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; | X | X | X | X | 1 | SRST | ~IEN | 0 | +; +-------+-------+-------+-------+-------+-------+-------+-------+ +; +; SRST: SOFTWARE RESET +; ~IEN: INTERRUPT ENABLE +; +#IF (IDETRACE >= 3) #DEFINE DCALL CALL #ELSE #DEFINE DCALL \; #ENDIF ; -; IO PORT ADDRESSES +; UNIT MAPPING IS AS FOLLOWS: +; IDE0: PRIMARY MASTER +; IDE1: PRIMARY SLAVE +; IDE2: SECONDARY MASTER +; IDE3: SECONDARY SLAVE +; +IDE_UNITCNT .EQU 2 ; ASSUME ONLY PRIMARY INTERFACE ; #IF (IDEMODE == IDEMODE_MK4) -IDEBASE .EQU MK4_IDE +IDE_IO_BASE .EQU MK4_IDE #ELSE -IDEBASE .EQU $20 +IDE_IO_BASE .EQU $20 #ENDIF #IF ((IDEMODE == IDEMODE_DIO) | (IDEMODE == IDEMODE_MK4)) #IF (IDE8BIT) -IDEDATA .EQU $IDEBASE + $00 ; DATA PORT (8 BIT) +IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA PORT (8 BIT PIO) (R/W) #ELSE -IDEDATALO .EQU $IDEBASE + $00 ; DATA PORT (16 BIT LO BYTE) -IDEDATAHI .EQU $IDEBASE + $08 ; DATA PORT (16 BIT HI BYTE) -IDEDATA .EQU IDEDATALO +IDE_IO_DATALO .EQU $IDE_IO_BASE + $00 ; DATA PORT (16 BIT PIO LO BYTE) (R/W) +IDE_IO_DATAHI .EQU $IDE_IO_BASE + $08 ; DATA PORT (16 BIT PIO HI BYTE) (R/W) +IDE_IO_DATA .EQU IDE_IO_DATALO #ENDIF #ENDIF ; #IF (IDEMODE == IDEMODE_DIDE) +IDE_UNITCNT .SET 4 ; DIDE HAS PRIMARY AND SECONDARY INTERACES #IF (IDE8BIT) -IDEDATA .EQU $IDEBASE + $00 ; DATA PORT (8 BIT OR 16 BIT PIO LO/HI BYTES) +IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA PORT (8 BIT PIO) (R/W) #ELSE -IDEDATA .EQU $IDEBASE + $08 ; DATA PORT (16 BIT PIO LO/HI BYTES) -IDEDMA .EQU $IDEBASE + $09 ; DATA PORT (16 BIT DMA LO/HI BYTES) +IDE_IO_DATA .EQU $IDE_IO_BASE + $08 ; DATA PORT (16 BIT PIO LO/HI BYTES) (R/W) +IDE_IO_DMA .EQU $IDE_IO_BASE + $09 ; DATA PORT (16 BIT DMA LO/HI BYTES) (R/W) #ENDIF #ENDIF ; -IDEERR .EQU $IDEBASE + $01 ; READ: ERROR REGISTER; WRITE: PRECOMP -IDESECTC .EQU $IDEBASE + $02 ; SECTOR COUNT -IDESECTN .EQU $IDEBASE + $03 ; SECTOR NUMBER -IDECYLLO .EQU $IDEBASE + $04 ; CYLINDER LOW -IDECYLHI .EQU $IDEBASE + $05 ; CYLINDER HIGH -IDEDEVICE .EQU $IDEBASE + $06 ; DRIVE/HEAD -IDESTTS .EQU $IDEBASE + $07 ; READ: STATUS; WRITE: COMMAND -IDECTRL .EQU $IDEBASE + $0E ; READ: ALTERNATIVE STATUS; WRITE; DEVICE CONTROL -IDEADDR .EQU $IDEBASE + $0F ; DRIVE ADDRESS (READ ONLY) +;IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA INPUT/OUTPUT (R/W) +IDE_IO_ERR .EQU $IDE_IO_BASE + $01 ; ERROR REGISTER (R) +IDE_IO_FEAT .EQU $IDE_IO_BASE + $01 ; FEATURES REGISTER (W) +IDE_IO_COUNT .EQU $IDE_IO_BASE + $02 ; SECTOR COUNT REGISTER (R/W) +IDE_IO_SECT .EQU $IDE_IO_BASE + $03 ; SECTOR NUMBER REGISTER (R/W) +IDE_IO_CYLLO .EQU $IDE_IO_BASE + $04 ; CYLINDER NUM REGISTER (LSB) (R/W) +IDE_IO_CYLHI .EQU $IDE_IO_BASE + $05 ; CYLINDER NUM REGISTER (MSB) (R/W) +IDE_IO_DRVHD .EQU $IDE_IO_BASE + $06 ; DRIVE/HEAD REGISTER (R/W) +IDE_IO_LBA0 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 0 (BITS 0-7) (R/W) +IDE_IO_LBA1 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 1 (BITS 8-15) (R/W) +IDE_IO_LBA2 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 2 (BITS 16-23) (R/W) +IDE_IO_LBA3 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 3 (BITS 24-27) (R/W) +IDE_IO_STAT .EQU $IDE_IO_BASE + $07 ; STATUS REGISTER (R) +IDE_IO_CMD .EQU $IDE_IO_BASE + $07 ; COMMAND REGISTER (EXECUTE) (W) +IDE_IO_ALTSTAT .EQU $IDE_IO_BASE + $0E ; ALTERNATE STATUS REGISTER (R) +IDE_IO_CTRL .EQU $IDE_IO_BASE + $0E ; DEVICE CONTROL REGISTER (W) +IDE_IO_DRVADR .EQU $IDE_IO_BASE + $0F ; DRIVE ADDRESS REGISTER (R) +; +; COMMAND BYTES +; +IDE_CMD_RECAL .EQU $10 +IDE_CMD_READ .EQU $20 +IDE_CMD_WRITE .EQU $30 +IDE_CMD_IDDEV .EQU $EC +IDE_CMD_SETFEAT .EQU $EF +; +; FEATURE BYTES ; +IDE_FEAT_ENABLE8BIT .EQU $01 +IDE_FEAT_DISABLE8BIT .EQU $81 ; +; IDE DEVICE TYPES ; -IDECMD_RECAL .EQU $10 -IDECMD_READ .EQU $20 -IDECMD_WRITE .EQU $30 -IDECMD_IDDEV .EQU $EC -IDECMD_SETFEAT .EQU $EF +IDE_TYPEUNK .EQU 0 +IDE_TYPEATA .EQU 1 +IDE_TYPEATAPI .EQU 2 ; -IDE_RCOK .EQU 0 -IDE_RCCMDERR .EQU 1 -IDE_RCRDYTO .EQU 2 -IDE_RCBUFTO .EQU 3 -IDE_RCBSYTO .EQU 4 +; IDE DEVICE STATUS ; -; UNIT CONFIGURATION +IDE_STOK .EQU 0 +IDE_STINVUNIT .EQU -1 +IDE_STNOMEDIA .EQU -2 +IDE_STCMDERR .EQU -3 +IDE_STIOERR .EQU -4 +IDE_STRDYTO .EQU -5 +IDE_STDRQTO .EQU -6 +IDE_STBSYTO .EQU -7 ; -IDE_DEVICES: -IDE_DEVICE0 .DB %11100000 ; LBA, MASTER DEVICE -IDE_DEVICE1 .DB %11110000 ; LBA, SLAVE DEVICE +; DRIVE SELECTION BYTES (FOR USE IN DRIVE/HEAD REGISTER) ; +IDE_DRVSEL: +IDE_DRVMASTER .DB %11100000 ; LBA, MASTER DEVICE +IDE_DRVSLAVE .DB %11110000 ; LBA, SLAVE DEVICE ; +; PER UNIT DATA OFFSETS (CAREFUL NOT TO EXCEED PER UNIT SPACE IN IDE_UNITDATA) +; SEE IDE_UNITDATA IN DATA STORAGE BELOW +; +IDE_STAT .EQU 0 ; LAST STATUS (1 BYTE) +IDE_TYPE .EQU 1 ; DEVICE TYPE (1 BYTE) +IDE_CAPACITY .EQU 2 ; DEVICE CAPACITY (1 DWORD/4 BYTES) +; +; MACRO TO RETURN POINTER TO FIELD WITHIN UNIT DATA +; +#DEFINE IDE_DPTR(FIELD) CALL IDE_DPTRIMP \ .DB FIELD +; +;============================================================================= +; INITIALIZATION ENTRY POINT +;============================================================================= ; IDE_INIT: PRTS("IDE:$") ; LABEL FOR IO ADDRESS @@ -81,116 +214,87 @@ IDE_INIT: PRTS(" MODE=MK4$") #ENDIF ; PRINT IDE INTERFACE PORT ADDRESS - PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS - LD A,IDEDATA ; GET IO ADDRESS + PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS + LD A,IDE_IO_DATA ; GET IO ADDRESS CALL PRTHEXBYTE ; PRINT IT ; - ; RESET INTERFACE - CALL IDE_RESET ; INTERFACE RESET - CALL DELAY ; SMALL DELAY + ; CLEAR OUT ALL DATA (FOR ALL UNITS) + LD HL,IDE_UDATA + LD BC,IDE_UDLEN + XOR A + CALL FILL ; - ; SET GLOBAL STATUS TO OK (ZERO) - XOR A ; STATUS OK - LD (IDE_STAT),A ; INITIALIZE IT + ; INITIALIZE THE IDE INTERFACE NOW + CALL IDE_RESET ; DO HARDWARE SETUP/INIT + JR NZ,IDE_INIT2 ; SKIP PROBING IF INTERFACE SETUP FAILS ; - ; PROBE FOR DEVICE(S) - LD A,(IDE_DEVICE0) ; DEVICE 0 + ; INITIAL DEVICE PROBING (CHECKING SIGNATURES) + LD B,IDE_UNITCNT ; NUMBER OF UNITS TO TRY + LD C,0 ; UNIT INDEX FOR LOOP +IDE_INIT1: + LD A,C ; UNIT NUMBER TO A DCALL PC_SPACE ; IF DEBUGGING, PRINT A SPACE DCALL PC_LBKT ; IF DEBUGGING, PRINT LEFT BRACKET - CALL IDE_PROBE ; PROBE FOR DEVICE 0 PRESENCE - DCALL PC_RBKT ; IF DEBUGGING, PRINT A RIGHT BRACKET - JR NZ,IDE_INIT1 ; IF DEVCIE 0 NOT PRESENT, SKIP DEVICE 1 PROBE - LD HL,IDE_UNITCNT ; POINT TO UNIT COUNT - INC (HL) ; INCREMENT IT - LD A,(IDE_DEVICE1) ; DEVICE 1 - DCALL PC_SPACE ; IF DEBUGGING, PRINT A SPACE - DCALL PC_LBKT ; IF DEBUGGING, PRINT A LEFT BRACKET - CALL IDE_PROBE ; PROBE FOR DEVICE 1 PRESENT + PUSH BC + CALL IDE_PROBE ; PROBE FOR DEVICE PRESENCE + POP BC DCALL PC_RBKT ; IF DEBUGGING, PRINT A RIGHT BRACKET - JR NZ,IDE_INIT1 ; IF DEVICE 1 NOT PRESENT, SKIP - LD HL,IDE_UNITCNT ; POINT TO UNIT COUNT - INC (HL) ; INCREMENT IT -; -IDE_INIT1: - ; RESTORE DEFAULT DEVICE SELECTION (DEVICE 0) - LD A,(IDE_DEVICE0) ; DEVICE 0 - OUT (IDEDEVICE),A ; SELECT IT - CALL DELAY ; SMALL DELAY AFTER SELECT + INC C ; NEXT UNIT + DJNZ IDE_INIT1 ; LOOP AS NEEDED ; ; PRINT UNIT COUNT PRTS(" UNITS=$") ; PRINT LABEL FOR UNIT COUNT - LD A,(IDE_UNITCNT) ; GET UNIT COUNT + LD A,IDE_UNITCNT ; GET UNIT COUNT CALL PRTDECB ; PRINT IT IN DECIMAL ; - ; CHECK FOR ZERO DEVICES AND BAIL OUT IF SO - LD A,(IDE_UNITCNT) ; GET UNIT COUNT - OR A ; SET FLAGS - RET Z ; IF ZERO, WE ARE DONE +IDE_INIT2: + ; CHECK FOR ZERO UNITS AND GET OUT IF SO! ; ; DEVICE SETUP LOOP LD B,A ; LOOP ONCE PER UNIT LD C,0 ; C IS UNIT INDEX -IDE_INIT2: +IDE_INIT3: PUSH BC ; SAVE LOOP CONTROL - CALL IDE_INIT3 ; HANDLE THE NEXT UNIT + LD A,C ; UNIT NUM TO ACCUM + CALL IDE_INITUNIT ; IF EXISTS (NZ), INIT UNIT +#IF (IDETRACE < 2) + CALL NZ,IDE_PRTSTAT ; IF ERROR, NOTIFY USER IF NOT DONE PREVIOUSLY +#ENDIF POP BC ; RESTORE LOOP CONTROL INC C ; INCREMENT UNIT INDEX - DJNZ IDE_INIT2 ; LOOP UNTIL DONE + DJNZ IDE_INIT3 ; LOOP UNTIL DONE RET ; INIT FINISHED ; -IDE_INIT3: ; SUBROUTINE TO QUERY A DEVICE - - ; PRINT PREFIX FOR UNIT INFO "IDE#:" - CALL NEWLINE ; FORMATTING: START A NEW LINE - LD DE,IDESTR_PREFIX ; POINT TO STRING "IDE" - CALL WRITESTR ; PRINT STRING - LD A,C ; UNIT NUMBER TO ACCUM - LD (IDE_CURUNIT),A ; SAVE THE CURRENT UNIT - CALL PRTDECB ; PRINT IT IN DECIMAL - CALL PC_COLON ; PRINT THE ENDING COLON -; - LD A,C ; UNIT NUMBER TO ACCUM - CALL IDE_SELECT ; SELECT THE CORRECT DEVICE +; INITIALIZE UNIT DESIGNATED IN ACCUM +; +IDE_INITUNIT: + CALL IDE_SELUNIT ; SELECT UNIT + RET NZ ; ABORT ON ERROR +; + CALL IDE_INITDEV ; INIT DEVICE; FILL DIOBUF W/ IDENTIFY RESULTS + RET NZ ; ABORT ON ERROR +; + CALL IDE_PRTPREFIX ; PRINT DEVICE PREFIX ; #IF (IDE8BIT) PRTS(" 8BIT$") - CALL IDE_SET8BIT ; SET 8BIT TRANSFER FEATURE - RET NZ ; BAIL OUT ON ERROR #ENDIF -; - CALL IDE_IDENTIFY ; EXECUTE IDENTIFY COMMAND - RET NZ ; BAIL OUT ON ERROR -; - LD DE,(DIOBUF) ; POINT TO BUFFER - DCALL DUMP_BUFFER ; DUMP IT IF DEBUGGING ; ; PRINT LBA/NOLBA - CALL PC_SPACE ; SPACING + CALL PC_SPACE ; FORMATTING LD HL,(DIOBUF) ; POINT TO BUFFER START LD DE,98+1 ; OFFSET OF BYTE CONTAINING LBA FLAG ADD HL,DE ; POINT TO FINAL BUFFER ADDRESS LD A,(HL) ; GET THE BYTE BIT 1,A ; CHECK THE LBA BIT - LD DE,IDESTR_NO ; POINT TO "NO" STRING + LD DE,IDE_STR_NO ; POINT TO "NO" STRING CALL Z,WRITESTR ; PRINT "NO" BEFORE "LBA" IF LBA NOT SUPPORTED PRTS("LBA$") ; PRINT "LBA" REGARDLESS ; - ; PRECOMPUTE LOC TO STORE 32-BIT CAPACITY - LD HL,IDE_CAPLIST ; POINT TO CAPACITY ARRAY - LD A,(IDE_CURUNIT) ; GET CUR UNIT NUM - RLCA ; MULTIPLY BY 4 - RLCA ; ... TO OFFSET BY DWORDS - CALL ADDHLA ; ADD OFFSET TO POINTER - PUSH HL ; SAVE POINTER -; - ; GET, SAVE, AND PRINT STORAGE CAPACITY (BLOCK COUNT) + ; PRINT STORAGE CAPACITY (BLOCK COUNT) PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL - LD HL,(DIOBUF) ; POINT TO BUFFER START - LD DE,120 ; OFFSET OF SECGTOR COUNT - ADD HL,DE ; POINT TO ADDRESS OF SECTOR COUNT - CALL LD32 ; LOAD IT TO DE:HL - POP BC ; RECOVER POINTER TO CAPACITY ARRAY ENTRY - CALL ST32 ; SAVE CAPACITY + IDE_DPTR(IDE_CAPACITY) ; SET HL TO ADR OF DEVICE CAPACITY + CALL LD32 ; GET THE CAPACITY VALUE CALL PRTHEX32 ; PRINT HEX VALUE ; ; PRINT STORAGE SIZE IN MB @@ -200,12 +304,19 @@ IDE_INIT3: ; SUBROUTINE TO QUERY A DEVICE CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED) PRTS("MB$") ; PRINT SUFFIX ; - RET -; + XOR A ; SIGNAL SUCCESS + RET ; RETURN WITH A=0, AND Z SET ; +;============================================================================= +; FUNCTION DISPATCH ENTRY POINT +;============================================================================= ; IDE_DISPATCH: - LD A,B ; GET REQUESTED FUNCTION + LD A,C ; DEVICE/UNIT TO A + AND $0F ; REMOVE DEVICE BITS LEAVING JUST UNIT ID + CALL IDE_SELUNIT ; SELECT DESIRED UNIT + RET NZ ; ABORT ON ERROR + LD A,B ; GET REQUESTED FUNCTION AND $0F JR Z,IDE_READ DEC A @@ -214,189 +325,434 @@ IDE_DISPATCH: JR Z,IDE_STATUS DEC A JR Z,IDE_MEDIA + DEC A + JR Z,IDE_CAP + DEC A + JR Z,IDE_GEOM CALL PANIC ; ; ; IDE_READ: - LD A,IDECMD_READ - LD (IDE_CMD),A - CALL IDE_RW - RET NZ - CALL IDE_BUFRD - RET + JP IDE_RDSEC ; ; ; IDE_WRITE: - LD A,IDECMD_WRITE - LD (IDE_CMD),A - CALL IDE_RW - RET NZ - CALL IDE_BUFWR - RET + JP IDE_WRSEC ; ; ; IDE_STATUS: - LD A,(IDE_STAT) ; LOAD STATUS - OR A ; SET FLAGS - RET + ; RETURN UNIT STATUS + IDE_DPTR(IDE_STAT) ; HL := ADR OF STATUS, AF TRASHED + LD A,(HL) ; GET STATUS OF SELECTED UNIT + OR A ; SET FLAGS + RET ; AND RETURN ; ; IDE_MEDIA ; IDE_MEDIA: - LD A,C ; GET THE DEVICE/UNIT - AND $0F ; ISOLATE UNIT - LD HL,IDE_UNITCNT ; POINT TO UNIT COUNT - CP (HL) ; COMPARE TO UNIT COUNT - LD A,MID_HD ; ASSUME WE ARE OK - RET C ; RETURN - XOR A ; NO MEDIA - RET ; AND RETURN -; -; -; -IDE_RW: - ; SELECT DEVICE - LD A,(HSTDSK) ; HSTDSK -> HEAD BIT 4 TO SELECT UNIT - AND $0F - CALL IDE_SELECT - CALL IDE_SETUP ; SETUP CYL, TRK, HEAD - JR IDE_RUNCMD ; RETURN THRU RUNCMD -; + CALL IDE_INITDEVICE ; RE-INIT SELECTED UNIT + LD A,MID_HD ; ASSUME WE ARE OK + RET Z ; RETURN IF GOOD INIT + LD A,MID_NONE ; SIGNAL NO MEDA + OR A ; SET FLAGS + RET ; AND RETURN ; +; NEED TO IMPLEMENT BELOW!!!! ; -IDE_RUNCMD: - ; CLEAR RESULTS - XOR A ; A = 0 - LD (IDE_STAT),A ; CLEAR DRIVER STATUS CODE - LD (IDE_STTS),A ; CLEAR SAVED STTS - LD (IDE_ERRS),A ; CLEAR SAVED ERR - CALL IDE_WAITRDY ; WAIT FOR DRIVE READY - RET NZ ; BAIL OUT ON TIMEOUT - LD A,(IDE_CMD) ; GET THE COMMAND - OUT (IDESTTS),A ; SEND IT (STARTS EXECUTION) - CALL IDE_WAITRDY ; WAIT FOR DRIVE READY (COMMAND DONE) - RET NZ ; BAIL OUT ON TIMEOUT - CALL IDE_CHKERR ; CHECK FOR ERRORS - RET NZ ; BAIL OUT ON TIMEOUT - DCALL IDE_PRT ; PRINT COMMAND IF DEBUG ENABLED - XOR A ; SET RESULT - RET ; DONE +IDE_INITDEVICE: + XOR A + RET ; ; ; -IDE_ERRCMD: - LD A,IDE_RCCMDERR - JR IDE_ERR +IDE_CAP: + IDE_DPTR(IDE_CAPACITY) ; POINT HL TO CAPACITY OF CUR UNIT + CALL LD32 ; GET THE CURRENT CAPACITY DO DE:HL + LD BC,512 ; 512 BYTES PER BLOCK + XOR A ; SIGNAL SUCCESS + RET ; AND DONE ; -IDE_ERRRDYTO: - LD A,IDE_RCRDYTO - JR IDE_ERR ; -IDE_ERRBUFTO: - LD A,IDE_RCBUFTO - JR IDE_ERR ; -IDE_ERRBSYTO: - LD A,IDE_RCBSYTO - JR IDE_ERR +IDE_GEOM: + ; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS + ; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE + CALL IDE_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC + LD L,H ; DIVIDE BY 256 FOR # TRACKS + LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL + LD D,16 | $80 ; HEADS / CYL = 16, SET LBA CAPABILITY BIT + LD E,16 ; SECTORS / TRACK = 16 + XOR A ; SIGNAL SUCCESS + RET ; -IDE_ERR: - LD (IDE_STAT),A ; SAVE ERROR AS STATUS -#IF (IDETRACE >= 1) - PUSH AF ; SAVE ACCUM - CALL IDE_PRT ; PRINT COMMAND SUMMARY - POP AF ; RESTORE ACCUM -#ENDIF - OR A ; MAKE SURE FLAGS ARE SET - RET ; DONE +;============================================================================= +; FUNCTION SUPPORT ROUTINES +;============================================================================= ; -; SOFT RESET OF ALL DEVICES +; SOFT RESET OF ALL DEVICES ON BUS ; IDE_RESET: LD A,%00001110 ; NO INTERRUPTS, ASSERT RESET BOTH DRIVES - OUT (IDECTRL),A + OUT (IDE_IO_CTRL),A LD DE,16 ; DELAY ~250US CALL VDELAY LD A,%00001010 ; NO INTERRUPTS, DEASSERT RESET - OUT (IDECTRL),A - XOR A - LD (IDE_STAT),A ; STATUS OK - RET ; SAVE IT + OUT (IDE_IO_CTRL),A + CALL DELAY ; SMALL DELAY + XOR A ; SIGNAL SUCCESS + RET ; RETURN ; -; SELECT DEVICE IN A +; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT +; UNIT IS SPECIFIED IN A ; -IDE_SELECT: - LD HL,IDE_DEVICES - CALL ADDHLA - LD A,(HL) ; LOAD DEVICE - LD (IDE_DEVICE),A ; SHADOW REGISTER - - CALL IDE_WAITBSY - RET NZ +IDE_SELUNIT: + LD HL,IDE_UNIT ; POINT TO PREVIOUSLY SELECTED UNIT + CP (HL) ; SAME? + RET Z ; IF SO, NOTHING MORE TO DO - LD A,(IDE_DEVICE) ; RECOVER DEVICE VALUE - OUT (IDEDEVICE),A ; SELECT DEVICE - ; DELAY??? + CP IDE_UNITCNT ; CHECK VALIDITY (EXCEED UNIT COUNT?) + JP NC,IDE_INVUNIT ; HANDLE INVALID UNIT +; + ; NEW UNIT SELECTED, IMPLEMENT IT + LD (IDE_UNIT),A ; RECORD NEW UNIT NUMBER +; +#IF (IDEMODE == IDEMODE_DIDE) + ; SELECT PRIMARY/SECONDARY INTERFACE FOR DIDE HARDWARE +#ENDIF +; + AND $01 ; LS BIT DETERMINES MASTER/SLAVE + LD HL,IDE_DRVSEL + CALL ADDHLA + LD A,(HL) ; LOAD DRIVE/HEAD VALUE +; + OUT (IDE_IO_DRVHD),A ; SELECT DRIVE + LD (IDE_DRVHD),A ; UPDATE SHADOW REGISTER +; + ; SPEC REQUIRES 400NS DELAY BEFORE CHECKING STATUS REGISTER +; + XOR A RET ; +; (RE)INITIALIZE DEVICE +; +IDE_INITDEV: +; + IDE_DPTR(IDE_TYPE) ; POINT HL TO UNIT TYPE FIELD, A IS TRASHED + LD A,(HL) ; GET THE DEVICE TYPE + OR A ; SET FLAGS + JP Z,IDE_NOMEDIA ; EXIT SETTING NO MEDIA STATUS +; + ; CLEAR OUT UNIT SPECIFIC DATA, BUT PRESERVE THE EXISTING + ; VALUE OF THE UNIT TYPE WHICH WAS ESTABLISHED BY THE DEVICE + ; PROBES WHEN THE IDE BUS WAS RESET + PUSH AF ; SAVE UNIT TYPE VALUE FROM ABOVE + PUSH HL ; SAVE UNIT TYPE FIELD POINTER + IDE_DPTR(0) ; SET HL TO START OF UNIT DATA + LD BC,IDE_UDLEN + XOR A + CALL FILL + POP HL ; RECOVER UNIT TYPE FIELD POINTER + POP AF ; RECOVER UNIT TYPE VALUE + LD (HL),A ; AND PUT IT BACK +; +#IF (IDE8BIT) + LD A,IDE_FEAT_ENABLE8BIT ; FEATURE VALUE = ENABLE 8-BIT PIO + CALL IDE_SETFEAT ; SET FEATURE + RET NZ ; BAIL OUT ON ERROR +#ENDIF +; + CALL IDE_IDENTIFY ; EXECUTE IDENTIFY COMMAND + RET NZ ; BAIL OUT ON ERROR +; + LD DE,(DIOBUF) ; POINT TO BUFFER + DCALL DUMP_BUFFER ; DUMP IT IF DEBUGGING +; + ; GET DEVICE CAPACITY AND SAVE IT + IDE_DPTR(IDE_CAPACITY) ; POINT HL TO UNIT CAPACITY FIELD + PUSH HL ; SAVE POINTER + LD HL,(DIOBUF) ; POINT TO BUFFER START + LD A,120 ; OFFSET OF SECTOR COUNT + CALL ADDHLA ; POINT TO ADDRESS OF SECTOR COUNT + CALL LD32 ; LOAD IT TO DE:HL + POP BC ; RECOVER POINTER TO CAPACITY ENTRY + CALL ST32 ; SAVE CAPACITY +; + ; RESET CARD STATUS TO 0 (OK) + IDE_DPTR(IDE_STAT) ; HL := ADR OF STATUS, AF TRASHED + XOR A ; A := 0 (STATUS = OK) + LD (HL),A ; SAVE IT +; + RET ; RETURN, A=0, Z SET +; ; ; IDE_PROBE: - OUT (IDEDEVICE),A ; SELECT IT + CALL IDE_SELUNIT ; SELECT UNIT + RET NZ ; ABORT ON ERROR CALL DELAY - IN A,(IDESECTC) +; + ; CHECK STATUS + IN A,(IDE_IO_STAT) ; GET STATUS + DCALL PRTHEXBYTE ; IF DEBUG, PRINT STATUS + OR A ; SET FLAGS TO TEST FOR ZERO + JR NZ,IDE_PROBE0 ; CONTINUE IF NON-ZERO + DEC A ; ZERO MEANS NO MEDIA, SIGNAL ERROR + RET ; AND DONE +; +IDE_PROBE0: + ;CALL IDE_WAITBSY ; WAIT FOR BUSY TO CLEAR + ;RET NZ ; ABORT ON TIMEOUT +; + ;; CHECK STATUS + ;IN A,(IDE_IO_STAT) ; GET STATUS + ;DCALL PRTHEXBYTE ; IF DEBUG, PRINT STATUS + ;OR A ; SET FLAGS TO TEST FOR ZERO + ;JR NZ,IDE_PROBE1 ; CONTINUE IF NON-ZERO + ;DEC A ; ZERO MEANS NO MEDIA, SIGNAL ERROR + ;RET ; AND DONE +; +IDE_PROBE1: + ; CHECK SIGNATURE + DCALL PC_SPACE + IN A,(IDE_IO_COUNT) DCALL PRTHEXBYTE CP $01 RET NZ DCALL PC_SPACE - IN A,(IDESECTN) + IN A,(IDE_IO_SECT) DCALL PRTHEXBYTE CP $01 RET NZ DCALL PC_SPACE - IN A,(IDECYLLO) + IN A,(IDE_IO_CYLLO) DCALL PRTHEXBYTE CP $00 RET NZ DCALL PC_SPACE - IN A,(IDECYLHI) + IN A,(IDE_IO_CYLHI) DCALL PRTHEXBYTE CP $00 RET NZ - DCALL PC_SPACE - IN A,(IDESTTS) - DCALL PRTHEXBYTE ; PRINT STATUS - CP 0 - JR NZ,IDE_PROBE1 - OR $FF ; SIGNAL ERROR - RET - -IDE_PROBE1: +; + ; SIGNATURE MATCHES ATA DEVICE, RECORD TYPE AND RETURN SUCCESS + IDE_DPTR(IDE_TYPE) ; POINT HL TO UNIT TYPE FIELD, A IS TRASHED + LD (HL),IDE_TYPEATA ; SET THE DEVICE TYPE XOR A ; SIGNAL SUCCESS - RET + RET ; DONE, NOTE THAT A=0 AND Z IS SET ; ; -; -IDE_SET8BIT: - ; DEVICE *MUST* ALREADY BE SELECTED! - LD A,IDECMD_SETFEAT - LD (IDE_CMD),A - LD A,$01 ; $01 ENABLES 8-BIT XFR FEATURE - OUT (IDEERR),A ; SET FEATURS VALUE - JP IDE_RUNCMD ; EXIT THRU RUNCMD +; +IDE_SETFEAT: + PUSH AF +#IF (IDETRACE >= 3) + CALL IDE_PRTPREFIX + PRTS(" SETFEAT$") +#ENDIF + LD A,(IDE_DRVHD) + OUT (IDE_IO_DRVHD),A + DCALL PC_SPACE + DCALL PRTHEXBYTE + POP AF + OUT (IDE_IO_FEAT),A ; SET THE FEATURE VALUE + DCALL PC_SPACE + DCALL PRTHEXBYTE + LD A,IDE_CMD_SETFEAT ; CMD = SETFEAT + LD (IDE_CMD),A ; SAVE IT + JP IDE_RUNCMD ; RUN COMMAND AND EXIT ; ; ; IDE_IDENTIFY: - ; DEVICE *MUST* ALREADY BE SELECTED! - LD A,IDECMD_IDDEV +#IF (IDETRACE >= 3) + CALL IDE_PRTPREFIX + PRTS(" IDDEV$") +#ENDIF + LD A,(IDE_DRVHD) + OUT (IDE_IO_DRVHD),A + DCALL PC_SPACE + DCALL PRTHEXBYTE + LD A,IDE_CMD_IDDEV + LD (IDE_CMD),A + CALL IDE_RUNCMD + RET NZ + JP IDE_GETBUF ; EXIT THRU BUFRD +; +; +; +IDE_RDSEC: + CALL IDE_CHKDEVICE + RET NZ +; +#IF (IDETRACE >= 3) + CALL IDE_PRTPREFIX + PRTS(" READ$") +#ENDIF + LD A,(IDE_DRVHD) + OUT (IDE_IO_DRVHD),A + DCALL PC_SPACE + DCALL PRTHEXBYTE + CALL IDE_SETADDR ; SETUP CYL, TRK, HEAD + LD A,IDE_CMD_READ LD (IDE_CMD),A CALL IDE_RUNCMD RET NZ - JP IDE_BUFRD ; EXIT THRU BUFRD + JP IDE_GETBUF +; +; +; +IDE_WRSEC: + CALL IDE_CHKDEVICE + RET NZ +; +#IF (IDETRACE >= 3) + CALL IDE_PRTPREFIX + PRTS(" WRITE$") +#ENDIF + LD A,(IDE_DRVHD) + OUT (IDE_IO_DRVHD),A + DCALL PC_SPACE + DCALL PRTHEXBYTE + CALL IDE_SETADDR ; SETUP CYL, TRK, HEAD + LD A,IDE_CMD_WRITE + LD (IDE_CMD),A + CALL IDE_RUNCMD + RET NZ + JP IDE_PUTBUF +; +; +; +IDE_CHKDEVICE: + IDE_DPTR(IDE_STAT) + LD A,(HL) + OR A + RET Z ; RETURN IF ALL IS WELL +; + ; ATTEMPT TO REINITIALIZE HERE??? + JP IDE_ERR + RET +; +; +; +IDE_SETADDR: + ; SEND 3 LOWEST BYTES OF LBA IN REVERSE ORDER + ; IDE_IO_LBA3 HAS ALREADY BEEN SET BY IDE_SELECT + ; HSTLBA2-0 --> IDE_IO_LBA2-0 + LD C,IDE_IO_LBA0 + 3 ; STARTING IO PORT (NOT PRE-DEC BELOW) + LD HL,HSTLBA + 2 ; STARTING LBA BYTE ADR + LD B,3 ; SEND 3 BYTES +IDE_SETADDR1: +; +#IF (IDETRACE >= 3) + LD A,(HL) + CALL PC_SPACE + CALL PRTHEXBYTE +#ENDIF +; + DEC C ; NEXT PORT + OUTD ; SEND NEXT BYTE + JR NZ,IDE_SETADDR1 ; LOOP TILL DONE +; + ; SEND COUNT OF BLOCKS TO TRANSFER + ; 1 --> IDE_IO_COUNT + LD A,1 ; COUNT VALUE IS 1 BLOCK +; +#IF (IDETRACE >= 3) + DCALL PC_SPACE + DCALL PRTHEXBYTE +#ENDIF +; + DEC C ; PORT := IDE_IO_COUNT + OUT (C),A ; SEND IT +; +#IF (DSKYENABLE) + CALL IDE_DSKY +#ENDIF +; + RET +; +; +; +IDE_RUNCMD: + CALL IDE_WAITRDY ; WAIT FOR DRIVE READY + RET NZ ; BAIL OUT ON TIMEOUT +; + LD A,(IDE_CMD) ; GET THE COMMAND + DCALL PC_SPACE + DCALL PRTHEXBYTE + OUT (IDE_IO_CMD),A ; SEND IT (STARTS EXECUTION) +#IF (IDETRACE >= 3) + PRTS(" -->$") +#ENDIF +; + CALL IDE_WAITBSY ; WAIT FOR DRIVE READY (COMMAND DONE) + RET NZ ; BAIL OUT ON TIMEOUT +; + CALL IDE_GETRES + JP NZ,IDE_CMDERR + RET +; +; +; +IDE_GETBUF: +#IF (IDETRACE >= 3) + PRTS(" GETBUF$") +#ENDIF + + CALL IDE_WAITDRQ ; WAIT FOR BUFFER READY + RET NZ ; BAIL OUT IF TIMEOUT + + LD HL,(DIOBUF) + LD B,0 + +#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) + LD C,IDE_IO_DATA + INIR + INIR +#ELSE + LD C,IDE_IO_DATAHI +IDE_GETBUF1: + IN A,(IDE_IO_DATALO) ; READ THE LO BYTE + LD (HL),A ; SAVE IN BUFFER + INC HL ; INC BUFFER POINTER + INI ; READ AND SAVE HI BYTE, INC HL, DEC B + JP NZ,IDE_GETBUF1 ; LOOP AS NEEDED +#ENDIF + CALL IDE_GETRES + JP NZ,IDE_IOERR + RET +; +; +; +IDE_PUTBUF: +#IF (IDETRACE >= 3) + PRTS(" GETBUF$") +#ENDIF + + CALL IDE_WAITDRQ ; WAIT FOR BUFFER READY + RET NZ ; BAIL OUT IF TIMEOUT + + LD HL,(DIOBUF) + LD B,0 + +#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) + LD C,IDE_IO_DATA + OTIR + OTIR +#ELSE + LD C,IDE_IO_DATAHI +IDE_PUTBUF1: + LD A,(HL) ; GET THE LO BYTE AND KEEP IT IN A FOR LATER + INC HL ; BUMP TO NEXT BYTE IN BUFFER + OUTI ; WRITE HI BYTE, INC HL, DEC B + OUT (IDE_IO_DATALO),A ; NOW WRITE THE SAVED LO BYTE TO LO BYTE + JP NZ,IDE_PUTBUF1 ; LOOP AS NEEDED +#ENDIF + CALL IDE_GETRES + JP NZ,IDE_IOERR + RET ; ; ; @@ -405,11 +761,10 @@ IDE_WAITRDY: IDE_WAITRDY1: LD DE,-1 ; ~1 SECOND INNER LOOP IDE_WAITRDY2: - IN A,(IDESTTS) ; READ STATUS - LD (IDE_STTS),A ; SAVE IT + IN A,(IDE_IO_STAT) ; READ STATUS + LD C,A ; SAVE IT AND %11000000 ; ISOLATE BUSY AND RDY BITS XOR %01000000 ; WE WANT BUSY(7) TO BE 0 AND RDY(6) TO BE 1 - ;JR Z,IDE_WAITRPT ; DIAGNOSTIC RET Z ; ALL SET, RETURN WITH Z SET CALL DELAY ; DELAY 16US DEC DE @@ -417,28 +772,27 @@ IDE_WAITRDY2: OR E JR NZ,IDE_WAITRDY2 ; INNER LOOP RETURN DJNZ IDE_WAITRDY1 ; OUTER LOOP RETURN - JP IDE_ERRRDYTO ; EXIT WITH RDYTO ERR + JP IDE_RDYTO ; EXIT WITH RDYTO ERR ; ; ; -IDE_WAITBUF: +IDE_WAITDRQ: LD B,3 ; ~3 SECOND TIMEOUT??? -IDE_WAITBUF1: +IDE_WAITDRQ1: LD DE,-1 ; ~1 SECOND INNER LOOP -IDE_WAITBUF2: - IN A,(IDESTTS) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER - LD (IDE_STTS),A ; SAVE IT +IDE_WAITDRQ2: + IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER + LD C,A ; SAVE IT AND %10001000 ; TO FILL (OR READY TO FILL) XOR %00001000 - ;JR Z,IDE_WAITRPT ; DIAGNOSTIC RET Z CALL DELAY ; DELAY 16US DEC DE LD A,D OR E - JR NZ,IDE_WAITBUF2 - DJNZ IDE_WAITBUF1 - JP IDE_ERRBUFTO ; EXIT WITH BUFTO ERR + JR NZ,IDE_WAITDRQ2 + DJNZ IDE_WAITDRQ1 + JP IDE_DRQTO ; EXIT WITH BUFTO ERR ; ; ; @@ -447,10 +801,9 @@ IDE_WAITBSY: IDE_WAITBSY1: LD DE,-1 ; ~1 SECOND INNER LOOP IDE_WAITBSY2: - IN A,(IDESTTS) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER - LD (IDE_STTS),A ; SAVE IT + IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER + LD C,A ; SAVE IT AND %10000000 ; TO FILL (OR READY TO FILL) - ;JR Z,IDE_WAITRPT ; DIAGNOSTIC RET Z CALL DELAY ; DELAY 16US DEC DE @@ -458,273 +811,222 @@ IDE_WAITBSY2: OR E JR NZ,IDE_WAITBSY2 DJNZ IDE_WAITBSY1 - JP IDE_ERRBSYTO ; EXIT WITH BSYTO ERR + JP IDE_BSYTO ; EXIT WITH BSYTO ERR ; ; ; -IDE_WAITRPT: - PUSH AF - CALL PC_SPACE - LD A,B - CALL PRTHEXBYTE - LD A,D - CALL PRTHEXBYTE - LD A,E - CALL PRTHEXBYTE - POP AF - RET +IDE_GETRES: + IN A,(IDE_IO_STAT) ; GET STATUS + DCALL PC_SPACE + DCALL PRTHEXBYTE + AND %00000001 ; ERROR BIT SET? + RET Z ; NOPE, RETURN WITH ZF ; + IN A,(IDE_IO_ERR) ; READ ERROR REGISTER + DCALL PC_SPACE + DCALL PRTHEXBYTE + OR $FF ; FORCE NZ TO SIGNAL ERROR + RET ; RETURN ; +;============================================================================= +; HARDWARE INTERFACE ROUTINES +;============================================================================= ; -IDE_CHKERR: - IN A,(IDESTTS) ; GET STATUS - LD (IDE_STTS),A ; SAVE IT - AND %00000001 ; ERROR BIT SET? - RET Z ; NOPE, RETURN WITH ZF ; - IN A,(IDEERR) ; READ ERROR REGISTER - LD (IDE_ERRS),A ; SAVE IT - JP IDE_ERRCMD ; EXIT VIA ERRCMD +;============================================================================= +; ERROR HANDLING AND DIAGNOSTICS +;============================================================================= ; +; ERROR HANDLERS ; +IDE_INVUNIT: + LD A,IDE_STINVUNIT + JR IDE_ERR2 ; SPECIAL CASE FOR INVALID UNIT ; -IDE_BUFRD: - CALL IDE_WAITBUF ; WAIT FOR BUFFER READY - RET NZ ; BAIL OUT IF TIMEOUT - - LD HL,(DIOBUF) - LD B,0 - -#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) - LD C,IDEDATA - INIR - INIR -#ELSE - LD C,IDEDATAHI -IDE_BUFRD1: - IN A,(IDEDATALO) ; READ THE LO BYTE - LD (HL),A ; SAVE IN BUFFER - INC HL ; INC BUFFER POINTER - INI ; READ AND SAVE HI BYTE, INC HL, DEC B - JP NZ,IDE_BUFRD1 ; LOOP AS NEEDED -#ENDIF - JP IDE_CHKERR ; RETURN THRU CHKERR +IDE_NOMEDIA: + LD A,IDE_STNOMEDIA + JR IDE_ERR ; +IDE_CMDERR: + LD A,IDE_STCMDERR + JR IDE_ERR ; +IDE_IOERR: + LD A,IDE_STIOERR + JR IDE_ERR ; -IDE_BUFWR: - CALL IDE_WAITBUF ; WAIT FOR BUFFER READY - RET NZ ; BAIL OUT IF TIMEOUT - - LD HL,(DIOBUF) - LD B,0 - -#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) - LD C,IDEDATA - OTIR - OTIR -#ELSE - LD C,IDEDATAHI -IDE_BUFWR1: - LD A,(HL) ; GET THE LO BYTE AND KEEP IT IN A FOR LATER - INC HL ; BUMP TO NEXT BYTE IN BUFFER - OUTI ; WRITE HI BYTE, INC HL, DEC B - OUT (IDEDATALO),A ; NOW WRITE THE SAVED LO BYTE TO LO BYTE - JP NZ,IDE_BUFWR1 ; LOOP AS NEEDED -#ENDIF - JP IDE_CHKERR ; RETURN THRU CHKERR -; -; -; -IDE_SETUP: - LD A,1 - OUT (IDESECTC),A - - ; SEND 3 BYTES OF LBA T:SS -> CYL:SEC (CC:S) - LD A,(HSTLBAHI) ; LBA HIGH LSB - LD (IDE_CYLHI),A ; SAVE IT - OUT (IDECYLHI),A ; -> CYLINDER HI - LD A,(HSTLBALO + 1) ; LBA LOW MSB - LD (IDE_CYLLO),A ; SAVE IT - OUT (IDECYLLO),A ; -> CYLINDER LO - LD A,(HSTLBALO) ; LBA LOW LSB - LD (IDE_SEC),A ; SAVE IT - OUT (IDESECTN),A ; -> SECTOR NUM -#IF (DSKYENABLE) - CALL IDE_DSKY -#ENDIF - RET +IDE_RDYTO: + LD A,IDE_STRDYTO + JR IDE_ERR ; +IDE_DRQTO: + LD A,IDE_STDRQTO + JR IDE_ERR ; +IDE_BSYTO: + LD A,IDE_STBSYTO + JR IDE_ERR ; -#IF (DSKYENABLE) -IDE_DSKY: - LD HL,DSKY_HEXBUF - LD A,(IDE_DEVICE) - LD (HL),A - INC HL - LD A,(IDE_CYLHI) - LD (HL),A - INC HL - LD A,(IDE_CYLLO) - LD (HL),A - INC HL - LD A,(IDE_SEC) - LD (HL),A - CALL DSKY_HEXOUT - RET +IDE_ERR: + PUSH HL ; IS THIS NEEDED? + PUSH AF ; SAVE INCOMING STATUS + IDE_DPTR(IDE_STAT) ; GET STATUS ADR IN HL, AF TRASHED + POP AF ; RESTORE INCOMING STATUS + LD (HL),A ; UPDATE STATUS + POP HL ; IS THIS NEEDED? +IDE_ERR2: +#IF (IDETRACE >= 2) + CALL IDE_PRTSTAT + CALL IDE_REGDUMP #ENDIF + OR A ; SET FLAGS + RET ; +; PRINT STATUS STRING (STATUS NUM IN A) ; -; -IDE_PRT: +IDE_PRTSTAT: + PUSH AF + PUSH DE + PUSH HL + OR A + LD DE,IDE_STR_STOK + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STINVUNIT + JR Z,IDE_PRTSTAT2 ; INVALID UNIT IS SPECIAL CASE + INC A + LD DE,IDE_STR_STNOMEDIA + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STCMDERR + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STIOERR + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STRDYTO + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STDRQTO + JR Z,IDE_PRTSTAT1 + INC A + LD DE,IDE_STR_STBSYTO + JR Z,IDE_PRTSTAT1 + LD DE,IDE_STR_STUNK +IDE_PRTSTAT1: + CALL IDE_PRTPREFIX ; PRINT UNIT PREFIX + JR IDE_PRTSTAT3 +IDE_PRTSTAT2: CALL NEWLINE - - LD DE,IDESTR_PREFIX - CALL WRITESTR - CALL PC_COLON - - CALL PC_SPACE - LD DE,IDESTR_CMD + PRTS("IDE:$") ; NO UNIT NUM IN PREFIX FOR INVALID UNIT +IDE_PRTSTAT3: + CALL PC_SPACE ; FORMATTING CALL WRITESTR - LD A,(IDE_CMD) - CALL PRTHEXBYTE - + POP HL + POP DE + POP AF + RET +; +; +; +IDE_REGDUMP: + PUSH AF + PUSH BC CALL PC_SPACE CALL PC_LBKT - LD A,(IDE_CMD) - LD DE,IDESTR_READ - CP IDECMD_READ - JP Z,IDE_PRTCMD - LD DE,IDESTR_WRITE - CP IDECMD_WRITE - JP Z,IDE_PRTCMD - LD DE,IDESTR_SETFEAT - CP IDECMD_SETFEAT - JP Z,IDE_PRTCMD - LD DE,IDESTR_IDDEV - CP IDECMD_IDDEV - JP Z,IDE_PRTCMD - LD DE,IDESTR_RECAL - CP IDECMD_RECAL - JP Z,IDE_PRTCMD - LD DE,IDESTR_UNKCMD -IDE_PRTCMD: - CALL WRITESTR - CALL PC_RBKT - - CALL PC_SPACE - LD A,(IDE_DEVICE) - CALL PRTHEXBYTE - LD A,(IDE_CYLHI) - CALL PRTHEXBYTE - LD A,(IDE_CYLLO) - CALL PRTHEXBYTE - LD A,(IDE_SEC) + LD C,IDE_IO_CMD + LD B,7 +IDE_REGDUMP1: + IN A,(C) CALL PRTHEXBYTE - - CALL PC_SPACE - LD DE,IDESTR_ARROW - CALL WRITESTR - - CALL PC_SPACE - IN A,(IDESTTS) - CALL PRTHEXBYTE - - CALL PC_SPACE - IN A,(IDEERR) - CALL PRTHEXBYTE - - CALL PC_SPACE - LD DE,IDESTR_RC - CALL WRITESTR - LD A,(IDE_STAT) - CALL PRTHEXBYTE - - CALL PC_SPACE - CALL PC_LBKT - LD A,(IDE_STAT) - LD DE,IDESTR_RCOK - CP IDE_RCOK - JP Z,IDE_PRTRC - LD DE,IDESTR_RCCMDERR - CP IDE_RCCMDERR - JP Z,IDE_PRTRC - LD DE,IDESTR_RCRDYTO - CP IDE_RCRDYTO - JP Z,IDE_PRTRC - LD DE,IDESTR_RCBUFTO - CP IDE_RCBUFTO - JP Z,IDE_PRTRC - LD DE,IDESTR_RCBSYTO - CP IDE_RCBSYTO - JP Z,IDE_PRTRC - LD DE,IDESTR_RCUNK -IDE_PRTRC: - CALL WRITESTR + DEC C + DJNZ IDE_REGDUMP1 CALL PC_RBKT - + POP BC + POP AF RET ; +; PRINT DIAGNONSTIC PREFIX ; +IDE_PRTPREFIX: + PUSH AF + CALL NEWLINE + PRTS("IDE$") + LD A,(IDE_UNIT) + ADD A,'0' + CALL COUT + CALL PC_COLON + POP AF + RET +; +; +; +#IF (DSKYENABLE) +IDE_DSKY: + LD HL,DSKY_HEXBUF ; POINT TO DSKY BUFFER + IN A,(IDE_IO_DRVHD) ; GET DRIVE/HEAD + LD (HL),A ; SAVE IN BUFFER + INC HL ; INCREMENT BUFFER POINTER + IN A,(IDE_IO_CYLHI) ; GET DRIVE/HEAD + LD (HL),A ; SAVE IN BUFFER + INC HL ; INCREMENT BUFFER POINTER + IN A,(IDE_IO_CYLLO) ; GET DRIVE/HEAD + LD (HL),A ; SAVE IN BUFFER + INC HL ; INCREMENT BUFFER POINTER + IN A,(IDE_IO_SECT) ; GET DRIVE/HEAD + LD (HL),A ; SAVE IN BUFFER + CALL DSKY_HEXOUT ; SEND IT TO DSKY + RET +#ENDIF ; -IDESTR_PREFIX .TEXT "IDE$" -IDESTR_CMD .TEXT "CMD=$" -IDESTR_RC .TEXT "RC=$" -IDESTR_ARROW .TEXT "-->$" -IDESTR_READ .TEXT "READ$" -IDESTR_WRITE .TEXT "WRITE$" -IDESTR_SETFEAT .TEXT "SETFEAT$" -IDESTR_IDDEV .TEXT "IDDEV$" -IDESTR_RECAL .TEXT "RECAL$" -IDESTR_UNKCMD .TEXT "UNKCMD$" -IDESTR_RCOK .TEXT "OK$" -IDESTR_RCCMDERR .TEXT "COMMAND ERROR$" -IDESTR_RCRDYTO .TEXT "READY TIMEOUT$" -IDESTR_RCBUFTO .TEXT "BUFFER TIMEOUT$" -IDESTR_RCBSYTO .TEXT "BUSY TIMEOUT$" -IDESTR_RCUNK .TEXT "UNKNOWN ERROR$" -IDESTR_NO .TEXT "NO$" -; -;================================================================================================== -; IDE DISK DRIVER - DATA -;================================================================================================== -; -IDE_UNITCNT .DB 0 -IDE_CURUNIT .DB 0 -IDE_STAT .DB 0 -; -IDE_CMD .DB 0 -IDE_DEVICE .DB 0 -IDE_CYLHI .DB 0 -IDE_CYLLO .DB 0 -IDE_SEC .DB 0 -IDE_STTS .DB 0 -IDE_ERRS .DB 0 -; -IDE_CAPLIST .FILL 2 * 4,0 ; CAPACITY OF EACH UNIT IN BLOCKS, 1 DWORD PER UNIT -; -; -; -; -; Error Register (ERR bit being set in the Status Register) -; -; Bit 7: BBK (Bad Block Detected) Set when a Bad Block is detected. -; Bit 6: UNC (Uncorrectable Data Error) Set when Uncorrectable Error is encountered. -; Bit 5: MC (Media Changed) Set to 0. -; Bit 4: IDNF (ID Not Found) Set when Sector ID not found. -; Bit 3: MCR (Media Change Request) Set to 0. -; Bit 2: ABRT (Aborted Command) Set when Command Aborted due to drive error. -; Bit 1: TKONF (Track 0 Not Found) Set when Executive Drive Diagnostic Command. -; Bit 0: AMNF (Address mark Not Found) Set in case of a general error. -; -; Status Register (When the contents of this register are read by the host, the IREQ# bit is cleared) -; -; Bit 7: BSY (Busy) Set when the drive is busy and unable to process any new ATA commands. -; Bit 6: DRDY (Data Ready) Set when the device is ready to accept ATA commands from the host. -; Bit 5: DWF (Drive Write Fault) Always set to 0. -; Bit 4: DSC (Drive Seek Complete) Set when the drive heads have been positioned over a specific track. -; Bit 3: DRQ (Data Request) Set when device is ready to transfer a word or byte of data to or from the host and the device. -; Bit 2: CORR (Corrected Data) Always set to 0. -; Bit 1: IDX (Index) Always set to 0. -; Bit 0: ERR (Error) Set when an error occurred during the previous ATA command. \ No newline at end of file +;============================================================================= +; STRING DATA +;============================================================================= +; +IDE_STR_STOK .TEXT "OK$" +IDE_STR_STINVUNIT .TEXT "INVALID UNIT$" +IDE_STR_STNOMEDIA .TEXT "NO MEDIA$" +IDE_STR_STCMDERR .TEXT "COMMAND ERROR$" +IDE_STR_STIOERR .TEXT "IO ERROR$" +IDE_STR_STRDYTO .TEXT "READY TIMEOUT$" +IDE_STR_STDRQTO .TEXT "DRQ TIMEOUT$" +IDE_STR_STBSYTO .TEXT "BUSY TIMEOUT$" +IDE_STR_STUNK .TEXT "UNKNOWN ERROR$" +; +IDE_STR_NO .TEXT "NO$" +; +;============================================================================= +; DATA STORAGE +;============================================================================= +; +IDE_CMD .DB 0 ; PENDING COMMAND TO PROCESS +IDE_DRVHD .DB 0 ; CURRENT DRIVE/HEAD MASK +; +; UNIT SPECIFIC DATA STORAGE +; +IDE_UNIT .DB -1 ; ACTIVE UNIT, INIT TO -1 TO INDICATE NOTHING SELECTED +IDE_UDATA .FILL IDE_UNITCNT*8,0 ; PER UNIT DATA, 8 BYTES +IDE_DLEN .EQU $ - IDE_UDATA ; LENGTH OF ENTIRE DATA STORAGE FOR ALL UNITS +IDE_UDLEN .EQU IDE_DLEN / IDE_UNITCNT ; LENGTH OF PER UNIT DATA +; +;============================================================================= +; HELPER ROUTINES +;============================================================================= +; +; IMPLEMENTATION FOR MACRO IDE_DPTR +; SET HL TO ADDRESS OF FIELD WITHIN PER UNIT DATA +; HL := ADR OF IDE_UNITDATA[(IDE_UNIT)][(SP)] +; ENTER WITH TOP-OF-STACK = ADDRESS OF FIELD OFFSET +; AF IS TRASHED +; +IDE_DPTRIMP: + LD HL,IDE_UDATA ; POINT TO START OF UNIT DATA ARRAY + LD A,(IDE_UNIT) ; GET CURRENT UNIT NUM + RLCA ; MULTIPLY BY + RLCA ; ... SIZE OF PER UNIT DATA + RLCA ; ... (8 BYTES) + EX (SP),HL ; GET PTR TO FIELD OFFSET VALUE FROM TOS + ADD A,(HL) ; ADD IT TO START OF UNIT DATA IN ACCUM + INC HL ; BUMP HL TO NEXT REAL INSTRUCTION + EX (SP),HL ; AND PUT IT BACK ON STACK, HL GETS ADR OF START OF DATA + JP ADDHLA ; CALC FINAL ADR IN HL AND RETURN diff --git a/Source/HBIOS/sd.asm b/Source/HBIOS/sd.asm index 38782356..70ed9341 100644 --- a/Source/HBIOS/sd.asm +++ b/Source/HBIOS/sd.asm @@ -1,6 +1,6 @@ ; ;============================================================================= -; MMC/SD/SDHC/SDXC CARD STORAGE DRIVER +; MMC/SD/SDHC/SDXC CARD STORAGE DRIVER ;============================================================================= ; ; 1) TESTING @@ -51,98 +51,109 @@ ; ;------------------------------------------------------------------------------ ; -; ALL COMMAND RESPONSES START WITH R1 DEFINED AS FOLLOWS: -; -; R1 RESPONSE CODE: -; -; 7 6 5 4 3 2 1 0 -; +---+---+---+---+---+---+---+---+ -; | 0 | X | X | X | X | X | X | X | -; +---+---+---+---+---+---+---+---+ -; | | | | | | | -; | | | | | | +--- IDLE -; | | | | | +------- ERASE RESET -; | | | | +----------- ILLEGAL COMMAND -; | | | +--------------- COM CRC ERROR -; | | +------------------- ERASE SEQUENCE ERROR -; | +----------------------- ADDRESS ERROR -; +--------------------------- PARAMETER ERROR -; -#IF (SDMODE == SDMODE_JUHA) ; JUHA MINI-BOARD -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION -SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE??? -SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC -SD_CS .EQU %00000100 ; RTC:2 IS SELECT -SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK -SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) -SD_DO .EQU %10000000 ; RTC:7 IS DATA OUT (CARD -> CPU) +; === R1 RESPONSE === +; ALL COMMAND RESPONSES START WITH R1 +; +; 7 6 5 4 3 2 1 0 +; +---+---+---+---+---+---+---+---+ +; | 0 | X | X | X | X | X | X | X | +; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +; | | | | | | | +; | | | | | | +--- IDLE +; | | | | | +------- ERASE RESET +; | | | | +----------- ILLEGAL COMMAND +; | | | +--------------- COM CRC ERROR +; | | +------------------- ERASE SEQUENCE ERROR +; | +----------------------- ADDRESS ERROR +; +--------------------------- PARAMETER ERROR +; +; === DATA ERROR TOKEN === +; +; 7 6 5 4 3 2 1 0 +; +---+---+---+---+---+---+---+---+ +; | 0 | 0 | 0 | 0 | X | X | X | X | +; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +; | | | | +; | | | +--- ERROR - GENERAL OR UNKNOWN ERROR +; | | +------- CC ERROR - INTERNAL CARD CONTROLER ERROR +; | +----------- CARD ECC FAILED - CARD INTERNAL ECC FAILED TO CORRECT DATA +; +--------------- OUT OF RANGE - PARAMAETER OUT OF RANGE ALLOWED FOR CARD +; +#IF (SDMODE == SDMODE_JUHA) ; JUHA MINI-BOARD +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION +SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE??? +SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC +SD_CS .EQU %00000100 ; RTC:2 IS SELECT +SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK +SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) +SD_DO .EQU %10000000 ; RTC:7 IS DATA OUT (CARD -> CPU) #ENDIF ; -#IF (SDMODE == SDMODE_N8) ; UNMODIFIED N8-2511 -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION -SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE??? -SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC -SD_CS .EQU %00000100 ; RTC:2 IS SELECT -SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK -SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) -SD_DO .EQU %01000000 ; RTC:6 IS DATA OUT (CARD -> CPU) +#IF (SDMODE == SDMODE_N8) ; UNMODIFIED N8-2511 +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION +SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE??? +SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC +SD_CS .EQU %00000100 ; RTC:2 IS SELECT +SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK +SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) +SD_DO .EQU %01000000 ; RTC:6 IS DATA OUT (CARD -> CPU) #ENDIF ; -#IF (SDMODE == SDMODE_CSIO) ; N8-2312 -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION -SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE -SD_CS .EQU %00000100 ; RTC:2 IS SELECT +#IF (SDMODE == SDMODE_CSIO) ; N8-2312 +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION +SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE +SD_CS .EQU %00000100 ; RTC:2 IS SELECT SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR #ENDIF ; -#IF (SDMODE == SDMODE_PPI) ; PPISD -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_PPIBASE .EQU PPIBASE ; BASE IO PORT FOR PPI -SD_PPIB .EQU PPIBASE + 1 ; PPI PORT B (INPUT: DOUT) -SD_PPIC .EQU PPIBASE + 2 ; PPI PORT C (OUTPUT: CS, CLK, DIN) -SD_PPIX .EQU PPIBASE + 3 ; PPI CONTROL PORT -SD_OPRREG .EQU SD_PPIC ; PPI PORT C IS OPR REG -SD_OPRDEF .EQU %00110001 ; CS HI, DI HI -SD_INPREG .EQU SD_PPIB ; INPUT REGISTER IS PPI PORT B -SD_CS .EQU %00010000 ; PPIC:4 IS SELECT -SD_CLK .EQU %00000010 ; PPIC:1 IS CLOCK -SD_DI .EQU %00000001 ; PPIC:0 IS DATA IN (CARD <- CPU) -SD_DO .EQU %10000000 ; PPIB:7 IS DATA OUT (CARD -> CPU) +#IF (SDMODE == SDMODE_PPI) ; PPISD +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_PPIBASE .EQU PPIBASE ; BASE IO PORT FOR PPI +SD_PPIB .EQU PPIBASE + 1 ; PPI PORT B (INPUT: DOUT) +SD_PPIC .EQU PPIBASE + 2 ; PPI PORT C (OUTPUT: CS, CLK, DIN) +SD_PPIX .EQU PPIBASE + 3 ; PPI CONTROL PORT +SD_OPRREG .EQU SD_PPIC ; PPI PORT C IS OPR REG +SD_OPRDEF .EQU %00110001 ; CS HI, DI HI +SD_INPREG .EQU SD_PPIB ; INPUT REGISTER IS PPI PORT B +SD_CS .EQU %00010000 ; PPIC:4 IS SELECT +SD_CLK .EQU %00000010 ; PPIC:1 IS CLOCK +SD_DI .EQU %00000001 ; PPIC:0 IS DATA IN (CARD <- CPU) +SD_DO .EQU %10000000 ; PPIB:7 IS DATA OUT (CARD -> CPU) #ENDIF ; #IF (SDMODE == SDMODE_UART) -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN) -SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE -SD_INPREG .EQU SIO_MSR ; INPUT REGISTER IS MSR -SD_CS .EQU %00001000 ; UART MCR:3 IS SELECT -SD_CLK .EQU %00000100 ; UART MCR:2 IS CLOCK -SD_DI .EQU %00000001 ; UART MCR:0 IS DATA IN (CARD <- CPU) -SD_DO .EQU %00100000 ; UART MSR:5 IS DATA OUT (CARD -> CPU) +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN) +SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE +SD_INPREG .EQU SIO_MSR ; INPUT REGISTER IS MSR +SD_CS .EQU %00001000 ; UART MCR:3 IS SELECT +SD_CLK .EQU %00000100 ; UART MCR:2 IS CLOCK +SD_DI .EQU %00000001 ; UART MCR:0 IS DATA IN (CARD <- CPU) +SD_DO .EQU %00100000 ; UART MSR:5 IS DATA OUT (CARD -> CPU) #ENDIF ; -#IF (SDMODE == SDMODE_DSD) ; DUAL SD -SD_UNITCNT .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU $08 ; DEDICATED OPERATIONS REGISTER -SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE -SD_INPREG .EQU SD_OPRREG ; INPUT REGISTER IS OPRREG -SD_SELREG .EQU SD_OPRREG + 1 ; DEDICATED SELECTION REGISTER -SD_SELDEF .EQU %00000000 ; SELECTION REGISTER DEFAULT -SD_CS .EQU %00000100 ; RTC:2 IS SELECT -SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK -SD_DI .EQU %00000001 ; RTC:6 IS DATA IN (CARD <- CPU) -SD_DO .EQU %00000001 ; RTC:0 IS DATA OUT (CARD -> CPU) +#IF (SDMODE == SDMODE_DSD) ; DUAL SD +SD_UNITCNT .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU $08 ; DEDICATED OPERATIONS REGISTER +SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE +SD_INPREG .EQU SD_OPRREG ; INPUT REGISTER IS OPRREG +SD_SELREG .EQU SD_OPRREG + 1 ; DEDICATED SELECTION REGISTER +SD_SELDEF .EQU %00000000 ; SELECTION REGISTER DEFAULT +SD_CS .EQU %00000100 ; RTC:2 IS SELECT +SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK +SD_DI .EQU %00000001 ; RTC:6 IS DATA IN (CARD <- CPU) +SD_DO .EQU %00000001 ; RTC:0 IS DATA OUT (CARD -> CPU) #ENDIF ; -#IF (SDMODE == SDMODE_MK4) ; MARK IV (CSIO STYLE INTERFACE) -SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) -SD_OPRREG .EQU MK4_SD ; DEDICATED MK4 SDCARD REGISTER -SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE -SD_CS .EQU %00000100 ; SELECT ACTIVE +#IF (SDMODE == SDMODE_MK4) ; MARK IV (CSIO STYLE INTERFACE) +SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) +SD_OPRREG .EQU MK4_SD ; DEDICATED MK4 SDCARD REGISTER +SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE +SD_CS .EQU %00000100 ; SELECT ACTIVE SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR #ENDIF @@ -167,38 +178,40 @@ SD_ACMD_SEND_SCR .EQU $40 + 51 ; $73, ACMD51 -> R1 ; ; SD CARD TYPE ; -SD_TYPEUNK .EQU 0 ; CARD TYPE UNKNOWN/UNDETERMINED -SD_TYPEMMC .EQU 1 ; MULTIMEDIA CARD (MMC STANDARD) -SD_TYPESDSC .EQU 2 ; SDSC CARD (V1) -SD_TYPESDHC .EQU 3 ; SDHC CARD (V2) -SD_TYPESDXC .EQU 4 ; SDXC CARD (V3) +SD_TYPEUNK .EQU 0 ; CARD TYPE UNKNOWN/UNDETERMINED +SD_TYPEMMC .EQU 1 ; MULTIMEDIA CARD (MMC STANDARD) +SD_TYPESDSC .EQU 2 ; SDSC CARD (V1) +SD_TYPESDHC .EQU 3 ; SDHC CARD (V2) +SD_TYPESDXC .EQU 4 ; SDXC CARD (V3) ; ; SD CARD STATUS (SD_STAT) ; -SD_STOK .EQU 0 ; OK -SD_STNOTRDY .EQU -1 ; NOT READY (INITIALIZATION PENDING) -SD_STRDYTO .EQU -2 ; TIMEOUT WAITING FOR CARD TO BE READY -SD_STINITTO .EQU -3 ; INITIALIZATOIN TIMEOUT -SD_STCMDTO .EQU -4 ; TIMEOUT WAITING FOR COMMAND RESPONSE -SD_STCMDERR .EQU -5 ; COMMAND ERROR OCCURRED (REF SD_RC) -SD_STDATAERR .EQU -6 ; DATA ERROR OCCURRED (REF SD_TOK) -SD_STDATATO .EQU -7 ; DATA TRANSFER TIMEOUT -SD_STCRCERR .EQU -8 ; CRC ERROR ON RECEIVED DATA PACKET -SD_STNOMEDIA .EQU -9 ; NO MEDIA IN CONNECTOR -SD_STWRTPROT .EQU -10 ; ATTEMPT TO WRITE TO WRITE PROTECTED MEDIA +SD_STOK .EQU 0 ; OK +SD_STINVUNIT .EQU -1 ; INVALID UNIT +SD_STRDYTO .EQU -2 ; TIMEOUT WAITING FOR CARD TO BE READY +SD_STINITTO .EQU -3 ; INITIALIZATOIN TIMEOUT +SD_STCMDTO .EQU -4 ; TIMEOUT WAITING FOR COMMAND RESPONSE +SD_STCMDERR .EQU -5 ; COMMAND ERROR OCCURRED (REF SD_RC) +SD_STDATAERR .EQU -6 ; DATA ERROR OCCURRED (REF SD_TOK) +SD_STDATATO .EQU -7 ; DATA TRANSFER TIMEOUT +SD_STCRCERR .EQU -8 ; CRC ERROR ON RECEIVED DATA PACKET +SD_STNOMEDIA .EQU -9 ; NO MEDIA IN CONNECTOR +SD_STWRTPROT .EQU -10 ; ATTEMPT TO WRITE TO WRITE PROTECTED MEDIA ; ; PER UNIT DATA OFFSETS (CAREFUL NOT TO EXCEED PER UNIT SPACE IN SD_UNITDATA) ; SEE SD_UNITDATA IN DATA STORAGE BELOW ; -SD_STAT .EQU 0 ; LAST STATUS (1 BYTE) -SD_TYPE .EQU 1 ; CARD TYPE (1 BYTE) -SD_CAPACITY .EQU 2 ; CARD CAPACITY (1 DWORD/4 BYTES) +SD_STAT .EQU 0 ; LAST STATUS (1 BYTE) +SD_TYPE .EQU 1 ; CARD TYPE (1 BYTE) +SD_CAPACITY .EQU 2 ; CARD CAPACITY (1 DWORD/4 BYTES) ; ; MACRO TO RETURN POINTER TO FIELD WITHIN UNIT DATA ; #DEFINE SD_DPTR(FIELD) CALL SD_DPTRIMP \ .DB FIELD ; -; SD DRIVER INITIALIZATION ENTRY POINT +;============================================================================= +; INITIALIZATION ENTRY POINT +;============================================================================= ; SD_INIT: PRTS("SD:$") @@ -281,8 +294,8 @@ SD_INIT: CALL PRTDECB ; ; INITIALIZE THE SD INTERFACE NOW - CALL SD_SETUP ; DO HARDWARE SETUP/INIT - RET NZ ; ABORT ON ERROR + CALL SD_SETUP ; DO HARDWARE SETUP/INIT + RET NZ ; ABORT ON ERROR ; ; CLEAR OUT ALL DATA (FOR ALL UNITS) LD HL,SD_UNITDATA @@ -291,70 +304,66 @@ SD_INIT: CALL FILL ; ; INITIALIZE INDIVIDUAL UNIT(S) - LD B,SD_UNITCNT ; INIT LOOP COUNTER TO UNIT COUNT - LD C,0 ; INIT UNIT INDEX TO ZERO + LD B,SD_UNITCNT ; INIT LOOP COUNTER TO UNIT COUNT + LD C,0 ; INIT UNIT INDEX TO ZERO SD_INIT1: - PUSH BC ; SAVE LOOP COUNTER/INDEX - LD A,C ; UNIT ID TO A - CALL SD_INITUNIT ; INITIALIZE IT - POP BC ; RESTORE LOOP COUNTER/INDEX - INC C ; INCREMENT UNIT INDEX - DJNZ SD_INIT1 ; DECREMENT LOOP COUNTER AND LOOP AS NEEDED + PUSH BC ; SAVE LOOP COUNTER/INDEX + LD A,C ; UNIT ID TO A + CALL SD_INITUNIT ; INITIALIZE IT +#IF (SDTRACE < 2) + CALL NZ,SD_PRTSTAT ; IF ERROR, SHOW IT +#ENDIF + POP BC ; RESTORE LOOP COUNTER/INDEX + INC C ; INCREMENT UNIT INDEX + DJNZ SD_INIT1 ; DECREMENT LOOP COUNTER AND LOOP AS NEEDED ; - RET ; DONE + RET ; DONE ; ; INITIALIZE UNIT DESIGNATED IN ACCUM ; SD_INITUNIT: - ; SELECT UNIT - CALL SD_SELUNIT ; SELECT UNIT - JP NZ,SD_PRTERR ; EXIT VIA PRINT STATUS + CALL SD_SELUNIT ; SELECT UNIT + RET NZ ; ABORT ON ERROR ; - ; CLEAR OUT UNIT SPECIFIC DATA - SD_DPTR(0) ; SET HL TO START OF UNIT DATA - LD BC,SD_UNITDATALEN - XOR A - CALL FILL + CALL SD_INITCARD ; INIT THE SELECTED CARD + RET NZ ; ABORT ON ERROR ; - ; INIT CARD - CALL SD_INITCARD ; INIT THE SELECTED CARD - CALL SD_PRTPREFIX ; - JP NZ,SD_PRTSTAT ; EXIT VIA PRINT STATUS + CALL SD_PRTPREFIX ; ; PRINT CARD TYPE PRTS(" TYPE=$") - SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF + SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF LD A,(HL) - LD DE,SD_STRTYPEMMC + LD DE,SD_STR_TYPEMMC CP SD_TYPEMMC JR Z,SD_INITUNIT1 - LD DE,SD_STRTYPESDSC + LD DE,SD_STR_TYPESDSC CP SD_TYPESDSC JR Z,SD_INITUNIT1 - LD DE,SD_STRTYPESDHC + LD DE,SD_STR_TYPESDHC CP SD_TYPESDHC JR Z,SD_INITUNIT1 - LD DE,SD_STRTYPESDXC + LD DE,SD_STR_TYPESDXC CP SD_TYPESDXC JR Z,SD_INITUNIT1 - LD DE,SD_STRTYPEUNK + LD DE,SD_STR_TYPEUNK SD_INITUNIT1: CALL WRITESTR ; GET CID (WHICH CONTAINS PRODUCT NAME) - LD A,SD_CMD_SEND_CID ; SEND_CID - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_EXECCMD ; RUN COMMAND - RET NZ ; ABORT ON ERROR - LD BC,16 ; 16 BYTES OF CID + LD A,SD_CMD_SEND_CID ; SEND_CID + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_EXECCMD ; RUN COMMAND + RET NZ ; ABORT ON ERROR + LD BC,16 ; 16 BYTES OF CID LD HL,SD_BUF CALL SD_GETDATA CALL SD_DONE - JP NZ,SD_ERRDATA ; DATA XFER ERROR + JP NZ,SD_ERRDATA ; DATA XFER ERROR -#IF (SDTRACE >= 2) +#IF (SDTRACE >= 3) CALL SD_PRTPREFIX - LD DE,SD_STRCID + LD DE,SD_STR_CID CALL WRITESTR LD DE,SD_BUF LD A,16 @@ -362,49 +371,45 @@ SD_INITUNIT1: #ENDIF ; PRINT PRODUCT NAME - PRTS(" NAME=$") ; PRINT LABEL - LD B,5 ; PREPARE TO PRINT 5 BYTES - LD HL,SD_BUF + 3 ; AT BYTE OFFSET 3 IN RESULT BUFFER + PRTS(" NAME=$") ; PRINT LABEL + LD B,5 ; PREPARE TO PRINT 5 BYTES + LD HL,SD_BUF + 3 ; AT BYTE OFFSET 3 IN RESULT BUFFER SD_INITUNIT2: - LD A,(HL) ; GET NEXT BYTE - CALL COUT ; PRINT IT - INC HL ; POINT TO NEXT BYTE - DJNZ SD_INITUNIT2 ; LOOP FOR ALL 5 BYTES + LD A,(HL) ; GET NEXT BYTE + CALL COUT ; PRINT IT + INC HL ; POINT TO NEXT BYTE + DJNZ SD_INITUNIT2 ; LOOP FOR ALL 5 BYTES ; - PRTS(" BLOCKS=0x$") ; LABEL - SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF - CALL LD32 ; GET THE VALUE - CALL PRTHEX32 ; PRINT IT + PRTS(" BLOCKS=0x$") ; LABEL + SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF + CALL LD32 ; GET THE VALUE + CALL PRTHEX32 ; PRINT IT ; ; CONVERT VALUE TO MEGABYTES AND PRINT IT - LD B,11 ; SHIFT 11 BITS TO CONVERT FROM - CALL SRL32 ; ... 512 BYTE BLOCKS TO MEGABYTES + LD B,11 ; SHIFT 11 BITS TO CONVERT FROM + CALL SRL32 ; ... 512 BYTE BLOCKS TO MEGABYTES ; - PRTS(" SIZE=$") ; PRINT LABEL - CALL PRTDEC ; PRINT VALUE - PRTS("MB$") ; PRINT SUFFIX + PRTS(" SIZE=$") ; PRINT LABEL + CALL PRTDEC ; PRINT VALUE + PRTS("MB$") ; PRINT SUFFIX ; ; CHECK FOR WRITE PROTECT AND NOTIFY USER IF SO - CALL SD_CHKWP ; WRITE PROTECTED? - JR Z,SD_INITUNIT3 ; NOPE, BYPASS - CALL PC_SPACE ; SEPARATOR - CALL NZ,SD_PRTSTAT - ; WE DON'T EXIT HERE SINCE WRITE PROTECTION IS NOT A FATAL ERROR + CALL SD_CHKWP ; WRITE PROTECTED? + RET Z ; IF NOT, DONE + PRTS(" WP$") ; NOTIFY USER + RET ; DONE ; -SD_INITUNIT3: - SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED - XOR A ; SIGNAL SUCCESS - LD A,(HL) ; GET STATUS OF SELECTED UNIT - RET ; RETURN WITH A=0, AND Z SET -; -; SD DRIVER FUNCTION DISPATCH ENTRY POINT +;============================================================================= +; FUNCTION DISPATCH ENTRY POINT +;============================================================================= ; SD_DISPATCH: - LD A,C ; DEVICE/UNIT TO A - AND $0F ; REMOVE DEVICE BITS LEAVING JUST UNIT ID - CALL SD_SELUNIT ; SELECT DESIRED UNIT - LD A,B ; GET REQUESTED FUNCTION - AND $0F ; ISOLATE SUB-FUNCTION BITS + LD A,C ; DEVICE/UNIT TO A + AND $0F ; REMOVE DEVICE BITS LEAVING JUST UNIT ID + CALL SD_SELUNIT ; SELECT DESIRED UNIT + RET NZ ; ABORT ON ERROR + LD A,B ; GET REQUESTED FUNCTION + AND $0F ; ISOLATE SUB-FUNCTION BITS JP Z,SD_READ DEC A JP Z,SD_WRITE @@ -419,102 +424,105 @@ SD_DISPATCH: CALL PANIC ; SD_READ: +#IF (SDTRACE == 1) + LD HL,SD_PRTERR ; SET UP SD_PRTERR + PUSH HL ; ... TO FILTER ALL EXITS +#ENDIF ; READ A SECTOR - LD C,SD_CMD_READ_SNGL_BLK ; SET READ_SINGLE_BLOCK COMMAND - CALL SD_SECTIO ; DO SECTOR I/O - RET Z ; NO ERRORS, DONE - JP SD_PRTERR ; RETURN VIA PRINT ERROR + LD C,SD_CMD_READ_SNGL_BLK ; SET READ_SINGLE_BLOCK COMMAND + JP SD_SECTIO ; DO SECTOR I/O ; SD_WRITE: +#IF (SDTRACE == 1) + LD HL,SD_PRTERR ; SET UP SD_PRTERR + PUSH HL ; ... TO FILTER ALL EXITS +#ENDIF ; WRITE A SECTOR - CALL SD_CHKWP ; CHECK FOR WRITE PROTECT - JP NZ,SD_PRTERR ; IF PROTECTED, EXIT VIA PRINT ERROR - LD C,SD_CMD_WRITE_BLOCK ; SET WRITE_BLOCK COMMAND - CALL SD_SECTIO ; DO SECTOR I/O - RET Z ; NO ERRORS, DONE - JP SD_PRTERR ; RETURN VIA PRINT ERROR + CALL SD_CHKWP ; CHECK FOR WRITE PROTECT + JP NZ,SD_WRTPROT ; HANDLE IT IF SO + LD C,SD_CMD_WRITE_BLOCK ; SET WRITE_BLOCK COMMAND + JP SD_SECTIO ; DO SECTOR I/O ; SD_STATUS: ; RETURN UNIT STATUS - SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED - LD A,(HL) ; GET STATUS OF SELECTED UNIT - OR A ; SET FLAGS - RET ; AND RETURN + SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED + LD A,(HL) ; GET STATUS OF SELECTED UNIT + OR A ; SET FLAGS + RET ; AND RETURN ; SD_MEDIA: ; RE-INITIALIZE THE SD CARD TO ACCOMMODATE HOT SWAPPING - CALL SD_INITCARD ; RE-INIT SELECTED UNIT - LD A,MID_HD ; ASSUME SUCCESS, SETUP MEDIA ID - RET Z ; RETURN IF GOOD INIT - CALL SD_PRTERR ; FAILURE, PRINT ERROR - LD A,MID_NONE ; SIGNAL NO MEDA - OR A ; SET FLAGS - RET ; AND RETURN + CALL SD_INITCARD ; RE-INIT SELECTED UNIT + LD A,MID_HD ; ASSUME SUCCESS, SETUP MEDIA ID + RET Z ; RETURN IF GOOD INIT + LD A,MID_NONE ; SIGNAL NO MEDA + OR A ; SET FLAGS + RET ; AND RETURN ; SD_CAP: - SD_DPTR(SD_CAPACITY) ; POINT HL TO CAPACITY OF CUR UNIT - CALL LD32 ; GET THE CURRENT CAPACITY DO DE:HL - LD BC,512 ; 512 BYTES PER BLOCK - XOR A ; SIGNAL SUCCESS - RET ; AND DONE + SD_DPTR(SD_CAPACITY) ; POINT HL TO CAPACITY OF CUR UNIT + CALL LD32 ; GET THE CURRENT CAPACITY DO DE:HL + LD BC,512 ; 512 BYTES PER BLOCK + XOR A ; SIGNAL SUCCESS + RET ; AND DONE ; SD_GEOM: ; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS ; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE - CALL SD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC - LD L,H ; DIVIDE BY 256 FOR # TRACKS - LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL - LD D,16 | $80 ; HEADS / CYL = 16, SET LBA BIT - LD E,16 ; SECTORS / TRACK = 16 - XOR A ; SIGNAL SUCCESS + CALL SD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC + LD L,H ; DIVIDE BY 256 FOR # TRACKS + LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL + LD D,16 | $80 ; HEADS / CYL = 16, SET LBA BIT + LD E,16 ; SECTORS / TRACK = 16 + XOR A ; SIGNAL SUCCESS RET ; ;============================================================================= -; SD I/O ROUTINES +; FUNCTION SUPPORT ROUTINES ;============================================================================= ; ; (RE)INITIALIZE CARD ; SD_INITCARD: ; -#IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4)) - IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER - BIT 5,A ; TEST CARD DETECT BIT -#IF (SDTRACE >= 2) - CALL Z,SD_PRTPREFIX -#ENDIF - JP Z,SD_NOMEDIA ; NO MEDIA DETECTED IF ZERO -#ENDIF + ; CLEAR OUT UNIT SPECIFIC DATA + SD_DPTR(0) ; SET HL TO START OF UNIT DATA + LD BC,SD_UNITDATALEN + XOR A + CALL FILL +; + CALL SD_CHKCD ; CHECK CARD DETECT + JP Z,SD_NOMEDIA ; Z=NO MEDIA, HANDLE IF SO ; ; WAKE UP THE CARD, KEEP DIN HI (ASSERTED) AND /CS HI (DEASSERTED) LD B,$10 ; MIN 74 CLOCKS REQUIRED, WE USE 128 ($10 * 8) SD_INITCARD1: - LD A,$FF ; KEEP DIN HI - PUSH BC ; SAVE LOOP CONTROL - CALL SD_PUT ; SEND 8 CLOCKS - POP BC ; RESTORE LOOP CONTROL - DJNZ SD_INITCARD1 ; LOOP AS NEEDED + LD A,$FF ; KEEP DIN HI + PUSH BC ; SAVE LOOP CONTROL + CALL SD_PUT ; SEND 8 CLOCKS + POP BC ; RESTORE LOOP CONTROL + DJNZ SD_INITCARD1 ; LOOP AS NEEDED ; ; PUT CARD IN IDLE STATE - CALL SD_GOIDLE ; GO TO IDLE - RET NZ ; ABORT IF FAILED + CALL SD_GOIDLE ; GO TO IDLE + RET NZ ; ABORT IF FAILED ; SD_INITCARD2: - SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF - LD A,SD_TYPESDSC ; ASSUME SDSC CARD TYPE - LD (HL),A ; SAVE IT + SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF + LD A,SD_TYPESDSC ; ASSUME SDSC CARD TYPE + LD (HL),A ; SAVE IT ; ; CMD8 IS REQUIRED FOR V2 CARDS. FAILURE HERE IS OK AND ; JUST MEANS THAT IT IS A V1 CARD - LD A,SD_CMD_SEND_IF_COND ; SEND_IF_COND - CALL SD_INITCMD ; SETUP COMMAND BUFFER - LD HL,SD_CMDP2 ; POINT TO 3RD PARM BYTE - LD (HL),1 ; VHS=1, 2.7-3.6V - INC HL ; POINT TO 4TH PARM BYTE - LD (HL),$AA ; CHECK PATTERN - INC HL ; POINT TO CRC - LD (HL),$87 ; ... AND SET IT TO KNOWN VALUE OF $87 - CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED + LD A,SD_CMD_SEND_IF_COND ; SEND_IF_COND + CALL SD_INITCMD ; SETUP COMMAND BUFFER + LD HL,SD_CMDP2 ; POINT TO 3RD PARM BYTE + LD (HL),1 ; VHS=1, 2.7-3.6V + INC HL ; POINT TO 4TH PARM BYTE + LD (HL),$AA ; CHECK PATTERN + INC HL ; POINT TO CRC + LD (HL),$87 ; ... AND SET IT TO KNOWN VALUE OF $87 + CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED ; ; GET CARD OUT OF IDLE STATE BY SENDING SD_APP_OP_COND ; REPEATEDLY UNTIL IDLE BIT IS CLEAR @@ -522,30 +530,30 @@ SD_INITCARD2: LD (SD_LCNT),A SD_INITCARD3: ; DELAY A BIT PER SPEC - LD DE,300 ; 16US * 300 = ~5MS - CALL VDELAY ; CPU SPEED NORMALIZED DELAY + LD DE,300 ; 16US * 300 = ~5MS + CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; SEND APP CMD INTRODUCER - CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER - CP SD_STCMDERR ; COMMAND ERROR? - JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT - OR A ; SET FLAGS - RET NZ ; ABORT IF ANY OTHER ERROR + CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER + CP SD_STCMDERR ; COMMAND ERROR? + JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT + OR A ; SET FLAGS + RET NZ ; ABORT IF ANY OTHER ERROR ; SEND APP_OP_COND - LD A,SD_ACMD_SEND_OP_COND ; SD_APP_OP_COND - CALL SD_INITCMD ; SETUP COMMAND BUFFER - LD A,$40 ; P0 = $40 INDICATES WE SUPPORT V2 CARDS - LD (SD_CMDP0),A ; SET COMMAND PARM 0 - CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED - RET NZ ; ABORT ON ERROR + LD A,SD_ACMD_SEND_OP_COND ; SD_APP_OP_COND + CALL SD_INITCMD ; SETUP COMMAND BUFFER + LD A,$40 ; P0 = $40 INDICATES WE SUPPORT V2 CARDS + LD (SD_CMDP0),A ; SET COMMAND PARM 0 + CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED + RET NZ ; ABORT ON ERROR ; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED - LD A,(SD_RC) ; GET CARD RESULT CODE - OR A ; SET FLAGS - JR Z,SD_INITCARD4 ; IF IDLE BIT CLEAR, EXIT LOOP + LD A,(SD_RC) ; GET CARD RESULT CODE + OR A ; SET FLAGS + JR Z,SD_INITCARD4 ; IF IDLE BIT CLEAR, EXIT LOOP ; LOOP AS NEEDED - LD HL,SD_LCNT ; POINT TO LOOP COUNTER - DEC (HL) ; DECREMENT LOOP COUNTER - JR NZ,SD_INITCARD3 ; LOOP UNTIL COUNTER EXHAUSTED - JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR + LD HL,SD_LCNT ; POINT TO LOOP COUNTER + DEC (HL) ; DECREMENT LOOP COUNTER + JR NZ,SD_INITCARD3 ; LOOP UNTIL COUNTER EXHAUSTED + JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR ; SD_INITCARD3A: ; TRY MMC CARD INITIALIZATION @@ -554,62 +562,62 @@ SD_INITCARD3A: LD (SD_LCNT),A SD_INITCARD3B: ; DELAY A BIT PER SPEC - LD DE,300 ; 16US * 300 = ~5MS - CALL VDELAY ; CPU SPEED NORMALIZED DELAY + LD DE,300 ; 16US * 300 = ~5MS + CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; SEND OP_COND COMMAND - LD A,SD_CMD_SEND_OP_COND ; SD_OP_COND - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_EXECCMDND ; EXEC COMMAND WITH NO DATA - RET NZ ; ABORT ON ERROR + LD A,SD_CMD_SEND_OP_COND ; SD_OP_COND + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_EXECCMDND ; EXEC COMMAND WITH NO DATA + RET NZ ; ABORT ON ERROR ; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED - LD A,(SD_RC) ; GET CARD RESULT CODE - OR A ; SET FLAGS - JR Z,SD_INITCARD3C ; IDLE BIT CLEAR, EXIT LOOP + LD A,(SD_RC) ; GET CARD RESULT CODE + OR A ; SET FLAGS + JR Z,SD_INITCARD3C ; IDLE BIT CLEAR, EXIT LOOP ; LOOP AS NEEDED - LD HL,SD_LCNT ; POINT TO LOOP COUNTER - DEC (HL) ; DECREMENT LOOP COUNTER - JR NZ,SD_INITCARD3B ; LOOP UNTIL COUNTER EXHAUSTED - JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR + LD HL,SD_LCNT ; POINT TO LOOP COUNTER + DEC (HL) ; DECREMENT LOOP COUNTER + JR NZ,SD_INITCARD3B ; LOOP UNTIL COUNTER EXHAUSTED + JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR ; SD_INITCARD3C: ; SUCCESSFUL MMC CARD INITIALIZATION - LD C,SD_TYPEMMC ; MMC CARD TYPE - JR SD_INITCARD5 ; RESUME FLOW + LD C,SD_TYPEMMC ; MMC CARD TYPE + JR SD_INITCARD5 ; RESUME FLOW ; SD_INITCARD4: ; CMD58 RETURNS THE 32 BIT OCR REGISTER (R3), WE WANT TO CHECK ; BIT 30, IF SET THIS IS SDHC/XC CARD - LD A,SD_CMD_READ_OCR ; READ_OCR - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_EXECCMD ; EXECUTE COMMAND - RET NZ ; ABORT ON ERROR + LD A,SD_CMD_READ_OCR ; READ_OCR + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_EXECCMD ; EXECUTE COMMAND + RET NZ ; ABORT ON ERROR ; CMD58 WORKED, GET OCR DATA AND SET CARD TYPE - CALL SD_GET ; BITS 31-24 - CALL SD_DONE ; FINISH THE TRANSACTION - AND $40 ; ISOLATE BIT 30 (CCS) - LD C,SD_TYPESDSC ; ASSUME V1 CARD - JR Z,SD_INITCARD5 ; IF BIT NOT SET, THIS IS SDSC CARD + CALL SD_GET ; BITS 31-24 + CALL SD_DONE ; FINISH THE TRANSACTION + AND $40 ; ISOLATE BIT 30 (CCS) + LD C,SD_TYPESDSC ; ASSUME V1 CARD + JR Z,SD_INITCARD5 ; IF BIT NOT SET, THIS IS SDSC CARD ; SD_INITCARD4A: ; ACMD51 RETURNS THE 64 BIT SCR REGISTER (ONLY AVAILABLE ON SDSC AND ABOVE) ; SD_SPEC3 (BIT 47) IS SET IF CARD IS SDXC OR GREATER - CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER - RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) - LD A,SD_ACMD_SEND_SCR ; APP CMD SEND_SCR - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_EXECCMD ; EXECUTE COMMAND - RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) + CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER + RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) + LD A,SD_ACMD_SEND_SCR ; APP CMD SEND_SCR + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_EXECCMD ; EXECUTE COMMAND + RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) ; ACMD51 SUCCEEDED, NOW GET THE SCR REGISTER CONTENTS - LD BC,8 ; 8 BYTES OF SCR - LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER - CALL SD_GETDATA ; GET THE DATA - CALL SD_DONE ; CLOSE THE TRANSACTION - JP NZ,SD_ERRDATA ; DATA XFER ERROR + LD BC,8 ; 8 BYTES OF SCR + LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER + CALL SD_GETDATA ; GET THE DATA + CALL SD_DONE ; CLOSE THE TRANSACTION + JP NZ,SD_ERRDATA ; DATA XFER ERROR ; -#IF (SDTRACE >= 2) +#IF (SDTRACE >= 3) ; IF TRACING, DUMP THE SCR CONTENTS CALL SD_PRTPREFIX - LD DE,SD_STRSCR + LD DE,SD_STR_SCR CALL WRITESTR LD DE,SD_BUF LD A,8 @@ -617,59 +625,59 @@ SD_INITCARD4A: #ENDIF ; ; EXTRACT THE SD_SECURITY FIELD AND SET SDHC/SDXC BASED ON VALUE - LD A,(SD_BUF + 1) ; GET THIRD BYTE (BITS 47-40) (55-48) - AND %01110000 ; ISOLATE SD_SECURITY BITS - CP $40 ; CHECK FOR SDXC VALUE - LD C,SD_TYPESDHC ; ASSUME CARD TYPE = SDHC - JR NZ,SD_INITCARD5 ; IF NOT SDXC, DONE - LD C,SD_TYPESDXC ; OTHERWISE, THIS IS SDXC CARD + LD A,(SD_BUF + 1) ; GET THIRD BYTE (BITS 47-40) (55-48) + AND %01110000 ; ISOLATE SD_SECURITY BITS + CP $40 ; CHECK FOR SDXC VALUE + LD C,SD_TYPESDHC ; ASSUME CARD TYPE = SDHC + JR NZ,SD_INITCARD5 ; IF NOT SDXC, DONE + LD C,SD_TYPESDXC ; OTHERWISE, THIS IS SDXC CARD ; SD_INITCARD5: - SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF - LD (HL),C ; SAVE IT + SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF + LD (HL),C ; SAVE IT -#IF (SDTRACE >= 2) +#IF (SDTRACE >= 3) CALL SD_PRTPREFIX - LD DE,SD_STRSDTYPE + LD DE,SD_STR_SDTYPE CALL WRITESTR LD A,C CALL PRTHEXBYTE #ENDIF ; SET OUR DESIRED BLOCK LENGTH (512 BYTES) - LD A,SD_CMD_SET_BLOCKLEN ; SET_BLOCKLEN - CALL SD_INITCMD ; SETUP COMMAND BUFFER - LD DE,512 ; 512 BYTE BLOCK LENGTH - LD HL,SD_CMDP2 ; PUT VALUE INTO PARMS - LD (HL),D ; ... HIGH WORD (P0, P1) REMAIN ZERO - INC HL ; ... VALUE OF DE GET PUT IN LOW WORD (P2, P3) - LD (HL),E ; ... BUT OBSERVE BIG ENDIAN LAYOUT - CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA - RET NZ ; ABORT ON ERROR + LD A,SD_CMD_SET_BLOCKLEN ; SET_BLOCKLEN + CALL SD_INITCMD ; SETUP COMMAND BUFFER + LD DE,512 ; 512 BYTE BLOCK LENGTH + LD HL,SD_CMDP2 ; PUT VALUE INTO PARMS + LD (HL),D ; ... HIGH WORD (P0, P1) REMAIN ZERO + INC HL ; ... VALUE OF DE GET PUT IN LOW WORD (P2, P3) + LD (HL),E ; ... BUT OBSERVE BIG ENDIAN LAYOUT + CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA + RET NZ ; ABORT ON ERROR #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4)) ; PER SPEC, THE CARD SHOULD NOW BE ABLE TO HANDLE FULL SPEED OPERATION ; SO, FOR CSIO OPERATION, WE SET CSIO TO MAXIMUM SPEED - CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING - XOR A ; ZERO MEANS MAX SPEED - OUT (Z180_CNTR),A ; NOW SET CSIO PORT + CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING + XOR A ; ZERO MEANS MAX SPEED + OUT (Z180_CNTR),A ; NOW SET CSIO PORT #ENDIF ; ; ISSUE SEND_CSD (TO DERIVE CARD CAPACITY) - LD A,SD_CMD_SEND_CSD ; SEND_CSD - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_EXECCMD ; EXECUTE COMMAND - RET NZ ; ABORT ON ERROR - LD BC,16 ; 16 BYTES OF CSD - LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER - CALL SD_GETDATA ; GET THE DATA - CALL SD_DONE ; CLOSE THE TRANSACTION - JP NZ,SD_ERRDATA ; DATA XFER ERROR -; -#IF (SDTRACE >= 2) + LD A,SD_CMD_SEND_CSD ; SEND_CSD + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_EXECCMD ; EXECUTE COMMAND + RET NZ ; ABORT ON ERROR + LD BC,16 ; 16 BYTES OF CSD + LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER + CALL SD_GETDATA ; GET THE DATA + CALL SD_DONE ; CLOSE THE TRANSACTION + JP NZ,SD_ERRDATA ; DATA XFER ERROR +; +#IF (SDTRACE >= 3) ; IF TRACING, DUMP THE CSD CONTENTS CALL SD_PRTPREFIX - LD DE,SD_STRCSD + LD DE,SD_STR_CSD CALL WRITESTR LD DE,SD_BUF LD A,16 @@ -677,155 +685,154 @@ SD_INITCARD5: #ENDIF ; ; GET SIZE OF DEVICE IN BLOCKS - SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF - LD A,(HL) ; GET CARD TYPE - OR A ; SET FLAGS - CALL Z,PANIC ; PANIC IF CARD TYPE UNKNOWN - CP SD_TYPESDHC ; COMPARE TO SDHC (V2) - JP NC,SD_INITCARD8 ; HANDLE SDHC (V2) OR BETTER - JR SD_INITCARD6 ; HANDLE MMC OR SDSC + SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF + LD A,(HL) ; GET CARD TYPE + OR A ; SET FLAGS + CALL Z,PANIC ; PANIC IF CARD TYPE UNKNOWN + CP SD_TYPESDHC ; COMPARE TO SDHC (V2) + JP NC,SD_INITCARD8 ; HANDLE SDHC (V2) OR BETTER + JR SD_INITCARD6 ; HANDLE MMC OR SDSC ; ; CAPACITY CALCULATION FOR MMC OR SDSD (V1) CARDS: ; BYTES = (C_SIZE + 1) * 2^(2+C_SIZE_MULT+READ_BL_LEN) = (C_SIZE+1) << (2+C_SIZE_MULT+READ_BL_LEN) ; BLOCKS = BYTES / 512 = BYTES >> 9 ; SD_INITCARD6: ; GET SIZE FOR V1 CARD - PUSH IX ; SAVE IX - LD IX,SD_BUF ; POINT IX TO BUFFER - LD A,(IX+6) ; GET C_SIZE MSB - AND %00000011 ; MASK OFF TOP 6 BITS (NOT PART OF C_SIZE) - LD C,A ; MSB -> C - LD D,(IX+7) ; D - LD E,(IX+8) ; LSB -> E - LD B,6 ; RIGHT SHIFT WHOLE THING BY 6 BITS + PUSH IX ; SAVE IX + LD IX,SD_BUF ; POINT IX TO BUFFER + LD A,(IX+6) ; GET C_SIZE MSB + AND %00000011 ; MASK OFF TOP 6 BITS (NOT PART OF C_SIZE) + LD C,A ; MSB -> C + LD D,(IX+7) ; D + LD E,(IX+8) ; LSB -> E + LD B,6 ; RIGHT SHIFT WHOLE THING BY 6 BITS SD_INITCARD7: - SRA C ; SHIFT MSB - RR D ; SHIFT NEXT BYTE - RR E ; SHIFT LSB - DJNZ SD_INITCARD7 ; LOOP TILL DONE - PUSH DE ; DE = C_SIZE, SAVE IT - LD A,(IX+9) ; GET C_SIZE_MULT MSB - LD B,(IX+10) ; GET C_SIZE_MULT LSB - SLA B ; SHIFT LEFT MSB - RLA ; SHIFT LEFT LSB - AND %00000111 ; ISOLATE RELEVANT BITS - LD C,A ; C := C_SIZE_MULT - LD A,(IX+5) ; GET READ_BL_LEN - AND %00001111 ; ISLOATE RELEVANT BITS - LD B,A ; B := READ_BL_LEN + SRA C ; SHIFT MSB + RR D ; SHIFT NEXT BYTE + RR E ; SHIFT LSB + DJNZ SD_INITCARD7 ; LOOP TILL DONE + PUSH DE ; DE = C_SIZE, SAVE IT + LD A,(IX+9) ; GET C_SIZE_MULT MSB + LD B,(IX+10) ; GET C_SIZE_MULT LSB + SLA B ; SHIFT LEFT MSB + RLA ; SHIFT LEFT LSB + AND %00000111 ; ISOLATE RELEVANT BITS + LD C,A ; C := C_SIZE_MULT + LD A,(IX+5) ; GET READ_BL_LEN + AND %00001111 ; ISLOATE RELEVANT BITS + LD B,A ; B := READ_BL_LEN ; FINAL MULTIPLIER IS 2^(C_SIZE_MULT + READ_BL_LEN + 2) - LD A,B ; READ_BL_LEN - ADD A,C ; AND C_SIZE_MULT - ADD A,2 ; AND 2 MORE BY DEFINITION + LD A,B ; READ_BL_LEN + ADD A,C ; AND C_SIZE_MULT + ADD A,2 ; AND 2 MORE BY DEFINITION ; RELOAD C_SIZE AND CONVERT TO 32 BIT VALUE IN DE:HL - POP HL ; RECOVE C_SIZE - INC HL ; ADD 1 - LD DE,0 ; HI WORD IS ZERO + POP HL ; RECOVE C_SIZE + INC HL ; ADD 1 + LD DE,0 ; HI WORD IS ZERO ; ADJUST TO 512 BYTE BLOCK COUNT - LD B,A ; NORMALIZE TO BYTE COUNT - CALL SLA32 ; BIT SHIFT LEFT ACCORDING TO MULTIPLIERS - LD B,9 ; NORMALIZE TO 512 BYTE BLOCK COUNT - CALL SRL32 ; BIT SHIFT RIGHT 9 BITS - POP IX ; RESTORE IX - JR SD_INITCARD9 ; RECORD VALUE + LD B,A ; NORMALIZE TO BYTE COUNT + CALL SLA32 ; BIT SHIFT LEFT ACCORDING TO MULTIPLIERS + LD B,9 ; NORMALIZE TO 512 BYTE BLOCK COUNT + CALL SRL32 ; BIT SHIFT RIGHT 9 BITS + POP IX ; RESTORE IX + JR SD_INITCARD9 ; RECORD VALUE ; ; CAPACITY CALCULATION FOR SDHC/SDXC (V2/V3) CARDS: ; BLOCKS = (C_SIZE + 1) * 1024 = C_SIZE << 10 ; SD_INITCARD8: ; GET SIZE FOR V2 CARD - PUSH IX ; SAVE IX - LD IX,SD_BUF ; POINT IX TO BUFFER - LD A,(IX + 7) ; GET C_SIZE MSB TO A - AND %00111111 ; ISOLATE RELEVANT BITS - LD H,(IX + 8) ; GET NEXT BYTE TO H - LD L,(IX + 9) ; GET C_SIZE LSB TO L - POP IX ; RESTORE IX + PUSH IX ; SAVE IX + LD IX,SD_BUF ; POINT IX TO BUFFER + LD A,(IX + 7) ; GET C_SIZE MSB TO A + AND %00111111 ; ISOLATE RELEVANT BITS + LD H,(IX + 8) ; GET NEXT BYTE TO H + LD L,(IX + 9) ; GET C_SIZE LSB TO L + POP IX ; RESTORE IX ; ADD 1 TO C_SIZE IN A:HL - LD DE,1 ; LOAD 1 - ADD HL,DE ; ADD TO HL - ADC A,0 ; HANDLE CARRY + LD DE,1 ; LOAD 1 + ADD HL,DE ; ADD TO HL + ADC A,0 ; HANDLE CARRY ; CONVERT TO 32 BIT, A:HL -> DE:HL LD D,0 LD E,A ; DIVIDE BY 1024 TO NORMALIZE, LEFT SHIFT 10 BITS - LD B,10 ; SHIFT BY 10 BITS - CALL SLA32 ; SHIFT THE 32 BIT VALUE - JR SD_INITCARD9 ; CONTINUE + LD B,10 ; SHIFT BY 10 BITS + CALL SLA32 ; SHIFT THE 32 BIT VALUE + JR SD_INITCARD9 ; CONTINUE ; SD_INITCARD9: ; COMMON CODE TO RECORD RESULTANT SIZE (IN DE:HL) ; SAVE DERIVED CAPACITY VALUE - PUSH HL ; SAVE HL - SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF - PUSH HL ; MOVE ADDRESS - POP BC ; ... TO BC - POP HL ; RECOVER HL - CALL ST32 ; SAVE THE CAPACITY VALUE (DWORD) + PUSH HL ; SAVE HL + SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF + PUSH HL ; MOVE ADDRESS + POP BC ; ... TO BC + POP HL ; RECOVER HL + CALL ST32 ; SAVE THE CAPACITY VALUE (DWORD) ; ; RESET CARD STATUS TO 0 (OK) - SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED - XOR A ; A := 0 (STATUS = OK) - LD (HL),A ; SAVE IT -; - XOR A ; SIGNAL SUCCESS - RET ; RETURN + SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED + XOR A ; A := 0 (STATUS = OK) + LD (HL),A ; SAVE IT ; + RET ; RETURN, A=0, Z SET + ; SECTOR I/O ; SD CARD COMMAND BYTE MUST BE PASSED IN C ; SD_SECTIO: PUSH BC - CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED + CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED POP BC - RET NZ ; ABORT IF REINIT FAILED + RET NZ ; ABORT IF REINIT FAILED - LD A,C ; LOAD SD CARD COMMADN BYTE - CALL SD_INITCMD ; SETUP COMMAND BUFFER - CALL SD_SETADDR ; SETUP LBA ADDRESS - CALL SD_EXECCMD ; EXECUTE COMMAND - RET NZ ; ABORT ON ERROR + LD A,C ; LOAD SD CARD COMMAND BYTE + CALL SD_INITCMD ; SETUP COMMAND BUFFER + CALL SD_SETADDR ; SETUP LBA ADDRESS + CALL SD_EXECCMD ; EXECUTE COMMAND + RET NZ ; ABORT ON ERROR LD HL,(DIOBUF) - LD BC,512 ; LENGTH TO READ - LD A,(SD_CMD) ; GET THE COMMAND - CP SD_CMD_READ_SNGL_BLK ; READ_SINGLE_BLOCK? - JR Z,SD_SECTIO1 ; HANDLE READ - CP SD_CMD_WRITE_BLOCK ; WRITE_BLOCK? - JR Z,SD_SECTIO2 ; HANDLE WRITE - CALL PANIC ; PANIC ON ANYTHING ELSE + LD BC,512 ; LENGTH TO READ + LD A,(SD_CMD) ; GET THE COMMAND + CP SD_CMD_READ_SNGL_BLK ; READ_SINGLE_BLOCK? + JR Z,SD_SECTIO1 ; HANDLE READ + CP SD_CMD_WRITE_BLOCK ; WRITE_BLOCK? + JR Z,SD_SECTIO2 ; HANDLE WRITE + CALL PANIC ; PANIC ON ANYTHING ELSE SD_SECTIO1: ; GET SECTOR DATA - CALL SD_GETDATA ; GET THE BLOCK - JR SD_SECTIO3 ; AND CONTINUE + CALL SD_GETDATA ; GET THE BLOCK + JR SD_SECTIO3 ; AND CONTINUE SD_SECTIO2: ; PUT SECTOR DATA - CALL SD_PUTDATA ; PUT THE BLOCK AND FALL THRU + CALL SD_PUTDATA ; PUT THE BLOCK AND FALL THRU SD_SECTIO3: ; CONTINUE WITH COMMON CODE - CALL SD_DONE ; CLOSE THE TRANSACTION - RET Z ; RETURN WITH A=0 AND Z SET - JP SD_ERRDATA ; DATA XFER ERROR + CALL SD_DONE ; CLOSE THE TRANSACTION + RET Z ; RETURN WITH A=0 AND Z SET + JP SD_ERRDATA ; DATA XFER ERROR ; ; CHECK THE SD CARD, ATTEMPT TO REINITIALIZE IF NEEDED ; SD_CHKCARD: - SD_DPTR(SD_STAT) ; HL = ADR OF STATUS, AF TRASHED - LD A,(HL) ; GET CURRENT STATUS - OR A ; SET FLAGS - RET Z ; RETURN WITH A=0 AND Z SET - JP SD_INITCARD ; OTHERWISE INIT CARD + SD_DPTR(SD_STAT) ; HL = ADR OF STATUS, AF TRASHED + LD A,(HL) ; GET CURRENT STATUS + OR A ; SET FLAGS + RET Z ; RETURN WITH A=0 AND Z SET + JP SD_INITCARD ; OTHERWISE INIT CARD ; ; CONVERT LBA ADDRESS IN HSTLBA TO CARD SPECIFIC ADDRESS IN CMD PARMS ; V1 CARDS REQUIRE BYTE ADDRESSING, SO A TRANSLATION IS DONE IN THAT CASE ; SD_SETADDR: - SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF - LD A,(HL) ; GET CARD TYPE - PUSH AF ; SAVE IT - LD HL,HSTLBA ; POINT TO INCOMING LBA VALUE - CALL LD32 ; LOAD IT TO DE:HL, AF IS TRASHED - POP AF ; GET CARD TYPE BACK - CP SD_TYPESDHC ; IS IT V2 OR BETTER? - JR NC,SD_SETADDR1 ; IF SO, BYPASS TRANSLATION + SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF + LD A,(HL) ; GET CARD TYPE + PUSH AF ; SAVE IT + LD HL,HSTLBA ; POINT TO INCOMING LBA VALUE + CALL LD32 ; LOAD IT TO DE:HL, AF IS TRASHED + POP AF ; GET CARD TYPE BACK + CP SD_TYPESDHC ; IS IT V2 OR BETTER? + JR NC,SD_SETADDR1 ; IF SO, BYPASS TRANSLATION ; ; TRANSLATE BLOCK ADDRESS TO BYTE ADDRESS FOR V1 CARDS LD D,E @@ -839,208 +846,209 @@ SD_SETADDR: ; SD_SETADDR1: ; STORE RESULTANT ADDRESS INTO PARMS (BIG ENDIAN!) - PUSH HL ; SAVE LOW WORD OF ADDRESS - LD HL,SD_CMDP0 ; POINT TO START OF PARM BYTES - LD (HL),D ; SAVE MSB OF HI WORD - INC HL ; NEXT BYTE - LD (HL),E ; SAVE LSB OF HI WORD - INC HL ; NEXT BYTE - POP DE ; RECOVER LOW WORD OF ADDRESS INTO DE - LD (HL),D ; SAVE MSB OF LO WORD - INC HL ; NEXT BYTE - LD (HL),E ; SAVE LSB OF LO WORD - RET ; DONE -; -;================================================================================================== -; SD DISK DRIVER COMMAND PROCESSING -;================================================================================================== + PUSH HL ; SAVE LOW WORD OF ADDRESS + LD HL,SD_CMDP0 ; POINT TO START OF PARM BYTES + LD (HL),D ; SAVE MSB OF HI WORD + INC HL ; NEXT BYTE + LD (HL),E ; SAVE LSB OF HI WORD + INC HL ; NEXT BYTE + POP DE ; RECOVER LOW WORD OF ADDRESS INTO DE + LD (HL),D ; SAVE MSB OF LO WORD + INC HL ; NEXT BYTE + LD (HL),E ; SAVE LSB OF LO WORD + RET ; DONE +; +;============================================================================= +; COMMAND PROCESSING +;============================================================================= ; ; PUT CARD IN IDLE STATE ; SD_GOIDLE: - CALL SD_GOIDLE1 ; FIRST ATTEMPT - RET Z ; DONE IF SUCCEEDED + CALL SD_GOIDLE1 ; FIRST ATTEMPT + RET Z ; DONE IF SUCCEEDED ; FALL THRU FOR SECOND ATTEMPT IF NEEDED ; SD_GOIDLE1: ; SEEMS TO HELP SOME CARDS? - ;CALL SD_SELECT ; ASSERT CS - ;CALL SD_DONE ; SEND 8 CLOCKS AND DEASSERT CS + ;CALL SD_SELECT ; ASSERT CS + ;CALL SD_DONE ; SEND 8 CLOCKS AND DEASSERT CS ; SMALL DELAY HERE HELPS SOME CARDS - LD DE,300 ; 16US * 300 = ~5MS - CALL VDELAY ; CPU SPEED NORMALIZED DELAY + ;;LD DE,300 ; 16US * 300 = ~5MS + ;LD DE,60 ; 16US * 60 = ~1MS + ;CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; PUT CARD IN IDLE STATE - LD A,SD_CMD_GO_IDLE_STATE ; CMD0 = ENTER IDLE STATE - CALL SD_INITCMD ; INIT COMMAND BUFFER - LD A,$95 ; CRC FOR GO_IDLE_STATE COMMAND IS $95 - LD (SD_CMDCRC),A ; SET CRC - CALL SD_EXECCMDND ; EXECUTE COMMAND W/ NO DATA RETURNED - RET NZ ; ABORT ON ERROR - LD A,(SD_RC) ; GET CARD RESULT - DEC A ; MAP EXPECTED $01 -> $00 - RET Z ; ALL IS GOOD, RETURN WITH Z=0 AND Z SET - LD A,SD_STCMDERR ; SET COMMAND ERROR VALUE, NZ ALREADY SET - RET ; AND RETURN + LD A,SD_CMD_GO_IDLE_STATE ; CMD0 = ENTER IDLE STATE + CALL SD_INITCMD ; INIT COMMAND BUFFER + LD A,$95 ; CRC FOR GO_IDLE_STATE COMMAND IS $95 + LD (SD_CMDCRC),A ; SET CRC + CALL SD_EXECCMDND ; EXECUTE COMMAND W/ NO DATA RETURNED + RET NZ ; ABORT ON ERROR + LD A,(SD_RC) ; GET CARD RESULT + DEC A ; MAP EXPECTED $01 -> $00 + RET Z ; ALL IS GOOD, RETURN WITH Z=0 AND Z SET + LD A,SD_STCMDERR ; SET COMMAND ERROR VALUE, NZ ALREADY SET + RET ; AND RETURN ; ; INITIALIZE COMMAND BUFFER ; COMMAND BYTE IN ACCUM ; HL AND AF DESTROYED ; SD_INITCMD: - LD HL,SD_CMDBUF ; POINT TO START OF BUFFER - LD (HL),A ; SET THE COMMAND BYTE - XOR A ; CLEAR ACCUM - LD B,7 ; PREPARE TO CLEAR NEXT 7 BYTES (PARMS, CRC, RC, TOK) + LD HL,SD_CMDBUF ; POINT TO START OF BUFFER + LD (HL),A ; SET THE COMMAND BYTE + XOR A ; CLEAR ACCUM + LD B,7 ; PREPARE TO CLEAR NEXT 7 BYTES (PARMS, CRC, RC, TOK) SD_INITCMD1: - INC HL ; POINT TO NEXT BYTE - LD (HL),A ; CLEAR IT - DJNZ SD_INITCMD1 ; LOOP TILL DONE + INC HL ; POINT TO NEXT BYTE + LD (HL),A ; CLEAR IT + DJNZ SD_INITCMD1 ; LOOP TILL DONE RET ; ; EXECUTE APP COMMAND ; SD_EXECACMD: - LD A,SD_CMD_APP_CMD ; APP_CMD, AN APP CMD IS NEXT - CALL SD_INITCMD ; SETUP COMMAND BUFFER - JR SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED + LD A,SD_CMD_APP_CMD ; APP_CMD, AN APP CMD IS NEXT + CALL SD_INITCMD ; SETUP COMMAND BUFFER + JR SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED ; ; EXECUTE COMMAND WITH NO DATA ; SD_EXECCMDND: - CALL SD_EXECCMD ; RUN THE COMMAND - JP Z,SD_DONE ; RETURN THRU SD_DONE IF NO ERROR - RET ; ERROR STATUS, JUST RETURN, SD_DONE WAS ALREADY RUN + CALL SD_EXECCMD ; RUN THE COMMAND + JP Z,SD_DONE ; RETURN THRU SD_DONE IF NO ERROR + RET ; ERROR STATUS, JUST RETURN, SD_DONE WAS ALREADY RUN ; ; EXECUTE A COMMAND ; WILL FINISH TRANSACTION IF ERROR OCCURS ; RETURNS STATUS IN A WITH ZF SET ACCORDINGLY ; SD_EXECCMD: -#IF (SDTRACE >= 2) +#IF (SDTRACE >= 3) PUSH AF CALL SD_PRTPREFIX LD DE,SD_CMDBUF - PRTS(" CMD =$") + PRTS(" CMD$") LD A,6 CALL PRTHEXBUF - LD DE,SD_STRARROW + LD DE,SD_STR_ARROW CALL WRITESTR POP AF #ENDIF ; WAIT FOR CARD TO BE READY - CALL SD_WAITRDY ; WAIT FOR CARD TO BE READY FOR A COMMAND - JP NZ,SD_ERRRDYTO ; HANDLE TIMEOUT ERROR + CALL SD_WAITRDY ; WAIT FOR CARD TO BE READY FOR A COMMAND + JP NZ,SD_ERRRDYTO ; HANDLE TIMEOUT ERROR ; SEND THE COMMAND - LD HL,SD_CMDBUF ; POINT TO COMMAND BUFFER - LD E,6 ; COMMANDS ARE 6 BYTES + LD HL,SD_CMDBUF ; POINT TO COMMAND BUFFER + LD E,6 ; COMMANDS ARE 6 BYTES SD_EXECCMD1: - LD A,(HL) ; PREPARE TO SEND NEXT BYTE - CALL SD_PUT ; SEND IT - INC HL ; POINT TO NEXT BYTE - DEC E ; DEC LOOP COUNTER - JR NZ,SD_EXECCMD1 ; LOOP TILL DONE W/ ALL 6 BYTES + LD A,(HL) ; PREPARE TO SEND NEXT BYTE + CALL SD_PUT ; SEND IT + INC HL ; POINT TO NEXT BYTE + DEC E ; DEC LOOP COUNTER + JR NZ,SD_EXECCMD1 ; LOOP TILL DONE W/ ALL 6 BYTES ; ; GET RESULT - LD E,0 ; INIT TIMEOUT LOOP COUNTER + LD E,0 ; INIT TIMEOUT LOOP COUNTER SD_EXECCMD2: - CALL SD_GET ; GET A BYTE FROM THE CARD - OR A ; SET FLAGS - JP P,SD_EXECCMD3 ; IF HIGH BIT IS 0, WE HAVE RESULT - DEC E ; OTHERWISE DECREMENT LOOP COUNTER - JR NZ,SD_EXECCMD2 ; AND LOOP UNTIL TIMEOUT + CALL SD_GET ; GET A BYTE FROM THE CARD + OR A ; SET FLAGS + JP P,SD_EXECCMD3 ; IF HIGH BIT IS 0, WE HAVE RESULT + DEC E ; OTHERWISE DECREMENT LOOP COUNTER + JR NZ,SD_EXECCMD2 ; AND LOOP UNTIL TIMEOUT JP SD_ERRCMDTO ; SD_EXECCMD3: ; COMMAND COMPLETE, SAVE RC AND PRINT DIAGNOSTICS AS APPROPRIATE - LD (SD_RC),A ; RECORD THE RESULT -#IF (SDTRACE >= 2) - CALL SD_PRTRC ; IF MAX TRACING, PRINT RC + LD (SD_RC),A ; RECORD THE RESULT +#IF (SDTRACE >= 3) + CALL SD_PRTRC ; IF MAX TRACING, PRINT RC #ENDIF #IF (DSKYENABLE) PUSH AF - CALL SD_DSKY ; IF USING DSKY, SHOW IT THERE + CALL SD_DSKY ; IF USING DSKY, SHOW IT THERE POP AF #ENDIF - AND ~$01 ; MASK OFF IDLE BIT AND SET FLAGS - RET Z ; IF RC = 0, NO ERROR, RETURN - CALL SD_DONE ; IF ERROR, COMPLETE TRANSACTION - JP SD_ERRCMD ; ... AND HANDLE IT + AND ~$01 ; MASK OFF IDLE BIT AND SET FLAGS + RET Z ; IF RC = 0, NO ERROR, RETURN + CALL SD_DONE ; IF ERROR, COMPLETE TRANSACTION + JP SD_ERRCMD ; ... AND HANDLE IT ; ; SD_GETDATA ; SD_GETDATA: - PUSH HL ; SAVE DESTINATION ADDRESS - PUSH BC ; SAVE LENGTH TO RECEIVE - LD DE,$7FFF ; LOOP MAX (TIMEOUT) + PUSH HL ; SAVE DESTINATION ADDRESS + PUSH BC ; SAVE LENGTH TO RECEIVE + LD DE,$7FFF ; LOOP MAX (TIMEOUT) SD_GETDATA1: CALL SD_GET - CP $FF ; WANT BYTE != $FF - JR NZ,SD_GETDATA2 ; NOT $FF, MOVE ON + CP $FF ; WANT BYTE != $FF + JR NZ,SD_GETDATA2 ; NOT $FF, MOVE ON DEC DE BIT 7,D - JR Z,SD_GETDATA1 ; KEEP TRYING UNTIL TIMEOUT + JR Z,SD_GETDATA1 ; KEEP TRYING UNTIL TIMEOUT SD_GETDATA2: - LD (SD_TOK),A ; SAVE TOKEN VALUE -#IF (SDTRACE >= 2) + LD (SD_TOK),A ; SAVE TOKEN VALUE +#IF (SDTRACE >= 3) PUSH AF CALL SD_PRTTOK POP AF #ENDIF - POP DE ; RESTORE LENGTH TO RECEIVE - POP HL ; RECOVER DEST ADDRESS - CP $FE ; PACKET START? - JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE + POP DE ; RESTORE LENGTH TO RECEIVE + POP HL ; RECOVER DEST ADDRESS + CP $FE ; PACKET START? + JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE SD_GETDATA3: - CALL SD_GET ; GET NEXT BYTE - LD (HL),A ; SAVE IT + CALL SD_GET ; GET NEXT BYTE + LD (HL),A ; SAVE IT INC HL DEC DE LD A,D OR E - JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES - CALL SD_GET ; DISCARD CRC BYTE 1 - CALL SD_GET ; DISCARD CRC BYTE 2 - XOR A ; RESULT IS ZERO + JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES + CALL SD_GET ; DISCARD CRC BYTE 1 + CALL SD_GET ; DISCARD CRC BYTE 2 + XOR A ; RESULT IS ZERO SD_GETDATA4: RET ; ; SD_PUTDATA ; SD_PUTDATA: - PUSH HL ; SAVE SOURCE ADDRESS - PUSH BC ; SAVE LENGTH TO SEND + PUSH HL ; SAVE SOURCE ADDRESS + PUSH BC ; SAVE LENGTH TO SEND - LD A,$FE ; PACKET START - CALL SD_PUT ; SEND IT + LD A,$FE ; PACKET START + CALL SD_PUT ; SEND IT - POP DE ; RECOVER LENGTH TO SEND - POP HL ; RECOVER SOURCE ADDRESS + POP DE ; RECOVER LENGTH TO SEND + POP HL ; RECOVER SOURCE ADDRESS SD_PUTDATA1: - LD A,(HL) ; GET NEXT BYTE TO SEND - CALL SD_PUT ; SEND IF + LD A,(HL) ; GET NEXT BYTE TO SEND + CALL SD_PUT ; SEND IF INC HL DEC DE LD A,D OR E - JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES - LD A,$FF ; DUMMY CRC BYTE + JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES + LD A,$FF ; DUMMY CRC BYTE CALL SD_PUT - LD A,$FF ; DUMMY CRC BYTE + LD A,$FF ; DUMMY CRC BYTE CALL SD_PUT - LD DE,$7FFF ; LOOP MAX (TIMEOUT) + LD DE,$7FFF ; LOOP MAX (TIMEOUT) SD_PUTDATA2: CALL SD_GET - CP $FF ; WANT BYTE != $FF - JR NZ,SD_PUTDATA3 ; NOT $FF, MOVE ON + CP $FF ; WANT BYTE != $FF + JR NZ,SD_PUTDATA3 ; NOT $FF, MOVE ON DEC DE BIT 7,D - JR Z,SD_PUTDATA2 ; KEEP TRYING UNTIL TIMEOUT + JR Z,SD_PUTDATA2 ; KEEP TRYING UNTIL TIMEOUT SD_PUTDATA3: LD (SD_TOK),A -#IF (SDTRACE >= 2) +#IF (SDTRACE >= 3) PUSH AF CALL SD_PRTTOK POP AF @@ -1054,19 +1062,19 @@ SD_PUTDATA3: ; SELECT CARD AND WAIT FOR IT TO BE READY ($FF) ; SD_WAITRDY: - CALL SD_SELECT ; SELECT CARD - LD DE,$FFFF ; LOOP MAX (TIMEOUT) + CALL SD_SELECT ; SELECT CARD + LD DE,$FFFF ; LOOP MAX (TIMEOUT) SD_WAITRDY1: CALL SD_GET - INC A ; $FF -> $00 - RET Z ; IF READY, RETURN + INC A ; $FF -> $00 + RET Z ; IF READY, RETURN DEC DE LD A,D OR E - JR NZ,SD_WAITRDY1 ; KEEP TRYING UNTIL TIMEOUT - XOR A ; ZERO ACCUM - DEC A ; ACCUM := $FF TO SIGNAL ERROR - RET ; TIMEOUT + JR NZ,SD_WAITRDY1 ; KEEP TRYING UNTIL TIMEOUT + XOR A ; ZERO ACCUM + DEC A ; ACCUM := $FF TO SIGNAL ERROR + RET ; TIMEOUT ; ; FINISH A TRANSACTION - PRESERVE AF ; @@ -1087,7 +1095,7 @@ SD_DONE: RET ; ;============================================================================= -; SD HARDWARE INTERFACE ROUTINES +; HARDWARE INTERFACE ROUTINES ;============================================================================= ; ; PERFORM HARDWARE SPECIFIC INITIALIZATION @@ -1102,8 +1110,8 @@ SD_SETUP: ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4)) ; CSIO SETUP -; LD A,2 ; 18MHz/20 <= 400kHz - LD A,6 ; ??? +; LD A,2 ; 18MHz/20 <= 400kHz + LD A,6 ; ??? OUT0 (SD_CNTR),A LD A,SD_OPRDEF LD (SD_OPRVAL),A @@ -1111,7 +1119,7 @@ SD_SETUP: #ENDIF ; #IF (SDMODE == SDMODE_PPI) - LD A,82H ; PPI PORT A=OUT, B=IN, C=OUT + LD A,82H ; PPI PORT A=OUT, B=IN, C=OUT OUT (SD_PPIX),A LD A,SD_OPRDEF LD (SD_OPRVAL),A @@ -1120,11 +1128,11 @@ SD_SETUP: ; #IF (SDMODE == SDMODE_UART) SD_OPRMSK .EQU (SD_CS | SD_CLK | SD_DI) - IN A,(SD_OPRREG) ; OPRREG == SIO_MCR - AND ~SD_OPRMSK ; MASK OFF SD CONTROL BITS - OR SD_OPRDEF ; SET DEFAULT BITS - LD (SD_OPRVAL),A ; RECORD THE WORKING VALLUE - OUT (SD_OPRREG),A ; OPRREG == SIO_MCR + IN A,(SD_OPRREG) ; OPRREG == SIO_MCR + AND ~SD_OPRMSK ; MASK OFF SD CONTROL BITS + OR SD_OPRDEF ; SET DEFAULT BITS + LD (SD_OPRVAL),A ; RECORD THE WORKING VALLUE + OUT (SD_OPRREG),A ; OPRREG == SIO_MCR #ENDIF ; XOR A @@ -1134,47 +1142,52 @@ SD_OPRMSK .EQU (SD_CS | SD_CLK | SD_DI) ; UNIT IS SPECIFIED IN A ; SD_SELUNIT: - LD HL,SD_UNIT ; POINT TO PREVIOUSLY SELECTED UNIT - CP (HL) ; SAME? - RET Z ; IF SO, NOTHING MORE TO DO - CP SD_UNITCNT ; CHECK VALIDITY (EXCEED UNIT COUNT?) - CALL NC,PANIC ; PANIC ON INVALID VALUE + LD HL,SD_UNIT ; POINT TO PREVIOUSLY SELECTED UNIT + CP (HL) ; SAME? + RET Z ; IF SO, NOTHING MORE TO DO +; + CP SD_UNITCNT ; CHECK VALIDITY (EXCEED UNIT COUNT?) + JP NC,SD_INVUNIT ; HANDLE INVALID UNIT ; ; NEW UNIT SELECTED, IMPLEMENT IT - LD (SD_UNIT),A ; SAVE CURRENT UNIT NUMBER + LD (SD_UNIT),A ; SAVE CURRENT UNIT NUMBER #IF (SDMODE == SDMODE_DSD) ; SELECT REQUESTED UNIT - OUT (SD_SELREG),A ; ACTUALLY SELECT THE CARD + OUT (SD_SELREG),A ; ACTUALLY SELECT THE CARD +#ENDIF + XOR A ; SIGNAL SUCCESS + RET ; DONE +; +; CHECK FOR CARD DETECT (NZ = MEDIA PRESENT) +; +SD_CHKCD: +#IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4)) + IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER + BIT 5,A ; TEST CARD DETECT BIT +#ELSE + OR $FF ; ASSUME CARD PRESENT #ENDIF - XOR A ; SIGNAL SUCCESS - RET ; DONE + RET ; DONE ; ; CHECK FOR WRITE PROTECT (NZ = WRITE PROTECTED) ; SD_CHKWP: #IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4)) - IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER - BIT 4,A ; TEST WP BIT + IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER + BIT 4,A ; TEST WP BIT #ELSE - XOR A ; WP NOT SUPPORTED BY HARDWARE, ASSUME WP OFF + XOR A ; WP NOT SUPPORTED BY HARDWARE, ASSUME WP OFF #ENDIF - -#IF (SDTRACE >= 2) - CALL NZ,SD_PRTPREFIX -#ENDIF - - JP NZ,SD_WRTPROT ; HANDLE AS ERROR - - RET + RET ; AND RETURN ; ; SELECT CARD ; SD_SELECT: LD A,(SD_OPRVAL) #IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART)) - AND ~SD_CS ; SET SD_CS (CHIP SELECT) + AND ~SD_CS ; SET SD_CS (CHIP SELECT) #ELSE - OR SD_CS ; SET SD_CS (CHIP SELECT) + OR SD_CS ; SET SD_CS (CHIP SELECT) #ENDIF LD (SD_OPRVAL),A OUT (SD_OPRREG),A @@ -1185,9 +1198,9 @@ SD_SELECT: SD_DESELECT: LD A,(SD_OPRVAL) #IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART)) - OR SD_CS ; RESET SD_CS (CHIP SELECT) + OR SD_CS ; RESET SD_CS (CHIP SELECT) #ELSE - AND ~SD_CS ; RESET SD_CS (CHIP SELECT) + AND ~SD_CS ; RESET SD_CS (CHIP SELECT) #ENDIF LD (SD_OPRVAL),A OUT (SD_OPRREG),A @@ -1197,16 +1210,16 @@ SD_DESELECT: ; ; CSIO WAIT FOR TRANSMIT READY (TX REGSITER EMPTY) ; -SD_WAITTX: ; WAIT FOR TX EMPTY - IN0 A,(SD_CNTR) ; GET CSIO STATUS - BIT 4,A ; TX EMPTY? +SD_WAITTX: ; WAIT FOR TX EMPTY + IN0 A,(SD_CNTR) ; GET CSIO STATUS + BIT 4,A ; TX EMPTY? JR NZ,SD_WAITTX RET ; ; CSIO WAIT FOR RECEIVER READY (BYTE AVAILABLE) ; SD_WAITRX: - IN0 A,(SD_CNTR) ; WAIT FOR RECEIVER TO FINISH + IN0 A,(SD_CNTR) ; WAIT FOR RECEIVER TO FINISH BIT 5,A JR NZ,SD_WAITRX RET @@ -1214,22 +1227,22 @@ SD_WAITRX: ; MSB<-->LSB MIRROR BITS IN A, RESULT IN C ; MIRROR: - #IF (SDCSIOFAST) ; SLOW SPEED, LEAST CODE SPACE - LD BC,MIRTAB ; 256 BYTE MIRROR TABLE - ADD A,C ; ADD OFFSET + #IF (SDCSIOFAST) ; SLOW SPEED, LEAST CODE SPACE + LD BC,MIRTAB ; 256 BYTE MIRROR TABLE + ADD A,C ; ADD OFFSET LD C,A JR NC,MIRROR2 INC B MIRROR2: - LD A,(BC) ; GET RESULT - LD C,A ; RETURN RESULT IN C + LD A,(BC) ; GET RESULT + LD C,A ; RETURN RESULT IN C RET - #ELSE ; FASTEST BUT USES MOST CODE SPACE - LD B,8 ; BIT COUNTER + #ELSE ; FASTEST BUT USES MOST CODE SPACE + LD B,8 ; BIT COUNTER MIRROR1: - RLA ; ROTATE BIT 7 INTO CARRY - RR C ; ROTATE CARRY INTO RESULT - DJNZ MIRROR1 ; DO ALL 8 BITS + RLA ; ROTATE BIT 7 INTO CARRY + RR C ; ROTATE CARRY INTO RESULT + DJNZ MIRROR1 ; DO ALL 8 BITS RET #ENDIF #ENDIF @@ -1238,166 +1251,145 @@ MIRROR1: ; SD_PUT: #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4)) - CALL MIRROR ; MSB<-->LSB MIRROR BITS, RESULT IN C - CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING - OUT0 (SD_TRDR),C ; PUT BYTE IN BUFFER + CALL MIRROR ; MSB<-->LSB MIRROR BITS, RESULT IN C + CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING + OUT0 (SD_TRDR),C ; PUT BYTE IN BUFFER IN0 A,(SD_CNTR) - SET 4,A ; SET TRANSMIT ENABLE + SET 4,A ; SET TRANSMIT ENABLE OUT0 (SD_CNTR),A #ELSE #IF (SDMODE == SDMODE_UART) - XOR $FF ; DI IS INVERTED ON UART + XOR $FF ; DI IS INVERTED ON UART #ENDIF - LD C,A ; C=BYTE TO SEND - LD B,8 ; SEND 8 BITS (LOOP 8 TIMES) - LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE + LD C,A ; C=BYTE TO SEND + LD B,8 ; SEND 8 BITS (LOOP 8 TIMES) + LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE SD_PUT1: - RRA ; PREPARE TO GET DATA BIT FROM CF - RL C ; ROTATE NEXT BIT FROM C INTO CF - RLA ; ROTATE CF INTO A:0, SD_DO is OPR:0 - OUT (SD_OPRREG),A ; ASSERT DATA BIT - XOR SD_CLK ; TOGGLE CLOCK - OUT (SD_OPRREG),A ; UPDATE CLOCK AND ASSERT DATA BIT - XOR SD_CLK ; TOGGLE CLOCK - OUT (SD_OPRREG),A ; UPDATE CLOCK - DJNZ SD_PUT1 ; REPEAT FOR ALL 8 BITS - LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE - OUT (SD_OPRREG),A ; LEAVE WITH CLOCK LOW + RRA ; PREPARE TO GET DATA BIT FROM CF + RL C ; ROTATE NEXT BIT FROM C INTO CF + RLA ; ROTATE CF INTO A:0, SD_DO is OPR:0 + OUT (SD_OPRREG),A ; ASSERT DATA BIT + XOR SD_CLK ; TOGGLE CLOCK + OUT (SD_OPRREG),A ; UPDATE CLOCK AND ASSERT DATA BIT + XOR SD_CLK ; TOGGLE CLOCK + OUT (SD_OPRREG),A ; UPDATE CLOCK + DJNZ SD_PUT1 ; REPEAT FOR ALL 8 BITS + LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE + OUT (SD_OPRREG),A ; LEAVE WITH CLOCK LOW #ENDIF - RET ; DONE + RET ; DONE ; ; RECEIVE ONE BYTE ; SD_GET: #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4)) - CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING - IN0 A,(Z180_CNTR) ; GET CSIO STATUS - SET 5,A ; START RECEIVER + CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING + IN0 A,(Z180_CNTR) ; GET CSIO STATUS + SET 5,A ; START RECEIVER OUT0 (Z180_CNTR),A CALL SD_WAITRX - IN0 A,(Z180_TRDR) ; GET RECEIVED BYTE - CALL MIRROR ; MSB<-->LSB MIRROR BITS - LD A,C ; KEEP RESULT + IN0 A,(Z180_TRDR) ; GET RECEIVED BYTE + CALL MIRROR ; MSB<-->LSB MIRROR BITS + LD A,C ; KEEP RESULT #ELSE - LD B,8 ; RECEIVE 8 BITS (LOOP 8 TIMES) - LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE + LD B,8 ; RECEIVE 8 BITS (LOOP 8 TIMES) + LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE SD_GET1: - XOR SD_CLK ; TOGGLE CLOCK - OUT (SD_OPRREG),A ; UPDATE CLOCK - IN A,(SD_INPREG) ; READ THE DATA WHILE CLOCK IS ACTIVE + XOR SD_CLK ; TOGGLE CLOCK + OUT (SD_OPRREG),A ; UPDATE CLOCK + IN A,(SD_INPREG) ; READ THE DATA WHILE CLOCK IS ACTIVE #IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_PPI)) - RLA ; ROTATE INP:7 INTO CF + RLA ; ROTATE INP:7 INTO CF #ENDIF #IF (SDMODE == SDMODE_N8) - RLA ; ROTATE INP:6 INTO CF - RLA ; " + RLA ; ROTATE INP:6 INTO CF + RLA ; " #ENDIF #IF (SDMODE == SDMODE_UART) - RLA ; ROTATE INP:5 INTO CF - RLA ; " - RLA ; " + RLA ; ROTATE INP:5 INTO CF + RLA ; " + RLA ; " #ENDIF #IF (SDMODE == SDMODE_DSD) - RRA ; ROTATE INP:0 INTO CF + RRA ; ROTATE INP:0 INTO CF #ENDIF - RL C ; ROTATE CF INTO C:0 - LD A,(SD_OPRVAL) ; BACK TO INITIAL VALUES (TOGGLE CLOCK) - OUT (SD_OPRREG),A ; DO IT - DJNZ SD_GET1 ; REPEAT FOR ALL 8 BITS - LD A,C ; GET BYTE RECEIVED INTO A + RL C ; ROTATE CF INTO C:0 + LD A,(SD_OPRVAL) ; BACK TO INITIAL VALUES (TOGGLE CLOCK) + OUT (SD_OPRREG),A ; DO IT + DJNZ SD_GET1 ; REPEAT FOR ALL 8 BITS + LD A,C ; GET BYTE RECEIVED INTO A #IF (SDMODE == SDMODE_UART) - XOR $FF ; DO IS INVERTED ON UART + XOR $FF ; DO IS INVERTED ON UART #ENDIF #ENDIF RET ; -;================================================================================================== -; PRINTING AND DIAGNOSTIC OUTPUT -;================================================================================================== -; -; PRINT DIAGNONSTIC PREFIX +;============================================================================= +; ERROR HANDLING AND DIAGNOSTICS +;============================================================================= ; -SD_PRTPREFIX: - CALL NEWLINE - PRTS("SD$") - PUSH AF - LD A,(SD_UNIT) - ADD A,'0' - CALL COUT - POP AF - CALL PC_COLON - RET +; ERROR HANDLERS ; -; HANDLE READY TIMEOUT ERROR +SD_INVUNIT: + LD A,SD_STINVUNIT + JR SD_ERR2 ; SPECIAL CASE FOR INVALID UNIT ; SD_ERRRDYTO: LD A,SD_STRDYTO - JR SD_CARDERR + JR SD_ERR ; SD_ERRINITTO: LD A,SD_STINITTO - JR SD_CARDERR + JR SD_ERR ; SD_ERRCMDTO: LD A,SD_STCMDTO - JR SD_CARDERR + JR SD_ERR ; SD_ERRCMD: LD A,SD_STCMDERR - JR SD_CARDERR + JR SD_ERR ; SD_ERRDATA: LD A,SD_STDATAERR - JR SD_CARDERR + JR SD_ERR ; SD_ERRDATATO: LD A,SD_STDATATO - JR SD_CARDERR + JR SD_ERR ; SD_ERRCRC: LD A,SD_STCRCERR - JR SD_CARDERR + JR SD_ERR ; SD_NOMEDIA: LD A,SD_STNOMEDIA - JR SD_CARDERR + JR SD_ERR ; SD_WRTPROT: LD A,SD_STWRTPROT - JR SD_CARDERR -; -; GENERIC ERROR HANDLER -; -SD_CARDERR: - PUSH HL ; IS THIS NEEDED? - PUSH AF ; SAVE INCOMING STATUS - SD_DPTR(SD_STAT) ; GET STATUS ADR IN HL, AF TRASHED - POP AF ; RESTORE INCOMING STATUS - LD (HL),A ; UPDATE STATUS - POP HL ; IS THIS NEEDED? + JR SD_ERR2 ; DO NOT UPDATE UNIT STATUS! +; +SD_ERR: + PUSH HL ; IS THIS NEEDED? + PUSH AF ; SAVE INCOMING STATUS + SD_DPTR(SD_STAT) ; GET STATUS ADR IN HL, AF TRASHED + POP AF ; RESTORE INCOMING STATUS + LD (HL),A ; UPDATE STATUS + POP HL ; IS THIS NEEDED? +SD_ERR2: #IF (SDTRACE >= 2) - CALL PC_SPACE - PUSH AF ; IS THIS NEEDED? - PRTC('<') CALL SD_PRTSTAT - PRTC('>') - POP AF ; IS THIS NEEDED? + CALL SD_REGDUMP #ENDIF - OR A ; SET FLAGS + OR A ; SET FLAGS RET ; -; PRINT STATUS IF AN ERROR HAS OCCURRED +; ; SD_PRTERR: -#IF (SDTRACE >= 1) - ;RET Z ; IF NO ERROR, GET OUT - PUSH AF ; SAVE ACCUM/FLAGS - CALL SD_PRTPREFIX ; PRINT UNIT PREFIX - CALL PC_SPACE ; FORMATTING - CALL SD_PRTSTAT ; PRINT STATUS - POP AF ; RESTORE ACCUM/FLAGS -#ENDIF - RET ; DONE + RET Z ; DONE IF NO ERRORS + ; FALL THRU TO SD_PRTSTAT ; ; PRINT STATUS STRING ; @@ -1405,44 +1397,48 @@ SD_PRTSTAT: PUSH AF PUSH DE PUSH HL - SD_DPTR(SD_STAT) ; GET ADR OF STATUS IN HL, AF TRASHED - LD A,(HL) ; GET STATUS OR A - LD DE,SD_STRSTOK + LD DE,SD_STR_STOK JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTNOTRDY - JR Z,SD_PRTSTAT1 + LD DE,SD_STR_STINVUNIT + JR Z,SD_PRTSTAT2 ; INVALID UNIT IS SPECIAL CASE INC A - LD DE,SD_STRSTRDYTO + LD DE,SD_STR_STRDYTO JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTINITTO + LD DE,SD_STR_STINITTO JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTCMDTO + LD DE,SD_STR_STCMDTO JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTCMDERR + LD DE,SD_STR_STCMDERR JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTDATAERR + LD DE,SD_STR_STDATAERR JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTDATATO + LD DE,SD_STR_STDATATO JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTCRCERR + LD DE,SD_STR_STCRCERR JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTNOMEDIA + LD DE,SD_STR_STNOMEDIA JR Z,SD_PRTSTAT1 INC A - LD DE,SD_STRSTWRTPROT + LD DE,SD_STR_STWRTPROT JR Z,SD_PRTSTAT1 - LD DE,SD_STRSTUNK -; + LD DE,SD_STR_STUNK SD_PRTSTAT1: + CALL SD_PRTPREFIX ; PRINT UNIT PREFIX + JR SD_PRTSTAT3 +SD_PRTSTAT2: + CALL NEWLINE + PRTS("SD:$") ; NO UNIT NUM IN PREFIX FOR INVALID UNIT +SD_PRTSTAT3: + CALL PC_SPACE ; FORMATTING CALL WRITESTR POP HL POP DE @@ -1452,7 +1448,7 @@ SD_PRTSTAT1: SD_PRTRC: PUSH AF PUSH DE - LD DE,SD_STRRC + LD DE,SD_STR_RC CALL WRITESTR LD A,(SD_RC) CALL PRTHEXBYTE @@ -1463,7 +1459,7 @@ SD_PRTRC: SD_PRTTOK: PUSH AF PUSH DE - LD DE,SD_STRTOK + LD DE,SD_STR_TOK CALL WRITESTR LD A,(SD_TOK) CALL PRTHEXBYTE @@ -1471,6 +1467,41 @@ SD_PRTTOK: POP AF RET ; +; +; +SD_REGDUMP: + PUSH AF + PUSH BC + PUSH HL + CALL PC_SPACE + CALL PC_LBKT + LD HL,SD_CMDBUF + LD B,8 +SD_REGDUMP1: + LD A,(HL) + INC HL + CALL PRTHEXBYTE + DJNZ SD_REGDUMP1 + CALL PC_RBKT + POP HL + POP BC + POP AF + RET + +; +; PRINT DIAGNONSTIC PREFIX +; +SD_PRTPREFIX: + CALL NEWLINE + PRTS("SD$") + PUSH AF + LD A,(SD_UNIT) + ADD A,'0' + CALL COUT + POP AF + CALL PC_COLON + RET +; ; DISPLAY COMMAND, LOW ORDER WORD OF PARMS, AND RC ; #IF (DSKYENABLE) @@ -1494,60 +1525,61 @@ SD_DSKY: RET #ENDIF ; -;================================================================================================== +;============================================================================= ; STRING DATA -;================================================================================================== -; -SD_STRARROW .TEXT " ->$" -SD_STRRC .TEXT " RC=$" -SD_STRTOK .TEXT " TOK=$" -SD_STRCSD .TEXT " CSD =$" -SD_STRCID .TEXT " CID =$" -SD_STRSCR .TEXT " SCR =$" -SD_STRSDTYPE .TEXT " SD CARD TYPE ID=$" -; -SD_STRSTOK .TEXT "OK$" -SD_STRSTNOTRDY .TEXT "NOT READY$" -SD_STRSTRDYTO .TEXT "READY TIMEOUT$" -SD_STRSTINITTO .TEXT "INITIALIZATION TIMEOUT$" -SD_STRSTCMDTO .TEXT "COMMAND TIMEOUT$" -SD_STRSTCMDERR .TEXT "COMMAND ERROR$" -SD_STRSTDATAERR .TEXT "DATA ERROR$" -SD_STRSTDATATO .TEXT "DATA TIMEOUT$" -SD_STRSTCRCERR .TEXT "CRC ERROR$" -SD_STRSTNOMEDIA .TEXT "NO MEDIA$" -SD_STRSTWRTPROT .TEXT "WRITE PROTECTED$" -SD_STRSTUNK .TEXT "UNKNOWN$" -SD_STRTYPEUNK .TEXT "UNK$" -SD_STRTYPEMMC .TEXT "MMC$" -SD_STRTYPESDSC .TEXT "SDSC$" -SD_STRTYPESDHC .TEXT "SDHC$" -SD_STRTYPESDXC .TEXT "SDXC$" -; -;================================================================================================== -; SD DISK DRIVER - DATA -;================================================================================================== -; -SD_OPRVAL .DB 0 ; CURRENT OPR REG VALUE -SD_LCNT .DB 0 ; LOOP COUNTER -; -SD_BUF .FILL 16,0 ; WORK BUFFER -; -SD_CMDBUF: ; START OF STD CMD BUF -SD_CMD .DB 0 ; COMMAND BYTE -SD_CMDP0 .DB 0 ; FIRST PARM BYTE (MSB) +;============================================================================= +; +SD_STR_ARROW .TEXT " -->$" +SD_STR_RC .TEXT " RC=$" +SD_STR_TOK .TEXT " TOK=$" +SD_STR_CSD .TEXT " CSD =$" +SD_STR_CID .TEXT " CID =$" +SD_STR_SCR .TEXT " SCR =$" +SD_STR_SDTYPE .TEXT " SD CARD TYPE ID=$" +; +SD_STR_STOK .TEXT "OK$" +SD_STR_STINVUNIT .TEXT "INVALID UNIT$" +SD_STR_STRDYTO .TEXT "READY TIMEOUT$" +SD_STR_STINITTO .TEXT "INITIALIZATION TIMEOUT$" +SD_STR_STCMDTO .TEXT "COMMAND TIMEOUT$" +SD_STR_STCMDERR .TEXT "COMMAND ERROR$" +SD_STR_STDATAERR .TEXT "DATA ERROR$" +SD_STR_STDATATO .TEXT "DATA TIMEOUT$" +SD_STR_STCRCERR .TEXT "CRC ERROR$" +SD_STR_STNOMEDIA .TEXT "NO MEDIA$" +SD_STR_STWRTPROT .TEXT "WRITE PROTECTED$" +SD_STR_STUNK .TEXT "UNKNOWN$" +SD_STR_TYPEUNK .TEXT "UNK$" +SD_STR_TYPEMMC .TEXT "MMC$" +SD_STR_TYPESDSC .TEXT "SDSC$" +SD_STR_TYPESDHC .TEXT "SDHC$" +SD_STR_TYPESDXC .TEXT "SDXC$" +; +;============================================================================= +; DATA STORAGE +;============================================================================= +; +SD_OPRVAL .DB 0 ; CURRENT OPR REG VALUE +SD_LCNT .DB 0 ; LOOP COUNTER +; +SD_BUF .FILL 16,0 ; WORK BUFFER +; +SD_CMDBUF: ; START OF STD CMD BUF +SD_CMD .DB 0 ; COMMAND BYTE +SD_CMDP0 .DB 0 ; FIRST PARM BYTE (MSB) SD_CMDP1 .DB 0 SD_CMDP2 .DB 0 -SD_CMDP3 .DB 0 ; LAST PARM BYTE (LSB) -SD_CMDCRC .DB 0 ; CRC +SD_CMDP3 .DB 0 ; LAST PARM BYTE (LSB) +SD_CMDCRC .DB 0 ; CRC ; -SD_RC .DB 0 ; RETURN CODE FROM CMD -SD_TOK .DB 0 ; TOKEN FROM DATA XFR +SD_RC .DB 0 ; RETURN CODE FROM CMD +SD_TOK .DB 0 ; TOKEN FROM DATA XFR ; -; UNIT SPECIFIC DATA STORATE +SD_UNIT .DB 0 ; ACTIVE UNIT, DEFAULT TO ZERO ; -SD_UNIT .DB -1 ; ACTIVE UNIT, INIT TO -1 TO INDICATE NOTHING SELECTED -SD_UNITDATA .FILL SD_UNITCNT*8,0 ; PER UNIT DATA, 8 BYTES +; UNIT SPECIFIC DATA STORAGE +; +SD_UNITDATA .FILL SD_UNITCNT * 8, 0 ; PER UNIT DATA, 8 BYTES SD_DATALEN .EQU $ - SD_UNITDATA ; LENGTH OF ENTIRE DATA STORAGE FOR ALL UNITS SD_UNITDATALEN .EQU SD_DATALEN / SD_UNITCNT ; LENGTH OF PER UNIT DATA ; @@ -1574,9 +1606,9 @@ MIRTAB .DB 00H, 80H, 40H, 0C0H, 20H, 0A0H, 60H, 0E0H, 10H, 90H, 50H, 0D0H, 30H, ; #ENDIF ; -;================================================================================================== +;============================================================================= ; HELPER ROUTINES -;================================================================================================== +;============================================================================= ; ; IMPLEMENTATION FOR MACRO SD_DPTR ; SET HL TO ADDRESS OF FIELD WITHIN PER UNIT DATA