3740 lines
90 KiB
NASM
3740 lines
90 KiB
NASM
|
|
;**************************************************************
|
|
;*
|
|
;* C P / M version 2 . 2
|
|
;*
|
|
;* Reconstructed from memory image on February 27, 1981
|
|
;*
|
|
;* by Clark A. Calkins
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
; Set memory limit here. This is the amount of contigeous
|
|
; ram starting from 0000. CP/M will reside at the end of this space.
|
|
;
|
|
MEM EQU 62 ;for a 62k system (TS802 TEST - WORKS OK).
|
|
;
|
|
IOBYTE EQU 3 ;i/o definition byte.
|
|
TDRIVE EQU 4 ;current drive name and user number.
|
|
ENTRY EQU 5 ;entry point for the cp/m bdos.
|
|
TFCB EQU 5CH ;default file control block.
|
|
TBUFF EQU 80H ;i/o buffer and command line storage.
|
|
TBASE EQU 100H ;transiant program storage area.
|
|
;
|
|
; Set control character equates.
|
|
;
|
|
CNTRLC EQU 3 ;control-c
|
|
CNTRLE EQU 05H ;control-e
|
|
BS EQU 08H ;backspace
|
|
TAB EQU 09H ;tab
|
|
LF EQU 0AH ;line feed
|
|
FF EQU 0CH ;form feed
|
|
CR EQU 0DH ;carriage return
|
|
CNTRLP EQU 10H ;control-p
|
|
CNTRLR EQU 12H ;control-r
|
|
CNTRLS EQU 13H ;control-s
|
|
CNTRLU EQU 15H ;control-u
|
|
CNTRLX EQU 18H ;control-x
|
|
CNTRLZ EQU 1AH ;control-z (end-of-file mark)
|
|
DEL EQU 7FH ;rubout
|
|
;
|
|
; Set origin for CP/M
|
|
;
|
|
ORG (MEM-7)*1024
|
|
;
|
|
CBASE JMP COMMAND ;execute command processor (ccp).
|
|
JMP CLEARBUF ;entry to empty input buffer before starting ccp.
|
|
|
|
;
|
|
; Standard cp/m ccp input buffer. Format is (max length),
|
|
; (actual length), (char #1), (char #2), (char #3), etc.
|
|
;
|
|
INBUFF DB 127 ;length of input buffer.
|
|
DB 0 ;current length of contents.
|
|
DB 'Copyright'
|
|
DB ' 1979 (c) by Digital Research '
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
INPOINT DW INBUFF+2;input line pointer
|
|
NAMEPNT DW 0 ;input line pointer used for error message. Points to
|
|
; ;start of name in error.
|
|
;
|
|
; Routine to print (A) on the console. All registers used.
|
|
;
|
|
PRINT MOV E,A ;setup bdos call.
|
|
MVI C,2
|
|
JMP ENTRY
|
|
;
|
|
; Routine to print (A) on the console and to save (BC).
|
|
;
|
|
PRINTB PUSH B
|
|
CALL PRINT
|
|
POP B
|
|
RET
|
|
;
|
|
; Routine to send a carriage return, line feed combination
|
|
; to the console.
|
|
;
|
|
CRLF MVI A,CR
|
|
CALL PRINTB
|
|
MVI A,LF
|
|
JMP PRINTB
|
|
;
|
|
; Routine to send one space to the console and save (BC).
|
|
;
|
|
SPACE MVI A,' '
|
|
JMP PRINTB
|
|
;
|
|
; Routine to print character string pointed to be (BC) on the
|
|
; console. It must terminate with a null byte.
|
|
;
|
|
PLINE PUSH B
|
|
CALL CRLF
|
|
POP H
|
|
PLINE2 MOV A,M
|
|
ORA A
|
|
RZ
|
|
INX H
|
|
PUSH H
|
|
CALL PRINT
|
|
POP H
|
|
JMP PLINE2
|
|
;
|
|
; Routine to reset the disk system.
|
|
;
|
|
RESDSK MVI C,13
|
|
JMP ENTRY
|
|
;
|
|
; Routine to select disk (A).
|
|
;
|
|
DSKSEL MOV E,A
|
|
MVI C,14
|
|
JMP ENTRY
|
|
;
|
|
; Routine to call bdos and save the return code. The zero
|
|
; flag is set on a return of 0ffh.
|
|
;
|
|
ENTRY1 CALL ENTRY
|
|
STA RTNCODE ;save return code.
|
|
INR A ;set zero if 0ffh returned.
|
|
RET
|
|
;
|
|
; Routine to open a file. (DE) must point to the FCB.
|
|
;
|
|
OPEN MVI C,15
|
|
JMP ENTRY1
|
|
;
|
|
; Routine to open file at (FCB).
|
|
;
|
|
OPENFCB XRA A ;clear the record number byte at fcb+32
|
|
STA FCB+32
|
|
LXI D,FCB
|
|
JMP OPEN
|
|
;
|
|
; Routine to close a file. (DE) points to FCB.
|
|
;
|
|
CLOSE MVI C,16
|
|
JMP ENTRY1
|
|
;
|
|
; Routine to search for the first file with ambigueous name
|
|
; (DE).
|
|
;
|
|
SRCHFST MVI C,17
|
|
JMP ENTRY1
|
|
;
|
|
; Search for the next ambigeous file name.
|
|
;
|
|
SRCHNXT MVI C,18
|
|
JMP ENTRY1
|
|
;
|
|
; Search for file at (FCB).
|
|
;
|
|
SRCHFCB LXI D,FCB
|
|
JMP SRCHFST
|
|
;
|
|
; Routine to delete a file pointed to by (DE).
|
|
;
|
|
DELETE MVI C,19
|
|
JMP ENTRY
|
|
;
|
|
; Routine to call the bdos and set the zero flag if a zero
|
|
; status is returned.
|
|
;
|
|
ENTRY2 CALL ENTRY
|
|
ORA A ;set zero flag if appropriate.
|
|
RET
|
|
;
|
|
; Routine to read the next record from a sequential file.
|
|
; (DE) points to the FCB.
|
|
;
|
|
RDREC MVI C,20
|
|
JMP ENTRY2
|
|
;
|
|
; Routine to read file at (FCB).
|
|
;
|
|
READFCB LXI D,FCB
|
|
JMP RDREC
|
|
;
|
|
; Routine to write the next record of a sequential file.
|
|
; (DE) points to the FCB.
|
|
;
|
|
WRTREC MVI C,21
|
|
JMP ENTRY2
|
|
;
|
|
; Routine to create the file pointed to by (DE).
|
|
;
|
|
CREATE MVI C,22
|
|
JMP ENTRY1
|
|
;
|
|
; Routine to rename the file pointed to by (DE). Note that
|
|
; the new name starts at (DE+16).
|
|
;
|
|
RENAM MVI C,23
|
|
JMP ENTRY
|
|
;
|
|
; Get the current user code.
|
|
;
|
|
GETUSR MVI E,0FFH
|
|
;
|
|
; Routne to get or set the current user code.
|
|
; If (E) is FF then this is a GET, else it is a SET.
|
|
;
|
|
GETSETUC:MVI C,32
|
|
JMP ENTRY
|
|
;
|
|
; Routine to set the current drive byte at (TDRIVE).
|
|
;
|
|
SETCDRV CALL GETUSR ;get user number
|
|
ADD A ;and shift into the upper 4 bits.
|
|
ADD A
|
|
ADD A
|
|
ADD A
|
|
LXI H,CDRIVE;now add in the current drive number.
|
|
ORA M
|
|
STA TDRIVE ;and save.
|
|
RET
|
|
;
|
|
; Move currently active drive down to (TDRIVE).
|
|
;
|
|
MOVECD LDA CDRIVE
|
|
STA TDRIVE
|
|
RET
|
|
;
|
|
; Routine to convert (A) into upper case ascii. Only letters
|
|
; are affected.
|
|
;
|
|
UPPER CPI 'a' ;check for letters in the range of 'a' to 'z'.
|
|
RC
|
|
CPI '{'
|
|
RNC
|
|
ANI 5FH ;convert it if found.
|
|
RET
|
|
;
|
|
; Routine to get a line of input. We must check to see if the
|
|
; user is in (BATCH) mode. If so, then read the input from file
|
|
; ($$$.SUB). At the end, reset to console input.
|
|
;
|
|
GETINP LDA BATCH ;if =0, then use console input.
|
|
ORA A
|
|
JZ GETINP1
|
|
;
|
|
; Use the submit file ($$$.sub) which is prepared by a
|
|
; SUBMIT run. It must be on drive (A) and it will be deleted
|
|
; if and error occures (like eof).
|
|
;
|
|
LDA CDRIVE ;select drive 0 if need be.
|
|
ORA A
|
|
MVI A,0 ;always use drive A for submit.
|
|
CNZ DSKSEL ;select it if required.
|
|
LXI D,BATCHFCB
|
|
CALL OPEN ;look for it.
|
|
JZ GETINP1 ;if not there, use normal input.
|
|
LDA BATCHFCB+15;get last record number+1.
|
|
DCR A
|
|
STA BATCHFCB+32
|
|
LXI D,BATCHFCB
|
|
CALL RDREC ;read last record.
|
|
JNZ GETINP1 ;quit on end of file.
|
|
;
|
|
; Move this record into input buffer.
|
|
;
|
|
LXI D,INBUFF+1
|
|
LXI H,TBUFF ;data was read into buffer here.
|
|
MVI B,128 ;all 128 characters may be used.
|
|
CALL HL2DE ;(HL) to (DE), (B) bytes.
|
|
LXI H,BATCHFCB+14
|
|
MVI M,0 ;zero out the 's2' byte.
|
|
INX H ;and decrement the record count.
|
|
DCR M
|
|
LXI D,BATCHFCB;close the batch file now.
|
|
CALL CLOSE
|
|
JZ GETINP1 ;quit on an error.
|
|
LDA CDRIVE ;re-select previous drive if need be.
|
|
ORA A
|
|
CNZ DSKSEL ;don't do needless selects.
|
|
;
|
|
; Print line just read on console.
|
|
;
|
|
LXI H,INBUFF+2
|
|
CALL PLINE2
|
|
CALL CHKCON ;check console, quit on a key.
|
|
JZ GETINP2 ;jump if no key is pressed.
|
|
;
|
|
; Terminate the submit job on any keyboard input. Delete this
|
|
; file such that it is not re-started and jump to normal keyboard
|
|
; input section.
|
|
;
|
|
CALL DELBATCH;delete the batch file.
|
|
JMP CMMND1 ;and restart command input.
|
|
;
|
|
; Get here for normal keyboard input. Delete the submit file
|
|
; incase there was one.
|
|
;
|
|
GETINP1 CALL DELBATCH;delete file ($$$.sub).
|
|
CALL SETCDRV ;reset active disk.
|
|
MVI C,10 ;get line from console device.
|
|
LXI D,INBUFF
|
|
CALL ENTRY
|
|
CALL MOVECD ;reset current drive (again).
|
|
;
|
|
; Convert input line to upper case.
|
|
;
|
|
GETINP2 LXI H,INBUFF+1
|
|
MOV B,M ;(B)=character counter.
|
|
GETINP3 INX H
|
|
MOV A,B ;end of the line?
|
|
ORA A
|
|
JZ GETINP4
|
|
MOV A,M ;convert to upper case.
|
|
CALL UPPER
|
|
MOV M,A
|
|
DCR B ;adjust character count.
|
|
JMP GETINP3
|
|
GETINP4 MOV M,A ;add trailing null.
|
|
LXI H,INBUFF+2
|
|
SHLD INPOINT ;reset input line pointer.
|
|
RET
|
|
;
|
|
; Routine to check the console for a key pressed. The zero
|
|
; flag is set is none, else the character is returned in (A).
|
|
;
|
|
CHKCON MVI C,11 ;check console.
|
|
CALL ENTRY
|
|
ORA A
|
|
RZ ;return if nothing.
|
|
MVI C,1 ;else get character.
|
|
CALL ENTRY
|
|
ORA A ;clear zero flag and return.
|
|
RET
|
|
;
|
|
; Routine to get the currently active drive number.
|
|
;
|
|
GETDSK MVI C,25
|
|
JMP ENTRY
|
|
;
|
|
; Set the stabdard dma address.
|
|
;
|
|
STDDMA LXI D,TBUFF
|
|
;
|
|
; Routine to set the dma address to (DE).
|
|
;
|
|
DMASET MVI C,26
|
|
JMP ENTRY
|
|
;
|
|
; Delete the batch file created by SUBMIT.
|
|
;
|
|
DELBATCH:LXI H,BATCH ;is batch active?
|
|
MOV A,M
|
|
ORA A
|
|
RZ
|
|
MVI M,0 ;yes, de-activate it.
|
|
XRA A
|
|
CALL DSKSEL ;select drive 0 for sure.
|
|
LXI D,BATCHFCB;and delete this file.
|
|
CALL DELETE
|
|
LDA CDRIVE ;reset current drive.
|
|
JMP DSKSEL
|
|
;
|
|
; Check to two strings at (PATTRN1) and (PATTRN2). They must be
|
|
; the same or we halt....
|
|
;
|
|
VERIFY LXI D,PATTRN1;these are the serial number bytes.
|
|
LXI H,PATTRN2;ditto, but how could they be different?
|
|
MVI B,6 ;6 bytes each.
|
|
VERIFY1 LDAX D
|
|
CMP M
|
|
JNZ HALT ;jump to halt routine.
|
|
INX D
|
|
INX H
|
|
DCR B
|
|
JNZ VERIFY1
|
|
RET
|
|
;
|
|
; Print back file name with a '?' to indicate a syntax error.
|
|
;
|
|
SYNERR CALL CRLF ;end current line.
|
|
LHLD NAMEPNT ;this points to name in error.
|
|
SYNERR1 MOV A,M ;print it until a space or null is found.
|
|
CPI ' '
|
|
JZ SYNERR2
|
|
ORA A
|
|
JZ SYNERR2
|
|
PUSH H
|
|
CALL PRINT
|
|
POP H
|
|
INX H
|
|
JMP SYNERR1
|
|
SYNERR2 MVI A,'?' ;add trailing '?'.
|
|
CALL PRINT
|
|
CALL CRLF
|
|
CALL DELBATCH;delete any batch file.
|
|
JMP CMMND1 ;and restart from console input.
|
|
;
|
|
; Check character at (DE) for legal command input. Note that the
|
|
; zero flag is set if the character is a delimiter.
|
|
;
|
|
CHECK LDAX D
|
|
ORA A
|
|
RZ
|
|
CPI ' ' ;control characters are not legal here.
|
|
JC SYNERR
|
|
RZ ;check for valid delimiter.
|
|
CPI '='
|
|
RZ
|
|
CPI '_'
|
|
RZ
|
|
CPI '.'
|
|
RZ
|
|
CPI ':'
|
|
RZ
|
|
CPI ';'
|
|
RZ
|
|
CPI '<'
|
|
RZ
|
|
CPI '>'
|
|
RZ
|
|
RET
|
|
;
|
|
; Get the next non-blank character from (DE).
|
|
;
|
|
NONBLANK:LDAX D
|
|
ORA A ;string ends with a null.
|
|
RZ
|
|
CPI ' '
|
|
RNZ
|
|
INX D
|
|
JMP NONBLANK
|
|
;
|
|
; Add (HL)=(HL)+(A)
|
|
;
|
|
ADDHL ADD L
|
|
MOV L,A
|
|
RNC ;take care of any carry.
|
|
INR H
|
|
RET
|
|
;
|
|
; Convert the first name in (FCB).
|
|
;
|
|
CONVFST MVI A,0
|
|
;
|
|
; Format a file name (convert * to '?', etc.). On return,
|
|
; (A)=0 is an unambigeous name was specified. Enter with (A) equal to
|
|
; the position within the fcb for the name (either 0 or 16).
|
|
;
|
|
CONVERT LXI H,FCB
|
|
CALL ADDHL
|
|
PUSH H
|
|
PUSH H
|
|
XRA A
|
|
STA CHGDRV ;initialize drive change flag.
|
|
LHLD INPOINT ;set (HL) as pointer into input line.
|
|
XCHG
|
|
CALL NONBLANK;get next non-blank character.
|
|
XCHG
|
|
SHLD NAMEPNT ;save pointer here for any error message.
|
|
XCHG
|
|
POP H
|
|
LDAX D ;get first character.
|
|
ORA A
|
|
JZ CONVRT1
|
|
SBI 'A'-1 ;might be a drive name, convert to binary.
|
|
MOV B,A ;and save.
|
|
INX D ;check next character for a ':'.
|
|
LDAX D
|
|
CPI ':'
|
|
JZ CONVRT2
|
|
DCX D ;nope, move pointer back to the start of the line.
|
|
CONVRT1 LDA CDRIVE
|
|
MOV M,A
|
|
JMP CONVRT3
|
|
CONVRT2 MOV A,B
|
|
STA CHGDRV ;set change in drives flag.
|
|
MOV M,B
|
|
INX D
|
|
;
|
|
; Convert the basic file name.
|
|
;
|
|
CONVRT3 MVI B,08H
|
|
CONVRT4 CALL CHECK
|
|
JZ CONVRT8
|
|
INX H
|
|
CPI '*' ;note that an '*' will fill the remaining
|
|
JNZ CONVRT5 ;field with '?'.
|
|
MVI M,'?'
|
|
JMP CONVRT6
|
|
CONVRT5 MOV M,A
|
|
INX D
|
|
CONVRT6 DCR B
|
|
JNZ CONVRT4
|
|
CONVRT7 CALL CHECK ;get next delimiter.
|
|
JZ GETEXT
|
|
INX D
|
|
JMP CONVRT7
|
|
CONVRT8 INX H ;blank fill the file name.
|
|
MVI M,' '
|
|
DCR B
|
|
JNZ CONVRT8
|
|
;
|
|
; Get the extension and convert it.
|
|
;
|
|
GETEXT MVI B,03H
|
|
CPI '.'
|
|
JNZ GETEXT5
|
|
INX D
|
|
GETEXT1 CALL CHECK
|
|
JZ GETEXT5
|
|
INX H
|
|
CPI '*'
|
|
JNZ GETEXT2
|
|
MVI M,'?'
|
|
JMP GETEXT3
|
|
GETEXT2 MOV M,A
|
|
INX D
|
|
GETEXT3 DCR B
|
|
JNZ GETEXT1
|
|
GETEXT4 CALL CHECK
|
|
JZ GETEXT6
|
|
INX D
|
|
JMP GETEXT4
|
|
GETEXT5 INX H
|
|
MVI M,' '
|
|
DCR B
|
|
JNZ GETEXT5
|
|
GETEXT6 MVI B,3
|
|
GETEXT7 INX H
|
|
MVI M,0
|
|
DCR B
|
|
JNZ GETEXT7
|
|
XCHG
|
|
SHLD INPOINT ;save input line pointer.
|
|
POP H
|
|
;
|
|
; Check to see if this is an ambigeous file name specification.
|
|
; Set the (A) register to non zero if it is.
|
|
;
|
|
LXI B,11 ;set name length.
|
|
GETEXT8 INX H
|
|
MOV A,M
|
|
CPI '?' ;any question marks?
|
|
JNZ GETEXT9
|
|
INR B ;count them.
|
|
GETEXT9 DCR C
|
|
JNZ GETEXT8
|
|
MOV A,B
|
|
ORA A
|
|
RET
|
|
;
|
|
; CP/M command table. Note commands can be either 3 or 4 characters long.
|
|
;
|
|
NUMCMDS EQU 6 ;number of commands
|
|
CMDTBL DB 'DIR '
|
|
DB 'ERA '
|
|
DB 'TYPE'
|
|
DB 'SAVE'
|
|
DB 'REN '
|
|
DB 'USER'
|
|
;
|
|
; The following six bytes must agree with those at (PATTRN2)
|
|
; or cp/m will HALT. Why?
|
|
;
|
|
PATTRN1 DB 0,22,0,0,0,0;(* serial number bytes *).
|
|
;
|
|
; Search the command table for a match with what has just
|
|
; been entered. If a match is found, then we jump to the
|
|
; proper section. Else jump to (UNKNOWN).
|
|
; On return, the (C) register is set to the command number
|
|
; that matched (or NUMCMDS+1 if no match).
|
|
;
|
|
SEARCH LXI H,CMDTBL
|
|
MVI C,0
|
|
SEARCH1 MOV A,C
|
|
CPI NUMCMDS ;this commands exists.
|
|
RNC
|
|
LXI D,FCB+1 ;check this one.
|
|
MVI B,4 ;max command length.
|
|
SEARCH2 LDAX D
|
|
CMP M
|
|
JNZ SEARCH3 ;not a match.
|
|
INX D
|
|
INX H
|
|
DCR B
|
|
JNZ SEARCH2
|
|
LDAX D ;allow a 3 character command to match.
|
|
CPI ' '
|
|
JNZ SEARCH4
|
|
MOV A,C ;set return register for this command.
|
|
RET
|
|
SEARCH3 INX H
|
|
DCR B
|
|
JNZ SEARCH3
|
|
SEARCH4 INR C
|
|
JMP SEARCH1
|
|
;
|
|
; Set the input buffer to empty and then start the command
|
|
; processor (ccp).
|
|
;
|
|
CLEARBUF:XRA A
|
|
STA INBUFF+1;second byte is actual length.
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;*
|
|
;* C C P - C o n s o l e C o m m a n d P r o c e s s o r
|
|
;*
|
|
;**************************************************************
|
|
;*
|
|
COMMAND LXI SP,CCPSTACK;setup stack area.
|
|
PUSH B ;note that (C) should be equal to:
|
|
MOV A,C ;(uuuudddd) where 'uuuu' is the user number
|
|
RAR ;and 'dddd' is the drive number.
|
|
RAR
|
|
RAR
|
|
RAR
|
|
ANI 0FH ;isolate the user number.
|
|
MOV E,A
|
|
CALL GETSETUC;and set it.
|
|
CALL RESDSK ;reset the disk system.
|
|
STA BATCH ;clear batch mode flag.
|
|
POP B
|
|
MOV A,C
|
|
ANI 0FH ;isolate the drive number.
|
|
STA CDRIVE ;and save.
|
|
CALL DSKSEL ;...and select.
|
|
LDA INBUFF+1
|
|
ORA A ;anything in input buffer already?
|
|
JNZ CMMND2 ;yes, we just process it.
|
|
;
|
|
; Entry point to get a command line from the console.
|
|
;
|
|
CMMND1 LXI SP,CCPSTACK;set stack straight.
|
|
CALL CRLF ;start a new line on the screen.
|
|
CALL GETDSK ;get current drive.
|
|
ADI 'a'
|
|
CALL PRINT ;print current drive.
|
|
MVI A,'>'
|
|
CALL PRINT ;and add prompt.
|
|
CALL GETINP ;get line from user.
|
|
;
|
|
; Process command line here.
|
|
;
|
|
CMMND2 LXI D,TBUFF
|
|
CALL DMASET ;set standard dma address.
|
|
CALL GETDSK
|
|
STA CDRIVE ;set current drive.
|
|
CALL CONVFST ;convert name typed in.
|
|
CNZ SYNERR ;wild cards are not allowed.
|
|
LDA CHGDRV ;if a change in drives was indicated,
|
|
ORA A ;then treat this as an unknown command
|
|
JNZ UNKNOWN ;which gets executed.
|
|
CALL SEARCH ;else search command table for a match.
|
|
;
|
|
; Note that an unknown command returns
|
|
; with (A) pointing to the last address
|
|
; in our table which is (UNKNOWN).
|
|
;
|
|
LXI H,CMDADR;now, look thru our address table for command (A).
|
|
MOV E,A ;set (DE) to command number.
|
|
MVI D,0
|
|
DAD D
|
|
DAD D ;(HL)=(CMDADR)+2*(command number).
|
|
MOV A,M ;now pick out this address.
|
|
INX H
|
|
MOV H,M
|
|
MOV L,A
|
|
PCHL ;now execute it.
|
|
;
|
|
; CP/M command address table.
|
|
;
|
|
CMDADR DW DIRECT,ERASE,TYPE,SAVE
|
|
DW RENAME,USER,UNKNOWN
|
|
;
|
|
; Halt the system. Reason for this is unknown at present.
|
|
;
|
|
HALT LXI H,76F3H ;'DI HLT' instructions.
|
|
SHLD CBASE
|
|
LXI H,CBASE
|
|
PCHL
|
|
;
|
|
; Read error while TYPEing a file.
|
|
;
|
|
RDERROR LXI B,RDERR
|
|
JMP PLINE
|
|
RDERR DB 'Read error',0
|
|
;
|
|
; Required file was not located.
|
|
;
|
|
NONE LXI B,NOFILE
|
|
JMP PLINE
|
|
NOFILE DB 'No file',0
|
|
;
|
|
; Decode a command of the form 'A>filename number{ filename}.
|
|
; Note that a drive specifier is not allowed on the first file
|
|
; name. On return, the number is in register (A). Any error
|
|
; causes 'filename?' to be printed and the command is aborted.
|
|
;
|
|
DECODE CALL CONVFST ;convert filename.
|
|
LDA CHGDRV ;do not allow a drive to be specified.
|
|
ORA A
|
|
JNZ SYNERR
|
|
LXI H,FCB+1 ;convert number now.
|
|
LXI B,11 ;(B)=sum register, (C)=max digit count.
|
|
DECODE1 MOV A,M
|
|
CPI ' ' ;a space terminates the numeral.
|
|
JZ DECODE3
|
|
INX H
|
|
SUI '0' ;make binary from ascii.
|
|
CPI 10 ;legal digit?
|
|
JNC SYNERR
|
|
MOV D,A ;yes, save it in (D).
|
|
MOV A,B ;compute (B)=(B)*10 and check for overflow.
|
|
ANI 0E0H
|
|
JNZ SYNERR
|
|
MOV A,B
|
|
RLC
|
|
RLC
|
|
RLC ;(A)=(B)*8
|
|
ADD B ;.......*9
|
|
JC SYNERR
|
|
ADD B ;.......*10
|
|
JC SYNERR
|
|
ADD D ;add in new digit now.
|
|
DECODE2 JC SYNERR
|
|
MOV B,A ;and save result.
|
|
DCR C ;only look at 11 digits.
|
|
JNZ DECODE1
|
|
RET
|
|
DECODE3 MOV A,M ;spaces must follow (why?).
|
|
CPI ' '
|
|
JNZ SYNERR
|
|
INX H
|
|
DECODE4 DCR C
|
|
JNZ DECODE3
|
|
MOV A,B ;set (A)=the numeric value entered.
|
|
RET
|
|
;
|
|
; Move 3 bytes from (HL) to (DE). Note that there is only
|
|
; one reference to this at (A2D5h).
|
|
;
|
|
MOVE3 MVI B,3
|
|
;
|
|
; Move (B) bytes from (HL) to (DE).
|
|
;
|
|
HL2DE MOV A,M
|
|
STAX D
|
|
INX H
|
|
INX D
|
|
DCR B
|
|
JNZ HL2DE
|
|
RET
|
|
;
|
|
; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here.
|
|
;
|
|
EXTRACT LXI H,TBUFF
|
|
ADD C
|
|
CALL ADDHL
|
|
MOV A,M
|
|
RET
|
|
;
|
|
; Check drive specified. If it means a change, then the new
|
|
; drive will be selected. In any case, the drive byte of the
|
|
; fcb will be set to null (means use current drive).
|
|
;
|
|
DSELECT XRA A ;null out first byte of fcb.
|
|
STA FCB
|
|
LDA CHGDRV ;a drive change indicated?
|
|
ORA A
|
|
RZ
|
|
DCR A ;yes, is it the same as the current drive?
|
|
LXI H,CDRIVE
|
|
CMP M
|
|
RZ
|
|
JMP DSKSEL ;no. Select it then.
|
|
;
|
|
; Check the drive selection and reset it to the previous
|
|
; drive if it was changed for the preceeding command.
|
|
;
|
|
RESETDR LDA CHGDRV ;drive change indicated?
|
|
ORA A
|
|
RZ
|
|
DCR A ;yes, was it a different drive?
|
|
LXI H,CDRIVE
|
|
CMP M
|
|
RZ
|
|
LDA CDRIVE ;yes, re-select our old drive.
|
|
JMP DSKSEL
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* D I R E C T O R Y C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
DIRECT CALL CONVFST ;convert file name.
|
|
CALL DSELECT ;select indicated drive.
|
|
LXI H,FCB+1 ;was any file indicated?
|
|
MOV A,M
|
|
CPI ' '
|
|
JNZ DIRECT2
|
|
MVI B,11 ;no. Fill field with '?' - same as *.*.
|
|
DIRECT1 MVI M,'?'
|
|
INX H
|
|
DCR B
|
|
JNZ DIRECT1
|
|
DIRECT2 MVI E,0 ;set initial cursor position.
|
|
PUSH D
|
|
CALL SRCHFCB ;get first file name.
|
|
CZ NONE ;none found at all?
|
|
DIRECT3 JZ DIRECT9 ;terminate if no more names.
|
|
LDA RTNCODE ;get file's position in segment (0-3).
|
|
RRC
|
|
RRC
|
|
RRC
|
|
ANI 60H ;(A)=position*32
|
|
MOV C,A
|
|
MVI A,10
|
|
CALL EXTRACT ;extract the tenth entry in fcb.
|
|
RAL ;check system file status bit.
|
|
JC DIRECT8 ;we don't list them.
|
|
POP D
|
|
MOV A,E ;bump name count.
|
|
INR E
|
|
PUSH D
|
|
ANI 03H ;at end of line?
|
|
PUSH PSW
|
|
JNZ DIRECT4
|
|
CALL CRLF ;yes, end this line and start another.
|
|
PUSH B
|
|
CALL GETDSK ;start line with ('A:').
|
|
POP B
|
|
ADI 'A'
|
|
CALL PRINTB
|
|
MVI A,':'
|
|
CALL PRINTB
|
|
JMP DIRECT5
|
|
DIRECT4 CALL SPACE ;add seperator between file names.
|
|
MVI A,':'
|
|
CALL PRINTB
|
|
DIRECT5 CALL SPACE
|
|
MVI B,1 ;'extract' each file name character at a time.
|
|
DIRECT6 MOV A,B
|
|
CALL EXTRACT
|
|
ANI 7FH ;strip bit 7 (status bit).
|
|
CPI ' ' ;are we at the end of the name?
|
|
JNZ DRECT65
|
|
POP PSW ;yes, don't print spaces at the end of a line.
|
|
PUSH PSW
|
|
CPI 3
|
|
JNZ DRECT63
|
|
MVI A,9 ;first check for no extension.
|
|
CALL EXTRACT
|
|
ANI 7FH
|
|
CPI ' '
|
|
JZ DIRECT7 ;don't print spaces.
|
|
DRECT63 MVI A,' ' ;else print them.
|
|
DRECT65 CALL PRINTB
|
|
INR B ;bump to next character psoition.
|
|
MOV A,B
|
|
CPI 12 ;end of the name?
|
|
JNC DIRECT7
|
|
CPI 9 ;nope, starting extension?
|
|
JNZ DIRECT6
|
|
CALL SPACE ;yes, add seperating space.
|
|
JMP DIRECT6
|
|
DIRECT7 POP PSW ;get the next file name.
|
|
DIRECT8 CALL CHKCON ;first check console, quit on anything.
|
|
JNZ DIRECT9
|
|
CALL SRCHNXT ;get next name.
|
|
JMP DIRECT3 ;and continue with our list.
|
|
DIRECT9 POP D ;restore the stack and return to command level.
|
|
JMP GETBACK
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* E R A S E C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
ERASE CALL CONVFST ;convert file name.
|
|
CPI 11 ;was '*.*' entered?
|
|
JNZ ERASE1
|
|
LXI B,YESNO ;yes, ask for confirmation.
|
|
CALL PLINE
|
|
CALL GETINP
|
|
LXI H,INBUFF+1
|
|
DCR M ;must be exactly 'y'.
|
|
JNZ CMMND1
|
|
INX H
|
|
MOV A,M
|
|
CPI 'Y'
|
|
JNZ CMMND1
|
|
INX H
|
|
SHLD INPOINT ;save input line pointer.
|
|
ERASE1 CALL DSELECT ;select desired disk.
|
|
LXI D,FCB
|
|
CALL DELETE ;delete the file.
|
|
INR A
|
|
CZ NONE ;not there?
|
|
JMP GETBACK ;return to command level now.
|
|
YESNO DB 'All (y/n)?',0
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* T Y P E C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
TYPE CALL CONVFST ;convert file name.
|
|
JNZ SYNERR ;wild cards not allowed.
|
|
CALL DSELECT ;select indicated drive.
|
|
CALL OPENFCB ;open the file.
|
|
JZ TYPE5 ;not there?
|
|
CALL CRLF ;ok, start a new line on the screen.
|
|
LXI H,NBYTES;initialize byte counter.
|
|
MVI M,0FFH ;set to read first sector.
|
|
TYPE1 LXI H,NBYTES
|
|
TYPE2 MOV A,M ;have we written the entire sector?
|
|
CPI 128
|
|
JC TYPE3
|
|
PUSH H ;yes, read in the next one.
|
|
CALL READFCB
|
|
POP H
|
|
JNZ TYPE4 ;end or error?
|
|
XRA A ;ok, clear byte counter.
|
|
MOV M,A
|
|
TYPE3 INR M ;count this byte.
|
|
LXI H,TBUFF ;and get the (A)th one from the buffer (TBUFF).
|
|
CALL ADDHL
|
|
MOV A,M
|
|
CPI CNTRLZ ;end of file mark?
|
|
JZ GETBACK
|
|
CALL PRINT ;no, print it.
|
|
CALL CHKCON ;check console, quit if anything ready.
|
|
JNZ GETBACK
|
|
JMP TYPE1
|
|
;
|
|
; Get here on an end of file or read error.
|
|
;
|
|
TYPE4 DCR A ;read error?
|
|
JZ GETBACK
|
|
CALL RDERROR ;yes, print message.
|
|
TYPE5 CALL RESETDR ;and reset proper drive
|
|
JMP SYNERR ;now print file name with problem.
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* S A V E C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
SAVE CALL DECODE ;get numeric number that follows SAVE.
|
|
PUSH PSW ;save number of pages to write.
|
|
CALL CONVFST ;convert file name.
|
|
JNZ SYNERR ;wild cards not allowed.
|
|
CALL DSELECT ;select specified drive.
|
|
LXI D,FCB ;now delete this file.
|
|
PUSH D
|
|
CALL DELETE
|
|
POP D
|
|
CALL CREATE ;and create it again.
|
|
JZ SAVE3 ;can't create?
|
|
XRA A ;clear record number byte.
|
|
STA FCB+32
|
|
POP PSW ;convert pages to sectors.
|
|
MOV L,A
|
|
MVI H,0
|
|
DAD H ;(HL)=number of sectors to write.
|
|
LXI D,TBASE ;and we start from here.
|
|
SAVE1 MOV A,H ;done yet?
|
|
ORA L
|
|
JZ SAVE2
|
|
DCX H ;nope, count this and compute the start
|
|
PUSH H ;of the next 128 byte sector.
|
|
LXI H,128
|
|
DAD D
|
|
PUSH H ;save it and set the transfer address.
|
|
CALL DMASET
|
|
LXI D,FCB ;write out this sector now.
|
|
CALL WRTREC
|
|
POP D ;reset (DE) to the start of the last sector.
|
|
POP H ;restore sector count.
|
|
JNZ SAVE3 ;write error?
|
|
JMP SAVE1
|
|
;
|
|
; Get here after writing all of the file.
|
|
;
|
|
SAVE2 LXI D,FCB ;now close the file.
|
|
CALL CLOSE
|
|
INR A ;did it close ok?
|
|
JNZ SAVE4
|
|
;
|
|
; Print out error message (no space).
|
|
;
|
|
SAVE3 LXI B,NOSPACE
|
|
CALL PLINE
|
|
SAVE4 CALL STDDMA ;reset the standard dma address.
|
|
JMP GETBACK
|
|
NOSPACE DB 'No space',0
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* R E N A M E C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
RENAME CALL CONVFST ;convert first file name.
|
|
JNZ SYNERR ;wild cards not allowed.
|
|
LDA CHGDRV ;remember any change in drives specified.
|
|
PUSH PSW
|
|
CALL DSELECT ;and select this drive.
|
|
CALL SRCHFCB ;is this file present?
|
|
JNZ RENAME6 ;yes, print error message.
|
|
LXI H,FCB ;yes, move this name into second slot.
|
|
LXI D,FCB+16
|
|
MVI B,16
|
|
CALL HL2DE
|
|
LHLD INPOINT ;get input pointer.
|
|
XCHG
|
|
CALL NONBLANK;get next non blank character.
|
|
CPI '=' ;only allow an '=' or '_' seperator.
|
|
JZ RENAME1
|
|
CPI '_'
|
|
JNZ RENAME5
|
|
RENAME1 XCHG
|
|
INX H ;ok, skip seperator.
|
|
SHLD INPOINT ;save input line pointer.
|
|
CALL CONVFST ;convert this second file name now.
|
|
JNZ RENAME5 ;again, no wild cards.
|
|
POP PSW ;if a drive was specified, then it
|
|
MOV B,A ;must be the same as before.
|
|
LXI H,CHGDRV
|
|
MOV A,M
|
|
ORA A
|
|
JZ RENAME2
|
|
CMP B
|
|
MOV M,B
|
|
JNZ RENAME5 ;they were different, error.
|
|
RENAME2 MOV M,B; reset as per the first file specification.
|
|
XRA A
|
|
STA FCB ;clear the drive byte of the fcb.
|
|
RENAME3 CALL SRCHFCB ;and go look for second file.
|
|
JZ RENAME4 ;doesn't exist?
|
|
LXI D,FCB
|
|
CALL RENAM ;ok, rename the file.
|
|
JMP GETBACK
|
|
;
|
|
; Process rename errors here.
|
|
;
|
|
RENAME4 CALL NONE ;file not there.
|
|
JMP GETBACK
|
|
RENAME5 CALL RESETDR ;bad command format.
|
|
JMP SYNERR
|
|
RENAME6 LXI B,EXISTS;destination file already exists.
|
|
CALL PLINE
|
|
JMP GETBACK
|
|
EXISTS DB 'File exists',0
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* U S E R C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
USER CALL DECODE ;get numeric value following command.
|
|
CPI 16 ;legal user number?
|
|
JNC SYNERR
|
|
MOV E,A ;yes but is there anything else?
|
|
LDA FCB+1
|
|
CPI ' '
|
|
JZ SYNERR ;yes, that is not allowed.
|
|
CALL GETSETUC;ok, set user code.
|
|
JMP GETBACK1
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* T R A N S I A N T P R O G R A M C O M M A N D
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
UNKNOWN CALL VERIFY ;check for valid system (why?).
|
|
LDA FCB+1 ;anything to execute?
|
|
CPI ' '
|
|
JNZ UNKWN1
|
|
LDA CHGDRV ;nope, only a drive change?
|
|
ORA A
|
|
JZ GETBACK1;neither???
|
|
DCR A
|
|
STA CDRIVE ;ok, store new drive.
|
|
CALL MOVECD ;set (TDRIVE) also.
|
|
CALL DSKSEL ;and select this drive.
|
|
JMP GETBACK1;then return.
|
|
;
|
|
; Here a file name was typed. Prepare to execute it.
|
|
;
|
|
UNKWN1 LXI D,FCB+9 ;an extension specified?
|
|
LDAX D
|
|
CPI ' '
|
|
JNZ SYNERR ;yes, not allowed.
|
|
UNKWN2 PUSH D
|
|
CALL DSELECT ;select specified drive.
|
|
POP D
|
|
LXI H,COMFILE ;set the extension to 'COM'.
|
|
CALL MOVE3
|
|
CALL OPENFCB ;and open this file.
|
|
JZ UNKWN9 ;not present?
|
|
;
|
|
; Load in the program.
|
|
;
|
|
LXI H,TBASE ;store the program starting here.
|
|
UNKWN3 PUSH H
|
|
XCHG
|
|
CALL DMASET ;set transfer address.
|
|
LXI D,FCB ;and read the next record.
|
|
CALL RDREC
|
|
JNZ UNKWN4 ;end of file or read error?
|
|
POP H ;nope, bump pointer for next sector.
|
|
LXI D,128
|
|
DAD D
|
|
LXI D,CBASE ;enough room for the whole file?
|
|
MOV A,L
|
|
SUB E
|
|
MOV A,H
|
|
SBB D
|
|
JNC UNKWN0 ;no, it can't fit.
|
|
JMP UNKWN3
|
|
;
|
|
; Get here after finished reading.
|
|
;
|
|
UNKWN4 POP H
|
|
DCR A ;normal end of file?
|
|
JNZ UNKWN0
|
|
CALL RESETDR ;yes, reset previous drive.
|
|
CALL CONVFST ;convert the first file name that follows
|
|
LXI H,CHGDRV;command name.
|
|
PUSH H
|
|
MOV A,M ;set drive code in default fcb.
|
|
STA FCB
|
|
MVI A,16 ;put second name 16 bytes later.
|
|
CALL CONVERT ;convert second file name.
|
|
POP H
|
|
MOV A,M ;and set the drive for this second file.
|
|
STA FCB+16
|
|
XRA A ;clear record byte in fcb.
|
|
STA FCB+32
|
|
LXI D,TFCB ;move it into place at(005Ch).
|
|
LXI H,FCB
|
|
MVI B,33
|
|
CALL HL2DE
|
|
LXI H,INBUFF+2;now move the remainder of the input
|
|
UNKWN5 MOV A,M ;line down to (0080h). Look for a non blank.
|
|
ORA A ;or a null.
|
|
JZ UNKWN6
|
|
CPI ' '
|
|
JZ UNKWN6
|
|
INX H
|
|
JMP UNKWN5
|
|
;
|
|
; Do the line move now. It ends in a null byte.
|
|
;
|
|
UNKWN6 MVI B,0 ;keep a character count.
|
|
LXI D,TBUFF+1;data gets put here.
|
|
UNKWN7 MOV A,M ;move it now.
|
|
STAX D
|
|
ORA A
|
|
JZ UNKWN8
|
|
INR B
|
|
INX H
|
|
INX D
|
|
JMP UNKWN7
|
|
UNKWN8 MOV A,B ;now store the character count.
|
|
STA TBUFF
|
|
CALL CRLF ;clean up the screen.
|
|
CALL STDDMA ;set standard transfer address.
|
|
CALL SETCDRV ;reset current drive.
|
|
CALL TBASE ;and execute the program.
|
|
;
|
|
; Transiant programs return here (or reboot).
|
|
;
|
|
LXI SP,BATCH ;set stack first off.
|
|
CALL MOVECD ;move current drive into place (TDRIVE).
|
|
CALL DSKSEL ;and reselect it.
|
|
JMP CMMND1 ;back to comand mode.
|
|
;
|
|
; Get here if some error occured.
|
|
;
|
|
UNKWN9 CALL RESETDR ;inproper format.
|
|
JMP SYNERR
|
|
UNKWN0 LXI B,BADLOAD;read error or won't fit.
|
|
CALL PLINE
|
|
JMP GETBACK
|
|
BADLOAD DB 'Bad load',0
|
|
COMFILE DB 'COM' ;command file extension.
|
|
;
|
|
; Get here to return to command level. We will reset the
|
|
; previous active drive and then either return to command
|
|
; level directly or print error message and then return.
|
|
;
|
|
GETBACK CALL RESETDR ;reset previous drive.
|
|
GETBACK1:CALL CONVFST ;convert first name in (FCB).
|
|
LDA FCB+1 ;if this was just a drive change request,
|
|
SUI ' ' ;make sure it was valid.
|
|
LXI H,CHGDRV
|
|
ORA M
|
|
JNZ SYNERR
|
|
JMP CMMND1 ;ok, return to command level.
|
|
;
|
|
; ccp stack area.
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
CCPSTACK:EQU $ ;end of ccp stack area.
|
|
;
|
|
; Batch (or SUBMIT) processing information storage.
|
|
;
|
|
BATCH DB 0 ;batch mode flag (0=not active).
|
|
BATCHFCB:DB 0,'$$$ SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; File control block setup by the CCP.
|
|
;
|
|
FCB DB 0,' ',0,0,0,0,0,' ',0,0,0,0,0
|
|
RTNCODE DB 0 ;status returned from bdos call.
|
|
CDRIVE DB 0 ;currently active drive.
|
|
CHGDRV DB 0 ;change in drives flag (0=no change).
|
|
NBYTES DW 0 ;byte counter used by TYPE.
|
|
;
|
|
; Room for expansion?
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; Note that the following six bytes must match those at
|
|
; (PATTRN1) or cp/m will HALT. Why?
|
|
;
|
|
PATTRN2 DB 0,22,0,0,0,0;(* serial number bytes *).
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* B D O S E N T R Y
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
FBASE JMP FBASE1
|
|
;
|
|
; Bdos error table.
|
|
;
|
|
BADSCTR DW ERROR1 ;bad sector on read or write.
|
|
BADSLCT DW ERROR2 ;bad disk select.
|
|
RODISK DW ERROR3 ;disk is read only.
|
|
ROFILE DW ERROR4 ;file is read only.
|
|
;
|
|
; Entry into bdos. (DE) or (E) are the parameters passed. The
|
|
; function number desired is in register (C).
|
|
;
|
|
FBASE1 XCHG ;save the (DE) parameters.
|
|
SHLD PARAMS
|
|
XCHG
|
|
MOV A,E ;and save register (E) in particular.
|
|
STA EPARAM
|
|
LXI H,0
|
|
SHLD STATUS ;clear return status.
|
|
DAD SP
|
|
SHLD USRSTACK;save users stack pointer.
|
|
LXI SP,STKAREA;and set our own.
|
|
XRA A ;clear auto select storage space.
|
|
STA AUTOFLAG
|
|
STA AUTO
|
|
LXI H,GOBACK;set return address.
|
|
PUSH H
|
|
MOV A,C ;get function number.
|
|
CPI NFUNCTS ;valid function number?
|
|
RNC
|
|
MOV C,E ;keep single register function here.
|
|
LXI H,FUNCTNS;now look thru the function table.
|
|
MOV E,A
|
|
MVI D,0 ;(DE)=function number.
|
|
DAD D
|
|
DAD D ;(HL)=(start of table)+2*(function number).
|
|
MOV E,M
|
|
INX H
|
|
MOV D,M ;now (DE)=address for this function.
|
|
LHLD PARAMS ;retrieve parameters.
|
|
XCHG ;now (DE) has the original parameters.
|
|
PCHL ;execute desired function.
|
|
;
|
|
; BDOS function jump table.
|
|
;
|
|
NFUNCTS EQU 41 ;number of functions in followin table.
|
|
;
|
|
FUNCTNS DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
|
|
DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
|
|
DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
|
|
DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
|
|
DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
|
|
DW RTN,WTSPECL
|
|
;
|
|
; Bdos error message section.
|
|
;
|
|
ERROR1 LXI H,BADSEC ;bad sector message.
|
|
CALL PRTERR ;print it and get a 1 char responce.
|
|
CPI CNTRLC ;re-boot request (control-c)?
|
|
JZ 0 ;yes.
|
|
RET ;no, return to retry i/o function.
|
|
;
|
|
ERROR2 LXI H,BADSEL ;bad drive selected.
|
|
JMP ERROR5
|
|
;
|
|
ERROR3 LXI H,DISKRO ;disk is read only.
|
|
JMP ERROR5
|
|
;
|
|
ERROR4 LXI H,FILERO ;file is read only.
|
|
;
|
|
ERROR5 CALL PRTERR
|
|
JMP 0 ;always reboot on these errors.
|
|
;
|
|
BDOSERR DB 'Bdos Err On '
|
|
BDOSDRV DB ' : $'
|
|
BADSEC DB 'Bad Sector$'
|
|
BADSEL DB 'Select$'
|
|
FILERO DB 'File '
|
|
DISKRO DB 'R/O$'
|
|
;
|
|
; Print bdos error message.
|
|
;
|
|
PRTERR PUSH H ;save second message pointer.
|
|
CALL OUTCRLF ;send (cr)(lf).
|
|
LDA ACTIVE ;get active drive.
|
|
ADI 'A' ;make ascii.
|
|
STA BDOSDRV ;and put in message.
|
|
LXI B,BDOSERR;and print it.
|
|
CALL PRTMESG
|
|
POP B ;print second message line now.
|
|
CALL PRTMESG
|
|
;
|
|
; Get an input character. We will check our 1 character
|
|
; buffer first. This may be set by the console status routine.
|
|
;
|
|
GETCHAR LXI H,CHARBUF;check character buffer.
|
|
MOV A,M ;anything present already?
|
|
MVI M,0 ;...either case clear it.
|
|
ORA A
|
|
RNZ ;yes, use it.
|
|
JMP CONIN ;nope, go get a character responce.
|
|
;
|
|
; Input and echo a character.
|
|
;
|
|
GETECHO CALL GETCHAR ;input a character.
|
|
CALL CHKCHAR ;carriage control?
|
|
RC ;no, a regular control char so don't echo.
|
|
PUSH PSW ;ok, save character now.
|
|
MOV C,A
|
|
CALL OUTCON ;and echo it.
|
|
POP PSW ;get character and return.
|
|
RET
|
|
;
|
|
; Check character in (A). Set the zero flag on a carriage
|
|
; control character and the carry flag on any other control
|
|
; character.
|
|
;
|
|
CHKCHAR CPI CR ;check for carriage return, line feed, backspace,
|
|
RZ ;or a tab.
|
|
CPI LF
|
|
RZ
|
|
CPI TAB
|
|
RZ
|
|
CPI BS
|
|
RZ
|
|
CPI ' ' ;other control char? Set carry flag.
|
|
RET
|
|
;
|
|
; Check the console during output. Halt on a control-s, then
|
|
; reboot on a control-c. If anything else is ready, clear the
|
|
; zero flag and return (the calling routine may want to do
|
|
; something).
|
|
;
|
|
CKCONSOL:LDA CHARBUF ;check buffer.
|
|
ORA A ;if anything, just return without checking.
|
|
JNZ CKCON2
|
|
CALL CONST ;nothing in buffer. Check console.
|
|
ANI 01H ;look at bit 0.
|
|
RZ ;return if nothing.
|
|
CALL CONIN ;ok, get it.
|
|
CPI CNTRLS ;if not control-s, return with zero cleared.
|
|
JNZ CKCON1
|
|
CALL CONIN ;halt processing until another char
|
|
CPI CNTRLC ;is typed. Control-c?
|
|
JZ 0 ;yes, reboot now.
|
|
XRA A ;no, just pretend nothing was ever ready.
|
|
RET
|
|
CKCON1 STA CHARBUF ;save character in buffer for later processing.
|
|
CKCON2 MVI A,1 ;set (A) to non zero to mean something is ready.
|
|
RET
|
|
;
|
|
; Output (C) to the screen. If the printer flip-flop flag
|
|
; is set, we will send character to printer also. The console
|
|
; will be checked in the process.
|
|
;
|
|
OUTCHAR LDA OUTFLAG ;check output flag.
|
|
ORA A ;anything and we won't generate output.
|
|
JNZ OUTCHR1
|
|
PUSH B
|
|
CALL CKCONSOL;check console (we don't care whats there).
|
|
POP B
|
|
PUSH B
|
|
CALL CONOUT ;output (C) to the screen.
|
|
POP B
|
|
PUSH B
|
|
LDA PRTFLAG ;check printer flip-flop flag.
|
|
ORA A
|
|
CNZ LIST ;print it also if non-zero.
|
|
POP B
|
|
OUTCHR1 MOV A,C ;update cursors position.
|
|
LXI H,CURPOS
|
|
CPI DEL ;rubouts don't do anything here.
|
|
RZ
|
|
INR M ;bump line pointer.
|
|
CPI ' ' ;and return if a normal character.
|
|
RNC
|
|
DCR M ;restore and check for the start of the line.
|
|
MOV A,M
|
|
ORA A
|
|
RZ ;ingnore control characters at the start of the line.
|
|
MOV A,C
|
|
CPI BS ;is it a backspace?
|
|
JNZ OUTCHR2
|
|
DCR M ;yes, backup pointer.
|
|
RET
|
|
OUTCHR2 CPI LF ;is it a line feed?
|
|
RNZ ;ignore anything else.
|
|
MVI M,0 ;reset pointer to start of line.
|
|
RET
|
|
;
|
|
; Output (A) to the screen. If it is a control character
|
|
; (other than carriage control), use ^x format.
|
|
;
|
|
SHOWIT MOV A,C
|
|
CALL CHKCHAR ;check character.
|
|
JNC OUTCON ;not a control, use normal output.
|
|
PUSH PSW
|
|
MVI C,'^' ;for a control character, preceed it with '^'.
|
|
CALL OUTCHAR
|
|
POP PSW
|
|
ORI '@' ;and then use the letter equivelant.
|
|
MOV C,A
|
|
;
|
|
; Function to output (C) to the console device and expand tabs
|
|
; if necessary.
|
|
;
|
|
OUTCON MOV A,C
|
|
CPI TAB ;is it a tab?
|
|
JNZ OUTCHAR ;use regular output.
|
|
OUTCON1 MVI C,' ' ;yes it is, use spaces instead.
|
|
CALL OUTCHAR
|
|
LDA CURPOS ;go until the cursor is at a multiple of 8
|
|
|
|
ANI 07H ;position.
|
|
JNZ OUTCON1
|
|
RET
|
|
;
|
|
; Echo a backspace character. Erase the prevoius character
|
|
; on the screen.
|
|
;
|
|
BACKUP CALL BACKUP1 ;backup the screen 1 place.
|
|
MVI C,' ' ;then blank that character.
|
|
CALL CONOUT
|
|
BACKUP1 MVI C,BS ;then back space once more.
|
|
JMP CONOUT
|
|
;
|
|
; Signal a deleted line. Print a '#' at the end and start
|
|
; over.
|
|
;
|
|
NEWLINE MVI C,'#'
|
|
CALL OUTCHAR ;print this.
|
|
CALL OUTCRLF ;start new line.
|
|
NEWLN1 LDA CURPOS ;move the cursor to the starting position.
|
|
LXI H,STARTING
|
|
CMP M
|
|
RNC ;there yet?
|
|
MVI C,' '
|
|
CALL OUTCHAR ;nope, keep going.
|
|
JMP NEWLN1
|
|
;
|
|
; Output a (cr) (lf) to the console device (screen).
|
|
;
|
|
OUTCRLF MVI C,CR
|
|
CALL OUTCHAR
|
|
MVI C,LF
|
|
JMP OUTCHAR
|
|
;
|
|
; Print message pointed to by (BC). It will end with a '$'.
|
|
;
|
|
PRTMESG LDAX B ;check for terminating character.
|
|
CPI '$'
|
|
RZ
|
|
INX B
|
|
PUSH B ;otherwise, bump pointer and print it.
|
|
MOV C,A
|
|
CALL OUTCON
|
|
POP B
|
|
JMP PRTMESG
|
|
;
|
|
; Function to execute a buffered read.
|
|
;
|
|
RDBUFF LDA CURPOS ;use present location as starting one.
|
|
STA STARTING
|
|
LHLD PARAMS ;get the maximum buffer space.
|
|
MOV C,M
|
|
INX H ;point to first available space.
|
|
PUSH H ;and save.
|
|
MVI B,0 ;keep a character count.
|
|
RDBUF1 PUSH B
|
|
PUSH H
|
|
RDBUF2 CALL GETCHAR ;get the next input character.
|
|
ANI 7FH ;strip bit 7.
|
|
POP H ;reset registers.
|
|
POP B
|
|
CPI CR ;en of the line?
|
|
JZ RDBUF17
|
|
CPI LF
|
|
JZ RDBUF17
|
|
CPI BS ;how about a backspace?
|
|
JNZ RDBUF3
|
|
MOV A,B ;yes, but ignore at the beginning of the line.
|
|
ORA A
|
|
JZ RDBUF1
|
|
DCR B ;ok, update counter.
|
|
LDA CURPOS ;if we backspace to the start of the line,
|
|
STA OUTFLAG ;treat as a cancel (control-x).
|
|
JMP RDBUF10
|
|
RDBUF3 CPI DEL ;user typed a rubout?
|
|
JNZ RDBUF4
|
|
MOV A,B ;ignore at the start of the line.
|
|
ORA A
|
|
JZ RDBUF1
|
|
MOV A,M ;ok, echo the prevoius character.
|
|
DCR B ;and reset pointers (counters).
|
|
DCX H
|
|
JMP RDBUF15
|
|
RDBUF4 CPI CNTRLE ;physical end of line?
|
|
JNZ RDBUF5
|
|
PUSH B ;yes, do it.
|
|
PUSH H
|
|
CALL OUTCRLF
|
|
XRA A ;and update starting position.
|
|
STA STARTING
|
|
JMP RDBUF2
|
|
RDBUF5 CPI CNTRLP ;control-p?
|
|
JNZ RDBUF6
|
|
PUSH H ;yes, flip the print flag filp-flop byte.
|
|
LXI H,PRTFLAG
|
|
MVI A,1 ;PRTFLAG=1-PRTFLAG
|
|
SUB M
|
|
MOV M,A
|
|
POP H
|
|
JMP RDBUF1
|
|
RDBUF6 CPI CNTRLX ;control-x (cancel)?
|
|
JNZ RDBUF8
|
|
POP H
|
|
RDBUF7 LDA STARTING;yes, backup the cursor to here.
|
|
LXI H,CURPOS
|
|
CMP M
|
|
JNC RDBUFF ;done yet?
|
|
DCR M ;no, decrement pointer and output back up one space.
|
|
CALL BACKUP
|
|
JMP RDBUF7
|
|
RDBUF8 CPI CNTRLU ;cntrol-u (cancel line)?
|
|
JNZ RDBUF9
|
|
CALL NEWLINE ;start a new line.
|
|
POP H
|
|
JMP RDBUFF
|
|
RDBUF9 CPI CNTRLR ;control-r?
|
|
JNZ RDBUF14
|
|
RDBUF10 PUSH B ;yes, start a new line and retype the old one.
|
|
CALL NEWLINE
|
|
POP B
|
|
POP H
|
|
PUSH H
|
|
PUSH B
|
|
RDBUF11 MOV A,B ;done whole line yet?
|
|
ORA A
|
|
JZ RDBUF12
|
|
INX H ;nope, get next character.
|
|
MOV C,M
|
|
DCR B ;count it.
|
|
PUSH B
|
|
PUSH H
|
|
CALL SHOWIT ;and display it.
|
|
POP H
|
|
POP B
|
|
JMP RDBUF11
|
|
RDBUF12 PUSH H ;done with line. If we were displaying
|
|
LDA OUTFLAG ;then update cursor position.
|
|
ORA A
|
|
JZ RDBUF2
|
|
LXI H,CURPOS;because this line is shorter, we must
|
|
SUB M ;back up the cursor (not the screen however)
|
|
STA OUTFLAG ;some number of positions.
|
|
RDBUF13 CALL BACKUP ;note that as long as (OUTFLAG) is non
|
|
LXI H,OUTFLAG;zero, the screen will not be changed.
|
|
DCR M
|
|
JNZ RDBUF13
|
|
JMP RDBUF2 ;now just get the next character.
|
|
;
|
|
; Just a normal character, put this in our buffer and echo.
|
|
;
|
|
RDBUF14 INX H
|
|
MOV M,A ;store character.
|
|
INR B ;and count it.
|
|
RDBUF15 PUSH B
|
|
PUSH H
|
|
MOV C,A ;echo it now.
|
|
CALL SHOWIT
|
|
POP H
|
|
POP B
|
|
MOV A,M ;was it an abort request?
|
|
CPI CNTRLC ;control-c abort?
|
|
MOV A,B
|
|
JNZ RDBUF16
|
|
CPI 1 ;only if at start of line.
|
|
JZ 0
|
|
RDBUF16 CMP C ;nope, have we filled the buffer?
|
|
JC RDBUF1
|
|
RDBUF17 POP H ;yes end the line and return.
|
|
MOV M,B
|
|
MVI C,CR
|
|
JMP OUTCHAR ;output (cr) and return.
|
|
;
|
|
; Function to get a character from the console device.
|
|
;
|
|
GETCON CALL GETECHO ;get and echo.
|
|
JMP SETSTAT ;save status and return.
|
|
;
|
|
; Function to get a character from the tape reader device.
|
|
;
|
|
GETRDR CALL READER ;get a character from reader, set status and return.
|
|
JMP SETSTAT
|
|
;
|
|
; Function to perform direct console i/o. If (C) contains (FF)
|
|
; then this is an input request. If (C) contains (FE) then
|
|
; this is a status request. Otherwise we are to output (C).
|
|
;
|
|
DIRCIO MOV A,C ;test for (FF).
|
|
INR A
|
|
JZ DIRC1
|
|
INR A ;test for (FE).
|
|
JZ CONST
|
|
JMP CONOUT ;just output (C).
|
|
DIRC1 CALL CONST ;this is an input request.
|
|
ORA A
|
|
JZ GOBACK1 ;not ready? Just return (directly).
|
|
CALL CONIN ;yes, get character.
|
|
JMP SETSTAT ;set status and return.
|
|
;
|
|
; Function to return the i/o byte.
|
|
;
|
|
GETIOB LDA IOBYTE
|
|
JMP SETSTAT
|
|
;
|
|
; Function to set the i/o byte.
|
|
;
|
|
SETIOB LXI H,IOBYTE
|
|
MOV M,C
|
|
RET
|
|
;
|
|
; Function to print the character string pointed to by (DE)
|
|
; on the console device. The string ends with a '$'.
|
|
;
|
|
PRTSTR XCHG
|
|
MOV C,L
|
|
MOV B,H ;now (BC) points to it.
|
|
JMP PRTMESG
|
|
;
|
|
; Function to interigate the console device.
|
|
;
|
|
GETCSTS CALL CKCONSOL
|
|
;
|
|
; Get here to set the status and return to the cleanup
|
|
; section. Then back to the user.
|
|
;
|
|
SETSTAT STA STATUS
|
|
RTN RET
|
|
;
|
|
; Set the status to 1 (read or write error code).
|
|
;
|
|
IOERR1 MVI A,1
|
|
JMP SETSTAT
|
|
;
|
|
OUTFLAG DB 0 ;output flag (non zero means no output).
|
|
STARTING:DB 2 ;starting position for cursor.
|
|
CURPOS DB 0 ;cursor position (0=start of line).
|
|
PRTFLAG DB 0 ;printer flag (control-p toggle). List if non zero.
|
|
CHARBUF DB 0 ;single input character buffer.
|
|
;
|
|
; Stack area for BDOS calls.
|
|
;
|
|
USRSTACK:DW 0 ;save users stack pointer here.
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
STKAREA EQU $ ;end of stack area.
|
|
;
|
|
USERNO DB 0 ;current user number.
|
|
ACTIVE DB 0 ;currently active drive.
|
|
PARAMS DW 0 ;save (DE) parameters here on entry.
|
|
STATUS DW 0 ;status returned from bdos function.
|
|
;
|
|
; Select error occured, jump to error routine.
|
|
;
|
|
SLCTERR LXI H,BADSLCT
|
|
;
|
|
; Jump to (HL) indirectly.
|
|
;
|
|
JUMPHL MOV E,M
|
|
INX H
|
|
MOV D,M ;now (DE) contain the desired address.
|
|
XCHG
|
|
PCHL
|
|
;
|
|
; Block move. (DE) to (HL), (C) bytes total.
|
|
;
|
|
DE2HL INR C ;is count down to zero?
|
|
DE2HL1 DCR C
|
|
RZ ;yes, we are done.
|
|
LDAX D ;no, move one more byte.
|
|
MOV M,A
|
|
INX D
|
|
INX H
|
|
JMP DE2HL1 ;and repeat.
|
|
;
|
|
; Select the desired drive.
|
|
;
|
|
SELECT LDA ACTIVE ;get active disk.
|
|
MOV C,A
|
|
CALL SELDSK ;select it.
|
|
MOV A,H ;valid drive?
|
|
ORA L ;valid drive?
|
|
RZ ;return if not.
|
|
;
|
|
; Here, the BIOS returned the address of the parameter block
|
|
; in (HL). We will extract the necessary pointers and save them.
|
|
;
|
|
MOV E,M ;yes, get address of translation table into (DE).
|
|
INX H
|
|
MOV D,M
|
|
INX H
|
|
SHLD SCRATCH1 ;save pointers to scratch areas.
|
|
INX H
|
|
INX H
|
|
SHLD SCRATCH2 ;ditto.
|
|
INX H
|
|
INX H
|
|
SHLD SCRATCH3 ;ditto.
|
|
INX H
|
|
INX H
|
|
XCHG ;now save the translation table address.
|
|
SHLD XLATE
|
|
LXI H,DIRBUF ;put the next 8 bytes here.
|
|
MVI C,8 ;they consist of the directory buffer
|
|
CALL DE2HL ;pointer, parameter block pointer,
|
|
LHLD DISKPB ;check and allocation vectors.
|
|
XCHG
|
|
LXI H,SECTORS ;move parameter block into our ram.
|
|
MVI C,15 ;it is 15 bytes long.
|
|
CALL DE2HL
|
|
LHLD DSKSIZE ;check disk size.
|
|
MOV A,H ;more than 256 blocks on this?
|
|
LXI H,BIGDISK
|
|
MVI M,0FFH ;set to samll.
|
|
ORA A
|
|
JZ SELECT1
|
|
MVI M,0 ;wrong, set to large.
|
|
SELECT1 MVI A,0FFH ;clear the zero flag.
|
|
ORA A
|
|
RET
|
|
;
|
|
; Routine to home the disk track head and clear pointers.
|
|
;
|
|
HOMEDRV CALL HOME ;home the head.
|
|
XRA A
|
|
LHLD SCRATCH2;set our track pointer also.
|
|
MOV M,A
|
|
INX H
|
|
MOV M,A
|
|
LHLD SCRATCH3;and our sector pointer.
|
|
MOV M,A
|
|
INX H
|
|
MOV M,A
|
|
RET
|
|
;
|
|
; Do the actual disk read and check the error return status.
|
|
;
|
|
DOREAD CALL READ
|
|
JMP IORET
|
|
;
|
|
; Do the actual disk write and handle any bios error.
|
|
;
|
|
DOWRITE CALL WRITE
|
|
IORET ORA A
|
|
RZ ;return unless an error occured.
|
|
LXI H,BADSCTR;bad read/write on this sector.
|
|
JMP JUMPHL
|
|
;
|
|
; Routine to select the track and sector that the desired
|
|
; block number falls in.
|
|
;
|
|
TRKSEC LHLD FILEPOS ;get position of last accessed file
|
|
MVI C,2 ;in directory and compute sector #.
|
|
CALL SHIFTR ;sector #=file-position/4.
|
|
SHLD BLKNMBR ;save this as the block number of interest.
|
|
SHLD CKSUMTBL;what's it doing here too?
|
|
;
|
|
; if the sector number has already been set (BLKNMBR), enter
|
|
; at this point.
|
|
;
|
|
TRKSEC1 LXI H,BLKNMBR
|
|
MOV C,M ;move sector number into (BC).
|
|
INX H
|
|
MOV B,M
|
|
LHLD SCRATCH3;get current sector number and
|
|
MOV E,M ;move this into (DE).
|
|
INX H
|
|
MOV D,M
|
|
LHLD SCRATCH2;get current track number.
|
|
MOV A,M ;and this into (HL).
|
|
INX H
|
|
MOV H,M
|
|
MOV L,A
|
|
TRKSEC2 MOV A,C ;is desired sector before current one?
|
|
SUB E
|
|
MOV A,B
|
|
SBB D
|
|
JNC TRKSEC3
|
|
PUSH H ;yes, decrement sectors by one track.
|
|
LHLD SECTORS ;get sectors per track.
|
|
MOV A,E
|
|
SUB L
|
|
MOV E,A
|
|
MOV A,D
|
|
SBB H
|
|
MOV D,A ;now we have backed up one full track.
|
|
POP H
|
|
DCX H ;adjust track counter.
|
|
JMP TRKSEC2
|
|
TRKSEC3 PUSH H ;desired sector is after current one.
|
|
LHLD SECTORS ;get sectors per track.
|
|
DAD D ;bump sector pointer to next track.
|
|
JC TRKSEC4
|
|
MOV A,C ;is desired sector now before current one?
|
|
SUB L
|
|
MOV A,B
|
|
SBB H
|
|
JC TRKSEC4
|
|
XCHG ;not yes, increment track counter
|
|
POP H ;and continue until it is.
|
|
INX H
|
|
JMP TRKSEC3
|
|
;
|
|
; here we have determined the track number that contains the
|
|
; desired sector.
|
|
;
|
|
TRKSEC4 POP H ;get track number (HL).
|
|
PUSH B
|
|
PUSH D
|
|
PUSH H
|
|
XCHG
|
|
LHLD OFFSET ;adjust for first track offset.
|
|
DAD D
|
|
MOV B,H
|
|
MOV C,L
|
|
CALL SETTRK ;select this track.
|
|
POP D ;reset current track pointer.
|
|
LHLD SCRATCH2
|
|
MOV M,E
|
|
INX H
|
|
MOV M,D
|
|
POP D
|
|
LHLD SCRATCH3;reset the first sector on this track.
|
|
MOV M,E
|
|
INX H
|
|
MOV M,D
|
|
POP B
|
|
MOV A,C ;now subtract the desired one.
|
|
SUB E ;to make it relative (1-# sectors/track).
|
|
MOV C,A
|
|
MOV A,B
|
|
SBB D
|
|
MOV B,A
|
|
LHLD XLATE ;translate this sector according to this table.
|
|
XCHG
|
|
CALL SECTRN ;let the bios translate it.
|
|
MOV C,L
|
|
MOV B,H
|
|
JMP SETSEC ;and select it.
|
|
;
|
|
; Compute block number from record number (SAVNREC) and
|
|
; extent number (SAVEXT).
|
|
;
|
|
GETBLOCK:LXI H,BLKSHFT;get logical to physical conversion.
|
|
MOV C,M ;note that this is base 2 log of ratio.
|
|
LDA SAVNREC ;get record number.
|
|
GETBLK1 ORA A ;compute (A)=(A)/2^BLKSHFT.
|
|
RAR
|
|
DCR C
|
|
JNZ GETBLK1
|
|
MOV B,A ;save result in (B).
|
|
MVI A,8
|
|
SUB M
|
|
MOV C,A ;compute (C)=8-BLKSHFT.
|
|
LDA SAVEXT
|
|
GETBLK2 DCR C ;compute (A)=SAVEXT*2^(8-BLKSHFT).
|
|
JZ GETBLK3
|
|
ORA A
|
|
RAL
|
|
JMP GETBLK2
|
|
GETBLK3 ADD B
|
|
RET
|
|
;
|
|
; Routine to extract the (BC) block byte from the fcb pointed
|
|
; to by (PARAMS). If this is a big-disk, then these are 16 bit
|
|
; block numbers, else they are 8 bit numbers.
|
|
; Number is returned in (HL).
|
|
;
|
|
EXTBLK LHLD PARAMS ;get fcb address.
|
|
LXI D,16 ;block numbers start 16 bytes into fcb.
|
|
DAD D
|
|
DAD B
|
|
LDA BIGDISK ;are we using a big-disk?
|
|
ORA A
|
|
JZ EXTBLK1
|
|
MOV L,M ;no, extract an 8 bit number from the fcb.
|
|
MVI H,0
|
|
RET
|
|
EXTBLK1 DAD B ;yes, extract a 16 bit number.
|
|
MOV E,M
|
|
INX H
|
|
MOV D,M
|
|
XCHG ;return in (HL).
|
|
RET
|
|
;
|
|
; Compute block number.
|
|
;
|
|
COMBLK CALL GETBLOCK
|
|
MOV C,A
|
|
MVI B,0
|
|
CALL EXTBLK
|
|
SHLD BLKNMBR
|
|
RET
|
|
;
|
|
; Check for a zero block number (unused).
|
|
;
|
|
CHKBLK LHLD BLKNMBR
|
|
MOV A,L ;is it zero?
|
|
ORA H
|
|
RET
|
|
;
|
|
; Adjust physical block (BLKNMBR) and convert to logical
|
|
; sector (LOGSECT). This is the starting sector of this block.
|
|
; The actual sector of interest is then added to this and the
|
|
; resulting sector number is stored back in (BLKNMBR). This
|
|
; will still have to be adjusted for the track number.
|
|
;
|
|
LOGICAL LDA BLKSHFT ;get log2(physical/logical sectors).
|
|
LHLD BLKNMBR ;get physical sector desired.
|
|
LOGICL1 DAD H ;compute logical sector number.
|
|
DCR A ;note logical sectors are 128 bytes long.
|
|
JNZ LOGICL1
|
|
SHLD LOGSECT ;save logical sector.
|
|
LDA BLKMASK ;get block mask.
|
|
MOV C,A
|
|
LDA SAVNREC ;get next sector to access.
|
|
ANA C ;extract the relative position within physical block.
|
|
ORA L ;and add it too logical sector.
|
|
MOV L,A
|
|
SHLD BLKNMBR ;and store.
|
|
RET
|
|
;
|
|
; Set (HL) to point to extent byte in fcb.
|
|
;
|
|
SETEXT LHLD PARAMS
|
|
LXI D,12 ;it is the twelth byte.
|
|
DAD D
|
|
RET
|
|
;
|
|
; Set (HL) to point to record count byte in fcb and (DE) to
|
|
; next record number byte.
|
|
;
|
|
SETHLDE LHLD PARAMS
|
|
LXI D,15 ;record count byte (#15).
|
|
DAD D
|
|
XCHG
|
|
LXI H,17 ;next record number (#32).
|
|
DAD D
|
|
RET
|
|
;
|
|
; Save current file data from fcb.
|
|
;
|
|
STRDATA CALL SETHLDE
|
|
MOV A,M ;get and store record count byte.
|
|
STA SAVNREC
|
|
XCHG
|
|
MOV A,M ;get and store next record number byte.
|
|
STA SAVNXT
|
|
CALL SETEXT ;point to extent byte.
|
|
LDA EXTMASK ;get extent mask.
|
|
ANA M
|
|
STA SAVEXT ;and save extent here.
|
|
RET
|
|
;
|
|
; Set the next record to access. If (MODE) is set to 2, then
|
|
; the last record byte (SAVNREC) has the correct number to access.
|
|
; For sequential access, (MODE) will be equal to 1.
|
|
;
|
|
SETNREC CALL SETHLDE
|
|
LDA MODE ;get sequential flag (=1).
|
|
CPI 2 ;a 2 indicates that no adder is needed.
|
|
JNZ STNREC1
|
|
XRA A ;clear adder (random access?).
|
|
STNREC1 MOV C,A
|
|
LDA SAVNREC ;get last record number.
|
|
ADD C ;increment record count.
|
|
MOV M,A ;and set fcb's next record byte.
|
|
XCHG
|
|
LDA SAVNXT ;get next record byte from storage.
|
|
MOV M,A ;and put this into fcb as number of records used.
|
|
RET
|
|
;
|
|
; Shift (HL) right (C) bits.
|
|
;
|
|
SHIFTR INR C
|
|
SHIFTR1 DCR C
|
|
RZ
|
|
MOV A,H
|
|
ORA A
|
|
RAR
|
|
MOV H,A
|
|
MOV A,L
|
|
RAR
|
|
MOV L,A
|
|
JMP SHIFTR1
|
|
;
|
|
; Compute the check-sum for the directory buffer. Return
|
|
; integer sum in (A).
|
|
;
|
|
CHECKSUM:MVI C,128 ;length of buffer.
|
|
LHLD DIRBUF ;get its location.
|
|
XRA A ;clear summation byte.
|
|
CHKSUM1 ADD M ;and compute sum ignoring carries.
|
|
INX H
|
|
DCR C
|
|
JNZ CHKSUM1
|
|
RET
|
|
;
|
|
; Shift (HL) left (C) bits.
|
|
;
|
|
SHIFTL INR C
|
|
SHIFTL1 DCR C
|
|
RZ
|
|
DAD H ;shift left 1 bit.
|
|
JMP SHIFTL1
|
|
;
|
|
; Routine to set a bit in a 16 bit value contained in (BC).
|
|
; The bit set depends on the current drive selection.
|
|
;
|
|
SETBIT PUSH B ;save 16 bit word.
|
|
LDA ACTIVE ;get active drive.
|
|
MOV C,A
|
|
LXI H,1
|
|
CALL SHIFTL ;shift bit 0 into place.
|
|
POP B ;now 'or' this with the original word.
|
|
MOV A,C
|
|
ORA L
|
|
MOV L,A ;low byte done, do high byte.
|
|
MOV A,B
|
|
ORA H
|
|
MOV H,A
|
|
RET
|
|
;
|
|
; Extract the write protect status bit for the current drive.
|
|
; The result is returned in (A), bit 0.
|
|
;
|
|
GETWPRT LHLD WRTPRT ;get status bytes.
|
|
LDA ACTIVE ;which drive is current?
|
|
MOV C,A
|
|
CALL SHIFTR ;shift status such that bit 0 is the
|
|
MOV A,L ;one of interest for this drive.
|
|
ANI 01H ;and isolate it.
|
|
RET
|
|
;
|
|
; Function to write protect the current disk.
|
|
;
|
|
WRTPRTD LXI H,WRTPRT;point to status word.
|
|
MOV C,M ;set (BC) equal to the status.
|
|
INX H
|
|
MOV B,M
|
|
CALL SETBIT ;and set this bit according to current drive.
|
|
SHLD WRTPRT ;then save.
|
|
LHLD DIRSIZE ;now save directory size limit.
|
|
INX H ;remember the last one.
|
|
XCHG
|
|
LHLD SCRATCH1;and store it here.
|
|
MOV M,E ;put low byte.
|
|
INX H
|
|
MOV M,D ;then high byte.
|
|
RET
|
|
;
|
|
; Check for a read only file.
|
|
;
|
|
CHKROFL CALL FCB2HL ;set (HL) to file entry in directory buffer.
|
|
CKROF1 LXI D,9 ;look at bit 7 of the ninth byte.
|
|
DAD D
|
|
MOV A,M
|
|
RAL
|
|
RNC ;return if ok.
|
|
LXI H,ROFILE;else, print error message and terminate.
|
|
JMP JUMPHL
|
|
;
|
|
; Check the write protect status of the active disk.
|
|
;
|
|
CHKWPRT CALL GETWPRT
|
|
RZ ;return if ok.
|
|
LXI H,RODISK;else print message and terminate.
|
|
JMP JUMPHL
|
|
;
|
|
; Routine to set (HL) pointing to the proper entry in the
|
|
; directory buffer.
|
|
;
|
|
FCB2HL LHLD DIRBUF ;get address of buffer.
|
|
LDA FCBPOS ;relative position of file.
|
|
;
|
|
; Routine to add (A) to (HL).
|
|
;
|
|
ADDA2HL ADD L
|
|
MOV L,A
|
|
RNC
|
|
INR H ;take care of any carry.
|
|
RET
|
|
;
|
|
; Routine to get the 's2' byte from the fcb supplied in
|
|
; the initial parameter specification.
|
|
;
|
|
GETS2 LHLD PARAMS ;get address of fcb.
|
|
LXI D,14 ;relative position of 's2'.
|
|
DAD D
|
|
MOV A,M ;extract this byte.
|
|
RET
|
|
;
|
|
; Clear the 's2' byte in the fcb.
|
|
;
|
|
CLEARS2 CALL GETS2 ;this sets (HL) pointing to it.
|
|
MVI M,0 ;now clear it.
|
|
RET
|
|
;
|
|
; Set bit 7 in the 's2' byte of the fcb.
|
|
;
|
|
SETS2B7 CALL GETS2 ;get the byte.
|
|
ORI 80H ;and set bit 7.
|
|
MOV M,A ;then store.
|
|
RET
|
|
;
|
|
; Compare (FILEPOS) with (SCRATCH1) and set flags based on
|
|
; the difference. This checks to see if there are more file
|
|
; names in the directory. We are at (FILEPOS) and there are
|
|
; (SCRATCH1) of them to check.
|
|
;
|
|
MOREFLS LHLD FILEPOS ;we are here.
|
|
XCHG
|
|
LHLD SCRATCH1;and don't go past here.
|
|
MOV A,E ;compute difference but don't keep.
|
|
SUB M
|
|
INX H
|
|
MOV A,D
|
|
SBB M ;set carry if no more names.
|
|
RET
|
|
;
|
|
; Call this routine to prevent (SCRATCH1) from being greater
|
|
; than (FILEPOS).
|
|
;
|
|
CHKNMBR CALL MOREFLS ;SCRATCH1 too big?
|
|
RC
|
|
INX D ;yes, reset it to (FILEPOS).
|
|
MOV M,D
|
|
DCX H
|
|
MOV M,E
|
|
RET
|
|
;
|
|
; Compute (HL)=(DE)-(HL)
|
|
;
|
|
SUBHL MOV A,E ;compute difference.
|
|
SUB L
|
|
MOV L,A ;store low byte.
|
|
MOV A,D
|
|
SBB H
|
|
MOV H,A ;and then high byte.
|
|
RET
|
|
;
|
|
; Set the directory checksum byte.
|
|
;
|
|
SETDIR MVI C,0FFH
|
|
;
|
|
; Routine to set or compare the directory checksum byte. If
|
|
; (C)=0ffh, then this will set the checksum byte. Else the byte
|
|
; will be checked. If the check fails (the disk has been changed),
|
|
; then this disk will be write protected.
|
|
;
|
|
CHECKDIR:LHLD CKSUMTBL
|
|
XCHG
|
|
LHLD ALLOC1
|
|
CALL SUBHL
|
|
RNC ;ok if (CKSUMTBL) > (ALLOC1), so return.
|
|
PUSH B
|
|
CALL CHECKSUM;else compute checksum.
|
|
LHLD CHKVECT ;get address of checksum table.
|
|
XCHG
|
|
LHLD CKSUMTBL
|
|
DAD D ;set (HL) to point to byte for this drive.
|
|
POP B
|
|
INR C ;set or check ?
|
|
JZ CHKDIR1
|
|
CMP M ;check them.
|
|
RZ ;return if they are the same.
|
|
CALL MOREFLS ;not the same, do we care?
|
|
RNC
|
|
CALL WRTPRTD ;yes, mark this as write protected.
|
|
RET
|
|
CHKDIR1 MOV M,A ;just set the byte.
|
|
RET
|
|
;
|
|
; Do a write to the directory of the current disk.
|
|
;
|
|
DIRWRITE:CALL SETDIR ;set checksum byte.
|
|
CALL DIRDMA ;set directory dma address.
|
|
MVI C,1 ;tell the bios to actually write.
|
|
CALL DOWRITE ;then do the write.
|
|
JMP DEFDMA
|
|
;
|
|
; Read from the directory.
|
|
;
|
|
DIRREAD CALL DIRDMA ;set the directory dma address.
|
|
CALL DOREAD ;and read it.
|
|
;
|
|
; Routine to set the dma address to the users choice.
|
|
;
|
|
DEFDMA LXI H,USERDMA;reset the default dma address and return.
|
|
JMP DIRDMA1
|
|
;
|
|
; Routine to set the dma address for directory work.
|
|
;
|
|
DIRDMA LXI H,DIRBUF
|
|
;
|
|
; Set the dma address. On entry, (HL) points to
|
|
; word containing the desired dma address.
|
|
;
|
|
DIRDMA1 MOV C,M
|
|
INX H
|
|
MOV B,M ;setup (BC) and go to the bios to set it.
|
|
JMP SETDMA
|
|
;
|
|
; Move the directory buffer into user's dma space.
|
|
;
|
|
MOVEDIR LHLD DIRBUF ;buffer is located here, and
|
|
XCHG
|
|
LHLD USERDMA; put it here.
|
|
MVI C,128 ;this is its length.
|
|
JMP DE2HL ;move it now and return.
|
|
;
|
|
; Check (FILEPOS) and set the zero flag if it equals 0ffffh.
|
|
;
|
|
CKFILPOS:LXI H,FILEPOS
|
|
MOV A,M
|
|
INX H
|
|
CMP M ;are both bytes the same?
|
|
RNZ
|
|
INR A ;yes, but are they each 0ffh?
|
|
RET
|
|
;
|
|
; Set location (FILEPOS) to 0ffffh.
|
|
;
|
|
STFILPOS:LXI H,0FFFFH
|
|
SHLD FILEPOS
|
|
RET
|
|
;
|
|
; Move on to the next file position within the current
|
|
; directory buffer. If no more exist, set pointer to 0ffffh
|
|
; and the calling routine will check for this. Enter with (C)
|
|
; equal to 0ffh to cause the checksum byte to be set, else we
|
|
; will check this disk and set write protect if checksums are
|
|
; not the same (applies only if another directory sector must
|
|
; be read).
|
|
;
|
|
NXENTRY LHLD DIRSIZE ;get directory entry size limit.
|
|
XCHG
|
|
LHLD FILEPOS ;get current count.
|
|
INX H ;go on to the next one.
|
|
SHLD FILEPOS
|
|
CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS)
|
|
JNC NXENT1 ;is there more room left?
|
|
JMP STFILPOS;no. Set this flag and return.
|
|
NXENT1 LDA FILEPOS ;get file position within directory.
|
|
ANI 03H ;only look within this sector (only 4 entries fit).
|
|
MVI B,5 ;convert to relative position (32 bytes each).
|
|
NXENT2 ADD A ;note that this is not efficient code.
|
|
DCR B ;5 'ADD A's would be better.
|
|
JNZ NXENT2
|
|
STA FCBPOS ;save it as position of fcb.
|
|
ORA A
|
|
RNZ ;return if we are within buffer.
|
|
PUSH B
|
|
CALL TRKSEC ;we need the next directory sector.
|
|
CALL DIRREAD
|
|
POP B
|
|
JMP CHECKDIR
|
|
;
|
|
; Routine to to get a bit from the disk space allocation
|
|
; map. It is returned in (A), bit position 0. On entry to here,
|
|
; set (BC) to the block number on the disk to check.
|
|
; On return, (D) will contain the original bit position for
|
|
; this block number and (HL) will point to the address for it.
|
|
;
|
|
CKBITMAP:MOV A,C ;determine bit number of interest.
|
|
ANI 07H ;compute (D)=(E)=(C and 7)+1.
|
|
INR A
|
|
MOV E,A ;save particular bit number.
|
|
MOV D,A
|
|
;
|
|
; compute (BC)=(BC)/8.
|
|
;
|
|
MOV A,C
|
|
RRC ;now shift right 3 bits.
|
|
RRC
|
|
RRC
|
|
ANI 1FH ;and clear bits 7,6,5.
|
|
MOV C,A
|
|
MOV A,B
|
|
ADD A ;now shift (B) into bits 7,6,5.
|
|
ADD A
|
|
ADD A
|
|
ADD A
|
|
ADD A
|
|
ORA C ;and add in (C).
|
|
MOV C,A ;ok, (C) ha been completed.
|
|
MOV A,B ;is there a better way of doing this?
|
|
RRC
|
|
RRC
|
|
RRC
|
|
ANI 1FH
|
|
MOV B,A ;and now (B) is completed.
|
|
;
|
|
; use this as an offset into the disk space allocation
|
|
; table.
|
|
;
|
|
LHLD ALOCVECT
|
|
DAD B
|
|
MOV A,M ;now get correct byte.
|
|
CKBMAP1 RLC ;get correct bit into position 0.
|
|
DCR E
|
|
JNZ CKBMAP1
|
|
RET
|
|
;
|
|
; Set or clear the bit map such that block number (BC) will be marked
|
|
; as used. On entry, if (E)=0 then this bit will be cleared, if it equals
|
|
; 1 then it will be set (don't use anyother values).
|
|
;
|
|
STBITMAP:PUSH D
|
|
CALL CKBITMAP;get the byte of interest.
|
|
ANI 0FEH ;clear the affected bit.
|
|
POP B
|
|
ORA C ;and now set it acording to (C).
|
|
;
|
|
; entry to restore the original bit position and then store
|
|
; in table. (A) contains the value, (D) contains the bit
|
|
; position (1-8), and (HL) points to the address within the
|
|
; space allocation table for this byte.
|
|
;
|
|
STBMAP1 RRC ;restore original bit position.
|
|
DCR D
|
|
JNZ STBMAP1
|
|
MOV M,A ;and stor byte in table.
|
|
RET
|
|
;
|
|
; Set/clear space used bits in allocation map for this file.
|
|
; On entry, (C)=1 to set the map and (C)=0 to clear it.
|
|
;
|
|
SETFILE CALL FCB2HL ;get address of fcb
|
|
LXI D,16
|
|
DAD D ;get to block number bytes.
|
|
PUSH B
|
|
MVI C,17 ;check all 17 bytes (max) of table.
|
|
SETFL1 POP D
|
|
DCR C ;done all bytes yet?
|
|
RZ
|
|
PUSH D
|
|
LDA BIGDISK ;check disk size for 16 bit block numbers.
|
|
ORA A
|
|
JZ SETFL2
|
|
PUSH B ;only 8 bit numbers. set (BC) to this one.
|
|
PUSH H
|
|
MOV C,M ;get low byte from table, always
|
|
MVI B,0 ;set high byte to zero.
|
|
JMP SETFL3
|
|
SETFL2 DCR C ;for 16 bit block numbers, adjust counter.
|
|
PUSH B
|
|
MOV C,M ;now get both the low and high bytes.
|
|
INX H
|
|
MOV B,M
|
|
PUSH H
|
|
SETFL3 MOV A,C ;block used?
|
|
ORA B
|
|
JZ SETFL4
|
|
LHLD DSKSIZE ;is this block number within the
|
|
MOV A,L ;space on the disk?
|
|
SUB C
|
|
MOV A,H
|
|
SBB B
|
|
CNC STBITMAP;yes, set the proper bit.
|
|
SETFL4 POP H ;point to next block number in fcb.
|
|
INX H
|
|
POP B
|
|
JMP SETFL1
|
|
;
|
|
; Construct the space used allocation bit map for the active
|
|
; drive. If a file name starts with '$' and it is under the
|
|
; current user number, then (STATUS) is set to minus 1. Otherwise
|
|
; it is not set at all.
|
|
;
|
|
BITMAP LHLD DSKSIZE ;compute size of allocation table.
|
|
MVI C,3
|
|
CALL SHIFTR ;(HL)=(HL)/8.
|
|
INX H ;at lease 1 byte.
|
|
MOV B,H
|
|
MOV C,L ;set (BC) to the allocation table length.
|
|
;
|
|
; Initialize the bitmap for this drive. Right now, the first
|
|
; two bytes are specified by the disk parameter block. However
|
|
; a patch could be entered here if it were necessary to setup
|
|
; this table in a special mannor. For example, the bios could
|
|
; determine locations of 'bad blocks' and set them as already
|
|
; 'used' in the map.
|
|
;
|
|
LHLD ALOCVECT;now zero out the table now.
|
|
BITMAP1 MVI M,0
|
|
INX H
|
|
DCX B
|
|
MOV A,B
|
|
ORA C
|
|
JNZ BITMAP1
|
|
LHLD ALLOC0 ;get initial space used by directory.
|
|
XCHG
|
|
LHLD ALOCVECT;and put this into map.
|
|
MOV M,E
|
|
INX H
|
|
MOV M,D
|
|
;
|
|
; End of initialization portion.
|
|
;
|
|
CALL HOMEDRV ;now home the drive.
|
|
LHLD SCRATCH1
|
|
MVI M,3 ;force next directory request to read
|
|
INX H ;in a sector.
|
|
MVI M,0
|
|
CALL STFILPOS;clear initial file position also.
|
|
BITMAP2 MVI C,0FFH ;read next file name in directory
|
|
CALL NXENTRY ;and set checksum byte.
|
|
CALL CKFILPOS;is there another file?
|
|
RZ
|
|
CALL FCB2HL ;yes, get its address.
|
|
MVI A,0E5H
|
|
CMP M ;empty file entry?
|
|
JZ BITMAP2
|
|
LDA USERNO ;no, correct user number?
|
|
CMP M
|
|
JNZ BITMAP3
|
|
INX H
|
|
MOV A,M ;yes, does name start with a '$'?
|
|
SUI '$'
|
|
JNZ BITMAP3
|
|
DCR A ;yes, set atatus to minus one.
|
|
STA STATUS
|
|
BITMAP3 MVI C,1 ;now set this file's space as used in bit map.
|
|
CALL SETFILE
|
|
CALL CHKNMBR ;keep (SCRATCH1) in bounds.
|
|
JMP BITMAP2
|
|
;
|
|
; Set the status (STATUS) and return.
|
|
;
|
|
STSTATUS:LDA FNDSTAT
|
|
JMP SETSTAT
|
|
;
|
|
; Check extents in (A) and (C). Set the zero flag if they
|
|
; are the same. The number of 16k chunks of disk space that
|
|
; the directory extent covers is expressad is (EXTMASK+1).
|
|
; No registers are modified.
|
|
;
|
|
SAMEXT PUSH B
|
|
PUSH PSW
|
|
LDA EXTMASK ;get extent mask and use it to
|
|
CMA ;to compare both extent numbers.
|
|
MOV B,A ;save resulting mask here.
|
|
MOV A,C ;mask first extent and save in (C).
|
|
ANA B
|
|
MOV C,A
|
|
POP PSW ;now mask second extent and compare
|
|
ANA B ;with the first one.
|
|
SUB C
|
|
ANI 1FH ;(* only check buts 0-4 *)
|
|
POP B ;the zero flag is set if they are the same.
|
|
RET ;restore (BC) and return.
|
|
;
|
|
; Search for the first occurence of a file name. On entry,
|
|
; register (C) should contain the number of bytes of the fcb
|
|
; that must match.
|
|
;
|
|
FINDFST MVI A,0FFH
|
|
STA FNDSTAT
|
|
LXI H,COUNTER;save character count.
|
|
MOV M,C
|
|
LHLD PARAMS ;get filename to match.
|
|
SHLD SAVEFCB ;and save.
|
|
CALL STFILPOS;clear initial file position (set to 0ffffh).
|
|
CALL HOMEDRV ;home the drive.
|
|
;
|
|
; Entry to locate the next occurence of a filename within the
|
|
; directory. The disk is not expected to have been changed. If
|
|
; it was, then it will be write protected.
|
|
;
|
|
FINDNXT MVI C,0 ;write protect the disk if changed.
|
|
CALL NXENTRY ;get next filename entry in directory.
|
|
CALL CKFILPOS;is file position = 0ffffh?
|
|
JZ FNDNXT6 ;yes, exit now then.
|
|
LHLD SAVEFCB ;set (DE) pointing to filename to match.
|
|
XCHG
|
|
LDAX D
|
|
CPI 0E5H ;empty directory entry?
|
|
JZ FNDNXT1 ;(* are we trying to reserect erased entries? *)
|
|
PUSH D
|
|
CALL MOREFLS ;more files in directory?
|
|
POP D
|
|
JNC FNDNXT6 ;no more. Exit now.
|
|
FNDNXT1 CALL FCB2HL ;get address of this fcb in directory.
|
|
LDA COUNTER ;get number of bytes (characters) to check.
|
|
MOV C,A
|
|
MVI B,0 ;initialize byte position counter.
|
|
FNDNXT2 MOV A,C ;are we done with the compare?
|
|
ORA A
|
|
JZ FNDNXT5
|
|
LDAX D ;no, check next byte.
|
|
CPI '?' ;don't care about this character?
|
|
JZ FNDNXT4
|
|
MOV A,B ;get bytes position in fcb.
|
|
CPI 13 ;don't care about the thirteenth byte either.
|
|
JZ FNDNXT4
|
|
CPI 12 ;extent byte?
|
|
LDAX D
|
|
JZ FNDNXT3
|
|
SUB M ;otherwise compare characters.
|
|
ANI 7FH
|
|
JNZ FINDNXT ;not the same, check next entry.
|
|
JMP FNDNXT4 ;so far so good, keep checking.
|
|
FNDNXT3 PUSH B ;check the extent byte here.
|
|
MOV C,M
|
|
CALL SAMEXT
|
|
POP B
|
|
JNZ FINDNXT ;not the same, look some more.
|
|
;
|
|
; So far the names compare. Bump pointers to the next byte
|
|
; and continue until all (C) characters have been checked.
|
|
;
|
|
FNDNXT4 INX D ;bump pointers.
|
|
INX H
|
|
INR B
|
|
DCR C ;adjust character counter.
|
|
JMP FNDNXT2
|
|
FNDNXT5 LDA FILEPOS ;return the position of this entry.
|
|
ANI 03H
|
|
STA STATUS
|
|
LXI H,FNDSTAT
|
|
MOV A,M
|
|
RAL
|
|
RNC
|
|
XRA A
|
|
MOV M,A
|
|
RET
|
|
;
|
|
; Filename was not found. Set appropriate status.
|
|
;
|
|
FNDNXT6 CALL STFILPOS;set (FILEPOS) to 0ffffh.
|
|
MVI A,0FFH ;say not located.
|
|
JMP SETSTAT
|
|
;
|
|
; Erase files from the directory. Only the first byte of the
|
|
; fcb will be affected. It is set to (E5).
|
|
;
|
|
ERAFILE CALL CHKWPRT ;is disk write protected?
|
|
MVI C,12 ;only compare file names.
|
|
CALL FINDFST ;get first file name.
|
|
ERAFIL1 CALL CKFILPOS;any found?
|
|
RZ ;nope, we must be done.
|
|
CALL CHKROFL ;is file read only?
|
|
CALL FCB2HL ;nope, get address of fcb and
|
|
MVI M,0E5H ;set first byte to 'empty'.
|
|
MVI C,0 ;clear the space from the bit map.
|
|
CALL SETFILE
|
|
CALL DIRWRITE;now write the directory sector back out.
|
|
CALL FINDNXT ;find the next file name.
|
|
JMP ERAFIL1 ;and repeat process.
|
|
;
|
|
; Look through the space allocation map (bit map) for the
|
|
; next available block. Start searching at block number (BC-1).
|
|
; The search procedure is to look for an empty block that is
|
|
; before the starting block. If not empty, look at a later
|
|
; block number. In this way, we return the closest empty block
|
|
; on either side of the 'target' block number. This will speed
|
|
; access on random devices. For serial devices, this should be
|
|
; changed to look in the forward direction first and then start
|
|
; at the front and search some more.
|
|
;
|
|
; On return, (DE)= block number that is empty and (HL) =0
|
|
; if no empry block was found.
|
|
;
|
|
FNDSPACE:MOV D,B ;set (DE) as the block that is checked.
|
|
MOV E,C
|
|
;
|
|
; Look before target block. Registers (BC) are used as the lower
|
|
; pointer and (DE) as the upper pointer.
|
|
;
|
|
FNDSPA1 MOV A,C ;is block 0 specified?
|
|
ORA B
|
|
JZ FNDSPA2
|
|
DCX B ;nope, check previous block.
|
|
PUSH D
|
|
PUSH B
|
|
CALL CKBITMAP
|
|
RAR ;is this block empty?
|
|
JNC FNDSPA3 ;yes. use this.
|
|
;
|
|
; Note that the above logic gets the first block that it finds
|
|
; that is empty. Thus a file could be written 'backward' making
|
|
; it very slow to access. This could be changed to look for the
|
|
; first empty block and then continue until the start of this
|
|
; empty space is located and then used that starting block.
|
|
; This should help speed up access to some files especially on
|
|
; a well used disk with lots of fairly small 'holes'.
|
|
;
|
|
POP B ;nope, check some more.
|
|
POP D
|
|
;
|
|
; Now look after target block.
|
|
;
|
|
FNDSPA2 LHLD DSKSIZE ;is block (DE) within disk limits?
|
|
MOV A,E
|
|
SUB L
|
|
MOV A,D
|
|
SBB H
|
|
JNC FNDSPA4
|
|
INX D ;yes, move on to next one.
|
|
PUSH B
|
|
PUSH D
|
|
MOV B,D
|
|
MOV C,E
|
|
CALL CKBITMAP;check it.
|
|
RAR ;empty?
|
|
JNC FNDSPA3
|
|
POP D ;nope, continue searching.
|
|
POP B
|
|
JMP FNDSPA1
|
|
;
|
|
; Empty block found. Set it as used and return with (HL)
|
|
; pointing to it (true?).
|
|
;
|
|
FNDSPA3 RAL ;reset byte.
|
|
INR A ;and set bit 0.
|
|
CALL STBMAP1 ;update bit map.
|
|
POP H ;set return registers.
|
|
POP D
|
|
RET
|
|
;
|
|
; Free block was not found. If (BC) is not zero, then we have
|
|
; not checked all of the disk space.
|
|
;
|
|
FNDSPA4 MOV A,C
|
|
ORA B
|
|
JNZ FNDSPA1
|
|
LXI H,0 ;set 'not found' status.
|
|
RET
|
|
;
|
|
; Move a complete fcb entry into the directory and write it.
|
|
;
|
|
FCBSET MVI C,0
|
|
MVI E,32 ;length of each entry.
|
|
;
|
|
; Move (E) bytes from the fcb pointed to by (PARAMS) into
|
|
; fcb in directory starting at relative byte (C). This updated
|
|
; directory buffer is then written to the disk.
|
|
;
|
|
UPDATE PUSH D
|
|
MVI B,0 ;set (BC) to relative byte position.
|
|
LHLD PARAMS ;get address of fcb.
|
|
DAD B ;compute starting byte.
|
|
XCHG
|
|
CALL FCB2HL ;get address of fcb to update in directory.
|
|
POP B ;set (C) to number of bytes to change.
|
|
CALL DE2HL
|
|
UPDATE1 CALL TRKSEC ;determine the track and sector affected.
|
|
JMP DIRWRITE ;then write this sector out.
|
|
;
|
|
; Routine to change the name of all files on the disk with a
|
|
; specified name. The fcb contains the current name as the
|
|
; first 12 characters and the new name 16 bytes into the fcb.
|
|
;
|
|
CHGNAMES:CALL CHKWPRT ;check for a write protected disk.
|
|
MVI C,12 ;match first 12 bytes of fcb only.
|
|
CALL FINDFST ;get first name.
|
|
LHLD PARAMS ;get address of fcb.
|
|
MOV A,M ;get user number.
|
|
LXI D,16 ;move over to desired name.
|
|
DAD D
|
|
MOV M,A ;keep same user number.
|
|
CHGNAM1 CALL CKFILPOS;any matching file found?
|
|
RZ ;no, we must be done.
|
|
CALL CHKROFL ;check for read only file.
|
|
MVI C,16 ;start 16 bytes into fcb.
|
|
MVI E,12 ;and update the first 12 bytes of directory.
|
|
CALL UPDATE
|
|
CALL FINDNXT ;get te next file name.
|
|
JMP CHGNAM1 ;and continue.
|
|
;
|
|
; Update a files attributes. The procedure is to search for
|
|
; every file with the same name as shown in fcb (ignoring bit 7)
|
|
; and then to update it (which includes bit 7). No other changes
|
|
; are made.
|
|
;
|
|
SAVEATTR:MVI C,12 ;match first 12 bytes.
|
|
CALL FINDFST ;look for first filename.
|
|
SAVATR1 CALL CKFILPOS;was one found?
|
|
RZ ;nope, we must be done.
|
|
MVI C,0 ;yes, update the first 12 bytes now.
|
|
MVI E,12
|
|
CALL UPDATE ;update filename and write directory.
|
|
CALL FINDNXT ;and get the next file.
|
|
JMP SAVATR1 ;then continue until done.
|
|
;
|
|
; Open a file (name specified in fcb).
|
|
;
|
|
OPENIT MVI C,15 ;compare the first 15 bytes.
|
|
CALL FINDFST ;get the first one in directory.
|
|
CALL CKFILPOS;any at all?
|
|
RZ
|
|
OPENIT1 CALL SETEXT ;point to extent byte within users fcb.
|
|
MOV A,M ;and get it.
|
|
PUSH PSW ;save it and address.
|
|
PUSH H
|
|
CALL FCB2HL ;point to fcb in directory.
|
|
XCHG
|
|
LHLD PARAMS ;this is the users copy.
|
|
MVI C,32 ;move it into users space.
|
|
PUSH D
|
|
CALL DE2HL
|
|
CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified).
|
|
POP D ;now get the extent byte from this fcb.
|
|
LXI H,12
|
|
DAD D
|
|
MOV C,M ;into (C).
|
|
LXI H,15 ;now get the record count byte into (B).
|
|
DAD D
|
|
MOV B,M
|
|
POP H ;keep the same extent as the user had originally.
|
|
POP PSW
|
|
MOV M,A
|
|
MOV A,C ;is it the same as in the directory fcb?
|
|
CMP M
|
|
MOV A,B ;if yes, then use the same record count.
|
|
JZ OPENIT2
|
|
MVI A,0 ;if the user specified an extent greater than
|
|
JC OPENIT2 ;the one in the directory, then set record count to 0.
|
|
MVI A,128 ;otherwise set to maximum.
|
|
OPENIT2 LHLD PARAMS ;set record count in users fcb to (A).
|
|
LXI D,15
|
|
DAD D ;compute relative position.
|
|
MOV M,A ;and set the record count.
|
|
RET
|
|
;
|
|
; Move two bytes from (DE) to (HL) if (and only if) (HL)
|
|
; point to a zero value (16 bit).
|
|
; Return with zero flag set it (DE) was moved. Registers (DE)
|
|
; and (HL) are not changed. However (A) is.
|
|
;
|
|
MOVEWORD:MOV A,M ;check for a zero word.
|
|
INX H
|
|
ORA M ;both bytes zero?
|
|
DCX H
|
|
RNZ ;nope, just return.
|
|
LDAX D ;yes, move two bytes from (DE) into
|
|
MOV M,A ;this zero space.
|
|
INX D
|
|
INX H
|
|
LDAX D
|
|
MOV M,A
|
|
DCX D ;don't disturb these registers.
|
|
DCX H
|
|
RET
|
|
;
|
|
; Get here to close a file specified by (fcb).
|
|
;
|
|
CLOSEIT XRA A ;clear status and file position bytes.
|
|
STA STATUS
|
|
STA FILEPOS
|
|
STA FILEPOS+1
|
|
CALL GETWPRT ;get write protect bit for this drive.
|
|
RNZ ;just return if it is set.
|
|
CALL GETS2 ;else get the 's2' byte.
|
|
ANI 80H ;and look at bit 7 (file unmodified?).
|
|
RNZ ;just return if set.
|
|
MVI C,15 ;else look up this file in directory.
|
|
CALL FINDFST
|
|
CALL CKFILPOS;was it found?
|
|
RZ ;just return if not.
|
|
LXI B,16 ;set (HL) pointing to records used section.
|
|
CALL FCB2HL
|
|
DAD B
|
|
XCHG
|
|
LHLD PARAMS ;do the same for users specified fcb.
|
|
DAD B
|
|
MVI C,16 ;this many bytes are present in this extent.
|
|
CLOSEIT1:LDA BIGDISK ;8 or 16 bit record numbers?
|
|
ORA A
|
|
JZ CLOSEIT4
|
|
MOV A,M ;just 8 bit. Get one from users fcb.
|
|
ORA A
|
|
LDAX D ;now get one from directory fcb.
|
|
JNZ CLOSEIT2
|
|
MOV M,A ;users byte was zero. Update from directory.
|
|
CLOSEIT2:ORA A
|
|
JNZ CLOSEIT3
|
|
MOV A,M ;directories byte was zero, update from users fcb.
|
|
STAX D
|
|
CLOSEIT3:CMP M ;if neither one of these bytes were zero,
|
|
JNZ CLOSEIT7 ;then close error if they are not the same.
|
|
JMP CLOSEIT5 ;ok so far, get to next byte in fcbs.
|
|
CLOSEIT4:CALL MOVEWORD;update users fcb if it is zero.
|
|
XCHG
|
|
CALL MOVEWORD;update directories fcb if it is zero.
|
|
XCHG
|
|
LDAX D ;if these two values are no different,
|
|
CMP M ;then a close error occured.
|
|
JNZ CLOSEIT7
|
|
INX D ;check second byte.
|
|
INX H
|
|
LDAX D
|
|
CMP M
|
|
JNZ CLOSEIT7
|
|
DCR C ;remember 16 bit values.
|
|
CLOSEIT5:INX D ;bump to next item in table.
|
|
INX H
|
|
DCR C ;there are 16 entries only.
|
|
JNZ CLOSEIT1;continue if more to do.
|
|
LXI B,0FFECH;backup 20 places (extent byte).
|
|
DAD B
|
|
XCHG
|
|
DAD B
|
|
LDAX D
|
|
CMP M ;directory's extent already greater than the
|
|
JC CLOSEIT6 ;users extent?
|
|
MOV M,A ;no, update directory extent.
|
|
LXI B,3 ;and update the record count byte in
|
|
DAD B ;directories fcb.
|
|
XCHG
|
|
DAD B
|
|
MOV A,M ;get from user.
|
|
STAX D ;and put in directory.
|
|
CLOSEIT6:MVI A,0FFH ;set 'was open and is now closed' byte.
|
|
STA CLOSEFLG
|
|
JMP UPDATE1 ;update the directory now.
|
|
CLOSEIT7:LXI H,STATUS;set return status and then return.
|
|
DCR M
|
|
RET
|
|
;
|
|
; Routine to get the next empty space in the directory. It
|
|
; will then be cleared for use.
|
|
;
|
|
GETEMPTY:CALL CHKWPRT ;make sure disk is not write protected.
|
|
LHLD PARAMS ;save current parameters (fcb).
|
|
PUSH H
|
|
LXI H,EMPTYFCB;use special one for empty space.
|
|
SHLD PARAMS
|
|
MVI C,1 ;search for first empty spot in directory.
|
|
CALL FINDFST ;(* only check first byte *)
|
|
CALL CKFILPOS;none?
|
|
POP H
|
|
SHLD PARAMS ;restore original fcb address.
|
|
RZ ;return if no more space.
|
|
XCHG
|
|
LXI H,15 ;point to number of records for this file.
|
|
DAD D
|
|
MVI C,17 ;and clear all of this space.
|
|
XRA A
|
|
GETMT1 MOV M,A
|
|
INX H
|
|
DCR C
|
|
JNZ GETMT1
|
|
LXI H,13 ;clear the 's1' byte also.
|
|
DAD D
|
|
MOV M,A
|
|
CALL CHKNMBR ;keep (SCRATCH1) within bounds.
|
|
CALL FCBSET ;write out this fcb entry to directory.
|
|
JMP SETS2B7 ;set 's2' byte bit 7 (unmodified at present).
|
|
;
|
|
; Routine to close the current extent and open the next one
|
|
; for reading.
|
|
;
|
|
GETNEXT XRA A
|
|
STA CLOSEFLG;clear close flag.
|
|
CALL CLOSEIT ;close this extent.
|
|
CALL CKFILPOS
|
|
RZ ;not there???
|
|
LHLD PARAMS ;get extent byte.
|
|
LXI B,12
|
|
DAD B
|
|
MOV A,M ;and increment it.
|
|
INR A
|
|
ANI 1FH ;keep within range 0-31.
|
|
MOV M,A
|
|
JZ GTNEXT1 ;overflow?
|
|
MOV B,A ;mask extent byte.
|
|
LDA EXTMASK
|
|
ANA B
|
|
LXI H,CLOSEFLG;check close flag (0ffh is ok).
|
|
ANA M
|
|
JZ GTNEXT2 ;if zero, we must read in next extent.
|
|
JMP GTNEXT3 ;else, it is already in memory.
|
|
GTNEXT1 LXI B,2 ;Point to the 's2' byte.
|
|
DAD B
|
|
INR M ;and bump it.
|
|
MOV A,M ;too many extents?
|
|
ANI 0FH
|
|
JZ GTNEXT5 ;yes, set error code.
|
|
;
|
|
; Get here to open the next extent.
|
|
;
|
|
GTNEXT2 MVI C,15 ;set to check first 15 bytes of fcb.
|
|
CALL FINDFST ;find the first one.
|
|
CALL CKFILPOS;none available?
|
|
JNZ GTNEXT3
|
|
LDA RDWRTFLG;no extent present. Can we open an empty one?
|
|
INR A ;0ffh means reading (so not possible).
|
|
JZ GTNEXT5 ;or an error.
|
|
CALL GETEMPTY;we are writing, get an empty entry.
|
|
CALL CKFILPOS;none?
|
|
JZ GTNEXT5 ;error if true.
|
|
JMP GTNEXT4 ;else we are almost done.
|
|
GTNEXT3 CALL OPENIT1 ;open this extent.
|
|
GTNEXT4 CALL STRDATA ;move in updated data (rec #, extent #, etc.)
|
|
XRA A ;clear status and return.
|
|
JMP SETSTAT
|
|
;
|
|
; Error in extending the file. Too many extents were needed
|
|
; or not enough space on the disk.
|
|
;
|
|
GTNEXT5 CALL IOERR1 ;set error code, clear bit 7 of 's2'
|
|
JMP SETS2B7 ;so this is not written on a close.
|
|
;
|
|
; Read a sequential file.
|
|
;
|
|
RDSEQ MVI A,1 ;set sequential access mode.
|
|
STA MODE
|
|
RDSEQ1 MVI A,0FFH ;don't allow reading unwritten space.
|
|
STA RDWRTFLG
|
|
CALL STRDATA ;put rec# and ext# into fcb.
|
|
LDA SAVNREC ;get next record to read.
|
|
LXI H,SAVNXT;get number of records in extent.
|
|
CMP M ;within this extent?
|
|
JC RDSEQ2
|
|
CPI 128 ;no. Is this extent fully used?
|
|
JNZ RDSEQ3 ;no. End-of-file.
|
|
CALL GETNEXT ;yes, open the next one.
|
|
XRA A ;reset next record to read.
|
|
STA SAVNREC
|
|
LDA STATUS ;check on open, successful?
|
|
ORA A
|
|
JNZ RDSEQ3 ;no, error.
|
|
RDSEQ2 CALL COMBLK ;ok. compute block number to read.
|
|
CALL CHKBLK ;check it. Within bounds?
|
|
JZ RDSEQ3 ;no, error.
|
|
CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte).
|
|
CALL TRKSEC1 ;set the track and sector for this block #.
|
|
CALL DOREAD ;and read it.
|
|
JMP SETNREC ;and set the next record to be accessed.
|
|
;
|
|
; Read error occured. Set status and return.
|
|
;
|
|
RDSEQ3 JMP IOERR1
|
|
;
|
|
; Write the next sequential record.
|
|
;
|
|
WTSEQ MVI A,1 ;set sequential access mode.
|
|
STA MODE
|
|
WTSEQ1 MVI A,0 ;allow an addition empty extent to be opened.
|
|
STA RDWRTFLG
|
|
CALL CHKWPRT ;check write protect status.
|
|
LHLD PARAMS
|
|
CALL CKROF1 ;check for read only file, (HL) already set to fcb.
|
|
CALL STRDATA ;put updated data into fcb.
|
|
LDA SAVNREC ;get record number to write.
|
|
CPI 128 ;within range?
|
|
JNC IOERR1 ;no, error(?).
|
|
CALL COMBLK ;compute block number.
|
|
CALL CHKBLK ;check number.
|
|
MVI C,0 ;is there one to write to?
|
|
JNZ WTSEQ6 ;yes, go do it.
|
|
CALL GETBLOCK;get next block number within fcb to use.
|
|
STA RELBLOCK;and save.
|
|
LXI B,0 ;start looking for space from the start
|
|
ORA A ;if none allocated as yet.
|
|
JZ WTSEQ2
|
|
MOV C,A ;extract previous block number from fcb
|
|
DCX B ;so we can be closest to it.
|
|
CALL EXTBLK
|
|
MOV B,H
|
|
MOV C,L
|
|
WTSEQ2 CALL FNDSPACE;find the next empty block nearest number (BC).
|
|
MOV A,L ;check for a zero number.
|
|
ORA H
|
|
JNZ WTSEQ3
|
|
MVI A,2 ;no more space?
|
|
JMP SETSTAT
|
|
WTSEQ3 SHLD BLKNMBR ;save block number to access.
|
|
XCHG ;put block number into (DE).
|
|
LHLD PARAMS ;now we must update the fcb for this
|
|
LXI B,16 ;newly allocated block.
|
|
DAD B
|
|
LDA BIGDISK ;8 or 16 bit block numbers?
|
|
ORA A
|
|
LDA RELBLOCK ;(* update this entry *)
|
|
JZ WTSEQ4 ;zero means 16 bit ones.
|
|
CALL ADDA2HL ;(HL)=(HL)+(A)
|
|
MOV M,E ;store new block number.
|
|
JMP WTSEQ5
|
|
WTSEQ4 MOV C,A ;compute spot in this 16 bit table.
|
|
MVI B,0
|
|
DAD B
|
|
DAD B
|
|
MOV M,E ;stuff block number (DE) there.
|
|
INX H
|
|
MOV M,D
|
|
WTSEQ5 MVI C,2 ;set (C) to indicate writing to un-used disk space.
|
|
WTSEQ6 LDA STATUS ;are we ok so far?
|
|
ORA A
|
|
RNZ
|
|
PUSH B ;yes, save write flag for bios (register C).
|
|
CALL LOGICAL ;convert (BLKNMBR) over to loical sectors.
|
|
LDA MODE ;get access mode flag (1=sequential,
|
|
DCR A ;0=random, 2=special?).
|
|
DCR A
|
|
JNZ WTSEQ9
|
|
;
|
|
; Special random i/o from function #40. Maybe for M/PM, but the
|
|
; current block, if it has not been written to, will be zeroed
|
|
; out and then written (reason?).
|
|
;
|
|
POP B
|
|
PUSH B
|
|
MOV A,C ;get write status flag (2=writing unused space).
|
|
DCR A
|
|
DCR A
|
|
JNZ WTSEQ9
|
|
PUSH H
|
|
LHLD DIRBUF ;zero out the directory buffer.
|
|
MOV D,A ;note that (A) is zero here.
|
|
WTSEQ7 MOV M,A
|
|
INX H
|
|
INR D ;do 128 bytes.
|
|
JP WTSEQ7
|
|
CALL DIRDMA ;tell the bios the dma address for directory access.
|
|
LHLD LOGSECT ;get sector that starts current block.
|
|
MVI C,2 ;set 'writing to unused space' flag.
|
|
WTSEQ8 SHLD BLKNMBR ;save sector to write.
|
|
PUSH B
|
|
CALL TRKSEC1 ;determine its track and sector numbers.
|
|
POP B
|
|
CALL DOWRITE ;now write out 128 bytes of zeros.
|
|
LHLD BLKNMBR ;get sector number.
|
|
MVI C,0 ;set normal write flag.
|
|
LDA BLKMASK ;determine if we have written the entire
|
|
MOV B,A ;physical block.
|
|
ANA L
|
|
CMP B
|
|
INX H ;prepare for the next one.
|
|
JNZ WTSEQ8 ;continue until (BLKMASK+1) sectors written.
|
|
POP H ;reset next sector number.
|
|
SHLD BLKNMBR
|
|
CALL DEFDMA ;and reset dma address.
|
|
;
|
|
; Normal disk write. Set the desired track and sector then
|
|
; do the actual write.
|
|
;
|
|
WTSEQ9 CALL TRKSEC1 ;determine track and sector for this write.
|
|
POP B ;get write status flag.
|
|
PUSH B
|
|
CALL DOWRITE ;and write this out.
|
|
POP B
|
|
LDA SAVNREC ;get number of records in file.
|
|
LXI H,SAVNXT;get last record written.
|
|
CMP M
|
|
JC WTSEQ10
|
|
MOV M,A ;we have to update record count.
|
|
INR M
|
|
MVI C,2
|
|
;
|
|
;* This area has been patched to correct disk update problem
|
|
;* when using blocking and de-blocking in the BIOS.
|
|
;
|
|
WTSEQ10 NOP ;was 'dcr c'
|
|
NOP ;was 'dcr c'
|
|
LXI H,0 ;was 'jnz wtseq99'
|
|
;
|
|
; * End of patch.
|
|
;
|
|
PUSH PSW
|
|
CALL GETS2 ;set 'extent written to' flag.
|
|
ANI 7FH ;(* clear bit 7 *)
|
|
MOV M,A
|
|
POP PSW ;get record count for this extent.
|
|
WTSEQ99 CPI 127 ;is it full?
|
|
JNZ WTSEQ12
|
|
LDA MODE ;yes, are we in sequential mode?
|
|
CPI 1
|
|
JNZ WTSEQ12
|
|
CALL SETNREC ;yes, set next record number.
|
|
CALL GETNEXT ;and get next empty space in directory.
|
|
LXI H,STATUS;ok?
|
|
MOV A,M
|
|
ORA A
|
|
JNZ WTSEQ11
|
|
DCR A ;yes, set record count to -1.
|
|
STA SAVNREC
|
|
WTSEQ11 MVI M,0 ;clear status.
|
|
WTSEQ12 JMP SETNREC ;set next record to access.
|
|
;
|
|
; For random i/o, set the fcb for the desired record number
|
|
; based on the 'r0,r1,r2' bytes. These bytes in the fcb are
|
|
; used as follows:
|
|
;
|
|
; fcb+35 fcb+34 fcb+33
|
|
; | 'r-2' | 'r-1' | 'r-0' |
|
|
; |7 0 | 7 0 | 7 0|
|
|
; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0|
|
|
; | overflow | | extra | extent | record # |
|
|
; | ______________| |_extent|__number___|_____________|
|
|
; also 's2'
|
|
;
|
|
; On entry, register (C) contains 0ffh if this is a read
|
|
; and thus we can not access unwritten disk space. Otherwise,
|
|
; another extent will be opened (for writing) if required.
|
|
;
|
|
POSITION:XRA A ;set random i/o flag.
|
|
STA MODE
|
|
;
|
|
; Special entry (function #40). M/PM ?
|
|
;
|
|
POSITN1 PUSH B ;save read/write flag.
|
|
LHLD PARAMS ;get address of fcb.
|
|
XCHG
|
|
LXI H,33 ;now get byte 'r0'.
|
|
DAD D
|
|
MOV A,M
|
|
ANI 7FH ;keep bits 0-6 for the record number to access.
|
|
PUSH PSW
|
|
MOV A,M ;now get bit 7 of 'r0' and bits 0-3 of 'r1'.
|
|
RAL
|
|
INX H
|
|
MOV A,M
|
|
RAL
|
|
ANI 1FH ;and save this in bits 0-4 of (C).
|
|
MOV C,A ;this is the extent byte.
|
|
MOV A,M ;now get the extra extent byte.
|
|
RAR
|
|
RAR
|
|
RAR
|
|
RAR
|
|
ANI 0FH
|
|
MOV B,A ;and save it in (B).
|
|
POP PSW ;get record number back to (A).
|
|
INX H ;check overflow byte 'r2'.
|
|
MOV L,M
|
|
INR L
|
|
DCR L
|
|
MVI L,6 ;prepare for error.
|
|
JNZ POSITN5 ;out of disk space error.
|
|
LXI H,32 ;store record number into fcb.
|
|
DAD D
|
|
MOV M,A
|
|
LXI H,12 ;and now check the extent byte.
|
|
DAD D
|
|
MOV A,C
|
|
SUB M ;same extent as before?
|
|
JNZ POSITN2
|
|
LXI H,14 ;yes, check extra extent byte 's2' also.
|
|
DAD D
|
|
MOV A,B
|
|
SUB M
|
|
ANI 7FH
|
|
JZ POSITN3;same, we are almost done then.
|
|
;
|
|
; Get here when another extent is required.
|
|
;
|
|
POSITN2 PUSH B
|
|
PUSH D
|
|
CALL CLOSEIT ;close current extent.
|
|
POP D
|
|
POP B
|
|
MVI L,3 ;prepare for error.
|
|
LDA STATUS
|
|
INR A
|
|
JZ POSITN4 ;close error.
|
|
LXI H,12 ;put desired extent into fcb now.
|
|
DAD D
|
|
MOV M,C
|
|
LXI H,14 ;and store extra extent byte 's2'.
|
|
DAD D
|
|
MOV M,B
|
|
CALL OPENIT ;try and get this extent.
|
|
LDA STATUS ;was it there?
|
|
INR A
|
|
JNZ POSITN3
|
|
POP B ;no. can we create a new one (writing?).
|
|
PUSH B
|
|
MVI L,4 ;prepare for error.
|
|
INR C
|
|
JZ POSITN4 ;nope, reading unwritten space error.
|
|
CALL GETEMPTY;yes we can, try to find space.
|
|
MVI L,5 ;prepare for error.
|
|
LDA STATUS
|
|
INR A
|
|
JZ POSITN4 ;out of space?
|
|
;
|
|
; Normal return location. Clear error code and return.
|
|
;
|
|
POSITN3 POP B ;restore stack.
|
|
XRA A ;and clear error code byte.
|
|
JMP SETSTAT
|
|
;
|
|
; Error. Set the 's2' byte to indicate this (why?).
|
|
;
|
|
POSITN4 PUSH H
|
|
CALL GETS2
|
|
MVI M,0C0H
|
|
POP H
|
|
;
|
|
; Return with error code (presently in L).
|
|
;
|
|
POSITN5 POP B
|
|
MOV A,L ;get error code.
|
|
STA STATUS
|
|
JMP SETS2B7
|
|
;
|
|
; Read a random record.
|
|
;
|
|
READRAN MVI C,0FFH ;set 'read' status.
|
|
CALL POSITION;position the file to proper record.
|
|
CZ RDSEQ1 ;and read it as usual (if no errors).
|
|
RET
|
|
;
|
|
; Write to a random record.
|
|
;
|
|
WRITERAN:MVI C,0 ;set 'writing' flag.
|
|
CALL POSITION;position the file to proper record.
|
|
CZ WTSEQ1 ;and write as usual (if no errors).
|
|
RET
|
|
;
|
|
; Compute the random record number. Enter with (HL) pointing
|
|
; to a fcb an (DE) contains a relative location of a record
|
|
; number. On exit, (C) contains the 'r0' byte, (B) the 'r1'
|
|
; byte, and (A) the 'r2' byte.
|
|
;
|
|
; On return, the zero flag is set if the record is within
|
|
; bounds. Otherwise, an overflow occured.
|
|
;
|
|
COMPRAND:XCHG ;save fcb pointer in (DE).
|
|
DAD D ;compute relative position of record #.
|
|
MOV C,M ;get record number into (BC).
|
|
MVI B,0
|
|
LXI H,12 ;now get extent.
|
|
DAD D
|
|
MOV A,M ;compute (BC)=(record #)+(extent)*128.
|
|
RRC ;move lower bit into bit 7.
|
|
ANI 80H ;and ignore all other bits.
|
|
ADD C ;add to our record number.
|
|
MOV C,A
|
|
MVI A,0 ;take care of any carry.
|
|
ADC B
|
|
MOV B,A
|
|
MOV A,M ;now get the upper bits of extent into
|
|
RRC ;bit positions 0-3.
|
|
ANI 0FH ;and ignore all others.
|
|
ADD B ;add this in to 'r1' byte.
|
|
MOV B,A
|
|
LXI H,14 ;get the 's2' byte (extra extent).
|
|
DAD D
|
|
MOV A,M
|
|
ADD A ;and shift it left 4 bits (bits 4-7).
|
|
ADD A
|
|
ADD A
|
|
ADD A
|
|
PUSH PSW ;save carry flag (bit 0 of flag byte).
|
|
ADD B ;now add extra extent into 'r1'.
|
|
MOV B,A
|
|
PUSH PSW ;and save carry (overflow byte 'r2').
|
|
POP H ;bit 0 of (L) is the overflow indicator.
|
|
MOV A,L
|
|
POP H ;and same for first carry flag.
|
|
ORA L ;either one of these set?
|
|
ANI 01H ;only check the carry flags.
|
|
RET
|
|
;
|
|
; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to
|
|
; reflect the last record used for a random (or other) file.
|
|
; This reads the directory and looks at all extents computing
|
|
; the largerst record number for each and keeping the maximum
|
|
; value only. Then 'r0', 'r1', and 'r2' will reflect this
|
|
; maximum record number. This is used to compute the space used
|
|
; by a random file.
|
|
;
|
|
RANSIZE MVI C,12 ;look thru directory for first entry with
|
|
CALL FINDFST ;this name.
|
|
LHLD PARAMS ;zero out the 'r0, r1, r2' bytes.
|
|
LXI D,33
|
|
DAD D
|
|
PUSH H
|
|
MOV M,D ;note that (D)=0.
|
|
INX H
|
|
MOV M,D
|
|
INX H
|
|
MOV M,D
|
|
RANSIZ1 CALL CKFILPOS;is there an extent to process?
|
|
JZ RANSIZ3 ;no, we are done.
|
|
CALL FCB2HL ;set (HL) pointing to proper fcb in dir.
|
|
LXI D,15 ;point to last record in extent.
|
|
CALL COMPRAND;and compute random parameters.
|
|
POP H
|
|
PUSH H ;now check these values against those
|
|
MOV E,A ;already in fcb.
|
|
MOV A,C ;the carry flag will be set if those
|
|
SUB M ;in the fcb represent a larger size than
|
|
INX H ;this extent does.
|
|
MOV A,B
|
|
SBB M
|
|
INX H
|
|
MOV A,E
|
|
SBB M
|
|
JC RANSIZ2
|
|
MOV M,E ;we found a larger (in size) extent.
|
|
DCX H ;stuff these values into fcb.
|
|
MOV M,B
|
|
DCX H
|
|
MOV M,C
|
|
RANSIZ2 CALL FINDNXT ;now get the next extent.
|
|
JMP RANSIZ1 ;continue til all done.
|
|
RANSIZ3 POP H ;we are done, restore the stack and
|
|
RET ;return.
|
|
;
|
|
; Function to return the random record position of a given
|
|
; file which has been read in sequential mode up to now.
|
|
;
|
|
SETRAN LHLD PARAMS ;point to fcb.
|
|
LXI D,32 ;and to last used record.
|
|
CALL COMPRAND;compute random position.
|
|
LXI H,33 ;now stuff these values into fcb.
|
|
DAD D
|
|
MOV M,C ;move 'r0'.
|
|
INX H
|
|
MOV M,B ;and 'r1'.
|
|
INX H
|
|
MOV M,A ;and lastly 'r2'.
|
|
RET
|
|
;
|
|
; This routine select the drive specified in (ACTIVE) and
|
|
; update the login vector and bitmap table if this drive was
|
|
; not already active.
|
|
;
|
|
LOGINDRV:LHLD LOGIN ;get the login vector.
|
|
LDA ACTIVE ;get the default drive.
|
|
MOV C,A
|
|
CALL SHIFTR ;position active bit for this drive
|
|
PUSH H ;into bit 0.
|
|
XCHG
|
|
CALL SELECT ;select this drive.
|
|
POP H
|
|
CZ SLCTERR ;valid drive?
|
|
MOV A,L ;is this a newly activated drive?
|
|
RAR
|
|
RC
|
|
LHLD LOGIN ;yes, update the login vector.
|
|
MOV C,L
|
|
MOV B,H
|
|
CALL SETBIT
|
|
SHLD LOGIN ;and save.
|
|
JMP BITMAP ;now update the bitmap.
|
|
;
|
|
; Function to set the active disk number.
|
|
;
|
|
SETDSK LDA EPARAM ;get parameter passed and see if this
|
|
LXI H,ACTIVE;represents a change in drives.
|
|
CMP M
|
|
RZ
|
|
MOV M,A ;yes it does, log it in.
|
|
JMP LOGINDRV
|
|
;
|
|
; This is the 'auto disk select' routine. The firsst byte
|
|
; of the fcb is examined for a drive specification. If non
|
|
; zero then the drive will be selected and loged in.
|
|
;
|
|
AUTOSEL MVI A,0FFH ;say 'auto-select activated'.
|
|
STA AUTO
|
|
LHLD PARAMS ;get drive specified.
|
|
MOV A,M
|
|
ANI 1FH ;look at lower 5 bits.
|
|
DCR A ;adjust for (1=A, 2=B) etc.
|
|
STA EPARAM ;and save for the select routine.
|
|
CPI 1EH ;check for 'no change' condition.
|
|
JNC AUTOSL1 ;yes, don't change.
|
|
LDA ACTIVE ;we must change, save currently active
|
|
STA OLDDRV ;drive.
|
|
MOV A,M ;and save first byte of fcb also.
|
|
STA AUTOFLAG;this must be non-zero.
|
|
ANI 0E0H ;whats this for (bits 6,7 are used for
|
|
MOV M,A ;something)?
|
|
CALL SETDSK ;select and log in this drive.
|
|
AUTOSL1 LDA USERNO ;move user number into fcb.
|
|
LHLD PARAMS ;(* upper half of first byte *)
|
|
ORA M
|
|
MOV M,A
|
|
RET ;and return (all done).
|
|
;
|
|
; Function to return the current cp/m version number.
|
|
;
|
|
GETVER MVI A,022h ;version 2.2
|
|
JMP SETSTAT
|
|
;
|
|
; Function to reset the disk system.
|
|
;
|
|
RSTDSK LXI H,0 ;clear write protect status and log
|
|
SHLD WRTPRT ;in vector.
|
|
SHLD LOGIN
|
|
XRA A ;select drive 'A'.
|
|
STA ACTIVE
|
|
LXI H,TBUFF ;setup default dma address.
|
|
SHLD USERDMA
|
|
CALL DEFDMA
|
|
JMP LOGINDRV;now log in drive 'A'.
|
|
;
|
|
; Function to open a specified file.
|
|
;
|
|
OPENFIL CALL CLEARS2 ;clear 's2' byte.
|
|
CALL AUTOSEL ;select proper disk.
|
|
JMP OPENIT ;and open the file.
|
|
;
|
|
; Function to close a specified file.
|
|
;
|
|
CLOSEFIL:CALL AUTOSEL ;select proper disk.
|
|
JMP CLOSEIT ;and close the file.
|
|
;
|
|
; Function to return the first occurence of a specified file
|
|
; name. If the first byte of the fcb is '?' then the name will
|
|
; not be checked (get the first entry no matter what).
|
|
;
|
|
GETFST MVI C,0 ;prepare for special search.
|
|
XCHG
|
|
MOV A,M ;is first byte a '?'?
|
|
CPI '?'
|
|
JZ GETFST1 ;yes, just get very first entry (zero length match).
|
|
CALL SETEXT ;get the extension byte from fcb.
|
|
MOV A,M ;is it '?'? if yes, then we want
|
|
CPI '?' ;an entry with a specific 's2' byte.
|
|
CNZ CLEARS2 ;otherwise, look for a zero 's2' byte.
|
|
CALL AUTOSEL ;select proper drive.
|
|
MVI C,15 ;compare bytes 0-14 in fcb (12&13 excluded).
|
|
GETFST1 CALL FINDFST ;find an entry and then move it into
|
|
JMP MOVEDIR ;the users dma space.
|
|
;
|
|
; Function to return the next occurence of a file name.
|
|
;
|
|
GETNXT LHLD SAVEFCB ;restore pointers. note that no
|
|
SHLD PARAMS ;other dbos calls are allowed.
|
|
CALL AUTOSEL ;no error will be returned, but the
|
|
CALL FINDNXT ;results will be wrong.
|
|
JMP MOVEDIR
|
|
;
|
|
; Function to delete a file by name.
|
|
;
|
|
DELFILE CALL AUTOSEL ;select proper drive.
|
|
CALL ERAFILE ;erase the file.
|
|
JMP STSTATUS;set status and return.
|
|
;
|
|
; Function to execute a sequential read of the specified
|
|
; record number.
|
|
;
|
|
READSEQ CALL AUTOSEL ;select proper drive then read.
|
|
JMP RDSEQ
|
|
;
|
|
; Function to write the net sequential record.
|
|
;
|
|
WRTSEQ CALL AUTOSEL ;select proper drive then write.
|
|
JMP WTSEQ
|
|
;
|
|
; Create a file function.
|
|
;
|
|
FCREATE CALL CLEARS2 ;clear the 's2' byte on all creates.
|
|
CALL AUTOSEL ;select proper drive and get the next
|
|
JMP GETEMPTY;empty directory space.
|
|
;
|
|
; Function to rename a file.
|
|
;
|
|
RENFILE CALL AUTOSEL ;select proper drive and then switch
|
|
CALL CHGNAMES;file names.
|
|
JMP STSTATUS
|
|
;
|
|
; Function to return the login vector.
|
|
;
|
|
GETLOG LHLD LOGIN
|
|
JMP GETPRM1
|
|
;
|
|
; Function to return the current disk assignment.
|
|
;
|
|
GETCRNT LDA ACTIVE
|
|
JMP SETSTAT
|
|
;
|
|
; Function to set the dma address.
|
|
;
|
|
PUTDMA XCHG
|
|
SHLD USERDMA ;save in our space and then get to
|
|
JMP DEFDMA ;the bios with this also.
|
|
;
|
|
; Function to return the allocation vector.
|
|
;
|
|
GETALOC LHLD ALOCVECT
|
|
JMP GETPRM1
|
|
;
|
|
; Function to return the read-only status vector.
|
|
;
|
|
GETROV LHLD WRTPRT
|
|
JMP GETPRM1
|
|
;
|
|
; Function to set the file attributes (read-only, system).
|
|
;
|
|
SETATTR CALL AUTOSEL ;select proper drive then save attributes.
|
|
CALL SAVEATTR
|
|
JMP STSTATUS
|
|
;
|
|
; Function to return the address of the disk parameter block
|
|
; for the current drive.
|
|
;
|
|
GETPARM LHLD DISKPB
|
|
GETPRM1 SHLD STATUS
|
|
RET
|
|
;
|
|
; Function to get or set the user number. If (E) was (FF)
|
|
; then this is a request to return the current user number.
|
|
; Else set the user number from (E).
|
|
;
|
|
GETUSER LDA EPARAM ;get parameter.
|
|
CPI 0FFH ;get user number?
|
|
JNZ SETUSER
|
|
LDA USERNO ;yes, just do it.
|
|
JMP SETSTAT
|
|
SETUSER ANI 1FH ;no, we should set it instead. keep low
|
|
STA USERNO ;bits (0-4) only.
|
|
RET
|
|
;
|
|
; Function to read a random record from a file.
|
|
;
|
|
RDRANDOM:CALL AUTOSEL ;select proper drive and read.
|
|
JMP READRAN
|
|
;
|
|
; Function to compute the file size for random files.
|
|
;
|
|
WTRANDOM:CALL AUTOSEL ;select proper drive and write.
|
|
JMP WRITERAN
|
|
;
|
|
; Function to compute the size of a random file.
|
|
;
|
|
FILESIZE:CALL AUTOSEL ;select proper drive and check file length
|
|
JMP RANSIZE
|
|
;
|
|
; Function #37. This allows a program to log off any drives.
|
|
; On entry, set (DE) to contain a word with bits set for those
|
|
; drives that are to be logged off. The log-in vector and the
|
|
; write protect vector will be updated. This must be a M/PM
|
|
; special function.
|
|
;
|
|
LOGOFF LHLD PARAMS ;get drives to log off.
|
|
MOV A,L ;for each bit that is set, we want
|
|
CMA ;to clear that bit in (LOGIN)
|
|
MOV E,A ;and (WRTPRT).
|
|
MOV A,H
|
|
CMA
|
|
LHLD LOGIN ;reset the login vector.
|
|
ANA H
|
|
MOV D,A
|
|
MOV A,L
|
|
ANA E
|
|
MOV E,A
|
|
LHLD WRTPRT
|
|
XCHG
|
|
SHLD LOGIN ;and save.
|
|
MOV A,L ;now do the write protect vector.
|
|
ANA E
|
|
MOV L,A
|
|
MOV A,H
|
|
ANA D
|
|
MOV H,A
|
|
SHLD WRTPRT ;and save. all done.
|
|
RET
|
|
;
|
|
; Get here to return to the user.
|
|
;
|
|
GOBACK LDA AUTO ;was auto select activated?
|
|
ORA A
|
|
JZ GOBACK1
|
|
LHLD PARAMS ;yes, but was a change made?
|
|
MVI M,0 ;(* reset first byte of fcb *)
|
|
LDA AUTOFLAG
|
|
ORA A
|
|
JZ GOBACK1
|
|
MOV M,A ;yes, reset first byte properly.
|
|
LDA OLDDRV ;and get the old drive and select it.
|
|
STA EPARAM
|
|
CALL SETDSK
|
|
GOBACK1 LHLD USRSTACK;reset the users stack pointer.
|
|
SPHL
|
|
LHLD STATUS ;get return status.
|
|
MOV A,L ;force version 1.4 compatability.
|
|
MOV B,H
|
|
RET ;and go back to user.
|
|
;
|
|
; Function #40. This is a special entry to do random i/o.
|
|
; For the case where we are writing to unused disk space, this
|
|
; space will be zeroed out first. This must be a M/PM special
|
|
; purpose function, because why would any normal program even
|
|
; care about the previous contents of a sector about to be
|
|
; written over.
|
|
;
|
|
WTSPECL CALL AUTOSEL ;select proper drive.
|
|
MVI A,2 ;use special write mode.
|
|
STA MODE
|
|
MVI C,0 ;set write indicator.
|
|
CALL POSITN1 ;position the file.
|
|
CZ WTSEQ1 ;and write (if no errors).
|
|
RET
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* BDOS data storage pool.
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
EMPTYFCB:DB 0E5H ;empty directory segment indicator.
|
|
WRTPRT DW 0 ;write protect status for all 16 drives.
|
|
LOGIN DW 0 ;drive active word (1 bit per drive).
|
|
USERDMA DW 080H ;user's dma address (defaults to 80h).
|
|
;
|
|
; Scratch areas from parameter block.
|
|
;
|
|
SCRATCH1:DW 0 ;relative position within dir segment for file (0-3).
|
|
SCRATCH2:DW 0 ;last selected track number.
|
|
SCRATCH3:DW 0 ;last selected sector number.
|
|
;
|
|
; Disk storage areas from parameter block.
|
|
;
|
|
DIRBUF DW 0 ;address of directory buffer to use.
|
|
DISKPB DW 0 ;contains address of disk parameter block.
|
|
CHKVECT DW 0 ;address of check vector.
|
|
ALOCVECT:DW 0 ;address of allocation vector (bit map).
|
|
;
|
|
; Parameter block returned from the bios.
|
|
;
|
|
SECTORS DW 0 ;sectors per track from bios.
|
|
BLKSHFT DB 0 ;block shift.
|
|
BLKMASK DB 0 ;block mask.
|
|
EXTMASK DB 0 ;extent mask.
|
|
DSKSIZE DW 0 ;disk size from bios (number of blocks-1).
|
|
DIRSIZE DW 0 ;directory size.
|
|
ALLOC0 DW 0 ;storage for first bytes of bit map (dir space used).
|
|
ALLOC1 DW 0
|
|
OFFSET DW 0 ;first usable track number.
|
|
XLATE DW 0 ;sector translation table address.
|
|
;
|
|
;
|
|
CLOSEFLG:DB 0 ;close flag (=0ffh is extent written ok).
|
|
RDWRTFLG:DB 0 ;read/write flag (0ffh=read, 0=write).
|
|
FNDSTAT DB 0 ;filename found status (0=found first entry).
|
|
MODE DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random).
|
|
EPARAM DB 0 ;storage for register (E) on entry to bdos.
|
|
RELBLOCK:DB 0 ;relative position within fcb of block number written.
|
|
COUNTER DB 0 ;byte counter for directory name searches.
|
|
SAVEFCB DW 0,0 ;save space for address of fcb (for directory searches).
|
|
BIGDISK DB 0 ;if =0 then disk is > 256 blocks long.
|
|
AUTO DB 0 ;if non-zero, then auto select activated.
|
|
OLDDRV DB 0 ;on auto select, storage for previous drive.
|
|
AUTOFLAG:DB 0 ;if non-zero, then auto select changed drives.
|
|
SAVNXT DB 0 ;storage for next record number to access.
|
|
SAVEXT DB 0 ;storage for extent number of file.
|
|
SAVNREC DW 0 ;storage for number of records in file.
|
|
BLKNMBR DW 0 ;block number (physical sector) used within a file or logical sector.
|
|
LOGSECT DW 0 ;starting logical (128 byte) sector of block (physical sector).
|
|
FCBPOS DB 0 ;relative position within buffer for fcb of file of interest.
|
|
FILEPOS DW 0 ;files position within directory (0 to max entries -1).
|
|
;
|
|
; Disk directory buffer checksum bytes. One for each of the
|
|
; 16 possible drives.
|
|
;
|
|
CKSUMTBL:DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; Extra space ?
|
|
;
|
|
DB 0,0,0,0
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* B I O S J U M P T A B L E
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
BOOT JMP 0 ;NOTE WE USE FAKE DESTINATIONS
|
|
WBOOT JMP 0
|
|
CONST JMP 0
|
|
CONIN JMP 0
|
|
CONOUT JMP 0
|
|
LIST JMP 0
|
|
PUNCH JMP 0
|
|
READER JMP 0
|
|
HOME JMP 0
|
|
SELDSK JMP 0
|
|
SETTRK JMP 0
|
|
SETSEC JMP 0
|
|
SETDMA JMP 0
|
|
READ JMP 0
|
|
WRITE JMP 0
|
|
PRSTAT JMP 0
|
|
SECTRN JMP 0
|
|
;
|
|
;*
|
|
;****************** E N D O F C P / M *****************
|
|
;*
|
|
|