; ; BDOS CP/M REV. 2.2 ; MSIZE EQU 56 ;Memory size DOSLOC EQU (MSIZE-20)*1024+3C00H ;Where BDOS resides ; BOOTV EQU 0000H ;Vector to BIOS warm boot routine IOBYTE EQU 0003H ;System I/O device assignment TBUFF EQU 0080H ;Default DMA buffer location ; FALSE EQU 0 ; TRUE EQU 1 ; ; CR EQU 0DH ; LF EQU 0AH ; TAB EQU 09H ;Tab BACKSP EQU 08H ;Backspace CTRLC EQU 03H ;Control-C CTRLE EQU 05H ;Control-E CTRLP EQU 10H ;Control-P CTRLR EQU 12H ;Control-R CTRLS EQU 13H ;Control-S CTRLU EQU 15H ;Control-U CTRLX EQU 18H ;Control-X DELDAT EQU 0E5H ;Deleted data byte ; ORG DOSLOC ; SERIAL: DB 0,0,0 ;Room for serial number DB 0,0,0 ; ;************************************************************ ;bdos entry point. data enters as follows: ; C = command ; DE = address or 16 bit data word ; E = 8 bit data word ; ; return from bdos data is as follows: ; A = status or value ; HL = 16 bit value or address ; ENTRY: JMP BDOS ;Entry to disk monitor ; ;****************************************************************************** ; BDOS error message table - used by other CP/M utilities perhaps ; PERERR: DW PERSUB ;Permanent error subroutine SELERR: DW SELSUB ;Select error subroutine ROERR: DW ROSUB ;Read-only subroutine RONERR: DW RONSUB ;Read-only error subroutine ; ;****************************************************************************** ;bdos entry saves input data as follows: ; DE -> FCB 0 -> OLDDSK ; E -> IDATA 0 -> OUT1 ; 0 -> ODATA sets return to BEXIT on stack ; SP -> OLDSP ; ; saves input data and jumps to command routine ; BDOS: XCHG ;swap DE and HL - entry FCB is now in HL SHLD FCB ;save data in FCB for now XCHG ;put registers back MOV A,E ;move data byte into A STA IDATA ;save it LXI H,0 ;zero out HL SHLD ODATA ;save this for the return DAD SP ;add in the user stack pointer SHLD OLDSP ;save this for the return also LXI SP,USRCOD ;point to BDOS stack XRA A ;zero out a STA OLDDSK ;save this as the old disk STA OUT1 ;and at OUT1 for disk relog on exit LXI H,BEXIT ;get the return point address PUSH H ;and stuff it on the stack for exiting MOV A,C ;move the command into A CPI 41 ;Max cmds +1 - is it too big? RNC ;return if so MOV C,E ;get input data from E into C LXI H,DISTBL ;point to the dispatch table MOV E,A ;move the command from A into E MVI D,0 ;zero out D DAD D ;add in the dispatch table DAD D ;once more - over two bytes MOV E,M ;pull the jump address INX H ; into MOV D,M ; DE LHLD FCB ;get the info word into HL XCHG ;swap DE and HL PCHL ;jump to the command routine ; ;****************************************************************************** ; command dispatch table ; DISTBL: DW WBOOTF ; 0: System reset DW REDCON ; 1: Console input DW WRTCON ; 2: Console output DW REDRDR ; 3: Reader input DW PUNF ; 4: Punch output DW LISTF ; 5: List output DW DIRTIO ; 6: Direct console I/O DW GETIOB ; 7: Get I/O Byte DW PUTIOB ; 8: Set I/O Byte DW PRNBUF ; 9: Print string DW REDBUF ;10: Read console buffer DW GCSTAT ;11: Get console status DW GETVER ;12: Return version number DW RESET ;13: Reset disk system DW LOGIN ;14: Select disk DW OPEN ;15: Open file DW CLOSE ;16: Close file DW SEAR1 ;17: Search for first DW SEARN ;18: Search for next DW DELETE ;19: Delete file DW READ ;20: Read sequential DW WRITE ;21: Write sequential DW CREATE ;22: Make file DW RENAME ;23: Rename file DW GLOGIN ;24: Return login vector DW GETDRV ;25: Return current disk DW DMASET ;26: Set DMA address DW GALLOC ;27: Get addr (alloc) DW MAKRO ;28: Write protect disk DW GROVEC ;29: Get R/O vector DW SETATT ;30: Set file attributes DW GETPAR ;31: Get addr (disk parms) DW MODUSR ;32: Set/Get user code DW REDRND ;33: Read random DW WRTRND ;34: Write random DW FILSIZ ;35: Compute file size DW SETRND ;36: Set 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 ;if so go get another character DCR B ;if not decrement the count in B LDA COLUMN ;get the column counter STA CTEMP1 ;save it at CTEMP1 for later JMP RB65 ;jump over ; ; Check for Rubout (remove & echo last char.) ; CHKRUB: CPI 7FH ;do we have a rubout? JNZ CHKEOL ;if not jump over MOV A,B ;if so move b into A ORA A ;set the flags JZ RB0 ;if b was zero go get another MOV A,M ;get the char at (ODATA) DCR B ;decrement the count in B DCX H ;point back to FCB JMP RB10 ;jump to echo the char ; ; Check for Control-E (physical end-of-line) ; CHKEOL: CPI CTRLE ; is it end of line? JNZ CKPTOG ;jump over if not PUSH B ;if so save BC PUSH H ;and FCB address CALL CRLF ;Turn up a new line XRA A ;zero A STA CTEMP2 ;set it into CTEMP2 JMP RB1 ;and go get more ; ; Check for Control-P ; CKPTOG: CPI CTRLP ;is it the print toggle JNZ CKBOL ;if not jump past PUSH H ;if so save FCB address LXI H,LSTCPY ;get the pointer to print toggle byte MVI A,01H ;put a 1 into A SUB M ;subtract it from the toggle MOV M,A ;and put it back POP H ;recover FCB JMP RB0 ;and go get more ; ; Check for Control-X (bac+space to beg. current line) ; CKBOL: CPI CTRLX ;do we back up? JNZ CKREML ;if not on to the next choice POP H ;if so restore the stack pointer BLOOP: LDA CTEMP2 ;get the byte at CTEMP2 LXI H,COLUMN ;and point to the column counter CMP M ;are they the same? JNC REDBUF ;if so go try again for input DCR M ;if not decrement the CTEMP2 count CALL DELAST ;delete the character there JMP BLOOP ;and loop until we are done ; ; check for control-U ; CKREML: CPI CTRLU ;do we remove the line after newline? JNZ CKRETL ;if not try again CALL LBCRLF ;if so print a "#" and CR POP H ;restore the stack JMP REDBUF ;and try for input again ; ; Check for Control-R (retype current line after new line) ; CKRETL: CPI CTRLR ;want to retype? JNZ ECHOCC ;if not onward for next RB65: PUSH B ;if so save the count in B CALL LBCRLF ;print a "#" and CRLF POP B ;recover ODATA POP H ;and FCB PUSH H ;saving FCB again PUSH B ;and ODATA RB7: MOV A,B ;move the count into ORA A ;see if it is zero JZ FIXCOL ;if so jump over INX H ;if not point to ODATA MOV C,M ;pull the byte there into C DCR B ;decrement the count PUSH B ;save it on the stack PUSH H ;and the ODATA pointer CALL CTLOUT ;print it out expanding control chars POP H ;recover the ODATA pointer POP B ;and the count in B JMP RB7 ;loop until the line is out ; ;fix up the column counters ; FIXCOL: PUSH H ;save the pointer LDA CTEMP1 ;get CTEMP1 ORA A ;set the flags JZ RB1 ;if zero go get the next character LXI H,COLUMN ;point to the column counter SUB M ;subtract it from the value of CTEMP1 STA CTEMP1 ;and save this back in CTEMP1 FXLOOP: CALL DELAST ;delete the last character LXI H,CTEMP1 ;point to CTEMP1 DCR M ;decrement it by one JNZ FXLOOP ;loop to delete all of them JMP RB1 ;go get the next character ; ;echo the control character ; ECHOCC: INX H ;must be some other control character MOV M,A ;put the character into ODATA+1 INR B ;bump the count by one RB10: PUSH B ;save it on the stack PUSH H ;and the pointer as well MOV C,A ;put the character into C CALL CTLOUT ;print it out with grafic control chars POP H ;recover the pointer POP B ;and the count MOV A,M ;put the byte at (HL) into A CPI CTRLC ;is it an abort? MOV A,B ;put the count into A JNZ RB11 ;if no abort jump over CPI 01H ;is the count 1? JZ BOOTV ;if so boot RB11: CMP C ;if not does it equal C JC RB0 ;if less go get another char RBEXIT: POP H ;recover the pointer MOV M,B ;put the count in b there MVI C,CR ;get a CR JMP CONOUT ;and print it out ; ;****************************************************************************** ;BDOS function 1: Read console - return with byte in A ; REDCON: CALL CIECHO ;get a char echo if printable JMP GOBAK ;and go back with it ; ;****************************************************************************** ; BDOS function 3: Read reader - return with byte in A ; REDRDR: CALL READF ;get byte from reader JMP GOBAK ;and return with it ; ;****************************************************************************** ; BDOS function 6: Direct I/O ; on entry, C=FF for input, C=char for output ; (Book says E reg vice C) ; appears can enter with FE or FF --- ???? ; ; return with char or status in A ; DIRTIO: MOV A,C ;Get request INR A ;Test for FF=input request JZ INREQ ;Skip down if input request INR A ;if FF adding one will set zero flag JZ CONSF ;if it was go get console status JMP CONOF ;otherwise go send it out ; INREQ: CALL CONSF ;get console status ORA A ;set flags JZ REXIT ;return if none - restore first CALL CONIF ;if someone is there go get it JMP GOBAK ;and return with it ; ;****************************************************************************** ; BDOS function 7: get IO byte into A ; GETIOB: LDA IOBYTE ;get the iobyte JMP GOBAK ;and go back with it ; ;****************************************************************************** ; BDOS function 8: set IO byte from C into place ; PUTIOB: LXI H,IOBYTE ;point to the iobyte MOV M,C ;put the new value in from C RET ;and return ; ;****************************************************************************** ; BDOS function 9: Print console buffer until '$' ; entry string address in DE ; PRNBUF: XCHG ;swap DE and HL - HL points to buffer MOV C,L ;and get a copy MOV B,H ; of HL into BC JMP PRINT ;go to the print routine ; ;****************************************************************************** ; BDOS function 11: check console status - return in A ; GCSTAT: CALL CONBRK ;Check for abort GOBAK: STA ODATA ;save result in return info byte JR: RET ;and go back to caller ; JR1: MVI A,01H ;get a 1 for return JMP GOBAK ;set it in to rinfo and return ; ;****************************************************************************** ; console input buffer data storage area ; CTEMP1: DB 0 ;temp used for console input buffer CTEMP2: DB 02H ;temp count holder for buffer COLUMN: DB 0 ;column pointer for buffer LSTCPY: DB 0 ;list copy toggle byte CHRRDY: DB 0 ;char waiting flag OLDSP: DS 2 ;Caller's Stack ptr DS 30H ;48 (room for stack) USRCOD: DB 0 ;user and disk code lives here CURDSK: DB 1 ;current disk holder FCB: DW 0 ;Storage for caller's parms ODATA: DW 0 ;return information storage ; ;****************************************************************************** ; Goes to Select error ; PTSERR: LXI H,SELERR ;HL=Select error sub addr ; ;****************************************************************************** ;routine branches to (HL) ; HLGO: MOV E,M ;get (HL) into E INX H ;bump pointer MOV D,M ;and pull in (HL+1) into D XCHG ;swap DE and HL PCHL ;swap PC and HL and off we go ; ;****************************************************************************** ; routine to move C bytes DE to HL ; MOVE: INR C ;add 1 to move count to start loop MOVLOP: DCR C ;decrement the loop count RZ ;return if we are done LDAX D ;otherwise get the byte at (DE) MOV M,A ;and put it at (HL) INX D ;bump the source pointer INX H ;and the destination pointer JMP MOVLOP ;and loop til you are done ; ;****************************************************************************** ;routine to set up disk identification data for access ; DISKID: LDA CURDSK ;get the current logged disk MOV C,A ;and put it into C CALL SELF ;Select disk - HL comes back with DISP parm MOV A,H ;move H into A for the zero test ORA L ;or in L - zero says select error RZ ;so return to LOGDSK with error MOV E,M ;otherwise move INX H ; translation address MOV D,M ; into DE INX H ;bump once more SHLD SCRT0 ;and save this address at scratch 0 INX H ;move the pointer INX H ; two doors down SHLD SCRT1 ;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 DIRPNT ;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 get a 12 DAD D ;add in the offset RET ;return with HL set to extent address ; ;****************************************************************************** ;routine sets HL to current record (CR) and DE to record count (RC) ; SETPT: LHLD FCB ;get the FCB address LXI D,15 ;get an offset of 15 DAD D ;HL now points to RC address XCHG ;swap new value into DE LXI H,17 ;now get an offset of 17 DAD D ;add this in so HL points to CR RET ;return with HL and DE set ; ;****************************************************************************** ;routine appears to get the extent byte from FCB and set up extent pointers ; FIXEXT: CALL SETPT ;set record pointers MOV A,M ;get the current record for sequential I/O STA ESCNT2 ;and save it XCHG ;swap DE and HL MOV A,M ;pull in the record count for this extent STA RECCNT ;and save it CALL GETEX ;point to extent byte in FCB LDA EXTMSK ;get the extent mask ANA M ;and the extent byte extent mask STA ESCNT1 ;saving the result RET ;and return ; ;****************************************************************************** ;routine appears to set up record counters ; FIXREC: CALL SETPT ;set the record pointers LDA DCODE ;get the byte CPI 02H ;is it a 2? JNZ FIXRC1 ;if not jump past XRA A ;zero out A FIXRC1: MOV C,A ;move A into C LDA ESCNT2 ;get the byte ADD C ;add it to C MOV M,A ;put the result away at CR in FCB XCHG ;swap DE and HL LDA RECCNT ;get the byte ( record count) MOV M,A ;place it at RC in FCB RET ;and return ; ;****************************************************************************** ; spin HL by C bits right ; SPINHL: INR C ;set up for the loop decrement of C SPH1: DCR C ;decrement loop count RZ ;return if all is well MOV A,H ;otherwise get H into A ORA A ;clear carry RAR ;rotate right through carry MOV H,A ;put the new byte back MOV A,L ;move L into A RAR ;rotate it as well MOV L,A ;and put it back JMP SPH1 ;loop until the count quits ; ;****************************************************************************** ;routine to compute a check sum across a buffer of 128 bytes ; SUM128: MVI C,128 ;we will sum 128 bytes in the temp buffer LHLD DTEMP ;get the address of the buffer from DTEMP XRA A ;zero A SUM1: ADD M ;add in the byte at (HL) INX H ;bump pointer to next DCR C ;decrement count JNZ SUM1 ;loop if we are not finished RET ;otherwise go back with sum in A ; ;****************************************************************************** ;routine to slide HL left C bits ; SPINBIT:INR C ;bump for the decrement SPB1: DCR C ;decrement the loop count RZ ;return when done DAD H ;slide HL left one bit JMP SPB1 ;spin until count zeroes ; ;****************************************************************************** ;set the disk bit in the vector - BC has the current vector ; SETDBT: PUSH B ;save the BC pair LDA CURDSK ;get the current disk MOV C,A ;put it into C LXI H,1 ;get a 1 into HL CALL SPINBIT ;spin HL into right spot to set RO POP B ;recover BC MOV A,C ;put C into A ORA L ;or in the RO flag low MOV L,A ;put it back MOV A,B ;pull in the high byte ORA H ;or in the RO flag high MOV H,A ;put it back RET ;and return with new RO vector in HL ; ;****************************************************************************** ;routine to check for drive READ-ONLY state ; ISRO: LHLD ROVEC ;point to the RO vector LDA CURDSK ;get the current disk MOV C,A ;and put it into C CALL SPINHL ;spin the RO vector into position MOV A,L ;and pull L into A ANI 01H ;mask off all but bit 0 RET ;and return with flags zero for no,1 for yes ; ;****************************************************************************** ; BDOS function 28: makes drive read only ; MAKRO: LXI H,ROVEC ;point to the RO vector MOV C,M ;pull in the low half INX H ;point to high byte MOV B,M ;pull this one into B - got it all CALL SETDBT ;go set this drive RO SHLD ROVEC ;and save the new RO vector back for later LHLD DIRMAX ;get the directory max value INX H ;point to RO + 1 XCHG ;swap this address into DE LHLD SCRT0 ;point to scratch0 MOV M,E ;put RO + 1 INX H ; into MOV M,D ; scratch0 RET ;and go back ; ;****************************************************************************** ;routine to check for file read only ; CHKFRO: CALL PNTDIR ;set up HL to DMA + offset CHKFR1: LXI D,9 ;get 9 - point to file ro bit in t1 DAD D ;add it to the pointer MOV A,M ;pull in this byte RAL ;rotate it left through carry RNC ;if bit 7 was not set return LXI H,RONERR ;HL=Read-only error sub addr JMP HLGO ;Go to routine ; ;****************************************************************************** ;see if the selected drive is read only - say we cant write if so ; ROCHK: CALL ISRO ;is the current drive read only? RZ ;return if not - we can write LXI H,ROERR ;otherwise point to error routine JMP HLGO ;Go to routine ; ;****************************************************************************** ;routine to point to directory entry stored in buffer ; PNTDIR: LHLD DTEMP ;get the temp buffer address out LDA DIROFF ;get the offset from storage PNTD1: ADD L ;add it to A MOV L,A ; and put it into L RNC ; if no overflow go back INR H ;otherwise add in the overflow RET ;and then return ; ;****************************************************************************** ;routine gets the S2 byte at (FCB + 14) ; GETS2: LHLD FCB ;get the input FCB address LXI D,14 ;point 14 downstream DAD D ;set up this address MOV A,M ;pull this byte into A RET ;and go back ; ;****************************************************************************** ;routine sets the S2 byte at (FCB + 14) to zero ; ZS2: CALL GETS2 ;get the address of S2 MVI M,0 ;and set in a zero RET ;and go back ; ;****************************************************************************** ;routine sets bit 8 of S2 ; SETS28: CALL GETS2 ;point to S2 ORI 80H ;or in the eigth bit MOV M,A ;and put this value back RET ;and return ; ;****************************************************************************** ;routine subtracts counter from word at scratch0 ; SUBCS0: LHLD COUNT ;get the counter XCHG ;swap it into DE LHLD SCRT0 ;then get scratch0 data word MOV A,E ;and subtract SUB M ; data at scratch 0 INX H ; from MOV A,D ; the SBB M ; counter RET ;and go back with result set in flags? ; ;****************************************************************************** ;routine checks count against scratch0 and decrements count if ok ; CHKINC: CALL SUBCS0 ;compare counter and (scratch0) RC ;if (scratch0)>counter return INX D ;otherwise bump DE MOV M,D ;and place it DCX H ; into MOV M,E ; (scratch0) RET ;and return ; ;****************************************************************************** ;routine to compute HL = DE - HL ; SUBHD: MOV A,E ;pull E into A SUB L ;subtract from L MOV L,A ;and put it back MOV A,D ;then move in the D value SBB H ;and subtract with borrow MOV H,A ;put back into H RET ;and return ; ;****************************************************************************** ;routine to check directory area with check sum to see if it changed ; CHKSUM: MVI C,0FFH ;get the default return code CHKCNG: LHLD DIRPNT ;point to the directory area XCHG ;put the address into DE LHLD CKSIZE ;get the check size CALL SUBHD ;compute HL = DE - HL RNC ;return if they were the same PUSH B ;otherwise save the BC regs CALL SUM128 ;and compute the new check sum LHLD CHANGE ;point to the change storage area XCHG ;swap DE and HL LHLD DIRPNT ;point to the directory area DAD D ;add in the change word POP B ;recover BC INR C ;bump count by one JZ CHK1 ;if zero save A at (HL) and return CMP M ;if not check A and (HL) RZ ;return if they are the same CALL SUBCS0 ;otherwise check counter and (scratch0) RNC ;return if they are ok CALL MAKRO ;make the disk read 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 -> allocation byte ; D -> bit for this group ; A -> allocation byte spun into place ; GETBIT: MOV A,C ;save entry C into A ANI 07H ;strip off all but low 3 INR A ;add one to the result - bit number for group MOV E,A ;and put this into E MOV D,A ;and another into D for exit MOV A,C ;get another copy of C RRC ;spin RRC ; it RRC ; right 3 ANI 1FH ;strip off high 3 leaving high 5 in place MOV C,A ;put this into C MOV A,B ;now get entry B ADD A ;and spin ADD A ; it left ADD A ; four ADD A ; bits ADD A ; ORA C ;or in the word at C MOV C,A ;and put it back into C MOV A,B ;get another copy of B RRC ;slide RRC ; over 3 RRC ; right ANI 1FH ;strip off all but low 3 MOV B,A ;place this into B, BC is set LHLD ALLOCA ;now get the allocation pointer DAD B ;add in the offset MOV A,M ;and pull in this byte of it GBLOOP: RLC ;rotate it left through carry DCR E ;decrement the rotate count JNZ GBLOOP ;loop until the bit is in place RET ;and then return with it ; ;****************************************************************************** ;routine to set allocation bit - BC has the group pointer ;enter with E set with current allocation byte ; SETBIT: PUSH D ;save DE CALL GETBIT ;get allocation bit into position ANI 0FEH ;clear bit zero POP B ;restore BC - C has bit to set ORA C ;or in the allocation bit PUTBAK: RRC ;spin right DCR D ;decrement count JNZ PUTBAK ;loop til it is back in place MOV M,A ;and then put it back in RET ;and return ; ;****************************************************************************** ;routine to update allocation map for current directory ; FIXALL: CALL PNTDIR ;set pointer to DMA + offset LXI D,16 ;get offset of 16 DAD D ;add this in - point to group bytes PUSH B ;save BC with set bit in C MVI C,17 ;set up to spin 16 times FIXAL1: POP D ;get data off stack DCR C ;decrement counter RZ ;return if it is zero PUSH D ;otherwise save the data again LDA BSIZE ;get the block size ORA A ;set the flags JZ FIXAL2 ;if zero jump - blocks are > 1024 PUSH B ;otherwise save the current count PUSH H ;and the address data MOV C,M ;pull in the word at (HL) MVI B,0 ;zero out B - BC now has group pointer JMP FIXAL3 ;and jump over ; FIXAL2: DCR C ;these groups are two bytes wide so dec again PUSH B ;save the new count on the stack MOV C,M ;pull in the high group byte INX H ; point to low MOV B,M ; and pull it in so BC has both PUSH H ;save this address FIXAL3: MOV A,C ;put the new data into A ORA B ;compare with B JZ FIXAL4 ;if it is zero block is not allocated LHLD DSIZE ;get the number of disk blocks MOV A,L ;subtract SUB C ; HL MOV A,H ; from SBB B ; BC CNC SETBIT ;if not too big, go set the bit FIXAL4: POP H ;recover the group pointer INX H ;bump it by one POP B ;recover count JMP FIXAL1 ;and spin until done ; ;****************************************************************************** ;routine to get the allocation vector ; GETAL: LHLD DSIZE ;get the max number of blocks MVI C,03H ;set to CALL SPINHL ; divide by eight INX H ;add 1 for the map size MOV B,H ;and put map size MOV C,L ; into BC - number of bytes in allocation map LHLD ALLOCA ;point to allocation storage area CLRALL: MVI M,0 ;put in a zero INX H ;bump the pointer DCX B ;one less group to zero out MOV A,B ;put the new count into A ORA C ;check for done JNZ CLRALL ;if not loop until so LHLD AL01 ;if done get directory allocation bits XCHG ;swap DE and AL01 LHLD ALLOCA ;point to allocation stogare MOV M,E ;put directory allocation bits INX H ; into that MOV M,D ; storage area cause they are busy CALL HOMDSK ;home the selected disk LHLD SCRT0 ;get the scratch 0 address MVI M,03H ;save a 3 at (scratch0) INX H ;point to (scratch0+1) MVI M,0 ;and zero this out - it has 16 bit 3 now CALL SETCNT ;set a counter to -1 GETA1: MVI C,0FFH ;set the exit code in CALL NXTDIR ;set the next directory pointers CALL CHKCNT ;was there one? RZ ;return if not CALL PNTDIR ;point to the right spot MVI A,DELDAT ;get the file deleted mark CMP M ;is this what we see? JZ GETA1 ;keep looking for a nonempty block LDA USRCOD ;get the user byte CMP M ;test this against the first byte JNZ GETA2 ;if we don't match mark this block INX H ;bump pointer to file name entry MOV A,M ;pull the byte in SUI '$' ;is this a submit file name $$$.SUB JNZ GETA2 ;if not take this block also DCR A ;otherwise set A to FF STA ODATA ;and save it at ODATA for CCP to use GETA2: MVI C,01H ;set the bit into C CALL FIXALL ;fix this allocation bit CALL CHKINC ;test for too far - bump count if not JMP GETA1 ;spin until done with the scan ; ;****************************************************************************** ;routine loads return code and goes back to sender ; RETCOD: LDA SCODE1 ; JMP GOBAK ; ; ;****************************************************************************** ;routine appears to check extent byte ; CHKEXT: PUSH B ;get the entry BC safe PUSH PSW ;and the accumulator LDA EXTMSK ;get the extent mask CMA ;flip it over MOV B,A ;and save it into B MOV A,C ;move C into A ANA B ;and it with the flipped extent mask MOV C,A ;save this into C POP PSW ;get the entry A back ANA B ;and with flip extent mask SUB C ;subtract C ANI 1FH ;mask off high bit POP B ;recover the entry BC RET ;and go back confused ; ;****************************************************************************** ;search for a specified file - set flags to say how it went ;used by all file routines to set up file information ; SEARCH: MVI A,0FFH ;set return code up STA SCODE1 ;and save it for later LXI H,SCODE ;point to search code area MOV M,C ;save entry code away LHLD FCB ;get the entry FCB address SHLD SEARA ;save a copy CALL SETCNT ;set the search counter up CALL HOMDSK ;home the selected disk SERCHN: MVI C,0 ; CALL NXTDIR ;locate next directory entry CALL CHKCNT ;check to see if one was found JZ NOTFND ;jump over if not LHLD SEARA ;get the FCB address out XCHG ;into DE LDAX D ;get the byte at (DE) CPI DELDAT ;is it a deleted file? JZ SERCH1 ;if so jump over PUSH D ;save this address for later CALL SUBCS0 ;check count and increment POP D ;recover the address JNC NOTFND ;if no carry we didn't find the file SERCH1: CALL PNTDIR ;point to the indicated directory entry LDA SCODE ;get the search code out MOV C,A ;stuff it into C MVI B,0 ;zero out the high byte SERCH2: MOV A,C ;move it to A ORA A ;set the flags - check for zero JZ SEREXT ;if so jump over LDAX D ;otherwise get the FCB file name char CPI '?' ;is it a wild card name? JZ MATCH ;if so jump over MOV A,B ;check B CPI 0DH ;is it past extent byte? JZ MATCH ;if so this is a good file to go back with CPI 0CH ;how about at the extent byte? LDAX D ;get the byte again JZ SERCH3 ;if so jump over SUB M ;otherwise subtract the two bytes ANI 7FH ;strip off the special bits JNZ SERCHN ;if not zero this doesnt match so try again JMP MATCH ;if it matches press on ; SERCH3: PUSH B ;save BC from harm MOV C,M ;pull the byte into C CALL CHKEXT ;check the extent byte POP B ;recover BC JNZ SERCHN ;if not zero search again MATCH: INX D ;otherwise bump FCB pointer INX H ;and pointer into memory INR B ;increment the FCB count DCR C ;decrement the C counter JMP SERCH2 ;and keep going with the search ; SEREXT: LDA COUNT ;get the count ANI 03H ;strip off the high 5 bits STA ODATA ;save this at ODATA for return LXI H,SCODE1 ;get the return code address MOV A,M ;pull it into A RAL ;slide it left one bit through carry RNC ;if bit 7 not set return XRA A ;otherwise zero out A MOV M,A ;put it back at SCODE1 RET ;then go back ; NOTFND: CALL SETCNT ;set the counter for error code MVI A,0FFH ;and set the return byte into A JMP GOBAK ;then return to sender ; ;****************************************************************************** ;routine to delete specified file ; DELETF: CALL ROCHK ;see if the selected drive is read only MVI C,0CH ;look for name and extent CALL SEARCH ;go find the file DELOOP: CALL CHKCNT ;was it there RZ ;return if not - all extents are marked CALL CHKFRO ;see if file was read only CALL PNTDIR ;point to directory entry MVI M,DELDAT ;put in the file deleted marker MVI C,0 ;set C for the fix bit routine CALL FIXALL ;fix the allocation vector CALL WRTDS ;write out the directory sector CALL SERCHN ;search for the next extent JMP DELOOP ;and loop until done ; ;****************************************************************************** ;routine to check for block availability ; BLKAVL: MOV D,B ;move BC MOV E,C ; into DE BLKA1: MOV A,C ;save a copy of C into A ORA B ; check if BC is zero JZ BLKA2 ;if so jump over and look again DCX B ;decrement the counter PUSH D ;save on the stack PUSH B ;save BC as well CALL GETBIT ;check the allocation bit RAR ;slide it right through carry JNC TAKBLK ;if no carry it is free POP B ;restore POP D ; stack BLKA2: LHLD DSIZE ;get the disk size block count MOV A,E ;subtract SUB L ; HL MOV A,D ; from SBB H ; DE JNC BLKA3 ;if no carry all is well - jump over INX D ;otherwise bump DE PUSH B ;save on stack PUSH D ; for next look MOV B,D ;put DE MOV C,E ; into BC CALL GETBIT ;get this allocation bit out RAR ;slide right to test JNC TAKBLK ;if no carry take the block POP D ;recover the POP B ; stack JMP BLKA1 ;and keep looking ; TAKBLK: RAL ;restore the allocation bit INR A ;add one to set bit CALL PUTBAK ;write it back into the vector POP H ;recover POP D ; the stack RET ;and return to the caller ; BLKA3: MOV A,C ;is BC ORA B ; zero? JNZ BLKA1 ;if not jump back and continue LXI H,0 ;otherwise get a 16 bit zero RET ;and return with it ; ;****************************************************************************** ;routine to move the directory entry ; MOVDIR: MVI C,0 ;set C to zero for the move MVI E,32 ;get the directory size ; ;****************************************************************************** ;routine to set the directory onto the disk ; SETDIR: PUSH D ;save the count on the stack MVI B,0 ;zero the high byte LHLD FCB ;get the FCB address DAD B ;add in the offset for the move XCHG ;save it in DE CALL PNTDIR ;point to the directory area POP B ;recover the count CALL MOVE ;Move C bytes DE to HL SETD1: CALL GETDS ;go pull in the rest of the directory JMP WRTDS ;and write the current sector out ; ;****************************************************************************** ;routine to rename the selected file ; RENAMF: CALL ROCHK ;see if the selected disk is read only MVI C,0CH ;no- check name and extent CALL SEARCH ;by calling the search routine LHLD FCB ;get the information FCB address MOV A,M ;and pull the drivecode LXI D,16 ;we will move down 16 bytes DAD D ;point to the new file name in FCB MOV M,A ;put in the new drive code RENF1: CALL CHKCNT ;check the loop counter RZ ;return if all extents are renamed CALL CHKFRO ;see if the filw is read only MVI C,10H ;set to move 16 bytes of the file name MVI E,0CH ;get the search code ready CALL SETDIR ;set in the file name CALL SERCHN ;search for the next extent JMP RENF1 ;loop until no more extents ; ;****************************************************************************** ;routine sets up pointers to indicated file ; SETFN: MVI C,0CH ;look at name and extent CALL SEARCH ;search for the file SETF1: CALL CHKCNT ;see if there was an entry left RZ ;return when all extents have been found MVI C,0 ; MVI E,0CH ; CALL SETDIR ; CALL SERCHN ;look for the next extent JMP SETF1 ;and loop till all found ; ;****************************************************************************** ;routine to open selected file ; OPENF: MVI C,0FH ;look at FCB through S1 and S2 bytes CALL SEARCH ;search for the file CALL CHKCNT ;was it found RZ ;return if not OPEN1: CALL GETEX ;get the extent byte MOV A,M ;pull it into A PUSH PSW ;save it for later PUSH H ;save the address as well CALL PNTDIR ;point to the directory area XCHG ;and put the address into DE LHLD FCB ;get the FCB address MVI C,32 ;Bytes to move PUSH D ;save the directory address CALL MOVE ;Move C bytes DE to HL CALL SETS28 ;set bit 7 of S2 POP D ;recover the directory address LXI H,12 ;set to point downstream DAD D ;now pointing to extent byte of directory MOV C,M ;pull this byte in LXI H,15 ;ready to move down again DAD D ;now point to MOV B,M ;put the extent byte back here POP H ;recover the FCB address POP PSW ;and the entry extent byte MOV M,A ;put the byte back MOV A,C ;move the directory extent byte into A CMP M ;are they the same MOV A,B ; JZ OPEN2 ;if so jump MVI A,0 ;otherwise get a zero JC OPEN2 ;if it was too big jump over MVI A,80H ;now get bit 7 set OPEN2: LHLD FCB ;point to the FCB again LXI D,15 ;ready to move it DAD D ;now point to record count MOV M,A ;pull this out RET ;and go back with it ; ;****************************************************************************** ;routine to check if (HL) is zero - if so move (DE) to (HL) ; CHKCPY: MOV A,M ;test (HL) INX H ; for ORA M ; zero DCX H ;point back RNZ ;return if not zero LDAX D ;otherwise get (DE) MOV M,A ;put it at (HL) INX D ;bump INX H ;bothof them LDAX D ;get (DE) MOV M,A ;and put it at (DE) DCX D ;restore both DCX H ; pointers RET ;and return ; ;****************************************************************************** ;routine to close selected file ; CLOSEF: XRA A ;cheap zero STA ODATA ;put it at return info byte STA COUNT ;and zero out the count STA COUNT+1 ; word CALL ISRO ;is the disk read only RNZ ;return if so - can't close it CALL GETS2 ;get the S2 byte from the FCB ANI 80H ;strip off the low eight bits RNZ ;return if not zero MVI C,0FH ;otherwise set to look through S2 in FCB CALL SEARCH ;search for the file CALL CHKCNT ;was it there RZ ;zero says done LXI B,16 ;get the pointer for group bytes in dir CALL PNTDIR ;point to the directory DAD B ;add in the offset - pointing right XCHG ;swap DE and HL LHLD FCB ;point to the FCB DAD B ;add in the offset again MVI C,10H ;get set for 16 bytes to move CLOSF1: LDA BSIZE ;get the block size byte out ORA A ;is it zero JZ CLOSF4 ;if so blocks > 1024 so jump past ; ;small blocks ; MOV A,M ;pull the byte out ORA A ;test for zero LDAX D ;get the (DE) out JNZ CLOSF2 ;if not zero jump over MOV M,A ;otherwise put A into place CLOSF2: ORA A ;test the byte again JNZ CLOSF3 ;if not zero jump past MOV A,M ;otherwise pull in the byte STAX D ;stuff it back at (DE) CLOSF3: CMP M ;are they the same JNZ CLOSF7 ;if not jump JMP CLOSF5 ;otherwise skip over ; ;big blocks ; CLOSF4: CALL CHKCPY ;check and increment XCHG ;exchange again CALL CHKCPY ;check again for next byte XCHG ;exchange back - ready to continue LDAX D ;get the new byte from (DE) CMP M ;does it match (HL) JNZ CLOSF7 ;if not over we go INX D ;bump pointer INX H ; data LDAX D ;get next byte CMP M ;compare it as well JNZ CLOSF7 ;again if no match jump DCR C ;decrement the count CLOSF5: INX D ;increment the INX H ; pointers DCR C ;decrement the count JNZ CLOSF1 ;if not done spinn around again LXI B,-20 ;point back DAD B ; 20 bytes XCHG ;swap pointers DAD B ;and back this one up as well LDAX D ;pull in the byte at (DE) CMP M ;test against (HL) JC CLOSF6 ;if (HL)>(DE) jump MOV M,A ;if not put A into (HL) LXI B,3 ;get a 3 DAD B ;point down 3 XCHG ;swap pointers again DAD B ;down three also MOV A,M ;get the byte at (HL) STAX D ;stuff it in at (DE) CLOSF6: MVI A,0FFH ;get the flag data STA RWFLG1 ;set flag to say reading JMP SETD1 ;write out the directory block and return ; CLOSF7: LXI H,ODATA ;point to output data byte DCR M ;decrement it 1 RET ;and go back ; ;****************************************************************************** ;make a new directory entry for a new file ; MAKE: CALL ROCHK ; is the disk read only LHLD FCB ;nope - so get the FCB address PUSH H ;and save it on the stack LXI H,TINFO ;point to the TINFO area SHLD FCB ;save this as the FCB address MVI C,01H ;set to look at only the ET byte in the FCB CALL SEARCH ;search for the new file name CALL CHKCNT ;was it there POP H ;get the old FCB back SHLD FCB ;restore it right RZ ;return if it was there XCHG ;save FCB in DE LXI H,15 ;get offset to record count DAD D ;and set the pointer there MVI C,17 ;get set to spin through 16 bytes XRA A ;get a zero ready MAK1: MOV M,A ;and move zero INX H ; into DCR C ; all JNZ MAK1 ; the group bytes LXI H,13 ;get the offset to the S1 byte DAD D ;and point to it MOV M,A ;zero this as well CALL CHKINC ;see if we have more to do CALL MOVDIR ;copy the directory in place JMP SETS28 ;set bit 7 of S2 and return ; ;****************************************************************************** ;routine sets up for the next extent access for either read or write ; NEXTEX: XRA A ;get a zero STA RWFLG1 ;say we are writing CALL CLOSEF ;close the file CALL CHKCNT ;did it exist RZ ;return if not LHLD FCB ;get the FCB address LXI B,12 ;set to point to extent byte DAD B ;now it does MOV A,M ;pull the thing in INR A ;bump by one ANI 1FH ;strip off bit 7 MOV M,A ;and put it back JZ NEXTE1 ;if it was zero jump over MOV B,A ;otherwise save it in B LDA EXTMSK ;get the extent mask ANA B ;and use it LXI H,RWFLG1 ;and point to the flag ANA M ;and in this byte as well JZ NEXTE2 ;if zero jump past JMP NEXTE3 ;otherwise off to ; NEXTE1: LXI B,2 ;ready to bump 2 more DAD B ;add it in INR M ;increment this byte MOV A,M ;then pull it in ANI 0FH ;strip off the high 4 bits JZ NEXTE5 ;if zero jump NEXTE2: MVI C,0FH ;look through S2 byte in FCB CALL SEARCH ;look for the indicated file CALL CHKCNT ;see if it was there JNZ NEXTE3 ;if so open the next extent LDA RWFLG2 ;get the read/write flag INR A ;add one JZ NEXTE5 ;if it was FF then return CALL MAKE ;make a new file directory entry CALL CHKCNT ;was it ok JZ NEXTE5 ;if not set up exit routine JMP NEXTE4 ;otherwise set up extent pointers and return ; NEXTE3: CALL OPEN1 ;go open the next extent NEXTE4: CALL FIXEXT ;set the pointers up XRA A ;zero A for the return JMP GOBAK ;and go back ; NEXTE5: CALL JR1 ;set to return with problems JMP SETS28 ;set bit 7 of S2 to say so ; ;****************************************************************************** ;routine to read the disk ; DSKRED: MVI A,01H ;get a ! STA DCODE ;save it for the disk code byte RRERR: MVI A,0FFH ;get a read flag STA RWFLG2 ;set it in for the control CALL FIXEXT ;go fix the extent LDA ESCNT2 ;get the extent low byte LXI H,RECCNT ;point to the record count CMP M ;are they the same? JC DSKR1 ;if record count was bigger, jump over CPI 80H ;was the count 80 hex? JNZ DERR ;if not jump out with an error CALL NEXTEX ;go get the next extent XRA A ;set a=0 STA ESCNT2 ;save this as the new extent low byte LDA ODATA ;get the output data ORA A ;set the flags JNZ DERR ;if it was not zero exit with error DSKR1: CALL SECGRP ;otherwise group from the sector count CALL TSTGRP ;see if it is ok JZ DERR ;if not go back CALL COMSEC ;if co compute the right sector CALL GETD1 ;get the directory set right CALL RDSEC ;read the sector JMP FIXREC ;fix up the record and return ; DERR: JMP JR1 ;set the error exit and return ; ;****************************************************************************** ;routine to write the disk ; DSKWRT: MVI A,01H ;set the disk STA DCODE ; code up RWERR: MVI A,0 ;set the read write flag for write STA RWFLG2 ;and save it for later CALL ROCHK ;see if the drive is protected LHLD FCB ;get the FCB address CALL CHKFR1 ;see if the file is read only CALL FIXEXT ;fix up the extent LDA ESCNT2 ;and get the low extent byte CPI 80H ;is it greater that 80h JNC JR1 ;if so exit with errors CALL SECGRP ;compute the right group CALL TSTGRP ;see if it is ok MVI C,0 ;get a zero JNZ DSKW05 ;if ok jump over CALL COMBLK ;compute the block STA EXTBLK ;save it at extent block area LXI B,0 ;get a 16 bit 0 ORA A ;set flags JZ DSKW01 ;if zero jump over MOV C,A ; DCX B ; CALL GETGRP ;get the group counter out MOV B,H ;save it MOV C,L ; in BC DSKW01: CALL BLKAVL ;see if a block is available MOV A,L ;check for ORA H ; zero returned JNZ DSKW02 ;if not zero jump over MVI A,02H ;set the disk full message JMP GOBAK ;and return ; DSKW02: SHLD GROUP ;save the group counter XCHG ;put it inti DE as well LHLD FCB ;get the FCB address LXI B,16 ;set the offset to the record count DAD B ;and point to it LDA BSIZE ;get the block size ORA A ;is it zero LDA EXTBLK ;get the extent block out JZ DSKW03 ;if blocks are greater than 1024 jump CALL PNTD1 ;point to the directory entry MOV M,E ;save in the group low byte JMP DSKW04 ;and jump over ; DSKW03: MOV C,A ;save the extblk byte into C MVI B,0 ;zero out B DAD B ;slide the offset DAD B ; right 2 bits MOV M,E ;put the group low byte into place INX H ;point to the high byte MOV M,D ;and put this in too DSKW04: MVI C,02H ;set the exit code DSKW05: LDA ODATA ;and the exit data ORA A ;see if it is zero RNZ ;return if not PUSH B ;otherwise save the extent block pointer CALL COMSEC ;go compute the sector LDA DCODE ;get the disk code out DCR A ;count it DCR A ; down 2 JNZ DSKW08 ;if not zero jump over POP B ;recover the block offset PUSH B ;save it again MOV A,C ;put low into A DCR A ;decrement it by 1 DCR A ; and 1 more JNZ DSKW08 ;if not zero then jump PUSH H ;save the address of the directory group LHLD DTEMP ;get the temp address MOV D,A ;save the group low byte into D DSKW06: MOV M,A ;and put it into memory INX H ;point to the high INR D ;add one to D JP DSKW06 ;if positive jump past CALL SETEMP ;set the temp buffer LHLD DIRPNT ;get the directory pointer MVI C,02H ;put a 2 into C DSKW07: SHLD GROUP ;save the group pointer PUSH B ;push the counter onto the stack CALL GETD1 ;get the next directory POP B ;recover the group pointer CALL WRSEC ;write the sector LHLD GROUP ;get the group data MVÉ C,° ; LDA BLKMSK ;get the block mask MOV B,A ;put it into B ANA L ;and in L CMP B ;compare with B INX H ;and bump to next JNZ DSKW07 ;if not the same jump over POP H ;recover HL SHLD GROUP ;and save it at GROUP CALL SETDMA ;set the dma address DSKW08: CALL GETD1 ;and get the next directory POP B ;recover BC PUSH B ;and save it again CALL WRSEC ;write the sector POP B ;restore BC again LDA ESCNT2 ;get the extent count low byte LXI H,RECCNT ;point to the record count CMP M ;are they the same JC DSKW09 ;if RECCNT>ESCNT2 jump MOV M,A ;otherwise save a new RECCNT INR M ;bump pointer by one MVI C,02H ;get a 2 to force fall through below DSKW09: DCR C ;decrement DCR C ; by 2 JNZ DSKW10 ;if not zero jump over PUSH PSW ;save the flags CALL GETS2 ;point to S2 byte ANI 7FH ;strip off bit 7 MOV M,A ;and put it back this way POP PSW ;recover the flags DSKW10: CPI 7FH ;test the byte in A JNZ DSKW12 ;if not 7F jump over LDA DCODE ;get the disk code out CPI 01H ;is it 1 JNZ DSKW12 ;if not jump CALL FIXREC ;if so fix the record bytes CALL NEXTEX ;and get the next extent ready LXI H,ODATA ;point to the output data byte MOV A,M ;pull it in ORA A ;check for zero JNZ DSKW11 ;if not jump DCR A ;decrement it by one if it was zero STA ESCNT2 ;and save this at ESCNT2 DSKW11: MVI M,0 ;zero out ODATA DSKW12: JMP FIXREC ;fix the records up and return ; ;****************************************************************************** ;random file access routine - C has the code for access type ; FF for read, 0 for write ; RANFIL: XRA A ;zero the DCODE STA DCODE ; byte RANF1: PUSH B ;save the entry code in C LHLD FCB ;point to the FCB address XCHG ;and save it in DE for later LXI H,33 ;get the offset to R0 DAD D ;pointing to R0 byte MOV A,M ;pull it in ANI 7FH ;and strip off the sector count for this ext PUSH PSW ;save it on the stack MOV A,M ;get R0 again RAL ;slide it left INX H ;point to R1 MOV A,M ;pull this in RAL ;rotate this left also pulling in bit 7 of R0 ANI 1FH ;strip off the low 5 bits for extent count MOV C,A ;save this in C MOV A,M ;get R1 again RAR ;rotate RAR ; it RAR ; right RAR ; 4 bits ANI 0FH ;and mask off the block counter MOV B,A ;put it into B POP PSW ;A has record, B has block, C has extent ; (0-127) (0-15) (0-31) INX H ;point to the overflow byte MOV L,M ;pull it into L INR L ;add one DCR L ;then sub 1 to set flag MVI L,06H ;get seek past end of disk error JNZ RANF5 ;if not zero exit with error LXI H,32 ;offset to current record DAD D ;point to it MOV M,A ;put the current record byte into place LXI H,12 ;now get the DAD D ; extent pointer MOV A,C ;pull the new one into A SUB M ;compare with the one we are looking at JNZ RANF2 ;if not right, jump over LXI H,14 ;otherwise check the DAD D ; S2 byte MOV A,B ;put the new block into A SUB M ;and compare with what is there ANI 7FH ;strip off bit 7 for the check JZ RANF3 ;if all is well jump back RANF2: PUSH B ;otherwise save the counters PUSH D ;and the FCB address CALL CLOSEF ;and close the current extent POP D ;recover the FCB address POP B ;and the counters MVI L,03H ;set the close error message LDA ODATA ;check the result INR A ; should be >0 if ok JZ RANF4 ;if not we had problems so go back LXI H,12 ;point to DAD D ; the extent byte MOV M,C ;put in the new value LXI H,14 ;now point to DAD D ; the S2 byte MOV M,B ;put the new block in place CALL OPENF ;go open the new extent LDA ODATA ;get the result INR A ;and check if ok JNZ RANF3 ;if non zero the open was ok so go back POP B ;recover the entry code PUSH B ;then resave it MVI L,04H ;set the seek to unwritten data error INR C ;bump the entry code 1 JZ RANF4 ;if it is zero we exit with the error CALL MAKE ;otherwise we are writing so open new extent MVI L,05H ;get the directory overflow error LDA ODATA ;get the result INR A ;add one to check JZ RANF4 ;if errors exit RANF3: POP B ;otherwise restore the entry code XRA A ;set zero into A JMP GOBAK ;and return with FCB set right ; RANF4: PUSH H ;save the current error byte in L CALL GETS2 ;point to the S2 byte MVI M,0C0H ;set in a C0 hex POP H ;recover the error byte RANF5: POP B ;recover the entry code MOV A,L ;set the error byte into A STA ODATA ;save it for the exit JMP SETS28 ;set bit 7 of S2 for later ; ;****************************************************************************** ;routine to read random record ; RDREC: MVI C,0FFH ;set the code for reading CALL RANFIL ;call the random routine to fix FCB up CZ RRERR ;If ok then read the record RET ;and return ; ;****************************************************************************** ;routine to write random record ; WRREC: MVI C,0 ;set the code for writing CALL RANFIL ;go fix the FCB up CZ RWERR ;if ok then write the sector RET ;and return ; ;****************************************************************************** ;routine computes record count for random access ; COMREC: XCHG ;put the FCB into DE - offset into HL DAD D ;set the pointer to the record byte MOV C,M ;pull the record count into C MVI B,0 ;zero out B LXI H,12 ;now set offset DAD D ; to point to the extent byte MOV A,M ;pull it into A RRC ;slide right through carry - carry has bit0 ANI 80H ;strip all but the bit in 7 ADD C ;add in the current record count MOV C,A ;and save this in C MVI A,0 ;zero A leaving carry set ADC B ;add in the carry and zero in B MOÖ B,Á ;savå thió iî B MOV A,M ;get the extent byte again RRC ;slide right through carry ANI 0FH ;mask all but low four ADD B ;add in the data in B MOÖ B,Á ;anä savå bacë intï B LXI H,14 ;set the pointer DAD D ; to the S2 byte MOV A,M ;pull it into A ADD A ;and slide ADD A ; it ADD A ; left ADD A ; four bits PUSH PSW ;save the result on the stack ADD B ;add in B, if s2>=16 and B=17 carry is set MOV B,A ;save the result into B PUSH PSW ;save the flags and A POP H ;pull flags into L MOV A,L ;put then into A POP H ;recover the second flag set into L ORA L ;or the two together ANI 01H ;and strip off all but carry RET ;then return ; ;****************************************************************************** ;routine gets the record count from the last extent of a random file ; GETSIZ: MVI C,0CH ;look at file name ant extent CALL SEARCH ;go locate the file LHLD FCB ;point to the FCB LXI D,33 ;get the offset to the DAD D ; R0 byte PUSH H ;save the address on the stack MOV M,D ;put the 0 into position INX H ;point to R1 MOV M,D ;0 goes there as well INX H ;point to R2 - overflow byte MOV M,D ;once more with zero GSLOOP: CALL CHKCNT ;check the counter JZ GSEXIT ;if zero we exit now CALL PNTDIR ;otherwise point to the directory LXI D,15 ;get the offset to extent byte CALL COMREC ;check extent overflow POP H ;recover the R0 address PUSH H ;save it again MOV E,A ;save the directory code in E MOV A,C ;and put the R0 byte into A SUB M ;subtract the byte from the FCB INX H ;point to the R1 byte MOV A,B ;put B in A SBB M ;subtract with borrow from here as well INX H ;once more with the overflow byte MOV A,E ;E into A SBB M ;subtract the overflow JC GETSZ1 ;if carry jump over - file is too big MOV M,E ;put R2 into place DCX H ;point back MOV M,B ;set R1 in as well DCX H ;back again MOV M,C ;and set R0 into place GETSZ1: CALL SERCHN ;go look for next extent JMP GSLOOP ;loop until we hit the end ; GSEXIT: POP H ;recover the address of random file size RET ;and return with it ; ;****************************************************************************** ; BDOS function 36 - set random record with data set by RANFIL ; SETRND: LHLD FCB ;get the FCB address LXI D,32 ;and the offset to the record count CALL COMREC ;get the file pointers into A b and c LXI H,33 ;offset to the R0 byte DAD D ;point to it MOV M,C ;put the byte in C into R0 INX H ;point to R1 MOV M,B ;put B in there INX H ;point to overflow byte MOV M,A ;stuff A there RET ;and return ; ;****************************************************************************** ;routine to log the selected disk ; LOGDSK: LHLD DLOG ;point to the login vector LDA CURDSK ;get the current disk byte MOV C,A ;and put it into C CALL SPINHL ;spin the selected disk bit into place PUSH H ;save this vector on the stack XCHG ;careful study says this is useless CALL DISKID ;go get the disk information set POP H ;recover the vector CZ PTSERR ;if trouble say so and quit MOV A,L ;otherwise put the low byte into A RAR ;slide it right bit 0 into carry RC ;return if bit was set - we already got it LHLD DLOG ;otherwise point to the login vector MOV C,L ;put the vector MOV B,H ; into BC CALL SETDBT ;set the disk bit using setro SHLD DLOG ;save the vector into place again JMP GETAL ;get the new allocation vector ; ;****************************************************************************** ; BDOS function 14: select disk ; enter with selected disk in IDATA ; LOGIN: LDA IDATA ;get the disk to log LXI H,CURDSK ;point to the current disk storage CMP M ;are they the same RZ ;return if so MOV M,A ;otherwise make this current JMP LOGDSK ;and log it in then return ; ;****************************************************************************** ;routine to select disk indicated in FCB drive code ; RSELCT: MVI A,0FFH ;get an FF STA OUT1 ;save it to force disk reset on exit LHLD FCB ;point to the FCB MOV A,M ;pull in the new drive code ANI 1FH ;strip off the user bits DCR A ;decrement to make it right STA IDATA ;save it for the login routines CPI 1EH ;is it too big JNC XIT ;if so go back LDA CURDSK ;get the current disk out STA OUTDSK ;save it for the return MOV A,M ;pull in the new code again STA OLDDSK ;save the value as the olddisk ANI 0E0H ;strip off the drive bits MOV M,A ;put the user bits back CALL LOGIN ;go log the new disk XIT: LDA USRCOD ;get the user code out LHLD FCB ;point back to the FCB ORA M ;set in the new user code bits MOV M,A ;and put the result back RET ;then return ; ;****************************************************************************** ;BDOS function 12: routine returns CP/M version in A ; GETVER: MVI A,22H ;say this is CP/M version 2.2 JMP GOBAK ;and return ; ;****************************************************************************** ; BDOS function 13: routine to reset the system disk ; RESET: LXI H,0 ;get ready to reset the vectors SHLD ROVEC ;set both read only bits SHLD DLOG ; and login bits to off XRA A ;zero A STA CURDSK ; and make it the current disk LXI H,TBUFF ;Set DMA addr to 80H SHLD DMAADR ;save this as the new DMA value CALL SETDMA ;make it so for real JMP LOGDSK ;go log in drive A and return ; ;****************************************************************************** ; BDOS function 15: open file ; enter with FCB address in HL and INFO ; exit with directory code in A ; OPEN: CALL ZS2 ;set S2 to zero for the new file CALL RSELCT ;select the right disk JMP OPENF ;then open the file in the FCB ; ;****************************************************************************** ; BDOS function 16: close the selected file ; enter with FCB address ; exit with directory code in A ; CLOSE: CALL RSELCT ;select the right drive JMP CLOSEF ;and finish off the close operation ; ;****************************************************************************** ; BDOS function 17: search for first file entry ; enter with FCB address ; exit with directory code in A ; SEAR1: MVI C,0 ;set the search code into C XCHG ;swap FCB into DE MOV A,M ;pull in the file name byte CPI '?' ;is it a wild card JZ SRF2 ;if so jump over CALL GETEX ;otherwise get the extent MOV A,M ;pull in the byte here CPI '?' ;is it a wild card CNZ ZS2 ;if not set S2 to 0 CALL RSELCT ;select the right drive MVI C,0FH ;look at name thru S2 byte SRF2: CALL SEARCH ;search for the file JMP MOVSEC ;jump to move the sector and return ; ;****************************************************************************** ;BDOS function 18: search for next directory entry ; enter with FCB set as before ; exit with directory code in A 255 says not found ; SEARN: LHLD SEARA ;get the last search FCB out SHLD FCB ;save this as the FCB CALL RSELCT ;select the right drive CALL SERCHN ;then search for the nex extent JMP MOVSEC ;move the sector and return ; ;****************************************************************************** ; BDOS function 19: delete file ; enter with FCB address set ; exit with directory code in A ; DELETE: CALL RSELCT ;select the right drive CALL DELETF ;delete the indicated file JMP RETCOD ;set the return code and go back ; ;****************************************************************************** ; BDOS function 20: read sequential ; enter with FCB address ; exit with directory code in A ; READ: CALL RSELCT ;select the indicated drive JMP DSKRED ;and go read the disk ; ;****************************************************************************** ; BDOS function 21: write sequential ; enter with FCB address ; exit with directory code in A ; WRITE: CALL RSELCT ;select the drive JMP DSKWRT ;and go write the disk ; ;****************************************************************************** ; BDOS function 22: make new file entry ; enter with FCB ; exit with directory code in A ; CREATE: CALL ZS2 ;zero out S2 CALL RSELCT ;select the drive JMP MAKE ;and make a new directory entry ; ;****************************************************************************** ; BDOS function 23: rename file ; enter with FCB address ; exit with directory code in A ; RENAME: CALL RSELCT ;select the drive CALL RENAMF ;and go rename the file JMP RETCOD ;set the return code and exit ; ;****************************************************************************** ; BDOS function 24: return login vector ; exit with login vector in HL ; GLOGIN: LHLD DLOG ;get the login vector out JMP RETHL ;return with it ; ;****************************************************************************** ; BDOS function 25: return current disk ; exit with current disk in A ; GETDRV: LDA CURDSK ;get the current disk out JMP GOBAK ;and go back with it ; ;****************************************************************************** ; BDOS function 26: set DMA address ; enter with DMA address set ; DMASET: XCHG ;save the new address into HL SHLD DMAADR ;save it into place JMP SETDMA ;then make it true ; ;****************************************************************************** ; BDOS function 27: get allocation vector address ; return address in HL ; GALLOC: LHLD ALLOCA ;get the allocation vector out JMP RETHL ;and go back with it ; ;****************************************************************************** ; BDOS function 29: get read/only vector ; return vector in HL ; GROVEC: LHLD ROVEC ;get the read only vector out JMP RETHL ;and go back with it ; ;****************************************************************************** ; BDOS function 30: set file attributes ; enter with FCB address with new attributes set in F1..F7,T1..T3 ; exit with directory code in A ; SETATT: CALL RSELCT ;select the indicate drive CALL SETFN ;set in the new file name with bits set JMP RETCOD ;set the return code and exit ; ;****************************************************************************** ; BDOS function 31: get address of disk parameter table ; return address in HL ; GETPAR: LHLD DPB ;get the disk parameter table address RETHL: SHLD ODATA ;save it at return data word RET ;and go back with it ; ;****************************************************************************** ; BDOS function 32: get or set user code ; enter with LINFO set to FF for get and to value for set ; MODUSR: LDA FCB ;point to the FCB CPI 0FFH ;do we do a set JNZ SETCODE ;or a get LDA USRCOD ;must be get so get JMP GOBAK ;and go back with it ; SETCODE:ANI 1FH ;otherwise strip off the high bits STA USRCOD ;and save the new value RET ;then return ; ;****************************************************************************** ; BDOS function 33: read random record ; enter with FCB address ; exit with return code in A ; REDRND: CALL RSELCT ;select the right drive JMP RDREC ;and go read the sector ; ;****************************************************************************** ; BDOS function 34: write random record ; enter with FCB address ; exit with return code in A ; WRTRND: CALL RSELCT ;go select the drive JMP WRREC ;and write the sector ; ;****************************************************************************** ; BDOS function 35: compute file size ; enter with FCB address ; exit with random record set in FCB ; FILSIZ: CALL RSELCT ;select the right drive JMP GETSIZ ;and compute the file size ; ;****************************************************************************** ; BDOS function 37: Routine to reset drive ; enter with drive vector for reset ; return zero in A ; RESDRV: LHLD FCB ;has the drive vector for reset MOV A,L ;move low into A CMA ;and flip it MOV E,A ;put the result back into E MOV A,H ;get the high vector byte CMA ; flip itr alse LHLD DLOG ;get the login vector ANA H ;and in the new bits in high MOV D,A ;save the result into D MOV A,L ;now on the the low bits ANA E ;and in the new bits MOV E,A ;and save the result into E LHLD ROVEC ;point to the ro vector XCHG ;swap DE and HL SHLD DLOG ;put the new login vector into place MOV A,L ;get the read only low bits ANA E ;and in the new bits MOV L,A ;put then back into L MOV A,H ;now on the the high bits ANA D ;set the new ones in MOV H,A ;and put the result back SHLD ROVEC ;then save the new value into place RET ;and return when done ; ;****************************************************************************** ; bdos exit routine ; BEXIT: LDA OUT1 ;get the out flag for drive reset ORA A ;set the flags JZ REXIT ;if zero we may exit as is LHLD FCB ;otherwise get the FCB address MVI M,0 ;put a zero into drive select byte LDA OLDDSK ;get the old disk out ORA A ;was it zero JZ REXIT ;if so exit MOV M,A ;otherwise set in the old disk byte LDA OUTDSK ;get the disk to relog STA IDATA ;save it for the login CALL LOGIN ;go log in the old disk REXIT: LHLD OLDSP ;get the old stack pointer out SPHL ;put it back in place LHLD ODATA ;get the output data into place MOV A,L ;put L into A MOV B,H ;and H into B RET ;then return from BDOS happy ; ;****************************************************************************** ; BDOS function 40: Routine to write random file with zero fill ; enter with FCB address ; exit with return code in A ; ZERRND: CALL RSELCT ;select the desired drive MVI A,2 ;get the code for zero fill STA DCODE ;set it into place MVI C,0 ;zero c as well CALL RANF1 ;then off the finish the job set pointers CZ RWERR ;then write the sector if all is well RET ;and return ; ;****************************************************************************** ;bdos data area follows ; TINFO: DB 0E5H ; ROVEC: DW 0 ;read only vector address storage DLOG: DW 3 ;disk login areax DMAADR: DW 0080H ;DMA address defaults to 80H SCRT0: DW 0DA45H ; SCRT1: DW 0DA47H ;holds current track SCRT2: DW 0DA49H ;holds starting sector number of current track DTEMP: DW 0DCDFH ;temp DMA address storage DPB: DW 0DA53H ;disk parameter block address CHANGE: DW 0DDADH ;scratch storage for disk change check ALLOCA: DW 0DD8EH ;allocation vector area DPBLK: DW 001AH ;disk parm block - number of sectors/trk BLSHFT: DB 3 ;block shift factor BLKMSK: DB 7 ;block mask EXTMSK: DB 0 ;null mask DSIZE: DW 00F2H ;disk size DIRMAX: DW 003FH ;directory max AL01: DW 00C0H ;Allocation storage CKSIZE: DW 0010H ;number of entries to check TOFS: DW 0002H ;track offset TRANS: DW 0DA62H ;translation routine address holder RWFLG1: DB 0FFH ;flag indicates read or write RWFLG2: DB 0 ;flag also used to indicate read or write SCODE1: DB 0 DCODE: DB 01H ;disk code byte IDATA: DB 80H ;input data storage area EXTBLK: DB 0BH SCODE: DB 01H SEARA: DB 0ACH DB 0D9H DB 23H DB 0B7H BSIZE: DB 0FFH ;block size byte OUT1: DB 0 ;used to force relog of disk on exit OUTDSK: DB 0DAH ;BDOS exit disk OLDDSK: DB 0 ;disk entered with RECCNT: DB 5DH ;record count holder ESCNT1: DB 0 ;extent recort count high byte ESCNT2: DB 5CH ;extent record count low byte DB 0C3H GROUP: DB 3CH ;group number storage DB 02H DIRPNT: DB 38H ;directory pointer area DB 02H DIROFF: DB 60H ;offset into the directory area COUNT: DB 0FH ;holds 16 bit counter for looping DB 0 LDIRPNT:DB 03H DB 0,0,0,0 DB 0,0,0,0 DB 0,0,0,0 DB 0,0,0,0 DB 0,0,0 ; ; BIOS Entry Vector Definitions ; ; (These are actually in CBIOS) ; DS 3 ;(BOOT) Cold boot entry WBOOTF: DS 3 ;Warm boot entry CONSF: DS 3 ;Console status CONIF: DS 3 ;Console char in CONOF: DS 3 ;Console char out LISTF: DS 3 ;List char out PUNF: DS 3 ;Punch char out READF: DS 3 ;Reader char in HOMF: DS 3 ;Home disk SELF: DS 3 ;Select disk TRKF: DS 3 ;Set disk track addr SECF: DS 3 ;Set disk sector addr DMAF: DS 3 ;Set DMA buffer addr DRDF: DS 3 ;Read sector DWRF: DS 3 ;Write sector DS 3 ; SECTRN: DS 3 ;Sector translation routine ; END