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