}|Ø *B!~#) !N#F>O>G0 !N#Fywx#w!@>#? *D~Ae *D>Ze *D~OG! MDm *D~OGylv x? *D#"D? *D~a *D>z !!N#F! N#F !~#fo }|:@2@:A2A*D#"D*D~A *D>Z !*D~A *D>Z *D~OG! MD *D~OG! }|!!   # *D!yOx#G= ' !~#fo"D*D"B!#~ !># !~#fo>o>g"@*B*B#"B6-ì !~#fo"@!~# !> w#w !#~ !!~#fo>o>g}|!:@:A#! !N#F*@!N#F *BR *B "B*B*@!N#F4 0 }*B>9L *B~'w*B!DyOx#G= ' !~#fo"D>2@2A!:@:A# *D*D~A *D>Z *D~OG! MDö *D~OGyw*@#"@*D#"Dw !~#foMD= !,~# ! :, :- *,i`", *. ' *,"D!~#fo",*DMD= !9*D*B*@i`!"@"B"D!9~w#~w#~w#~w+MD!9 _ #Z { !94Z ! 9~ +w>#w! 9N#Fx O>GP O>G! 9q#p! 9~ +w>#w! 9N#Fx O>GP  O>G! 9q#p! 9N#FP ! 9q#p! 9N#FP ! 9q#p!9~_ /<à O!9~#fo) k !9~ /<_ O!9~#fo||g}o  !9~ /<_ O!9~#fo|g}o º MD!9q#p!! 9~+ ~ ! 9N#F ! 9~w#~w! 9~w+~w !9q#p&!!9OG!9N#F!9~#fo ## # +++ #!9MD!9~#~#~#~ !9~w#~w#~w#~w#~w#~w#~w#~w!9MD! 9  # # #x!9MD! 9  # # #!94x!9!9#~#~+++~#~#~#>#>+++>#~#~#~ #~{$>#># >#!9MD!9~#fo! 9MD!9~#foU!9N#F!97!9MD!9~#fo! 9MD!9~#foU!9N#F!97! 9MD!9~#fo#!9MD!9~#fo#U!9N#Fkb9$!9~#foN#F#N#F! 9~#foN#F#N#F!!9~+++ʓ~i!9MD!9  # # #!9~w#~w#~w#~w!9~w+~w+~w+~w:!9N#F!9~#~}|! 9&}!!9!9!9N#F###q#p+++q!9N#F#q#pMorrow Designs Inc.H  _ O!9~#fo||g}o  !9~ /<_ O!9~#fo|g}o º MD!9q#p!! 9~+ ~ ! 9N#F ! 9~w#~w! 9~w+~w !9q#p&!!9OG!9N#F!9~#fo ## # +++ #!9MD!9~#~#~#~ !9~w#~w#~w#~w#~w#~w#~w#~w!9MD! 9  # # #x!9MD! 9  # # #!94x!9!9#~#~+++~#~#~#>#>+++>#~#~#~ #~{$>#># >#!9MD!9~#fo! 9MD!9~#foU!9N#F!97!9MD!9~#fo! 9MD!9~#foU!9N#F!97! 9MD!9~#fo#!9MD!9~#fo#U!9N#Fkb9$!9~#foN#F#N#F! 9~#foN#F#N#F!!9~+++ʓ~i!9MD!9  # # #!9~w#~w#~w#~w!9~w+~w+~w+~w:!9N#F!9~#~}|! 9&}!!9!9!9N#F###q#p+++q!9N#F#q#pMo*K͞+ 2,'+FÎr ͖ 8{ Ăw#w#w^#V#*~#fo^#*~#fo^#V#*n^#*n^#V# ~#fo^#& ~#fo!+!#!+!#!+!+}|z{|}|z7||7zZZ)|/g}/o#|͉k|/g}/o#ɯ2qZZk:q|/g}/o#|/g}/o#:q<2qqDM!xxGyO҃)v|͔`i|)Öxڷz/W{/_ѯzW{_=yOxGæ2qZZ͉M|}ȯ|g}o)|/g}/o#z/W{/_!9~#fo! ! ! ! ! ! !9~#A"s!`*"!"!Y">2>2>22!"!"!@"!" ʞ!F#x±~#±!b2r~# "2r+}|~#G:rx"2r+w# +6#!6#2w2x*s!>r<o&F=-` r'~h6!+`W?_!~7z?` :>ª@w#G.¶ww#?*>?w#> w#.7:77!a{   `OE!y6$ -7rBo&))T])))!yZ>E>ci!",i!",i!",i!",!͐*,Can't create %s%sWrite error %s%sCan't find %s%sRead error on %s%s ...rebooting%+17=CFIORX[adjw}(C*v#a(a͡(C:v_*vͰL aͲa(C*v#Ͳa(C*"v#$%>aA>-c(a:$vc(*6|ja>+0((*7*6Re."6:f5!6V(~ʁa>͡( C:pvʏa>͡($Cʛa>%͡("C:)a>0͡((C0c(;aHa+|axa`i;ac(iÎ :6:6W?)O*7:I=G:&b:+:e5\b:1vWb:0v0bIbrb{!g5zAb!e5!9DM! "(,!"$,!"&,!* ͢ ͐!G ͢ !W ͢  ! ^#Vr+s~#fo" ,! ^#Vr+s~#fo" ,͐2! ^#Vr+s~#fo",!"",8!"",!* ,e",#|[* ,! !* ,e",#|~* ,! !", "$$"(!*$*,ͩ",#|º* ,! !*(*,ͩ",#|* ,! `iw#w͐*,*, *,t random record DW RESDRV ;37: Reset drive DW JR ;38: Undefined - go back DW JR ;39: Undefined - go back DW ZERRND ;40: Fill random file w/ zeros ; ;********************************************************************** ; Print the permanent error message ; PERSUB: LXI H,PERMSG ;point to bad sector message CALL DOSERR ;print out with disk and return CPI CTRLC ;return with a char - is it ctrlc? JZ BOOTV ;if so go reboot RET ;otherwise just go back ; ;********************************************************************** ; Print the select error message then die ; SELSUB: LXI H,SELMSG ;'Select' msg JMP BOMB ;say too bad then abort ; ;********************************************************************** ; Print read only error message then die ; ROSUB: LXI H,ROMSG ;Send R/O msg JMP BOMB ; say too bad then abort ; ;********************************************************************** ; Print file read-only error message, die ; RONSUB: LXI H,ROEMSG ;'File R/O' msg ; ;********************************************************************** ; Print out the error message then reboot ; BOMB: CALL DOSERR ;Print BDOS err on [dsk #] JMP BOOTV ;Reboot system ; ;********************************************************************** ; Error messages ; DOSMSG: DB 'Bdos Err On ' DSKCH: DB ' : $' PERMSG: DB 'Bad Sector$' SELMSG: DB 'Select$' ROEMSG: DB 'File ' ROMSG: DB 'R/O$' ; ;********************************************************************** ; Print BDOS error msg with disk # ; DOSERR: PUSH H ;save the message pointer CALL CRLF ;Turn up a new line LDA CURDSK ;get the current disk ADI 'A' ;add in the ascii bias STA DSKCH ;Form drive ID char LXI B,DOSMSG ;Print the CALL PRINT ; 'Bdos Err on' msg POP B ;restore the message pointer CALL PRINT ;Print specific error message ; ;********************************************************************** ; Read next console character ; CONIN: LXI H,CHRRDY ;Point to the char ready flag MOV A,M ;move the ready byte into A MVI M,0 ;and zero out the flag ORA A ;was anyone home RNZ ;return if not JMP CONIF ;otherwise go get the thing ; ;********************************************************************** ; Read console character, echo print if it is ok ; CIECHO: CALL CONIN ;get a character CALL GRAFIC ;check the control characters RC ;return if the char is not printable PUSH PSW ;otherwise save it MOV C,A ;put a copy into C CALL WRTCON ;and print it out POP PSW ;restore it RET ;and return to sender ; ;********************************************************************** ;see if A is a good control char, ret with CY set if not ; GRAFIC: CPI CR ;test for carriage return RZ ;return if it was CPI LF ;how about line feed RZ ;that is ok CPI TAB ;as is tab RZ ;so back with it CPI BACKSP ;Backspace in 2.0 RZ ;is known CPI ' ' ;test all other control characters RET ;and go back with carry set for no echo ; ;********************************************************************** ; Check for console break and abort ; CONBRK: LDA CHRRDY ;get the character ready byte ORA A ;and check for waiting char JNZ CB1 ;non zero says something is there CALL CONSF ;check the console if none there ANI 01H ;and in the status bit RZ ;return if it came up zero CALL CONIF ;otherwise go get the waiting char CPI CTRLS ;was it a control-s JNZ CB0 ;if not return with ready flag set CALL CONIF ;if it was wait for the next char CPI CTRLC ;is this one control c? JZ BOOTV ;if so we reboot XRA A ;or else zero a RET ;and simply return ; CB0: STA CHRRDY ;Save input char CB1: MVI A,TRUE ;and say true to the caller RET ;and go back ; ;********************************************************************** ; Output char in C to console, list also if LSTCPY = true ; CONOUT: LDA CTEMP1 ;get the first temp column counter ORA A ;is it zero? JNZ CONOU1 ;if nonzero jump over PUSH B ;otherwise save input character CALL CONBRK ;Check for abort POP B ;recover the char PUSH B ;save it again CALL CONOF ;and print the character on the console POP B ;recover once more PUSH B ;would you believe save it again LDA LSTCPY ;get the list flag byte ORA A ;to set flags CNZ LISTF ; and to list if LSTCPY=true POP B ;restore char one last time CONOU1: MOV A,C ;put the byte into A LXI H,COLUMN ;point to the buffer column counter CPI 7FH ;Rubout ? RZ ;return if so INR M ;Increment column CPI ' ' ;is the character a space? RNC ;return if less - must be control DCR M ;Decrement column MOV A,M ;and get the character there ORA A ;check for a null RZ ;return if it is MOV A,C ;move the char into C CPI BACKSP ;was it a backspace? JNZ CONOU2 ;if not jump over DCR M ;Decrement column RET ;and return ; CONOU2: CPI LF ;was it a linefeed? RNZ ;return if not again MVI M,0 ;Set column to 0 RET ;and return ; ;********************************************************************** ; Print char in C at console, convert Ctrl chars to ^[char] ; CTLOUT: MOV A,C ;move the char into A CALL GRAFIC ;see if it is printable JNC WRTCON ;jump past cause it is ok PUSH PSW ;otherwise save ctrl char on the stack MVI C,5EH ;get the "^" char CALL CONOUT ;and print it POP PSW ;get the char back ORI 40H ;add in the bias to make it print MOV C,A ;put it into C for the next ; ;********************************************************************** ; BDOS function 2: write to the system console char in C ; WRTCON: MOV A,C ;which puts it into A CPI TAB ;check for tab JNZ CONOUT ;if not jump and print TABOUT: MVI C,' ' ;Space gets printed to expand tabs CALL CONOUT ; to LDA COLUMN ; next ANI 07H ; tab JNZ TABOUT ; stop RET ;Return ; DELAST: CALL BACKUP ;back up one character MVI C,' ' ;then get a blank CALL CONOF ;and print it out BACKUP: MVI C,BACKSP ;get a backspace JMP CONOF ;and print it as well to move into spot ; ;********************************************************************** ; Print pound sign, CRLF, and fix columns ; LBCRLF: MVI C,'#' ;get a pound sign CALL CONOUT ;and print it CALL CRLF ;Turn up a new line LB1: LDA COLUMN ;get the column counter LXI H,CTEMP2 ;point to the temp counter CMP M ;are they equal? RNC ;not there yet MVI C,' ' ;so get a space CALL CONOUT ;and print it out JMP LB1 ;loop til the counts are the same ; ;********************************************************************** ; Print CR/LF at console ; CRLF: MVI C,CR ; get a cr CALL CONOUT ;and print it out MVI C,LF ;followed by a lf JMP CONOUT ;which goes out too ; ;********************************************************************** ; Print string at (BC) until '$' with TAB expansion ; PRINT: LDAX B ;get the byte at (BC) CPI '$' ;is it the end mark? RZ ;if so we are done INX B ;otherwise bump the pointer PUSH B ;and save it in the stack MOV C,A ;get the character into C CALL WRTCON ;write it out expanding tabs POP B ;recover the pointer JMP PRINT ;and loop til we hit the stop ; ;********************************************************************** ; BDOS function 10: Read console buffer at ; enter with BC -> console buffer address ; REDBUF: LDA COLUMN ;get the column counter into A STA CTEMP2 ;save it at CTEMP2 LHLD FCB ;get the information into HL MOV C,M ;get the buffer count byte INX H ;and bump the pointer to the next spot PUSH H ;save it for later MVI B,0 ;zero B RB0: PUSH B ;save BC for later PUSH H ;and HL as well RB1: CALL CONIN ;go get a char from the console ANI 7FH ;strip the parity bit POP H ;recover HL POP B ;and BC CPI CR ;is this character a CR? JZ RBEXIT ;if so jump over CPI LF ;how about a LF JZ RBEXIT ;jump with that as well CPI BACKSP ;do we have a backspace? JNZ CHKRUB ;if not over we go MOV A,B ;put B into A ORA A ;is it still zero? JZ RB0 ;and save this address at scratch 1 INX H ;move over INX H ; two more SHLD SCRT2 ;saving the result at scratch 2 INX H ;two more INX H ; for good measure XCHG ;swap DE and HL - DE has DIRBUF pointer SHLD TRANS ;save the original disp parm header LXI H,DTEMP ;point to the temp DMA area MVI C,8 ;Bytes to move CALL MOVE ;Move C bytes DIRBUF to DTEMP (dont know why) LHLD DPB ;get the disk parm block address XCHG ;and put it into DE LXI H,DPBLK ;now set HL to point to storage area MVI C,15 ;Bytes to move CALL MOVE ;Move disk data into storage area LHLD DSIZE ;get the size of this disk MOV A,H ;high byte zero says 1024 byte blocks LXI H,BSIZE ;point to block size byte MVI M,0FFH ;put in an FF ORA A ;set flags JZ JRFF ;Return FF if blocks are 1024 bytes MVI M,0 ;otherwise set in a ZERO JRFF: MVI A,0FFH ;and get an FF into A ORA A ;set flags RET ;and return ; ;********************************************************************** ;routine to home the selected disk and reset data words ; HOMDSK: CALL HOMF ;home the selected drive XRA A ;get a zero LHLD SCRT1 ;point to the current track storage MOV M,A ;put in the zero INX H ;point to the low half MOV M,A ;and zero this as well LHLD SCRT2 ;point to starting sector count for this track MOV M,A ;zero it INX H ;bump pointer MOV M,A ;both halves of this word RET ;and return ; ;********************************************************************** ;routine reads indicated sector and checks status ; RDSEC: CALL DRDF ;Read sector JMP PERCHK ;jump to check error and return ; ;********************************************************************** ;routine to write indicated sector and check result ; WRSEC: CALL DWRF ;Write sector ; PERCHK: ORA A ;set flags RZ ;return if all is well LXI H,PERERR ;HL=Permanent err sub addr JMP HLGO ;Go to routine ; ;********************************************************************** ;routine to get the next directory sector location for reading ; GETDS: LHLD COUNT ;get the directory count MVI C,02H ;ready to slide it over 2 bits right CALL SPINHL ;spin hl into position - sector number SHLD GROUP ;save a copy at group SHLD LDIRPNT ;and another at directory pointer GETD1: LXI H,GROUP ;point to directory sector number MOV C,M ;pull it INX H ; into MOV B,M ; BC LHLD SCRT2 ;pull current sector address MOV E,M ;pull (scratch2) 16 bits wide INX H ; into MOV D,M ; DE LHLD SCRT1 ;pull current track address MOV A,M ;pull (scratch1) into A INX H ; into MOV H,M ; HL MOV L,A ; 16 bits at (scratch1) into HL ; ;next two blocks set the track and starting sector number for the track ; so the directory disk access will be right ; GETD2: MOV A,C ;subtract SUB E ; track start sector MOV A,B ; from SBB D ; desired sector number JNC GETD3 ;if too far jump PUSH H ;save current track on stack LHLD DPBLK ;get sectors per track out MOV A,E ;subtract SUB L ; value MOV E,A ; from MOV A,D ; track start sector SBB H ; and leave MOV D,A ; result in DE POP H ;restore track count DCX H ;decrement count JMP GETD2 ;and loop until start is less than desired ; ; BC has the sector we want, DE the track start sector number, HL the track ; GETD3: PUSH H ;save the track count LHLD DPBLK ;get the sectors per track DAD D ;compute the next track start sector number JC GETD4 ;if this exceeds the desired number jump MOV A,C ;otherwise SUB L ; subtract MOV A,B ; new track start number SBB H ; from the desired sector JC GETD4 ;if ok then jump over XCHG ;otherwise swap DE and HL POP H ;recover track number INX H ;bump it by 1 JMP GETD3 ;and loop until done ; ;point the disk to the right spot for access now that the computing is done ; GETD4: POP H ;recover track number PUSH B ;save desired sector number PUSH D ;and the track starting sector number PUSH H ;and the track number again XCHG ;swap DE and HL LHLD TOFS ;get track offset into HL DAD D ;add in the desired track number MOV B,H ;and move it MOV C,L ; into BC CALL TRKF ;Select track POP D ;recover entry track into DE LHLD SCRT1 ;get pointer to current track storage MOV M,E ;and put INX H ; the new track MOV M,D ; back into scratch POP D ;recover current sector number LHLD SCRT2 ;get storage address MOV M,E ;and save INX H ; the new current sector number MOV M,D ; there POP B ;recover new track sector start number MOV A,C ;subtract SUB E ; track sector start number MOV C,A ; from MOV A,B ; the desired number SBB D ; and MOV B,A ; leave result in BC LHLD TRANS ;point to translation routine XCHG ;put address into DE CALL SECTRN ;go do the translation MOV C,L ;put the result MOV B,H ; into BC JMP SECF ;finish setting up the disk pointers ; ;********************************************************************** ;routine gets extent sector count from storage and figures block number ; COMBLK: LXI H,BLSHFT ;point to block shift factor MOV C,M ;pull it into C LDA ESCNT2 ; pull in the extent low byte COMBL1: ORA A ;set flags - clear carry RAR ;rotate right through carry DCR C ;decrement count JNZ COMBL1 ;loop until done MOV B,A ;then save the byte into B MVI A,08H ;get bit 3 set SUB M ;subtract block shift from this value MOV C,A ;and save it into C LDA ESCNT1 ;get the extent high byte COMBL2: DCR C ;decrement count again JZ COMBL3 ;if done jump ORA A ;otherwise set flags - clear carry RAL ;rotate left through carry JMP COMBL2 ;loop until done ; COMBL3: ADD B ;add in the offset RET ;and return ; ;********************************************************************** ;routine points into the FCB block and returns with group pointer. ;If BSIZE=0 blocks are>1024 and 16 bits come back ; GETGRP: LHLD FCB ;get the FCB address LXI D,16 ;and a 16 DAD D ;add in the offset DAD B ;add in BC as well LDA BSIZE ;get the block size byte ORA A ;set flags JZ HLPBC ;if zero add HL and BC MOV L,M ;pull in this byte MVI H,0 ;set upper byte 0 RET ;return with HL set ; ; routine sets HL = (HL+BC) ; HLPBC: DAD B ;add HL and BC MOV E,M ;move this address byte into E INX H ;point to next MOV D,M ;pull this one into D XCHG ;swap DE and HL RET ;and go back ; ;********************************************************************** ;routine takes sector count for directory area and gets the group pointer out ; SECGRP: CALL COMBLK ;get the block number MOV C,A ;and put it into C MVI B,0 ;zero out B CALL GETGRP ;now go get the right group SHLD GROUP ;save it at group storage RET ;and go back ; ;********************************************************************** ; routine checks group and sets flags if zero - no group 0 should be there ; TSTGRP: LHLD GROUP ;get the group pointer out MOV A,L ;move low into A ORA H ;or with H to set flags RET ;and go back ; ;********************************************************************** ; routine appears to figure actual sector number from group pointer ; COMSEC: LDA BLSHFT ;get the block shift factor LHLD GROUP ;and the group pointer into HL COMS1: DAD H ;slide it right DCR A ;and decrement count JNZ COMS1 ;looping until it has shifted right amount SHLD DIRPNT ;then save it in the directory pointer LDA BLKMSK ;now get the block mask out MOV C,A ;save it in C LDA ESCNT2 ;get the low extent counter ANA C ;and it with the block mask ORA L ;or in the directory pointer low byte MOV L,A ;slide this back into L SHLD GROUP ;and save this address at group RET ;then back to sender ; ;********************************************************************** ;routine to point to information + 12 - the extent byte of the FCB ; GETEX: LHLD FCB ;get the FCB pointer LXI D,12 ;and d only if not RET ;and return ; CHK1: MOV M,A ;put A into (HL) RET ;and return ; ;********************************************************************** ;routine to write directory sector out ; WRTDS: CALL CHKSUM ;check the directory check sum CALL SETEMP ;if ok set DMA address up MVI C,01H ;get the code into C CALL WRSEC ;write the sector JMP SETDMA ;reset the DMA address and return ; ;********************************************************************** ;routine to read the next sector from the disk ; GETNXT: CALL SETEMP ;set the DMA address to (DTEMP) CALL RDSEC ;and read the next sector ; ;********************************************************************** ;tell BIOS the correct DMA address ; SETDMA: LXI H,DMAADR ;point to the DMA address word JMP SET2 ;and tell BIOS about it ; ;********************************************************************** ; set the dma address to DTEMP ; SETEMP: LXI H,DTEMP ;get the DMA address from DTEMP ; SET2: MOV C,M ;move the low byte into C INX H ;point to the next MOV B,M ;and pull high into B JMP DMAF ;Set DMA buffer addr & return ; ;********************************************************************** ;routine to move the data stored at (DTEMP) to (DMAADR) ; MOVSEC: LHLD DTEMP ;point to the DMA buffer XCHG ;put it into DE LHLD DMAADR ;get the DMA address for the load MVI C,128 ;Bytes to move JMP MOVE ;Move C bytes DE to HL ; ;********************************************************************** ;routine checks count - returns 1 if it is zero ;and the first byte at count if it is nonzero ; CHKCNT: LXI H,COUNT ;get count MOV A,M ;check to INX H ; see CMP M ; if it is zero RNZ ; return if it is not INR A ;bump up to 1 if it is RET ;and then return ; ;********************************************************************** ;routine sets counter to -1 for starting loop ; SETCNT: LXI H,-1 ;start off with a 16 bit -1 SHLD COUNT ;and save it at count RET ;return to sender ; ;********************************************************************** ;routine moves to next directory entry - COUNT says the last one looked at ; NXTDIR: LHLD DIRMAX ;get the number of directory entries XCHG ;save it in DE LHLD COUNT ;now get the directory counter INX H ;add one to it SHLD COUNT ;and save it back for later CALL SUBHD ;looking too far? JNC GOTDIR ;if no carry still entries left JMP SETCNT ;otherwise return with a 16 bit -1 ; ;********************************************************************** ;routine to get the next directory sector off of the disk ; GOTDIR: LDA COUNT ;get the directory count low byte ANI 03H ;strip off all but low two (4 entries per sec) MVI B,5 ;set to spin low 3 over 5 SPINAB: ADD A ;move over 1 bit DCR B ;decrement count JNZ SPINAB ;loop until done STA DIROFF ;save this for later ORA A ;set flags RNZ ;return if not zero PUSH B ;otherwise save BC CALL GETDS ;looks like we set for getting the next dir CALL GETNXT ;and read the next sector POP B ;recover the BC pair JMP CHKCNG ;see if the disk changed and return ; ;********************************************************************** ;routine gets an allocation bit set into position ; and sets pointer to allocation byte for marking blocks ; BC has the group pointer - Exit with BC -> allocat