IMD 1.16: 29/05/2007 18:44:43 FOGCPM.073 --FOGCPM073DICT DIC DICT DIC !"DICT DIC#$%&'()*+,-./012DICT DIC;3456789:-06-00 86 -CPM073 DOCDICCRE10COM;<DICCRE10MACy=>?@ABCDEFGHIJKLSPELLM20COM-MNOPQRSPELLM20DESSSPELLM20DOCTUVWXYZ[\]^_`abcSPELLM20DOCdefSPELLM20MACghijklmnopqrstuvSPELLM20MACwxyz{|}~SPELLM20MACSPELLM20MAC4SPELLM21COM.This is the disk name. #_58ti]N2i]N4GK:եu8E9 ‘һpY:sϗ,N)KtJ\.?\2$>\k"\.?I ͉ydY/wEdпl"//B<'>M ; x8 6&huWSKS:qRrIೄ`Kk'W8O'R i{fP{ti!٥u8Npн, xc,΢g0'K2e;Z/.6d5.!I /KI خxE8,(/RB{.+^ -a/w.6p˖|dNiYU ^N'N4i]N BKqfг)>X#Hx ԇggC) Σ о,\n-N 8E {%xC s2"KL烽ܶ|qYu;Cg`MKzZ4Kн,lKCD4KŠNF ¬䴮.gN\g24-$NX.x2\vgܴ:ZUn%xK ³>)^iu9d'=xNgDB.Ke5If2pd0-f%͊|m@"\.?yhr_Y|74GƎyi3>O45o{KBˍZX4/ -JTͼyfrEgP,g%HYz[>\iһоK#bGgDS' } Ibij%lVg\dNL x5[:tKJ%Yyngȳ{%xK ^Ye`DZtK.XSơxڅ /{ᗃӖK/, Z%ep:&[^h^){hӖ/KIӽ^ Һ|i@dԹ3ebA^^X˖^ [<{Խ\~%xC ZSh  ˍLsJȼ Lu9mDܲx 1/ ,OATܳDZ%Q괮,WEpXoM<.^V{%9jYnYr`_<y8E н, -WS >]N Ӎ|/{ςH\HYfg &Z&evZWSix$$4axFx){b3<i]N5|oy/K3R ¯,7tH >4&\|h։ogED.Bg2F^ṛhqٞr4/`Yő]Ne' lrщ £LMl%͋K6$t`KJ/|"ax0мjtS=dtl.I^ 'x7 x+J%)8,’pZ!:) GHT,ct :pW=h2')w8 ^2/{| -ZSGcYAJ. w6 悅8h_vg4ܸ|Ɖ-8,/`Q4h_v.Y0o}:$|?rt^)I KުL/KZWSiYJ, T'I:Y: Ow,鰳`J_.1&$3)3$'-Gi fgIWQ_,] NY - a;< Tpw`D$.ೡe$<MjӧXx9!EM8[Ʃw 4!!gއ.& bȅwh]bLT~#3<E^ 4 yn𐱙Hx;\I˔HgI]Pоl&tS;JqWS%峋^r,LaG%3̧ rԽ-T/-_$|EgхF/> ZY!|S\d%Ջ0о+\䏂SZWS)8<2\~ ~ 32< E,Һi{mbYxNZ\ts 7'N7 $/yIQH, -]VIKP< oT fYw r~Vb.Y:8iR-Yp9U3oHPX*%opy,^ Y7V+pdjρ\9o .l8[pоl .]4ρ},Z˖<x#fx+JpDGB >\,BᗂY pC4-Y(`Ra\2v^9>i 8X 1HHx+&Jr4>RAC䑇x2jc^N^ :p3stk#> V:4KY< yfYӁ,ɦx"gNςANή{.Йd^0rZ$ܷ}g{|Z e6$uZTδ t,*H+Uais/{Fg-< ,.AaDΜ$&m0qeđIC l?H ϰ-R<> jr։ ; xA x:GBg03k9  8$|+Cԏl!t|m 灡lɡxBf li+c,"wo2>g/-􏁵!ihYrY/Y<.g,Vr#fA3^Q# 3o|$<Hx$<>8,T02 #$>X, ֹ r3oI4< G^ }$$|'< l/,qq4K҄4/ XeY}T^au9ipRˍR<ghQj“%+opO!yh4,> g/wSLg}9  .IN{0 ͼsN]$|0,fxFCGCB˖) rЁx#-$ˁ嬸#$௃ag`rtxC_(!#c3вHYR$|jgT4/ /x+z4K4Mtu$ɛx+^Zh]N W-+&t+hΏFpFrxpkݞ!whT=gy H2H]zb4H,4/w *aDH< ,W!g%kKI$|/<,>D,ٓN %ܵgN˙*ρY,ZgYgm|80#/DAd^P<xPT,#Eg$ M3)Y 3Hx0I7#L9ZJK҉ym˃l Ie媂5D/-F%xC N[/w.l^XԼ>.۠Kv夨Jq/{ fB&gPl+)Y M"axS˫*yښ4wXg0A|VHh_v.Z,{dZݥ:ʅp\27Ӗow- )| M4 1x$ VyJ$| G| &,A- 8Լ!QFI9d$в嬃$<#>^a˖o-Jp! ϣcΑ.tx*GgME9YA&h|8h"^2̼hWcΑ*t9`^g0+wςУ.^䏄Ag`lY&tlJ" \xyfӂG+CZq8B&i]N\{]&m2pD< gE8`U2:ag/K(Ke |'wKv壎^]u9kN tON\vg>\MZ jͅ/Ϊ r/)g-R> +S> $ Ayo1|> Y hyI&|NYhzV`h_v_.;0GN\T,fx*F^ti41gN 0GgK< %,|E>2\Y,lS]ˀо*UiehuGSTtK{.,'>+gˍ)4x3>L/)ylҺjX݅Iӥ\29`iM i °,6+3“L˖ ^N *sҺN^u^^ZH.yj^XK3< Dͼ%`g<6L"6 8 Ӗpx*B56OgZ&mL I&pAd^\| g,Dg4Kg`P괮Ɨ}jgBGʁ;bi]NZ^w sWSel)8\2✶)ykDܹ 8ZibZa˖K4/KJT\p7,]/w.,Vg8|hV °K0#QVNC[p:%,d@/3ԉ ᗖ৐[V<YgN\g,I KT=_.[uYrm+4K.4<x) /nFp7< c(݅8$ed в/`Y4K$ć< +𐱙T/ oKd2H]S6& g rИp4ͼ-A>pocґXd˄3*c+^ys>֏hh_6HHXwIaymDؼN>\{R>ϰ/ ΤysM'SD&]3>M,Z[ bC,񍂖wXg^h/K7S#/K>x^Jr@-$RpipTeTܴK$YJ%ௗ,M3䏌0kDܳI;PxF~tnӥU,NPNhyENoix w '%/x;"󤐳&<YP#!c34%zYρ0{FrܱxAaC$G%"BazYzZ!'L05CB4rP|f1<ku4Ƃ+W$ZOFh?-  W,i1,Z3G#/: )dhiIku+4dO-B˖y'2owdpz)⬷> Rpd q'ڗQF3  eg,\K3HIgADܳ[K<`l)XyhᗽݞYPg_K@4πg{rtdwr3GƏ{Zrj夞({yi0V:$|Bɢ{fvYN^-- ςNq9ipIDpL/5Yс!f ^YhlҺ,YԕaN,RpYr.?KOAK:i^26,e,Ee.YP$|,r|g}Ba.?c9h d}^gə—t/I4/-*Mw-'Gɽ-2oihYrIw-K9TpZp2A"rх*\^Z4.u9f<Gơc3I54-9l - NsKNZ?2'- ¢^ρScG:hu> '{yi> Jow,%I]ƲmZ%/{d弞r}\N[4-ÖU 2p<&W&x:YgZrҼC.X r:Z0; 5mtРD|Bs|3G;Bt5(0<&H6|V!yh䯖u ϰ*U|+Yزix%f^t{KU.;30L/K֘VbfB˖qlt0gN,Q$!]5UBHH'-҅ 9gD&Ə!7{:+hM7p8q9hخ/O#!2 4 N 'K$^ g$gRd~3 iƴKv Nj˖qfpZU!.;3pK_.8 ; xU>P 'N5Gؼd҅'`S=,ƎfyftܵxKΩk`9u)F9h'疅Z$oxBi rȮxU;b )]~-Yr<5X倥N ~NyK𻥓uZ/d5&4G"GNܴ0GG˖\2B̳oB%{ ᗄӁ,i:(RpWvRI./qxjP> :rAzY6/KvCSSiⓂIk+V6%›j@R, >pV&S)FZ%i!xЧ/shfPq%Np8+Q#aIVp:tHY~e 2.*r< $/spG˅KfD+"qNbr< K:ZH ",\%fxCJpJ ,.uB>B嘊NH  8^ym%C K HOph^WPrB Yf 6/q6',0$+,hg"{W9!gx0YpYӁ}9x:%彋Yzi$Y/9yf2২Y͟.Ȝ-:Z$2'ߠGߝ&gaGV\C3Ne Y &^4xˍl(e4Y'Ig 530Ae˄͏:FvN[gHy(TLB2o`e,cG &DhDF%ee4/ 9 sX0#/'E9k>14GN{lr,Z#ܳKT2iqٞBjӹKNVϬ2+/-$м *ٞE,E'_Z#Oc_ޢ,[&D"rԮ)8:\~ |bgN Y꾴ښ#O- 6L//{~MGKBxETt^s/ }g3ƔtS'E0_TI2p2S4rg90INdY/a:Zfx"8rsফ;,Y kJ~DN1r|_{P-^Zxe囉RUVsAIN @ȡoϢoo:t N eC&hYp2wK2PETȜ{Zrɚ8$$Sƴ &JzN\IR\Jt:ZP`2e˂KeTg`NYxIFh] pܴdNY b)8"]cL7._,м!x#/fxyR\KPȜRtSg)8# γIg{YAxY> 0T0F^$< px[%|x% .td| ݥu8,> =N Y &л " ŧ:u,{&x] 3/-^Z=YK˗ q# NZLaeTŋ> GȼaBޖ\~ZQw,DtsKDO09Ie˄ta˘).?xB8 ?`,KG|  :8<~Joo,Ν uw\tx飸,[M/:05l|K> 6*u8YZp6lnzs ¡yj~R}g,>i9 +>:rqxY'EfVr͝\^Y.s.Yp,o94Wh{4xc/KVYi3G/,Ƈ)84*\~tc hK YЭZ3GK\5D< h`(N:JR|jȼ<H_W<iYݝFyszZ*$82BP?XxdN[㖷KYr}DR>HY:wz]NZ}N^OeIӝkK&ag>5l2p2'YT<< $2?$Wp;ӥi~P,rEtƾV%.2 YM7'Yo-gD|;R:pvg -P̅TGz U3/K7 hZPhjdȼbV^ '@,g^fQ k5"rд 4>iw] 8ؑ"r̅'K33;`<XYB˧.XT->X =fr˖q&yf%4+XrҜ | '-3Џ?-^BӖw-pYg%&L/{"m)8"\~R}g1^g ~Z0^YT,> RpD>/-n,?< {Kr˚r#g+s .Z02t^nBYƥL7|KY<:Y˖&|$},Vb~yfg-.}-3Gs$sgN@Ȝaz BTaqxYs˖t{Yw: 8;3gV_-]ayi"}, м oI2˖Z[dNZ]'-3s$^w ,Y).!e*Wx',rA9yfrԴ h/,<D+^d^YYzY0-M31`h_˖՟+r ",A}T3䇂A| ?W%fxşh yf9zYý NR>Y $g-%Yl/w-e2$X4/-^i#_-. t&ˁDr:/KW΅-ZӁw-R02{YzZC<r'}GWr\giy{eYIbiZ<i}g n\SKgP<tͼ&hK꽜IK䑥SR{k`оl,^[&-+"RlrOFI мw-BG%tϖd"pD|gha4,в塤RZ*0T) Y4nM[>Oog-Ru .Zӥ-3ŗӽ,pGA3]WI^Yؿ.0,Qu9fQ%,BӖs,Μ/{P+S<,ǟޥW3-$Ӏ$ )#cG{0 |01/w /{ZK c.YYZ\hHg:ÁхU:)vLZ%zn҉hN{] V~JbK/,2 9i'r4,D% Յ.% )aY4-% :$, @˖9iV\ u:ZP,7˥ИpJȽj;B˥QihYq BAg`]: pL @VY唊rR 8#^䅞_<T ]x&HDq[>n9{bF3<tKV,r͉yh+^M,`RZ@'qx2N Иr/Kf1+Yru$4axNx#Ζ)8LpS#aK.Y|- ZQKiB˖r@.S:$| K˲xY̡L/KZL/-fx%>] L/,N̽yiBT̽8!C^X4eYk-ȇCaJtƾT"ax(ruo,ʾZKgUK3XY* L7,ڑYS3GKIg-9Ju$~*/,^e7hVKfc= {S4xY\vgIqs7rг/_:)84.?9hڹ|:Y̬SY'~_< \^ 03,5 4,^Mx^NiK\~ )Y2Wp)&t+[$.C>$|U^ǝ# ;9D, Lk Λx9 f!9ht/,RBˁ2iB,fPAe) iTӖ`eM3_6i"e.83:/w-z/.-b.&ϖSdYP$+9iDܳ/K4Иp<3*hb-B&:6EKG˥>-K7pC&ϻ :ZCH, ! CBO{ygJ] 2,"ޭU,jĸ{IzT.yj K障pΜ л$ag_K;^RH^Z/]NYܳhh^[$| ,fx.9{^YhT?IDg`Q}Jp N .Zj^Y<+嘄Yt] 3/|3'K:\qzY̾qg>5ӝJȼ8)zYԝ-XYςJ/XJ堊N },6å\7΅: -(YtxFV hY-DS/՝̜ꅜ,!0Qrki@xx#NZ=ܲ'/ˡKDL{lYr=,c$i+9 l"xLܮxR\Kdr44/ 4K^ᗄS "MBY^Npk)%, $/K Y^2a=F ´r &^kB, ^Zl \Xw˖/-$j^YGrԬBдm{9{\_.BGSU$p:rέw-Bt^Mˡ@X.-/w.r!g`l|l^p2"B[疌K9I"ax%9fgʝD&gfxGP&u>SqٞҺ+_ пl"z\4-4KiAx5:[Y+>\xDi{ZqJrڅ'/-G4-"ޡV Leh0#/\e'hUg`X`, ´rt3ZxRi ^N=^r{r0&hL86yk ,M3^Dؽ˖lܶJs'Ek9t(4/`VXg / +8م'KqViaez[e &{sb.Z%^[זا-JҺaZWP2}h]/wx!p݅˖x3JwAx.z=u9st̼eudо,% 85d^ W.f%xc b?/wLL ROc .ZUKw<â^ @,M3^Dܻ ;u,/4& о*,uWS-SNu0+<-]IaiㄏœKz4K[:,旻|*B^\XG4 dN "оaiYqNᗅgiKV q%utMw!ghQ/KJ6/^d<yfbh_pepi"\Kg>{nr$ bp ,&qٞ z"axMr-\^v嬛4KY4/ HoKt<'KI=vDܴ\vgHY NǐiVAT ´˄g`aw\2p5tqI*a/w.lVgȽnn(ܤqg>/.SLw˖LZ˖/K8Բu&\9g>4/.(}g~Ph_ b]:σSՍJᗗ4 $,ᩦxV r3.b$,\L/ }U;ZΝ-Ӱ(L/^[IBW嬩5oGW}iy.[ӠDhT`hph+]S/ ^೰ |YB4< Q圂rw}D\z)`ZyY,k' Ȝ]+p]NZK倨Dc;j _ H̲uR< $ÖY} -gH"갽,ju͡wπ?,Ɛ.e,м_KT<>;:SiBw;=Y A)+hW<eiBfxC9t<d^Yx"D&/K`9ϗ-*<)9hZChh_v^ N19ӲF^YߥE1KD, M&㗥 g> B!wHlv20[-cRxWh-8NITs|*0A-_b˖|: hNGtvMd/TA< 3/yvX^azYhmA $/yT"ax*\vgSBYh9eN 3<$|/<>xr݉qxNƢyHέe"Lg,Α&3ͩph_6NYӖh_v.ZVAg`X,"oixX& 9qP嘞f8 NK&0< ]$ſ,g16wr By\K3suh^ M 1 NSrQtG`VAyk> "ax){44 I-.SN.[%C g L(f^5Yl^Qh_6h.疴 H,ND,^[ؽ4"KLvihY/ A[r k`&4K˛,^Q. rؽ_B%xS 4KisK$V 3< F&hZ{#k:H"axM ´O-$'EgYp'r! YC4/K6Kj\%JL/^]R^s/LΝ-gNY:[4. Yeq:[Ibibe9v:tah_v W=-WSoDu8$Ϊi/w-.ZSr ̡Fо`+9KD4Kb8,iYFzYH)gK1gK,0{qbԽ,2]N[SH&K˛0r< {l_qٞi!dռuBrز$$4axFx)zZk%82n9,Bf^/}9l9T/w-̶ykJо@)'祹eZ{sb*hvi]NZ%-]̣Ddܷg.Z:{$aV Gr X/{Q#c3'Ne&#\KJp+t ec봮.}TyhZh L,ޒpϡ}V#3J˭VJ |9gIx BBϘDrL=>Z=ghRAo-0& Rpr.?gKBymRͥei$ac3r- ygI$E=xFxL:aYИpD2-*YtEM$ΝhF1epˊvRraqHvsJ̾:} $L/{.Z%4Ӗg`M6yxE$,旻K/,̈́ևi9\2zvB|ByhrvL43GKZ/wxJN .ZΜ.? >Mŗ,B $/,Ǵ<x=9p8\KR/ "pZ-Jpy,݌E"i̱i\<څ T. ox%g>{f@$G'/K3$zY9x$2j+WQ'f^ugN#3< - P(t >]-.K&ic+4WSdYФjz]O)W` {fxfĬ-0о*Mg`W= 9g-WSR$x6zZHjTrtRpK м4-h4wg3%ue2.WSjP̽.+"G"ax;9l^d/w-$h^N5 lKNr)"t).s 3tK˔KBue@/w-$u9j԰2Iykоl(4/&s{+DܶzY, ejvgB[˂%uDçXK˛+:]L/|/w-$ӥ^[T," 2g irtZgNe+!u/-< G,r̽^yqN 2gͅ5KIӥH^Y壋uM3I7eN9zYkY/4$5G˝`м\^Yեu;݀<^Yt>Ӂg)<\~< 6&CU6r2LKdХܳ/:Ggоl&/`ZaT gp0н,\g.ZN-.tк4|)*r̽,<'K&֧-s)L/5Yag>]ZrAt `sI:)'@S([(.3^"f{6gC.?{jsuk/hykDgΜr ިH0,9#c3tSw4( |Hrѝ-t5Yo{gK>Ln:Y.Z\Ie3Sc:3ܾT"ax9gbydoK8r'-RΧE9gbwwI T,CtyhғKq29jW8ϫDjpD<R 9< },l$~ɌrxxE5As8u8eyfrܲ0о,>\VAXgX^ DܳK 'NS>MVx<ᑹ+I.# %t'zZNB4p#^M唿Pl},PwsrK: Y+e-&ehҺK3(Y;TdN3ϠjJş$9IˤB$geu$4HgD,HCBρ10zY=>xԏ},@<,^"ax3$e:Z%d*4/ {RIx QY6+3\Yσ>\XFxF炤x❁C灾T 9'+TE8ԥwUJ, :4/ E:X賁Oh_v)8L,DM3!e8GOTܶg_.[I <ilZ݅r ˤ 8\2jvWSgK<  ϗ- Ft:D&/j\݅ˌ&&/-> !gGzğ/`JeAd^Q|Lg4ؾJşhFФn A^ |3KyfRq N 1:\-%V7-+\F^I .vsƙ-Aa9hrp3zpL$Au9mBᗃS'N4IDܷ ;P Y>\@fYdσ-h 0iyjtΜc"ax1Kyr٥u9f],Yρ,ϫо+>\pDcKVm+:Y -KHN h Һ 0AeCayc^Ցyf`ϥ]B˥ri]N8K9fnբ,Yl| DcDt&fP˥.l,)JpKi]NYA 32RK"<,r##>C/{Һ9z[4-R<q .iqylYt`\NQ3Gl_ .z[,vSj0#<u8#i]NY]u8$γJpK,g0,54 Qj^u"H'/K4,ti]NYL 4ܳ`l^n Υi"T YaxeLVKݔ i]NYHf9.>3< #/{"4 <\2Pә|W -a"- JΝ,,"ax= 0q/wXqz*I.Һ 8$r󪗻<Յ,VAgHfB#/-(V:FpVt p(Ӗjt^O8,ǻơgkU4-^RaUB$RM[ƐV)82\~ @ ᗎiyfD4/ "ax!{Jȼ tj,ԸK0K7eXQ/wa`h_6Dbl^YfSY$| B^ zY4yQd^2sΙ\2։ ;BTJr҅Jޖihh_vh>Y8, $\(YH>Դx ]QOAjfZ>I^ q:A2*] пL"EwT@\B*%9jj+P0gy (ȼ<O&x) B\~ tEI:Z ᗥ$+ghQpΓ^l dܹ ,^7/,pB ¥8Յ'KN Jȼ~b<I+^+%V`h_vl/ `P 81{Bɝ9oL.G".x⬧ p~Y.?bI8$/,擢飋9 Μ>[a:`Ha!y/Js0T4/K|ijX^YӖ{htHY%yiP'VS/-2tG44/ /wKpYtrЮt%O&vSL̼頲0.'{zY/K:,tag>x.P੡xbP˥ 99Ibi Jt-YwzY U "p2, Q/w/x>] #_gɡDIɉi3gCGߖBa:qB.KL:˖N/{&n%囹fVxBLDd<A^^Nu;,D,{ɦxʃGKTeΕI@&mV,LR"˖r|Lg_'Dܷ ;K>xXNMr}b7: #g$~Yd& 倓Һ/ρGBϗB ΅ODp\ Iܳ ;Bx6D-2HY,fOBez%,X hu(_._ByhfgINлAyi4y84>Wu8%"9Ӵ* c ;hu &<uy:T f;t)8,h%b>] aLfx+9:VE~Zq$v^m/{Љn_[YÖr/K9bNr|!RwCiu;NK;>_,Σ 9E,RpV%xex"\~ OR\;<$Ibi6ax>x1#_[d囆88TgNӭkft8r fx.x@Wp9ϥ\B˥LSe>Q CBWS&cgBi'hU4qd掖t/CJ],iL/,b𷥐xgȄ,yd~Yfx+3!$ '/_-t)2q< Μy9i:T:Z&h nNPAc9hws^ZBIu9i:e/>A9zZgb r.x"I+:\~2  edZ Kӽax!D\KYr,Ygq8" r'wxлt{&RDwpTVXΗ-+(V3N b˂\2w34Lx!zeZӍVxjL/ K`9"qٞpj },;&޷@,4K˂u8֟`{ob<'-Bə>MI&Ux= 7$a^0%BD Kn#$aV!x#tS) sSG}W  ռdO%9K*ljꆉ{jYY9jr $^ Bߠ+KGDܸ ;zY,/i]Nx$ᗖ xKw|pԼBf^Z/H8X:N ;ygRrK"|^tB=Kƍ H{4t4pMg8Ԯxޝ5].w5Yral^4^\0 s2ag>A+M -4^Y4/-O` qٞ j)q̜c>ƾYlHͼ j˖sK\:#E Tg`M4婪`Nth_v5+:Y4M&,gKv6z S< az藻YM3Ư,uЙdrqY,UϠ4#/-4|d^'ߝ: nȼ6/S,_6A*%੦xd^YkY"%l/Z8NYN$< ;&x:Z <:Xu| a{268\^ :\,A9y29yfB; ́q9HJrŗ287:\,ϰ&Yqx:Y̤qΒH+tKBs4_.L/:nx52< EtFNP˥f^YwaI&,f^ZH, t3 r r#c3_4<>3< !g`R國:/v<YuK'yHPYcΑtKI$|BTeb-9fY|ews Rpir.?-MVxD d rп.WS ;/ f[Y,ƍ mD, 2- 5R\ .Z/hx3˖C&[X&/K8Yx,$^sKPqEs!:gɅiR4h+v/YMD,,yrP̼Sje'K3sGzUT.wy I2 ^ZKW}63|П&gGKBIR=iO 1\-,^t^sYƥ Q]_Iؽ:Y(Ζേ-(*d9yfYf7`MazY,j){Ք<-JFg.ИpԲXsiNY$p Ozjĸa: )RVs Jn(x:\`3r͝-38sY;fLRpYr.?BˁH/:*q .t̲j[SBy唟1r4^Z, C_{NӁ2'`I,g.Z%˖,3$^3pK< hdZ>\9;D{&U.YK.g˝'~Zct5YƉaˈ|Y )h{K5K4K(WpL LeGز;h"x6s p+X %9i]N'˥=4&d^ >L_ zZ ,侳 /C8wr}ZgNhtv,>7n*fx&|9f4t5-9f^CUNE'D<>Mnn9iN+kp7]}zIh2'-Y2ќ>@Kdlg ;@pD`\NZjF )K< J gVK3%8tPews)fx+ 9\NDB;whQ,Ϡr󪇟E':,$ŗ:)]Ϭrg>Y0 o-9gPgTag?YpAd^Y_,:g w,^ gʈh].3 \M"| neDӍMB˂ <eE"ip`x%>Zϑx%Dp4'/4!g>6  ^Zͼܳ\~Y̬7hS-BT,rd#X+cpYHge(+SEd4$ag`c<:=(b:)ŜipK/[$ $BJ>,V8¡dRpdApƓINrNP唋J|},-嬒r| , gT Y{&,5Yr.?xe i2L! iZt\"LɄ5&^t@疓G>-S< gNyψhN^^х#BBe\{_(!#so-& ®(I*\2'Ko9\T<}gA  ydRtfx6x&x(s*׽5 u_1 ,Υ7,ìNnP25YY)唈^YOw-vnP圊>>RH_/ NF л,, Lh0'<M3t^thh,gYв೧]9Z'x.RZ>L^be~[\ n5,"d^Y)#1o!TU4,9W< $ ujg1!}VxJN]?d4sI3 CI%gNRm (VRHԤfx&zYӤGK5g хr,rGNIH#ɗW5ܲd'#h+f3м!dԏ(̳ z葅)в,>  MV/~X ?,HPѝTͼhf$ag`X^wUY:FvÐg]Q#G_W*J|< i,p4K&H.x=;bqNv8Xg4Ӱ(|h^|y0'V}T^[XZH4/ lVg\K?'М%9^\ؽbX daRhGV PHYL2!xޅ 5HY2%2O |Qu;oi `,M3^W&Ctl>\LZJ$, NeJᗄS&PpRKJrќKqi ,M3˜^ 6/hT;){j&mT9}gh\W *3DYg`faVq]Nϰ)J O/KG| u:Zi Һ.B&Q/w._.Z$au9oBxGȽ 3›NX.xE;K&^^Z"KL shr҅p,Y&hiu8:$a ™M3 4̾A*wL/r"axRgN+KowKx&ez[&a^^ HWS(ɗQɦx$SL҅E"KL1DZ#Px:iBơx L%x%* a ܖ |$оNmz,/ 0^ 44K$/w,$2̼6/-!Ds GK4x=9pv fĸ4Kb|Ҥ/KY彋ނȮ`Q*Һuh^ "ax^xolD,M3˦p©f * IbiG DfVix,HYI о^t+]Ӱ+b%pIA ²(P̽-< s}Vrv оl.r\j0E'KQPLpB.M <l% / hRcD0R<{j0 !x B\,-WS1dW *CϬ9gDu;KL$:vq$. ]0,M3^ DL0%BgD Lb|FyvSqXgO90^ 2 8%ЉvM$40'<8 0YDHM deUD#HrήrEj|<2.ؗL INZr./hh^Y;dIJ&Y5xᗃӴ+W~R~Urέ,$4 axVxD4/`J,TȽyk4/w.l_'e0T< rHK2t&etDؽ<DIS^YR*嫒d.xe8#݅Iӥ\2v`˂0x=;p(/w-.Z&A^[΁XgX\4Kv嫆^6h.+>CB 8"NWp Y Jо% ;zZJ^]&fxCftv:r{Ld%u; -iDI//{rkL/GDܰ8#0).Z, ¬g19sP,/ `a"KL8-f_d>nLZxd셝It`K˛Ix( t@\r<5ɦx3ڹ# qGN{oYL\Bepdi<K0F^bȽ.\2\vg2(l^riH<*\0 /KzP`Y ]ΰYNމ{i )g`sо+̽-Z} L/ ^\fx;YςȬ ^0ZD𧻖(/w-YLl-@"ഺ\^ 9lWr%{wخ\p-_fR@оl&"axVxC^N/w-0a9t<%Y୬ u8Eba'H< =KHHXG/K% 8tKƶ,̥H^yrrڜ 悉^\t/`J!{F$pо+ZL/B&烸e M/w,} %彊sϗ.3IL*ӽ( 'N8h_˖+g˖ip&M"KLvgP4KRx&Q/w-2YzWP2K3³l^++D /"\^1h_^Y.'$>s{KYH4Kv7ز^J"axVx!\&E5h0`eM3ƌ-ᗎi9L/gD(,M"axfx%)L/IBB>yiNY;tO`h_vZI#𠱙3o,R YPet| r|l![D4Kbh_dؽg˖Sea ,,A,RpDk:D/2Ig{3iI'&,gP )f^.Yr)8"\~]NZ XKe~YgZ% Yna9rɌ-L9OdD,s.ye:uSt)8"\~rVYLgq^/,WR%c3tazYSRpD pYtͼ&zZ?,NY噪?Q%/!ѥu9f!gx3>fvHLIDS< â^ZEx^Y .JI:% wTV rX^Yb^ )YރSyⳖPВn+9fT+FYf'DNYΒz;DFH!I7m˖qh\Ɍt(o<4ihYq=FY2]Pʅ'azZHg ИpF st1zYЮx *C|&v/,qyK͌8FO.Y,ΊvӌY'c_{rΜIR;^߂%fx# CL// ^QA,η7Шhg}g̒#ρ -$݅5K.tBd,yd~Mw,!wI8dɢN 藻ipe' jdt(<|FK˖`^e'V:Yвw,2s/K)Lg`R)\G9Ν* ,5G<Ag`M4 9% .F^YTj0@DTe4|!eVYr+9e`J>Y\Bˁhi_f;l,94K.Yge͡w+iJӖZ)8Y< D'˩BhL9fn,LL/z9yfW)å8̽-Jp: ]`*Cd^H &4r9hΜ,ipȜGZ3*gh4K̮'-,3.<D&/{i6a|`JȼZP$β/`I,ZN'yz#%LW<9Ξ@(r (I w,%NYвtK*2*Hx^Z3갤K%P=,O, г,X"KLхi9rEYNڅaw˖,G9I mY zYYELnx% ຜ1zY eq$(tt{$eT>/-\*Np%kA*;g>Z]3/`G<ɢ/-'G 4uB$4fx5r-I+~] n|2 &}L//KGrKJtGAc3oaj2 g>YCsF r46{G˖SO:{(WwqeNL BzȼAx ./0< /щCN ,%ȜxILJngDvKoix>!gZTܴ>2KgB˭A*M/?˅DҤ/,P((IZpJÖmf{ 9gN))4& YS/y>ZgBtT ,*jB˖n9zY<  3>SK+LfhΜ !g`VE$.$TRHDkQ,ag> A #?sK-&&3@< ,Ϭpd˂Q#cGKRܳ?:R:YYLi}g(gIπye!u;ϬR& GZ9e"4eM qz{<OF=+^Ž/-f7#D ao!]V\tezfV0 wg%NY|, . мqfӃ$|mL/ `[>n+J8]{ϗ rͽ,4сq8$J.v$4 ax)\ x:$|<|M3%L//ɡI&8Ɂ&yi$ #3Uâ^ix*_YC.`M,ZI4KΓCZ8ba9{`+ir!g h^ϖx"D2Ҷ,Y 懼 !-Y:vDܵ{ FB<%^_˗ykKRp,RqZ\,\i{gBFu ;bYۗ-$^ 10,6p^RpD&4ugKlvwU ^J%kt(0fx->X Ibih0#/,Tρ!9yhL9jt $,-;0q3?SX0g\ hLGrYqdl+2ғ/:/I:wAcUh$/,t,+!a*rht*0~| I!gha:/KW4pB4HB]O#,5X%{q N .u$YϖuhgZT[Yb/@a 8)x$jp-%A'}fr)n .qnj+TKK.l,%d0'<n+:rh >\y r,rՉ{fF!  jvR೰.^X- @пl*o,0"jt $h{37/ L JfC.g3q#c/wVGhh4H Ag ˗ Y05> `! >\H3I.?Cd, -ǝ# ;sVyg$g|τ.WE#/BNp9E|Ddx# M:)pz]NYΜiVwx!gNPEw:)R\ @  “ 5B߁9x$i]N炤< }Br>MnKKBˍ^S"!yk2'NqydԏVRpd /K2t5YYϰ&_7TY3,VEt%Eh7eFT4Kro .,7oy(iYΒ+9gN:ςP#JA Z`LgDp) 1Dܴ+TBJng~QaDܰc{IT]P噓NZ>T3q,Ru8 t'hh4tsYp80#/K9,{g?D{,c>X ӽ &̅g> YәLX\Bt nN 2TNNw,Ҳ/,\V.g.N,0. ,"TQȜ /{$cr. ~Y0E2ΉXʰ2KJ 8+ b4KpX0K˖\N]Q6/uZOg>,ՔL$-)8j\~ :ഩ [I &Лs'  &g`e4-(p2]>YuLl/KJ2e`)V{< #ghU!whM|hZrA,ؗp9XF"pL*Ibi JL M, Ž,YwwY֘^XI!' VвK?Z$| 80=>y`IgK;*м e'Ri{@,V^XϙF5ܲ9|HAl^Z>RYY A)Q/w-gN\]=Yp'xgAoV/-Y)ӭхeNY >B%/2w|ʲ 8#ag ']:H, \YXAXYρ_jb|\B WpH,$.XHC/^B KΑF^YGGhTc8)wpTayjȼgh_+ W~s88ʒy$4a}DлgCd೰$NYqydXYϰ(ggI4Jȼ . ;=ph_'p11Z\~7eDؼ#bGQȡ\~ 10`Z,y/`m3o G3< x%VsLW *t& .d.?1dW l^ĸ`e4-0 b7ʈyೆp,4L/RVAg`X Z0,s+^BΜF$agR೗TK $!ggɼE7q D _6, <>M L}{AzY 2Y;Ií"qgNY;L,$Ta N҉{pp1}T޾Z]3З el0'<uMK>DNq*rLݥu8Zt3Fiye~3h,j CaC. $g0eΠegh[e| 9mG˖W| ܲp)L-eYҲ/K*rI^|5\ghm(YDR$xޝnneq;YY6+3\YςKКF^Z[HYNZBIqyҺ|9,'bťw,~NsK:q N 90fWSLi+^\ 8Y9Gz\~ĸ e,pȜbix9g) /+W\~Y< \N[пl*{> r.K:q]NZJq{ xU>Lej,Y]/w|eNiFt<3oG‚gh4Kro{lve^Y&㗖tKG˖吸iꅝ!2'- :H/-g P`$^Y.G[Ӗ^Dܼ ;t$l%噒2w-iB'u:\%P@]Ε?pYM3icB.1$4AeƷLYrƧE8^2̬7gf)+e:ra9hM|̚rK$[W.Μk~iD-l^z #>MhӖ-'/::)*3iB ˖L> @q-&qv} ;I|4j.?Y9gEw, A<2'Z^ Ӗ/IY>`ز#^ x![i IgD4/ KD-Vt(I0NF NgN_KG$pܲk$-J{$B%^BZ;&PσWKP>-pzpT`ě=-hJr\%;Y{͇pTH wKK^൑ynx%[+.;3_%h^+&>7/K&re4&c I DdwYr$2i2Gs"kur-9gNYd)8"\~L#ܳ/{@KBI7sj):iqt'/L0yf_WEgHh_Rp66ai'r#(yf0!Z&e:Ztf'KYӥ .ZK<@< t+Qk%9%aR0yhBIiHpCܵ`3aY3o z\0)qAc ǞAo-s4σh-Z\N&Q/w.兜<> &x+pG=!-9jei4мHegKDs*Һ w{0'< $N[,>"Y{4rҙktؐ.,G˖oazY)d_Bkݞ=ܳ@f ;x(|YXvfr։{Z8^hL8$Cp9@s ꫎Yr/ph^LsNZ]', -/-.[YyeO0:Z_+L/ D=:FpYIKF\,p$i $W͝,.,&SX3G eg:diVU-$a.;3.Yw,Ձ pYn)8"XYςye䇂gsK'Nܲ|^|YtS`Yu]N7 axH^qŋYGKPe\W| ,G9!g`Lr`QGФYhtI&m ᗄS(Pоl("KLg=qbij.?Dܶ/K8H˴+IaeB Œ.:s(B[Ap 3Kre4 =ܶ vvLA^^6|YgGDؼHϰ- /wH0r t ].++!s.%fpRv i{9ҜΉXXW Y4/w[ռ',HrW .o,^H]NY+Y{t,g]Ae˔,iylZ$|h0G ϒGZKS0Gs)e`%Ui!Y!w-NZ:FvscR փ!5qx]  Ұ囒>YHYq|{1Jp[erzY<+6Iea~Y $9yfX % ̬}ftw,Ǝea@ /,57l/- N$'K:ϰ%K5/h^YNg-ZT`Lc9fB& 0`N'{V}:JdNZեu9hvRI9hQ&7-_NYM3 | Q'-RJ| rmeiB yjӖ塟&.x% .YiXg09h0塪y>'-B\~Z^I:)"t]O^qٞ YT3h^YMx% ςg`W<LtS<ITGh]лU灟)8*\~Z^uZ%Bn!e6M3ވkܐhN{"rȭ9hYydS) |<eQv}g^&੦yf^H=z{4.w-&RGg`]!w,•|M-,h!g,)8bKg|<{:YjP.%XK.gh4KKڅaU̽,&A>$t$4ax+9hW^ZH)-etN{\ s,.Ag|D,M3fU{ "hI|=3piв>ty{caNZU}UyeNYhM[( Y24/-vSLw,gB˖qFYpН:teL+fYt5YXY*̴8 :$a ³M3emDŗ,#㐱iK&^U.;32:/t/CB˖ENi;Y2K?Z%H^yqNuaNZ%ç-_YX$e /Z K>H{TؼB{lD/w:.Y4/KBS%p% &hu8= µ>$4 axVx:^Z=p]N el).#/KBˍ&/.(-":gp]NΏG!bt^j^e2м-K,H^Y4M3 D4)8"K.Z̼yf p4K˛x1zZ>i^]>J{1r,jHyDؼxQ/w.^Vtjᗖz^Z^7/{NN\,p+Yt^e(U3Kˊ^t+L+Q/)J%tKV/`PDt"şIo`O=,2,!zY#Y Z,l_'̼ /hZs`vSL݅RDW֜ -sZ=I3{-ρ#о*%9y,^ID&tHhXg^ B倣 [ ;c)+ 0^Z,g>{;F-u%lg aJȽJr̉8YK> QH^ OtܸYg./`PKtܺ ;Cx;C\$i+N ֝-$4axNx1K.[PzL[vIbi˗ q)g?ܹ{IHOܹYl{qb< wdӂ$Őh_6H#RWJKᗄS+2{gо% 8$)diV4/ P>"axKJrҮCDܳyg@. x= ¡Y)8Dj香fx39jgDy֭N[|4K>嬨YYҺag-%vvjtK@4GOܹY>rngM+DA|N>rfxS9w˗!x)TKp/wfx;G嫹km # ; i{gRz NXg0<,D^ ᗖ 8,t ;hߖ#;<%gx2iR/`MtAjG,CI&tx+ag`jtx44>TLHDܶM3^G{5],DazI,ϗ,Q4ɰhNJz%/O4-&4-(ӥx9fr-3) {݀e&ϻ !GʝHY \g`N],^JM9gd huςgSYtKгJp唂ԓFt G!g`^/-kڙig-OlIDؽ,M372-( $"ax>}Vh/[h4K֭r +:[/-) ]jr\hhb˥xJؤtpYg:w{] L/ K2/,^U.?H7\Rptie.ZzV/c8%:Y./P\~b˥&N > ?'@։{h^+$ 4 7ܳ/{&-Yt e4W Z݅g˖ ;B KV jwGzY I'{RO ;K{D,,o,I0/w,@ʜ ht3BkJeDFoY;(r̽ni'hU4`R/-Y)j,L), iå)gSeufˁaGg``M3:/K:0V 倣J Yi~ "df4uL/^\Y嵃BYg\'ɡ@,M3^g*IZr>Sgwtx59n𐱙"axNx=rؽ-7zIí Z)Yri r0%u9jYә9.& L si]NZpҺY]AyfVrՉ{qY7g' cs(8"Ҕ 8Ձ}UgKH&`h_4d^I2, eL)[L//{xHP#ayl/XM3'?d 0/-RWSx x$Vi@KRMK{p qgͅy MrAXg0/< Yь(/w-$-ZW4KX/ K /w2OYL/ ^]:\I .A^ ITtvH.*t,Yt^Yȡ~UqY*u tq^[Ձ|iVβY/.)Kf_6K{'"ax1zY)E Ws+. :pDL`d^o{d-Dܴl(M'wKKJp4ȣ\~q祭$VfTL/2r 3!g>u{T,M3^ Dܴq))9iQ{Qӥ`:/K7ZWSPk tʆa<qzYRt/[*N\$, ő]Dx,[.?K"ax#ҺaGOtܸYg\-b< o{0pP)q5Ẍ́KZhu<u9hA:-^ ^ZL,i@KKp-ؔ:[0+ пl"Z^[;ӝGg`Rt,[H4K9s`KZq&g.?}aά BxڅaW|dS 9n^$, -e2,Π'AB|^*th^-+(YBEKY<zZXK:A|IE:[%7euwK&EgKHLqU/pY[X!]3o GgS<n^ZgC28h^Kfs ^7`M!Xg0_</- n bKz*:5LhibxI0о)t ^NzY4)wKb\[L/)3Ѣ,[H.y` q;Tep˪v)@en 7hA~E%2Jj^ ZL fNP`+>^Yng}\2K@uh.O]D,Z.NDܹ/K($vh]$%L(YNX"ax1HFq.x$Y/w-hȼ^\XM ./`F^p+\+4t|#|S>)TuFgL-SD-, ,t^Dܾ ;/V^Z$/KWSZ<#GKRTev$40/<"{jyrғ%7zu>\ܴ"CBɗR 3XK:wz[-f,P/K)t  :th /KGSJpIX4Kb=28։q$/Kg .vj^Z-Dܴw-'O`9x❁d4|` oeh_`l0ᗍ%Nq:JenXg+<gWMD4Kh^¸u;q:reHrfx-GJȽ,t hDRrR(/w._.2RUJpF$4K$N2{ i Z2/J8 ͅ^S< C 9L",f 9Q&H$, )V\t/:ix-9lF/Pȝ-)2?i}g^ВI %0y/-&л %u8#,߂g^r;L N g`nK $80.:$Ag3jgduZ@0,$]#G /*j D< | b\~Y xHƂeh&]$^ZLrK3%xK β ¤ygp*ZgN^YX'//ˡ4\|Z_RrΏ}hDa"r{ @M eBt4/F>CmeMh@#N .Z h2T<- ZpL:PdWI- Yg H%9Ӆ .Y 4: w~yleC:\ZI+^Z&HPXyӖs7[ g_Bu5Y^Yсq8\IKSS#:YH- ZЬ N A׀IKBPGKW|K4L/ZzJBoc:Y\ /Y('EoSfSg˖ՑyӔ̀ 4AybPȜ o 6ugo 9g.GˡdK"$/{] L/ g`N,C) ͕!If}$<#<q8\ <6wv,B$|pv:r|10.xgB˖i h'rĨrg-HM v,ii0/w.BzZTH[: -l;I&sj)fZ8\~4ITMLSXFryf}%tSs˖ "c_٥ hғn!e(~ NpH.?:YӖSB˥+,RN >]BpA$2pBgE=^yqIÖ JN .[ trh^R'KsDpYJGK;JCh m堋.e!Λ,Z%X?TZfxC.lgK\Ju%ht/G 4D.x% 񯤙&K7Sx" .U9^EK9f4IT<C9:hdPK3s%2˥&7K',^gZr-FCLJ7[^/A_K8tK!u^-Hc,B$4ax>x+;ɛxE9o- Z=:Fv3д9fF,5]ydtS'w L̤:Yv 5Y4HN'UIS"igԅgQ%x+ (v^^UDYvg9}#^JQ q*Z^4N Y;a?,Zr8"8)zܠΉhRp|) ,mD)jr}lj)A ӽҩNYUK8r^TygDpR•"KNX#/ˡY0 -(p7r3G_KTqITEz\vg 2_KI>}N^Z$, PNdP^ .tƐ.ӗ r-Z|^>8 ggVܴ_-X^u(Ձ`Z-K.Z<[jU~V.Yh 9Bwfx-a\63ЛYpBwH,T, $eS>L,^tη7qF ^,j,jKb ;BtB5YJ$gˁet(օ: -hYr.?zY鬼껂͡w+^RoKy0 ϰ&\Fu;J} fx,p:rх)84je:YH.?:Zg̡'ɡB'ਣ3~Zix6}֝ Zt3̣.Z%wKJt .dF5'-drь夋I8fP:ZEsI8#EyՑYƁhPE'K<{Fkcwg{ '(hຜ $j|\f^Z9Λ џ,.S&һpx#K.Hw!zYh>H D 'A^^rtK4Y3-ȜhPnLbNpI.?{9i`/w/K:ߡVpD{,Y#㐳2/-*ȼ|kBK33"v/:ydP3UK3sEџZ!,o{ СhrhL9fRpFwz Itc.Z4qZy 9h- RpTx[S0/;)Yd4K˂1.?wS:9dR\ LхfvgV ¬ Y7Jp8X݅9qN .Z GK'>3<.BBa .YwhF7x!h7|i/)84j'/, /KT>/-FϠ3Νi5;wJA9^Q'E=3pȞbw{O0q:Zt/ˡj4KHrҁ9[R#Mtj ~ZyitYqz NXa-z# Z N}-(4wWp4T!z ^qٞ۟+Ϡ!R˖q%@M/Y{F3pKkബzv)U-WLk  Ҥ]NZ%fctϠ&:Y9.?`RM#>IdK,/{+Q#!c3tRpG#c`g>Z={r޴ r_Y*VEd-旻/K2,旻fIXNZ;ZH0G}a4Y^b<%4bZ%b+:r͡\^P9zZ&sKZq NTt^+3f^fx2h, {Џ<d\ $L/GN'Kr˖/ ^\V} t ]ʕx9.iw,ߥx$:Z灻pH.?zZQtIȡY;2Х=Ƶ+] 9f|8^I5=4ieT7ӓ:ht,'D»,NZ;U 9Z1 G3o,$iG[$| ,fx<%fI ;DNfx>yh<|V[pa2hܝ{Q{݇} D2$a|ROiyfY3oG췖g>@ ;,>HIqu [ Ee6l/KPזc> 8=ѪZӖo,˃P+\вi_CFt+ 3.?K:Z>],Nd_BL/-Nt|K%G߁4/NK'p2 -pD/2kYr΁bt*y` /ˡI.?%Y\~'&|Ni1g,:x)Yн}*PQJ u:ǝ# ;啋'rK0Ka,#!c3\(xh4*0zY˥{pjCC02g mcΑӗ9"%Tx[hS8)zYwaFHDhy;π {t, gAq{݄gLozZS0/-,݅w˖l4˥ZWSᗃоb/-gNW}Fyhy%Jw^Zfx@a_YNY+53Yρu9fe/L9:t^uh'YI-೶/Bry՟f^<AyKhYI 8"D|R˖H6zZHY3G˖ɛxE9r}gN.$4 t^ ҺH^Z=^-ayiP+"󢀻/~VgM:rh;/0в嬒0&jr26@#| e|<Y$Vډ ;) S:rA^[,H2gN[gD|,(L t&\|lвreDρl/xFaKNY&tpY6囼 !._y0#<ŗ89J@sax~'r<:<"eu:859nj+T\O4u$uiwKBc|&H N wq/K7 DRpdiJ0-*tpw֜Yf NJ3͢in!e-Gɼ}+Te)BӖ/K"2σ/zr˖h֥wˡ& , %^NMe̿/&.?D$ Ṇ ,Yeхf<>SK< JIeD/zZ,&)Nu/,)bl/:/A',N!c s#,^Ig[i%ܲa8d%x; 32;/ &iGbρo<gJOw,yJ4E8r9 56wt@YH(u| ;m| >cΑS:=.9hമ,܉D2 N Ki4&Ket*0}>B$4ax+2hdZ͡wMr, -Rpr.?9IZ$|,fxYQKJr嚊:>YaՅspH, & H.GN+䮧{ 84".rŗ.&)8Tr%ɡx3d"axUr,৑g>fr%u;Bӣjpt" )x; "KL݅g|mQL旻$R\~ &ϐ 2+]S,t|IbiDep%pt!gITg4p:4HpBЬrܽ-gL<(F`@ H ,Н%&pZȽn:ZeJr< J>_,&\G}G˖n: Np ުOI 9h㰼.^ZUg-ZY=jϠKG-$4 8̪^|BJN . :pTf喜-^[t7 8Cy;$<t[gQ0-\wi{GeƔw+9ePx J_{C2M3o,ٞsZu az[g:t)yjj*fx-*#>W<ফ<{e5$:LY9呩#h10DoՔ_Y'L//{g:Paye!ܴ v5RGxI#/?,ؓgw- ԨLs;:  ]4e3N ӂ^Ie4=$iY ,yHgӖcT`_<LϠX2˖TKCBZ),.B ECB0 c,(%xS k:='[v)8*AI: Hg O +$4 ax% 'hz Q/"ax"Zϰ);E'F'JN 9,^ tR,M3˜^ W 4:& ֈ|)`D&,/zH0+<|@e'(d0-&3)85|*s'"a ,N0ec N H&btRpJevi3@\rL/'K]̣Rp7ϕ&g4I$iq5h0,RvH% ;(RpY/$ഓ,M3^Rp[Kvᔜ%.?:L/ )8 o0+<,Y5u NFL/)8+"|&.<- NL4pR=ढ़оl*f>-pSg^ yn/,lK `9.?09k1EZq>H IbiIpZe42 9 ;&I4/y4"KLPDIqI,V>'I8$:cL,N +!!I@Pui]B| FgȳK:3<Y &7HLRpH1)8CJp7.?bNqjv%Sg, eup; NWxjxfRpH)8)GB*'|N B'0S/D KvLK>A JE'K4M97-$Y(Dd.?hiBҮ Rp9$#3%)84H B }\B2!IB 0Y A),3 B$ofB2JNtKvL3>MKuMR.?hNL/HRpVu N P.q 4HXct!I藸, H\~$ZU NT4Kƥ s{9h^坂i^[咫:vd iq<pq&EaI.j&P-'>*54)8#'K Ykh\"ax^x+N %I)8$RpJ 'LL/yd'pZRpJVAgS(9IfxVx!HRp7#)82 N @ :%'148' "@#_KI+ N 2.qI  oXp<EHWPH0cDRpb'KJN& TQФ(Rp[IbibLLRp(,IRv :%RpGФR Ζe0 E :$|,,fx,yyܝL/ )8#B"KLAIdY^ 7'4ؼAKNФ &oc:YՙS rhgQ30GGR'iBÁ AI8BA!zY 'Kwe䲖 q8&{ . Μ I:)&o #eH^@S tBJ,d w,_`I. …ND&/9xȽ~N[Aeʡg`NPQQ饜KLA^[_:p'!w,YvBᗖ|,-i}K U =夳4<]NK5$L/-2։qyiNY.`PT4/ l.yfp:L//w- ؼw,'Ef^Y:ݒ<83GK  tB$4a|`x+^[S+Dܴ-3G}7&h~X LY5*rʤ-nn2% 8r: pdr"aym$ܶ~DV/`M#aykV\ ,fLxLŧq.YA Pei ϰ'Nuоl)$d_BRLG<-^D/9zdIbi\JL/j] D!4/ E м T.8"FxbρNeg,΢N^[$,T,ZB\灿PXf%)Xg0< B02OKLG~RsM*BΉo--ϰ-ZT,!_hES{0K#p/,ZP|+> lR[ N"GDؾL8e2t о+>\+^ N9hr3,YrGKlzgҡg`T|jϰ &U,49xWSiXg<e N^Z\|g@Ρg`Y:/[ M3x-$/EVYFv(g`H/H铭ɁX^g$|as3t@gVdggdӥ %Qҿ[Y,t#V2 M嬞rхq>7S,2'/Np9ؼ+Юh|e7t39uL/K3f)G( 3+9gD]-v/YdN3!If́,̙'+8u$`+> Nr'`I{K̝nf G,N"Gn t,*\2'S)Ф೧KP g#pK|-. 7ŗ(iegˡ6C ,P4(R X=ŗ .ŗaxyi˥0+Y,H\K93rq8$ }s Y|  4/]x-4s)F&N > ?ee_*T); +=o-.tyhY̼ <#_ZI_A;pBNtG+ bD; NZN,lhj(,~Yӥ|efNQc9h< Yŗ-Ytx H-YςP0|}&Pg,j)ScGIQ&GppBH7 $pſAXN Т]ZpI.'Ig ŜY< şdNX g`IeL7gvEPew$NB˥+$ϰ'NdO4*5_- g8`\Nл@YeKK)nMa%"tK33P"ax>x ᣖzhe\v 8#B4&C6*ĸH^ޖnrLLqٞ2D&n8%98 TC 9Y[,/ϠpWn ebGpazH\KtG(WƔG:c_{GK9ӂ&4sR8Ibayӝ9kph9h0&eD B:UqB9i:$k囹ewux ᣖzhe\v 8#B4&C6*ĸH^ޖnrLLqٞ2D&n8%98 TC 9Y[,/ϠpWn ebGpazH\KtG(WƔG:c_{GK9ӂ&4sR8Ibayӝ9kph9h0&eD B:UqB9i:$k囹ewu2C 2A 2B :l22>2\ "6 Input file not found $( (> >2C c!~u#Ͱ>Al Ͱ>@A!n # >!xG>*_R# cl!"!"H!n>:\!(K !A "  !h>w#\6 :\m  !~#u!C >(%T]  `*C B(  >~#%("(' >[@O>>+T]m%( <!6< N#F*xgyo"<*z!"> ڑ= ڑ= O>y*O>O ">G>G>!^*> #"w!s*:_>(!C ">2D ( (` Disk full $!/~#u!~#(%( u >'u>G>'u>,u>0u>2 (!2  w# 6:OG_A[xx'(% : y2x* A R)~#" !A>w#w+T]\  1:A6!B" %>!D w#*w#"C R(!C T]  ”!C "È `!C 6#  _(Y( N(e>i> A C C Delete current version of DICT.DIC? $Delete current version of SPELL0.MAC? $Y $N $Insert a bootable disk in drive A and type CR$More? (if Y, insert new disk in input drive) $DICT DICSPELL0 MACTABTOP:: DB '[[[[' TABBOT:: DB 'Z Y R G D M S H V J X T N P  @ @ELL0 MACTABTOP:: DB '[[[[' TABBOT:: DB 'Z Y R G D M S H V J X T N P  @ @ELL0 MACw!s*:_>(!C ">2D ( (` Disk full $!/~#u!~#(%( u >'u>G>'u>,u>0u>2 (!2  w# 6:OG_A[xx'(% :; ; DICCRE August 16, 1982 ; ; written by Michael C. Adler ; ; Creates a dictionary file DICT.DIC and a pointer file SPELL0.MAC ; from dictionary source files. Flags are converted into bit values and ; characters are stored in 5-bit. ; TITLE DICCRE .Z80 BOOT EQU 0000H BDOS EQU 0005H IFCB EQU 005CH STROUT EQU 9 OPEN EQU 15 CLOSE EQU 16 DELETE EQU 19 READ EQU 20 WRITE EQU 21 MAKE EQU 22 RENAME EQU 23 SETDMA EQU 26 ;SET DMA ADDRESS RANREA EQU 33 ;READ RANDOM LF EQU 10 CR EQU 13 EOF EQU 01AH ;END OF FILE CHARACTER ; ; The following are bit flags for the dictionary ; ZFLAG EQU 1 YFLAG EQU 2 RFLAG EQU 4 GFLAG EQU 8 DFLAG EQU 16 MFLAG EQU 32 SFLAG EQU 64 HFLAG EQU 256 VFLAG EQU 512 JFLAG EQU 1024 XFLAG EQU 2048 TFLAG EQU 4096 NFLAG EQU 8192 PFLAG EQU 16384 START: LD SP,STACK ;CREATE A STACK LD A,EOF ;MARK EOF FOR BUFFERS LD (OBUFF+512),A LD (INBUF+512),A LD (INBUF+513),A LD A,(BOOT+06CH) ;GET DRIVE FOR OUTPUT FILES LD (OFCB),A  LD (DICFCB),A LD A,0C9H ;RETURN INSTRUCTION LD (100H),A ;THIS PROGRAM IS CAN NOT BE GOED LD DE,IFCB ;OPEN INPUT FILE LD C,OPEN CALL BDOS CP 0FFH ;FILE FOUND? JR NZ,START0 LD DE,NOTFOU ;ERROR LD C,STROUT CALL BDOS CALL BOOT NOTFOU: DB 'Input file not found',CR,LF,'$' START0: LD DE,DICFCB LD C,OPEN CALL BDOS CP 0FFH ;DOES "DICT.DIC" ALREADY EXIST? JR Z,START1 ;IF NOT, CONTINUE LD DE,EXDIC ;DELETE DICT.DIC MESSAGE LD C,STROUT CALL BDOS CALL YESNO ;ASK WHETHER DELETE IT CALL NZ,BOOT ;DON'T RUN IF DON'T DELETE LD DE,DICFCB LD C,DELETE CALL BDOS START1: LD DE,DICFCB LD C,MAKE CALL BDOS LD DE,OFCB LD C,OPEN ;DOES "SPELL0.MAC" EXIST? CALL BDOS CP 0FFH JR Z,START2 ;IF NOT, CONTINUE LD DE,EXSPEL ;DELETE SPELL0.MAC MESSAGE LD C,STROUT CALL BDOS CALL YESNO ;ASK WHETHER DELETE IT CALL NZ,BOOT ;DON'T RUN IF SAVE FILE LD DE,OFCB LD C,DELETE CALL BDOS START2: LD DE,OFCB LD C,MAKE CALL BDOS CALL CTRLZ ;PUT ^Z IN OBUFF LD A,01AH ;ALSO AT END OF IT LD (OBUFF+512),A CALL DICZER ;ZERO DICTIONARY BUFFER LD HL,TABTOP ;CHARACTER STRING FOR TABTOP:: LD B,8 ;OUTPUT 10 CHARACTERS LOOP: LD A,(HL) CALL PUTCHR INC HL DJNZ LOOP CALL GETWRD ;GET THE FIRST WORD LD A,0 ;END OF FILE? UNLIKELY! CP C CALL Z,BOOT LD B,C LD C,0 CALL MAKTBL ;PUT WORD IN RECORD TABLE JR MAINSK MAIN: CALL GETWRD ;GET A WORD LD A,0 ;IF 0 LENGTH WORD THEN END CP C JP Z,DONE LD B,C ;PUT NUMBER OF CHARACTERS READ IN B LD C,0 ;ZERO COUNTER MAINSK: LD HL,SRCWRD LD DE,OLDWRD MAIN0: LD A,(DE) ;COMPARE THIS WORD TO LAST (GET # OF ;SIMILAR CHARACTERS) CP (HL) JR NZ,MAIN1 INC DE INC HL INC C JR MAIN0 MAIN1: LD A,15 ;MAY COPY MAXIMUM OF 15 CHARACTERS CP C JP NC,MAIN2 ;JUMP IF LESS THAN 15 LD C,15 LD HL,SRCWRD+15 ;POINT TO 1ST CHARACTER TO USE MAIN2: PUSH BC ;COMPUTE NUMBER OF BITS IN WORD LD A,B ;COMPUTE NUMBER OF CHARACTERS IN WORD- ;NUMBER TO COPY SUB C LD B,A LD A,0 MAIN3: ADD A,5 ;5 BITS PER LETTER DJNZ MAIN3 POP BC ADD A,25 ;4 BITS FOR COPY AMOUNT ;14 BITS FOR STATUS FLAGS ;4 BITS FOR LENGTH OF STATUS FLAGS ;3 BITS FOR END OF WORD PUSH DE PUSH HL LD HL,(BITS) ;NUMBER OF BITS LEFT IN 256 BYTE RECORD LD E,A ;PUT BITS IN THIS WORD IN DE LD D,0 XOR A ;CLEAR CARRY SBC HL,DE ;NUMBER OF BITS LEFT AFTER THIS JP NC,MAIN4 ;JUMP IF WORD WILL FIT CALL DWRITE ;WRITE THIS RECORD CALL DICZER ;ZERO DICTIONARY BUFFER LD C,0 ;BEGINNING OF RECORD. COPY 0 CALL MAKTBL ;WRITE TO TABLE FILE LD HL,2048 ;NUMBER OF BITS IN 256 BYTES LD (BITS),HL ;RESET COUNTER POP HL POP DE LD HL,SRCWRD JP MAIN2 ;RECALCULATE LENGTH OF WORD MAIN4: LD (BITS),HL POP HL POP DE PUSH BC PUSH HL LD C,B ;NUMBER OF CHARACTERS IN SRCWRD TO BC LD B,0 LD HL,SRCWRD ;PUT SRCWRD IN OLDWRD LD DE,OLDWRD LDIR LD A,1 LD (DE),A ;MARK END OF WORD POP HL POP BC CALL BUFWRD ;BUFFER WORD IN RECORD JP MAIN ;GET NEXT WORD DONE: LD A,(IFCB) ;IF INPUT FILE ON DIFFERENT DISK THAN ;OUTPUT FILE, ASK IF MORE LD HL,OFCB CP (HL) JR Z,ENDIT LD DE,MOREST ;MORE QUESTION LD C,STROUT CALL BDOS LD HL,INBUF+512 ;GET PREPARED IN CASE MORE LD (INPTR),HL CALL YESNO JR NZ,NOMORE ;END IF NO MORE LD HL,IFCB+0CH ;CLEAR THE IFCB RECORD POINTERS LD B,23 LD A,0 DONF: LD (HL),A INC HL DJNZ DONF LD DE,IFCB ;OPEN THE FILE LD C,OPEN CALL BDOS CP 0FFH ;FOUND? JP NZ,MAIN ;GET NEXT WORD LD DE,NOTFOU ;NOT FOUND MESSAGE LD C,STROUT CALL BDOS NOMORE: LD A,(IFCB) ;IS IT ON DRIVE OTHER THAN A? CP 2 JP P,ENDIT ;JUST BOOT IF SO LD DE,TYPECR ;MESSAGE: INSERT BOOTABLE DISK... LD C,STROUT CALL BDOS LD DE,BOOT+80H ;WAIT FOR A LINE FROM TERMINAL LD C,0AH CALL BDOS ENDIT: LD HL,TBTSTR ;"TABBOT::" LD B,24 ;12 CHARACTERS WITH CR,LF DONE0: LD A,(HL) ;GET THE CHARACTER INC HL CALL PUTCHR DJNZ DONE0 LD HL,OBUFF ;WRITE OUT REMAINING DATA IN BUFFER LD DE,128 DONE1: LD A,EOF ;DONE? CP (HL) JR Z,DONE3 PUSH DE PUSH HL LD D,H ;SET UP DMA ADDRESS LD E,L LD C,SETDMA CALL BDOS LD DE,OFCB ;WRITE 128 BYTES TO FILE LD C,WRITE CALL BDOS POP HL POP DE CP 0 ;ERROR? JR NZ,DONE2 ;DISK FULL --> BRANCH ADD HL,DE ;POINT TO NEXT RECORD JR DONE1 DONE2: LD C,STROUT ;DISK FULL LD DE,DSKFUL ;DISK FULL MESSAGE (SEE PUTCHR) CALL BDOS CALL BOOT DONE3: LD DE,OFCB ;CLOSE OUTPUT FILE LD C,CLOSE CALL BDOS LD HL,(CURBYT) ;SEE IF DATA LEFT IN DICTIONARY REC LD BC,DICBUF-1 XOR A ;CLEAR CARRY SBC HL,BC JR Z,DONE4 ;DON'T WRITE IF NO DATA CALL DWRITE ;WRITE OUT CURRENT DICTIONARY RECORD DONE4: LD DE,DICFCB ;CLOSE FILE LD C,CLOSE CALL BDOS CALL BOOT BUFWRD: RRC C ;OUTPUT NUMBER OF BYTES TO COPY RRC C RRC C RRC C LD B,4 ;SEND 4 BITS BUFWR0: LD A,0 RLC C ;SET/CLEAR CARRY BASED ON BIT ADC A,0 CALL PUTBIT DJNZ BUFWR0 BUFWR1: LD A,(HL) ;GET CHARACTER TO OUTPUT INC HL CP '%' ;STOP IF END OF WORD JR Z,BUFWR3 CP 0 JR Z,BUFWR3 LD B,5 ;5 BITS CP 027H ;IS IT "'"? JR NZ,NORMAL LD A,'Z'+1 ;IF IT IS, ENCODE AS Z+1 NORMAL: SUB 'A'-1 ;MAKE IT BASED AT 1 LD C,A RLC C ;PUT BITS IN POSITION TO BE READ RLC C RLC C BUFWR2: LD A,0 RLC C ;SET/CLEAR CARRY BASED ON BIT ADC A,0 CALL PUTBIT DJNZ BUFWR2 JR BUFWR1 ;GET NEXT CHARACTER BUFWR3: LD A,1 ;MARK END OF WORD WITH 111B CALL PUTBIT CALL PUTBIT CALL PUTBIT DEC HL LD D,H ;PUT POINTER TO SRCWRD IN DE LD E,L BUFWR4: LD A,(DE) CP 0 ;ANY FLAGS? JP Z,BUFFLG ;WRITE NULL FLAGS IF NOT CP '%' ;ANOTHER FLAG? INC DE JR Z,BUFWR4 CP ' ' ;ILLEGAL IF SPACE JP Z,BUFWR4 LD HL,FLGDAT ;TABLE OF FLAGS LD BC,29 ;14 WORD FLAGS. IF REACHES END, ERROR. CPIR ;SEARCH THROUGH TABLE JP PO,BUFWR4 ;JUST SKIP IF ILLEGAL FLAG LD BC,FLGDA0-FLGDAT-1 ;OFFSET TO TABLE OF VALUES FOR FLAGS ADD HL,BC ;POINT TO WORD WITH FLAG LD C,(HL) ;PUT IT IN DE INC HL  LD B,(HL) LD HL,(CURFLG) ;GET CURRENT VALUE OF FLAG LD A,B ;MAKE SURE FLAG NOT REPEATED OR H LD H,A LD A,C OR L LD L,A LD (CURFLG),HL JP BUFWR4 ;GET ANOTHER FLAG BUFFLG: LD HL,(CURFLG) ;GET CURRENT FLAG CALL PUTFLG ;OUTPUT THE FLAG LD HL,0 LD (CURFLG),HL ;CLEAR CURRENT FLAG VALUE RET PUTFLG: LD A,14 ;FIND FIRST USED BIT PUSH HL PUTFL0: RRC H JP C,PUTFL2 ;IF BIT SET, EXIT DEC A CP 7 JR NZ,PUTFL0 ;LOOP THROUGH WHOLE BYTE PUTFL1: RRC L JP C,PUTFL2 DEC A CP 0 JR NZ,PUTFL1 PUTFL2: POP HL RRC A ;PUT A IN A POSITION FOR OUTPUT RRC A RRC A RRC A LD B,4 ;4 BITS LD C,A PUTFL4: LD A,0 RLC C ADC A,0 CALL PUTBIT DJNZ PUTFL4 LD A,C PUSH PSW PUSH HL LD HL,(BITS) ;UPDATE NUMBER OF BITS LEFT (GREATER?) LD C,A ;COMPUTE NUMBER OF BITS SAVED LD A,14 SUB C LD B,0 ;UPDATE BITS LD C,A ADD HL,BC LD (BITS),HL POP HL POP PSW CP 0 ;ANY BITS TO COPY? RET Z PUSH PSW CP 8 ;GREATER THAN 7? JP M,PUTFM4 LD A,7 ;DO ALL 7 BITS PUTFM4: LD B,A ;NUMBER OF BITS TO COPY RLC L ;GET TO FIRST BIT PUTFL5: LD A,0 RLC L ADC A,0 CALL PUTBIT DJNZ PUTFL5 POP PSW SUB 7 ;NUMBER OF BITS TO COPY IN BYTE 2 RET M ;RETURN IF NONE RET Z LD B,A RLC H ;GET TO FIRST BIT PUTFL6: LD A,0 RLC H ADC A,0 CALL PUTBIT DJNZ PUTFL6 RET PUTBIT: PUSH PSW PUSH DE PUSH HL LD HL,CURBIT ;GET BIT INDEX LD E,(HL) LD HL,(CURBYT) ;ADDRESS OF CURRENT BYTE FOR OUTPUT PUSH PSW LD A,0111B ;MASK 1ST THREE BITS AND E JR NZ,PUTBI0 ;IF .NE. 0 THEN NOT TIME TO INC CURBYT INC HL LD (CURBYT),HL PUTBI0: INC E POP PSW RLC (HL) ;MOVE BYTE SO NEXT BIT IN RIGHT PLACE OR (HL) ;PUT DESIRED BIT IN OUTPUT BUFFER LD (HL),A LD HL,CURBIT ;UPDATE MASK LD (HL),E POP HL POP DE POP PSW RET DWRITE: PUSH BC PUSH DE PUSH HL LD HL,(CURBYT) ;GET CURRENT BYTE LD A,(CURBIT) ;CURRENT BIT LD E,A DWRIT0: LD A,0111B ;ROTATE LAST BYTE INTO PLACE AND E JR Z,DWRIT1 RLC (HL) INC E JR DWRIT0 DWRIT1: LD HL,DICBUF-1 ;RESET POINTERS LD (CURBYT),HL LD A,0 LD (CURBIT),A LD DE,DICBUF ;SET DMA FOR FIRST 128 BYTES LD C,SETDMA CALL BDOS LD DE,DICFCB ;WRITE IT LD C,WRITE CALL BDOS CP 0FFH ;ERROR? JR Z,DWRERR LD DE,DICBUF+128 ;SET DMA FOR SECOND HALF LD C,SETDMA CALL BDOS LD DE,DICFCB ;AND WRITE IT LD C,WRITE CALL BDOS CP 0FFH ;ERROR? JR Z,DWRERR POP HL POP DE POP BC RET DWRERR: LD DE,DSKFUL ;DISK FULL ERROR LD C,STROUT CALL BDOS CALL BOOT DSKFUL: DB 'Disk full',CR,LF,'$' MAKTBL: PUSH HL PUSH BC LD HL,DBSTR ;STRING WITH CR,LF," DB '" LD B,7 ;7 CHARACTERS MAKTB0: LD A,(HL) INC HL CALL PUTCHR DJNZ MAKTB0 LD HL,SRCWRD ;WORD LD B,4 ;AT MOST 4 CHARACTERS GO IN TABLE LD C,0 ;COUNTER MAKTB1: LD A,(HL) INC HL CP 0 ;END OF WORD? JR Z,MAKTB2 CP '%' JR Z,MAKTB2 CALL PUTCHR INC C DJNZ MAKTB1 LD A,027H ;"'" CHARACTER CALL PUTCHR JR MAKRET MAKTB2: LD A,4 ;GET NUMBER OF CHARACTERS LEFT TO GO SUB C LD B,A LD A,027H ;"'" CHARACTER CALL PUTCHR MAKTB3: LD A,',' ;USE ,0 ISTEAD OF CHARACTER CALL PUTCHR LD A,'0' CALL PUTCHR DJNZ MAKTB3 MAKRET: POP BC POP HL RET GETWRD: LD A,0 ;ZERO LENGTH LD (LENGTH),A LD C,0 ;ZERO COUNTER GETWS0: CALL GETCHR ;GET A CHARACTER CP EOF ;END OF FILE? RET Z CALL LEGAL ;TEST IF LEGAL JR Z,GETWR0 ;EXIT LOOP IF LEGAL JR GETWS0 ;LOOP UNTIL LEGAL GETWR0: LD C,1 ;UPDATE CHARACTER COUNTER LD HL,SRCWRD+1 ;INITIALIZE POINTER TO SRCWRD: LD (SRCWRD),A ;STORE FIRST CHARACTER GETWR1: CALL GETCHR CALL LEGAL JR NZ,GETWR2 ;EXIT LOOP WHEN NOT WORD CHARACTER LD (HL),A INC HL INC C JR GETWR1 GETWR2: LD (HL),0 ;MARK END OF WORD LD A,(LENGTH) ;WAS % ALREADY REACHED? CP 0 RET Z ;RETURN VALUE IN C IF NOT LD C,A RET LEGAL: LD B,A AND 05FH ;KILL PARITY AND LOWER CASE CP 'A' ;MUST BE GREATER THAN "A" JP C,LEGAL0 CP 'Z'+1 ;GREATER THAN "Z" JP NC,LEGAL0 LD A,B CP A  ;SET ZERO FLAG RET LEGAL0: LD A,B AND 07FH ;KILL ONLY PARITY CP 27H ;"'" JR Z,LEGAL1 CP '%' ;"%" JR NZ,LEGAL1 PUSH PSW LD A,(LENGTH) ;END ALREADY MARKED? CP 0 JR NZ,LEGRET LD A,C LD (LENGTH),A ;MARK LENGTH OF WORD LEGRET: POP PSW LEGAL1: LD A,B RET GETCHR: PUSH BC PUSH DE PUSH HL LD HL,(INPTR) ;POINTER FOR INPUT LD DE,INBUF+512 ;END OF INPUT BUFFER XOR A ;CLEAR CARRY PUSH HL SBC HL,DE ;AT END OF BUFFER? POP HL JP Z,GETCH0 ;REFILL BUFFER LD A,(HL) ;GET THE CHARACTER AND 07FH ;KILL PARITY INC HL ;INCREMENTED POINTER LD (INPTR),HL GETRET: POP HL POP DE POP BC RET GETCH0: LD HL,INBUF LD DE,128 LD C,4 GETCH2: PUSH BC PUSH DE PUSH HL LD A,01AH ;MARK START OF RECORD WITH EOF IN CASE ;EOF LD (HL),A INC HL LD (HL),A DEC HL LD D,H ;SET UP DMA ADDRESS FOR READ LD E,L LD C,SETDMA CALL BDOS ;SET DMA ADDRESS LD DE,IFCB LD C,READ ;READ 128 BYTES OF INPUT FILE CALL BDOS POP HL POP DE POP BC CP 0 ;SUCCESS? JR NZ,GETCH3 ;JUMP IF EOF ADD HL,DE ;POINT TO NEXT RECORD ADDRESS DEC C JP NZ,GETCH2 ;LOOP FOR 4 RECORDS GETCH3: LD A,(INBUF) ;GET FIRST CHARACTER LD (HL),EOF ;PUT EOF AT BEGINNING OF FIRST UNUSED ;RECORD IN MEMORY LD HL,INBUF+1 LD (INPTR),HL ;SET UP POINTER TO RECORDS JP GETRET DICZER: PUSH PSW PUSH BC PUSH HL LD B,0 LD A,0 LD HL,DICBUF DICZE0: LD (HL),A INC HL DJNZ DICZE0 POP HL POP BC POP PSW RET PUTCHR: PUSH BC PUSH DE PUSH HL LD HL,(OPOSS) ;GET CURRENT POSITION IN OBUFF LD (HL),A ;PUT CHARACTER IN BUFFER INC HL LD (OPOSS),HL ;UPDATE POINTER LD DE,OBUFF+512 ;AT END OF BUFFER? XOR A ;CLEAR CARRY SBC HL,DE JR Z,PUTCH0 ;WRITE OUT DATA IF END OF BUFFER PUTRET: POP HL POP DE POP BC RET PUTCH0: LD C,4 ;LOOP COUNTER LD HL,OBUFF ;ADDRESS OF DATA LD DE,128 ;LENGTH OF EACH RECORD PUTCH1: PUSH BC PUSH DE PUSH HL LD D,H ;SET UP DMA ADDRESS LD E,L LD C,SETDMA CALL BDOS LD DE,OFCB ;WRITE RECORD TO OUTPUT FILE LD C,WRITE CALL BDOS CP 0 ;SUCCESS? JR NZ,PUTCH2 ;JUMP IF DISK FULL POP HL POP DE POP BC ADD HL,DE ;POINT TO NEXT RECORD DEC C JP NZ,PUTCH1 ;LOOP FOR 512 BYTE BUFFER LD HL,OBUFF ;RESET POINTER LD (OPOSS),HL CALL CTRLZ ;FILL BUFFER WITH EOF CHARACTER JP PUTRET ;RETURN PUTCH2: LD C,STROUT ;DISK FULL ERROR LD DE,DSKFUL CALL BDOS CALL BOOT ;GIVE UP CTRLZ: LD HL,OBUFF ;BUFFER ADDRESS LD B,2 ;LOOP 256 BYTES 2 TIMES LD C,0 CTRLZ0: LD (HL),EOF ;PUT EOF IN BUFFER INC HL DEC C ;FAST COUNTER JR NZ,CTRLZ0 DEC B ;SLOW COUNTER JR NZ,CTRLZ0 RET YESNO: LD C,6 ;DIRECT CONSOLE I/O LD E,0FFH ;INPUT CALL BDOS AND 05FH ;MAKE UPPER CASE CP 0 ;NO CHARACTER YET? JR Z,YESNO CP 'Y' ;YES? JR Z,YESNO1 CP 'N' JR Z,YESNO2 LD C,6 ;RING THE BELL (BAD INPUT) LD E,7 CALL BDOS JR YESNO ;TRY AGAIN YESNO1: LD DE,YESSTR ;ECHO Y LD A,0 ;INDICATE YES JR YESNO3 YESNO2: LD DE,NOSTR ;ECHO N LD A,1 ;INDICATE NO YESNO3: LD C,STROUT PUSH PSW CALL BDOS POP PSW AND A RET INPTR: DW INBUF+512 CURBYT: DW DICBUF-1 CURBIT: DB 0 CURFLG: DW 0 BITS: DW 256*8 LENGTH: DB 0 OPOSS: DW OBUFF EXDIC: DB 'Delete current version of DICT.DIC? $' EXSPEL: DB 'Delete current version of SPELL0.MAC? $' YESSTR: DB 'Y',CR,LF,'$' NOSTR: DB 'N',CR,LF,'$' TYPECR: DB 'Insert a bootable disk in drive A and type CR$' MOREST: DB 'More? (if Y, insert new disk in input drive) $' DICFCB: DB 0,'DICT DIC' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 OFCB: DB 0,'SPELL0 MAC' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 TABTOP: DB 'TABTOP::' TBTSTR: DB CR,LF,' DB ',027H,'[[[[',027H,CR,LF,'TABBOT::',CR,LF DBSTR: DB CR,LF,' DB ',027H FLGDAT: DB 'Z Y R G D M S H V J X T N P ' FLGDA0: DW ZFLAG DW YFLAG DW RFLAG DW GFLAG DW DFLAG DW MFLAG DW SFLAG DW HFLAG DW VFLAG DW JFLAG DW XFLAG DW TFLAG DW NFLAG DW PFLAG OLDWRD: DB 0FFH STACK EQU $+50 SRCWRD EQU STACK+80 INBUF EQU SRCWRD+80 OBUFF EQU INBUF+514 DICBUF EQU OBUFF+513 NEXT EQU DICBUF+256 END START CURBYT: DW DICBUF-1 CURBIT: DB 0 CURFLG: DW 0 BITS: DW 256*8 LENGTH: DB 0 OPOSS: DW OBUFF EXDIC: DB 'Delete current version of DICT.DIC? $' EXSPEL: DB 'Delete current version of SPELL0.MAC? $' YESSTR: DB 'Y',CR,LF,'$' NOSTR: DB 'N',CR,LF,'$' TYPECR: DB 'Insert a bootable disk in drive A and type CR$' MOREST: DB 'More? (if Y, insert new disk in input drive) $' DICFCB: DB 0,'DICT DIC' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 OFCB: DB 0,'SPELL0 MAC' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 TABTOP: DB 'TABTOP::' TBTSTR: DB CR,LF,' DB ',027H,'[[[[',027H,CR,LF,'TABBOT::',CR,LF DBSTR: DB CR,LF,' DB ',027H FLGDAT: DB 'Z Y R G D M S H V J X T N P ' FLGDA0: DW ZFLAG DW YFLAG DW RFLAG DW GFLAG DW DFLAG DW MFLAG DW SFLAG DW HFLAG DW VFLAG DW JFLAG DW XFLAG DW TFLAG DW NFLAG DW PFLAG OLDWRD: DB 0FFH STACK EQU $+50 SRCWG(C) 1982 Michael C. Adler. This program has been released into the public domain by the author. It may neither be sold for profit nor included in a sold software package without permission of the author.ABACABSCACCLACQUADDUADREAFFEAGILALBEALLOAMANAMPEANCIANNUANTIAPLOAPPRARCHARMPASCEASSIATHEAUDIAUTOAWOKBAGBANSBASSBEDABELABERRBIDDBIRDBLEABLUSBOOKBOUQBREABROABUFFBUSBCALICAPPCATACERTCHATCHOPCLAMCLOVCOLLCOMMCOMPCONCCONGCONSCONTCOORCOUNCRANCROWCURRDARIDECIDEFODENODESIDEVODIGRDISADISIDISSDOGMDRAPDURAEDIBELEVEMPHENGEENTRESCAEVIDEXECEXPLEXULFANCFENCFINGFLICFORAFORMFREEFUNNGAUGGINGOLFGRATGUERHANDHEHEREHITHHORRHURRILLEIMPEIMPRINCOINDIINEXINHEINSEINTAINTEINTRIRREJAZZJUTKNOWLAUGLEGILIGHLOBELUBRMAHOMANNMASTMEDIMETAMIGRMISCMODUMORTMULTNANONERVNOMINORMOARODIOOPPOOSCIOVERPAPARAPASSPEDEPERIPETTPILLPLAYPOLIPOSTPRECPRERPRISPROHPROSPUBLQUALRADIREABRECERECTREFEREIMREMIREPRRESORETIRHINROBURUPTSANDSCENSCUTSELFSERPSHEASHRISIMUSLABSMITSOLESPARSPLISTAGSTEASTORSTUDSUBPSUESUPESUSTSYNCTAPTELETESTTHINTILLTOPOTRANTREATRUCTWITUNATUNCOUNDIUNGRUNLOUNREUNSYUPSTVANTVERTVIRUWALNWEASWHITWITHWORTZEAL[[[[1>2Y =SPELL V2.0 -- December 22, 1982 (C) 1982 Michael C. Adler $:l>2 Can't find dictionary file DICT.DIC $!\ !\ !\ !\ >!\ (!:O >: ~_@22>\z bCan't find input file $« Directory full $ͭ >2>22:= 2>2= ] !\" *#" >(>)(; (*">22$\:  *<*[++R }o> o|gDM> *##^#V#!@w # y2*#" >*#" KK!" *":2= !" !" " #" :G:2x2' Total number of words in document: $ ʍ >(>)( ; (>= !~(= #:($[*}R*[R>2*"*"!\" !" !" " #" >2= *##"*+":G:2x2!>(%T]   >B2>A2>K2::\2l! \!eu>$2e2f2g2\\:(     *<  *<*>  >!N > No subtotals available due to size of document.$ Number of unique words: $ Unique "misspelled" words: $ͫ ͆ 2(= O.y = ͫ = 2 !2ͫ 9 o 2w# ͫ 2w# ͫ G x 2w# ͫ 2w# ͫ ͆ 2w# 6*+"G_Aږ [Җ xx'( : x.x*R ~#"!>wT]\  :6!" !@! '(_A8 w>)# 6! + q>((>'*w#"R(!T]  ^ !"ͭ Q    Disk full -- aborting $!6#  K[ T]DMbkB>, >, *##"RDMT]++*s#r!@:G~#S-R[R8>> }~o> o|g ^#V!@ ># !>w>>!>"i :2=  !" !" >(>)(*#"͹!@~(= #8 !" !"> = > = ͫ ( 8 ͫ (G!@ͫ w#>w͹ *#"!@~#(= > = > = !>(%T]>   ͂!:G> (#z>$(#p> #i #a>2bc> n>c> #~+: ~##@2b~ (.(  >(#> (#+͂Æ >!"i >2= ͭ !"*#"##"*+"!"ͭ > 2c *DM!>>U2G>D2H>C2Iq>D2G>$2H2I Words written to dictionary $!"!"b<2 :b 9>2bb!b" 8 >(>)(*#"͹* :b :2b:b@_:c!n>$2n - B $ not found$M *DM!bq Words read from dictionary $~ :w~@_:T] >$w  `i<: $*@w# w">!".>2>2!N!@ ~E;HtYʹGNDʆTRS>>S+>V+>I6E#6.++>E#6.>S+>T+>Y#6.>+>E+>I6Y#6.>S+>L6.>S+>N+>I6E#6.++>E#6.> S+>O>>E+>E>Y#6.>+>I6E#6.>++>T+>A+>C+>I6Y#6.>S+>E#6.++>E~Y+ ##6.#6.+>I+ #6Y#6.>S+>Sʓ>SÓ>@S+>Sʦ>'~Y + ##6.+~RʜNʈGʒ>E+o#6.>I+ #6Y#6.SSS >@S+>E+>N+~Y+ ##6.+>I+ #6Y#6. S6.>~!AEIOU~!*SXZH:n*@n BK !B(C*R";K*@ u RC"ͼ !B >>GyO*+B :*:(:*G>GC!C!!">2i!@i # ) *:( :( 'y>!iO >(ʘ@[ >'w#d>wG!>ox>GG>g>(G":*V r#"V<B(2r!>@ # >># @'ggdg gg Ri y0_ɗ  $$$ UDC ADD D$$SPELL DICDICT DIC D$$SPELL DICDICT DIC D$$SPELL DIC u RC"ͼ !B >>GyO*+B :*:(:*G>GC!C!!">2i!@i # ) *:( :( 'y>!iO >(ʘ@[ >'w#d>wG!>ox>GG>g>(G":*V r#"SPELLM20.DOC describes use of the command of WordStar to locate and correct flagged words. Those of us with WordStar version 2.xx do not have use of that particular command. Instead, edit the text file in Document mode and use the find-and- replace command . In response to FIND?, type . In response to REPLACE WITH?, type . In response to OPTIONS?, type N. WordStar will then move the cursor to the first flagged word and delete the flag. You may correct any mis-spelling, then press to repeat the procedure at the next flagged word. It should be noted that hyphenated words do not appear to be correctly treated.  SPELL V2.0 DOCUMENTATION Michael C. Adler December 22, 1982 (C) 1982 Michael C. Adler This program has been released into the public domain by the author. It may neither be sold for profit nor included in a sold software package without permission of the author. The first SPELL using this dictionary was probably written by Ralph Gorin at Stanford. It was transported to MIT by Wayne Mattson. Both the program at MIT and the dictionary were most recently revised by William Ackerman at MIT. Section 5 of this document was copied from portions of Mr. Ackerman's documentation. Thanks to all for the effort spent designing the dictionary! Spell is a program, written for Z80 processors running CP/M, designed to detect misspellings in a document. 1. USING SPELL The minimum configuration of SPELL requires the files SPELL.COM and DICT.DIC (the main dictionary). At the time of execution, DICT.DIC must be on either the default drive or drive A:. The name of the file to be corrected must be included on the command line that is used to invoke spell. If a drive name is specified as a second file name, output is directed to the speci- fied drive. Thus, SPELL useless.doc will check the file "useless.doc" and direct output to the default drive and SPELL b:useless.doc c: will check the file "b:useless.doc" and direct output to disk c. Spell will check the input file for errors by comparing each word in the file to the dictionary. If a word is not found, a null (ascii 0) is placed before the word. To change this marking character, see section 4, PATCHING SPELL. If a backup version (.BAK file type) of the input file exists, it will be deleted. The input file will be renamed to a backup file and the checked file will replace the input file. 2. USER DICTIONARIES A user dictionary is a list of correct words that can be 1 loaded by SPELL to augment the main dictionary. Words such as proper nouns can be placed in user dictionaries to inhibit error marking. User dictionary files may be formatted in any way that the user desires, as long as words are delimited by non-alphabe- tic characters. SPELL will automatically search for the user dictionary SPELL.DIC on the default drive and on drive A: if it is not on the default one. It's contents are then loaded and temporarily added to the dictionary. It must be loaded again to be included in subsequent executions of SPELL. SPELL will also automatically search for d:file.UDC, where file is the name of the file being corrected and d: is the drive on which file is found. If found, it is also loaded and tempo- rarily augments the dictionary. Thus, users may create separate dictionaries for each text file being corrected. After locating d:file.UDC, SPELL will search file d:file.ADD. This file is created by WordStar's ^QL command (see section 3) and is not an ASCII file. d:file.ADD contains commands generated by WordStar to include specific words in the user dictionary associated with d:file. SPELL will temporarily place all of the words in it in the dictionary and will also save the words by copying them into d:file.UDC. It is possible to load additional user dictionaries by specifying them on the SPELL command line. A list of user dic- tionaries must be preceded by a dollar sign. A dictionary is specified by a file name and an optional drive name. If no drive is specified, the default drive is searched and then drive A: is checked. Extensions are ignored and default to .DIC. Hence, the the command line: SPELL useless.doc b: $dict1 c:dict2 dict3.fun would correct useless.doc and direct output to drive B:. User dictionary DICT1.DIC would be loaded from the default drive or drive A:, dictionary DICT2.DIC would be loaded from drive C:, and DICT3.DIC would be loaded from the default drive or drive A:. Notice that the extension .fun was ignored. 3. WordStar's ^QL COMMAND Files checked by SPELL can be corrected using WordStar. In response to ^QL, the user is asked which portions of the file should be searched. WordStar will then position the cursor on the first marked word and print a menu offering F (Fix word), B (Bypass word), I (Ignore word), D (Add to dictionary), and S (Add to supplemental dictionary). The F option deletes the error marker and returns to the WordStar main menu, allowing the user to correct the word. B will leave the word marker and will search for the next misspelled word. In this implementation of SPELL, the I, D and S options all perform the same function (although I is easier to use because no question is asked by WordStar). If either of these options (I, D, S) are chosen, the 2 mark will be removed and the word will be added to file.ADD. Thus, choosing these options informs SPELL that the word is cor- rect and should not be marked again. The D and S options do not add the word to SPELL's main dictionary because the compression method used to store the dictionary is too complicated to allow such modification efficiently. After choosing all of the options except F, WordStar will automatically search for the next marked word. 4. PATCHING SPELL It is not necessary to recompile SPELL to change the charac- ter that marks misspelled words. The byte at 0800H contains the marking character. In the distribution version of SPELL, it is null, or 0. DDT or another debugger can be used to change 0800H to the ASCII value of the desired marker. 5. PROGRAM AND DICTIONARY CHARACTERISTICS 5.1 Word identification algorithm A word is any uninterrupted sequence of letters and apostrophes, which does not begin or end with an apostrophe. Any punctuation, digit, or control character separates words. Any word consisting of a single letter, or any word more than 40 letters long, is considered to be correctly spelled. 5.2 Dictionary policy It is the policy of this program to contain only one spelling of a word, even if ordinary dictionaries show two or more "acceptable" spellings. Hence, the dictionary contains LABELED and LABELING, but not LABELLED or LABELLING, even though all four are actually acceptable. The intention is to enforce uniformity within each document. The author apologizes for the restriction on creativity and diversity that this necessitates, but believes that it is the best policy for this program. The dictionary contains many technical and computer terms such as MICROPROGRAM and DEBUGGER, but does not contain extreme jargon words such as CONTROLIFY or VALRET. The dictionary contains no proper names other than names of countries and states of the United States. The reason is that it would be virtually impossible to contain all of the proper names that commonly arise in normal use. Users should keep proper names (and other correctly spelled words) that arise in their own work in private dictionaries to avoid having to repeat- edly tell SPELL to accept them. The dictionary is significantly smaller than that found in other spelling checkers, such as the DEC TOPS-20 program. The author believes that the larger dictionary would not reduce the number of false misspelling indications by very much. 3 [Note: I believe that this dictionary is actually MUCH larger than any dictionaries currently available for microcomputers. -Michael] 5.3 Dictionary flags Words in SPELL's main dictionary (but not the other dictio- naries) may have flags associated with them to indicate the legality of suffixes without the need to keep the full suffixed words in the dictionary. The flags have "names" consis- ting of single letters. Their meaning is as follows: Let # and @ be "variables" that can stand for any letter. Upper case letters are constants. "..." stands for any string of zero or more letters, but note that no word may exist in the dictionary which is not at least 2 letters long, so, for example, FLY may not be produced by placing the "Y" flag on "F". Also, no flag is effective unless the word that it creates is at least 4 letters long, so, for example, WED may not be produced by placing the "D" flag on "WE". "V" flag: ...E --> ...IVE as in CREATE --> CREATIVE if # .ne. E, ...# --> ...#IVE as in PREVENT --> PREVENTIVE "N" flag: ...E --> ...ION as in CREATE --> CREATION ...Y --> ...ICATION as in MULTIPLY --> MULTIPLICATION if # .ne. E or Y, ...# --> ...#EN as in FALL --> FALLEN "X" flag: ...E --> ...IONS as in CREATE --> CREATIONS ...Y --> ...ICATIONS as in MULTIPLY --> MULTIPLICATIONS if # .ne. E or Y, ...# --> ...#ENS as in WEAK --> WEAKENS "H" flag: ...Y --> ...IETH as in TWENTY --> TWENTIETH if # .ne. Y, ...# --> ...#TH as in HUNDRED --> HUNDREDTH "Y" FLAG: ... --> ...LY as in QUICK --> QUICKLY "G" FLAG: ...E --> ...ING as in FILE --> FILING if # .ne. E, ...# --> ...#ING as in CROSS --> CROSSING "J" FLAG" ...E --> ...INGS as in FILE --> FILINGS if # .ne. E, ...# --> ...#INGS as in CROSS --> CROSSINGS "D" FLAG: ...E --> ...ED as in CREATE --> CREATED if @ .ne. A, E, I, O, or U, ...@Y --> ...@IED as in IMPLY --> IMPLIED if # .ne. E or Y, or (# = Y and @ = A, E, I, O, or U) 4 ...@# --> ...@#ED as in CROSS --> CROSSED or CONVEY --> CONVEYED "T" FLAG: ...E --> ...EST as in LATE --> LATEST if @ .ne. A, E, I, O, or U, ...@Y --> ...@IEST as in DIRTY --> DIRTIEST if # .ne. E or Y, or (# = Y and @ = A, E, I, O, or U) ...@# --> ...@#EST as in SMALL --> SMALLEST or GRAY --> GRAYEST "R" FLAG: ...E --> ...ER as in SKATE --> SKATER if @ .ne. A, E, I, O, or U, ...@Y --> ...@IER as in MULTIPLY --> MULTIPLIER if # .ne. E or Y, or (# = Y and @ = A, E, I, O, or U) ...@# --> ...@#ER as in BUILD --> BUILDER or CONVEY --> CONVEYER "Z FLAG: ...E --> ...ERS as in SKATE --> SKATERS if @ .ne. A, E, I, O, or U,  ...@Y --> ...@IERS as in MULTIPLY --> MULTIPLIERS if # .ne. E or Y, or (# = Y and @ = A, E, I, O, or U) ...@# --> ...@#ERS as in BUILD --> BUILDERS or SLAY --> SLAYERS "S" FLAG: if @ .ne. A, E, I, O, or U, ...@Y --> ...@IES as in IMPLY --> IMPLIES if # .eq. S, X, Z, or H, ...# --> ...#ES as in FIX --> FIXES if # .ne. S, X, Z, H, or Y, or (# = Y and @ = A, E, I, O, or U) ...# --> ...#S as in BAT --> BATS or CONVEY --> CONVEYS "P" FLAG: if @ .ne. A, E, I, O, or U, ...@Y --> ...@INESS as in CLOUDY --> CLOUDINESS if # .ne. Y, or @ = A, E, I, O, or U, ...@# --> ...@#NESS as in LATE --> LATENESS or GRAY --> GRAYNESS "M" FLAG: ... --> ...'S as in DOG --> DOG'S Note: The existence of a flag on a root word in the directory is not by itself sufficient to cause SPELL to recognize the indicated word ending. If there is more than one root for which a flag will indicate a given word, only one of the roots is the correct one for which the flag is effective; generally it is the longest root. For example, the "D" rule implies that either PASS or PASSE, with a "D" flag, will yield PASSED. The flag must be on PASSE; it will be ineffective on PASS. This is because, when SPELL encounters the word PASSED and fails to 5 find it in its dictionary, it strips off the "D" and looks up PASSE. Upon finding PASSE, it then accepts PASSED if and only if PASSE has the "D" flag. Only if the word PASSE is not in the main dictionary at all does the program strip off the "E" and search for PASS. Furthermore, some combinations of flags are forbidden to allow for dense flag encoding to save space. For example, only one of the "P", "J", or "V" flags may be on in any one word. 6. SPELL INTERNALS SPELL uses a number of temporary files during execution. The file file.D$$ is the union of file.UDC and file.ADD. At the end of execution, file.UDC and file.ADD are deleted and file.D$$ is renamed to file.UDC. The file file.$$$ is the output file. At the end of execution, file.BAK is deleted, the input file is renamed to file.BAK, and file.$$$ is renamed to the input file name. Warning: if you do not have room on your disk for file.BAK, file.DOC and file.$$$ at the same time, either use two drives or delete file.BAK before you start. SPELL corrects files with two passes of the input file. On the first pass, the words in the file are sorted alphabetically and duplicate words are eliminated. An attempt is then made to search for the words in the dictionary. Words that are found are marked. On the second pass of the input file, SPELL determines  whether each word was found by locating them in memory. This method makes the operation of SPELL more efficient because common words must be looked up only once and because the dictionary can be searched sequentially, minimizing disk head travel. If all of the file does not fit in memory on the first pass, the input file is partitioned into sections small enough to fit into memory and is then corrected in a series of two pass operations until the entire file has been checked. It is unlikely that memory will be filled in large systems by even large text files as 3000 individ- ual words should fit easily. 7. DICTIONARY INTERNALS The dictionary has been compressed, significantly, in order to save space. Dictionary records are all 256 bytes long and each record contains as many words as will fit. Individual words are stored in the following code: 4 bits -- Number of characters to copy from the previous word. Because the dictionary is stored in alphabetical order, this saves a large number of characters. This field is 0 at the beginning of each record. x * 5 bits -- Characters are stored in 5 bit code. There may be any number of 5 bit characters. A character string is terminated by the following field. 3 bits -- Set to 111 binary to indicate the end of the word. 6 Since 11100 binary is greater than 26, all alphabetic characters can be stored without using this combination. 4 bits -- Number of bits of flag data following the word. The bit position of the flags has been ordered so that the flags most frequently used are earliest. Flags not stored are assumed to be off. x bits -- Flag data. x is determined by the previous field.  Each bit represents one of the 14 suffix flags. 8. MODIFYING THE MAIN DICTIONARY The source for the main dictionary can currently be found in the file "[MIT-XX]SRC:SPELL.DCT". In order to make it com- patible with SPELL, all of the "/" characters that delimit flags must be converted to "%" characters so that flags will be consid- ered earlier in the alphabet than hyphens (DOG%S should be before DOG'S). The file must then be sorted alphabetically. No utili- ties are provided with SPELL to accomplish either of these tasks. Without high capacity disk drives, you may find it necessary to perform the above steps on a larger computer. Once a copy of the main dictionary has been placed on the microcomputer, use the program DICCRE to create a dictionary. Include the name of the source file on the DICCRE command line. DICCRE will create the files DICT.DIC (compressed dictionary) and SPELL0.MAC (pointer file to dictionary) ON THE DEFAULT DISK DRIVE. When it has finished converting the input file to the dictionary file, it will execute a warm boot if the output file is on the same drive as the input file. However, if the output file is not on the same disk, it will ask whether another input file exists. This feature allows the user to put the source file on two disks in case it does not fit on one. DICCRE will combine them into one dictionary file. If no more files exist, answer N to the question. If another file does exist, put the disk with the new file in the input drive and type Y. After the dictionary file has been created, it is necessary to recompile SPELL with the new pointer file, SPELL0.MAC. If your assembler does not support the INCLUDE statement, you will have to replace the line INCLUDE SPELL0.MAC in the file SPELL.MAC with the contents of SPELL0.MAC. After SPELL is recompiled, be sure to use the correct copy of DICT.DIC with it or you will obtain unpredictable results. For more information about dictionaries, see the file: [MIT-XX]SS:DICT.LETTER Good luck and happy hacking! Michael Adler (MADLER@MIT-ML) 3 Sunny Knoll Terrace Lexington, MA 02173 7  DICCRE will combine them into one dictionary file. If no more files exist, answer N to the question. If another file does exist, put the disk with the new file in the input drive and type Y. After the dictionary file has been created, it is necessary to recompile SPELL with the new pointer file, SPELL0.MAC. If your assembler does not support the INCLUDE statement, you will have to replace the line INCLUDE SPELL0.MAC in the file SPELL.MAC with the contents of SPELL0.MAC. After SPELL is recompiled, be sure to use the correct copy of DICT.DIC with it or you will obtain unpredictable resu; ; SPELL version 2.0 December 22, 1982 ; ; written by Michael C. Adler ; ; History of SPELL's dictionary: ; The first SPELL using this dictionary was probably written ; by Ralph Gorin at Stanford. It was transported to MIT by ; Wayne Mattson. Both the program at MIT and the dictionary ; were most recently revised by William Ackerman. ; ; Thanks to all for the effort spent desigining ; the dictionary! ; -Michael ; ; .Z80 TITLE SPELL ; JMP START <--Include this line if assembled under anything ; but M80. DB '(C) 1982 Michael C. Adler. ' DB 'This program has been released into the public domain ' DB 'by the author. It may neither be sold for profit nor ' DB 'included in a sold software package without permission ' DB 'of the author.' ; ; SPELL will compare words in a document to a dictionary ; and mark any words that are not found. It is intended for use ; with WordStar's ^QL (Find Misspelling) command. Words are marked ; with a preceding null if they are not found. ; ; WARNING: SPELL requires a Z80 processor. ; ; MODIFICATION HISTORY: ; ; 2.0 - December 22, 1982 Michael Adler ; -Bug fix: modified to rename files correctly at end of ; run if output drive is different from input drive. ; -Bug fix: modified to recognize hyphenated words in indented ; text. ; -Enhanced to allow user specified dictionaries on command ; line. ; 1.3 - October 10, 1982 Michael Adler ; -Bug fix: crashed if number of unique words was multiple ; of 256. ; 1.2 - October 8, 1982 Michael Adler ; -Modified MEMWRD routine to store words backwards in ; memory starting just below the BDOS. Thus, maximum ; memory is utilized since pointers to words and the words ; grow toward each other. ; -FILE.UDC is now deleted if it is empty. ; -Added messages displaying number of words, number of ; unique words, and number of words not found. ; 1.1 - August 28, 1982 Michael Adler ; -Crashed if file.ADD had a zero length word. Fixed. ; -Fixed bug in AUXDIC: stopped reading from .ADD file ; when 0 encountered. ; -Fixed compatability with WS R(un program). WS initializes ; byte at 6CH with default drive instead of 0. Caused output ; to be directed to wrong drive sometimes. ; -Set SPELL to ignore lines starting with . because WS can't ; handle misspelling markers in dot command lines. ; -SPELL did not find hyphenated words if LF had parity bit ; set. Fixed. ; ; 1.0 - August 16, 1982 Michael Adler ; -Released ; INCLUDE SPELL0.MAC ERRCHR EQU 0 ;THIS IS THE CHARACTER THAT MARKS ;UNFOUND WORDS. FOR WordStar, IT ;MUST BE 0. IF YOU ARE USING ANOTHER ;EDITOR, SET ERRCHR TO THE ASCII ;EQUIVALENT OF THE CHARACTER THAT ;SHOULD MARK MISSPELLED WORDS. BOOT EQU 0000H BDOS EQU BOOT+0005H IFCB EQU BOOT+005CH TPA EQU BOOT+0100H CONOUT EQU 2 STROUT EQU 9 OPEN EQU 15 CLOSE EQU 16 DELETE EQU 19 READ EQU 20 WRITE EQU 21 MAKE EQU 22 RENAME EQU 23 CURDSK EQU 25 SETDMA EQU 26 ;SET DMA ADDRESS RANREA EQU 33 ;READ RANDOM LF EQU 10 CR EQU 13 EOF EQU 01AH ;END OF FILE CHARACTER ; ; The following are bit flags for the dictionary ; ZFLAG EQU 1 YFLAG EQU 2 RFLAG EQU 4 GFLAG EQU 8 DFLAG EQU 16 MFLAG EQU 32 SFLAG EQU 64 HFLAG EQU 256 VFLAG EQU 512 JFLAG EQU 1024 XFLAG EQU 2048 TFLAG EQU 4096 NFLAG EQU 8192 PFLAG EQU 16384 ; ; Macros ; JISIT MACRO CHAR, JMPLOC ;JUMP TO JMPLOC IF (HL) .EQ. CHAR LD A,CHAR CP (HL) JP Z,JMPLOC ENDM QISIT MACRO CHAR, JMPLOC ;QUICK FORM OF JISIT. CHARACTER ;TO COMPARE IS ALREADY IN A CP CHAR JP Z,JMPLOC ENDM ISIT MACRO CHAR ;JUMP TO WRDNOT IF (HL) .NE. CHAR LD A,CHAR CP (HL) JP NZ,WRDNOT ENDM START: LD SP,STACK ;CREATE A STACK LD A,0C9H ;RETURN INSTRUCTION LD (TPA),A ;DON'T ALLOW SECOND RUN OF PROGRAM LD DE,WELCOM ;PRINT SIGNON MESSAGE LD C,STROUT CALL BDOS JR START0 WELCOM: DB 'SPELL V2.0 -- December 22, 1982',CR,LF DB '(C) 1982 Michael C. Adler',CR,LF,'$' START0: LD A,(BOOT+6CH) ;GET OUTPUT DRIVE PUSH PSW ;SAVE IT LD C,OPEN ;OPEN THE DICTIONARY FILE LD DE,DICFCB CALL BDOS CP 0FFH ;FOUND? JP NZ,SETUP ;JUMP IF FOUND LD A,1 ;TRY DRIVE A IF NOT ON DEFAULT DRIVE LD (DICFCB),A LD C,OPEN LD DE,DICFCB CALL BDOS CP 0FFH ;FOUND? JP NZ,SETUP LD C,STROUT ;OUTPUT: "CAN'T FIND DICTIONARY..." LD DE,DICERR CALL BDOS CALL 0 DICERR: DB 'Can''t find dictionary file DICT.DIC',CR,LF,'$' SETUP: LD DE,OFCB ;COPY INPUT FILENAME TO OUTPUT LD HL,IFCB LD BC,9 LDIR LD DE,P2FCB ;COPY INPUT FILENAME TO PASS 2 FCB LD HL,IFCB ; (THIS FCB IS FOR INPUT ON PASS 2) LD BC,12 LDIR LD DE,FILDIC ;COPY INPUT FILENAME TO FILE.DIC FCB LD HL,IFCB LD BC,9 LDIR LD DE,FILADD ;COPY INPUT FILENAME TO FILE.ADD FCB LD HL,IFCB LD BC,9 LDIR LD DE,FILD$$ ;COPY INPUT FILENAME TO FILE.D$$ FCB LD HL,IFCB LD BC,9 LDIR POP PSW ;GET OUTPUT DRIVE, IF SPECIFIED (FROM ;6CH) CP 0 JR Z,NODRV LD HL,BOOT+81H ;SEARCH FOR DRIVE SPECIFICATION IN ;COMMAND LINE LD A,(BOOT+80H) ;NUMBER OF CHARACTERS IN COMMAND LINE LD B,0 LD C,A ;COUNTER ADD HL,BC ;HL POINTS TO LAST CHARACTER DEC BC ;DON'T TEST FIRST BYTE OF INPUT LD A,':' ;SEARCHING FOR ":" CPDR JR NZ,NODRV ;IF NO DRIVE SPECIFIED, THEN USE ;DEFAULT LD A,(HL) ;GET DRIVE NAME AND 5FH ;MAKE UPPER CASE SUB 40H ;MAKE A=1 LD (OFCB),A ;MAKE OUTPUT FILES ON OUTPUT DRIVE LD (FILD$$),A NODRV: LD C,OPEN ;OPEN THE INPUT FILE LD DE,IFCB CALL BDOS LD C,OPEN ;WITH PASS 1 AND PASS 2 FCB'S LD DE,P2FCB CALL BDOS CP 0FFH JP NZ,SETUP0 ;JUMP IF FOUND LD C,STROUT ;OUTPUT: "CAN'T FIND INPUT FILE" LD DE,NOINPT CALL BDOS CALL BOOT NOINPT: DB 'Can''t find input file',CR,LF,'$' SETUP0: LD C,DELETE ;DELETE OUTPUT FILE (FILE.$$$) LD DE,OFCB CALL BDOS LD C,MAKE ;MAKE A NEW OUTPUT FILE LD DE,OFCB CALL BDOS CP 0FFH JP NZ,SETUP1 ;JUMP IF SUCCESSFUL LD C,STROUT ;OUTPUT: "DIRECTORY FULL" LD DE,NODIR CALL BDOS CALL BOOT NODIR: DB 'Directory full',CR,LF,'$' SETUP1: CALL CTRLZ ;FILL OUTPUT BUFFER WITH EOF LD A,EOF ;MARK END OF OUTPUT BUFFER LD (OBUFF+512),A LD A,0 ;MARK END OF DICTIONARY BUFFER LD (DICBUF+256),A LD (DICBUF+257),A LD A,(PUTCHR) ;GET THE NORMAL FIRST INSTRUCTION ;AT PUTCHR LD (SAVPUT),A ;STORE IT FOR LATER LD A,0C9H ;RETURN INSTRUCTION LD (PUTCHR),A ;DISABLE OUTPUT CALL AUXDIC ;LOAD "SPELL.DIC", FILE.DIC AND ; FILE.ADD LD HL,IFCB ;INPUT FILE FCB LD (FILPAT),HL ;PATCH GETWRD: TO READ FROM IT ; ; SRTFIL -- Sort the file in memory, alphabetically. Duplicate words are ; discarded. This will save a lot of time during dictionary lookups ; since the words will have to be located only once. SRTFIL: CALL GETWRD ;GET A WORD INTO SRCWRD: JP Z,CHECK ;IF EOF THEN START CHECKING LD HL,(TOTWRD) ;KEEP TRACK OF TOTAL # OF WORDS INC HL LD (TOTWRD),HL CALL CREWRD ;CREATE WORD FROM SRCWRD: LD A,0 ;IS IT AT LEAST 2 CHARS LONG? CP C JR Z,SRTFIL ;FORGET IT IF NOT LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT CP C JR Z,SRTFIL CALL MEMWRD ;PUT WORD IN MEMORY AND A ;FULL? JR Z,SRTFIL ;GET ANOTHER WORD IF NOT LD HL,(INPTR) ;GET OLD INPUT BUFFER POINTER LD (OLDPTR),HL ;SAVE IT LD A,1 ;MARK PASS 1 INCOMPLETE LD (STOPED),A LD (MLTPAS),A ;RECORD PERMANENTLY THAT >1 PASS LD C,36 ;GET RECORD NUMBER OF INPUT FILE LD DE,IFCB CALL BDOS ; ; CHECK -- Compare the entries in the sorted table to the dictionary. ; Words that are found are marked. ; CHECK: LD A,(STOPED) ;IS READ OF SOURCE COMPLETE? CP 0 JR NZ,CHEC01 ;IF NOT, NO MESSAGE LD DE,TOTMSG ;OUTPUT: "TOTAL # OF WORDS" LD C,STROUT CALL BDOS LD HL,(TOTWRD) ;PRINT NUMBER CALL DECOUT CHEC01: LD HL,(SRTTOP) ;TOP ENTRY IN POINTER TABLE LD DE,(SRTBOT) ;BOTTOM XOR A ;CLEAR CARRY DEC HL ;POINT TO TRUE END, NOT DUMMY RECORD DEC HL SBC HL,DE ;HL=NUMBER OF WORDS TIMES 2 JP Z,MAIN ;IF EMPTY, WRITE OUT FILE RRC L ;DIVIDE HL BY 2 LD A,L AND 07FH LD L,A LD A,0 RRC H ADC A,0 RRC A OR L LD L,A LD A,H AND 07FH LD H,A LD B,H ;PUT COUNTER IN BC LD C,L INC B ;MAKE B WORD WITH DEC B AND JNZ ;COMBINATION LD A,0 CP C ;IF C 0? JR NZ,CHEC02 DEC B ;IF C=0 THEN READJUST B BECAUSE ;IT WOULD LOOP 256 TOO MANY TIMES CHEC02: LD HL,(SRTBOT) ;GET ADDRESS OF FIRST ENTRY INC HL ;BYPASS DUMMY ENTRY AT BEGINNING INC HL CHECK0: LD E,(HL) ;GET ADDRESS OF WORD IN MEMORY INC HL LD D,(HL) INC HL PUSH BC PUSH HL LD HL,WORD LD C,0FEH ;CHARACTER COUNTER ;AT THE END, C=CHARS-1. START OF WITH ;-2 SO THAT 0 AND 1 CHARACTER NOT ;COUNTED CHECK1: LD A,(DE) ;GET A CHARACTER LD (HL),A ;PUT IT IN WORD: INC C DEC DE INC HL CP 0 ;END OF WORD? JR NZ,CHECK1 ;LOOP UNTIL END LD A,C LD (LASTC),A ;NUMBER OF CHARACTERS LD HL,(UNQWRD) ;RECORD # OF UNIQUE WORDS INC HL LD (UNQWRD),HL CALL WRDTST ;SEARCH DICTIONARY FOR WORD CP 1 ;FOUND? JR NZ,CHECK2 ;LEAVE UNMARKED IF NOT FOUND INC DE ;POINT TO END MARKER OF WORD LD A,080H LD (DE),A ;MARK WORD AS FOUND JR CHECK3 CHECK2: LD HL,(MISWRD) ;INCREMENT MISSED WORDS COUNTER INC HL LD (MISWRD),HL CHECK3: POP HL POP BC DEC C JP NZ,CHECK0 ;LOOP DEC B JP NZ,CHECK0 ;16 BIT LOOP INDEX LD HL,P2FCB ;SET TO INPUT FROM PASS 2 FCB LD (FILPAT),HL LD HL,(P2OPTR) ;SET PASS 2 INPUT POINTER LD (INPTR),HL LD A,(SAVPUT) ;GET THE NORMAL FIRST INSTRUCTION ;AT PUTCHR LD (PUTCHR),A ;PATCH IT INTO THE WRITE ROUTINE LD HL,P2BUF+512 ;PATCH NEW BUFFER INTO GETCHR LD (BUFPAT),HL LD HL,P2BUF LD (BUFPA0),HL LD (BUFPA1),HL INC HL LD (BUFPA2),HL LD A,(LASTCH) ;SAVE LAST CHARACTER READ LD B,A LD A,(OLSTCH) ;PUT OLD LASTCH BACK LD (LASTCH),A LD A,B LD (OLSTCH),A ;SAVE CURRENT LASTCH JR MAIN TOTMSG: DB CR,LF,LF,'Total number of words in document: $' ; ; MAIN -- Do a second pass through the input file. Compare each word to ; the memory buffer to determine whether the words were found. ; If a word was not found, mark it with the ERRCHR. MAIN: CALL GETWRD ;GET A WORD INTO SRCWRD: JP Z,DONE ;IF EOF THEN STOP CALL CREWRD ;CREATE WORD FROM SRCWRD: LD A,0 ;IS WORD AT LEAST 2 CHARS LONG? CP C JR Z,MAIN0 ;ACCEPT IF NOT LD A,41 ;IF LONGER THAN 40, ALSO ACCEPT CP C JR Z,MAIN0 CALL MEMWRD ;SEARCH FOR WORD IN MEMORY LD A,(DE) ;GET MARKER FOR WORD CP 080H ;WORD SPELLED CORRECTLY? JR Z,MAIN0 LD A,ERRCHR ;IF NOT, MARK WORD CALL PUTCHR MAIN0: LD HL,SRCWRD ;OUTPUT THE ORIGINAL WORD MAIN1: LD A,(HL) CP 0 ;END OF WORD? JR Z,MAIN2 CALL PUTCHR ;OUTPUT CHARACTER INC HL ;POINT TO NEXT CHARACTER JR MAIN1 ;LOOP MAIN2: LD A,(STOPED) ;WAS PASS 1 INCOMPLETE AND A JR Z,MAIN ;GET NEXT WORD IF NOT LD DE,P2FCB ;COMPUTE CURRENT RECORD NUMBER LD C,36 CALL BDOS LD DE,(P2FCB+21H) LD HL,(IFCB+21H) ;COMPARE STOPPED RECORD NUMBER TO ;CURRENT XOR A ;CLEAR CARRY SBC HL,DE JP NZ,MAIN ;GET NEXT WORD IF NOT THE SAME LD HL,(OLDPTR) ;GET POSITION IN RECORD LD DE,P2BUF-INBUF ;OFFSET IT TO COMPARE WITH PASS 2 BUFF ADD HL,DE LD DE,(INPTR) XOR A SBC HL,DE ;COMPARE STOPPED POS TO CURRENT JP NZ,MAIN ;GET NEXT WORD IF NOT THE SAME LD A,0 ;UNMARK PASS 1 INCOMPLETE LD (STOPED),A LD HL,(INPTR) ;SAVE PASS 2 INPUT POINTER LD (P2OPTR),HL LD HL,(OLDPTR) ;GET OLD POINTER TO BUFFER LD (INPTR),HL ;RESET BUFFER POINTER LD HL,IFCB ;PATCH GETCHR: ROUTINE TO READ FROM ;PASS 1 FCB LD (FILPAT),HL LD HL,INBUF+512 ;PATCH THE CORRECT BUFFER INTO GETCHR LD (BUFPAT),HL LD HL,INBUF LD (BUFPA0),HL LD (BUFPA1),HL INC HL LD (BUFPA2),HL LD A,0C9H ;MAKE PUTCHR NOT WORK LD (PUTCHR),A LD HL,(SRTBOT) ;RESET MEMORY BUFFER POINTERS INC HL INC HL LD (SRTTOP),HL LD HL,(BDOS+1) ;GET BDOS VECTOR DEC HL ;POINT TO FREE MEMORY LD (FREE),HL ;POINTER TO FREE MEMORY LD A,(LASTCH) ;SAVE LAST CHARACTER READ LD B,A LD A,(OLSTCH) ;PUT OLD LASTCH BACK LD (LASTCH),A LD A,B LD (OLSTCH),A ;SAVE CURRENT LASTCH JP SRTFIL ;FILL BUFFER AGAIN ; ; DONE -- Write out the rest of the output buffer and then close and rename ; output files. DONE: LD HL,OBUFF ;WRITE OUT REMAINING DATA IN BUFFER LD DE,128 DONE1: LD A,EOF ;DONE? CP (HL) JR Z,DONE3 PUSH DE PUSH HL LD D,H ;SET UP DMA ADDRESS LD E,L LD C,SETDMA CALL BDOS LD DE,OFCB ;WRITE 128 BYTES TO FILE LD C,WRITE CALL BDOS POP HL POP DE CP 0 ;ERROR? JR NZ,DONE2 ;DISK FULL --> BRANCH ADD HL,DE ;POINT TO NEXT RECORD JR DONE1 DONE2: LD C,STROUT ;DISK FULL LD DE,DSKFUL ;DISK FULL MESSAGE (SEE PUTCHR) CALL BDOS CALL BOOT DONE3: LD DE,OFCB ;CLOSE OUTPUT FILE LD C,CLOSE CALL BDOS LD A,'B' LD (OFCB+9),A LD A,'A' LD (OFCB+10),A LD A,'K' LD (OFCB+11),A LD A,(OFCB) ;REMEMBER OUTPUT DRIVE NAME PUSH PSW LD A,(IFCB) ;GET THE DRIVE ON WHICH INPUT WAS DONE LD (OFCB),A ;TRY TO DELETE .BAK ON IT ONLY LD DE,OFCB LD C,DELETE CALL BDOS LD DE,IFCB+16 ;RENAME SOURCE TO SOURCE.BAK LD HL,OFCB LD BC,12 ;COPY FILENAME FOR RENAME LDIR LD DE,IFCB LD C,RENAME CALL BDOS LD HL,IFCB+9 ;RENAME .$$$ TO SOURCE LD DE,IFCB+25 LD BC,3 LDIR LD A,'$' LD (IFCB+9),A LD (IFCB+10),A LD (IFCB+11),A POP PSW ;RESTORE OUTPUT DRIVE NAME LD (IFCB),A ;AND PUT IT IN FCB LD DE,IFCB LD C,RENAME CALL BDOS ;NOW TAKE CARE OF USER DICTIONARIES LD DE,FILADD ;DELETE FILE.ADD LD C,DELETE CALL BDOS LD DE,FILDIC ;DELETE FILE.UDC LD C,DELETE CALL BDOS LD A,(MLTPAS) ;WAS MORE THAN 1 PASS REQUIRED? CP 0 JR Z,DONE30 ;NO MESSAGE IF NOT LD DE,NOTOUT ;OUTPUT: NO TOTALS PRINTED LD C,STROUT CALL BDOS JR DONE31 DONE30: LD DE,UNQMSG ;OUTPUT: # OF UNIQUE WORDS LD C,STROUT CALL BDOS LD HL,(UNQWRD) ;PRINT # OF WORDS CALL DECOUT LD DE,MISMSG ;OUTPUT: # OF "MISSPELLED" WORDS LD C,STROUT CALL BDOS LD HL,(MISWRD) ;PRINT # CALL DECOUT DONE31: LD HL,(FILUDC) ;DELETE FILE.D$$ IF IT HAS NO WORDS LD A,0 CP H JR NZ,DONE4 ;RENAME IT TO FILE.UDC IF >0 WORDS CP L JR NZ,DONE4 LD DE,FILD$$ ;DELETE IT LD C,DELETE CALL BDOS CALL BOOT DONE4: LD HL,FILDIC ;RENAME FILE.D$$ TO FILE.UDC LD DE,FILD$$+16 LD BC,12 LDIR LD DE,FILD$$ LD C,RENAME CALL BDOS CALL BOOT NOTOUT: DB CR,LF,'No subtotals available due to size of document.$' UNQMSG: DB CR,LF,' Number of unique words: $' MISMSG: DB CR,LF,' Unique "misspelled" words: $' ; ; GETWRD -- Read a word from an input file. Words are any series of ; alphabetic characters (with or without parity set) and ; apostrophes. ; ; Returns: C <-- number of characters in word ; Z flag set if EOF GETWRD: CALL GETCHR ;GET A CHARACTER CP EOF ;END OF FILE? RET Z CALL LEGAL ;TEST IF LEGAL LD (LASTCH),A ;SAVE LAST CHARACTER JR Z,KILPER ;EXIT LOOP IF LEGAL CALL PUTCHR ;SEND CHARACTER DIRECTLY OUT JR GETWRD ;LOOP UNTIL LEGAL KILPER: LD C,A AND 07FH ;MASK PARITY CP '.' ;IS IT A DOT COMMAND? LD A,C JR NZ,GETWR0 ;GET WORD IF NOT CALL PUTCHR KILPE0: CALL GETCHR ;ERADICATE THIS LINE CP EOF ;END OF FILE? RET Z CALL PUTCHR ;OUTPUT IT LD (LASTCH),A AND 07FH ;MASK PARITY CP LF ;IS IT A LINE TERMINATOR? JR NZ,KILPE0 ;LOOP THROUGH WHOLE LINE JR GETWRD ;GET A TEXT WORD NOW GETWR0: LD C,1 ;ZERO CHARACTER COUNTER LD HL,SRCWRD+1 ;INITIALIZE POINTER TO SRCWRD: LD (SRCWRD),A ;STORE FIRST CHARACTER GETWR1: CALL GETCHR HYPTST: CP 1FH ;SOFT HYPHEN? JP Z,ISHYP CP 1EH ;UNUSED SOFT HYPHEN? JP NZ,NOTHYP ISHYP: LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER LD (HL),A ;STORE HYPHEN INC HL INC C CALL GETCHR ISHYP0: CP CR OR 80H ;SOFT RETURN AFTER HYPHEN? JR NZ,HYPTST ;TEST FOR ANOTHER HYPHEN LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER LD (HL),A ;STORE RETURN INC HL INC C CALL GETCHR LD B,A ;SAVE THE CHAR AND 07FH ;KILL PARITY BIT CP LF ;SOFT LINE FEED? LD A,B ;RESTORE CHAR JR NZ,HYPTST ;TEST FOR ANOTHER HYPHEN LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER LD (HL),A ;STORE LF INC HL INC C CALL GETCHR ISHYP1: CP ' ' OR 80H ;ELIMINATE SOFT SPACES AT START OF LINE JR NZ,ISHYP0 ;TEST FOR REST OF WORD LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER LD (HL),A ;STORE SPACE INC HL INC C CALL GETCHR JR ISHYP1 ;TEST FOR MORE SPACES NOTHYP: CALL LEGAL JR NZ,GETWR2 ;EXIT LOOP WHEN NOT WORD CHARACTER LD (LASTCH),A LD (HL),A INC HL INC C JR GETWR1 GETWR2: LD (HL),0 ;MARK END OF WORD LD HL,(INPTR) ;DECREMENT POINT FOR INPUT BUFFER ;SO FIRST CHAR AFTER WORD IS KEPT DEC HL LD (INPTR),HL RET ; ; LEGAL -- Determines whether character is alphabetic, an apostrophe, ; or period for a dot command. ; ; Returns: Z flag set <-- characters was one of the above ; Z flag clear <-- was not one of the above LEGAL: LD B,A AND 05FH ;KILL PARITY AND LOWER CASE CP 'A' ;MUST BE GREATER THAN "A" JP C,LEGAL0 CP 'Z'+1 ;GREATER THAN "Z" JP NC,LEGAL0 LD A,B CP A ;SET ZERO FLAG RET LEGAL0: LD A,B AND 07FH ;KILL ONLY PARITY CP 27H ;"'" JR Z,LEGAL1 LD A,(LASTCH) ;WAS LAST CHARACTER A LF? AND 7FH ;MASK PARITY CP LF JR NZ,LEGAL1 LD A,B CP '.' ;ACCEPT PERIODS SO DOT COMMAND ACCEPTED LEGAL1: LD A,B RET ; ; GETCHR -- Read a character from an input file into a buffer. This routine ; is used for a number of different purposes. The file from which ; to read is patched into FILPAT. The buffer to which writes should ; occur is patched into BUFPAT-BUFPA2. ; ; Returns: A <-- next character from input file GETCHR: PUSH BC PUSH DE PUSH HL LD HL,(INPTR) ;POINTER FOR INPUT LD DE,INBUF+512 ;END OF INPUT BUFFER BUFPAT EQU $-2 ;PATCH FOR INPUT BUFFER XOR A ;CLEAR CARRY PUSH HL SBC HL,DE ;AT END OF BUFFER? POP HL JP Z,GETCH0 ;REFILL BUFFER LD A,(HL) ;GET THE CHARACTER INC HL ;INCREMENTED POINTER LD (INPTR),HL GETRET: POP HL POP DE POP BC RET GETCH0: LD HL,INBUF BUFPA0 EQU $-2 ;PATCH FOR INPUT BUFFER LD DE,128 LD C,4 GETCH2: PUSH BC PUSH DE PUSH HL LD A,EOF ;MARK WITH EOF IN CASE NO RECORD LD (HL),A LD D,H ;SET UP DMA ADDRESS FOR READ LD E,L LD C,SETDMA CALL BDOS ;SET DMA ADDRESS LD DE,IFCB FILPAT EQU $-2 ;FCB PATCH ADDRESS LD C,READ ;READ 128 BYTES OF INPUT FILE CALL BDOS POP HL POP DE POP BC CP 0 ;SUCCESS? JR NZ,GETCH3 ;JUMP IF EOF ADD HL,DE ;POINT TO NEXT RECORD ADDRESS DEC C JP NZ,GETCH2 ;LOOP FOR 4 RECORDS GETCH3: LD A,(INBUF) ;GET FIRST CHARACTER BUFPA1 EQU $-2 ;ANOTHER BUFFER PATCH LD (HL),EOF ;PUT EOF AT BEGINNING OF FIRST UNUSED ;RECORD IN MEMORY LD HL,INBUF+1 BUFPA2 EQU $-2 ;AND YET ANOTHER BUFFER PATCH LD (INPTR),HL ;SET UP POINTER TO RECORDS JP GETRET ; ; CREWRD -- Converts the word in SRCWRD to a word in WORD. Parity bits ; are cleared, lower case is converted to upper case, and apostrophes ; that are not imbedded in words are discarded. CREWRD: LD HL,WORD ;POINTER TO DESINATION BUFFER LD DE,SRCWRD ;SOURCE BUFFER LD C,0 CREWR0: LD A,(DE) ;GET A CHARACTER CP 0 ;END OF WORD? JP Z,CREWR2 AND 07FH ;MASK PARITY CP 027H ;"'" CHARACTER? JR Z,CREWR3 AND 05FH ;MASK PARITY AND CONVERT TO UPPER CASE CP 'A' ;IS IT ONLY A HYPHEN? JR C,CREWR1 ;IF SO, SKIP IT CREADD: INC C LD (HL),A ;PUT CHARACTER IN WORD BUFFER LD A,41 ;MAXIMUM OF 40 CHARACTERS IN WORD CP C RET Z ;WORD TOO LONG. ACCEPT IT INC HL CREWR1: INC DE JP CREWR0 CREWR2: LD (HL),0 ;MARK END OF WORD LD HL,LASTC ;PUT NUMBER OF CHARACTERS IN LASTC DEC C ;C=NUMBER OF CHARACTERS - 1 JP P,CREWS2 ;JUMP IF C WASN'T ALREADY 0 INC C CREWS2: LD (HL),C RET CREWR3: LD A,0 ;IF FIRST CHARACTER IN WORD, IGNORE ;"'" CP C JR Z,CREWR1 INC DE LD A,(DE) ;IF LAST CHAR IN WORD, IGNORE CP 0 DEC DE JR Z,CREWR1 LD A,27H  ;OTHERWISE, KEEP "'" JR CREADD ; ; PUTCHR -- write a character to output file ; ; Input: A --> character to output PUTCHR: PUSH PSW PUSH BC PUSH DE PUSH HL LD HL,(OPOSS) ;GET CURRENT POSITION IN OBUFF LD (HL),A ;PUT CHARACTER IN BUFFER INC HL LD (OPOSS),HL ;UPDATE POINTER LD DE,OBUFF+512 ;AT END OF BUFFER? XOR A ;CLEAR CARRY SBC HL,DE JR Z,PUTCH0 ;WRITE OUT DATA IF END OF BUFFER PUTRET: POP HL POP DE POP BC POP PSW RET PUTCH0: LD C,4 ;LOOP COUNTER LD HL,OBUFF ;ADDRESS OF DATA LD DE,128 ;LENGTH OF EACH RECORD PUTCH1: PUSH BC PUSH DE PUSH HL LD D,H ;SET UP DMA ADDRESS LD E,L LD C,SETDMA CALL BDOS LD DE,OFCB ;WRITE RECORD TO OUTPUT FILE OUTPAT EQU $-2 ;PATCH ADDRESS FOR OUTPUT FILE FCB LD C,WRITE CALL BDOS CP 0 ;SUCCESS? JR NZ,PUTCH2 ;JUMP IF DISK FULL POP HL POP DE POP BC ADD HL,DE ;POINT TO NEXT RECORD DEC C JP NZ,PUTCH1 ;LOOP FOR 512 BYTE BUFFER LD HL,OBUFF ;RESET POINTER LD (OPOSS),HL CALL CTRLZ ;FILL BUFFER WITH EOF CHARACTER JP PUTRET ;RETURN PUTCH2: LD C,STROUT ;DISK FULL ERROR LD DE,DSKFUL CALL BDOS CALL BOOT ;GIVE UP DSKFUL: DB CR,LF,'Disk full -- aborting',CR,LF,'$' ; ; CTRLZ -- Fill the output buffer with EOF characters to prepare it for ; writing CTRLZ: LD HL,OBUFF ;BUFFER ADDRESS LD B,2 ;LOOP 256 BYTES 2 TIMES LD C,0 CTRLZ0: LD (HL),EOF ;PUT EOF IN BUFFER INC HL DEC C ;FAST COUNTER JR NZ,CTRLZ0 DEC B ;SLOW COUNTER JR NZ,CTRLZ0 RET ; ; MEMWRD -- put word in WORD into memory. If word already exists in memory ; then its the address of its status byte is returned in DE. If the ; word is not found, the word is placed in memory and the pointers ; that alphabetize the words are updated. If memory is full, a ; 1 is returned in A. Otherwise 0 is returned in A. MEMWRD: LD BC,(SRTBOT) ;GET ADDRESS OF BOTTOM OF WORD POINTER LD DE,(SRTTOP) ;TOP OF WORD POINTER JR MEMSKP MEMWR0: POP HL POP DE POP BC JP M,MEMW00 LD D,H ;MOVE HIGH POINTER DOWN LD E,L JR MEMSKP MEMW00: LD B,H ;MOVE LOW POINTER UP LD C,L MEMSKP: LD H,D ;HL IS POINTER TO RECORD BETWEEN DE AND LD L,E ;BC XOR A ;CLEAR CARRY SBC HL,BC LD A,0 ;IS BC+1=HL? CP H JP NZ,MEMWR1 LD A,2 CP L JP NZ,MEMWR1 LD HL,(SRTTOP) ;UPDATE SRTTOP=SRTTOP+2 INC HL INC HL LD (SRTTOP),HL XOR A ;CLEAR CARRY PUSH HL SBC HL,DE ;NUMBER OF BYTES IN TABLE TO MOVE LD B,H ;PUT NUMBER OF COUNTER FOR LDDR LD C,L POP HL PUSH DE ;SAVE THE ADDRESS FOR NEW WORD LD D,H ;PUT DESTINATION ADDRESS IN DE LD E,L DEC HL ;SOURCE IN HL DEC HL LDDR ;PUT A SPACE FOR NEW POINTER IN TABLE POP DE LD HL,(FREE) ;ADDRESS TO STORE NEW WORD EX DE,HL LD (HL),E ;STORE ADDRESS OF WORD IN TABLE INC HL LD (HL),D LD HL,WORD LD A,(LASTC) ;GET NUMBER OF CHARACTERS LD B,A INC B ;B = NUMBER OF CHARACTERS IN WORD + 1 INC B MEMW01: LD A,(HL) ;GET A CHARACTER LD (DE),A ;PUT IT IN MEMORY DEC DE ;STORE WORD BACKWARDS INC HL ;NEXT CHAR IN WORD DJNZ MEMW01 LD (FREE),DE ;UPDATE FREE MEMORY POINTER EX DE,HL LD DE,45 ;ALLOW ROOM FOR ANOTHER WORD XOR A ;CLEAR CARRY SBC HL,DE LD DE,(SRTTOP) ;POINTER TO TOP OF POINTER TABLE XOR A SBC HL,DE ;IS ANY MEMORY LEFT? JR C,MEMFUL ;IF NOT, NO MORE MEMORY LD A,0 ;INDICATE MEMORY LEFT RET MEMFUL: LD A,1 ;NO MEMORY LEFT RET MEMWR1: RRC L ;DIVIDE HL BY 2 LD A,L AND 07EH ;MAKE IT EVEN LD L,A LD A,0 RRC H ADC A,0 ;IF BIT 0 OF H SET, THEN C SET RRC A OR L LD L,A LD A,H AND 07FH LD H,A ADD HL,BC ;HL POINTS TO RECORD BETWEEN BC AND ;DE PUSH BC PUSH DE PUSH HL LD E,(HL) ;DE= ADDRESS OF COMPARISON WORD IN BUFF INC HL LD D,(HL) LD HL,WORD ;HL= ADDRESS OF NEW WORD BUFFER MEMWR2: LD A,(DE) ;GET A CHARACTER AND 07FH ;KILL CARRY (FOR 0 BYTE OF CORRECTED ;WORDS) CP (HL) ;COMPARE THEM JP NZ,MEMWR0 ;IF .NE. THEN TRY ANOTHER WORD LD A,0 CP (HL) ;END OF WORD? DEC DE ;DECREMENT WORD TABLE POINTER (WORDS ;ARE STORED BACKWARDS INC HL ;INCREMENT POINTER (DON'T AFFECT ;FLAGS) JR NZ,MEMWR2 POP HL POP BC ;TRASH OLD DE POP BC INC DE ;POINT TO 0 OR 080H AT END OF WORD ; (80H IS CORRECTED AND WORD FOUND) RET ;DON'T BOTHER TO BUFFER THE SAME WORD ; ; AUXDIC -- Open SPELL.DIC and load it into memory. Open FILE.UDC and load ; it as well. If FILE.ADD exists (wordstar dictionary addition file), ; load it and put its contents in FILE.UDC. AUXDIC: LD HL,NEXT ;ZERO THE MEMORY DICTIONARY LD A,0 LD (HL),A AUXDI2: LD DE,FILD$$ ;MAKE TEMPORARY .DIC OUTPUT FILE LD C,DELETE CALL BDOS LD DE,FILD$$ LD C,MAKE CALL BDOS LD HL,FILD$$ ;PATCH THIS FCB INTO OUTPUT ROUTINE LD (OUTPAT),HL LD A,(SAVPUT) ;MAKE PUTCHR: WRITE TO A FILE LD (PUTCHR),A LD DE,FILDIC ;TRY TO OPEN FILE.UDC LD C,OPEN CALL BDOS CP 0FFH ;FOUND? JP Z,AUXDI4 ;JUMP IF NOT LD HL,FILDIC ;PATCH GETCHR: TO READ FROM THIS FILE LD (FILPAT),HL LD HL,INBUF+512 ;PATCH INPTR TO READ ON FIRST CALL LD (INPTR),HL AUXDI3: CALL GETWRD ;GET A WORD INTO SRCWRD: JP Z,AUXDI4 ;IF EOF THEN START CHECKING CALL CREWRD ;CREATE WORD FROM SRCWRD: LD A,0 ;IS IT AT LEAST 2 CHARS LONG? CP C JR Z,AUXDI3 ;FORGET IT IF NOT LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT CP C JR Z,AUXDI3 PUSH HL LD HL,(FILUDC) ;INCREMENT COUNTER FOR WORDS OUTPUT INC HL ; TO FILE.UDC LD (FILUDC),HL POP HL CALL SAVWRD ;PUT WORD IN MEMORY LD HL,WORD ;PUT WORD IN OUTPUT FILE AUXDH3: LD A,(HL) ;GET A CHARACTER CP 0 ;END OF WORD? JR Z,AUXDI3 ;QUIT OUTPUT IF END CALL PUTCHR ;OUTPUT CHARACTER INC HL JR AUXDH3 AUXDI4: LD DE,FILADD ;TRY TO OPEN FILE.ADD LD C,OPEN CALL BDOS CP 0FFH ;FOUND? JP Z,AUXDI7 ;RETURN IF NOT LD HL,FILADD ;PATCH GETCHR: TO READ FROM FILE.ADD LD (FILPAT),HL LD HL,INBUF+512 ;SET INPTR TO READ ON FIRST CALL LD (INPTR),HL LD A,CR ;PUT CR LF IN OUTPUT FILE CALL PUTCHR LD A,LF CALL PUTCHR AUXDI5: CALL GETCHR ;GET A CHARACTER FROM FILE.ADD ;IGNORE ALL WORD TYPES EXCEPT 0FFH ;(EOF) CP 0FFH ;END OF WORDS IN CURRENT RECORD? JR Z,AUXDI5 ;IF YES, LOOP THROUGH RECORD CP 0 ;ZERO USED AS FILLER CHARACTER? JP Z,AUXDI5 CP EOF ;EOF? JP Z,AUXDI7 CALL GETCHR ;THIS CHARACTER IS LENGTH OF WORD CP 0 ;NO CHARACTERS? JR Z,AUXDI5 LD B,A LD HL,WORD ;BUFFER IT IN WORD AUXDI6: CALL GETCHR ;GET A CHARACTER LD (HL),A ;SAVE IT IN WORD: INC HL DJNZ AUXDI6 ;GET WHOLE WORD LD A,0 ;MARK END OF WORD LD (HL),A CALL SAVWRD ;PUT WORD IN MEMORY JP Z,AUXDI5 ;IF WORD ALREADY IN MEMORY THEN DON'T ;OUTPUT TO FILE.UDC PUSH HL LD HL,(FILUDC) ;INCREMENT COUNTER FOR WORDS OUTPUT INC HL ; TO FILE.UDC LD (FILUDC),HL POP HL LD HL,WORD ;OUTPUT WORD AUXDH6: LD A,(HL) ;GET A CHARACTER INC HL CP 0 ;END? JR Z,AUXDK6 CALL PUTCHR JR AUXDH6 AUXDK6: LD A,CR ;PUT CR LF IN OUTPUT FILE CALL PUTCHR LD A,LF CALL PUTCHR JP AUXDI5  ;GET ANOTHER CHARACTER AUXDI7: LD HL,OBUFF ;WRITE OUT REMAINING DATA IN BUFFER LD DE,128 AUXDI8: LD A,EOF ;DONE? CP (HL) JR Z,AUXDJ0 PUSH DE PUSH HL LD D,H ;SET UP DMA ADDRESS LD E,L LD C,SETDMA CALL BDOS LD DE,FILD$$ ;WRITE 128 BYTES TO FILE LD C,WRITE CALL BDOS POP HL POP DE CP 0 ;ERROR? JR NZ,AUXDI9 ;DISK FULL --> BRANCH ADD HL,DE ;POINT TO NEXT RECORD JR AUXDI8 AUXDI9: LD C,STROUT ;DISK FULL LD DE,DSKFUL ;DISK FULL MESSAGE (SEE PUTCHR) CALL BDOS CALL BOOT AUXDJ0: CALL RDICT ;READ SPELL.DIC LD HL,BOOT+81H ;POINT TO INPUT LINE BUFFER LD A,(BOOT+80H) ;NUMBER OF CHARACTERS LD B,A LD A,' ' ;LOOK FOR A SPACE TO INDICATE END OF ;INPUT FILE NAME AUXDJ1: CP (HL) JR Z,AUXDJ2 ;EXIT LOOP IF SPACE INC HL ;NEXT CHARACTER DJNZ AUXDJ1 ;LOOP WHILE CHARACTERS LEFT JR AUXRET ;NOTHING AFTER FILE. JUST RETURN. AUXDJ2: LD A,'$' ;NOW LOOK FOR $ TO INDICATE MORE DICTS. AUXDJ3: CP (HL) JR Z,AUXDJ4 ;EXIT LOOP IF FOUND INC HL DJNZ AUXDJ3 ;CONTINUE WHILE CHARACTERS LEFT JR AUXRET ;NOT FOUND AUXDJ4: LD A,' ' ;ELMINATE SPACES INC HL ;SKIP '$' DJNZ AUXDJ5 ;CONTINUE IF CHAR LEFT JR AUXRET AUXDJ5: CP (HL) JR NZ,AUXDJ6 ;STOP IF NOT SPACE INC HL DJNZ AUXDJ5 JR AUXRET ;DONE IF JUST SPACES AUXDJ6: LD A,0 LD (SPLDIC),A ;USE DEFAULT DRIVE LD DE,SPLDIC+1 ;FCB BUFFER TO USE LD A,' ' ;NULL FILE NAME (SPACES) PUSH BC LD B,8 ;FIRST DO FILENAME, NOT EXTENSION AUXDJ7: LD (DE),A INC DE ;CLEAR ENTIRE FILENAME DJNZ AUXDJ7 LD DE,SPLDIC+12 ;GET REST OF FCB LD B,24 LD A,0 ;ZERO REST OF FCB AUXDJ8: LD (DE),A INC DE DJNZ AUXDJ8 POP BC LD DE,SPLDIC+1 ;POINT TO FCB LD C,0 ;NO CHARACTERS WRITTEN TO FCB YET LD A,2 ;ARE AT LEAST 3 CHAR LEFT? CP B JP NC,AUXDJ9 ;IF NOT, CAN'T BE A DRIVE SPECIFIED INC HL LD A,(HL) ;SEE IF NEXT CHAR IS A ':' DEC HL CP ':' JR NZ,AUXDJ9 ;JUMP IF NO DRIVE SPECIFIED LD A,(HL) ;GET THE DRIVE INC HL ;POINT TO NEXT CHAR AFTER :  INC HL DEC B DEC B AND 11011111B ;MAKE SURE IT'S UPPER CASE SUB 'A'-1 ;MAKE DRIVE NUMBER BASED AT 0 LD (SPLDIC),A ;STORE IT IN FCB AUXDJ9: LD A,(HL) ;GET A CHARACTER CP ' ' ;END OF FILENAME? JR Z,AUXDK2 ;READ THE FILE CP '.' ;PERIOD? DISCARD EXTENSION IF YES. JR Z,AUXDK0 LD (DE),A ;FCB IT INC DE INC C LD A,8 ;8 CHARACTERS WRITTEN YET? CP C JR Z,AUXDK0 INC HL DJNZ AUXDJ9 JR AUXDK2 ;READ THE FILE AUXDK0: LD A,' ' ;SEARCH FOR SPACE TO END FILENAME ;DISCARD EXTRA CHARACTERS AUXDK1: CP (HL) JR Z,AUXDK2 INC HL ;TRY NEXT CHAR DJNZ AUXDK1 AUXDK2: DEC HL ;POINT TO CHAR BEFORE END OR SPACE INC B ;1 MORE LEFT NOW CALL RDICT ;READ FROM THE DICTIONARY JP AUXDJ4 ;TRY FOR ANOTHER DICTIONARY AUXRET: LD DE,FILD$$ ;CLOSE TEMPORARY FILE LD C,CLOSE CALL BDOS LD HL,OFCB ;PATCH PUTCHR: TO OUTPUT TO FILE.$$$ LD (OUTPAT),HL LD A,0C9H ;RETURN OPCODE LD (PUTCHR),A ;MAKE PUTCHR: DO NOT OUTPUT CALL CTRLZ ;CLEAR OUTPUT BUFFER LD HL,OBUFF ;CLEAR OUTPUT BUFFER POSITION LD (OPOSS),HL LD HL,(FREE) ;GET ADDRESS OF NEXT FREE BYTE INC HL LD (SRTBOT),HL ;POINT TO TABLE FOR INPUT FILE INDEX INC HL INC HL LD (SRTTOP),HL ;TOP OF TABLE LD HL,(BDOS+1) ;GET ADDRESS OF BDOS DEC HL ;POINT TO FREE MEMORY LD (FREE),HL LD HL,INBUF+512 LD (INPTR),HL ;SET INPUT FILE POINTER TO READ RECORD ;ON NEXT GETCHR: CALL CALL CTRLZ ;CLEAN OUTPUT FILE BUFFER LD A,LF ;RESET LASTCH FOR INPUT LD (LASTCH),A LD DE,UDCMSG ;OUTPUT: "Words read from...FILE.UDC" LD C,STROUT CALL BDOS LD HL,(FILUDC) ;NUMBER OF WORDS WRITTEN LD B,H ;GOES IN BC LD C,L LD HL,FILD$$ ;FCB ADDRESS IN HL LD A,'U' ;CHANGE 'FILENAME.D$$' TO '.UDC' LD (FILD$$+9),A LD A,'D' LD (FILD$$+10),A LD A,'C' LD (FILD$$+11),A CALL TYPFIL ;WRITE FILENAME AND # TO CONSOLE LD A,'D' ;CHANGE IT BACK TO '.D$$' LD (FILD$$+9),A LD A,'$' LD (FILD$$+10),A LD (FILD$$+11),A RET UDCMSG: DB CR,LF,'Words written to dictionary $' ; ; RDICT - Read dictionary in SPLDIC FCB ; RDICT: PUSH PSW PUSH BC PUSH DE PUSH HL LD HL,INBUF+512 LD (INPTR),HL ;SET INPUT FILE POINTER TO READ RECORD ;ON NEXT GETCHR CALL LD HL,0 LD (SPLDC),HL ;RESET # WORDS READ LD DE,SPLDIC ;FCB FOR SPELL.DIC LD C,OPEN CALL BDOS PUSH PSW LD C,CURDSK ;GET THE DEFAULT DRIVE BEFORE ITS CALL BDOS ; TOO LATE INC A LD (DEFDRV),A POP PSW CP 0FFH ;FOUND? JR NZ,RDICT0 ;YES, READ FROM IT LD A,(SPLDIC) ;WAS DEFAULT DRIVE TESTED? CP 0 JR NZ,RDICT2 ;IF SPECIFIC DRIVE TESTED AND NOT ;FOUND THEN GIVE UP LD A,1 ;TRY DRIVE "A" LD (SPLDIC),A LD DE,SPLDIC LD C,OPEN CALL BDOS CP 0FFH ;FOUND? JP Z,RDICT2 ;IF NOT, GIVE UP RDICT0: LD HL,SPLDIC ;PATCH FCB ADDRESS INTO GETWRD LD (FILPAT),HL RDICT1: CALL GETWRD ;GET A WORD INTO SRCWRD: JP Z,RDICT3 ;IF EOF THEN START CHECKING CALL CREWRD ;CREATE WORD FROM SRCWRD: LD A,0 ;IS IT AT LEAST 2 CHARS LONG? CP C JR Z,RDICT1 ;FORGET IT IF NOT LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT CP C JR Z,RDICT1 PUSH HL LD HL,(SPLDC) ;INCREMENT COUNTER FOR WORDS IN INC HL ; SPELL.DIC LD (SPLDC),HL POP HL CALL SAVWRD ;PUT WORD IN MEMORY JR RDICT1 RDICT2: LD DE,RNT ;PRINT CR,LF LD C,STROUT CALL BDOS LD A,(SPLDIC) ;DEFAULT DRIVE USED? CP 0 JR NZ,RDICU0 ;JUMP IF NOT LD A,(DEFDRV) ;GET DEFAULT DRIVE LD (SPLDIC),A ;AND PUT IT IN FCB RDICU0: LD A,(SPLDIC) ;GET DRIVE ADD A,'A'-1 ;MAKE IT A LETTER LD C,CONOUT LD E,A CALL BDOS ;PRINT DRIVE NAME LD E,':' LD C,CONOUT CALL BDOS ;AND A COLON LD DE,SPLDIC+1 ;POINT TO FILENAME LD HL,SPLDIC+12 ;POINT TO FIRST BYTE AFTER NAME LD A,'$' LD (SPLDIC+12),A ;MARK END FOR OUPTUT LD C,STROUT CALL BDOS ;PRINT THE FILE NAME LD DE,RNT0 ;PRINT NOT FOUND LD C,STROUT CALL BDOS JR RDICTR RNT: DB CR,LF,'$' RNT0: DB ' not found$' RDICT3: LD DE,RDIWRD ;FOUND MESSAGE LD C,STROUT CALL BDOS LD HL,(SPLDC) ;STORE # WORDS READ IN BC LD B,H LD C,L LD HL,SPLDIC ;FCB ADDRESS CALL TYPFIL ;TYPE FILE NAME AND # WORDS IN IT JR RDICTR RDIWRD: DB CR,LF,'Words read from dictionary $' RDICTR: POP HL POP DE POP BC POP PSW RET ; ; TYPFIL -- print name of dictionary file being read and words found in it ; ; Input: BC --> Number of words found in file ; HL --> Address of file's FCB ; TYPFIL: PUSH PSW PUSH BC PUSH DE PUSH HL LD A,(HL) ;DEFAULT DRIVE USED? CP 0 JR NZ,TYPFI0 ;JUMP IF NOT LD A,(DEFDRV) ;GET DEFAULT DRIVE LD (HL),A ;AND PUT IT IN FCB TYPFI0: LD A,(HL) ;GET DRIVE ADD A,'A'-1 ;MAKE IT A LETTER PUSH BC PUSH HL LD C,CONOUT LD E,A CALL BDOS ;PRINT DRIVE NAME LD E,':' LD C,CONOUT CALL BDOS ;AND A COLON POP HL LD D,H LD E,L INC DE ;POINT TO FILENAME LD BC,12 ADD HL,BC ;HL POINTS TO END OF FILENAME LD A,'$' LD (HL),A ;MARK END FOR OUPTUT LD C,STROUT CALL BDOS ;PRINT THE FILE NAME LD DE,TYPSPA ;PRINT SPACES AFTER NAME LD C,STROUT CALL BDOS POP BC LD H,B ;NUMBER OF WORDS FOUND LD L,C CALL DECOUT ;PRINT NUMBER POP HL POP DE POP BC POP PSW RET TYPSPA: DB ': $' ; ; SAVWRD -- put WORD in memory dictionary ; SAVWRD: CALL USRTST ;WAS WORD ALREADY MARKED? CP 1 RET Z ;RETURN WITH Z SET IF ALREADY MARKED LD HL,(FREE) ;GET NEXT AVAILABLE BYTE LD DE,WORD ;POINT TO WORD SAVWR0: LD A,(DE) ;GET A CHARACTER LD (HL),A ;STORE IT IN BUFFER INC DE ;INCREMENT POINTERS INC HL CP 0 ;END OF WORD? JR NZ,SAVWR0 LD (HL),A ;MARK END OF TABLE WITH 0 LD (FREE),HL ;UPDATE FREE MEMORY POINTER LD A,1 AND A ;MAKE FOR Z NOT SET RET ; ; WRDTST -- Search for WORD in dictionary. If it is not found, try ; stripping of suffixes and looking for new word with a flag set. ; ; Returns in A: 0 <-- WORD not found ; 1 <-- WORD found ; 2 <-- root word found but necessary ; suffix flag not set. Returned ; only if suffixes were stripped. WRDTST: PUSH BC PUSH DE PUSH HL LD HL,0 LD (FLAG),HL ;NO FLAGS FOR FIRST LOOKUP CALL FINDIT ;LOOK FOR WORD IN DICTIONARY PUSH PSW LD A,1 ;ASSUME THAT WORD WAS FOUND AND ;INDICATE WORKING IN ALPHABETICAL ORDER ;STILL (DECODING OF CURRENT RECORD ;BY LOOKUP ROUTINE CAN CONTINUE FROM ;CURRENT POSITION) LD (ALPHA),A POP PSW CP 0 ;NOT FOUND? JP Z,WRDTS0 ;KEEP TRYING IF NOT FOUND WRDRET: POP HL POP DE POP BC RET WRDTS0: CALL USRTST ;TEST USER DICTIONARY CP 1 ;FOUND? JP Z,WRDRET LD A,0 ;NOT WORKING IN ALPHABETICAL ORDER ;ANY MORE LD (ALPHA),A LD HL,LASTC ;PUT NUMBER OF CHARACTERS -1 IN C LD C,(HL) LD B,0 ;MAKE BC AN OFFSET TO LAST CHAR LD HL,WORD ;POINT TO WORD ADD HL,BC ;POINT TO LAST CHARACTER IN WORD LD A,(HL) QISIT 'E',FLGV ;WORD ENDS IN "E" QISIT 'H',FLGH ;"H" QISIT 'Y',FLGY ;"LY" QISIT 'G',FLGG ;"ING" QISIT 'N',FLGN ;"TION, "EN" QISIT 'D',FLGD ;"ED", "IED" QISIT 'T',FLGT ;"EST", "IEST" QISIT 'R',FLGR ;"ER", "IER" QISIT 'S',FLGS ;LOTS OF WORDS ENDING IN "S" LD A,0 JP WRDRET ;NO FLAGS FIT FLGV: LD A,2 ;WORD MUST BE 4 CHARS LONG CP C JP P,WRDNOT ;NOT FOUND IF TOO SHORT LD DE,VFLAG ;LOOKING FOR V FLAG LD (FLAG),DE DEC HL ;CHARACTER BEFORE ISIT 'V' DEC HL ISIT 'I' LD (HL),'E' ;GET "CREATIVE" INC HL LD (HL),0 CALL FINDIT CP 0 ;FOUND? JP NZ,WRDRET ;BRANCH IF FOUND DEC HL ;POINT TO CHAR BEFORE NEW "E" DEC HL JISIT 'E',WRDNOT ;KILL "CREATEIVE" INC HL LD (HL),0 CALL FINDIT JP WRDRET FLGH: LD A,2 ;MUST BE 4 CHARS LONG CP C JP P,WRDNOT LD DE,HFLAG ;SEEKING WORD WITH H FLAG SET LD (FLAG),DE DEC HL ISIT 'T' DEC HL JISIT 'Y',WRDNOT ;KILL "TWENTYTH" INC HL LD (HL),0 ;NEW END OF WORD CALL FINDIT ;GET "HUNDREDTH" CP 0 ;FOUND? JP NZ,WRDRET ;RETURN IS FOUND LD A,4 ;WORDS WITH "IETH" MUST BE 6 CHARS LONG CP C JP P,WRDNOT DEC HL ISIT 'E' DEC HL ISIT 'I' LD (HL),'Y' ;MODIFY WORD TO END IN "Y" INC HL LD (HL),0 CALL FINDIT ;GET "TWENTIETH" JP WRDRET FLGY: LD A,2 ;MUST BE 4 CHARACTERS LONG (AT LEAST) CP C JP P,WRDNOT LD DE,YFLAG ;WORDS MUST HAVE Y FLAG SET LD (FLAG),DE DEC HL ISIT 'L' LD (HL),0 ;MARK NEW END OF WORD CALL FINDIT ;GET "QUICKLY" JP WRDRET FLGG: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG CP C JP P,WRDNOT LD DE,GFLAG ;SET G FLAG LD (FLAG),DE FLGGE: DEC HL ISIT 'N' DEC HL ISIT 'I' LD (HL),'E' INC HL LD (HL),0 CALL FINDIT ;GET "FILING" CP 0 JP NZ,WRDRET ;RETURN IF FOUND DEC HL DEC HL JISIT 'E',WRDNOT ;KILL "FILEING" INC HL LD (HL),0 CALL FINDIT ;GET "CROSSING" JP WRDRET FLGN: LD A,2 ;MUST BE 4 CHARS LONG CP C JP P,WRDNOT LD DE,NFLAG ;SET N FLAG LD (FLAG),DE FLGNEE: DEC HL JISIT 'O',FLGNO ;WORD WITH "TION" ISIT 'E' ;IF .EQ. "TION" THEN .EQ. "EN" DEC HL JISIT 'E',WRDNOT ;KILL "CREATEEN" JISIT 'Y',WRDNOT ;KILL "MULTIPLYEN" INC HL LD (HL),0 CALL FINDIT ;GET "FALLEN" JP WRDRET FLGNO: LD A,3 ;MUST BE 4 CHARS LONG CP C JP P,WRDNOT DEC HL ISIT 'I' LD (HL),'E' INC HL LD (HL),0 CALL FINDIT ;GET "CREATION" CP 0 JP NZ,WRDRET ;RETURN IF FOUND LD A,7 ;MUST BE AT LEAST 9 CHARS LONG CP C JP P,WRDNOT DEC HL DEC HL ISIT 'T' DEC HL ISIT 'A' DEC HL ISIT 'C' DEC HL ISIT 'I' LD (HL),'Y' INC HL LD (HL),0 CALL FINDIT JP WRDRET FLGD: LD A,2 ;MUST BE 4 CHARACTERS LONG CP C JP P,WRDRET LD DE,DFLAG ;SET D FLAG LD (FLAG),DE FLGDE: DEC HL ISIT 'E' INC HL LD (HL),0 CALL FINDIT ;GET "CREATED" CP 0 JP NZ,WRDRET DEC HL DEC HL JISIT 'E',WRDNOT ;KILL "CREATEED" LD A,(HL) CP 'Y' ;IF .NE. "Y" TRY OTHER SUFFIXES JP NZ,FLGD5 DEC HL CALL VOWEL ;VOWEL MUST BE BEFORE "Y" JP NZ,WRDNOT INC HL INC HL LD (HL),0 CALL FINDIT ;GET "CONVEYED" JP WRDRET FLGD5: INC HL LD (HL),0 CALL FINDIT ;GET "CROSSED" CP 0 JP NZ,WRDRET ;RETURN IF FOUND DEC HL ISIT 'I' DEC HL CALL VOWEL ;CAN'T BE A VOWEL JP Z,WRDNOT INC HL LD (HL),'Y' INC HL LD (HL),0 CALL FINDIT ;GET "IMPLIED" JP WRDRET FLGT: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG CP C JP P,WRDNOT LD DE,TFLAG ;T FLAG MUST BE SET LD (FLAG),DE DEC HL JISIT 'S',FLGDE ;SAME RULES AS 'D' FLAG IF ENDS ;IN "ST" JP WRDNOT ;NOT FOUND IF NOT "ST" FLGR: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG CP C JP P,WRDNOT LD DE,RFLAG ;SET R FLAG LD (FLAG),DE FLGRE: JP FLGDE ;SAME RULES AS D FLAG FLGS: LD A,2 ;MUST BE 4 CHARS LONG CP C JP P,WRDNOT LD DE,SFLAG ;TRY PURE "S" FLAG FIRST LD (FLAG),DE DEC HL JISIT 'S',FLGP ;"NESS", "INESS" JISIT 27H,FLGM ;"'S" CALL SXZH ;IS IT S, X, Z OR H? JP Z,WRDNOT ;IF YES, THEN ILLEGAL WORD LD A,(HL) ;PURE "S" LEGAL IF .NE. "Y" CP 'Y' JR NZ,FLGS2 DEC HL CALL VOWEL JP NZ,WRDNOT ;ILLEGAL WORD IF NOT A VOWEL INC HL FLGS2: INC HL LD (HL),0 CALL FINDIT ;GET "CONVEYS", "BATS" CP 0 JP NZ,WRDRET ;RETURN IF FOUND DEC HL LD A,(HL) QISIT 'R',FLGZ ;"ERS", "IERS" QISIT 'N',FLGX ;"IONS", "ICATIONS", "ENS" QISIT 'G',FLGJ ;"INGS" ISIT 'E' DEC HL CALL SXZH JP NZ,FLGS5 ;IF LETTER NOT S,X,Z OR H TRY "IES" INC HL LD (HL),0 CALL FINDIT ;GET "FIXES" JP WRDRET FLGS5: ISIT 'I' DEC HL CALL VOWEL JP Z,WRDNOT ;CAN'T BE A VOWEL INC HL LD (HL),'Y' INC HL LD (HL),0 CALL FINDIT ;GET "IMPLIES" JP WRDRET FLGX: LD DE,XFLAG ;SET X FLAG LD (FLAG),DE JP FLGNEE ;SAVE AS "N" FLAG NOW FLGJ: LD DE,JFLAG ;SET J FLAG LD (FLAG),DE JP FLGGE ;SAVE AS "G" FLAG NOW FLGZ: LD DE,ZFLAG ;SET Z FLAG LD (FLAG),DE JP FLGRE ;SAVE AS "R" FLAG NOW FLGP: LD A,3 ;MUST BE 5 CHARS LONG CP C JP P,WRDNOT LD DE,PFLAG ;SET P FLAG LD (FLAG),DE DEC HL ISIT 'E' DEC HL ISIT 'N' DEC HL LD A,(HL) CP 'Y' JP NZ,FLGP4 ;LEGAL IF .NE. "Y" DEC HL CALL VOWEL JP NZ,WRDNOT ;ILLEGAL IF "Y" AND NO VOWEL INC HL FLGP4: INC HL LD (HL),0 CALL FINDIT ;GET "LATENESS", "GRAYNESS" CP 0  JP NZ,WRDRET ;RETURN IF FOUND DEC HL ISIT 'I' DEC HL CALL VOWEL JP Z,WRDNOT ;CAN'T BE A VOWEL INC HL LD (HL),'Y' INC HL LD (HL),0 CALL FINDIT ;GET "CLOUDINESS" JP WRDRET FLGM: LD DE,MFLAG ;SET M FLAG LD (FLAG),DE LD (HL),0 CALL FINDIT ;GET "DOG'S" JP WRDRET WRDNOT: LD A,0 ;NOT FOUND JP WRDRET ; ; VOWEL -- determine whether character in (HL) is a vowel. ; ; Returns: Z flag SET <-- is a vowel ; Z flag CLEAR <-- is not a vowel VOWEL: PUSH BC PUSH HL LD A,(HL) ;GET CHARACTER LD HL,VOWELS ;POINTER TO VOWELS LD BC,5 ;5 VOWELS CPIR ;TEST AGAINST ALL VOWELS ;SET STATUS BITS TO BE USED AFTER RET POP HL POP BC RET VOWELS: DB 'AEIOU' ; ; SXZH -- same as VOWEL put for the characters S, X, Z and H SXZH: PUSH BC PUSH HL LD A,(HL) ;GET CHARACTER LD HL,SXZH0 ;POINTER TO CHARACTER LIST LD BC,4 ;4 POTENTIAL MATCHES CPIR ;TEST AGAINST S, X, Z, H POP HL POP BC RET SXZH0: DB 'SXZH' ; ; Determine which  record of dictionary would contain WORD. Puts record in ; BC FINDIT: PUSH BC PUSH DE PUSH HL LD A,(ALPHA) ;STILL IN ALPHABETICAL ORDER? AND A JP NZ,FINLO5 ;DON'T DECREMENT POINTERS IF YES LD HL,(LSTADR) ;GET ADDRESS OF LAST DICTIONARY POINTER ;USED FINLOW: LD DE,WORD EX DE,HL LD BC,4 FINLO1: LD A,(DE) ;MAKE SURE THAT CURRENT RECORD IS ;BEFORE WORD INC DE CPI JP M,FINLO5 ;IF EARLIER, JUMP JR NZ,FINLO2 ;IF LATER, THEN DECREMENT JP PE,FINLO1 ;LOOP WHILE BC-1 .NE. 0 FINLO2: LD BC,(LSTREC) ;DECREMENT THE POINTERS DEC BC LD HL,0 ;IS IT TOO LOW? XOR A SBC HL,BC JR Z,FINLO5 ;IF SO, THEN FORGET IT LD (LSTREC),BC LD HL,(LSTADR) LD DE,4 XOR A ;CLEAR CARRY SBC HL,DE LD (LSTADR),HL JP FINLOW ;TRY AGAIN FINLO5: LD BC,(LSTREC) ;GET LAST RECORD NUMBER READ LD HL,(LSTADR) ;GET ADDRESS OF LAST DICTIONARY POINTER ;USED FINDI0: PUSH BC PUSH HL LD DE,WORD EX DE,HL LD BC,4 ;COMPARING UP TO 4 CHARACTERS FINDI1: LD A,(DE) ;GET A CHARACTER FROM POINTER TABLE INC DE CPI ;COMPARE TO WORD: JR NZ,FINDI2 ;JUMP IF DIFFERENT JP PO,FINDI3 ;JUMP IF ALL 4 CHARACTERS EQUAL JR FINDI1 ;TRY ANOTHER FINDI2: JP P,FINDI3 ;TOO FAR IN DICTIONARY LD DE,4 POP HL POP BC ADD HL,DE ;POINT TO NEXT RECORD INDEX INC BC JP FINDI0 FINDI3: POP HL POP BC FINDI4: DEC BC LD DE,4 XOR A ;CLEAR CARRY SBC HL,DE LD (LSTREC),BC ;UPDATE POINTERS LD (LSTADR),HL ; ; LOOKUP -- loop through as many records as it takes to be sure that word is ; not in dictionary LOOKUP: CALL DICFND CP 0FFH ;IF RETURN STATUS=0FFH THEN WORD COULD ;BE IN NEXT RECORD JR NZ,LOOKU0 ;RETURN IF NOT IN NEXT RECORD INC BC LD HL,0+(TABBOT-TABTOP)/4 ;MAKE SURE NOT PAST LAST RECORD XOR A ;CLEAR THE CARRY BIT SBC HL,BC JR NZ,LOOKUP ;TRY NEXT RECORD IF NOT AT END LD A,0 LOOKU0: POP HL ;RESTORE THE STACK POP DE POP BC RET ; ; DICFND -- read record in BC from dictionary. Determine whether WORD is ; in it. ; ; Returns in A: 0 <-- word not found ; 1 <-- word found ; 2 <-- word found but flag not set ; 0FFH<-- word not found but it may be in next ; record DICFND: PUSH HL PUSH DE PUSH BC RLC B ;MULTIPLY BC BY 2 SO IT POINTS TO ;256 BYTE RECORD RLC C LD A,0 ADC A,B ;GET THE CARRY FROM RLC C LD B,A LD A,C AND 0FEH ;KILL BIT 0 LD C,A LD HL,(DICREC) ;GET THE CURRENT RECORD IN RAM DEC HL ;POINT TO FIRST 128 BYTES OF 256 ;BYTE DICTIONARY RECORDS XOR A ;CLEAR CARRY BIT SBC HL,BC ;ATTEMPT TO READ THE SAME RECORD? JR NZ,DICDSK ;IF NO, READ FROM DISK LD A,(ALPHA) ;WORKING IN APHABETICAL ORDER? AND A JP NZ,DICFO1 ;JUST GET ANOTHER WORD IF YES LD A,(CURBIT) ;ROTATE CURRENT BYTE SO IT IS THE RIGHT ;POSITION IN CASE SAME RECORD USED AND 0111B ;IS IT ON CORRECT ROTATION NOW? JR Z,DICFN0 LD HL,(CURBYT) ;ADDRESS OF CURRENT BYTE LD B,A LD A,8 ;MUST COMPLETE 8 ROTATIONS, TOTAL SUB B ;SUBTRACT NUMBER ALREADY DONE LD B,A ROTATE: RLC (HL) DJNZ ROTATE JP DICFN0 ;START TESTING WORDS DICDSK: PUSH BC LD (DICREC),BC ;NEW RECORD TO READ LD C,SETDMA ;SET DMA TO DICTIONARY BUFFER LD DE,DICBUF ;DICIONARY BUFFER IN RAM CALL BDOS LD C,RANREA ;BDOS RANDOM READ CODE LD DE,DICFCB ;FCB OF DICTIONARY CALL BDOS POP BC INC BC ;GET 256 BYTES LD (DICREC),BC ;NEXT RECORD NUMBER LD C,SETDMA ;DMA ADDRESS 256 BYTES HIGHER LD DE,DICBUF+128 CALL BDOS LD C,RANREA LD DE,DICFCB CALL BDOS ;READ NEXT RECORD DICFN0: LD HL,DICBUF-1 ;INITIALIZE POINTER TO DICTIONARY ;BUFFER LD (CURBYT),HL LD A,0 ;CURRENT BIT=0 LD (CURBIT),A DICFN1: CALL MOVWRD ;GET A WORD DICFO1: LD HL,WORD ;POINT TO WORD LD BC,DICWRD ;POINTER TO DICTIONARY WORD DICFN2: LD A,(BC) ;GET A LETTER FROM DICTIONARY WORD CP (HL) ;THE SAME? INC HL ;POINT TO NEXT CHARACTER INC BC JR NZ,DICFN4 ;IF NOT, TRY SOME MORE OR END CP 0 ;END OF WORD? JR NZ,DICFN2 ;TRY ANOTHER CHARACTER  LD C,1 ;INDICATE WORD FOUND LD HL,(DICFL) LD A,(FLAG) ;GET FIRST FLAG CP 0 ;NO FLAG WANTED? JR Z,DICFN3 ;TRY NEXT BYTE OF FLAG IF NONE AND L CP 0 ;INDICATE WORD FOUND IF .NE. 0 JR NZ,DICRET LD C,2 ;INDICATE MAIN WORD FOUND/NO FLAG JR DICRET DICFN3: LD A,(FLAG+1) ;TRY NEXT FLAG BYTE CP 0 ;NO FLAG WANTED? JR Z,DICRET ;IF NONE WANTED THEN WORD FOUND AND H CP 0 ;FOUND IF .NE. 0 JR NZ,DICRET LD C,2 ;INDICATE MAIN WORD FOUND/NO FLAG JR DICRET DICFN4: JP M,DICFN1 ;IF TEST WORD IS EARLIER IN ALPHABET, ;TRY NEXT DICTIONARY WORD LD C,0 ;NOT FOUND ; JP DICRET DICRET: LD A,C ;PUT STATUS BYTE IN A POP BC POP DE POP HL RET ; ; MOVWRD -- read a word from the dictionary by decoding the flags. ; ; Returns: DICWRD <-- word from dictionary MOVWRD: LD A,0 LD B,4 CALL GETBIT ;GET 4 BITS INTO A LD HL,DICWRD ;POINT TO DICTIONARY WORD BUFFER LD B,0 LD C,A ;NUMBER OF CHARACTERS TO KEEP ADD HL,BC MOVWR1: LD A,0 LD B,3 ;GET! FIRST 3 OF 5 BITS FOR CHARACTER CALL GETBIT ;BITS IN A CP 0111B ;IS IT AN END OF WORD MARK? JR Z,GETFLG ;GET THE FLAGS NOW LD B,2 ;GET REMAINING 2 BITS CALL GETBIT CP 0 ;IF ZERO THEN END OF RECORD JP Z,MOVMOR ;INDICATE MAY BE IN NEXT RECORD ADD A,40H ;MAKE IT ASCII CP 'Z'+1 ;IS IT AN ENCODED "'"? JR NZ,MOVWR4 ;JUMP IF NOT LD A,027H ;MAKE IT A "'" MOVWR4: LD (HL),A ;BUFFER THE CHARACTER INC HL JR MOVWR1 ;GET ANOTHER CHARACTER MOVMOR: LD C,0FFH ;INDICATE MAY BE IN NEXT RECORD POP DE ;GET USELESS RETURN WORD JP DICRET ;RETURN ; ; GETFLG -- read suffix flags from buffer. ; ; Returns: DICFL <-- 16 byte flag word GETFLG: LD A,0 LD (HL),A ;MARK THE END OF THE WORD LD B,4 ;GET 4 BITS FOR NUMBER OF FLAGS VALUE CALL GETBIT LD B,A ;NUMBER OF BITS IN B LD HL,0 CP 0 ;ARE ANY BITS THERE TO COPY? JP Z,GETFL8 ;RETURN IF NONE PUSH BC CP 8 ;MORE THAN 8 BITS? JP M,GETFL1 ;JUMP IF NOT LD B,7 ;GET 7 BITS FOR FIRST BYTE GETFL1: LD A,0 CALL GETBIT POP BC LD L,A ;THIS IS THE LOW BYTE OF FLAGS LD A,B CP 8 ;GET MORE IF GREATER THAN 8 BITS JP P,GETFL4 LD A,7 ;COMPUTE NUMBER OF ROTATIONS NECESSARY ;TO PUT IT IN THE RIGHT PLACE SUB B JP Z,GETFL8 ;IF EXACTLY 7 THEN DONE LD B,A ;COUNTER GETFL3: RLC L DJNZ GETFL3 JP GETFL8 ;NO RETURN GETFL4: SUB 7 ;GET NUMBER OF BITS NEEDED FOR BYTE 2 LD B,A LD A,0 PUSH BC CALL GETBIT ;GET BIT FOR HIGH BYTE OF STATUS FLAG POP BC LD H,A ;SAVE HIGH BYTE LD A,7 ;COMPUTE NUMBER OF ROTATIONS LEFT SUB B JR Z,GETFL8 ;RETURN IF NONE LD B,A GETFL6: RLC H DJNZ GETFL6 GETFL8: LD (DICFL),HL ;SAVE THE FLAG RET ; ; GETBIT -- read number of bits in B from dictionary buffer. ; ; Returns: A <-- byte value of B bits GETBIT: PUSH DE PUSH HL EX AF,AF' ;A HOLDS DESIRED DECODED BYTE. ;A' HOLD CURRENT BIT VALUE LD A,(CURBIT) LD HL,(CURBYT) ;ADDRESS OF CURRENT BYTE FOR OUTPUT LD D,(HL) ;D = CURRENT BYTE VALUE EX AF,AF' GETBI0: EX AF,AF' ;GET CURRENT BIT VALUE AND 0111B ;MASK 1ST THREE BITS JR NZ,GETBI1 ;IF .NE. 0 THEN NOT TIME TO INC CURBYT LD (HL),D ;RESTORE OLD BYTE TO ORIGINAL VALUE INC HL LD (CURBYT),HL LD D,(HL) ;UPDATE D = CURRENT BYTE VALUE GETBI1: INC A EX AF,AF' ;BACK TO DECODED BYTE RLC D ;MOVE BYTE SO NEXT BIT IN RIGHT PLACE RLC A ;MAKE A READY TO RECEIVE BIT 0,D ;IS THE BIT ON? JR Z,GETBI2 ;DON'T SET A IF IT ISN'T OR 1 ;SET BIT 1 OF A GETBI2: DJNZ GETBI0 ;LOOP THROUGH DESIRED NUMBER OF BITS EX AF,AF' ;GET CURRENT BIT LD (CURBIT),A ;UPDATE IT EX AF,AF' LD (HL),D ;UPDATE BYTE FOR NEXT CALL POP HL POP DE RET ; ; USRTST -- test user dictionary in memory for WORD. ; ; Returns in A: 0 <-- word not found ; 1 <-- word found USRTST: LD HL,NEXT ;BEGINNING OF USER DICTIONARY BUFFER LD A,0 CP (HL) ;DOES ONE EXIST? RET Z ;CAN'T FIND ELEMENT OF AN EMPTY SET! LD DE,WORD ;ADDRESS OF WORD FOR COMPARING USRTS1: LD A,(DE) ;GET A CHARACTER CP (HL) ;ARE THEY THE SAME? JR NZ,USRTS2 ;IF NOT SAME, TRY ANOTHER WORD INC DE INC HL CP 0 ;END OF WORD? JR NZ,USRTS1 ;LOOP THROUGH WHOLE WORD LD A,1 ;FOUND IT! RET USRTS2: LD A,0 USRTS3: CP (HL) ;LOOK FOR END OF WORD INC HL JR NZ,USRTS3 CP (HL) ;IF NEXT BYTE ALSO 0 THEN END OF TABLE RET Z LD DE,WORD JR USRTS1 ;TEST ANOTHER WORD ; ; DECOUT - Output number in HL to console in decimal ; DECOUT: PUSH PSW PUSH BC PUSH DE PUSH HL LD B,0 ;B WILL BE 1 ONCE NON-ZERO CHAR OUTPUT LD DE,10000 ;START BY TRYING 10,000'S CALL NUMOUT ;OUTPUT A NUMBER LD DE,1000 ;1000'S CALL NUMOUT LD DE,100 CALL NUMOUT LD DE,10 CALL NUMOUT LD B,1 ;GUARANTEE THAT 0 WILL PRINT LD DE,1 CALL NUMOUT POP HL POP DE POP BC POP PSW RET NUMOUT: LD C,0 ;COUNTER FOR NUMBER OF SUBTRACTIONS NUMOU0: INC C ;COUNT LOOPS THROUGH SUBTRACTION XOR A ;CLEAR CARRY SBC HL,DE ;SUBTRACT UNITS UNTIL CARRY JP NC,NUMOU0 ADD HL,DE ;RESET TO LAST POSITIVE VALUE DEC C ;DON'T COUNT LAST SUBRTRACTION JR NZ,NUMOU1 ;IF NOT ZERO, THEN OUTPUT CP B ;ANYTHING OUTPUT YET? RET Z ;IF NOT, THEN DON'T PRINT A 0 NUMOU1: LD B,1 ;INDICATE OUTPUT SENT LD A,C ADD A,'0' ;CONVERT TO ASCII LD E,A ;OUTPUT TO CONSOLE LD C,CONOUT ;CONSOLE OUTPUT CODE PUSH BC PUSH HL CALL BDOS POP HL POP BC RET INPTR: DW INBUF+512 ;POINTER TO CURRENT BYTE IN INPUT BUFF OPOSS: DW OBUFF ;POINTER TO CURRENT BYTE IN OUTPUT BUFF SRTBOT: DW NEXT ;POINTER TO BEGINNING OF MEMORY POINTER ;TABLE OF ALPHABETIZED WORDS FROM INPUT ;FILE SRTTOP: DW NEXT+2 ;POINTER TO TOP OF MEMORY POINTER TABLE FREE: DW NEXT ;POINTER TO NEXT FREE BYTE IN TPA LASTCH: DB LF ;LAST CHARACTER INDICATOR OLSTCH: DB LF ;BUFFER FOR OLD LAST CHARACTER WHEN ;MULTIPLE INPUT FCB'S IN USE SAVPUT: DS 1 ;BUFFER FOR NORMAL FIRST INSTRUCTION ;AT PUTCHR. USED WHILE OUTPUT IS ;DISABLED DICFL: DW 0 ;DICTIONARY FLAG FOR COM"PARE CURBYT: DW DICBUF-1 ;CURRENT BYTE OF DICBUF (FOR GETBIT) CURBIT: DB 0 ;CURRENT BIT OF BYTE (FOR GETBIT) ALPHA: DB 0 ;0 IF NOT WORKING IN ALPHABETICAL ;ORDER (DID FLAG SEEK). 1 IF IN ORDER LSTREC: DW 1 ;RECORD NUMBER OF LAST DICTIONARY ;RECORD READ LSTADR: DW TABTOP+4 ;POINTER TO TABLE WHERE FIRST 4 BYTES ;OF LAST RECORD READ ARE FOUND OLDPTR: DW 0 ;POINTER FOR PASS 1 OF INPUT FILE IS ;SAVED HERE DURING PASS 2 IF ALL OF ;FILE DID NOT FIT IN MEMORY P2OPTR: DW P2BUF+512 ;POINTER FOR PASS2 OF INPUT FILE IS ;SAVED HERE DURING PASS 1 IF ALL OF ;FILE DID NOT FIT IN MEMORY STOPED: DB 0 ;IS 1 IF ALL OF FILE DID NOT FIT IN ;MEMORY MLTPAS: DB 0 ;SAME AS STOPED BUT NEVER RESET ONCE ;SET DEFDRV: DB 0 ;RECEIVES DEFAULT DRIVE VALUE SPLDC: DW 0 ;STORES NUMBER OF WORDS IN SPELL.DIC FILUDC: DW 0 ;STORES NUMBER OF WORDS WRITTEN TO ;FILE.UDC TOTWRD: DW 0 ;RECORDS TOTAL NUMBER OF WORDS IN DOC UNQWRD: DW 0 ;NUMBER OF UNIQUE WORDS IN DOC MISWRD: DW 0 ;NUMBER OF MISSPELLED WORDS OFCB: DB 0,' $$$' ;FCB FOR OUTPUT FILE DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 P2FCB: DB 0,' ' ;FCB FOR PASS 2 OF INPUT FILE DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 FILDIC: DB 0,' UDC' ;FCB FILE FILE.UDC DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 FILADD: DB 0,' ADD' ;FCB FOR FILE.ADD DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 FILD$$: DB 0,' D$$' ;FCB FOR TEMPORARY FILE FILE.D$$ DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 SPLDIC: DB 0,'SPELL DIC' ;FCB FOR SPELL.DIC DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 DICFCB: DB 0,'DICT DIC' ;FCB FOR DICTIONARY FILE DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0 DICREC: DB 0,0,0 ;RECORD NUMBER FOR RANDOM READ ;(STILL PART OF FCB) STACK EQU $+50 ;STACK SRCWRD EQU STACK ;BUFFER FOR UNFIXED WORD WORD EQU SRCWRD+100 ;BUFFER FOR FIXED WORD DICWRD EQU WORD+41 ;BUFFER FOR WORDS READ FROM DICTIONARY FLAG EQU DICWRD+41 ;BUFFER FOR DESIRED DICTIONARY FLAG LASTC EQU FLAG+2 ;BUFFER FOR LAST CHARACTER READ DICBUF EQU LASTC+1 ;BUFFER FOR DICTIONARY RECORD INBUF EQU DICBUF+258 ;INPUT BUFFER FOR PASS 1 P2BUF EQU INBUF+513 ;INPUT BUFFER FOR PASS 2 OBUFF EQU P2BUF+513 ;OUTPUT BUFFER NEXT EQU OBUFF+513 ;ADDRESS OF NEXT FREE BYTE END START D$$' ;FCB FOR TEMPORARY FILE FILE.D$$ DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 SPLDIC: DB 0,'SPELL DIC' ;FCB FOR SPELL.DIC DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 DICFCB: DB 0,'DICT DIC' ;FCB FOR DICTIONARY FILE DB 0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0 DICREC: DB 0,0,0 ;RECORD NUMBER FOR RANDOM READ ;(STILL PART OF FCB) STACK EQU $+50 ;STACK SRCWRD EQU STACK ;BUFFER FOR UNFIXED WORD WORD EQU SRCWRD+100 ;BUFFER FOR FIXED WORD DICWRD G(C) 1982 Michael C. Adler. This program has been released into the public domain by the author. It may neither be sold for profit nor included in a sold software package without permission of the author.ABACABSCACCLACQUADDUADREAFFEAGILALBEALLOAMANAMPEANCIANNUANTIAPLOAPPRARCHARMPASCEASSIATHEAUDIAUTOAWOKBAGBANSBASSBEDABELABERRBIDDBIRDBLEABLUSBOOKBOUQBREABROABUFFBUSBCALICAPPCATACERTCHATCHOPCLAMCLOVCOLLCOMMCOMPCONCCONGCONSCONTCOORCOUNCRANCROWCURRDARIDECIDEFODENODESIDEVODIGRDISADISIDISSDOGMDRAPDURAEDIBELEVEMPHENGEENTRESCAEVIDEXECEXPLEXULFANCFENCFINGFLICFORAFORMFREEFUNNGAUGGINGOLFGRATGUERHANDHEHEREHITHHORRHURRILLEIMPEIMPRINCOINDIINEXINHEINSEINTAINTEINTRIRREJAZZJUTKNOWLAUGLEGILIGHLOBELUBRMAHOMANNMASTMEDIMETAMIGRMISCMODUMORTMULTNANONERVNOMINORMOARODIOOPPOOSCIOVERPAPARAPASSPEDEPERIPETTPILLPLAYPOLIPOSTPRECPRERPRISPROHPROSPUBLQUALRADIREABRECERECTREFEREIMREMIREPRRESORETIRHINROBURUPTSANDSCENSCUTSELFSERPSHEASHRISIMUSLABSMITSOLESPARSPLISTAGSTEASTORSTUDSUBPSUESUPESUSTSYNCTAPTELETESTTHINTILLTOPOTRANTREATRUCTWITUNATUNCOUNDIUNGRUNLOUNREUNSYUPSTVANTVERTVIRUWALNWEASWHITWITHWORTZEAL[[[[1>2Y =SPELL V2.0 -- December 22, 1982 (C) 1982 Michael C. Adler $:l>2 Can't find dictionary file DICT.DIC $!\ !\ !\ !\ >!\ (!:O >: ~_@22>\z bCan't find input file $« Directory full $ͭ >2>22:= 2>2= ] !\" *#" >(>)(; (*">22$\:  *<*[++R }o> o|gDM> *##^#V#!@w # y2*#" >*#" KK!" *":2= !" !" " #" :G:2x2' Total number of words in document: $ ʍ >(>)( ; (>= !~(= #:($[*}R*[R>2*"*"!\" !" !" " #" >2= *##"*+":G:2x2!>(%T]   >B2>A2>K2::\2l! \!eu#>$2e2f2g2\\:(     *<  *<*>  >!N > No subtotals available due to size of document.$ Number of unique words: $ Unique "misspelled" words: $ͫ ͆ 2(= O.y = ͫ = 2 !2ͫ 9 o 2w# ͫ 2w# ͫ G x 2w# ͫ 2w# ͫ ͆ 2w# 6*+"G_Aږ [Җ xx'( : x.x*R ~#"!>wT]\  :6!" !@! '(_A8 w>)# 6! + q>((>'*w#"R(!T]  ^ !"ͭ Q    Disk full -- aborting $!6#  K[ T]DMbkB>, >, *##"RDMT]++*s#r!@:G~#S-R[R8>> }~o> o|g ^#V!@ ># !>w>>!>"i :2=  !" !" >(>)(*#"͹!@~(= #8 !" !"> = > = ͫ ( 8 ͫ (G!@ͫ w#>w͹ *#"!@~#(= > = > = !>(%T]>   ͂!:G> (#z>$(#p> #i #a>2bc> n>c> #~+: ~##@2b~ (.(  >(#> (#+͂Æ >!"i >2= ͭ !"*#"##"*+"!"ͭ > 2c *DM!>>U2G>D2H>C2Iq>D2G>$2H2I Words written to dictionary $!"!"b<2 :b 9>2bb!b" 8 >(>)(*#"͹* :b :2b:b@_:c!n>$2n - B $ not found$M *DM!bq Words read from dictionary $~ :w~@_:T] >$w  `i<: $*@w# w">!".>2>2!N!@ ~E;HtYʹGNDʆTRS>>S+>V+>I6E#6.++>E#6.>S+>T+>Y#6.>+>E+>I6Y#6.>S+>L6.>S+>N+>I6E#6.++>E#6.> S+>O>>E+>E>Y#6.>+>I6E#6.>++>T+>A+>C+>I6Y#6.>S+>E#6.++>E~Y+ ##6.#6.+>I+ #6Y#6.>S+>Sʓ>SÓ>@S+>Sʦ>'~Y + ##6.+~RʜNʈGʒ>E+o#6.>I+ #6Y#6.SSS >@S+>E+>N+~Y+ ##6.+>I+ #6Y#6. S6.>~!AEIOU~!*SXZH:n*@n BK !B(C*R";K*@ u RC"ͼ !B >>GyO*+B :*:(:*G>GC!C!!">2i!@i # ) *:( :( 'y>!iO >(ʘ@[ >'w#d>wG!>ox>GG>g>(G":*V r#"V<B(2r!>@ # >># @'ggdg gg Ri y0_ɗ  $$$ UDC ADD D$$SPELL DICDICT DIC D$$SPELL DICDICT DIC D$$SPELL DIC#~ Ý ͦ :.,;?!0?:ɷ @[A?a{U'N#C!!">2i!@i # ) *:( :( 'y>!iO >(ʘ@[ >'w#d>wG!>ox>GG>g>(G":*V r#" This is the release date of the disk. CHEKLIST$$$$ Fog Library Disk FOG-CPM.073 Copyright (1986) by Fog International Computer Users Group to the extent not copyrighted by the original author for the exclusive use and enjoyment of its members. Any reproduction or distribution for profit or personal gain is strictly forbidden. For information, contact FOG, P. O. Box 3474, Daly City, CA. 94015-0474. as part of the description of a file indicates that the program is distributed on a "try first, pay if you like it" basis. If you find the program(s) meet your need, please refer to the author's documentation for information on becoming a registered user. Only by registering and paying for the programs you like and use will the authors of such programs continue development. Often, more complete documentation, additional modules, and new releases are available only to registered users. Spelling checker for most Z80 computers. Filename Description -06-00 .86 This is the release date of the disk. -CPM073 .DOC This is the description of the disk contents. SPELLM21.COM 19A9 6K ver. 2.1 [Spelling checker 1 of 8] Two versions of the spelling checker are included here. Works with WordStar or ASCII files. Also included here is program which generates the dictionary file. MAC source for both SPELL and DICCRE are included here. SPELLM20.COM 8659 6K ver. 2.1 [Spelling checker 2 of 8] SPELLM20.DOC 6113 19K ver. 2.1 [Spelling checker 3 of 8] SPELLM20.DES DF98 1K ver. 2.1 [Spelling checker 4 of 8] DICT .DIC 4C77 56K ver. 2.1 [Spelling checker 5 of 8] SPELLM20.MAC 85FA 55K ver. 2.1 [Spelling checker 6 of 8] DICCRE10.COM 40EF 2K ver. 2.1 [Spelling checker 7 of 8] DICCRE10.MAC 3822 16K ver. 2.1 [Spelling checker 8 of 8] , and new releases are available only to registered users. Spelling checker for most Z80 computers. Filename Description -06-00 .86 This is the release date of the d%&'