IMD 1.16: 7/09/2007 11:59:05 SEATTLE COMPUTER MS-DOS Operating System TARBELL DD Version 1.25 April 1982 Serial #MS - 435 Copyright 1979 - 1982 by Seattle Computer Products, Inc.  3؎мPx v X9z{Wx$ t x|r_$uǀ@x |ux3؎м@  @! #%`')+-/1 3@o79;=?A C@EKMOQ S@U`WY[]_Gc@gikoq s@uw{} @` @ ` @ `  @ ` ` ǀ ɠ @ ` ׀ ٠  @`@`!Aa   !Aa!!#A%a')+-/1!3A5a79;=?/CAEaIKMOQ!SAUa@  @! #%`')+-/1 3@o79;=?A C@EKMOQ S@U`WY[]_Gc@gikoq s@uw{} @` @ ` @ `  @ ` ` ǀ ɠ @ ` ׀ ٠  @`@`!Aa   !Aa!!#A%a')+-/1!3A5a79;=?/CAEaIKMOQ!SAUaIO SYS,(MSDOS SYSnc COMMAND COMr|RCHKDSK COMCc DEBUG COMeP$sEDLIN COM&[Q0 FILCOM COM`Q5 INSTALL COM#F;SYS COMyGTRANS COMI ASM COMxPHEX2BIN COMP\aRDCPM COM(bMAKRDCPMCOMe/FORMAT COM}(fA FORMAT OBJcmEXE2BIN EXE[cvCPMTAB ASMp(y,IO ASM5'|JINIT ASM}(ZMON ASMFUNKEY EXEJ%< IO HEXF(A FUNKEY DOCh&` !O BIN,(G +&6I^#3ռFdNf++dždž!یȎغ%! u/3FH@3'!tێÎӼ\3P!SP˺ !88wN7wN7 Error in loading Command Interpreter $COMMAND COM j$ QRCZYC'53ɋѰ   Ò DPXPV $<t<t<u'@.6f].;6dt ..6f^Xϰ @V.6d.;6ft1. ^˚@tV.6d.6d^.fh.dhFrhhhP$tXP$tX$tP$tX˴.:uPx$ Xt 0.80..|${t .4. .:t y{nr"ێrXr (r..t@ r@ r@..SQ@Y[Ën.|t..y:tǷ,u,:&tÊ&"$ttá@;F sO3ҋ^ڊf;tS}[.VË>G;~ s> ;r RXZ.r.$<u ?u.r x&'v t;^ wJ s?2|rw rlrg xc  "R[ÃuZ$С"^;sK "FNtuH3&]v3[á  t +F+s3ۣ&3v*(&]&U t+s3&]s܇BAJË ;u : tJ2 tRU.]Z u3HRZ . >&666 2vsá@;wTs t':Fvsv>ì< u < u 3 uG 6>u >&e>tui<6 u>PtPQWP_YR<u 33wc x&E&]+"$ru t;s' uʼn>&t>*tr^ *WPRSl_Xr%>t+؋N6sY[ns볋6+3&L;t" t+3s@ȋ&E&EIAá( t&+r 6@ttt <t*< <t&>떋*s>Q&E&U x?&]"$IvNP&E&Uv t@X|+t QXrȋBItr>&t* t@% *r WP Y[ء( t& +"$ʋ>&;E&Mr&E&UN t=-vNr݋>"&E$&E3"1D3&] tRSF*‹ыv,Ȁ;sIC;tK+Rf6ƣXZ+[.9r96r PF:Xt+*̈&QNJJ V YË=uE U ؊ֶ4RQSËC;^ ~)=)[\X+ZBŠVB+w3bt H~ɓYtڋu⳺kD[YZ9 uˋ>&]3#tD=rsur5 x:&E&m>:u  2&E&]>3 3 rыmE xR_rك!&M u33 xFFDP Zt@uBª@s&%..ðòr^FV JN6\TLL 6lL2 t. .QY.E!U#}@su$.:s.ÌȎ t܊:v8 t݊J..Vk<t.:t2<t~ <tz< t:< tZ<t[:sc u:sFǰs+ W_eY_uƌŌێÎݾð > 9냰\/^.^ tN&< s< tC u tNMWOΰ Sv &} t .**ˀ[_tO &*0:t:t w:tF6*tItWG_u*]@S_W^`[~Gk< s< tP^X @<t@<t<<t8Ê< rq<t.PX @.t @Ú@t<u@@<t)<t,<uްo..&X[YZ^_]#I..< t <t%< u. Qȵ kY.k.cxt PEXutÊ+;vIA&f&F&F &+F &N@@&vH3 INIT TABLE BAD $~t FFу>$th~t.$P<>$uP<6$)P<>$tsP^P<Ls 6r F7 ]UFs(6<F<@<=r PP] U6<F <@<=r PP] UPF PPNNFPF PPNNF>Tu^_2F^_2NÊ7:7v^7^:7sF:Fu^G^;GwɊ^㋇:F^㋏:N FFFF^GFF%FF^GFF%F9Fu|F;FwF;FulF;Fa^GFvDFD8Gw^GF~v,^ F^ N:wF:FrFFN΋^G^:G]UQ^ GF<t^㋇.^ G6] UVF8Iu(FP~tBFFPI0PAF>&u ^G& &^GNs_$PPPF^G $ ˆG Ss*^G ,^G 606^G6^GFF6HN;vˀ2tP0PAF^ tgBB^2vD FÈ^F;6s=Q0PAF^G ;Fu tBB^2FFtXJF6HN;wˀ2uJBB2FuՠQs6FJBBF^2:Bw%ù:854u Ұ~@FJ(BF6HN;vˊ2P0PAF^ uvD:^GF<t;Àptp2:v F^pF:Du :FvF^G PFX$<uFF:4v47<tƀzuF:FvBF^LJ.^G 7vD5FtNs^2FÊNꈏ4BFF:Fw À7uvPFؠNrFK8FwFƊNJFu^㋇5FLJ5:Bw3À7t"7rPp7ƈZuĊ^F5F:Bv@P7u@Y"s֠:BwRFK8FwFrPFuFF:Bw À7uvPFנQsH^㋇58:FFDpF-^㉇:^LJ.BEF6H]Ȏ؎м !-t ! !$_u9* ! !9 ! uW!<%t < u& )!<tt  \ )! t!\!uux tkб$-$-P3؋ וA<PZրt"̀0*Ѵ!'' ]]  >m t \!u3Ҵ!\!  u3}@j \'!3&<t !3&\6 yu3}.uG? ݀t.!]? tHЊ&&4" Ģ%" ¢lfMtJu03\6 6 t#u]t63. !' '3#$t uM_ t+#!>%t$$ttۋ6 #tV'^ %@<+u0&F)!< t u@SWH $_[} 3àuP!X\! $z $%"% ù #u !ú! u3@!+ʺ'!ˀ>%tы>uA+ы;rN벡 t>Ft;u.3>"u !ð u!>$u!I u>3@3ɇ>$u,3!(! t!!/ !ú'!W<u -ìtN< t <=t<,t< ú ! !Ȏ ˾< t -/=W !*!3ٹ \ʲdĒ-h-/tr7:t:u.r)l< t drĴȬ< u +! u^H !볾,< t ::t !,!:3::@thr33Ҭ< t":u&.`r< t:uPrԬ< u-! t3{ !뵴 ! !:< tr:t:uN:ur r Ê,0r< rFÊŷ /% :u. 00<0u*ǷĒP!XÌ !'! uy($+Љ$Ӌ;wQ0.;w8>,t+ȋً3Ҵ!$'!)$tuۺ  8 !>&t"'! ű> "Ŏ&-&uޡ.Ћ&0.6..4+!.3ɋ.-t.Bad command or file name $Missing file name$Duplicate file name or File not found$Error in EXE file$Insufficient disk space $File creation error $File cannot be copied onto itself $Content of destination lost before copy $ File(s) copied$ File(s)$Program too big to fit in memory$Invalid drive specification$Strike a key when ready . . . $Illegal switch $SunMonTueWedThuFriSat Invalid date$Current date is $ Enter new date: $ Invalid time$Current time is $ Enter new time: $Are you sure (Y/N)? $DIRRENAMERENERASEDELTYPEREMCOPYPPAUSE DATE TIME  >] u t6 ! R\д! tۀ?rʿ3ȣ.״!!uA,״!!uB"3v;6w( t懼 u?@tasv;>wUPFF,H! t 2P !. ! !XdP ! !XQ~t*P" :tGX XFVIAr;} 6 t@;tC3pv~Ճ"! tPF(!! !XP !Y,A;w3Futu 3@EEF t !>t63P++=t6+6+63*>t 8+`}+ºp ! &9t&GV &-u * -Z !PZրt"̀0*р>-t0|ʹ!''QH,6 !Yì!???????????badtrack and $Invalid drive specification$Invalid parameter$Allocation error for file $Directory error-file : $Files cross-linked: $File size error for file $Diskette not initialized $ bytes disk space freed $ bytes total disk space $ bytes in $ user files $ bytes in bad sectors $ hidden files $ bytes available on disk $ bytes total memory $ bytes free $valid drive specification$Invalid parameter$Allocation error for file $D DEBUG version 1.08 $N !"%!#!ʁ®&!¿^ڎº!=s-V&+ر&\)!z>] t_dfȎ؎мN>3t,Ar<w .Ȏ؎мN !븴 p!rt>eHEu>gX^!j'j(.dtl?ttPڹRRZXU.>j(t%&s/- u2U0YG>j(u] !@ádSu 3It3ۉ}>eEXu >gEuPR\>j(tj! u\jZX׉>R6P ځPRش!\ uϊ&j!+<ZXtы uú?!tj6P~3 ! tR !ZX|r !\!v>j't!\!j tKx'!%}J‹+ u$ Ʊȑ^+ar ;w>u6P>R3r RZ0}!>t"'\! u͋>Ŏ&-uޡţbVţdf`>j(u!\! tr3h|j\3.<:uZȵ9QLA>;vρu !<t <t u-.RPs. 4QPR^YIAtNO&G2Ћ.dn;u66PU6 tN6X=[C6 J $çW&*Ę6q fj$$&û< 2,u)(W_: Š$'@'< uû,Њ+ y-ܪH,>u$$, ,>t3[G<u>tT<vS<tO<v<u XBPs+s t1\DI t <t $ڰ]ð+PBXZSI$,22,[3,2$8LøL,X3$ Q>t,$7DXû">t B,tW,CLИ؋û! t <r221<r<stL,hALCLDLBLAHCHDHBHAXCXDXBXSPBPSIDIESCSSSDS          f W  f W  f W  f WEEEEEEEEEEEEEEEEMrJr"rr+r:r$rrOrGr=r@r2r/r4r-r fXWWWWWWWQW{WWWW  WWbWgWWWEE6W[UEEWW00^^EWurprlr'r  77Cr  ^l El l WWMMWWWWWWffDADADSUSBXOOANCMMOAAAAAAAACALCBCLCLCLCMCMPSCMPSCWDADADEDIESHLIDIIMUINININININTIREJJNJJBJCXJJJGJJLJMJNJPJPJMPJNJNJJLAHLDLELELOCLODSLODSLOOLOOPLOOPNMOVSMOVSMUNENONOOUTOUTPOPOPPUSPUSHRCRCREPREPNREROROSAHSASCASSCASSHSHSTSTSTSTOSSTOSTESWAIXCHXLASE?? 77`db^AXBXCXDXSPBPSIDIDSESSSCSIPPCOVDNEINGZRACPECYNVUPDIPLNZNAPONC Program terminated normally $Invalid drive specification $File not found $No room in disk directory $Insufficient space on disk $Disk$Write protect$ error reading drive A $readwritInsufficient memory $^ Error  Error in EXE/HEX file $EXE/HEX file cannot be written $Writing $ bytes $Z' P PLOOPNMOVS EDLIN version 1.02 $ * L P !X u>] t۾etB\!) t !\  !$ ! t F # 3}$ & @j M !ID ) u&M @ ȋсM B \'! >F H  L > M < , ) uL #%!*H ! . ( J Y, C<,uFNI. 3QWQY+Ϟ_) uF >, u;B s!D +t~\'!) Qu) 3ҋ, u;B v >B +ϻ8Et OD GGJY+χ>F )>};u) ú !) u* u, u@ >F +vځM v3һkCCϺM +t! (! uM >> F +AO>F < ô!d ! < >> ;tw t M ;t֋F +ϰ B;uS o:g*[;< t Y3й''Š$t0*, u < w]u>. G+wF +t1.< QhY< s< t< t < t P^X @< COuKÿ B<  tB< uOu68 +0 2 сwp6 RZ4 68 +IOVJ 2 D^60 ʃ7u*~>4 OJ 0 2 I4 AJ): s: Bu  Jú ;lu36 68 :t+>4 : u>4 >8 : 6 tͺ !á8 > 6 < ( t !!PX< t4 >8 6 . +4 +0 rA: : >4 uыߋ0 I :ʋu: ω>4 >8 +ϰ 6 BtJ6 8 23ɬ<t< tA, u< "uSW. uC_+v < >> 3Ug, u< C< 6> u;6F t0 6>  ! ` ?0 >> ;t.VWQF +;D sF +;wAY_^ú !Z#%(!, u< RF +A>D Gډ>> < .F [ ! <t%LB;s󤤰 CȌȎ؎мL .F >> FD +O>F !!$_Zu ZlurysoTP؋q󤺃n >Tu< tC.[tqÊ$ u@ C s?) >[tð   ջТ6<0| <9,0 Zsȝ    S,>Ts17~&Bu'>TsES'uS[UÜ>Tu 3X u>Tu:U RI X6V.SàT<s1TPUUtS[S[_ù  nuul tcoun t usu eruDIF !#$%&()+-NUL :.>(Pڋ$؋XS2 2[ð Ê uȈCCC  uBCuð t@B:BÊ uu';<R!u[ڴ!u[ZYXßPQRS>ܟPQRS: uֻSR2C 2[ZÈCußPRP u R!ZXßPR F AQY[ tNJ$t$NjTSPؠ u< u.QSȵQ!YZR(! ZYtSR><uC>?Z@PRQ!!YZtڇXu29XSR: u( uC:AZ< t;<uC:;Z[<t ú<QRSȵQ!YZ'![uY28yË@<@t s  * t# t <;uj <;u` <t <uà t <;t <t tl <;tb :tRs t * :M t * 2 t6 <;u0 $* t <;u <;u <us @t * `נ <uû 1t * SG ڋs ~ڋ +[S] ڋ fڋ +[XĞ XĞ Z PĠ PĻBSRRQSCCS ط[ڊKKJKuuYZS!+r[ZËCB7KËCJ7K !D u#20NQ2u0:uYÊQY$0<:s 202u0u ----- ----- ADDRESS  l\! ul;+ʴ!'!<u\! uL$Q!Y uء& t@;r%ȴ(!*!  ! File not found$IO.SYS not present on destination$New I/O system too big$IO SYSIO SYS8 :0402370000000000C3 :0000000000 020535953B2 :1A020B0000000000FPV !X"l,@ ! !2>] u\tҴ!,8\tô-! u_! u#!Bt-r!_ b\f4!! u\!ᑻ-Eu(_=w #-U_L ! '!O0GG,GG.ô! u֋G3HPG0H3Z;ô!Ӵ!3G(G*@G(O0!G,GG.G! Sys version 1.6 $Invalid drive specification$Invalid parameter$Insert system disk in drive A and strike any key when ready $No room for system on destination disk$Incompatible system size$System transferred$IO SYSMSDOS SYS???????????>\tҴ!,8\tô-! u_! u#!Bt-r  Ja h2Cu|\׋ޢASM\ t ts   2W   W ' #J <;u Y;T<t J< u빵=u0 ( b< t<t<;t< t <:t<,t< $'@'SRQP$ C wu   uXYZ[SRQ uw\<t<t wC  `t Xu:w KttsC;u<:u 2"t<(u [<)u ȟ] <'t#s8CTu< tCF<;t<'u<0r<:s<@r$_W [uˀ>X tĀ>X tB,àX uW ,0r<r2u20oZUHX#Popqy$ O P Q R S Z [ D D N I + AL̢  P PIҶ PD Cƶ E& JNڨ" A ɶ    Ө y ɶ XضS Qը NDIƶ ALԶ N&  ƨ @ @ Q DIҶ DD E  U RǨ OЪ USȥ EԈ L R LC RC ̨ Ҩ Lè Rè E$ ETɶ ETζ SԨ U B L R R̨ Cƶ E  O SILOD DISTOLAHFXCHG AH,ALPUSH AXXCHG AH,APOP AXXCHG AH,ALSAHDOWUPMOV SI,BXMOV DI,DXREPMOVBMOV DX,DIMOV BX,SUPMOV DI,BXREPNZSCABMOV BX,DLAHFADRCR SISAHFRCL SLAHSAHDEC CH ; *** WARNING: DJNZ does not affect flags - DEC does.JN ; *** WARNING: Parity flag not always same as Z80MOV DI,DXMOV DL,CLXOR DH,DH MOV DX,D ; *** WARNING: Flags not same as Z80 *** ERROR: Cannot determine bit numberLAHFOSAHLAHFANSAHRCR AHTESRCL APOP SIXCHG SPUSH SXCHG BX,[HL]XCHG DX,[DE]XCHG CX,[BCLAHFXCHG AX,BPSAHMOV A MOV SI,[IX] MOV DI,[IY] XCHG AX,S INBOUTBXCHJMJMPRCRCROROSASASHSTIREHLCMNOMOCMSBCALSIDIPEPOSNSNZZNCCAXSPBXDXCXBLBHDLDHCLCHALIXIYABCDEHLBDHSACNZNPMPPIICELPFCZOEYX Z80 to 8086 Translator version 2.21 $ File creation error $ File not found $ Translation complete $ Out of disk space $ *** Opcode Error L0000:  89D0 :010D060000EC :0000000000 163650D51 :1A0CE2000A240D0A2A2A2A204F70636F6465204572726F72A00D8A4C3030AE :050CFC003t Seattle Computer Products 8086 Assembler Version 2.42 Copyright 1979-1982 by Seattle Computer Products, Inc. $* !m3!e t\.!)uñu -!:ű#жu27%t&uڊ< 't7! u-! :u:u:t[P!X.!à-!< t< u .!<$t.! <{s uڋ wϠ.!<)uƶ2û ?sttCS!tgb]>S!uË>S!uGtfxtPY<;tR< tN<uo@u6 uaz]RP<,uRx<uIX[4$ t u94u1 PNXuP%=Xu$ Q<uX utu$ |ڊ$ ! x $ $8 9.+ <u<$t L B<t t2 )YtV!5!V!C5! >5!>5!QRS Q@RS u  t t  t%ŀtoj[ZYÊÊQYYŀtLQRS@$<t $t:t-:t+:Ʊu,<t: t ڊ t' :tPY<t, ±u idP<u͋ ñu X tCtt't6 $t(ڋ ډ ڰڋ ñ"t'C7É ;612& uڋ Ø;ÇuȵȵQ<t<tY<t$t!t$ $ x$8 / j$8 _ȵQ u<t<ugPoX$ 4 /ȵQU.!<,uRH, u2ZYQ`P$P Xu7-P <t3<t<t<uX$8 Š. t 'u$V.!<,tXZ!%u PQRaZYX>Z!_PtXu뭀P!@&P!~ Z!!SCڋ t[C(4!s ,آ4!Cu CCSG G>4!t U4![ uâG!SQPRrZ:!<u |uXoY @![ tJ!P Xvwt5 xCЀÂ>~ ZtPB!@!B!BD!!A! @!2XE!WOD!<u>>!egD!E! \!X!!+Ϻ~ (! u%Ë>X!>X!ËϺ!+t! (! t% |$ 0<:rdd nd"dc aa 7as ?amcadclignk mp :lc ld li mc bw wd allmpb mpw mpsb mpsw bwms9 i ec Iiv;0aa 'as /own i qu9 scndXndifHlt alt n f9 nc Anb nw ntmul;(div;8ret nto pztetl|brawgopsxcrmpnzuneunl}ge}nbsaesncsng~le~navbevpeznp{po{noqnsympscxznge|naernbewnleea ds es oopodb odw ahf ock oopeoopzodsb odsw oopneoopnzov1 ul; ovb ovw ovsb ovsw ot eg op r ut rg9 utb utw oput9 ush opf ushf et=ep olkorkclkcrkepz epe epnz epne ub *bb bc tc td ti hlk hrk(alk ark8eg&cab  caw tob tow ahf casb casw tosb tosw estp ait or2chg lat xmdddbl st0ohomhhompleiiv0ivivr8ecstisnre adlsu stsmudi0sub(div8cocomncstni dddpdl2dl2dlgdlndc(den uuloretaatandinsto tuubububrqrcalav0tc8ten0ts8saicAatracl2l2xp  2       :^q                    ;         +  :   JOZ   gr            6  =           B   IO            bpz    8FBK \j   Register not allowed in immediate valuIndex or base register must be BP, BX, SI, or DOnly one base register (BX, BP) alloweOnly one index register (SI or DI) alloweOnly addition allowed on register or undefined labeOnly one undefined label per expression alloweIllegal digit in hexadecimal numbeIllegal digit in decimal numbe Illegal character in label or opcod Label defined twic Opcode not recognizeInvalid operan"," and second operand expecteRegister mismatcImmediate operand not allowe"]" expecteTwo memory operands not alloweDestination must not be immediate valuBoth operands must not be registerOperand must be segment registeFirst operand must be registeUndefined label not alloweValue out of rang Missing or illegal operand size fla!Must have label on same lin#Zero-length string illega$ENDIF without I%One-character strings onl&Illegal expressio'End of string not foundUndefined labeeValue out of range (forward***** ERROR: File creation error $ Insufficient memory $ File not found $ Disk full $ Bad disk specifier $ Error Count Symbol Table ASMendififre  HEX PRN02020202020204845580000000014 :15209E000000000000002020Those of you who have function keys on your terminal's keyboard can customize these keys to work directly with the editing template as described in section 2.3 of the MS-DOS user's manual by running the MS-DOS utility file found in your System disk's directory under the name FUNKEY.EXE. A maximum of up to eleven function keys may be utilized. These keys can be programmed to work on the editing template for the following functions: 1. Copy one character from template 2. Skip over one character in te= uam< t1<+t <-ur3 جstۉnд&!&3\! uQ|j!\C<:uQȵ}HC8;>s/;vɺ ! Cu !<t4<t3  ,0r< r,r<sd !eCOgM\! u%3}@j3!ʹ(\!! lHEXError in HEX file--conversion aborted$File not found$Address out of range--conversion aborted$Disk directory full$BA0061626F72746564244469736B20646972656374д!+ر%>] t[\u52\m= u] l u!@H:+t2hx\'sSV  m m u m ?xlr C 374'+: u    ls ð R!ZË VWS[_^)t :~vu)FE =r5D"FL)Ft$F tN!3^,PSW>_[Z, W$t }?t_ f"Į_tIKxr͋@빋3ҋ^F u!^îйߠ+U%X]sj^ HARD DISK ERROR on CP/M drive $Source and destination drives must not be the same$Drive not available for CP/M reading$Insufficient disk space$No room in directory to create file$Source file name missing$Source file not found$File transfer complete$DIR p""?3     R?^     \#H!#! uk\! u_3}DF@j1#'! u?ḤD\'H! <u(#! u!! ! RDCPM file bad$Insufficient disk space to write file$RDCPM.COM not found on specified drive$Specified file not found$Specified file too long$RDCPM COMDF@j1#'! u?ḤD\'H! <u(#! u! u'u Sd[i t6>u3ҡu 6s ]  ; &i u &s { s}  6q t@ȡ +] 6q KZo SRm &7s &7Z[À>uo >m &Ei t )& !sw &s q w y 5V>u t 6u 3OC>{ u>} t6{ >} g'w y +{ } +u $S3؋ וNI( ZtdPZրt"̀0*Ѵ!''r 36s t@&s u ô !r/Tø !ú> !$m eE !w s q sBw 3ʋ>m >m &EC < !+=}ࣸ! tá  ! u_ !  '+_ !<u4 _ !'+!<u _ !_ !3G(G*Ӵ!G(O,!GG!Ë_ ! ! u37 9 $ +* , '!<u^   ! uA!7 9 $  (! u* ,  ! uInsert new diskette for  drive x: $and strike any key when ready$System transferred $Format another (Y/N)?$ $Invalid drive specification$Invalid parameter$Insert DOS disk in drive x: $Format failure $Fatal format error $Disk unsuitable for system disk $Track 0 bad - disk unusable $Insufficient memory for system transfer $ bytes total disk space $ bytes used by system $ bytes in bad sectors $ bytes available on disk $IO SYSMSDOS SYSCOMMAND COM???????????i t \|${t 6i 麲 'D \ D ] i tATC ɋT9u2ȋ\CCA|1t 2Ҋ‹\A|*Ƌ\CAs t ƀs @|:TsXt r uit1D D   d$Ϣ D) D r D u  D a &Z; rô !+ôi tĴ à i t R,TuZV t 謈Cu^ð(ËA{x|rՋ $tx |ux( NP(\ f MP: B PMr   PN 2N N 6NNNN( PN 2N N PNNNNNNN   0Read Error - can't determine density $Seek Error - disk not ready $Write Error - not ready, write protect, write fault, or lost data $Error writing boot sector $BCDS!=3؎мx  v X0z{Wx|r_$uǁǀ@x |ux3؎мx v X@ô<v,@z |{Wx|r_$uǀ@x |uxA;CODEDh O BADSECTOR DISKFORMATDONEFATID FREESPACEINIT STARTSECTOR SWITCHLISTu'u S[t6>u3ҡ6;&&s6 t@ȡ4) ) = 66))-25): >EINi V]`eu is mp t y|)Āi Ću ċ ďs ē{ ę} Ĝ Ġq b+6KSR&7s &7Z[À>u>&Et  !s&>t 63>u>t6>  q  o 'm OUX\o bm li svy)|)6Ďw Ēs Ėq Ĝw Ġy ħ5Įu ĵu ĺO{ } { } Je++S3؋ וZtPZրt"̀0*Ѵ!''r36 t@&ô !r/ø !ú !$)6)b !AÊ´! tú... .>eE !sB3ʋ>>&E!+=}! tá! u  ) )=6 .W38m =o AJm \cw gs lq uw ~m ćm đĖC ě< Ģķļ  4O!'+!<u4!'+!<u !9  $+/37A EKOVZ^blptw|ăćĎđĔėĚ ĠģĦĩ!3G(G*Ӵ!G(O,!GG!Ë!! u3+'!<u^! uA!(! u`  (.:>BFM Y7 _9 e$ kor* ux, {ćĎ )bėĜ ğ īij7 Ĺ9 Ŀ$  * , ! uInsert new diskette for drive x: $and strike any key when ready$System transferred $Format another (Y/N)?$ $Invalid drive specification$Invalid parameter$Insert DOS disk in drive x: $Format failure $Fatal format error $Disk unsuitab\ wle for system disk $Track 0 bad - disk unusable $Insufficient memory for system transfer $ bytes total disk space $ bytes used by system $ bytes in bad sectors $ bytes available on disk $IO SYSdMSDOS SYSi COMMAND COM2< ???????????Ii ϐ DRIVE> SWITCHMAPi 4 MZ -"+3P¸ )!Vڹ ^)!ڴ! t !˺}!'!>MZuܡ=s%ࣷ+Ёs tЉ5;w}  u t=u)>u3ۃ>tET ! !z !Dt,0r< r $_,< r<s ߀| u3Ҵ!'! t>t2!'! t>Î&-u۴! u-3Ҵ!(! u!˺<(|File not found$Insufficient memory$File creation error$Insufficient disk space$Fix-ups needed - base segment (hex): $ $File cannot be converted$ EXE  BIN;Source code for drive tables used by RDCPM ORG 0 PUT 100H DW END ;Address of first free byte ;Table of addresses of the parameter block for each of 16 drives. ;Note that 16 entries are ALWAYS required, with unused drives 0. DW IBM,IBM,0,0 DW 0,0,0,0 DW 0,0,0,0 DW 0,0,0,0 ;Below is the definition for standard single-density 8" drives IBM: DW 26 ;Sectors per track DB 3 ;Block shift DB 7 ;Block mask DB 0 ;Extent mask DW 242 ;Disk size - 1 DW 63 ;Directory entries - 1 DS 4 ;Not used DW 2 ;Tracks to skip DW MOD6 ;Modulo-6 sector translate table MOD6: DB 0,6,12,18,24 DB 4,10,16,22  DB 2,8,14,20 DB 1,7,13,19,25 DB 5,11,17,23 DB 3,9,15,21 ;This is the table for Cromemco 5" drives. SMALL: DW 18 ;Sectors per track DB 3 ;Block shift DB 7 ;Block mask DB 0 ;Extent mask DW 82 ;Disk size - 1 DW 63 ;Directory entries - 1 DS 4 ;Not used DW 3 ;Tracks to skip DW MOD5 ;Modulo-5 sector translate table MOD5: DB 0,5,10,15 DB 2,7,12,17 DB 4,9,14 DB 1,6,11,16 DB 3,8,13 END: : ; ; I/O System for 86-DOS version 1.20 and later. Revised 7-28-82. ; ; Assumes a CPU Support card at F0 hex for character I/O, ; with disk drivers for SCP, Tarbell, or Cromemco controllers. ; ; Select whether console input is interrupt-driven or pol led. INTINP: EQU 1 ; ; Select whether the auxiliary port is the Support Card parallel port ; or on channel 1 of a Multiport Serial card addressed at 10H. PARALLELAUX: EQU 0 SERIALAUX: EQU 1 ; ; Select whether the printer is connected to the Support card parallel ; output port (standard) or channel 0 of a Multiport Serial card ; addressed at 10H. PARALLELPRN: EQU 0 CENTRONICSPRN: EQU 0 SERIALPRN: EQU 1 ; ; If the Multiport Serial was chosen for either the auxiliary or the ; printer, select the baud rate here. Refer to Multiport Serial manual ; page 11 to pick the correct value for a given baud rate. PRNBAUD:EQU 5 ; 300 baud AUXBAUD:EQU 0EH ; 9600 baud ; ; Select disk controller here. SCP: EQU 0 TARBELLSD: EQU 0 TARBELLDD: EQU 1 CROMEMCO4FDC: EQU 0 CROMEMCO16FDC: EQU 0 ; ; Select if you want a special conversion version which can read/write ; both the new Microsoft format and the old SCP format. ; For a two drive system, drives A and B are the new Microsoft format, ; and drives C and D are the old SCP format (where C is the same physical ; drive as A, and D is the same drive as B). CONVERT has no effect ; on 5.25-inch drives. CONVERT:EQU 0 ; ; Select disk configuration: LARGE: EQU 1 ; Large drives. COMBIN: EQU 0 ; Two 8-inch and one 5.25-inch. SMALL: EQU 0 ; Three 5.25-inch drives. CUSTOM: EQU 0 ; User defined. ; ; If 8-inch drives are PerSci, select FASTSEEK here: ; (Fastseek with Tarbell controllers doesn't work yet). FASTSEEK: EQU 0 ; ; For double-density controllers, select double-sided operation of ; 8-inch disks in double-density mode. LARGEDS: EQU 1 ; ; For double-density controllers, select double-sided operation of ; 5.25-inch disks in double-density mode. SMALLDS: EQU 0 ; ; Use table below to select head step speed. Step times for 5" drives ; are double that shown in the table. Times for Fast Seek mode (using ; PerSci drives) is very small - 200-400 microseconds. ; ; Step value 1771 1793 ; ; 0 6ms 3ms ; 1 6ms 6ms ; 2  10ms 10ms ; 3 20ms 15ms ; STPSPD: EQU 0 ; ; ****** End of selections ******************************************** ;  BIOSSEG:EQU 40H ; I/O system segment. BIOSLEN:EQU 2048 ; Maximum length of I/O system. DOSLEN: EQU 8192 ; Maximum length of MS-DOS. QSIZE: EQU 80 ; Input queue size. BASE: EQU 0F0H ; CPU Support card base port number. SIOBASE:EQU 10H ; Base port number of Multiport Serial card. STAT: EQU BASE+7 ; Serial I/O status port. DATA: EQU BASE+6 ; Serial I/O data port. DAV: EQU 2 ; Data available bit. TBMT: EQU 1 ; Transmitter buffer empty bit. STCDATA:EQU BASE+4 ; Ports for 9513 Timer chip. STCCOM: EQU BASE+5 IF SERIALAUX AUXSTAT:EQU SIOBASE+3 AUXDATA:EQU SIOBASE+2 ENDIF IF PARALLELAUX AUXSTAT:EQU BASE+13 AUXDATA:EQU BASE+12 ENDIF IF SERIALPRN PRNSTAT:EQU SIOBASE+1 PRNDATA:EQU SIOBASE+0 ENDIF IF PARALLELPRN+CENTRONICSPRN PRNSTAT:EQU BASE+13 PRNDATA:EQU BASE+12 ENDIF IF PARALLELPRN+SERIALPRN PRNTBMT:EQU TBMT ENDIF IF CENTRONICSPRN PRNTBMT:EQU 04H ENDIF ORG 0 PUT 100H JMP INIT JSTAT: JMP STATUS JMP INP JOUT: JMP OUTP JMP PRINT JMP AUXIN JMP AUXOUT JREAD: JMP READ JWRITE: JMP WRITE JCHG: JMP DSKCHG JMP SETDATE JMP SETTIME JMP GETTIME JFLUSH: JMP FL USH JMP MAPDEV MAPDEV: RET L INIT: XOR BP,BP ; Set up stack just below I/O system. MOV SS,BP MOV SP,BIOSSEG*16  IF INTINP-1 MOV AL,0FFH ; Mask all interrupts. OUTB BASE+3 ENDIF IF INTINP DI ; Set up keyboard interrupt vector. MOV [BP+64H],KBINT MOV [BP+66H],CS EI ENDIF PUSH CS POP DS ; ; Initialize time-of-day clock. ; MOV SI,STCTAB MOV CX,4 ;Initialize 4 registers UP INITSTC: LODB OUT STCCOM ;Select register to initialize LODB OUT STCDATA  LODB OUT STCDATA LOOP INITSTC IF SERIALPRN MOV CX,4 SERPRN: LODB OUTB SIOBASE+1 LOOP SERPRN LODB ; Baud rate for channel 0 OUTB SIOBASE+8 ENDIF IF SERIALAUX MOV CX,4 SERAUX: LODB OUTB SIOBASE+3 LOOP SERAUX LODB ; Baud rate for channel 1. OUTB SIOBASE+9 ENDIF ; ; Move MS-DOS down to the first segment just above the I/O system. ; MOV SI,BIOSLEN ; Source points to where MS-DOS currently is. MOV AX,DOSSEG ; Destination is beginning of DOSSEG. MOV ES,AX SUB DI,DI MOV CX,DOSLEN/2 ; CX is number of words to move. REP MOVSW MOV SI,INITTAB MOV DX,1 ; Do auto memory scan.  CALL 0,DOSSEG ; ; Change disk read and write vectors (INT 37 and INT 38) to go to ; DIRECTREAD and DIRECTWRITE rather than READ and WRITE. ; SUB BP,BP MOV W,[BP+37*4],DIRECTREAD MOV W,[BP+38*4],DIRECTWRITE MOV DX,100H MOV AH,26 ;Set DMA address INT 33 MOV CX,[6] ;Get size of segment MOV BX,DS ;Save segment for later ; ; DS must be set to CS so we can point to the FCB. ; MOV AX,CS MOV DS,AX MOV DX,FCB ;File Control Block for COMMAND.COM MOV AH,15 INT 33 ;Open COMMAND.COM OR AL,AL JNZ COMERR ;Error if file not found XOR AX,AX MOV [FCB+33],AX ; Set 4-byte Random Record field to MOV [FCB+35],AX ; beginning of file. INC AX MOV [FCB+14],AX ;Set record length field MOV AH,39 ;Block read (CX already set)  INT 33 JCXZ COMERR ;Error if no records read TEST AL,1 JZ COMERR ;Error if not end-of-file ; ; Make all segment registers the same. ; MOV DS,BX MOV ES,BX MOV SS,BX MOV SP,5CH ;Set stack to standard value XOR AX,AX PUSH AX ;Put zero on top of stack for return MOV DX,80H MOV AH,26 INT 33 ;Set default transfer address (DS:0080) PUSH BX ;Put segment on stack MOV AX,100H PUSH AX ;Put address to execute within segment on stack RET L ;Jump to COMMAND COMERR: MOV DX,BADCOM MOV AH,9 ;Print string INT 33 EI STALL: JP STALL STCTAB: DB 17H ;Select master mode register DW 84F3H ;Enable time-of-day DB 1 ;Counter 1 mode register DW 0138H DB 2 DW 0038H DB 3 DW 0008H ;Set counter 3 to count days  IF SERIALPRN DB 0B7H,77H,4EH,37H,PRNBAUD ; Printer 8251A init bytes. ENDIF IF SERIALAUX DB 0B7H,77H,4EH,37H,AUXBAUD ; Auxilliary 8251A init bytes. ENDIF BADCOM: DB 13,10,"Error in loading Command Interpreter",13,10,"$" FCB: DB 1,"COMMAND COM" DS 25 ; ; ************ Time and Date ************ ; GETTIME: MOV AL,0A7H ;Save counters 1,2,3 OUT STCCOM MOV AL,0E0H ;Enable data pointer sequencing OUT STCCOM MOV AL,19H ;Select hold 1 / hold cycle OUT STCCOM CALL STCTIME ;Get seconds & 1/100's XCHG AX,DX CALL STCTIME ;Get hours & minutes XCHG AX,CX IN STCDATA MOV AH,AL IN STCDATA XCHG A L,AH ;Count of days JP POINTSTAT STCTIME: CALL STCBYTE MOV CL,AH STCBYTE: IN STCDATA MOV AH,AL SHR AH SHR AH  SHR AH SHR AH AND AL,0FH ;Unpack BCD digits AAD ;Convert to binary MOV AH,AL MOV AL,CL RET SETTIME: PUSH CX PUSH DX CALL LOAD0 ;Put 0 into load registers to condition timer MOV AL,43H ;Load counters 1 & 2 OUT STCCOM POP DX POP CX CALL LOAD MOV AL,43H OUT STCCOM ;Load counters 1&2 CALL LOAD0 MOV AL,27H ;Arm counters 1,2,3 OUT STCCOM  JP POINTSTAT LOAD0: XOR CX,CX MOV DX,CX LOAD: MOV AL,09 ;Counter 1 load register CALL OUTDX MOV AL,0AH ;Counter 2 load register MOV DX,CX OUTDX: OUT STCCOM ;Select a load register MOV AL,DL CALL OUTBCD MOV AL,DH OUTBCD: AAM ;Convert binary to unpacked BCD SHL AH SHL AH SHL AH SHL AH OR AL,AH ;Packed BCD OUT STCDATA RET SETDATE:  XCHG AX,DX ;Put date in DX MOV AL,0BH ;Select Counter 3 load register OUT STCCOM XCHG AX,DX OUT STCDATA MOV AL,AH  OUT STCDATA MOV AL,44H ;Load counter 3 OUT STCCOM POINTSTAT: PUSH AX MOV AL,1FH ;Point to status register OUT STCCOM ; so power-off glitches won't hurt POP AX RET L ; ; ************ CONSOLE INPUT ************ ; IF INTINP-1 ; Non-interrupt driven input. STATUS: IN STAT AND AL,DAV JZ NOTHING ; Jump if nothing there. PUSHF ; Save Z flag. INB DATA AND AL,7FH SEG CS MOV [QUEUE],AL ; Put new character in buffer. POPF ; Return with Z flag clear. RET L NOTHING: SEG CS MOV AL,[QUEUE] ; See if there's anything in the buffer. NOT AL ; Set up the Z flag. TEST AL,80H PUSHF NOT AL POPF RET L INP: MOV AL,-1 SEG CS XCHG AL,[QUEUE] ; Remove the character from the buffer. AND AL,AL JNS INRET  ; Return if we have a character. INLOOP: IN STAT ; Wait till a character is available. AND AL,DAV JZ INLOOP IN DATA  AND AL,7FH INRET: FLUSH: RET L QUEUE: DB -1 ; For storing characters from STATUS to INP. ENDIF IF INTINP ; Interrupt-driven input. ; ; Console keyboard interrupt handler. ; KBINT: PUSH AX PUSH SI MOV AL,20H ;End of Interrupt command OUT BASE+2 ;Send to slave IN DATA ;Get the character AND AL,7FH CMP AL,"C"-"@" JZ FLSH CMP AL,"S"-"@" JZ FLSH CMP AL,"F"-"@" JNZ SAVKY FLSH: CALL JFLUSH,BIOSSEG ; Call I/O system keyboard buffer flush. SAVKY: SEG CS MOV SI,[REAR] ;Pointer to rear of queue CALL INCQ SEG CS CMP SI,[FRONT] ;Any room in queue? JZ QFULL SEG CS MOV [SI],AL ;Put character in queue SEG CS MOV [REAR],SI ;Save pointer LEAVINT: POP SI POP AX IRET QFULL: MOV AL,7 ; BELL character. CALL JOUT,BIOSSEG ; Call I/O system console output function. JMPS LEAVINT STATUS: PUSH SI DI ; Disable interrupts while checking queue. SEG CS MOV SI,[FRONT] SEG CS CMP SI,[REAR] ; Anything in queue? JZ NOCHR ; Jump if nothing in queue. CALL INCQ SEG CS LODSB ;Get character (if there is one) OR SI,SI ;Reset zero flag NOCHR: EI POP SI RET L ;Zero clear if we have a character INP: CALL JSTAT,BIOSSEG ; Get I/O system console input status. JZ INP PUSH SI  DI ; Disable interrupts while changing queue pointers. SEG CS MOV SI,[FRONT] CALL INCQ ; Permanently remove char from  queue SEG CS MOV [FRONT],SI EI POP SI RET L FLUSH: DI SEG CS MOV [REAR],QUEUE SEG CS MOV [FRONT],QUEUE  EI RET L INCQ: INC SI CMP SI,ENDQ ;Exceeded length of queue? JB RET MOV SI,QUEUE RET FRONT: DW QUEUE REAR: DW QUEUE QUEUE: DS QSIZE ENDQ: EQU $ ENDIF ; ; ************ Console and Printer Output ************ ; OUTP: PUSH AX  OUTLP: IN STAT AND AL,TBMT JZ OUTLP POP AX OUT DATA RET L PRINT: PUSH AX PRINLP: IN PRNSTAT AND AL,PRNTBMT JZ PRINLP POP AX OUT PRNDATA RET L ; ; ************ Auxiliary I/O ************ ; AUXIN: IN AUXSTAT AND AL,DAV  JZ AUXIN IN AUXDATA RET L AUXOUT: PUSH AX AUXLP: IN AUXSTAT AND AL,TBMT JZ AUXLP POP AX OUT AUXDATA RET L ; ; ************ 1771/1793-type controller disk I/O ************ ; TARBELL:EQU TARBELLSD+TARBELLDD CROMEMCO:EQU CROMEMCO4FDC+CROMEMCO16FDC WD1793: EQU SCP+TARBELLDD+CROMEMCO16FDC WD1771: EQU TARBELLSD+CROMEMCO4FDC IF WD1793 READCOM:EQU 80H  WRITECOM:EQU 0A0H ENDIF IF WD1771 READCOM:EQU 88H WRITECOM:EQU 0A8H ENDIF IF SCP SMALLBIT:EQU 10H BACKBIT:EQU 04H DDENBIT:EQU 08H DONEBIT:EQU 01H DISK: EQU 0E0H ENDIF IF TARBELL BACKBIT:EQU 40H DDENBIT:EQU 08H DONEBIT:EQU 80H DISK: EQU 78H ENDIF IF CROMEMCO SMALLBIT:EQU 10H BACKBIT:EQU 0FDH ; Send this to port 4 to select back. DDENBIT:EQU 40H DONEBIT:EQU 01H DISK: EQU 30H ENDIF SMALLSDSECT: EQU 18 SMALLDDSECT: EQU 8 ; 8 512-byte sectors/track IBM PC standard. LARGESDSECT: EQU 26 LARGEDDSECT: EQU 8 ; 8 1024-byte sectors/track is standard. ; ; Disk change function. ; On entry: ; AL = disk drive number. ; On exit: ; AH = -1 (FF hex) if disk is changed. ; AH = 0 if don't know. ; AH = 1 if not changed. ; ; CF clear if no disk error. ; AL = disk I/O driver number. ; ; CF set if disk error. ; AL = disk error code (see disk read below). ; IF WD1771 DSKCHG: MOV AH,0 ; AH = 0 in case we don't know. SEG CS CMP AL,[CURDRV] JNZ RETL PUSH AX ; Save drive number. IF CROMEMCO INB DISK+4 ENDIF IF TARBELL INB DISK ENDIF AND AL,20H ; Look at head load bit POP AX JZ RETL MOV AH,1 ; AH = 1, disk not changed. RETL: CLC ; No disk error. RET L ENDIF ; End of 1771 DSKCHG. IF WD1793 DSKCHG: MOV AH,0 ; AH = 0 in case we don't know. SEG CS CMP AL,[CURDRV] JNZ DENSCHK ; Check density if not same drive. PUSH AX IF SCP+CROMEMCO INB DISK+4 ENDIF IF TARBELL INB DISK ENDIF AND AL,20H ; Look at head load bit POP AX JZ DENSCHK ; Check density if head not loaded. MOV AH,1 ; AH = 1, disk not changed. MOV BX,PREVDENS SEG CS XLAT ; Get previous density CLC ; No disk error. RET L DENSCHK: CALL CHKNEW ; Unload head if selecting new drive. CBW XCHG AX,SI ADD SI,PREVDENS MOV CX,4 ; Try each density twice MOV AH,0 ; Disk may not have been changed. CHKDENS: SEG CS MOV AL,[SI] ; Get previous disk I/O driver number. MOV BX,DRVTAB SEG CS XLAT ; Get drive select byte for previous density IF CROMEMCO16FDC CALL MOTOR ; Wait for motor to come up to speed. ENDIF OUT DISK+4 ; Select disk IF CROMEMCO16FDC MOV AL,0FFH ; Select side 1. OUTB 04H ENDIF MOV AL,0C4H ; READ ADDRESS command   CALL DCOM AND AL,98H IN DISK+3 ; Eat last byte to reset DRQ JZ HAVDENS ; Jump if no error in reading address. NOT AH ; AH = -1 (disk changed) if new density works. SEG CS XOR B,[SI],1 ; Try other density LOOP CHKDENS MOV AX,2 ; Couldn't read disk at all, AH = 0 for don't STC ; know if disk changed, AL = error code 2 - RET L ; disk not ready, carry set to indicate error. HAVDENS: SEG CS LODSB ; AL = disk I/O driver number. CLC ; No disk error. RET L PREVDENS:DB 1,3,5,7,9,11,13 ; Table of previous disk I/O driver numbers. ENDIF ; End of 1793 DSKCHG function. CHKNEW: MOV AH,AL  ; Save disk drive number in AH. SEG CS ; AL = previous disk drive number, XCHG AL,[CURDRV] ; make new drive current. CMP AL,AH ; Changing drives? JZ RET ; ; If changing drives, unload head so the head load delay one-shot will ; fire again. Do it by seeking to the same track with the H bit reset. ; IN DISK+1 ; Get current track number OUT DISK+3 ; Make it the track to seek to MOV AL,10H ; Seek and unload head CALL DCOM MOV AL,AH ; Restore current drive number RET IF CROMEMCO16FDC MOTOR: PUSH AX MOV AH,AL IN DISK+4 ; See if the motor is on. TEST AL,08H MOV AL,AH OUTB DISK+4 ; Select drive & start motor. JNZ MOTORSON ; No delay if motors already on. PUSH CX MOV CX,43716 ; Loop count for 1 second. MOTORDELAY: ; (8 MHz, 16-bit memory). AAM ; 83 clocks. AAM ; 83 clocks. LOOP MOTORDELAY ; 17 clocks. POP CX MOTORSON:  POP AX RET ENDIF ; ; Disk read function. ; ; On entry: ; AL = Disk I/O driver number ; BX = Disk transfer address in DS ; CX = Number of sectors to transfer ; DX = Logical record number of transfer ; On exit: ; CF clear if transfer complete  ; ; CF set if hard disk error. ; CX = number of sectors left to transfer. ; AL = disk error code ; 0 = write protect error ; 2 = not ready error ; 4 = "data" (CRC) error ; 6 = seek error ; 8 = sector not found ; 10 = write fault ;  12 = "disk" (none of the above) error ; READ: CALL SEEK ;Position head JC ERROR PUSH ES ; Make ES same as DS. MOV BX,DS MOV ES,BX RDLP: CALL READSECT ;Perform sector read JC POPESERROR INC DH ;Next sector number LOOP RDLP ;Read each sector requested CLC ; No errors. POP ES ; Restore ES register. RET L ; ; Disk write function. ; Registers same on entry and exit as read above. ; WRITE: CALL SEEK ;Position head JC ERROR WRTLP: CALL WRITESECT ;Perform sector write JC ERROR INC DH ;Bump sector counter LOOP WRTLP ;Write CX sectors CLC ; No errors. WRITERET: RET L POPESERROR: POP ES ; Restore ES register. ERROR: MOV BL,-1 SEG CS MOV [DI],BL ; Indicate we don't know where head is. MOV SI,ERRTAB GETCOD: INC BL ; Increment to next error code. SEG CS LODB TEST AH,AL ; See if error code matches disk status. JZ GETCOD ; Try another if not. MOV AL,BL ; Now we've got the code. SHL AL ; Multiply by two. STC RET L ERRTAB: DB 40H ;Write protect error DB 80H ;Not ready error DB 8 ;CRC error DB 2 ;Seek error DB 10H ;Sector not found DB 20H ;Write fault DB 7 ;"Disk" error ; ; Direct disk read and write from INT 37 and INT 38. Subroutine GETIODRIVER   ; calls DSKCHG to convert disk drive number to I/O driver number. ; ; Setting CURDRV to -1 before calling DSKCHG forces DSKCHG to check the disk's ; density before returning the I/O driver number. This is necessary because ; programs such as FORMAT could change the density of a disk and leave the ; head loaded. If the head is loaded DSKCHG assumes the disk hasn't been ; changed and returns the old I/O driver number which could be wrong. ; ; CURDRV is set to -1 before returning so when DSKCHG is called by the ; operating system, it will tell the operating system the disk may have ; been changed (because it may have been). ; DIRECTREAD: IF WD1793 CALL GETIODRIVER ; Convert drive number to I/O driver number. JC DIRECTRET ; Return if DSKCHG returned error. ENDIF CALL JREAD,BIOSSEG ; Call READ. JMPS DIRECTRET DIRECTWRITE: IF WD1793 CALL GETIODRIVER ; Convert drive number to I/O driver number. JC DIRECTRET ; Return if DSKCHG returned error. ENDIF CALL JWRITE,BIOSSEG ; Call WRITE. DIRECTRET: SEG CS MOV B,[CURDRV],-1 ; Force DSKCHG to do density check. RET L IF WD1793 GETIODRIVER: SEG CS MOV B,[CURDRV],-1 ; Force DSKCHG to do density check. PUSH BX PUSH CX CALL JCHG,BIOSSEG ; Call DSKCHG. POP CX  POP BX RET ENDIF ; ; Function: ; Seeks to proper track. ; On entry: ; Same as for disk read or write above. ; On exit: ; AH = Drive select byte ; DL = Track number ; DH = Sector number ; SI = Disk transfer address in DS ; DI = pointer to drive's track counter in CS ; CX unchanged (number of sectors) ; SEEK: MOV SI,BX ; Save transfer address CBW MOV BX,AX ; Prepare to index on drive number IF WD1793 ; If two disk formats per drive. SHR AL ; Convert to physical disk drive number. ENDIF CALL CHKNEW ; Unload head if changing drives. SEG CS MOV AL,[BX+DRVTAB] ; Get drive-select byte. IF CROMEMCO16FDC CALL MOTOR ; Wait for the motors to come up to speed. ENDIF OUTB DISK+4 ; Select drive. IF CROMEMCO  OR AL,80H ; Set auto-wait bit. ENDIF MOV AH,AL ; Save drive-select byte in AH. IF CROMEMCO16FDC MOV AL,0FFH ; Be sure side 1 of disk is selected. OUTB 04H ENDIF XCHG AX,DX ; AX = logical sector number, DH = drive-select MOV DL,LARGESDSECT ; Sectors/track 8-inch single-density. IF SCP TEST DH,SMALLBIT ; Check if small disk. JZ BIGONE ; Jump if big disk. MOV DL,SMALLSDSECT ; Sectors/track 5.25-inch single-density. TEST DH,DDENBIT ; Check if double-density. JZ HAVSECT  ; Jump if not. MOV DL,SMALLDDSECT*(SMALLDS+1) ; Sectors/`cylinder' 5.25" DD. JP HAVSECT BIGONE: TEST DH,DDENBIT ; Check if double-density. JZ HAVSECT ; Jump if not. MOV DL,LARGEDDSECT*(LARGEDS+1) ; Sectors/`cylinder' 8-inch DD. ENDIF IF TARBELLDD ; Tarbell DD controller. TEST DH,DDENBIT ; Check for double-density. JZ HAVSECT MOV DL,LARGEDDSECT*(LARGEDS+1) ; Sectors/`cylinder' 8-inch DD. ENDIF IF CROMEMCO4FDC TEST DH,SMALLBIT ; Check if small disk. JNZ HAVSECT ; Jump if not. MOV DL,SMALLSDSECT ; Sectors/track 5.25-inch single-density. ENDIF IF CROMEMCO16FDC TEST DH,SMALLBIT ; Check if small disk. JNZ BIGONE ; Jump if big disk. MOV DL,SMALLSDSECT ; Sectors/track 5.25-inch single-density. TEST DH,DDENBIT ; Ch eck if double-density. JZ HAVSECT ; Jump if not. MOV DL,SMALLDDSECT*(SMALLDS+1) ; Sectors/`cylinder' 5.25" DD. JP HAVSECT BIGONE: TEST DH,DDENBIT ; Check if double-density. JZ HAVSECT ; Jump if not. MOV DL,LARGEDDSECT*(LARGEDS+1) ; Sectors/`cylinder' 8-inch DD. ENDIF HAVSECT: DIV AL,DL ; AL = track, AH = sector. XCHG AX,DX ; AH has drive-select byte, DX = track & sector. INC DH ; Sectors start at one, not zero. SEG CS MOV BL,[BX+TRKPT] ; Get this drive's displacement into track table. ADD BX,TRKTAB ; BX now points to track counter for this drive. MOV DI,BX MOV AL,DL ; Move new track number into AL. SEG CS XCHG AL,[DI] ; Xchange current track with desired track OUT DISK+1 ; Inform controller chip of current track CMP AL,DL ; See if we're at the right track. JZ RET MOV BH,2 ; Seek retry count CMP AL,-1 ; Head position known? JNZ NOHOME ; If not, home head TRYSK: CALL HOME JC SEEKERR NOHOME: MOV AL,DL ; AL = new track number. OUT DISK+3 MOV AL,1CH+STPSPD ; Seek command. CALL MOVHEAD AND AL,98H ; Accept not ready, seek, & CRC error bits. JZ RET JS SEEKERR  ; No retries if not ready DEC BH JNZ TRYSK SEEKERR: MOV AH,AL ; Put status in AH. TEST AL,80H ; See if it was a Not Ready error. STC JNZ RET ; Status is OK for Not Ready error. MOV AH,2 ; Everything else is seek error. RET SETUP:  MOV BL,DH ; Move sector number to BL to play with. IF SCP+CROMEMCO16FDC TEST AH,DDENBIT ; Check for double density. JZ CHECKSMALL ; Not DD, go and see what size it is. ENDIF IF TARBELLDD TEST AH,DDENBIT ; Check for double density. JZ CHECK26 ; Not DD. ENDIF IF WD1793 IF (SCP+TARBELL)*LARGEDS+SCP*SMALLDS MOV AL,AH ; Select front side of disk. OUT DISK+4 ENDIF IF CROMEMCO*(LARGEDS+SMALLDS) MOV AL,0FFH ; Select front side of disk. OUT 04H ENDIF IF SCP TEST AH,SMALLBIT ; See if small. JNZ SMALLDOUBLE ; Jump if small. ENDIF IF CROMEMCO TEST AH,SMALLBIT ; See if small. JZ SMALLDOUBLE ; Jump if small. ENDIF LARGEDOUBLE: CMP BL,LARGEDDSECT ; See if legal 8-inch DD sector number. JBE PUTSEC ; Jump if ok. IF LARGEDS-1 JMPS STEP ; If 8-inch drives are SS, we gotta step. ENDIF IF LARGEDS SUB BL,LARGEDDSECT ; Find true sector for back side. CMP BL,LARGEDDSECT ; See if ok now. JA STEP ; Have to step if still too big. ENDIF  IF SCP+CROMEMCO JMPS SELECTBACK SMALLDOUBLE: CMP BL,SMALLDDSECT ; See if legal 5.25-inch DD sector number. JBE PUTSEC ; Jump if ok. IF SMALLDS-1 JMPS STEP ; If 5.25-inch drives are SS, we must step. ENDIF IF SMALLDS SUB BL,SMALLDDSECT ; Find true sector for back side. CMP BL,SMALLDDSECT ; See if ok now. JA STEP ; Have to step if still too big. ENDIF  ENDIF SELECTBACK: IF (SCP+TARBELL)*LARGEDS+SCP*SMALLDS MOV AL,AH ; Move drive select byte into AL. OR AL,BACKBIT ; Select back side. OUT DISK+4 ENDIF IF CROMEMCO*(LARGEDS+SMALLDS) MOV AL,BACKBIT ; Select back side. OUT 04H ENDIF JP PUTSEC ENDIF IF SCP CHECKSMALL: TEST AH,SMALLBIT ; See if big disk. JZ CHECK26 ; Jump if big. ENDIF IF CROMEMCO CHECKSMALL: TEST AH,SMALLBIT ; See if big disk. JNZ CHECK26 ; Jump if big. ENDIF IF SCP+CROMEMCO CMP  BL,SMALLSDSECT ; See if legal small SD/SS sector. JA STEP ; Jump if not. ENDIF CHECK26: CMP BL,LARGESDSECT ; See if legal large SD/SS sector. JBE PUTSEC ; Jump if ok. STEP: INC DL ; Increment track number. MOV AL,58H ; Step in with update. CALL DCOM SEG CS INC B,[DI] ; Increment the track pointer. MOV DH,1 ; After step, do first sector. MOV BL,DH ; Fix temporary sector number also. PUTSEC: MOV AL,BL ; Output sector number to controller. OUT DISK+2 ; ; 1771-type disk controllers need to find out if the head is loaded ; so the "E" bit of the read or write command can be set properly. ; IF WD1771 DI ; Interrupts not allowed until I/O done ENDIF IF CROMEMCO4FDC INB DISK+4 ; Get head-load bit. ENDIF IF TARBELLSD INB DISK ENDIF IF WD1771 NOT AL AND AL,20H ; Check head load status JZ RET MOV AL,4 ENDIF RET READSECT: CALL SETUP MOV BL,10 ; Retry count for hard error. XCHG DI,SI ; Transfer address to DI. PUSH DX ; Save track & sector number. MOV DX,DISK+3 ; Disk controller data port. RDAGN: IF WD1771 OR AL,READCOM ; Add "E" bit to read command. ENDIF IF WD1793 MOV AL,READCOM DI ; Interrupts off here for 1793. ENDIF OUT DISK ; Output read command. IF CROMEMCO MOV AL,AH ; Turn on auto-wait. OUT DISK+4 ENDIF MOV BP,DI ; Save address for retry. JMPS RLOOPENTRY RLOOP: STOB ; Write into memory. RLOOPENTRY: IF SCP IN DISK+5 ; Wait for DRQ or INTRQ. ENDIF IF TARBELL+CROMEMCO IN DISK+4 ENDIF IF TARBELL SHL AL INB DX ; Read data from disk controller chip. JC RLOOP ENDIF IF SCP+CROMEMCO SHR AL INB DX JNC RLOOP ENDIF EI ; Interrupts OK now CALL GETSTAT AND AL,9CH JZ RDPOP MOV DI,BP ; Get origainal address back for retry. MOV BH,AL ; Save error status for report. IF WD1771 MOV AL,0 ; "E" bit off in read command. ENDIF DEC BL JNZ RDAGN MOV AH,BH ; Put error status in AH. STC RDPOP: POP DX ; Get back track & sector number. XCHG SI,DI ; Address back to SI. IF TARBELL FORCINT: MOV AL,0D0H ; Tarbell controllers need this Force Interrupt OUT DISK ; so that Type I status is always available MOV AL,10 ; at the 1771/1793 status port so we can find INTDLY: ; out if the head is loaded. SCP and Cromemco DEC AL ; controllers have head-load status available JNZ INTDLY ; at the DISK+4 status port. ENDIF RET WRITESECT: CALL SETUP MOV BL,10 PUSH DX ; Save track & sector number. MOV DX,DISK+3 ; Disk controller data port. WRTAGN: IF WD1771 OR AL,WRITECOM ; Add "E" bit to write command. ENDIF IF WD1793 MOV AL,WRITECOM DI ; Disable interrupts here if write command. ENDIF OUT DISK ; Write sector command. IF CROMEMCO MOV AL,AH ; Turn on auto-wait. OUT DISK+4 ENDIF MOV BP,SI WRLOOP: IF SCP INB DISK+5 ENDIF IF TARBELL+CROMEMCO INB DISK+4 ENDIF IF SCP+CROMEMCO SHR AL LODB ; Get data from memory. OUTB DX ; Write to disk. JNC WRLOOP ENDIF IF TARBELL SHL AL LODB ; Get data from memory. OUTB DX ; Write to disk. JC WRLOOP  ENDIF EI ; Interrupts OK now. DEC SI CALL GETSTAT AND AL,0FCH JZ WRPOP MOV SI,BP MOV BH,AL IF WD1771 MO V AL,0 ; "E" bit off in write command. ENDIF DEC BL JNZ WRTAGN MOV AH,BH ; Error status to AH. STC WRPOP: POP DX ; Get back track & sector number. IF TARBELL JMPS FORCINT ENDIF IF SCP+CROMEMCO RET ENDIF ; ; Subroutine to restore the read/write head to track 0. ; IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1) HOME: ENDIF IF FASTSEEK*CROMEMCO TEST AH,SMALLBIT ; Check for large disk. JNZ RESTORE ; Big disks are fast seek PerSci. ENDIF MOV BL,3 TRYHOM: IF SCP*FASTSEEK MOV AL,AH ; Turn on Restore to PerSci. OR AL,80H OUTB DISK+4 ENDIF MOV AL,0CH+STPSPD ; Restore with verify command. CALL DCOM AND AL,98H IF SCP*FASTSEEK MOV AL,AH ; Restore off. OUTB DISK+4 ENDIF JZ RET JS HOMERR  ; No retries if not ready MOV AL,58H+STPSPD ; Step in with update CALL DCOM DEC BL JNZ TRYHOM HOMERR: STC RET ; ; RESTORE for PerSci drives. ; Doesn't exist yet for Tarbell controllers. ; IF FASTSEEK*TARBELL HOME: RESTORE: RET ENDIF IF FASTSEEK*CROMEMCO4FDC RESTORE: MOV AL,0C4H ;READ ADDRESS command to keep head loaded OUT DISK MOV AL,77H OUT 4 CHKRES: IN 4 AND AL,40H JZ RESDONE IN DISK+4 TEST AL,DONEBIT JZ CHKRES IN DISK JP RESTORE ;Reload head RESDONE: MOV AL,7FH OUT 4 CALL GETSTAT MOV AL,0 OUT DISK+1 ;Tell 1771 we're now on track 0 RET ENDIF IF FASTSEEK*CROMEMCO16FDC RESTORE: MOV AL,0D7H ; Turn on Drive-Select and Restore. OUTB 4 PUSH AX AAM ; 10 uS delay. POP AX RESWAIT: INB 4 ; Wait till Seek Complete is active. TEST AL,40H JNZ RESWAIT MOV AL,0FFH ; Turn off Drive-Select and Restore. OUTB 4 SUB AL,AL ; Tell 1793 we're on track 0. OUTB DISK+1 RET ENDIF ; ; Subroutine to move the read/write head to the desired track. ; Usually falls through to DCOM unless special handling for ; PerSci drives is required in which case go to FASTSK. ; IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1) MOVHEAD: ENDIF IF CROMEMCO*FASTSEEK TEST AH,SMALLBIT ; Check for PerSci. JNZ FASTSK ENDIF DCOM: OUT DISK PUSH AX AAM ;Delay 10 microseconds POP AX GETSTAT: IN DISK+4 TEST AL,DONEBIT IF TARBELL JNZ GETSTAT ENDIF IF SCP+CROMEMCO JZ GETSTAT ENDIF IN DISK RET ; ; Fast seek code for PerSci drives. ; Tarbell not installed yet. ; IF FASTSEEK*TARBELL MOVHEAD: FASTSK: RET ENDIF IF FASTSEEK*CROMEMCO FASTSK: MOV AL,6FH OUT 4 MOV AL,18H CALL DCOM SKWAIT: IN 4 TEST AL,40H JNZ SKWAIT MOV AL,7FH OUT 4  MOV AL,0 RET ENDIF CURDRV: DB -1 ; ; Explanation of tables below. ; ; DRVTAB is a table of bytes which are sent to the disk controller as drive- ; select bytes to choose which physical drive is selected for each disk I/O ; driver. It also selects whether the disk is 5.25-inch or 8-inch, single- ; density or double-density. Always select side 0 in the drive-select byte if ; a side-select bit is available. There should be one entry in the DRVTAB ; table for each disk I/O driver. Exactly which bits in the drive-select byte ; do what depends on which disk controller is used. ; ; TRKTAB is a table of bytes used to store which track the read/write ; head of each drive is on. Each physical drive should have its own ; entry in TRKTAB. ;  ; TRKPT is a table of bytes which indicates which TRKTAB entry each ; disk I/O driver should use. Since each physical drive may be used for ; more than one disk I/O driver, more than one entry in TRKPT may point ; to the same entry in TRKTAB. Drives such as PerSci 277s which use ; the same head positioner for more than one drive should share entrys ; in TRKTAB. ; ; INITTAB is the initialization table for 86-DOS as described in the ; 86-DOS Programer's Manual under "Customizing the I/O System." ;  IF SCP*COMBIN*FASTSEEK ; ; A PerSci 277 or 299 and one 5.25-inch drive. ; DRVTAB: DB 00H,08H,01H,09H,10H,18H,00H,08H,01H,09H TRKPT: DB 0,0,0,0,1,1,0,0,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 6 ; Number of disk I/O drivers. ENDIF IF CONVERT DB 10 ENDIF DB 0 ; Disk I/O driver 0 uses disk drive 0. DW LSDRIVE ; Disk I/O driver 0 is 8-inch single-density. DB 0 ; Disk I/O driver 1 uses disk drive 0. DW LDDRIVE ; Disk I/O driver 1 is 8-inch double-density. DB 1 ; Etc. DW LSDRIVE DB 1 DW LDDRIVE DB 2 DW SSDRIVE DB 2 DW SDDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE DB 4 DW OLDLSDRIVE DB 4 DW OLDLDDRIVE ENDIF ENDIF IF SCP*LARGE*(FASTSEEK-1) ; ; Two 8-inch Shugart-type drives. ; DRVTAB: DB 00H,08H,01H,09H,00H,08H,01H,09H TRKPT: DB 0,0,1,1,0,0,1,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE  IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF TARBELLDD ; ; Two 8-inch Shugart-type drives. ; DRVTAB: DB 0,8,10H,18H,0,8,10H,18H TRKPT: DB 0,0,1,1,0,0,1,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF TARBELLSD ; ; Four 8-inch Shugart-type drives. ; DRVTAB: DB 0F2H,0E2H,0F2H,0E2H TRKPT: DB 0,1,0,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF ; ; Cromemco drive select byte is derived as follows: ; Bit 7 = 0 ; Bit 6 = 1 if double density (if 16FDC) ; Bit 5 = 1 (motor on) ; Bit 4 = 0 for 5", 1 for 8" drives ; Bit 3 = 1 for drive 3 ; Bit 2 = 1 for drive 2 ; Bit 1 = 1 for drive 1 ; Bit 0 = 1 for drive 0 ; IF CROMEMCO4FDC*LARGE ; ; PerSci 277 drive. ; DRVTAB: DB 31H,32H,31H,32H TRKPT: DB 0,0,0,0 TRKTAB: DB -1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF IF CROMEMCO4FDC*COMBIN ; ; A PerSci 277 and one 5.25-inch drive. ; DRVTAB: DB 31H,32H,24H,31H,32H TRKPT: DB 0,0,1,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 3 ENDIF IF CONVERT DB 5 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE DB 2 DW SSDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 4 DW OLDLSDRIVE ENDIF ENDIF   IF CROMEMCO4FDC*SMALL ; ; Three 5.25-inch drives. ; DRVTAB: DB 21H,22H,24H TRKPT: DB 0,1,2 TRKTAB: DB -1,-1,-1 INITTAB:DB 3 DB 0 DW SSDRIVE DB 1 DW SSDRIVE DB 2 DW SSDRIVE ENDIF IF CUSTOM ; ; Cromemco 4FDC with two 8-inch Shugart-type drives. ; DRVTAB: DB 31H,32H,31H,32H TRKPT: DB 0,1,0,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF IF CROMEMCO16FDC*SMALL ; ; Three 5.25-inch drives. ; DRVTAB: DB 21H,61H,22H,62H,24H,64H TRKPT: DB 0,0,1,1,2,2 TRKTAB: DB -1,-1,-1 INITTAB:DB 6 DB 0 DW SSDRIVE DB 0 DW SDDRIVE DB 1 DW SSDRIVE DB 1 DW SDDRIVE DB 2 DW SSDRIVE DB 2 DW SDDRIVE ENDIF IF CROMEMCO16FDC*COMBIN ; ; A PerSci 277 or 299 and one 5.25-inch drive. ; DRVTAB: DB 31H,71H,32H,72H,24H,64H,31H,71H,32H,72H TRKPT: DB 0,0,0,0,1,1,0,0,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 6 ENDIF IF CONVERT DB 10 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE DB 2 DW SSDRIVE DB 2 DW SDDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE DB 4 DW OLDLSDRIVE DB 4 DW OLDLDDRIVE ENDIF ENDIF IF CROMEMCO16FDC*LARGE ; ; A PerSci 277 or 299. ; DRVTAB: DB 31H,71H,32H,72H,31H,71H,32H,72H TRKPT: DB 0,0,0,0,0,0,0,0 TRKTAB: DB -1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF SMALL+COMBIN SSDRIVE: DW 128 ; Sector size in bytes.  DB 2 ; Sector per allocation unit. DW 54 ; Reserved sectors. DB 2 ; Number of allocation tables. DW 64 ; Number of directory entrys. DW 720 ; Number of sectors on the disk. IF SMALLDS-1 SDDRIVE: ; This is the IBM Personal Computer DW 512 ; disk format. DB 1 DW 1 DB 2 DW 64 DW 320 ENDIF IF SMALLDS SDDRIVE: DW 512 DB 2 DW 1 DB 2 DW 112 DW 640 ENDIF ENDIF ; End of small drive DPTs. IF COMBIN+LARGE LSDRIVE: DW 128 ; Size of sector in bytes. DB 4 ; Sectors per allocation unit. DW 1 ; Number of reserved sectors. DB 2 ; Number of File Allocation Tables. DW 68 ; Number of directory entrys. DW 77*26 ; Number of sectors on the disk. IF CONVERT OLDLSDRIVE: DW 128 DB 4 DW 52 ; Old format had two tracks reserved. DB 2 DW 64 ; 64 directory entrys. DW 77*26 ENDIF IF LARGEDS-1 OLDLDDRIVE: LDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 96 DW 77*8 ENDIF IF LARGEDS LDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 192 ; 192 directory entrys in new 8-inch DD/DS format. DW 77*8*2 IF CONVERT OLDLDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 128 ; 128 directory entrys in old 8-inch DD/DS format. DW 77*8*2 ENDIF ENDIF ENDIF ; End of large drive DPTs. DOSSEG: EQU ($+15)/16+BIOSSEG ; Compute segment to use for 86-DOS. END o use for 86-DOS. END llocation Tables. DW 68 ; PAGE 66,132 ; ; SCP's OEM disk initialization module to link with ; Microsoft's FORMAT.OBJ disk formatting program. ; Revised 8-7-82. ; BIOSSEG EQU 40H ; Where boot sector loads IO.SYS. BOOTER EQU 200H ; "B" command puts boot sector here. SEGBIOS SEGMENT AT BIOSSEG BIOS LABEL FAR SEGBIOS ENDS CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CODE,DS:CODE,ES:CODE PUBLIC FATID,STARTSECTOR,SWITCHLIST,FREESPACE PUBLIC INIT,DISKFORMAT,BADSECTOR,DONE EXTRN SWITCHMAP:WORD,DRIVE:BYTE ; ; Select disk controller: ; SCP EQU 0 ; Seattle Computer Products controller. TARBELLSINGLE EQU 0 ; Tarbell single-density controller. TARBELLDOUBLE EQU 1 ; Tarbell double-density controller. CROMEMCO4FDC EQU 0 ; Cromemco 4FDC. CROMEMCO16FDC EQU 0 ; Cromemco 16FDC. LARGE EQU 1 ; All 8-inch disks. COMBIN EQU 0 ; Some 8-inch and some 5.25-inch. SMALL EQU 0 ; All 5.25-inch disks. CUSTOM EQU 0 ; Custom drive configuration. LARGEDS EQU 1 ; Turn this on if 8-inch disks are double- ; sided in double-density mode. SMALLDS EQU 0 ; Turn this on if 5.25-inch disk are double- ; sided in double-density mode. ; ; Turn this on if 8-inch drives are PerSci. This currently only works ; for the SCP controller. The Cromemco controllers seek and restore ; using slow-speed steps (use STPSPD = 2 for 10mS steps). ; FASTSEEK EQU 0 STPSPD EQU 0 ; ; ******************************************************************* ; TARBELL EQU TARBELLSINGLE + TARBELLDOUBLE CROMEMCO EQU CROMEMCO4FDC + CROMEMCO16FDC IF SCP DISK EQU 0E0H DONEBIT EQU 01H SMALLBIT EQU 10H BACKBIT EQU 04H DDENBIT EQU 08H ENDIF IF TARBELL  DISK EQU 78H DONEBIT EQU 80H BACKBIT EQU 40H DDENBIT EQU 08H ENDIF IF CROMEMCO DISK EQU 30H DONEBIT EQU 1 SMALLBIT EQU 10H BACKBIT EQU 0FDH ; Send this to port 4 to select back. DDENBIT EQU 40H ENDIF B_BIT EQU 08H ; Mask bits for SWITCHMAP. C_BIT EQU 04H ; This must be up here instead of near D_BIT EQU 02H ; SWITCHLIST because the assembler S_BIT EQU 01H ; can't handle the forward references. INIT: CLC ; No errors. RET DISKFORMAT: IF SCP+TARBELLDOUBLE+CROMEMCO16FDC TEST BYTE PTR [SWITCHMAP],C_BIT JZ KNOW_DENSITY ; ; If we are just clearing the disk, it is not necessary for the operator ; to specify the correct density. Therefore, look at the disk to figure ; out what it really is. ; IF CROMEMCO16FDC CALL MOTORON ; If 16FDC, wait for motors up to speed. ENDIF MOV CX,4 ; Try each density twice CHKDENS: CALL DRIVESELECT ; Select drive. OUT DISK+4,AL ; Select disk MOV AL,0C4H ; READ ADDRESS command CALL DCOM AND AL,98H IN AL,DISK+3 ; Eat last byte to reset DRQ JZ KNOW_DENSITY ; Jump if no error in reading address. XOR BYTE PTR [SWITCHMAP],D_BIT ; Try other density LOOP CHKDENS MOV DX,OFFSET READ_ERROR ; Error message. JMP ERROR KNOW_DENSITY: ENDIF CALL TABLE ; SI points to table of info for disk format. MOV AL,[SI+10] ; Get FATID byte for this format. MOV [FATID],AL MOV AX,[SI+11] ; Get STARTSECTOR for this format. MOV [STARTSECTOR],AX TEST BYTE PTR [SWITCHMAP],C_BIT JZ DOIT JMP BOOT_SECTOR ; Skip disk initialization if /C bit is on. DOIT: MOV BX,OFFSET PATTERN MOV DX,[SI+6] ; DX points to index pattern. CALL  MAKE ; Make pattern for index mark and one sector MOV CL,[SI] ; Get sector count for this sector. DEC CL ; Repeat sector pattern for remaining sectors. MAKSEC: MOV DX,[SI+8] ; DX points to sector pattern. CALL MAKE DEC CL JNZ MAKSEC CALL MAKE ; Fill out rest of track. ; ; Put in sequential sector numbers. ; MOV AL,1 ; Start with sector number 1 MOV CL,AL  ; Add one to each succeeding sector number MOV BX,[SI+2] ; Get offset from beginning of pattern to first INC BX ; track number. Increment to side, INC BX ; then sector. ADD BX,OFFSET PATTERN ; Compute actual memory address. CALL PUTSEC IF CROMEMCO16FDC-1 CALL DRIVESELECT ; Get drive-select byte. OUT DISK+4,AL ; Send it out. ENDIF IF CROMEMCO16FDC CALL MOTORON ; Wait for spindle motor to come up to speed. ENDIF CALL RESTORE ; Move the head to track 0. JZ TRACK0 ; Jump if no problem with restoring. MOV DX,OFFSET SEEK_ERROR ; Error message. JMP ERROR TRACK0: XOR DL,DL ; Start with track 0. TRACKLOOP: MOV AL,DL ; Get track number. MOV BX,[SI+2] ; Address of first track number in pattern. ADD BX,OFFSET PATTERN MOV CL,0 CALL PUTSEC ; Put track number in each sector. IF SCP+TARBELLDOUBLE CALL DRIVESELECT ; Get drive-select byte. OUT DISK+4,AL ; Select side. ENDIF IF CROMEMCO16FDC MOV AL,0FFH ; Select front side if 16FDC. OUT 04H,AL ENDIF SUB DH,DH ; Start with side 0. SIDELOOP: MOV AL,DH ; Get side number. MOV BX,[SI+2] ; Get offset from beginning of pattern to first INC BX ; track number. Increment to side. ADD BX,OFFSET PATTERN ; Compute actual memory address. MOV CL,0 CALL PUTSEC ; Put side byte in each sector ; ; Write a track. ; CALL TRACK JNC CHECK_SIDE ; Jump if no error.  MOV DX,OFFSET WRITE_ERROR ; Error message. JMP ERROR CHECK_SIDE: IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS CALL DRIVESELECT ; Get drive-select byte. TEST AL,DDENBIT ; See if double-density. JZ NEXTRACK ; Jump if not (single-density always SS). ENDIF IF SCP*LARGEDS*(SMALLDS-1)+CROMEMCO16FDC*SMALLDS*(LARGEDS-1) TEST AL,SMALLBIT ; Check for small disk. JNZ NEXTRACK ; Jump if small because SMALLDS is off or, ENDIF ; jump if large because LARGEDS is off. IF SCP*(LARGEDS-1)*SMALLDS+CROMEMCO16FDC*(SMALLDS-1)*LARGEDS TEST AL,SMALLBIT ; Check for large disk. JZ NEXTRACK ; Jump if large because LARGEDS is off or, ENDIF ; jump if small because SMALLDS if off. IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS INC DH ; Next side. CMP DH,2 ; See if too big. JAE NEXTRACK ; Finished this track, on to the next.  IF SCP+TARBELLDOUBLE OR AL,BACKBIT ; Select back side. OUT DISK+4,AL ENDIF IF CROMEMCO MOV AL,BACKBIT ; Select back side. OUT 04H,AL ENDIF JMP SHORT SIDELOOP ; Do the back side. NEXTRACK: ENDIF INC DL ; Next track. CMP DL,[SI+1] ; See if done. JAE FINIS MOV AL,58H+STPSPD ; Step in with update, no verify. CALL DCOM TEST AL,098H ; Check for errors, Not Ready, JZ TRACKLOOP ; Seek Error, CRC Error. MOV DX,OFFSET SEEK_ERROR JMP ERROR FINIS: CALL RESTORE ; Move the head back. MOV DX,OFFSET SEEK_ERROR ; Error message in case of error. JNZ ERROR ; ; Transfer the boot sector to the dis! k. ; BOOT_SECTOR: IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS CALL DRIVESELECT ; Get drive-select byte. TEST AL,DDENBIT ; If single-density, transfer single-sided boot JZ POKE_SINGLE ENDIF IF SCP*LARGEDS*(SMALLDS-1)+CROMEMCO16FDC*SMALLDS*(LARGEDS-1) TEST AL,SMALLBIT ; Check for small disk. JNZ POKE_SINGLE ; Jump if small because SMALLDS is off or, ENDIF ; jump if large because LARGEDS is off. IF SCP*(LARGEDS-1)*SMALLDS+CROMEMCO16FDC*(SMALLDS-1)*LARGEDS TEST AL,SMALLBIT ; Check for large disk. JZ POKE_SINGLE ; Jump if large because LARGEDS is off or, ENDIF ; jump if small because SMALLDS if off. IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS POKE_DOUBLE: MOV AX,[SI+13] ; Poke number of sectors to load into boot. MOV WORD PTR [B2POKE_SECTOR+1],AX MOV AL,[SI+11] ; Poke STARTSECTOR into boot sector. INC AL ; First sector on a track is 1, not 0. MOV BYTE PTR [B2POKE_FIRSECT+1],AL MOV AL,[SI] ; Poke sectors/track two places into boot. MOV BYTE PTR [B2POKE_HALFSECT_1+1],AL MOV BYTE PTR [B2POKE_HALFSECT_2+1],AL SAL AL,1 ; Compute number of sectors/"cylinder". MOV BYTE PTR [B2POKE_MAXSECT_1+2],AL MOV BYTE PTR [B2POKE_MAXSECT_2+2],AL CALL DRIVESELECT ; Get drive-select byte. IF SCP AND AL,0FCH ; Force drive 0. ENDIF IF TARBELL AND AL,0CFH ; Force drive 0. ENDIF IF CROMEMCO AND AL,0F0H ; Force all drive-selects off. OR AL,81H ; Turn on auto-wait bit & select drive 0. ENDIF MOV BYTE PTR [B2POKE_DRIVESELECT+1],AL MOV AX,[SI+15] ; Poke sector size into boot sector. MOV WORD PTR [B2POKE_SECSIZE+2],AX MOV BX,OFFSET BOOT2SIDED JMP SHORT WRITE_BOOT ENDIF POKE_SINGLE: MOV AX,[SI+13] ; Poke number of sectors to load into boot. MOV WORD PTR [B1POKE_SECTOR+1],AX MOV AL,[SI+11] ; Poke STARTSECTOR into boot sector. INC AL ; First sector on a track is 1, not 0. MOV BYTE PTR [B1POKE_FIRSECT+1],AL MOV AL,[SI] ; Poke sectors/track two places into boot. MOV BYTE PTR [B1POKE_MAXSECT_1+2],AL MOV BYTE PTR [B1POKE_MAXSECT_2+2],AL IF CROMEMCO CALL DRIVESELECT ; Get drive-select byte. AND AL,0F0H ; Force all drive-selects off. OR AL,81H ; Turn on auto-wait bit & select drive 0. MOV BYTE PTR [B1POKE_DRIVESELECT+1],AL ENDIF MOV AX,[SI+15] ; Poke sector size into boot sector. MOV WORD PTR [B1POKE_SECSIZE+2],AX MOV BX,OFFSET BOOT1SIDED WRITE_BOOT: MOV AL,[DRIVE] ; Which drive to write the boot to. MOV CX,1 ; One sector. MOV DX,0 ; Write sector number 0. INT 38 ; Call I/O system to write boot sector. POP DX ; Pop the original flags off the stack. MOV DX,OFFSET BOOT_ERROR ; Error message in case it didn't work. JC ERROR RET ERROR: MOV AH,9 ; Function 9, print string. INT 33 STC RET BADSECTOR: SUB AX,AX ; No bad sectors, no errors. RET DONE: CLC ; No errors. RET IF CROMEMCO16FDC ; ; Subroutine to wait for spindle motor to come up to speed if it's off. ; MOTORON: IN AL,DISK+4 ; Get "disk flags". TEST AL,08H ; See if motor on. LAHF ; Save Z flag for below. CALL DRIVESELECT ; Get drive-select byte. OUT DISK+4,AL ; Send it out turning motors on. SAHF ; Get Z flag back from above. JNZ MOTOR_RETURN ; If motors already on, don't wait. MOV CX,43716 ; Lo" op count, 1 second for loop below. MOTORDELAY: ; (8 MHz, 16-bit memory). AAM ; 83 clocks. AAM ; 83 clocks. LOOP MOTORDELAY ; 17 clocks. MOTOR_RETURN: RET ENDIF ; ; Set SI to point to a table of data for the selected disk format. ; ; [SI+0] = number of sectors/track ; [SI+1] = number of tracks (or cylindars) on disk. ; [SI+2] = number of bytes from beginning of format pattern track number is. ; [SI+4] = number of bytes in pattern for each sector. ; [SI+6] = address of pattern for index address mark. ; [SI+8] = address of pattern for each sector. ; [SI+10] = FATID byte for this format. ; [SI+11] = STARTSECTOR for this format. ; [SI+13] = number of sectors boot sector should load. ; [SI+15] = number of bytes/sector. ; TABLE:  IF SCP+CROMEMCO SUB AH,AH ; Zero out bit pattern for indexing. CALL DRIVESELECT ; Get drive-select byte. IF SCP NOT AL ; Make LARGE-bit instead of SMALL-bit. ENDIF AND AL,SMALLBIT ; See if small disk. JZ SIZEOK ; Jump if small. ENDIF MOV AH,02H ; Turn on bit 1 if 8-inch disk. SIZEOK: IF SCP+TARBELLDOUBLE+CROMEMCO16FDC TEST BYTE PTR [SWITCHMAP],D_BIT JZ DENSITYOK ; Jump if single-density. OR AH,01H ; Turn on bit 0 if single-density. DENSITYOK: ENDIF MOV AL,AH  ; Computed number to AL. MOV AH,17 ; And multiply by number of bytes in table. MUL AH ; AX = AL*AH. ADD AX,OFFSET DATATABLE ; Add the address of the table. MOV SI,AX RET DRIVESELECT: MOV AL,[DRIVE] ; Get drive-select byte for drive [DRIVE]. MOV BX,OFFSET DRVTAB XLAT IF SCP+CROMEMCO16FDC+TARBELLDOUBLE TEST BYTE PTR [SWITCHMAP],D_BIT ; See if double-density. JZ SPUTNIK ; Jump if not. OR AL,DDENBIT ; Turn on double-density. SPUTNIK: ENDIF RET PUTSEC: PUSH DX MOV CH,[SI] ; CH = number of sectors. MOV DX,[SI+4] ; DX = number of bytes in sector pattern. SEC: MOV [BX],AL ; Poke number in sector ID. ADD BX,DX ADD AL,CL ; Increment sector, side, or track number. DEC CH JNZ SEC POP DX RET MAKE: PUSH SI MOV SI,DX MAKELOOP: CLD ; a.k.a. "UP" LODSB ; Get byte count. OR AL,AL ; Return if zero. JZ MAKERETURN MOV CH,AL ; Count to CH. LODSB ; Get byte for pattern. PUTPAT: MOV [BX],AL ; Put byte in pattern. INC BX DEC CH JNZ PUTPAT JMP SHORT MAKELOOP MAKERETURN: POP SI RET ; ; Subroutine to restore head to track 0. ; On return, Z flag set if errors. ; RESTORE: IF SCP*FASTSEEK CALL DRIVESELECT TEST AL,SMALLBIT ; See if small disk. JNZ NORMALRESTORE ; Normal (step) restore for small disks. OR AL,80H ; Turn on RESTORE bit for PerSci drive. OUT DISK+4,AL SCWAIT: IN AL,DISK+4 ; Loop here till Seek Complete goes active. TEST AL,40H JZ SCWAIT CALL DRIVESELECT ; Turn RESTORE off. OUT DISK+4,AL SUB AL,AL ; Tell 1793 which track the head is on. OUT DISK+1,AL JMP SHORT RESTORE_RETURN ; Z flag set SUB AL,AL above (no error) NORMALRESTORE: ENDIF MOV AL,08H+STPSPD ; Restore without verify CALL DCOM TEST AL,098H ; Check for errors, Not Ready, RESTORE_RETURN: ; Seek Error, CRC Error. RET TRACK: IF CROMEMCO CALL DRIVESELECT ; Get drive-select byte.  OR AL,80H ; Turn on auto-wait. OUT DISK+4,AL ENDIF MOV DI,SI ; Save SI (pointer to DATATABLE). MOV SI,OFFSET PATTER# N MOV BP,DX ; Save DX. MOV DX,DISK+3 ; Disk controller data port. MOV AL,0F4H ; Write Track command. CLI ; Interrupts off. OUT DISK,AL WRTLP: IF SCP IN AL,DISK+5 ; Wait for DRQ or INTRQ. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4  ENDIF IF TARBELL SHL AL,1 LODSB ; Get a byte of the pattern. OUT DX,AL ; Send to controller. JC WRTLP ENDIF  IF SCP+CROMEMCO SHR AL,1 LODSB ; Get a byte of the pattern. OUT DX,AL ; Send to controller. JNC WRTLP ENDIF STI ; Interrupts back on. MOV DX,BP ; Restore DX. MOV SI,DI ; Restore SI. CALL WAIT ; Wait till status ready. AND AL,0E4H ; Accept "not ready", "write protect", "write JZ WRITERET ; fault", & "lost data" errors. STC ; Set CY flag if error. WRITERET: RET DCOM: OUT DISK,AL AAM ; 10 Microsecond delay. WAIT: IN AL,DISK+4 TEST AL,DONEBIT IF SCP+CROMEMCO JZ WAIT ENDIF IF TARBELL JNZ WAIT ENDIF IN AL,DISK ; Get status from disk. RET DATATABLE: DB 18  ; Number of sectors/track 5.25-inch SD/SS. DB 40 ; Number of tracks/disk 5.25-inch SD/SS format. DW 11 ; Beginning of pattern to first ID mark. DW 168 ; Number of bytes/sector in pattern. DW OFFSET SSINDEX ; Address of index pattern 5.25-inch SD/SS. DW OFFSET SSSECTOR ; Address of sector pattern 5.25-inch SD/SS. DB 0FEH ; FATID byte for 5.25-inch SD/SS format. DW 54+4+4+16 ; STARTSECTOR for 5.25-inch SD/SS format. DW 80 ; Number of sectors boot sector should load. DW 128 ; Bytes/sector. IF SMALLDS-1 DB 8 ; 5.25-inch DD/SS format. DB 40 DW 162 DW 652 DW OFFSET SDINDEX DW OFFSET SDSECTOR DB 0FEH DW 1+1+1+4 DW 20 DW 512 ENDIF IF SMALLDS DB 8 ; 5.25-inch DD/DS format. DB 40 DW 162 DW 652 DW OFFSET SDINDEX DW OFFSET SDSECTOR DB 0FFH DW 1+1+1+7 DW 20 DW 512 ENDIF DB 26 ; 8-inch SD/SS format. DB 77 DW 80 DW 186 DW OFFSET LSINDEX DW OFFSET LSSECTOR DB 0FEH DW 1+6+6+17 DW 80 DW 128 IF LARGEDS-1 DB 8 ; 8-inch DD/SS format. DB 77 DW 162 DW 1138 DW OFFSET LDINDEX DW OFFSET LDSECTOR DB 0FEH DW 1+1+1+3 DW 10 DW 1024 ENDIF IF LARGEDS DB 8 ; 8-inch DD/DS format. DB 77 DW 162 DW 1138 DW OFFSET LDINDEX DW OFFSET LDSECTOR DB 0FEH DW 1+2+2+6 DW 10 DW 1024 ENDIF LDINDEX: ; Pattern for 8-inch double-density. DB 80,4EH DB 12,0 DB 3,0F6H DB 1,0FCH DB 50,4EH LDSECTOR: DB 12,0 DB 3,0F5H DB 1,0FEH DB 3,0 ; Track, side, and sector DB 1,3 ; Sector size=1024  DB 1,0F7H DB 22,4EH DB 12,0 DB 3,0F5H DB 1,0FBH DB 255,0E5H DB 255,0E5H DB 255,0E5H DB 255,0E5H DB 4,0E5H DB 1,0F7H DB 54,4EH DB 0 DB 255,4EH DB 255,4EH DB 255,4EH DB 0 LSINDEX: ; Pattern for 8-inch single-density.  DB 40,-1 DB 6,0 DB 1,0FCH DB 26,-1 LSSECTOR: DB 6,0 DB 1,0FEH DB 4,0 DB 1,0F7H DB 11,-1 DB 6,0 DB 1,0FBH  DB 128,0E5H DB 1,0F7H DB 27,-1 DB 0 DB 255,-1 DB 255,-1 DB 0 SDINDEX: ; Pattern for 5.25-inch double-density. DB 80,4EH DB 12,0 DB 3,0F6H DB 1,0FCH DB 50,4EH SDSECTOR: DB 12,0 DB 3,0F5H DB 1,0FEH DB 3,0 ; Track, side, and sector. DB 1,2 ; Sector size = 512. DB 1,0F7H DB 22,4EH DB 12,0 DB 3,0F5H DB 1,0FBH DB 255,0E5H DB 255,0E$ 5H DB 2,0E5H DB 1,0F7H DB 80,4EH DB 0 DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 0 SSINDEX: ; Pattern for 5.25-inch single-density. DB 4,-1 ; No index mark. SSSECTOR: DB 6,0 DB 1,0FEH DB 4,0  DB 1,0F7H DB 11,-1 DB 6,0 DB 1,0FBH DB 128,0E5H DB 1,0F7H DB 9,-1 DB 0 DB 255,0 DB 255,0 DB 0 IF TARBELLDOUBLE DRVTAB DB 00H,10H,20H,30H ENDIF IF TARBELLSINGLE DRVTAB: DB 0F2H,0E2H,0D2H,0C2H ENDIF IF CROMEMCO*LARGE DRVTAB: DB 31H,32H,34H,38H ENDIF IF CROMEMCO*SMALL DRVTAB: DB 21H,22H,24H,28H ENDIF IF CROMEMCO*COMBIN DRVTAB: DB 31H,32H,24H ENDIF IF SCP*LARGE DRVTAB: DB 00H,01H,02H,03H ENDIF IF SCP*SMALL DRVTAB: DB 10H,11H,12H,13H ENDIF  IF SCP*COMBIN DRVTAB: DB 00H,01H,10H ENDIF ; ; Error messages. ; IF SCP+TARBELLDOUBLE+CROMEMCO16FDC READ_ERROR DB "Read Error - can't determine density",13,10,'$' ENDIF SEEK_ERROR DB 'Seek Error - disk not ready',13,10,'$' WRITE_ERROR DB 'Write Error - not ready, write protect, ' DB 'write fault, or lost data',13,10,'$' BOOT_ERROR DB 'Error writing boot sector',13,10,'$' SWITCHLIST DB 4,'BCDS' FATID DB 0 STARTSECTOR DW 0 FREESPACE DW OFFSET PATTERN+12000 ; ; Boot sectors, one for single-sided boot, a second for double-sided boot. ; Both have various variables such as STARTSECTOR and number of sectors to ; load poked in before the boot sector is written to the disk. The boot ; sectors should be ORGed at 200 hex, but since all jumps and calls are ; reletive, it doesn't matter that it is ORGed at a random location. ; BOOT1SIDED: CLI ; Interrupts off. XOR AX,AX MOV DS,AX MOV ES,AX MOV SS,AX MOV SP,BOOTER ; For debugging purposes. CLD ; a.k.a. "UP" MOV DI,BIOSSEG*16 B1POKE_SECTOR: MOV CX,0 ; CX = number of sectors/track. B1POKE_FIRSECT: MOV BL,0 ; BL = first sector to load. B1SECT: MOV AL,0D0H ; Force Interrupt command. OUT DISK,AL ; To force Type I status AAM ; Give force interrupt time. AAM AAM AAM B1SEEKLP: B1POKE_MAXSECT_1: CMP BL,0 ; Compare with number of sectors/track. JBE B1NOSTEP MOV AL,58H ; Step in with update. CALL B1DCOM B1POKE_MAXSECT_2: SUB BL,0 ; Subtract number of sectors/track. JMP SHORT B1SEEKLP B1NOSTEP: MOV AL,BL ; Get sector number. OUT DISK+2,AL ; Output sector number to disk controller. IF CROMEMCO B1POKE_DRIVESELECT: MOV AL,0 ; AL = drive-select byte to turn on auto-wait. OUT DISK+4,AL ; Turn on hardware wait ENDIF MOV DX,DISK+3 ; Disk controller data port. PUSH DI IF CROMEMCO4FDC+TARBELLSINGLE IN AL,DISK ; Get head load status. NOT AL AND AL,20H JZ B1OUTCOM MOV AL,4 B1OUTCOM: OR AL,88H ; Read Sector command. ENDIF IF SCP+CROMEMCO16FDC+TARBELLDOUBLE MOV AL,88H ; Read Sector command. ENDIF OUT DISK,AL JMP SHORT B1READ B1READLOOP: STOSB ; Put in memory. B1READ: IF SCP IN AL,DISK+5 ; Input from auto-wait port. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4 ENDIF IF TARBELL ROL AL,1 IN AL,DX ; Read data from disk controller chip. JC B1READLOOP ENDIF IF SCP+CROMEMCO ROR AL,1 IN AL,DX ; Read data from disk controller chip. JNC B1READLOOP ENDIF POP DI CALL B1STAT AND AL,9CH JNZ B1SECT B1POKE_S% ECSIZE: ADD DI,128 ; Add sector size to load next sector. INC BL LOOP B1SECT JMP BIOS B1DCOM: OUT DISK,AL AAM B1STAT: IN AL,DISK+4 TEST AL,DONEBIT IF TARBELL JNZ B1STAT ENDIF IF SCP+CROMEMCO JZ B1STAT ENDIF IN AL,DISK RET IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS BOOT2SIDED: CLI ; Interrupts off. XOR AX,AX  MOV DS,AX MOV ES,AX MOV SS,AX MOV SP,BOOTER ; For debugging purposes. CLD ; a.k.a. "UP" MOV DI,BIOSSEG*16 B2POKE_SECTOR: MOV CX,0 ; CX = number of sectors/track. B2POKE_FIRSECT: MOV BL,0 ; BL = first sector to load. B2SECT: MOV AL,0D0H ; Force Interrupt command. OUT DISK,AL ; To force Type I status AAM ; Give force interrupt time. AAM AAM AAM B2SEEKLP: B2POKE_MAXSECT_1: CMP BL,0 ; Compare with number of sectors/track. JBE B2NOSTEP MOV AL,58H ; Step in with update. CALL B2DCOM B2POKE_MAXSECT_2: SUB BL,0 ; Subtract number of sectors/track. JMP SHORT B2SEEKLP B2NOSTEP: MOV AL,BL ; Get sector number. IF SCP+TARBELL MOV AH,0 ; Assume side 0. ENDIF IF CROMEMCO MOV AH,0FFH ; Assume side 0.  ENDIF B2POKE_HALFSECT_1: CMP AL,0 ; Find out which sector and side to use. JBE B2SIDE B2POKE_HALFSECT_2: SUB AL,0 ; Compute sector number for back side. IF SCP MOV AH,04H ; Side 1 bit. ENDIF IF TARBELL MOV AH,40H ; Side 1 bit. ENDIF IF CROMEMCO MOV AH,0FDH ; Side 1 bit. ENDIF B2SIDE: OUT DISK+2,AL ; Output sector number to disk controller. IF SCP+TARBELL MOV AL,AH ; Get side-select byte. B2POKE_DRIVESELECT: OR AL,0 ; Add the rest of the drive-select bits. OUT DISK+4,AL ; And send the mess out. ENDIF IF CROMEMCO B2POKE_DRIVESELECT: MOV AL,0 OUT DISK+4,AL ; Turn on hardware wait MOV AL,AH ; Get side select byte. OUT 04H,AL ; Send to Auxillary drive-select port. ENDIF MOV DX,DISK+3 ; Disk controller data port. PUSH DI IF CROMEMCO4FDC+TARBELLSINGLE IN AL,DISK ; Get head load status. NOT AL AND AL,20H JZ B2OUTCOM MOV AL,4 B2OUTCOM: OR AL,88H ; Read Sector command. ENDIF IF SCP+CROMEMCO16FDC+TARBELLDOUBLE MOV AL,88H ; Read Sector command. ENDIF OUT DISK,AL JMP SHORT B2READ B2READLOOP: STOSB ; Put in memory. B2READ:  IF SCP IN AL,DISK+5 ; Input from auto-wait port. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4 ENDIF IF TARBELL ROL AL,1 IN AL,DX ; Read data from disk controller chip. JC B2READLOOP ENDIF IF SCP+CROMEMCO ROR AL,1 IN AL,DX ; Read data from disk controller chip. JNC B2READLOOP ENDIF POP DI CALL B2STAT AND AL,9CH JNZ B2SECT B2POKE_SECSIZE: ADD DI,128 ; Add sector size to load next sector. INC BL LOOP B2SECT JMP BIOS B2DCOM: OUT DISK,AL AAM B2STAT:  IN AL,DISK+4 TEST AL,DONEBIT IF TARBELL JNZ B2STAT ENDIF IF SCP+CROMEMCO JZ B2STAT ENDIF IN AL,DISK RET ENDIF ; End of double-sided boot sector. PATTERN LABEL BYTE ; 12000-byte buffer for track pattern. CODE ENDS END   END d data from disk controller chip. JNC B2READLOOP ENDIF POP DI CALL B2STAT AND AL,9CH JNZ B2SECT B2POKE_S& ; Seattle Computer Products 8086 Monitor version 1.5 6-10-82. ; by Tim Paterson ; This software is not copyrighted. ; To select a disk boot, set one of the following equates ; to 1, the rest to 0. SCP: EQU 0 ;1 for SCP, 0 for others. CROMEMCO4FDC: EQU 0 ;Cromemco 4FDC? CROMEMCO16FDC: EQU 0 ;Cromemco 16FDC? NORTHSTARSD: EQU 0 ;North Star single density? TARBELLSD: EQU 0 ;Tarbell SD controller or DD controller SD only TARBELLDD: EQU 1 ;Tarbell DD controller SD or DD? OTHER: EQU 0 ;User-defined disk PUTBASE:EQU 100H LOAD: EQU 200H ORG 7F0H PUT PUTBASE+7F0H JMP 0,0FF80H ;Power-on jump to monitor ;Baud Rate Table. The 9513 divides 2MHz by these values. ;They are for 9600, 1200, 300, 150, 110 baud BAUD: DW 13,104,416,832,1144 ORG 100H ;RAM area base address ;System Equates BASE: EQU 0F0H ;CPU Support base port address STAT: EQU BASE+7 ;UART status port DATA: EQU BASE+6 ;UART data port DAV: EQU 2 ;UART data available bit TBMT: EQU 1 ;UART transmitter ready bit BUFLEN: EQU 80 ;Maximum length of line input buffer BPMAX: EQU 10 ;Maximum number of breakpoints BPLEN: EQU BPMAX+BPMAX ;Length of breakpoint table REGTABLEN:EQU 14 ;Number of registers SEGDIF: EQU 800H ;-0FF800H (ROM address) PROMPT: EQU ">"  CAN: EQU "@" ;RAM area. BRKCNT: DS 2 ;Number of breakpoints TCOUNT: DS 2 ;Number of steps to trace BPTAB: DS BPLEN ;Breakpoint table LINEBUF:DS BUFLEN+1 ;Line input buffer ALIGN DS 50 ;Working stack area STACK: ;Register save area AXSAVE: DS 2 BXSAVE: DS 2 CXSAVE: DS 2 DXSAVE: DS 2 SPSAVE: DS 2 BPSAVE: DS 2 SISAVE: DS 2 DISAVE: DS 2 DSSAVE: DS 2 ESSAVE: DS 2 RSTACK: ;Stack set here so registers can be saved by pushing SSSAVE: DS 2 CSSAVE: DS 2 IPSAVE: DS 2 FSAVE: DS 2 ;Start of Monitor code ORG 0 PUT PUTBASE ;One-time initialization UP XOR AX,AX MOV SS,AX MOV DS,AX MOV ES,AX MOV DI,AXSAVE MOV CX,14 REP STOW ;Set register images to zero OR B,[FSAVE+1],2 ;Enable interrupts MOV CL,4  MOV AL,40H MOV DI,DSSAVE REP STOW ;Set segment reg. images to 40H MOV B [SPSAVE+1],0CH ;Set user stack to 400H+0C00H  MOV SP,STACK ;Prepare 9513 MOV AL,17H OUT BASE+5 ;Select Master Mode register MOV AL,0F3H OUT BASE+4 ;Low byte of Master Mode MOV AX,584H ;Output 84H to BASE+4 OUTW BASE+4 ;and 05H to BASE+5 ;Master Mode now set to 84F3H: ; Scaler set to BCD division ; Enable data pointer increment ; 8-bit data bus ; FOUT=100Hz, dividing F5 by 4 (F5=4MHz/10000) ; Both alarm comparators disabled ; Time-of-day enabled ;Counter 5 selected ;Initialize loop. Ports BASE through BASE+7 are initialized ;from table. Each table entry has number of bytes followed by ;data. MOV SI,INITTABLE ;Initialization table MOV DX,BASE ;DX has (variable) port no. INITPORT: SEG CS LODB ;Get byte count MOV CL,AL JCXZ NEXTPORT ;No init. for some ports  INITBYTE: SEG CS LODB ;Get init. data OUT DX ;Send to port LOOP INITBYTE ;As many bytes as required NEXTPORT: INC'  DX ;Prepare for next port CMP DL,BASE+8 ;Check against limit JNZ INITPORT ;Initialization complete except for determining baud rate. ;Both 8259As are ready to accept interrupts, the 9513 is ;providing 19.2k baud X 16 to the 8251A which is set for ;16X clock and one stop bit. CALL CHECKB ;Check for correct baud rate ;CHECKB does not return if baud rate is correct  ;Intial baud rate (19.2k) was wrong, so run auto-baud routine INITBAUD: MOV SI,BAUD ;First set up 9513 for slower baud rates (<=9600). ;Counter 5 mode register has already been selected. MOV AX,0E823H ;Output 23H to BASE+4 OUTW BASE+4 ;and 0E8H to BASE+5 ;23H to BASE+4 sets lower half of Counter 5 mode register. ;Reload from Load, count down repetively in binary, ;toggle output. ;0E8H to BASE+5 disables data pointer sequencing MOV AL,0DH OUT BASE+5 ;Select Counter 5 load reg. INITB: SEG CS LODW ;Get divisor OUT BASE+4 ;Output low byte MOV AL,AH OUT BASE+4 ;Output high byte CALL CHECKB ;Check if baud rate correct JP INITB ;Try next rate if not CHECKB: CALL IN ;First byte could be messed up CALL IN ;Get carriage return CMP AL,13 ;Correct? JZ MONITOR ;Don't return if correct RET ;Didn't get it yet ;Initialization complete, including baud rate. MONITOR: ; Do auto boot if sense switch 0 is on. MOV DI,LINEBUF MOV B,[DI],13 ;No breakpoints after boot IN BASE+0FH ;Sense switch port TEST AL,1 JZ DOMON JMP BOOT DOMON: MOV SI,HEADER CALL PRINTMES COMMAND: ;Re-establish initial conditions UP XOR AX,AX MOV DS,AX MOV ES,AX MOV SP,STACK MOV [64H],INT ;Set UART interrupt vector MOV [66H],CS MOV AL,PROMPT CALL OUT CALL INBUF ;Get command line ;From now and throughout command line processing, DI points ;to next character in command line to be processed. CALL SCANB ;Scan off leading blanks JZ COMMAND ;Null command? MOV AL,[DI] ;AL=first non-blank character ;Prepare command letter for table lookup SUB AL,"B" ;Low end range check JC ERR1 CMP AL,"T"+1-"B" ;Upper end range check JNC ERR1 INC DI SHL AL ;Times two CBW ;Now a 16-bit quantity XCHG BX,AX ;In BX we can address with it SEG CS CALL [BX+COMTAB] ;Execute command JP COMMAND ;Get next command ERR1: JMP ERROR ;Get input line INBUF: MOV DI,LINEBUF ;Next empty buffer location XOR CX,CX ;Character count GETCH: CALL IN ;Get input character CMP AL,20H ;Check for control characters JC CONTROL CMP AL,7FH ;RUBOUT is a backspace JZ BACKSP CALL OUT ;Echo character CMP AL,CAN ;Cancel line? JZ KILL STOB ;Put in input buffer INC CX ;Bump character count CMP CX,BUFLEN ;Buffer full? JBE GETCH ;Drop in to backspace if full BACKSP: JCXZ GETCH ;Can't backspace over nothing DEC DI ;Drop pointer DEC CX ;and character count CALL BACKUP ;Send physical backspace JP GETCH ;Get next char. CONTROL:  CMP AL,8 ;Check for backspace JZ BACKSP CMP AL,13 ;Check for carriage return JNZ GETCH ;Ignore all other control char. STOB ;Put the car. ret. in buffer MOV DI,LINEBUF ;Set up DI for command processing ;Output CR/LF sequence CRLF: MOV AL,13 CALL OUT MOV AL,10 JP OUT ;Cancel input line KILL: CALL CRLF JP COMMAND ;Character input routine (  IN: DI ;Poll, don't interrupt INB STAT TEST AL,DAV JZ IN ;Loop until ready INB DATA AND AL,7FH ;Only 7 bits EI ;Interrupts OK now RET ;Physical backspace - blank, backspace, blank BACKUP: MOV SI,BACMES ;Print ASCII message. Last char has bit 7 set PRINTMES: SEG CS LODB ;Get char to print CALL OUT SHL AL ;High bit set? JNC PRINTMES  RET ;Scan for parameters of a command SCANP: CALL SCANB ;Get first non-blank CMP B,[DI],"," ;One comma between params OK JNE EOLCHK ;If not comma, we found param INC DI ;Skip over comma ;Scan command line for next non-blank character  SCANB: MOV AL," " PUSH CX ;Don't disturb CX MOV CL,-1 ;but scan as many as necessary REPE SCAB DEC DI ;Back up to first non-blank POP CX EOLCHK: CMP B,[DI],13 RET ;Print the 5-digit hex address of SI and DS OUTSI: MOV DX,DS  ;Put DS where we can work with it MOV AH,0 ;Will become high bits of DS CALL SHIFT4 ;Shift DS four bits ADD DX,SI ;Compute absolute address JP OUTADD ;Finish below ;Print 5-digit hex address of DI and ES ;Same as OUTSI above OUTDI: MOV DX,ES MOV AH,0 CALL SHIFT4 ADD DX,DI ;Finish OUTSI here too OUTADD: ADC AH,0 ;Add in carry to high bits CALL HIDIG ;Output hex value in AH ;Print out 16-bit value in DX in hex OUT16: MOV AL,DH ;High-order byte first CALL HEX MOV AL,DL ;Then low-order byte ;Output byte in AL as two hex digits HEX: MOV AH,AL ;Save for second digit ;Shift high digit into low 4 bits PUSH CX MOV CL,4 SHR AL,CL POP CX CALL DIGIT ;Output first digit HIDIG: MOV AL,AH ;Now do digit saved in AH DIGIT: AND AL,0FH ;Mask to 4 bits ;Trick 6-byte hex conversion works on 8086 too. ADD AL,90H DAA ADC AL,40H DAA ;Console output of character in AL OUT: PUSH AX ;Character to output on stack OUT1: INB STAT AND AL,TBMT JZ OUT1 ;Wait until ready POP AX OUTB DATA RET ;Output one space BLANK: MOV AL," " JP OUT ;Output the number of blanks in CX TAB: CALL BLANK LOOP TAB RET ;Command Table. Command letter indexes into table to get ;address of command. PERR prints error for no such command. COMTAB: DW BOOT ;B DW PERR ;C DW DUMP ;D DW ENTER ;E DW FILL ;F DW GO ;G DW PERR ;H DW INPUT ;I DW PERR ;J DW PERR ;K DW PERR ;L DW MOVE ;M DW PERR ;N DW OUTPUT ;O DW PERR ;P DW PERR ;Q DW REG ;R DW SEARCH ;S DW TRACE ;T ;Given 20-bit address in AH:DX, breaks it down to a segment ;number in AX and a displacement in DX. Displacement is ;always zero except for least significant 4 bits. GETSEG: MOV AL,DL AND AL,0FH ;AL has least significant 4 bits CALL SHIFT4 ;4-bit left shift of AH:DX MOV DL,AL ;Restore lowest 4 bits MOV AL,DH ;Low byte of segment number XOR DH,DH ;Zero high byte of displacement RET ;Shift AH:DX left 4 bits SHIFT4: SHL DX RCL AH ;1 SHL DX RCL AH ;2 SHL DX RCL AH ;3 SHL DX RCL AH ;4 RET2: RET ;RANGE - Looks for parameters defining an address range. ;The first parameter is a hex number of 5 or less digits ;which specifies the starting address. The second parameter ;may specify the ending address, or it may be preceded by ;"L" and specify a length (4 d) igits max), or it may be ;omitted and a length of 128 bytes is assumed. Returns with ;segment no. in AX and displacement (0-F) in DX. RANGE: MOV CX,5 ;5 digits max CALL GETHEX ;Get hex number PUSH AX ;Save high 4 bits PUSH DX ;Save low 16 bits CALL SCANP ;Get to next parameter CMP B,[DI],"L" ;Length indicator? JE GETLEN MOV DX,128 ;Default length CALL HEXIN ;Second parameter present? JC RNGRET ;If not, use default MOV CX,5 ;5 hex digits CALL GETHEX ;Get ending address  MOV CX,DX ;Low 16 bits of ending addr. POP DX ;Low 16 bits of starting addr. POP BX ;BH=hi 4 bits of start addr. SUB CX,DX ;Compute range SBB AH,BH ;Finish 20-bit subtract JNZ RNGERR ;Range must be less than 64K XCHG AX,BX ;AH=starting, BH=ending hi 4 bits INC CX ;Range must include ending location JP RNGCHK ;Finish range testing and return GETLEN: INC DI ;Skip over "L" to length MOV CX,4 ;Length may have 4 digits CALL GETHEX ;Get the range RNGRET: MOV CX,DX ;Length  POP DX ;Low 16 bits of starting addr. POP AX ;AH=hi 4 bits of starting addr. ;RNGCHK verifies that the range lies entirely within one segment. ;CX=0 means count=10000H. Range is within one segment only if ;adding the low 4 bits of the starting address to the count is ;<=10000H, because segments can start only on 16-byte boundaries. RNGCHK: MOV BX,DX ;Low 16 bits of start addr. AND BX,0FH ;Low 4 bits of starting addr. JCXZ MAXRNG ;If count=10000H then BX must be 0 ADD BX,CX ;Must be <=10000H JNC GETSEG ;OK if strictly < MAXRNG: ;If here because of JCXZ MAXRNG, we are testing if low 4 bits ;(in BX) are zero. If we dropped straight in, we are testing ;for BX+CX=10000H (=0). Either way, zero flag set means ;withing range. JZ GETSEG RNGERR: MOV AX,4700H+"R" ;RG ERROR JMP ERR ;Dump an area of memory in both hex and ASCII DUMP: CALL RANGE ;Get range to dump PUSH AX ;Save segment CALL GETEOL ;Check for errors POP DS ;Set segment MOV SI,DX ;SI has displacement in segment ROW: CALL OUTSI ;Print address at start of line PUSH SI ;Save address for ASCII dump BYTE: CALL BLANK ;Space between bytes BYTE1: LODB ;Get byte to dump CALL HEX ;and display it POP DX ;DX has start addr. for ASCII dump DEC CX ;Drop loop count JZ ASCII ;If through do ASCII dump MOV AX,SI TEST AL,0FH ;On 16-byte boundary? JZ ENDROW PUSH DX ;Didn't need ASCII addr. yet TEST AL,7 ;On 8-byte boundary? JNZ BYTE MOV AL,"-" ;Mark every 8 bytes CALL OUT JP BYTE1 ENDROW: CALL ASCII ;Show it in ASCII JP ROW ;Loop until count is zero ASCII: PUSH CX ;Save byte count  MOV AX,SI ;Current dump address MOV SI,DX ;ASCII dump address SUB AX,DX ;AX=length of ASCII dump ;Compute tab length. ASCII dump always appears on right side ;screen regardless of how many bytes were dumped. Figure 3 ;characters for each byte dumped and subtract from 51, which ;allows a minimum of 3 blanks after the last byte dumped. MOV BX,AX SHL AX ;Length times 2 ADD AX,BX ;Length times 3 MOV CX,51 SUB CX,AX ;Amount to tab in CX CALL TAB MOV CX,BX ;ASCII dump length back in CX ASCDMP: LODB ;Get ASCII byte to dump AND AL,7FH ;ASCII uses 7 bits CMP AL,7FH ;Don't try to print RUBOUT JZ NOPR* T CMP AL," " ;Check for control characters JNC PRIN NOPRT: MOV AL,"." ;If unprintable character PRIN: CALL OUT ;Print ASCII character LOOP ASCDMP ;CX times POP CX ;Restore overall dump length JMP CRLF ;Print CR/LF and return ;Block move one area of memory to another. Overlapping moves ;are performed correctly, i.e., so that a source byte is not ;overwritten until after it has been moved. MOVE: CALL RANGE ;Get range of source area PUSH CX ;Save length PUSH AX ;Save segment MOV SI,DX ;Set source displacement MOV CX,5 ;Allow 5 digits CALL GETHEX ;in destination address CALL GETEOL ;Check for errors CALL GETSEG ;Convert dest. to seg/disp MOV DI,DX ;Set dest. displacement POP BX ;Source segment MOV DS,BX MOV ES,AX ;Destination segment POP CX ;Length CMP DI,SI ;Check direction of move SBB AX,BX ;Extend the CMP to 32 bits JB COPYLIST ;Move forward into lower mem. ;Otherwise, move backward. Figure end of source and destination ;areas and flip direction flag. DEC CX ADD SI,CX ;End of source area ADD DI,CX ;End of destination area DOWN ;Reverse direction  INC CX COPYLIST: MOVB ;Do at least 1 - Range is 1-10000H not 0-FFFFH DEC CX REP MOVB ;Block move RET ;Fill an area of memory with a list values. If the list ;is bigger than the area, don't use the whole list. If the ;list is smaller, repeat it as many times as necessary. FILL: CALL RANGE ;Get range to fill PUSH CX ;Save length PUSH AX ;Save segment number PUSH DX ;Save displacement CALL LIST ;Get list of values to fill with POP DI ;Displacement in segment POP ES ;Segment POP CX ;Length CMP BX,CX ;BX is length of fill list MOV SI,LINEBUF ;List is in line buffer JCXZ BIGRNG JAE COPYLIST ;If list is big, copy part of it BIGRNG: SUB CX,BX ;How much bigger is area than list? XCHG CX,BX ;CX=length of list PUSH DI ;Save starting addr. of area REP MOVB ;Move list into area POP SI ;The list has been copied into the beginning of the ;specified area of memory. SI is the first address ;of that area, DI is the end of the copy of the list ;plus one, which is where the list will begin to repeat. ;All we need to do now is copy [SI] to [DI] until the ;end of the memory area is reached. This will cause the ;list to repeat as many times as necessary. MOV CX,BX ;Length of area minus list PUSH ES ;Different index register POP DS ;requires different segment reg. JP COPYLIST ;Do the block move ;Search a specified area of memory for given list of bytes. ;Print address of first byte of each match. SEARCH: CALL RANGE ;Get area to be searched PUSH CX ;Save count PUSH AX ;Save segment number PUSH DX ;Save displacement CALL LIST ;Get search list DEC BX ;No. of bytes in list-1 POP DI ;Displacement within segment POP ES ;Segment POP CX ;Length to be searched SUB CX,BX ; minus length of list SCAN: MOV SI,LINEBUF ;List kept in line buffer LODB ;Bring first byte into AL DOSCAN: SCAB  ;Search for first byte LOOPNE DOSCAN ;Do at least once by using LOOP JNZ RET ;Exit if not found PUSH BX ;Length of list minus 1 XCHG BX,CX PUSH DI ;Will resume search here REPE CMPB ;Compare rest of string MOV CX,BX ;Area length ba+ ck in CX POP DI ;Next search location POP BX ;Restore list length JNZ TEST ;Continue search if no match DEC DI ;Match address CALL OUTDI ;Print it INC DI ;Restore search address CALL CRLF TEST: JCXZ RET JP SCAN ;Look for next occurrence ;Get the next parameter, which must be a hex number. ;CX is maximum number of digits the number may have. GETHEX:  CALL SCANP ;Scan to next parameter GETHEX1: XOR DX,DX ;Initialize the number MOV AH,DH CALL HEXIN ;Get a hex digit  JC ERROR ;Must be one valid digit MOV DL,AL ;First 4 bits in position GETLP: INC DI ;Next char in buffer DEC CX ;Digit count CALL HEXIN ;Get another hex digit? JC RET ;All done if no more digits JCXZ ERROR ;Too many digits? CALL SHIFT4 ;Multiply by 16 OR DL,AL ;and combine new digit JP GETLP ;Get more digits ;Check if next character in the input buffer is a hex digit ;and convert it to binary if it is. Carry set if not. HEXIN: MOV AL,[DI] ;Check if AL has a hex digit and convert it to binary if it ;is. Carry set if not. HEXCHK: SUB AL,"0" ;Kill ASCII numeric bias JC RET CMP AL,10  CMC JNC RET ;OK if 0-9 SUB AL,7 ;Kill A-F bias CMP AL,10 JC RET CMP AL,16 CMC RET: RET ;Process one parameter when a list of bytes is ;required. Carry set if parameter bad. Called by LIST LISTITEM: CALL SCANP ;Scan to parameter  CALL HEXIN ;Is it in hex? JC STRINGCHK ;If not, could be a string MOV CX,2 ;Only 2 hex digits for bytes CALL GETHEX ;Get the byte value MOV [BX],DL ;Add to list INC BX GRET: CLC ;Parameter was OK RET STRINGCHK: MOV AL,[DI] ;Get first character of param CMP AL,"'" ;String? JZ STRING CMP AL,'"' ;Either quote is all right JZ STRING STC ;Not string, not hex - bad RET STRING: MOV AH,AL ;Save for closing quote INC DI STRNGLP: MOV AL,[DI] ;Next char of string INC DI CMP AL,13 ;Check for end of line JZ ERROR ;Must find a close quote CMP AL,AH ;Check for close quote JNZ STOSTRG ;Add new character to list CMP AH,[DI] ;Two quotes in a row? JNZ GRET ;If not, we're done INC DI ;Yes - skip second one STOSTRG: MOV [BX],AL ;Put new char in list INC BX JP STRNGLP ;Get more characters ;Get a byte list for ENTER, FILL or SEARCH. Accepts any number ;of 2-digit hex values or character strings in either single ;(') or double (") quotes. LIST: MOV BX,LINEBUF ;Put byte list in the line buffer LISTLP: CALL LISTITEM ;Process a parameter JNC LISTLP ;If OK, try for more SUB BX,LINEBUF ;BX now has no. of bytes in list JZ ERROR ;List must not be empty ;Make sure there is nothing more on the line except for ;blanks and carriage return. If there is, it is an ;unrecognized parameter and an error. GETEOL: CALL SCANB ;Skip blanks JNZ ERROR ;Better be a RETURN RET ;Command error. DI has been incremented beyond the ;command letter so it must decremented for the ;error pointer to work. PERR: DEC DI ;Syntax error. DI points to character in the input buffer ;which caused error. By subtracting from start of buffer, ;we will know how far to tab over to appear directly below ;it on the terminal. Then print "^ Error". ERROR: SUB DI,LINEBUF-1 ;How many char processed so far? MOV CX,DI ;Param, eter for TAB in CX CALL TAB ;Directly below bad char MOV SI,SYNERR ;Error message ;Print error message and abort to command level PRINT: CALL PRINTMES JMP COMMAND ;Short form of ENTER command. A list of values from the ;command line are put into memory without using normal ;ENTER mode. GETLIST: CALL LIST ;Get the bytes to enter POP DI ;Displacement within segment POP ES ;Segment to enter into MOV SI,LINEBUF ;List of bytes is in line buffer MOV CX,BX ;Count of bytes REP MOVB ;Enter that byte list RET ;Enter values into memory at a specified address. If the ;line contains nothing but the address we go into "enter ;mode", where the address and its current value are printed ;and the user may change it if desired. To change, type in ;new value in hex. Backspace works to correct errors. If ;an illegal hex digit or too many digits are typed, the ;bell is sounded but it is otherwise ignored. To go to the ;next byte (with or without change), hit space bar. To ;back up to a previous address, type "-". On ;every 8-byte boundary a new line is started and the address ;is printed. To terminate command, type carriage return. ; Alternatively, the list of bytes to be entered may be ;included on the original command line immediately following ;the address. This is in regular LIST format so any number ;of hex values or strings in quotes may be entered. ENTER: MOV CX,5 ;5 digits in address CALL GETHEX ;Get ENTER address CALL GETSEG ;Convert to seg/disp format ;Adjust segment and displacement so we are in the middle ;of the segment instead of the very bottom. This allows ;backing up a long way. SUB AH,8 ;Adjust segment 32K down ADD DH,80H ; and displacement 32K up PUSH AX ;Save for later PUSH DX CALL SCANB ;Any more parameters? JNZ GETLIST ;If not end-of-line get list POP DI ;Displacement of ENTER POP ES ;Segment GETROW: CALL OUTDI ;Print address of entry CALL BLANK ;Leave a space GETBYTE: SEG ES MOV AL,[DI] ;Get current value CALL HEX ;And display it MOV AL,"." CALL OUT ;Prompt for new value MOV CX,2 ;Max of 2 digits in new value MOV DX,0 ;Intial new value GETDIG: CALL IN ;Get digit from user MOV AH,AL ;Save CALL HEXCHK ;Hex digit? XCHG AH,AL ;Need original for echo JC NOHEX ;If not, try special command CALL OUT ;Echo to console MOV DH,DL ;Rotate new value MOV DL,AH ;And include new digit LOOP GETDIG ;At most 2 digits ;We have two digits, so all we will accept now is a command. WAIT: CALL IN ;Get command character NOHEX: CMP AL,8 ;Backspace JZ BS CMP AL,7FH ;RUBOUT JZ BS CMP AL,"-" ;Back up to previous address JZ PREV CMP AL,13 ;All done with command? JZ EOL CMP AL," " ;Go to next address JZ NEXT ;If we got here, character was invalid. Sound bell. MOV AL,7 CALL OUT JCXZ WAIT ;CX=0 means no more digits JP GETDIG ;Don't have 2 digits yet BS: CMP CL,2 ;CX=2 means nothing typed yet JZ GETDIG ;Can't back up over nothing INC CL ;Accept one more character MOV DL,DH ;Rotate out last digit MOV DH,CH ;Zero this digit CALL BACKUP ;Physical backspace JP GETDIG ;Get more digits ;If new value has been entered, convert it to binary and ;put into memory. Always bump pointer to next - location STORE: CMP CL,2 ;CX=2 means nothing typed yet JZ NOSTO ;So no new value to store ;Rotate DH left 4 bits to combine with DL and make a byte value PUSH CX MOV CL,4 SHL DH,CL POP CX OR DL,DH ;Hex is now converted to binary SEG ES MOV [DI],DL ;Store new value NOSTO: INC DI ;Prepare for next location RET EOL: CALL STORE ;Enter the new value  JMP CRLF ;CR/LF and terminate NEXT: CALL STORE ;Enter new value INC CX ;Leave a space plus two for INC CX ; each digit not entered CALL TAB MOV AX,DI ;Next memory address AND AL,7 ;Check for 8-byte boundary JNZ GETBYTE ;Take 8 per line NEWROW: CALL CRLF ;Terminate line JMP GETROW ;Print address on new line PREV: CALL STORE ;Enter the new value ;DI has been bumped to next byte. Drop it 2 to go to previous addr DEC DI DEC DI JP NEWROW ;Terminate line after backing up ;Perform register dump if no parameters or set register if a ;register designation is a parameter. REG: CALL SCANP JZ DISPREG MOV DL,[DI] INC DI MOV DH,[DI] CMP DH,13 JZ FLAG INC DI CALL GETEOL CMP DH," " JZ FLAG MOV DI,REGTAB XCHG AX,DX PUSH CS POP ES MOV CX,REGTABLEN REPNZ SCAW JNZ BADREG OR CX,CX JNZ NOTPC DEC DI DEC DI SEG CS MOV AX,[DI-2] NOTPC: CALL OUT MOV AL,AH CALL OUT CALL BLANK PUSH DS POP ES LEA BX,[DI+REGDIF-2] MOV DX,[BX] CALL OUT16 CALL CRLF MOV AL,":" CALL OUT CALL INBUF CALL SCANB JZ RET3 MOV CX,4 CALL GETHEX1 CALL GETEOL  MOV [BX],DX RET3: RET BADREG: MOV AX,5200H+"B" ;BR ERROR JMP ERR DISPREG: MOV SI,REGTAB MOV BX,AXSAVE MOV CX,8 CALL DISPREGLINE CALL CRLF MOV CX,5 CALL DISPREGLINE CALL BLANK CALL DISPFLAGS JMP CRLF FLAG: CMP DL,"F" JNZ BADREG CALL DISPFLAGS MOV AL,"-" CALL OUT CALL INBUF CALL SCANB XOR BX,BX MOV DX,[FSAVE] GETFLG: MOV SI,DI LODW CMP AL,13 JZ SAVCHG CMP AH,13 JZ FLGERR MOV DI,FLAGTAB MOV CX,32 PUSH CS POP ES REPNE SCAW JNZ FLGERR MOV CH,CL AND CL,0FH MOV AX,1 ROL AX,CL TEST AX,BX JNZ REPFLG OR BX,AX OR DX,AX TEST CH,16 JNZ NEXFLG XOR DX,AX NEXFLG: MOV DI,SI PUSH DS POP ES CALL SCANP JP GETFLG DISPREGLINE: SEG CS LODW CALL OUT MOV AL,AH CALL OUT MOV AL,"=" CALL OUT MOV DX,[BX] INC BX INC BX CALL OUT16 CALL BLANK CALL BLANK LOOP DISPREGLINE RET REPFLG: MOV AX,4600H+"D" ;DF ERROR FERR: CALL SAVCHG ERR: CALL OUT MOV AL,AH CALL OUT MOV SI,ERRMES JMP PRINT SAVCHG: MOV [FSAVE],DX RET FLGERR: MOV AX,4600H+"B" ;BF ERROR JP FERR DISPFLAGS: MOV SI,FLAGTAB MOV CX,16 MOV DX,[FSAVE] DFLAGS: SEG CS LODW SHL DX JC FLAGSET SEG CS MOV AX,[SI+30] FLAGSET: OR AX,AX JZ NEXTFLG CALL OUT MOV AL,AH CALL OUT CALL BLANK NEXTFLG: LOOP DFLAGS RET ;Trace 1 instruction or the number of instruction specified ;by the parameter using 8086 trace mode. Registers are all ;set according to values in save area TRACE: CALL SCANP CALL HEXIN MOV DX,1 JC STOCNT MOV CX,4 CALL GETHEX STOCNT: MOV [TCOUNT],DX CALL GETEOL STEP: MOV [BRKCNT],0 OR B,[FSAVE+1],1 EXIT: MOV [12],BREAKFIX MOV [14],CS MOV [4],REENTER MOV [6],CS DI MOV [64H],REENTER MOV [66H],CS MOV SP,. STACK POP AX POP BX POP CX POP DX POP BP POP BP POP SI POP DI POP ES POP ES POP SS MOV SP,[SPSAVE] PUSH [FSAVE] PUSH [CSSAVE] PUSH [IPSAVE] MOV DS,[DSSAVE] IRET STEP1: JP STEP ;Re-entry point from breakpoint. Need to decrement instruction ;pointer so it points to location where breakpoint actually ;occured. BREAKFIX: XCHG SP,BP DEC [BP] XCHG SP,BP ;Re-entry point from trace mode or interrupt during ;execution. All registers are saved so they can be ;displayed or modified. REENTER: SEG CS MOV [SPSAVE+SEGDIF],SP SEG CS MOV [SSSAVE+SEGDIF],SS XOR SP,SP MOV SS,SP MOV SP,RSTACK PUSH ES PUSH DS PUSH DI PUSH SI PUSH BP DEC SP DEC SP PUSH DX PUSH CX PUSH BX PUSH AX PUSH SS POP DS MOV SP,[SPSAVE] MOV SS,[SSSAVE] POP [IPSAVE] POP [CSSAVE] POP AX AND AH,0FEH MOV [FSAVE],AX MOV [SPSAVE],SP PUSH DS POP ES PUSH DS POP SS MOV SP,STACK MOV [64H],INT MOV AL,20H OUT BASE+2 EI UP CALL CRLF CALL DISPREG DEC [TCOUNT] JNZ STEP1 ENDGO: MOV SI,BPTAB MOV CX,[BRKCNT] JCXZ COMJMP CLEARBP: MOV DX,[SI+BPLEN] LODW PUSH AX CALL GETSEG MOV ES,AX MOV DI,DX POP AX STOB LOOP CLEARBP COMJMP: JMP COMMAND ;Input from the specified port and display result INPUT: MOV CX,4 ;Port may have 4 digits CALL GETHEX ;Get port number in DX INB DX ;Variable port input CALL HEX ;And display JMP CRLF ;Output a value to specified port. OUTPUT: MOV CX,4 ;Port may have 4 digits CALL GETHEX ;Get port number PUSH DX ;Save while we get data MOV CX,2 ;Byte output only CALL GETHEX ;Get data to output XCHG AX,DX ;Output data in AL POP DX ;Port in DX OUTB DX ;Variable port output RET ;Jump to program, setting up registers according to the ;save area. Up to 10 breakpoint addresses may be specified. GO: MOV BX,LINEBUF XOR SI,SI GO1: CALL SCANP JZ EXEC MOV CX,5 CALL GETHEX MOV [BX],DX MOV [BX-BPLEN+1],AH INC BX INC BX INC SI CMP SI,BPMAX+1 JNZ GO1 MOV AX,5000H+"B" ;BP ERROR JMP ERR EXEC: MOV [BRKCNT],SI CALL GETEOL MOV CX,SI JCXZ NOBP MOV SI,BPTAB SETBP: MOV DX,[SI+BPLEN] LODW CALL GETSEG MOV DS,AX MOV DI,DX MOV AL,[DI] MOV B,[DI],0CCH PUSH ES POP DS MOV [SI-2],AL LOOP SETBP NOBP: MOV [TCOUNT],1 JMP EXIT ;Console input interrupt handler. Used to interrupt commands ;or programs under execution (if they have interrupts ;enabled). Control-S causes a loop which waits for any other ;character to be typed. Control-C causes abort to command ;mode. All other characters are ignored. INT: PUSH AX ;Don't destroy accumulator ;Output End-of-Interrupt commands to slave 8259A. This ;wouldn't be necessary if Automatic End of Interrupt mode  ;worked like it was supposed to! MOV AL,20H OUT BASE+2 IN DATA ;Get interrupting character AND AL,7FH ;ASCII has only 7 bits CMP AL,"S"-"@" ;Check for Control-S JNZ NOSTOP CALL IN ;Wait for continue character NOSTOP: CMP AL,"C"-"@" ;Check for Control-C JZ BREAK ;Just ignore interrupt - restore AX and return POP AX IRET BREAK: CALL CRLF JMP COMMAND REGTAB: DB "AXBXCXDXSPBPSIDIDSESSSCSIPPC" REGDIF: EQU AXSAVE-REGTAB ;Flags are ordered to correspond with the bits of the / flag ;register, most significant bit first, zero if bit is not ;a flag. First 16 entries are for bit set, second 16 for ;bit reset. FLAGTAB: DW 0 DW 0 DW 0 DW 0 DB "OV" DB "DN" DB "EI" DW 0 DB "NG" DB "ZR" DW 0 DB "AC" DW 0  DB "PE" DW 0 DB "CY" DW 0 DW 0 DW 0 DW 0 DB "NV" DB "UP" DB "DI" DW 0 DB "PL" DB "NZ" DW 0 DB "NA" DW 0 DB "PO" DW 0 DB "NC" ;Initialization table. First byte of each entry is no. ;of bytes to output to the corresponding port. That ;many initialization bytes follow. INITTABLE: ;Port BASE+0 - Master 8259A. Intialization Command Word (ICW) ;One sets level-triggered mode, multiple 8259As, require ;ICW4. DB 1 DB 19H ;Port BASE+1 - Master 8259A. ICW2 sets vector base to 10H ;ICW3 sets a slave on interrupt input 1; ICW4 sets buffered ;mode, as a master, with Automatic End of Interrupt, 8086 ;vector; Operation Command Word (OCW) One sets interrupt ;mask to enable line 1 (slave 8259A) only. DB 4 DB 10H,2,0FH,0FDH ;Port BASE+2 - Slave 8259A. ICW1 sets level-triggered mode, ;multiple 8259As, require ICW4. DB 1 DB 19H ;Port BASE+3 - Slave 8259A. ICW2 sets vector base to 18H ;ICW3 sets slave address as 1; ICW4 sets buffered mode, ;as slave, with Automatic End of Interrupt (which doesn't ;work in slaves), 8086 vector; OCW1 sets interrupt mask ;to enable line 1 (serial receive) only. DB 4 DB 18H,1,0BH,0FDH ;Port Base+4 - 9513 Data. 9513 has previously been set ;up for Counter 5 mode register with auto increment. Thus ;mode is set to 0B63H, which is no gating, count source is ;F1 (4 MHz), reload from load or hold, count down repetitively ;in binary, with output toggle. Load register is set to ;0007H, and Hold register is set to 0006H. Thus we ;alternately divide by 7 and 6, which is divided by 2 by ;the output toggle, thus providing a square wave of ;4 MHz/13 = 307.7 kHz, which divided by 16 in the 8251A ;provides 19,230 baud (0.16% high). DB 6 DB 63H,0BH,7,0,6,0 ;Port BASE+5 - 9513 Control. Load and arm counter 5, ;enabling baud rate generation. Then select counter ;5 mode register, in case baud rate wasn't right. DB 2 DB 70H,5 ;Port BASE+6 - 8251A Data. No initialization to this port. DB 0 ;Port BASE+7 - 8251A Control. Since it is not possible to ;know whether the 8251A next expects a Mode Instruction or ;a Command Instruction, a dummy byte is sent which could ;safely be interpreted as either but guarantees it is now ;expecting a Command. The command sent is Internal Reset ;which causes it to start expecting a mode. The mode sent ;is for 2 stop bits, no parity, 8 data bits, 16X clock. ;This is followed by the command to error reset, enable ;transmitter and receiver, set RTS and DTR to +12V. DB 4 DB 0B7H,77H,0CEH,37H HEADER: DM 13,10,10,"SCP 8086 Monitor 1.5",13,10 SYNERR: DB '^' ERRMES: DM " Error",13,10 BACMES: DM 8,32,8 ;Disk boot. Select one of the following routines by ;setting the equates at the start of this program. BOOT: PUSH DI ; ;************************************************ ; ; Boot for SCP, Cromemco 4FDC, or 16FDC disk controller with either ; large or small disks. Loads track 0, sector 1 into LOAD. ; IF SCP+CROMEMCO4FDC+CROMEMCO16FDC IF SCP DISK: EQU 0E0H ENDIF IF CROMEM0 CO4FDC+CROMEMCO16FDC DISK: EQU 30H MOV AL,0FFH ; Select side 0. OUTB 4 ENDIF IF SCP+CROMEMCO16FDC INB BASE+15 ; Get sense switch data. MOV CL,3 ; Shift bit 1 over to bit 4 so it ROL AL,CL ; can be used for large/small bit. AND AL,10H ; Keep only the large/small bit. ENDIF IF CROMEMCO16FDC OR AL,21H ; Add the rest of the drive-select bits. ENDIF  IF SCP+CROMEMCO16FDC MOV BH,AL ; Put drive select byte in BH. ENDIF IF CROMEMCO4FDC MOV BH,21H ; Drive-select byte. ENDIF IF CROMEMCO16FDC MOV AL,30H ; Turn on disk motors and OUT DISK+4 ; wait one second. MOV CX,43716 MOTORDELAY: AAM ; 83 clocks. AAM ; 83 clocks. LOOP MOTORDELAY ; 17 clocks. ENDIF RETRY: MOV AL,0D0H ; Force-interrupt command. OUTB DISK MOV CX,5 DELAY: AAM ; Give force-interrupt time. LOOP DELAY IF SCP XOR BH,08H ; If SCP, flip density bit. ENDIF IF CROMEMCO16FDC XOR BH,40H ; If 16FDC, flip density bit. ENDIF IF CROMEMCO4FDC XOR BH,10H ; If 4FDC, flip disk size bit. ENDIF MOV AL,BH IF SCP OR AL,80H ; Turn on RESTORE for PerSci's. ENDIF OUTB DISK+4 MOV DI,LOAD MOV AL,0FH ; Restore command. OUTB DISK HOME: INB DISK+4 ; Wait for INTRQ. ROR AL JNC HOME  IF SCP MOV AL,BH ; Turn RESTORE off. OUTB DISK+4 ENDIF INB DISK ; Read status. AND AL,98H JNZ RETRY ; Retry if bad. MOV AL,1 ; Ask for sector 1. OUTB DISK+2 IF CROMEMCO4FDC+CROMEMCO16FDC MOV AL,BH OR AL,80H ; Turn on auto-wait. OUTB DISK+4 ENDIF MOV DX,DISK+3 ; Disk controller data port. MOV AL,8CH ; Read command. OUTB DISK JMPS READ READLOOP: STOB ; Put data in memory. READ: IF SCP INB DISK+5 ; Wait for DQR or INTRQ. ENDIF IF CROMEMCO4FDC+CROMEMCO16FDC INB DISK+4 ENDIF ROR AL ; Check for INTRQ. INB DX ; Read data from disk controller chip. JNC READLOOP ; Jump if not. INB DISK ; Get status. AND AL,9CH JNZ RETRY ; Jump if error. ENDIF ; ;*************************************************** ; ; Boot for North Star disk, single density. ; Loads track 0, sector 0 into address LOAD ; IF NORTHSTARSD ; ; Disk command equates ; SEL: EQU 1 STP1: EQU 9 STP2: EQU 8 NOP: EQU 10H SEC: EQU 14H STPOUT: EQU 1CH RD: EQU 40H BST: EQU 20H PUSH DS MOV AX,0FEB8H MOV DS,AX MOV AL,[SEL] MOV CX,20 MOTOR: CALL SECTOR LOOP MOTOR CHKTRK: TEST B,[STPOUT],1 JNZ ONTRACK MOV AL,[STP1] AAM MOV AL,[STP2] CALL SECTOR CALL SECTOR JP CHKTRK SECTOR: MOV AL,[SEC] ; Reset sector flag. SECLP: TEST B,[NOP],80H ; Wait for sector flag. JZ SECLP RET ONTRACK: MOV DI,LOAD MOV CX,280 MOV BX,RD+NOP GETSEC: CALL SECTOR TEST B,[BST+NOP],0FH ; Test for sector zero. JNZ GETSEC GETSYNC: TEST B,[NOP],4 LOOPZ GETSYNC JZ ONTRACK MOV CX,100H XOR DL,DL AAD READ: MOV AL,[BX] STOB ;Uses ES XOR DL,AL ROL DL  AAD LOOP READ MOV AL,[BX] CMP AL,DL JNZ ONTRACK POP DS ENDIF ; ; ******************************************************************** ; ; Boot for Tarbell disk controllers. Load track 0, sector 1 into LOAD. ; IF TARBELLSD+TARBELLDD DISK: EQU 78H IF TARBELLDD SUB AH,AH ; Initial drive-select byte = 0. ENDIF JP RETRY DCOM: OUTB DISK MOV AL,50 1 HOLD: DEC AL JNZ HOLD RET RETRY: MOV AL,0D0H CALL DCOM READY: INB DISK ROR AL JC READY IF TARBELLDD XOR AH,08H ; Try other density. MOV AL,AH OUTB DISK+4 ENDIF MOV DI,LOAD MOV AL,0EH ; Home command @ 10ms/track CALL DCOM INB DISK+4 INB DISK AND AL,98H JNZ RETRY MOV AL,1 OUTB DISK+2 MOV DX,DISK+3 ; Disk controller data port. MOV AL,8CH CALL DCOM JMPS READ READLOOP: STOB ; Put in memory. READ: INB DISK+4 ; Wait for DRQ or INTRQ. ROL AL ; Check for INTRQ. INB DX ; Read data from disk controller chip. JC READLOOP ; Jump if not. INB DISK ; Read status. AND AL,9CH JNZ RETRY ENDIF ;*************************************************** IF OTHER ;User may insert customized disk boot here. All ;registers are available, stack pointer is valid ;and interrupts are enabled. Stack should be at ;same level on fall-through to code below. Last ;address available is 07DF hex. ORG 7E0H ;Simulate boot of maximum length ENDIF ;*************************************************** ;Successful read MOV [CSSAVE],0 MOV [IPSAVE],LOAD POP DI JMP GO  for INTRQ. INB DX ; Read data from disk controller chip. JC READLOOP ; Jump if not. INB DISK ; Read status. ANMZ os2،mo!"t  !3@! 'v!"t  !s.: !f !"y " !u} ! ! !!!!:.t"u  !ň. |~w LD D* !!$ !!$ ( 1) Copy one character from template $< > ( 2) Skip over one character in template $< > ( 3) Copy up to specified character $< > ( 4) Skip up to specified character $< > ( 5) Copy rest of template $< > ( 6) Kill line with no change in template (Ctrl-X) $< > ( 7) Cancel line and update template $< > ( 8) Backspace (same as Ctrl-H) $< > ( 9) Enter insert mode2  $< > (10) Exit insert mode $< > (11) Represent escape character $ ^ |_ 2nd char present escape char --> hexadecimal $MSDOS SYS MSDOS.SYS not found or deflicted$ Unsuccessful Read$ Select menu item number $ More selections? (Y/N)$ Press function key to $>>$ Aw C'mon! Re-enter (0-11)$ Continue ? (Y/N)$ Input escape character differs from that on the MSDOS system file$ Re-enter function key$ $ Save on disk? (Y/N)$:1A010000E92B00E91102E92602E9AC02E9B402E9BC02E9C202E93603E949E6 :1A011A0003E9C402E9A101E95E01E92301E91E02E90000CB33ED8ED5BC003D :1A01340004FAC74664D9018C4E66FB0E1FBEE600B90400FCACE6F5ACE6F496 :1A014E00ACE6F4E2F5B90400ACE611E2FBACE618B90400ACE613E2FBACE682 :1A01680019BE0008B892008EC02BFFB90010F3A5BEFD04BA01009A000092D5 :1A018200002BEDC78694009203C78698009E03BA0001B41ACD218B0E060039 :1A019C008CDB8CC88ED8BA2501B40FCD210AC0752F33C0A34601A348014020 :1A01B600A33301B427CD21E31DA80174198EDB8EC38ED3BC5C0033C050BA29 :1A01D0008000B41ACD2153B8000150CBBAFC00B409CD21FBEBFE17F38401DE :1A01EA003801023800030800B7774E3705B7774E370E0D0A4572726F7220C3 :1A020400696E206C6F6164696E6720436F6D6D616E6420496E746572707228 :13021E00657465720D0A2401434F4D4D414E4420434F4DE3 :1A024A00B0A7E6F5B0E0E6F5B019E6F5E80F0092E80B0091E4F48AE0E4F432 :1A02640086C4EB6AE802008ACCE4F48AE0D0ECD0ECD0ECD0EC240FD50A8AD3 :1A027E00E08AC1C35152E81600B043E6F55A59E81100B043E6F5E80600B0F1 :1A02980027E6F5EB3533C98BD1B009E80400B00A8BD1E6F58AC2E802008A7C :1A02B200C6D40AD0E4D0E4D0E4D0E40AC4E6F4C392B00BE6F592E6F48AC471 :1A02CC00E6F4B044E6F550B01FE6F558CB5056B020E6F2E4F6247F3C0374D4 :1A02E600083C1374043C0675059A270040002E8B366602E85D002E3B3664D3 :1A03000002740B2E88042E893666025E58CFB0079A09004000EBF456FA2ED7 :1A031A008B3664022E3B3666027407E831002EAC0BF6FB5ECB9A030040002B :1A03340074F956FA2E8B366402E819002E89366402FB5ECBFA2EC7066602C8 :1A034E0068022EC70664026802FBCB4681FEB8027203BE6802C368026802E7 :1A03B80050E4F7240174FA58E6F6CB50E411240174FA58E610CBE413240260 :1A03D20074FAE412CB50E413240174FA58E612CBB4002E3A06EA0475115007 :1A03EC00E4782420587409B401BB30032ED7F8CBE83800989681C63003B996 :1A0406000400B4002E8A04BBEB042ED7E67CB0C4E8C2012498E47B740DF6A6 :1A042000D42E803401E2E3B80200F9CB2EACF8CB01030507090B0D8AE02E62 :1A043A008606EA043AC4740BE479E67BB010E890018AC4C3E86E00722206B9 :1A0454008CDB8EC3E8FC007217FEC6E2F7F807CBE85800720CE828017207BA :1A046E00FEC6E2F7F8CB07B3FF2E881DBE8B03FEC32EAC84E074F88AC3D0B4 :1A048800E0F9CB40800802102007E81A0072119A15004000EB0AE80E0072E4 :1A04A200059A180040002EC606EA04FFCB2EC606EA04FF53519A1B00400017 :1A04BC00595BC38BF3988BD8D0E8E86EFF2E8A87EB04E67C8AE092B21AF6DB :1A04D600C6087402B210F6F292FEC62E8A9FF30481C3FB048BFB8AC22E86B1 :1A04F00005E6793AC274C7B7023CFF7505E8C30072138AC2E67BB01CE8D088 :1A050A0000249874AF7804FECF75E88AE0A880F975A2B402C38ADEF6C4080D :1A05240074198AC4E67C82FB08762382EB0882FB08770D8AC40C40E67CEBFD :1A053E001382FB1A760EFEC2B058E890002EFE05B6018ADE8AC3E67AC3E88D :1A055800C5FFB30A87F752BA7B00B080FAE6783 8BEFEB01AAE47CD0E0EC72FD :1A057200F8FBE86A00249C740B8BFD8AF8FECB75DF8AE7F95A87FEB0D0E615 :1A058C0078B00AFEC875FCC3E888FFB30A52BA7B00B0A0FAE6788BEEE47CF5 :1A05A600D0E0ACEE72F8FB4EE8300024FC740B8BF58AF8FECB75E08AE7F9FD :1A05C0005AEBC6B303B00CE81100249874C57809B058E80600FECB75ECF91C :1A05DA00C3E67850D40A58E47CA88075FAE478C3FF000810180008101800EB :1A05F40000010100000101FFFF04000A05001405010A050114058000040110 :10060E0000024400D207000401010002C000D00421 :0000000000 EB :1A05F40000010100000101FFFF04000Aemplate 3. Copy up to specified character 4. Skip up to specified character 5. Copy rest of template 6. Kill line with no change in template (Ctrl-X) 7. Cancel line and update template 8. Backspace (same as Ctrl-H) 9. Enter Insert Mode 10.Exit Insert Mode 11.Escape sequence to represent escape character If, for example, you have a terminal such as the Televideo 950 which has eleven function keys all commands for the editing template can be programmed. For terminals such as the ADDS Viewpoint which only has three funcion keys, you can program the most useful editing template commands such #3,#4,and #5 above. The lesser used commands can be utilized by such a terminal but not in such a convenient manner. To program the remaining functions you must enter the actual escape sequence when prompted. This procedure will be described in the discussion to follow.  BACKGROUND: If your terminal has special function keys (commonly located on the top row of the keyboard as F1,F2,...) then you can have these keys programmed to edit the editing template. When a special function key is depressed by the user a sequence of characters are sent to the to the computer's input system. These characters usually come in bursts of two or three characters. MS-DOS uses only the first two characters as a legal escape sequence for working with the editing template and disposes of the third character if there is one. For example, the Televideo 925 and 950 terminals send the following character sequence when a function key F1 is depressed: first char > ASCII 1 (Ctrl-A) second char > ASCII 64 (@) third char > ASCII 13 (carriage return) The first character is denoted by MS-DOS as the "escape character". The second character represents the function to be performed. The third character is disposed by the operating system. Note: If you have a Televideo terminal you must make a simple modification to the IO.ASM file and reassemble it to tell the operating system that you want the third character in the sequence to be disposed otherwise the function keys will not work as expected after programming them. See the appendix to this document on editing the IO.ASM file to accomplish this task. For the Zenith Data Systems terminals, the function key F1 emits the following character sequence: first char > ASCII 21 (ESC key) second char > ASCII 83 (S) RUNNING FUNKEY.EXE To invoke FUNKEY.EXE simply type FUNKEY at the current drive. If the file is found on the disk it will begin running the program as follows: _______________________________________________________________________________ FUNCTION KEY MENU ( 0) End program ( 1) Copy one character from template ( 2) Skip over one character in template ( 3) Copy up t4 o specified character ( 4) Skip up to specified character ( 5) Copy rest of template ( 6) Kill line with no change in template (Ctrl-X) ( 7) Cancel line and update template ( 8) Backspace (same as Ctrl-H)

