3765 lines
109 KiB
NASM
3765 lines
109 KiB
NASM
;**************************************************************
|
|
;*
|
|
;* C P / M version 2 . 2
|
|
;*
|
|
;* Reconstructed from memory image on February 27, 1981
|
|
;*
|
|
;* by Clark A. Calkins
|
|
;*
|
|
;**************************************************************
|
|
|
|
; Bring in definitions and macros.
|
|
INCLUDE "cpm_buildversion.asm"
|
|
INCLUDE "cpm_definitions.asm"
|
|
INCLUDE "macros.asm"
|
|
;
|
|
; Set memory limit here. This is the amount of contigeous
|
|
; ram starting from 0000. CP/M will reside at the end of this space.
|
|
;
|
|
|
|
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
|
|
|
|
; CP/M addresses
|
|
;CBASE EQU 09C00H
|
|
;CCP EQU CBASE ; CP/M System entry
|
|
;BDOS EQU CCP + 0806H ; BDOS entry
|
|
;BIOS EQU CCP + 01600H ; BIOS entry
|
|
CCPLEN EQU CBASE + 7 ; Address of current number of chars into the CCP input buffer
|
|
CCPFIRS EQU CBASE + 8 ; Address of the first charater of the CCP input buffer
|
|
|
|
|
|
;
|
|
; Set origin for CP/M
|
|
;
|
|
ORG CBASE ; Origin for the Sharp MZ-80A version
|
|
;
|
|
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: DB 127 ;length of input buffer.
|
|
DB 0 ;current length of contents.
|
|
IF BUILD_80C = 1
|
|
DB "CP/M v2.23 (64K) Copyright(c) 1979 by Digital Research", 00DH, 00AH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 68 chars
|
|
ELSE
|
|
DB "CP/M v2.23 (c) 1979 by Digital Research", 00DH, 00AH, 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, 0, 0 ; 68 chars
|
|
ENDIF
|
|
;DB "Copyright"
|
|
;DB " 1979 (c) by Digital Research "
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
INPOINT:DW INBUFF+2 ;input line pointer
|
|
NAMEPNT:DW 0 ;input line pointer used for error message. Points to
|
|
; ;start of name in error.
|
|
;
|
|
; Routine to print (A) on the console. All registers used.
|
|
;
|
|
PRINT: 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).
|
|
;
|
|
PRTSPACE: 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).
|
|
;
|
|
DELETEFILE: 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 DELETEFILE
|
|
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,HALTCPM ;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 03BH ; ';'
|
|
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: DB "DIR "
|
|
DB "ERA "
|
|
DB "TYPE"
|
|
DB "SAVE"
|
|
DB "REN "
|
|
DB "USER"
|
|
;
|
|
; The following six bytes must agree with those at (PATTRN2)
|
|
; or cp/m will HALT. Why?
|
|
;
|
|
PATTRN1:DB 0,22,0,0,0,0 ;(* serial number bytes *).
|
|
;
|
|
; Search the command table for a match with what has just
|
|
; been entered. If a match is found, then we jump to the
|
|
; proper section. Else jump to (UNKNOWN).
|
|
; On return, the (C) register is set to the command number
|
|
; that matched (or NUMCMDS+1 if no match).
|
|
;
|
|
SEARCH: 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: DW DIRECT,ERASE,TYPE,SAVE
|
|
DW RENAME,USER,UNKNOWN
|
|
;
|
|
; Halt the system. Reason for this is unknown at present.
|
|
;
|
|
HALTCPM: 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: DB "Read error"
|
|
DB 0
|
|
;
|
|
; Required file was not located.
|
|
;
|
|
NONE: LD BC,NOFILE
|
|
JP PLINE
|
|
NOFILE: DB "No file"
|
|
DB 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
|
|
IF BUILD_80C = 1
|
|
AND 03H ;at end of line?
|
|
ELSE
|
|
AND 01H ;at end of line?
|
|
ENDIF
|
|
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 PRTSPACE ;add seperator between file names.
|
|
LD A,':'
|
|
CALL PRINTB
|
|
DIRECT5:CALL PRTSPACE
|
|
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 PRTSPACE ;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 DELETEFILE ;delete the file.
|
|
INC A
|
|
CALL Z,NONE ;not there?
|
|
JP GETBACK ;return to command level now.
|
|
YESNO: DB "All (y/n)?"
|
|
DB 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 DELETEFILE
|
|
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:DB "No space"
|
|
DB 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: DB "File exists"
|
|
DB 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:DB "Bad load"
|
|
DB 0
|
|
COMFILE:DB "COM" ;command file extension.
|
|
;
|
|
; Get here to return to command level. We will reset the
|
|
; previous active drive and then either return to command
|
|
; level directly or print error message and then return.
|
|
;
|
|
GETBACK:CALL RESETDR ;reset previous drive.
|
|
GETBACK1: CALL CONVFST ;convert first name in (FCB).
|
|
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.
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
CCPSTACK EQU $ ;end of ccp stack area.
|
|
;
|
|
; Batch (or SUBMIT) processing information storage.
|
|
;
|
|
BATCH: DB 0 ;batch mode flag (0=not active).
|
|
BATCHFCB: DB 0
|
|
DB "$$$ SUB"
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; File control block setup by the CCP.
|
|
;
|
|
FCB: DB 0
|
|
DB " "
|
|
DB 0,0,0,0,0
|
|
DB " "
|
|
DB 0,0,0,0,0
|
|
RTNCODE:DB 0 ;status returned from bdos call.
|
|
CDRIVE: DB 0 ;currently active drive.
|
|
CHGDRV: DB 0 ;change in drives flag (0=no change).
|
|
NBYTES: DW 0 ;byte counter used by TYPE.
|
|
;
|
|
; Room for expansion?
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; Note that the following six bytes must match those at
|
|
; (PATTRN1) or cp/m will HALT. Why?
|
|
;
|
|
PATTRN2:DB 0,22,0,0,0,0 ;(* serial number bytes *).
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* B D O S E N T R Y
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
FBASE: JP FBASE1
|
|
;
|
|
; Bdos error table.
|
|
;
|
|
BADSCTR:DW ERROR1 ;bad sector on read or write.
|
|
BADSLCT:DW ERROR2 ;bad disk select.
|
|
RODISK: DW ERROR3 ;disk is read only.
|
|
ROFILE: DW ERROR4 ;file is read only.
|
|
;
|
|
; Entry into bdos. (DE) or (E) are the parameters passed. The
|
|
; function number desired is in register (C).
|
|
;
|
|
FBASE1:
|
|
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:DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
|
|
DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
|
|
DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
|
|
DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
|
|
DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
|
|
DW RTN,WTSPECL
|
|
;
|
|
; Bdos error message section.
|
|
;
|
|
ERROR1: 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:DB "Bdos Err On "
|
|
BDOSDRV:DB " : $"
|
|
BADSEC: DB "Bad Sector$"
|
|
BADSEL: DB "Select$"
|
|
FILERO: DB "File "
|
|
DISKRO: DB "R/O$"
|
|
;
|
|
; Print bdos error message.
|
|
;
|
|
PRTERR: PUSH 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:DB 0 ;output flag (non zero means no output).
|
|
STARTING: DB 2 ;starting position for cursor.
|
|
CURPOS: DB 0 ;cursor position (0=start of line).
|
|
PRTFLAG:DB 0 ;printer flag (control-p toggle). List if non zero.
|
|
CHARBUF:DB 0 ;single input character buffer.
|
|
;
|
|
; Stack area for BDOS calls.
|
|
;
|
|
USRSTACK: DW 0 ;save users stack pointer here.
|
|
;
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
STKAREA EQU $ ;end of stack area.
|
|
;
|
|
USERNO: DB 0 ;current user number.
|
|
ACTIVE: DB 0 ;currently active drive.
|
|
PARAMS: DW 0 ;save (DE) parameters here on entry.
|
|
STATUS: DW 0 ;status returned from bdos function.
|
|
;
|
|
; Select error occured, jump to error routine.
|
|
;
|
|
SLCTERR: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,(HL) ;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,(HL) ;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,(HL)
|
|
INC HL
|
|
LD A,E
|
|
SBC A,(HL)
|
|
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: DB 0E5H ;empty directory segment indicator.
|
|
WRTPRT: DW 0 ;write protect status for all 16 drives.
|
|
LOGIN: DW 0 ;drive active word (1 bit per drive).
|
|
USERDMA:DW 080H ;user's dma address (defaults to 80h).
|
|
;
|
|
; Scratch areas from parameter block.
|
|
;
|
|
SCRATCH1: DW 0 ;relative position within dir segment for file (0-3).
|
|
SCRATCH2: DW 0 ;last selected track number.
|
|
SCRATCH3: DW 0 ;last selected sector number.
|
|
;
|
|
; Disk storage areas from parameter block.
|
|
;
|
|
DIRBUF: DW 0 ;address of directory buffer to use.
|
|
DISKPB: DW 0 ;contains address of disk parameter block.
|
|
CHKVECT:DW 0 ;address of check vector.
|
|
ALOCVECT: DW 0 ;address of allocation vector (bit map).
|
|
;
|
|
; Parameter block returned from the bios.
|
|
;
|
|
SECTORS:DW 0 ;sectors per track from bios.
|
|
BLKSHFT:DB 0 ;block shift.
|
|
BLKMASK:DB 0 ;block mask.
|
|
EXTMASK:DB 0 ;extent mask.
|
|
DSKSIZE:DW 0 ;disk size from bios (number of blocks-1).
|
|
DIRSIZE:DW 0 ;directory size.
|
|
ALLOC0: DW 0 ;storage for first bytes of bit map (dir space used).
|
|
ALLOC1: DW 0
|
|
OFFSET: DW 0 ;first usable track number.
|
|
XLATE: DW 0 ;sector translation table address.
|
|
;
|
|
;
|
|
CLOSEFLG: DB 0 ;close flag (=0ffh is extent written ok).
|
|
RDWRTFLG: DB 0 ;read/write flag (0ffh=read, 0=write).
|
|
FNDSTAT:DB 0 ;filename found status (0=found first entry).
|
|
MODE: DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random).
|
|
EPARAM: DB 0 ;storage for register (E) on entry to bdos.
|
|
RELBLOCK: DB 0 ;relative position within fcb of block number written.
|
|
COUNTER:DB 0 ;byte counter for directory name searches.
|
|
SAVEFCB:DW 0,0 ;save space for address of fcb (for directory searches).
|
|
BIGDISK:DB 0 ;if =0 then disk is > 256 blocks long.
|
|
AUTO: DB 0 ;if non-zero, then auto select activated.
|
|
OLDDRV: DB 0 ;on auto select, storage for previous drive.
|
|
AUTOFLAG: DB 0 ;if non-zero, then auto select changed drives.
|
|
SAVNXT: DB 0 ;storage for next record number to access.
|
|
SAVEXT: DB 0 ;storage for extent number of file.
|
|
SAVNREC:DW 0 ;storage for number of records in file.
|
|
BLKNMBR:DW 0 ;block number (physical sector) used within a file or logical sect
|
|
LOGSECT:DW 0 ;starting logical (128 byte) sector of block (physical sector).
|
|
FCBPOS: DB 0 ;relative position within buffer for fcb of file of interest.
|
|
FILEPOS:DW 0 ;files position within directory (0 to max entries -1).
|
|
;
|
|
; Disk directory buffer checksum bytes. One for each of the
|
|
; 16 possible drives.
|
|
;
|
|
CKSUMTBL: DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
;
|
|
; Extra space ?
|
|
;
|
|
DB 0,0,0,0
|
|
;
|
|
;**************************************************************
|
|
;*
|
|
;* B I O S J U M P T A B L E
|
|
;*
|
|
;**************************************************************
|
|
;
|
|
ALIGN_NOPS CPMBIOS
|
|
|
|
;-----------------------------------------------------------------------
|
|
; MZ-80A CPM BIOS STUB - Main Logic in CBIOS.ASM
|
|
;-----------------------------------------------------------------------
|
|
|
|
; Bring in the CBIOS stub, has the bootstrap code and DPB place markers.
|
|
INCLUDE "cpm22-bios.asm"
|
|
|
|
ALIGN_NOPS CBIOSSTART
|
|
|
|
;*
|
|
;****************** E N D O F C P / M *****************
|