( 9) Enter insert mode (10) Exit insert mode (11) Escape sequence to represent escape character ^ |_ 2nd char present escape char --> 1B hexadecimal Select menu item number _______________________________________________________________________________ The present escape character is a 1B hexadecimal (i.e. the ESC character). The second character is displayed on the left column enclosed in brackets. These characters will most likely change as you request different function keys that represent different characters. EXAMPLE SESSION (using a Televideo 950 terminal): (user responses quoted for distinction) ______________________________________________________________________________ Select menu item number "5" Press function key to Copy rest of template >>"press a function key here. Most function keys other than those of the Televideo will not emit a third character and the user must follow the function key by a carriage return. NOTE THAT THE ESCAPE SEQUENCE WILL NOT BE ECHOED TO THE SCREEN! If you do not have all the function keys needed you can manually input the character sequence and then retype this sequence when you need to use that function" Input escape character differs from that on the MSDOS system file Continue (Y/N)? "Y" "This will change the default escape character" More selections? (Y/N) "Y" _______________________________________________________________________________ Continue the process until all of your function keys are programmed as desired. NOTES: 1. You must note that the escape character must be the same throughout the session or the functions keys will not all work as expected. 2. At any time during the session you can abort by typing Ctrl-C. You may need to type this one or more times to get out. 3. If your wish the changes to be stored on the system of your disk be sure to respond to the "Save on disk? (Y/N) " prompt with a "Y" for yes. Otherwise the changes will remain in effect only until the system is rebooted.  APPENDIX (Modifying the IO.ASM file to accomodate Televideo function keys) To modify IO.ASM use Perfect Writer to insert this portion of code into the appropriate place in the file. Follow the following procedure: 1. extract the code to follow and create its own file; call it PATCHIO.ASM 2. edit IO.ASM (PW IO.ASM) 3. find "; Console keyboard interrupt handler." 4. insert PATCH.ASM here 5. delete the corresponding previous code 6. end edit session 7. If the disk is in drive A: then type ASM IO.AAZ otherwise if it is in drive B: then type ASM IO.BBZ 8. HEX2BIN IO 9. INSTALL IO.COM (install the new IO system) 10. FUNKEY (run the function key programmer) ;---------------------------------------------------------------------------- ; PATCH TO HANDLE TELEVIDEO FUNCTION KEYS ; Console keyboard interrupt handler. ; KBINT: PUSH AX PUSH SI MOV AL,20H ;End of Interrupt command OUT BASE+2 ;Send to slave IN DATA ;Get the character AND AL,7FH CMP AL,"5 C"-"@" JZ FLSH CMP AL,"S"-"@" JZ FLSH CMP AL,"F"-"@" JZ FLSH CMP AL,"A"-"@" ; IF FUNCTION KEY STRUCK JNZ SAVKY ; SEG CS ; MOV B,[SOHFLAG],03H ; SET SOHFLAG JMPS SAVKY ; ENDIF FLSH: CALL JFLUSH,BIOSSEG ; Call I/O system keyboard buffer flush. SAVKY: SEG CS MOV SI,[REAR] ;Pointer to rear of queue CALL INCQ SEG CS CMP SI,[FRONT] ;Any room in queue? JZ QFULL SEG CS CMP B,[SOHFLAG],-1 JE PUTINCHARACTER SEG CS DEC B,[SOHFLAG] JNZ PUTINCHARACTER SEG CS  DEC B,[SOHFLAG] CMP AL,0DH JNZ PUTINCHARACTER JMPS LEAVINT PUTINCHARACTER: SEG CS MOV [SI],AL ;Put character in queue SEG CS MOV [REAR],SI ;Save pointer LEAVINT: POP SI POP AX IRET QFULL: MOV AL,7 ; BELL character. CALL JOUT,BIOSSEG ; Call I/O system console output function. JMPS LEAVINT STATUS: PUSH SI DI ; Disable interrupts while checking queue. SEG CS MOV SI,[FRONT] SEG CS CMP SI,[REAR] ; Anything in queue? JZ NOCHR ; Jump if nothing in queue. CALL INCQ SEG CS LODSB ;Get character (if there is one) OR SI,SI ;Reset zero flag NOCHR: EI POP SI RET L ;Zero clear if we have a character INP: CALL JSTAT,BIOSSEG ; Get I/O system console input status. JZ INP PUSH SI DI ; Disable interrupts while changing queue pointers. SEG CS MOV SI,[FRONT] CALL INCQ ; Permanently remove char from queue SEG CS MOV [FRONT],SI EI POP SI RET L FLUSH: DI SEG CS MOV [REAR],QUEUE SEG CS MOV [FRONT],QUEUE EI RET L INCQ: INC SI CMP SI,ENDQ ;Exceeded length of queue? JB RET MOV SI,QUEUE RET FRONT: DW QUEUE REAR: DW QUEUE QUEUE: DS QSIZE ENDQ: EQU $ SOHFLAG: DB -1 ENDIF ;_____________________________________________________________________________   CMP SI,ENDQ ;Exceeded length of queue? JB RET MOV SI,QUEUE RET FRONT: DW QUEUE REAR: DW QUEUE QUEUE: DI 83 (S) RUNNING FUNKEY.EXE To invoke FUNKEY.EXE simply type FUNKEY at the current drive. If the file is found on the disk it will begin running the program as follows: _______________________________________________________________________________ FUNCTION KEY MENU ( 0) End program ( 1) Copy one character from template ( 2) Skip over one character in template ( 3) Copy up to specified character ( 4) Skip up to specified character ( 5) Copy rest of template ( 6) Kill line with no change in template (Ctrl-X) ( 7) Cancel line and update template ( 8) Backspace (same as Ctrl-H)

( 9) Enter insert mode (10) Exit insert mode (11) Escape sequence to represent escape character ^ |_ 2nd char present escape char --> 1B hexadecimal Select menu item number _______________________________________________________________________________ The present escape character is a 1B hexadecimal (i.e. the ESC character). The second character is displayed on the left column enclosed in brackets. These characters will most likely change as you request different function keys that represent different characters. EXAMPLE SESSION (using a Televideo 950 terminal): (user responses quoted for distinction) ______________________________________________________________________________ Select menu item number "56 " Press function key to Copy rest of template >>"press a function key here. Most function keys other than those of the Televideo will not emit a third character and the user must follow the function key by a carriage return. NOTE THAT THE ESCAPE SEQUENCE WILL NOT BE ECHOED TO THE SCREEN! If you do not have all the function keys needed you can manually input the character sequence and then retype this sequence when you need to use that function" Input escape character differs from that on the MSDOS system file Continue (Y/N)? "Y" "This will change the default escape character" More selections? (Y/N) "Y" _______________________________________________________________________________ Continue the process until all of your function keys are programmed as desired. NOTES: 1. You must note that the escape character must be the same throughout the session or the functions keys will not all work as expected. 2. At any time during the session you can abort by typing Ctrl-C. You may need to type this one or more times to get out. 3. If your wish the changes to be stored on the system of your disk be sure to respond to the "Save on disk? (Y/N) " prompt with a "Y" for yes. Otherwise the changes will remain in effect only until the system is rebooted. APPENDIX (Modifying the IO.ASM file to accomodate Televideo function keys) To modify IO.ASM use Perfect Writer to insert this portion of code into the appropriate place in the file. Follow the following procedure: 1. extract the code to follow and create its own file; call it PATCHIO.ASM 2. edit IO.ASM (PW IO.ASM) 3. find "; Console keyboard interrupt handler." 4. insert PATCH.ASM here 5. delete the corresponding previous code 6. end edit session 7. If the disk is in drive A: then type ASM IO.AAZ otherwise if it is in drive B: then type ASM IO.BBZ 8. HEX2BIN IO 9. INSTALL IO.COM (install the new IO system) 10. FUNKEY (run the function key programmer) ;---------------------------------------------------------------------------- ; PATCH TO HANDLE TELEVIDEO FUNCTION KEYS ; Console keyboard interrupt handler. ; KBINT: PUSH AX PUSH SI MOV AL,20H ;End of Interrupt command OUT BASE+2 ;Send to slave IN DATA ;Get the character AND AL,7FH CMP AL,"C"-"@" JZ FLSH CMP AL,"S"-"@" JZ FLSH CMP AL,"F"-"@" JZ FLSH CMP AL,"A"-"@" ; IF FUNCTION KEY STRUCK JNZ SAVKY ; SEG CS ; MOV B,[SOHFLAG],03H ; SET SOHFLAG JMPS SAVKY ; ENDIF FLSH: CALL JFLUSH,BIOSSEG ; Call I/O system keyboard buffer flush. SAVKY: SEG CS MOV SI,[REAR] ;Pointer to rear of queue CALL INCQ SEG CS CMP SI,[FRONT] ;Any room in queue? JZ QFULL SEG CS CMP B,[SOHFLAG],-1 JE PUTINCHARACTER SEG CS DEC B,[SOHFLAG] JNZ PUTINCHARACTER SEG CS DEC B,[SOHFLAG] CMP AL,0DH JNZ PUTINCHARACTER JMPS LEAVINT PUTINCHARACTER: SEG CS MOV [SI],AL ;Put character in queue SEG CS MOV [REAR],SI ;Save pointer LEAVINT: POP SI POP AX IRET QFULL: MOV AL,7  ; BELL character. CALL JOUT,BIOSSEG ; Call I/O system console output function. JMPS LEAVINT STATUS: PUSH SI DI ; Disable interrupts while checking queue. SEG CS MOV SI,[FRONT] SEG CS CMP SI,[REAR] ; Anything in queue? JZ NOCHR ; Ju7 mp if nothing in queue. CALL INCQ SEG CS LODSB ;Get character (if there is one) OR SI,SI ;Reset zero flag NOCHR: EI POP SI RET L ;Zero clear if we have a character INP: CALL JSTAT,BIOSSEG ; Get I/O system console input status. JZ INP PUSH SI DI ; Disable interrupts while changing queue pointers. SEG CS MOV SI,[FRONT] CALL INCQ ; Permanently remove char from queue SEG CS MOV [FRONT],SI EI POP SI RET L FLUSH: DI SEG CS MOV [REAR],QUEUE SEG CS MOV [FRONT],QUEUE EI RET L INCQ: INC SI CMP SI,ENDQ ;Exceeded length of queue? JB RET MOV SI,QUEUE RET FRONT: DW QUEUE REAR: DW QUEUE QUEUE: DS QSIZE ENDQ: EQU $ SOHFLAG: DB -1 ENDIF ;_____________________________________________________________________________  of queue? JB RET MOV SI,QUEUE RET FRONT: DW QUEUE REAR: DW QUEUE QUEUE: DL POPF RET L INP: MOV AL,-1 SEG CS XCHG AL,[QUEUE] ; Remove the character from the buffer. AND AL,AL JNS INRET ECT_1+2],AL MOV BYTE PTR [B1POKE_MAXSECT_2+2],AL IF CROMEMCO CALL DRIVESELECT ; Get drive-select byte. AND AL,0F0H ; Force all drive-selects off. OR AL,81H ; Turn on auto-wait bit & select drive 0. MOV BYTE PTR [B1POKE_DRIVESELECT+1],AL ENDIF MOV AX,[SI+15] ; Poke sector size into boot sector. MOV WORD PTR [B1POKE_SECSIZE+2],AX MOV BX,OFFSET BOOT1SIDED WRITE_BOOT: MOV AL,[DRIVE] ; Which drive to write the boot to. MOV CX,1 ; One sector. MOV DX,0 ; Write sector number 0. INT 38 ; Call I/O system to write boot sector. POP DX ; Pop the original flags off the stack. MOV DX,OFFSET BOOT_ERROR ; Error message in case it didn't work. JC ERROR RET ERROR: MOV AH,9 ; Function 9, print string. INT 33 STC RET BADSECTOR: SUB AX,AX ; No bad sectors, no errors. RET DONE: CLC ; No errors. RET IF CROMEMCO16FDC ; ; Subroutine to wait for spindle motor to come up to speed if it's off. ; MOTORON: IN AL,DISK+4 ; Get "disk flags". TEST AL,08H ; See if motor on. LAHF ; Save Z flag for below. CALL DRIVESELECT ; Get drive-select byte. OUT DISK+4,AL ; Send it out turning motors on. SAHF ; Get Z flag back from above. JNZ MOTOR_RETURN ; If motors already on, don't wait. MOV CX,43716 ; Loop count, 1 second for loop below. MOTORDELAY: ; (8 MHz, 16-bit memory). AAM ; 83 clocks. AAM ; 83 clocks. LOOP MOTORDELAY ; 17 clocks. MOTOR_RETURN: RET ENDIF ; ; Set SI to point to a table of data for the selected disk format. ; ; [SI+0] = number of sectors/track ; [SI+1] = number of tracks (or cylindars) on disk. ; [SI+2] = number of bytes from beginning of format pattern track number is. ; [SI+4] = number of bytes in pattern for each sector. ; [SI+6] = address of pattern for index address mark. ; [SI+8] = address of pattern for each sector. ; [SI+10] = FATID byte for this format. ; [SI+11] = STARTSECTOR for this format. ; [SI+13] = number of sectors boot sector should load. ; [SI+15] = number of bytes/sector. ; TABLE:  IF SCP+CROMEMCO SUB AH,AH ; Zero out bit pattern for indexing. CALL DRIVESELECT ; Get drive-select byte. IF SCP NOT AL ; Make LARGE-bit instead of SMALL-bit. ENDIF AND AL,SMALLBIT ; See if small disk. JZ SIZEOK ; Jump if small. END8 IF MOV AH,02H ; Turn on bit 1 if 8-inch disk. SIZEOK: IF SCP+TARBELLDOUBLE+CROMEMCO16FDC TEST BYTE PTR [SWITCHMAP],D_BIT JZ DENSITYOK ; Jump if single-density. OR AH,01H ; Turn on bit 0 if single-density. DENSITYOK: ENDIF MOV AL,AH  ; Computed number to AL. MOV AH,17 ; And multiply by number of bytes in table. MUL AH ; AX = AL*AH. ADD AX,OFFSET DATATABLE ; Add the address of the table. MOV SI,AX RET DRIVESELECT: MOV AL,[DRIVE] ; Get drive-select byte for drive [DRIVE]. MOV BX,OFFSET DRVTAB XLAT IF SCP+CROMEMCO16FDC+TARBELLDOUBLE TEST BYTE PTR [SWITCHMAP],D_BIT ; See if double-density. JZ SPUTNIK ; Jump if not. OR AL,DDENBIT ; Turn on double-density. SPUTNIK: ENDIF RET PUTSEC: PUSH DX MOV CH,[SI] ; CH = number of sectors. MOV DX,[SI+4] ; DX = number of bytes in sector pattern. SEC: MOV [BX],AL ; Poke number in sector ID. ADD BX,DX ADD AL,CL ; Increment sector, side, or track number. DEC CH JNZ SEC POP DX RET MAKE: PUSH SI MOV SI,DX MAKELOOP: CLD ; a.k.a. "UP" LODSB ; Get byte count. OR AL,AL ; Return if zero. JZ MAKERETURN MOV CH,AL ; Count to CH. LODSB ; Get byte for pattern. PUTPAT: MOV [BX],AL ; Put byte in pattern. INC BX DEC CH JNZ PUTPAT JMP SHORT MAKELOOP MAKERETURN: POP SI RET ; ; Subroutine to restore head to track 0. ; On return, Z flag set if errors. ; RESTORE: IF SCP*FASTSEEK CALL DRIVESELECT TEST AL,SMALLBIT ; See if small disk. JNZ NORMALRESTORE ; Normal (step) restore for small disks. OR AL,80H ; Turn on RESTORE bit for PerSci drive. OUT DISK+4,AL SCWAIT: IN AL,DISK+4 ; Loop here till Seek Complete goes active. TEST AL,40H JZ SCWAIT CALL DRIVESELECT ; Turn RESTORE off. OUT DISK+4,AL SUB AL,AL ; Tell 1793 which track the head is on. OUT DISK+1,AL JMP SHORT RESTORE_RETURN ; Z flag set SUB AL,AL above (no error) NORMALRESTORE: ENDIF MOV AL,08H+STPSPD ; Restore without verify CALL DCOM TEST AL,098H ; Check for errors, Not Ready, RESTORE_RETURN: ; Seek Error, CRC Error. RET TRACK: IF CROMEMCO CALL DRIVESELECT ; Get drive-select byte.  OR AL,80H ; Turn on auto-wait. OUT DISK+4,AL ENDIF MOV DI,SI ; Save SI (pointer to DATATABLE). MOV SI,OFFSET PATTERN MOV BP,DX ; Save DX. MOV DX,DISK+3 ; Disk controller data port. MOV AL,0F4H ; Write Track command. CLI ; Interrupts off. OUT DISK,AL WRTLP: IF SCP IN AL,DISK+5 ; Wait for DRQ or INTRQ. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4  ENDIF IF TARBELL SHL AL,1 LODSB ; Get a byte of the pattern. OUT DX,AL ; Send to controller. JC WRTLP ENDIF  IF SCP+CROMEMCO SHR AL,1 LODSB ; Get a byte of the pattern. OUT DX,AL ; Send to controller. JNC WRTLP ENDIF STI ; Interrupts back on. MOV DX,BP ; Restore DX. MOV SI,DI ; Restore SI. CALL WAIT ; Wait till status ready. AND AL,0E4H ; Accept "not ready", "write protect", "write JZ WRITERET ; fault", & "lost data" errors. STC ; Set CY flag if error. WRITERET: RET DCOM: OUT DISK,AL AAM ; 10 Microsecond delay. WAIT: IN AL,DISK+4 TEST AL,DONEBIT IF SCP+CROMEMCO JZ WAIT ENDIF IF TARBELL JNZ WAIT ENDIF IN AL,DISK ; Get status from disk. RET DATATABLE: DB 18 9  ; Number of sectors/track 5.25-inch SD/SS. DB 40 ; Number of tracks/disk 5.25-inch SD/SS format. DW 11 ; Beginning of pattern to first ID mark. DW 168 ; Number of bytes/sector in pattern. DW OFFSET SSINDEX ; Address of index pattern 5.25-inch SD/SS. DW OFFSET SSSECTOR ; Address of sector pattern 5.25-inch SD/SS. DB 0FEH ; FATID byte for 5.25-inch SD/SS format. DW 54+4+4+16 ; STARTSECTOR for 5.25-inch SD/SS format. DW 80 ; Number of sectors boot sector should load. DW 128 ; Bytes/sector. IF SMALLDS-1 DB 8 ; 5.25-inch DD/SS format. DB 40 DW 162 DW 652 DW OFFSET SDINDEX DW OFFSET SDSECTOR DB 0FEH DW 1+1+1+4 DW 20 DW 512 ENDIF IF SMALLDS DB 8 ; 5.25-inch DD/DS format. DB 40 DW 162 DW 652 DW OFFSET SDINDEX DW OFFSET SDSECTOR DB 0FFH DW 1+1+1+7 DW 20 DW 512 ENDIF DB 26 ; 8-inch SD/SS format. DB 77 DW 80 DW 186 DW OFFSET LSINDEX DW OFFSET LSSECTOR DB 0FEH DW 1+6+6+17 DW 80 DW 128 IF LARGEDS-1 DB 8 ; 8-inch DD/SS format. DB 77 DW 162 DW 1138 DW OFFSET LDINDEX DW OFFSET LDSECTOR DB 0FEH DW 1+1+1+3 DW 10 DW 1024 ENDIF IF LARGEDS DB 8 ; 8-inch DD/DS format. DB 77 DW 162 DW 1138 DW OFFSET LDINDEX DW OFFSET LDSECTOR DB 0FEH DW 1+2+2+6 DW 10 DW 1024 ENDIF LDINDEX: ; Pattern for 8-inch double-density. DB 80,4EH DB 12,0 DB 3,0F6H DB 1,0FCH DB 50,4EH LDSECTOR: DB 12,0 DB 3,0F5H DB 1,0FEH DB 3,0 ; Track, side, and sector DB 1,3 ; Sector size=1024  DB 1,0F7H DB 22,4EH DB 12,0 DB 3,0F5H DB 1,0FBH DB 255,0E5H DB 255,0E5H DB 255,0E5H DB 255,0E5H DB 4,0E5H DB 1,0F7H DB 54,4EH DB 0 DB 255,4EH DB 255,4EH DB 255,4EH DB 0 LSINDEX: ; Pattern for 8-inch single-density.  DB 40,-1 DB 6,0 DB 1,0FCH DB 26,-1 LSSECTOR: DB 6,0 DB 1,0FEH DB 4,0 DB 1,0F7H DB 11,-1 DB 6,0 DB 1,0FBH  DB 128,0E5H DB 1,0F7H DB 27,-1 DB 0 DB 255,-1 DB 255,-1 DB 0 SDINDEX: ; Pattern for 5.25-inch double-density. DB 80,4EH DB 12,0 DB 3,0F6H DB 1,0FCH DB 50,4EH SDSECTOR: DB 12,0 DB 3,0F5H DB 1,0FEH DB 3,0 ; Track, side, and sector. DB 1,2 ; Sector size = 512. DB 1,0F7H DB 22,4EH DB 12,0 DB 3,0F5H DB 1,0FBH DB 255,0E5H DB 255,0E5H DB 2,0E5H DB 1,0F7H DB 80,4EH DB 0 DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 255,4EH DB 0 SSINDEX: ; Pattern for 5.25-inch single-density. DB 4,-1 ; No index mark. SSSECTOR: DB 6,0 DB 1,0FEH DB 4,0  DB 1,0F7H DB 11,-1 DB 6,0 DB 1,0FBH DB 128,0E5H DB 1,0F7H DB 9,-1 DB 0 DB 255,0 DB 255,0 DB 0 IF TARBELLDOUBLE DRVTAB DB 00H,10H,20H,30H ENDIF IF TARBELLSINGLE DRVTAB: DB 0F2H,0E2H,0D2H,0C2H ENDIF IF CROMEMCO*LARGE DRVTAB: DB 31H,32H,34H,38H ENDIF IF CROMEMCO*SMALL DRVTAB: DB 21H,22H,24H,28H ENDIF IF CROMEMCO*COMBIN DRVTAB: DB 31H,32H,24H ENDIF IF SCP*LARGE DRVTAB: DB 00H,01H,02H,03H ENDIF IF SCP*SMALL DRVTAB: DB 10H,11H,12H,13H ENDIF  IF SCP*COMBIN DRVTAB: DB 00H,01H,10H ENDIF ; ; Error messages. ; IF SCP+TARBELLDOUBLE+CROMEMCO16FDC READ_ERROR DB "Read Error - can't determine density",13,10,'$' ENDIF SEEK_ERROR DB 'Seek Error - disk not ready',13,10,'$' WRITE_ERROR DB:  'Write Error - not ready, write protect, ' DB 'write fault, or lost data',13,10,'$' BOOT_ERROR DB 'Error writing boot sector',13,10,'$' SWITCHLIST DB 4,'BCDS' FATID DB 0 STARTSECTOR DW 0 FREESPACE DW OFFSET PATTERN+12000 ; ; Boot sectors, one for single-sided boot, a second for double-sided boot. ; Both have various variables such as STARTSECTOR and number of sectors to ; load poked in before the boot sector is written to the disk. The boot ; sectors should be ORGed at 200 hex, but since all jumps and calls are ; reletive, it doesn't matter that it is ORGed at a random location. ; BOOT1SIDED: CLI ; Interrupts off. XOR AX,AX MOV DS,AX MOV ES,AX MOV SS,AX MOV SP,BOOTER ; For debugging purposes. CLD ; a.k.a. "UP" MOV DI,BIOSSEG*16 B1POKE_SECTOR: MOV CX,0 ; CX = number of sectors/track. B1POKE_FIRSECT: MOV BL,0 ; BL = first sector to load. B1SECT: MOV AL,0D0H ; Force Interrupt command. OUT DISK,AL ; To force Type I status AAM ; Give force interrupt time. AAM AAM AAM B1SEEKLP: B1POKE_MAXSECT_1: CMP BL,0 ; Compare with number of sectors/track. JBE B1NOSTEP MOV AL,58H ; Step in with update. CALL B1DCOM B1POKE_MAXSECT_2: SUB BL,0 ; Subtract number of sectors/track. JMP SHORT B1SEEKLP B1NOSTEP: MOV AL,BL ; Get sector number. OUT DISK+2,AL ; Output sector number to disk controller. IF CROMEMCO B1POKE_DRIVESELECT: MOV AL,0 ; AL = drive-select byte to turn on auto-wait. OUT DISK+4,AL ; Turn on hardware wait ENDIF MOV DX,DISK+3 ; Disk controller data port. PUSH DI IF CROMEMCO4FDC+TARBELLSINGLE IN AL,DISK ; Get head load status. NOT AL AND AL,20H JZ B1OUTCOM MOV AL,4 B1OUTCOM: OR AL,88H ; Read Sector command. ENDIF IF SCP+CROMEMCO16FDC+TARBELLDOUBLE MOV AL,88H ; Read Sector command. ENDIF OUT DISK,AL JMP SHORT B1READ B1READLOOP: STOSB ; Put in memory. B1READ: IF SCP IN AL,DISK+5 ; Input from auto-wait port. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4 ENDIF IF TARBELL ROL AL,1 IN AL,DX ; Read data from disk controller chip. JC B1READLOOP ENDIF IF SCP+CROMEMCO ROR AL,1 IN AL,DX ; Read data from disk controller chip. JNC B1READLOOP ENDIF POP DI CALL B1STAT AND AL,9CH JNZ B1SECT B1POKE_SECSIZE: ADD DI,128 ; Add sector size to load next sector. INC BL LOOP B1SECT JMP BIOS B1DCOM: OUT DISK,AL AAM B1STAT: IN AL,DISK+4 TEST AL,DONEBIT IF TARBELL JNZ B1STAT ENDIF IF SCP+CROMEMCO JZ B1STAT ENDIF IN AL,DISK RET IF (SCP+CROMEMCO16FDC)*(LARGEDS+SMALLDS)+TARBELLDOUBLE*LARGEDS BOOT2SIDED: CLI ; Interrupts off. XOR AX,AX  MOV DS,AX MOV ES,AX MOV SS,AX MOV SP,BOOTER ; For debugging purposes. CLD ; a.k.a. "UP" MOV DI,BIOSSEG*16 B2POKE_SECTOR: MOV CX,0 ; CX = number of sectors/track. B2POKE_FIRSECT: MOV BL,0 ; BL = first sector to load. B2SECT: MOV AL,0D0H ; Force Interrupt command. OUT DISK,AL ; To force Type I status AAM ; Give force interrupt time. AAM AAM AAM B2SEEKLP: B2POKE_MAXSECT_1: CMP BL,0 ; Compare with number of sectors/track. JBE B2NOSTEP MOV AL,58H ; Step in with update. CALL B2DCOM B2POKE_MAXSECT_2: SUB BL,0 ; Subtract number of sectors/track. JMP SHORT B2SEEKLP B2NOSTEP: MOV AL,B; L ; Get sector number. IF SCP+TARBELL MOV AH,0 ; Assume side 0. ENDIF IF CROMEMCO MOV AH,0FFH ; Assume side 0.  ENDIF B2POKE_HALFSECT_1: CMP AL,0 ; Find out which sector and side to use. JBE B2SIDE B2POKE_HALFSECT_2: SUB AL,0 ; Compute sector number for back side. IF SCP MOV AH,04H ; Side 1 bit. ENDIF IF TARBELL MOV AH,40H ; Side 1 bit. ENDIF IF CROMEMCO MOV AH,0FDH ; Side 1 bit. ENDIF B2SIDE: OUT DISK+2,AL ; Output sector number to disk controller. IF SCP+TARBELL MOV AL,AH ; Get side-select byte. B2POKE_DRIVESELECT: OR AL,0 ; Add the rest of the drive-select bits. OUT DISK+4,AL ; And send the mess out. ENDIF IF CROMEMCO B2POKE_DRIVESELECT: MOV AL,0 OUT DISK+4,AL ; Turn on hardware wait MOV AL,AH ; Get side select byte. OUT 04H,AL ; Send to Auxillary drive-select port. ENDIF MOV DX,DISK+3 ; Disk controller data port. PUSH DI IF CROMEMCO4FDC+TARBELLSINGLE IN AL,DISK ; Get head load status. NOT AL AND AL,20H JZ B2OUTCOM MOV AL,4 B2OUTCOM: OR AL,88H ; Read Sector command. ENDIF IF SCP+CROMEMCO16FDC+TARBELLDOUBLE MOV AL,88H ; Read Sector command. ENDIF OUT DISK,AL JMP SHORT B2READ B2READLOOP: STOSB ; Put in memory. B2READ:  IF SCP IN AL,DISK+5 ; Input from auto-wait port. ENDIF IF TARBELL+CROMEMCO IN AL,DISK+4 ENDIF IF TARBELL ROL AL,1 IN AL,DX ; Read data from disk controller chip. JC B2READLOOP ENDIF IF SCP+CROMEMCO ROR AL,1 IN AL,DX ; Read data from disk controller chip. JNC B2READLOOP ENDIF POP DI CALL B2STAT AND AL,9CH JNZ B2SECT B2POKE_SECSIZE: ADD DI,128 ; Add sector size to load next sector. INC BL LOOP B2SECT JMP BIOS B2DCOM: OUT DISK,AL AAM B2STAT:  IN AL,DISK+4 TEST AL,DONEBIT IF TARBELL JNZ B2STAT ENDIF IF SCP+CROMEMCO JZ B2STAT ENDIF IN AL,DISK RET ENDIF ; End of double-sided boot sector. PATTERN LABEL BYTE ; 12000-byte buffer for track pattern. CODE ENDS END   END d data from disk controller chip. JNC B2READLOOP ENDIF POP DI CALL B2STAT AND AL,9CH JNZ B2SECT B2POKE_S ; Select back side. OUT DISK+4 ENDIF IF CROMEMCO*(LARGEDS+SMALLDS) MOV AL,BACKBIT ; Select back side. OUT 04H ENDIF JP PUTSEC ENDIF IF SCP CHECKSMALL: TEST AH,SMALLBIT ; See if big disk. JZ CHECK26 ; Jump if big. ENDIF IF CROMEMCO CHECKSMALL: TEST AH,SMALLBIT ; See if big disk. JNZ CHECK26 ; Jump if big. ENDIF IF SCP+CROMEMCO CMP BL,SMALLSDSECT ; See if legal small SD/SS sector. JA STEP ; Jump if not. ENDIF CHECK26: CMP BL,LARGESDSECT ; See if legal large SD/SS sector. JBE PUTSEC ; Jump if ok. STEP: INC DL ; Increment track number. MOV AL,58H ; Step in with update. CALL DCOM SEG CS INC B,[DI] ; Increment the track pointer. MOV DH,1 ; After step, do first sector. MOV BL,DH ; Fix temporary sector number also. PUTSEC: MOV AL,BL ; Output sector number to controller. OUT DISK+2 ; ; 1771-type disk controllers need to find out if the head is loaded ; so the "E" bit of the read or write command can be set properly. ; IF WD1771 DI ; Interrupts not allowed until I/O done ENDIF IF CROMEMCO4FDC INB DISK+4 ; Get head-load bit. ENDIF < IF TARBELLSD INB DISK ENDIF IF WD1771 NOT AL AND AL,20H ; Check head load status JZ RET MOV AL,4 ENDIF RET READSECT: CALL SETUP MOV BL,10 ; Retry count for hard error. XCHG DI,SI ; Transfer address to DI. PUSH DX ; Save track & sector number. MOV DX,DISK+3 ; Disk controller data port. RDAGN: IF WD1771 OR AL,READCOM ; Add "E" bit to read command. ENDIF IF WD1793 MOV AL,READCOM DI ; Interrupts off here for 1793. ENDIF OUT DISK ; Output read command. IF CROMEMCO MOV AL,AH ; Turn on auto-wait. OUT DISK+4 ENDIF MOV BP,DI ; Save address for retry. JMPS RLOOPENTRY RLOOP: STOB ; Write into memory. RLOOPENTRY: IF SCP IN DISK+5 ; Wait for DRQ or INTRQ. ENDIF IF TARBELL+CROMEMCO IN DISK+4 ENDIF IF TARBELL SHL AL INB DX ; Read data from disk controller chip. JC RLOOP ENDIF IF SCP+CROMEMCO SHR AL INB DX JNC RLOOP ENDIF EI ; Interrupts OK now CALL GETSTAT AND AL,9CH JZ RDPOP MOV DI,BP ; Get origainal address back for retry. MOV BH,AL ; Save error status for report. IF WD1771 MOV AL,0 ; "E" bit off in read command. ENDIF DEC BL JNZ RDAGN MOV AH,BH ; Put error status in AH. STC RDPOP: POP DX ; Get back track & sector number. XCHG SI,DI ; Address back to SI. IF TARBELL FORCINT: MOV AL,0D0H ; Tarbell controllers need this Force Interrupt OUT DISK ; so that Type I status is always available MOV AL,10 ; at the 1771/1793 status port so we can find INTDLY: ; out if the head is loaded. SCP and Cromemco DEC AL ; controllers have head-load status available JNZ INTDLY ; at the DISK+4 status port. ENDIF RET WRITESECT: CALL SETUP MOV BL,10 PUSH DX ; Save track & sector number. MOV DX,DISK+3 ; Disk controller data port. WRTAGN: IF WD1771 OR AL,WRITECOM ; Add "E" bit to write command. ENDIF IF WD1793 MOV AL,WRITECOM DI ; Disable interrupts here if write command. ENDIF OUT DISK ; Write sector command. IF CROMEMCO MOV AL,AH ; Turn on auto-wait. OUT DISK+4 ENDIF MOV BP,SI WRLOOP: IF SCP INB DISK+5 ENDIF IF TARBELL+CROMEMCO INB DISK+4 ENDIF IF SCP+CROMEMCO SHR AL LODB ; Get data from memory. OUTB DX ; Write to disk. JNC WRLOOP ENDIF IF TARBELL SHL AL LODB ; Get data from memory. OUTB DX ; Write to disk. JC WRLOOP  ENDIF EI ; Interrupts OK now. DEC SI CALL GETSTAT AND AL,0FCH JZ WRPOP MOV SI,BP MOV BH,AL IF WD1771 MOV AL,0 ; "E" bit off in write command. ENDIF DEC BL JNZ WRTAGN MOV AH,BH ; Error status to AH. STC WRPOP: POP DX ; Get back track & sector number. IF TARBELL JMPS FORCINT ENDIF IF SCP+CROMEMCO RET ENDIF ; ; Subroutine to restore the read/write head to track 0. ; IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1) HOME: ENDIF IF FASTSEEK*CROMEMCO TEST AH,SMALLBIT ; Check for large disk. JNZ RESTORE ; Big disks are fast seek PerSci. ENDIF MOV BL,3 TRYHOM: IF SCP*FASTSEEK MOV AL,AH ; Turn on Restore to PerSci. OR AL,80H OUTB DISK+4 ENDIF MOV AL,0CH+STPSPD ; Restore with verify command. CALL DCOM AND AL,98H IF SCP*FASTSEEK MOV AL,AH ; Restore off. OUTB DISK+4 ENDIF JZ RET JS HOMERR =  ; No retries if not ready MOV AL,58H+STPSPD ; Step in with update CALL DCOM DEC BL JNZ TRYHOM HOMERR: STC RET ; ; RESTORE for PerSci drives. ; Doesn't exist yet for Tarbell controllers. ; IF FASTSEEK*TARBELL HOME: RESTORE: RET ENDIF IF FASTSEEK*CROMEMCO4FDC RESTORE: MOV AL,0C4H ;READ ADDRESS command to keep head loaded OUT DISK MOV AL,77H OUT 4 CHKRES: IN 4 AND AL,40H JZ RESDONE IN DISK+4 TEST AL,DONEBIT JZ CHKRES IN DISK JP RESTORE ;Reload head RESDONE: MOV AL,7FH OUT 4 CALL GETSTAT MOV AL,0 OUT DISK+1 ;Tell 1771 we're now on track 0 RET ENDIF IF FASTSEEK*CROMEMCO16FDC RESTORE: MOV AL,0D7H ; Turn on Drive-Select and Restore. OUTB 4 PUSH AX AAM ; 10 uS delay. POP AX RESWAIT: INB 4 ; Wait till Seek Complete is active. TEST AL,40H JNZ RESWAIT MOV AL,0FFH ; Turn off Drive-Select and Restore. OUTB 4 SUB AL,AL ; Tell 1793 we're on track 0. OUTB DISK+1 RET ENDIF ; ; Subroutine to move the read/write head to the desired track. ; Usually falls through to DCOM unless special handling for ; PerSci drives is required in which case go to FASTSK. ; IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1) MOVHEAD: ENDIF IF CROMEMCO*FASTSEEK TEST AH,SMALLBIT ; Check for PerSci. JNZ FASTSK ENDIF DCOM: OUT DISK PUSH AX AAM ;Delay 10 microseconds POP AX GETSTAT: IN DISK+4 TEST AL,DONEBIT IF TARBELL JNZ GETSTAT ENDIF IF SCP+CROMEMCO JZ GETSTAT ENDIF IN DISK RET ; ; Fast seek code for PerSci drives. ; Tarbell not installed yet. ; IF FASTSEEK*TARBELL MOVHEAD: FASTSK: RET ENDIF IF FASTSEEK*CROMEMCO FASTSK: MOV AL,6FH OUT 4 MOV AL,18H CALL DCOM SKWAIT: IN 4 TEST AL,40H JNZ SKWAIT MOV AL,7FH OUT 4  MOV AL,0 RET ENDIF CURDRV: DB -1 ; ; Explanation of tables below. ; ; DRVTAB is a table of bytes which are sent to the disk controller as drive- ; select bytes to choose which physical drive is selected for each disk I/O ; driver. It also selects whether the disk is 5.25-inch or 8-inch, single- ; density or double-density. Always select side 0 in the drive-select byte if ; a side-select bit is available. There should be one entry in the DRVTAB ; table for each disk I/O driver. Exactly which bits in the drive-select byte ; do what depends on which disk controller is used. ; ; TRKTAB is a table of bytes used to store which track the read/write ; head of each drive is on. Each physical drive should have its own ; entry in TRKTAB. ; ; TRKPT is a table of bytes which indicates which TRKTAB entry each ; disk I/O driver should use. Since each physical drive may be used for ; more than one disk I/O driver, more than one entry in TRKPT may point ; to the same entry in TRKTAB. Drives such as PerSci 277s which use ; the same head positioner for more than one drive should share entrys ; in TRKTAB. ; ; INITTAB is the initialization table for 86-DOS as described in the ; 86-DOS Programer's Manual under "Customizing the I/O System." ;  IF SCP*COMBIN*FASTSEEK ; ; A PerSci 277 or 299 and one 5.25-inch drive. ; DRVTAB: DB 00H,08H,01H,09H,10H,18H,00H,08H,01H,09H TRKPT: DB 0,0,0,0,1,1,0,0,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 6 ; Number of disk I/O drivers. ENDIF I> F CONVERT DB 10 ENDIF DB 0 ; Disk I/O driver 0 uses disk drive 0. DW LSDRIVE ; Disk I/O driver 0 is 8-inch single-density. DB 0 ; Disk I/O driver 1 uses disk drive 0. DW LDDRIVE ; Disk I/O driver 1 is 8-inch double-density. DB 1 ; Etc. DW LSDRIVE DB 1 DW LDDRIVE DB 2 DW SSDRIVE DB 2 DW SDDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE DB 4 DW OLDLSDRIVE DB 4 DW OLDLDDRIVE ENDIF ENDIF IF SCP*LARGE*(FASTSEEK-1) ; ; Two 8-inch Shugart-type drives. ; DRVTAB: DB 00H,08H,01H,09H,00H,08H,01H,09H TRKPT: DB 0,0,1,1,0,0,1,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE  IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF TARBELLDD ; ; Two 8-inch Shugart-type drives. ; DRVTAB: DB 0,8,10H,18H,0,8,10H,18H TRKPT: DB 0,0,1,1,0,0,1,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF TARBELLSD ; ; Four 8-inch Shugart-type drives. ; DRVTAB: DB 0F2H,0E2H,0F2H,0E2H TRKPT: DB 0,1,0,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF ; ; Cromemco drive select byte is derived as follows: ; Bit 7 = 0 ; Bit 6 = 1 if double density (if 16FDC) ; Bit 5 = 1 (motor on) ; Bit 4 = 0 for 5", 1 for 8" drives ; Bit 3 = 1 for drive 3 ; Bit 2 = 1 for drive 2 ; Bit 1 = 1 for drive 1 ; Bit 0 = 1 for drive 0 ; IF CROMEMCO4FDC*LARGE ; ; PerSci 277 drive. ; DRVTAB: DB 31H,32H,31H,32H TRKPT: DB 0,0,0,0 TRKTAB: DB -1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF IF CROMEMCO4FDC*COMBIN ; ; A PerSci 277 and one 5.25-inch drive. ; DRVTAB: DB 31H,32H,24H,31H,32H TRKPT: DB 0,0,1,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 3 ENDIF IF CONVERT DB 5 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE DB 2 DW SSDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 4 DW OLDLSDRIVE ENDIF ENDIF  IF CROMEMCO4FDC*SMALL ; ; Three 5.25-inch drives. ; DRVTAB: DB 21H,22H,24H TRKPT: DB 0,1,2 TRKTAB: DB -1,-1,-1 INITTAB:DB 3 DB 0 DW SSDRIVE DB 1 DW SSDRIVE DB 2 DW SSDRIVE ENDIF IF CUSTOM ; ; Cromemco 4FDC with two 8-inch Shugart-type drives. ; DRVTAB: DB 31H,32H,31H,32H TRKPT: DB 0,1,0,1 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 2 ENDIF IF CONVERT DB 4 ENDIF DB 0 DW LSDRIVE DB 1 DW LSDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 3 DW OLDLSDRIVE ENDIF ENDIF IF CROMEMCO16FDC*SMALL ; ; Three 5.25-inch drives. ; DRVTAB: DB 21H,61H,22H,62H,24H,64H TRKPT: DB 0,0,1,1,2,2 TRKTAB: DB -1,-1,-1 INITTAB:DB 6 DB 0 DW SSDRIVE DB 0 DW SDDRIVE DB 1 DW SSDRIVE DB 1 DW SDDRIVE DB ? 2 DW SSDRIVE DB 2 DW SDDRIVE ENDIF IF CROMEMCO16FDC*COMBIN ; ; A PerSci 277 or 299 and one 5.25-inch drive. ; DRVTAB: DB 31H,71H,32H,72H,24H,64H,31H,71H,32H,72H TRKPT: DB 0,0,0,0,1,1,0,0,0,0 TRKTAB: DB -1,-1 INITTAB: IF CONVERT-1 DB 6 ENDIF IF CONVERT DB 10 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE DB 2 DW SSDRIVE DB 2 DW SDDRIVE IF CONVERT DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE DB 4 DW OLDLSDRIVE DB 4 DW OLDLDDRIVE ENDIF ENDIF IF CROMEMCO16FDC*LARGE ; ; A PerSci 277 or 299. ; DRVTAB: DB 31H,71H,32H,72H,31H,71H,32H,72H TRKPT: DB 0,0,0,0,0,0,0,0 TRKTAB: DB -1 INITTAB: IF CONVERT-1 DB 4 ENDIF IF CONVERT DB 8 ENDIF DB 0 DW LSDRIVE DB 0 DW LDDRIVE DB 1 DW LSDRIVE DB 1 DW LDDRIVE IF CONVERT DB 2 DW OLDLSDRIVE DB 2 DW OLDLDDRIVE DB 3 DW OLDLSDRIVE DB 3 DW OLDLDDRIVE ENDIF ENDIF IF SMALL+COMBIN SSDRIVE: DW 128 ; Sector size in bytes.  DB 2 ; Sector per allocation unit. DW 54 ; Reserved sectors. DB 2 ; Number of allocation tables. DW 64 ; Number of directory entrys. DW 720 ; Number of sectors on the disk. IF SMALLDS-1 SDDRIVE: ; This is the IBM Personal Computer DW 512 ; disk format. DB 1 DW 1 DB 2 DW 64 DW 320 ENDIF IF SMALLDS SDDRIVE: DW 512 DB 2 DW 1 DB 2 DW 112 DW 640 ENDIF ENDIF ; End of small drive DPTs. IF COMBIN+LARGE LSDRIVE: DW 128 ; Size of sector in bytes. DB 4 ; Sectors per allocation unit. DW 1 ; Number of reserved sectors. DB 2 ; Number of File Allocation Tables. DW 68 ; Number of directory entrys. DW 77*26 ; Number of sectors on the disk. IF CONVERT OLDLSDRIVE: DW 128 DB 4 DW 52 ; Old format had two tracks reserved. DB 2 DW 64 ; 64 directory entrys. DW 77*26 ENDIF IF LARGEDS-1 OLDLDDRIVE: LDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 96 DW 77*8 ENDIF IF LARGEDS LDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 192 ; 192 directory entrys in new 8-inch DD/DS format. DW 77*8*2 IF CONVERT OLDLDDRIVE: DW 1024 DB 1 DW 1 DB 2 DW 128 ; 128 directory entrys in old 8-inch DD/DS format. DW 77*8*2 ENDIF ENDIF ENDIF ; End of large drive DPTs. DOSSEG: EQU ($+15)/16+BIOSSEG ; Compute segment to use for 86-DOS. END o use for 86-DOS. END llocation Tables. DW 68 ;@ A B C D E F G H I J K L