You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1327 lines
35 KiB

; ***********************************************************
;
; DISASSEMBLY OF CONSOLE COMMAND PROCESSOR
; FOR CP/M VERSION 2.2
;
; ***********************************************************
; LAST REVISION: 23 DEC 1981
; ***********************************************************
;
; LEGAL NOTICE:
;
; This assembly listing is not the product of Digital
; Research Inc. and as such cannot be guaranteed for
; accuracy or completeness. The assignment of labels,
; comments and interpretation of the code is thought
; to be appropriate, but may not be what was originally
; intended. This disclaimer renounces and ignores all
; other claims and disclaimers regardless of source.
; No warrantee is made as to the merchantability or
; fitness for any purpose. Similarity between this
; program listing and any other program living or dead
; is purely coincidental.
;
; EQUATES
;
MSIZE: EQU 56 ;CPM SIZE IN KILOBYTES
;
;START: EQU (MSIZE - 20) * 1024 + 2D00H
START: EQU 0D000H
;
BDOS: EQU 0005H ;Link to operating system
IOBYTE: EQU 0003H ;I/O assigment byte for transient programs
LOGIN: EQU 0004H ;User number and disk number for transient programs
DFCB: EQU 005CH ;Default file control block
TPA: EQU 0100H ;Start of transient program area
DDMA: EQU 0080H ;Default DMA block
SECLEN: EQU 0080H ;Sector length := 128 bytes.
FBASE: EQU START + 0800H ;Start of operating system &
;location of BDOS serial numbers.
;
; SERIAL NUMBERS
;
VER: EQU 2 ;CPM VERSION
REL EQU 2 ;CPM RELEASE
REV: EQU 00 ;USER REVISION
SNH EQU 00 ;SERIAL NUMBER, HIGH
SNL EQU 00 ;SERIAL NUMBER, LOW
;
; NOTE:
; USER MUST INSERT CORRECT SERIAL NUMBER
; IN THE FORMAT HH-LLLL, WHERE HH IS TWO
; DIGITS & LLLL IS FOUR DIGITS. IF THE
; SERIAL NUMBERS IN CCP/CPM AND BDOS/CPM
; DO NOT MATCH, YOU WILL BOMB OUT WHEN
; LOADING A TRANSIENT PROGRAM.
;
;
ORG START ;SET PROGRAM START
;
; NOTE:
; On entry (C) contains the user number in the
; upper 4 bits, and the current logged in disk
; number in to lower 4 bits. This is why you log
; back to disk B: if you rebooted while logged
; into B:
;
ORIGIN: JMP CCP1 ;JUMP TO START OF CONSOLE COMMAND PROGRAM
;
JMP CCP0 ;JUMP TO START OF CCP W/ CLEARING OF INPUT BUFFER COUNTER
;
; INPUT BUFFER WITH COPYRIGHT MESSAGE THROWN IN
;
INPB: DB 127 ;MAXIMUM LENGTH OF INPUT BUFFER
INBLEN: DB 0 ;CURRENT LENGTH OF INPUT BUFFER
BUFFER: DB ' '
DB 'COPYRIGHT (C) 1979, '
DB 'DIGITAL RESEARCH '
DS BUFFER+128-$
;
; POINTERS TO INPUT BUFFER
;
BPTR1: DW BUFFER ;Pointer to input buffer used while parsing input line
BPTR2: DW 0000H ;Pointer to start of current command inn input buffer.
;Used by WHAT for error prompt.
; ***************
; SUBROUTINE AREA
; ***************
;
; TIES TO BDOS VIA VECTOR IN 0005H
;
; NOTE:
; The code in this section could be cleaned up
; shortened by rearranging things to have the
; code flow from one segment to the next
; instead of jumping back.
;
OUTPUT: MOV E,A ;OUTPUT 1 BYTE TO CONSOLE
MVI C,02H ;
JMP BDOS ;
;
; NOTE:
; Why not include OUTPUT inside of PCHAR and
; thus eliminate an- extra call?
; The only other call to OUTPUT is in PMSG,
; which should be changed anyway.
;
;PCHAR: PUSH B ;
; MOV E,A ;
; MVI C,02H ;
; CALL BDOS ;
; POP B ;
; RET ;
;
PCHAR: PUSH B ;PRINT A CHARACTER ON THE CONSOLE
CALL OUTPUT ;
POP B ;
RET ;
;
PCRLF: MVI A,0DH ;PRINT A CARRIAGE RETURN & LINE FEED
CALL PCHAR ;
MVI A,0AH ;
JMP PCHAR ;
;
SPACE: MVI A,20H ;PRINT A SPACE
JMP PCHAR ;
;
CRMSG: PUSH B ;PRINT A STRING PRECEDED BY A CR/LF
CALL PCRLF ;
POP H ;
;
; This code could be shortened by terminating the
; error code strings with a '$' and using BDOS
; command 9.
;
PMSG: MOV A,M ;PRINT A STRING TERMINATED BY A NULL
ORA A ;
RZ ;
INX H ;
PUSH H ;
CALL OUTPUT ;
POP H ;
JMP PMSG ;
;
RESET: MVI C,0DH ;RESET THE DISK SYSTEM
JMP BDOS ;
;
SELDSK: MOV E,A ;SELECT DISK
MVI C,0EH ;
JMP BDOS ;
;
BDOS1: CALL BDOS ; CALL TO BDOS W/ RETURN VALUE SAVED
STA RETVAL ;
INR A ;Set the ZFLAG for error condition
RET ;
;
OPENF: MVI C,0FH ;OPEN DISK FILE FOR READING OR WRITING
JMP BDOS1 ; CALL TO BDOS W/ RETURN VALUE
;
OPENF1: XRA A ;OPEN FILE @ FCB, AND ZERO RECORD COUNTER
STA FCB+32 ;
LXI D,FCB ;
JMP OPENF ;
;
CLOSEF: MVI C,10H ;CLOSE FILE AFTER READING OR WRITING
JMP BDOS1 ; CALL TO BDOS W/ RETURN VALUE
;
SRCHF: MVI C,11H ;SEARCH FOR FIRST OCCURENCE OF FILE NAME
JMP BDOS1 ; CALL TO BDOS W/ RETURN VALUE
;
SRCHNX: MVI C,12H ;SEARCH FOR NEXT OCCURENCE OF FILE NAME
JMP BDOS1 ; CALL TO BDOS W/ RETURN VALUE
;
SRCHF1: LXI D,FCB ;SEARCH FOR FIRST OCCURENCE OF FILE
JMP SRCHF ; NAME USING (FCB).
;
DELETF: MVI C,13H ;DELETE FILE FROM DIRECTORY
JMP BDOS ;
;
BDOS2: CALL BDOS ; CALL TO BDOS W/ RETURN VALUE
ORA A ;
RET ;
;
READF: MVI C,14H ;READ ONE SECTOR FROM DISK
JMP BDOS2 ; CALL TO BDOS W/ RETURN VALUE
;
READ1: LXI D,FCB ;READ ONE SECTOR USING 'FCB'
JMP READF ;
;
WRITEF: MVI C,15H ;WRITE ONE SECTOR TO DISK
JMP BDOS2 ; CALL TO BDOS W/ RETURN VALUE
;
CREATE: MVI C,16H ;CREATE A NEW FILE NAME IN DISK DIRECTORY
JMP BDOS1 ; CALL TO BDOS W/ RETURN VALUE
;
RENAME: MVI C,17H ;RENAME EXISTING FILE IN DISK DIRECTORY
JMP BDOS ;
;
GETUSR: MVI E,0FFH ;GET CURRENT USER NUMBER
;
; SET USER NUMBER TO VALUE IN (E), EXCEPT IF
; VALUE IS 0FFH.
;
SETUSR: MVI C,20H ;
JMP BDOS ;
;
; Set log-in byte for transient program with current
; user number in upper 4 bits and current drive in
; lower 4 bits.
;
SETLOG: CALL GETUSR ; SET LOG-IN BYTE W/ CURRENT DRIVE & USER NUMBER
ADD A ;
ADD A ;
ADD A ;
ADD A ;Shift user number to upper nibble
LXI H,CURDSK ;
ORA M ;Combine with disk number
STA LOGIN ;Place where transient programs
RET ; can find it.
;
; LOG IN CURRENT DISK
; Place current drive number in log-in byte without
; including user number.
;
LOGCUR: LDA CURDSK ;
STA LOGIN ;
RET ;
;
; CONVERT lower case INPUT TO UPPER CASE
;
LCUC: CPI 61H ;'a'
RC ;
CPI 7BH ;'z'+1
RNC ;
ANI 5FH ;MASK OFF BIT 5
RET ;
;
; COMMAND INPUT
;
; IF SUBMIT FLAG <> 0 THEN INPUT FROM SUBMIT FILE\
; ELSE INPUT FROM KEYBOARD INPUT BUFFER
;
CMNDIN: LDA SUBFL ;CHECK SUBMIT FILE FLAG
ORA A ;
JZ LINEIN ;FLAG RESET: GET INPUT FROM KEYBOARD
;
; NOTE ABOUT SUBMIT FILES:
;
; When the command SUBMIT <filename>
; is given, a file named $$$.SUB is compiled with
; one command line per record in the file.
; the file is unusual in that the commands are stored
; in reverse order. This makes it simple for the
; following code to pull one command off at a time,
; erasing as it goes. Note that the sector counter is
; decremented and the file closed each time.
; When the last command has been executed, or if an
; error is encountered, the $$$.SUB file is erased
; and control reverts to the keyboard for input.
;
LDA CURDSK ;FLAG SET: GET INPUT FROM SUBMIT FILE ON CURRENT DISK
ORA A ;
MVI A,00H ;
CNZ SELDSK ;Set to disk 0 if not there already
LXI D,SUBFCB ; Point to submit file's file control block
CALL OPENF ; Open the submit file for reading
JZ LINEIN ;Go to console on Open File Error
LDA SUBFCB+15 ;Get top sector number
DCR A ;Decrement? Yes $$$.SUB files are backwards
STA SUBFCB+32 ;Put number in Next Sector slot
LXI D,SUBFCB ;
CALL READF ;Read sector into buffer
JNZ LINEIN ;Go to console on read error
LXI D,INBLEN ;Point to start of keyboard buffer
LXI H,DDMA ;Point to start of default buffer
MVI B,SECLEN ;Set counter
CALL BLKMOV ;Move line into keyboard buffer
LXI H,SUBFCB+14 ;
MVI M,00H ;
INX H ;
DCR M ;Decrement sector count
LXI D,SUBFCB ;close the file
CALL CLOSEF ;
JZ LINEIN ;Go to console on close error
LDA CURDSK ;
ORA A ;
CNZ SELDSK ;Set to current disk if not so already
LXI H,BUFFER ;
CALL PMSG ;Echo the $$$.SUB line to console
CALL KSTAT ;Check for a keyboard break
JZ LINP1 ;NO BREAK- Continue
CALL EXITSB ; BREAK- Close off submit mode
JMP NXTCMD ;This leaves the return address on the
;stack, but NXTCMD re-initializes stack
;
; Get the input from the Console Device
;
LINEIN: CALL EXITSB ;RESET SUBMIT MODE FLAGS< ETC
CALL SETLOG ; SET LOG-IN BYTE W/ CURRENT DRIVE & USER NUMBER
MVI C,0AH ;BDOS COMMAND 10 ::= READ INPUT BUFFER
LXI D,INPB ;POINT TO START OF INPUT BUFFER
CALL BDOS ;GET A LINE FROM THE CONSOLE
CALL LOGCUR ;
LINP1: LXI H,INBLEN ;
MOV B,M ;Move the line byte count into (B)
LINP2: INX H ;Step through buffer changing lower
MOV A,B ;case into upper case.
ORA A ;
JZ LINP3 ;Exit when (B)=0
MOV A,M ;
CALL LCUC ;Convert lower case to UPPER CASE
MOV M,A ;
DCR B ;
JMP LINP2 ;Loop
;
LINP3: MOV M,A ;Poke a zero into end of line to mark end
LXI H,BUFFER ;Save command starting address
SHLD BPTR1 ; for later use.
RET ;
;
; KEYBOARD STATUS CHECK
; Return with ZFLAG set is no key pressed
; Else return with character in accumulator
;
KSTAT: MVI C,0BH ;
CALL BDOS ;
ORA A ;
RZ ;
MVI C,01H ;
CALL BDOS ;
ORA A ;
RET ;
;
; INTEROGATE THE DISK
;
INTDSK: MVI C,19H ;
JMP BDOS ;
;
; SET THE DMA ADDRESS
;
SETDMA: LXI D,DDMA ;
STDMA1: MVI C,1AH ;
JMP BDOS ;
;
; EXIT THE SUBMIT MODE
; Check to see if the Submit flag is set
; and reset it, along with the disk selection
; Erase any $$$.SUB file that may be in the directory
;
EXITSB: LXI H,SUBFL ;SUBMIT FILE FLAG
MOV A,M ;
ORA A ;
RZ ;Not in submit mode, exit
MVI M,00H ;Reset submit flag
XRA A ;
CALL SELDSK ;Select correct disk
LXI D,SUBFCB ;
CALL DELETF ;Delete the $$$.SUB file from disk
LDA CURDSK ;
JMP SELDSK ;Restore current disk and exit
;
; COMPARE SERIAL NUMBERS
; Serial numbers of CCP and BDOS are
; compared. If there is no match, you
; are banished to Digital Siberia.
; This S/R is called before a transient
; program is loaded into the TPA.
;
CMPSER: LXI D,SERNO ;Point to serial no in CCP
LXI H,FBASE ;Point to serial no in BDOS
MVI B,6 ;Number of bytes to compared
CMPSN1: LDAX D ;
CMP M ;
JNZ FATAL ; OK, you've had it. You are locked out.
INX D ;
INX H ;
DCR B ;
JNZ CMPSN1 ;
RET ; Good comparison. You are home free
;
; GENERAL PURPOSE ERROR HANDLING ROUTINE
; Anything that you do wrong in entering a
; command lands you here. The command line
; is typed back at you for your edification
; and embarrassment. After you have been shown
; a '?', you are put back in the command mode.
;
WHAT: CALL PCRLF ;
LHLD BPTR2 ; Point to the offending command
WHAT1: MOV A,M ; and print until a space or null is found
CPI ' ' ;
JZ WHAT2 ;
ORA A ;
JZ WHAT2 ;
PUSH H ;
CALL OUTPUT ;
POP H ;
INX H ;
JMP WHAT1 ;
;
WHAT2: MVI A,'?' ; OK, what did you have in mind?
CALL OUTPUT ;
CALL PCRLF ;
CALL EXITSB ;
JMP NXTCMD ; Back for another command.
;
; SPECIAL CHARACTER CHECK
; Returns with ZFLAG set if any of the special
; characters is found, or bombs you out if a
; control character is found. Contains a useless
; byte.
;
CHRCHK: LDAX D ;
ORA A ;
RZ ;Exit on null
CPI ' ' ;
JC WHAT ;Bomb out on control character
RZ ;
CPI '=' ;
RZ ;
CPI '_' ;
RZ ;
CPI '.' ;
RZ ;
CPI ':' ;
RZ ;
CPI ';' ;
RZ ;
CPI '<' ;
RZ ;
CPI '>' ;
RZ ; This command is redundant!
RET ;
;
; STEP (DE) UNTIL A NULL OR NON-SPACE IS FOUND
; Return with the character in the accumulator.
;
STEP: LDAX D ;
ORA A ;
RZ ;
CPI 20H ;' '
RNZ ;
INX D ;
JMP STEP ;
;
; ADD THE ACCUMULATOR TO (HL)
; Return with the result in (HL)
;
ADDAH: ADD L ; (HL) <- (HL) + (A)
MOV L,A ;
RNC ;
INR H ;
RET ;
;
; TRANSFER FILE CONTROL BLOCK
; This is a key subroutine that handles all
; of the setting up of file control blocks
; and of moving around the control buffer.
;
; No entry parameters are passed in registers
; On exit (A) contains a count of the number of
; '?' wildcards in file control block and ZFLAG
; is set if this count =0 and FCB contains the
; file block. The input buffer pointer has been
; advanced to one beyond end of information used.
;
; This subroutine is not only used to set up
; file control blocks, but also the numbers
; for SAVE and USER.
;
TRFCB: MVI A,00H ;Initial offset=0, primary entry point
TRFCB1: LXI H,FCB ;(Alternate entry point, offset in (A))
CALL ADDAH ;Point to current character in FCB
PUSH H ;
PUSH H ;Save pointer twice.
XRA A ;
STA TRDISK ;
LHLD BPTR1 ;Get current pointer to the input buffer
XCHG ;
CALL STEP ;Get first non-blank character
XCHG ;
SHLD BPTR2 ;Save pointer in case of error
XCHG ;
POP H ;Get FCB pointer
LDAX D ;
ORA A ;Check for null command
JZ TRFCB2 ;
SBI 40H ;Remove ASCII bias.
MOV B,A ;Save possible disk number in (B)
INX D ;
LDAX D ;
CPI ':' ; This means that this is a disk number.
JZ TRFCB3 ; Go set transient disk.
DCX D ; Get current logged in disk number.
TRFCB2: LDA CURDSK ;
MOV M,A ;
JMP TRFCB4 ;
;
TRFCB3: MOV A,B ; Set transient disk number.
STA TRDISK ;
MOV M,B ; Put disk number into head of File Control Block.
INX D ;
;
; Move file name of up to 8 characters into bytes
; 1 through 8 of FCB. Exit at reaching a special
; character or a null. Pad excess with blanks.
; If a '*' is found, fill the rest of the name block
; with '?'
;
TRFCB4: MVI B,08H ; Move file name of up to 8 Characters.
TRFCB5: CALL CHRCHK ; Exit if a special character is found.
JZ TRFCB9 ; Go pad the rest of the block w/ ' '
INX H ;
CPI '*' ;This is a Wild card match
JNZ TRFCB6 ;
MVI M,'?' ;This is an 'anything match'.
JMP TRFCB7 ;
;
TRFCB6: MOV M,A ;
INX D ;
TRFCB7: DCR B ;
JNZ TRFCB5 ;
;
TRFCB8: CALL CHRCHK ;Check for special character
JZ TRFC10 ;
INX D ;
JMP TRFCB8 ;
;
TRFCB9: INX H ; Pad the rest of the file name with blanks.
MVI M,' ' ;
DCR B ;
JNZ TRFCB9 ;
;
; Move the file extention into bytes 9 through 11 of
; FCB. Pad excess with blancks, and fill with '?'
; in the same manner as the filename.
;
TRFC10: MVI B,03H ; Move a file type of up to 3 Characters.
CPI '.' ;A period is the only legal special character in this case.
JNZ TRFC15 ;
INX D ;
TRFC11: CALL CHRCHK ; Check for special characters.
JZ TRFC15 ;
INX H ;
CPI '*' ;Wild card match- Pad the 3 bytes with ???
JNZ TRFC12 ;
MVI M,'?' ;
JMP TRFC13 ;
;
TRFC12: MOV M,A ;Put the character into FCB
INX D ;Advance FCB pointer
TRFC13: DCR B ;Decrement character counter
JNZ TRFC11 ;Loop until counter is zero
TRFC14: CALL CHRCHK ;Step buffer until special character,
JZ TRFC16 ; or null is found.
INX D ;
JMP TRFC14 ;
;
TRFC15: INX H ;Pad the rest of the extent with ' '
MVI M,' ' ;
DCR B ;
JNZ TRFC15 ;
;
TRFC16: MVI B,03H ;Zero out bytes 12 through 14
TRFC17: INX H ;
MVI M,00H ;
DCR B ;
JNZ TRFC17 ;
;
XCHG ;
SHLD BPTR1 ;Save the input buffer pointer
POP H ;(HL) = FCB
LXI B,11 ;Set counter
TRFC18: INX H ;Count number of '?' in filename.ext
MOV A,M ;
CPI '?' ;
JNZ TRFC19 ;Skip if not'?'
INR B ;
TRFC19: DCR C ;
JNZ TRFC18 ;
MOV A,B ;Move '?' count into (A)
ORA A ;Set flags to indicate ambigous filename
RET ;
;
; COMMAND NAME LIST
;
CMNDL: DB 'DIR ' ;Read directory
DB 'ERA ' ;Erase file
DB 'TYPE' ;Output named file to console
DB 'SAVE' ;Save named file on disk
DB 'REN ' ;Rename file
DB 'USER' ;Set user number
;
; SERIAL NUMBERS
;
SERNO: DB SNH ;
DB VER*10+REL ;2.2 IS 16H
DW REV ;
DB SNL/256 ;
DB SNL MOD 256 ;
;
; COMMAND SEARCH
;
; Try to match command name starting at FCB + 1
; with those in the intrinsic command list.
; Exit with command number (0 to 5) in (A) if a
; match has been made, else exit with a 6 in (A)
; signifying that this may be a transient command.
;
CSRCH: LXI H,CMNDL ; Point to start of command list.
MVI C,00H ; Zero out command counter.
CSRCH1: MOV A,C ; Check counter for maximum number of tries.
CPI 6 ;(There are 6 intrinsic commands + transients)
RNC ; Return is no match in six tries. It is a transient command.
LXI D,FCB+1 ; Point to start of command to be matched.
MVI B,04H ; Maximum number of characters in command name.
CSRCH2: LDAX D ;
CMP M ;
JNZ CSRCH3 ; No match- exit loop.
INX D ;
INX H ;
DCR B ;
JNZ CSRCH2 ; Back for another match.
LDAX D ; Found the command, now check for a separating space.
CPI ' ' ;
JNZ CSRCH4 ; No space, try for something else.
MOV A,C ; Put command count in accumulator.
RET ; Exit routine with command number.
;
CSRCH3: INX H ; Step forwards to that start of the next command name.
DCR B ;
JNZ CSRCH3 ;
CSRCH4: INR C ; Bump command counter by one.
JMP CSRCH1 ; Back for another try.
;
; ******************************
; MAIN OPERATING ROUTINE FOR CCP
; ******************************
;
; Entry point number 1
; Input buffer counter is zeroed out so no imbedded
; command can be executed on initial boot up.
;
CCP0: XRA A ;
STA INBLEN ;
;
;
; Entry point number 2
; Entry a this point allows commands inbedded in
; input buffer to be automatically executed
; on initial boot up.
;
; On entry (C) has user number in the high nibble
; and the disk drive in the lower nibble
;
CCP1: LXI SP,STACK ;Set up loacal stack
PUSH B ;Save entry parameter
MOV A,C ;
RAR ;Move upper 4 bits down
RAR ;
RAR ;
RAR ;
ANI 0FH ;Extract user number from input parameter
MOV E,A ;
CALL SETUSR ;Put user number in its place
CALL RESET ;Reset disk system
STA SUBFL ;SUBMIT FILE FLAG
POP B ;Get back entry parameter
MOV A,C ;
ANI 0FH ;Extract disk drive number from input parameter
STA CURDSK ;Set to correct disk drive
CALL SELDSK ;
LDA INBLEN ;
ORA A ;Check to see if there is anything in the input buffer
JNZ CCP2 ;There is, so don't bother with input.
;
; RE-ENTRY POINT FOR NEXT COMMAND
;
NXTCMD: LXI SP,STACK ; Reset stack, and put out a prompt so
; that you know what disk you are operating on.
CALL PCRLF ;
CALL INTDSK ;Get drive number
ADI 'A' ;Add ASCII bias
CALL OUTPUT ;
MVI A,'>' ;Prompt character
CALL OUTPUT ;
CALL CMNDIN ; Input the next command line from the console or submit file.
CCP2: LXI D,DDMA ;Set to the default DMA (0080H)
CALL STDMA1 ;
CALL INTDSK ;Check disk drive
STA CURDSK ;
CALL TRFCB ;Put command name in FCB
CNZ WHAT ;Ambigous filename.ext No good
LDA TRDISK ;
ORA A ;
JNZ TRANS ;Transient command
CALL CSRCH ;Find command in command list
LXI H,CMNDT ;Point to command vector table
MOV E,A ;Put command number in (DE)
MVI D,00H ;
DAD D ;
DAD D ;(HL) <-- CMNDT + 2 * Command number
MOV A,M ;Move command vector into (HL)
INX H ;
MOV H,M ;
MOV L,A ;
PCHL ;Go forth and execute the command.
;
; COMMAND VECTOR TABLE
;
CMNDT: DW DIR ;Display directory
DW ERA ;Erase file
DW TYPE ;Type file to console
DW SAVE ;Save file on disk
DW REN ;Rename file
DW USER ;Set user number
DW TRANS ;Load and execute transient command
;
; OK- You were warned about the serial numbers, but
; you went ahead anyway. Now you must pay the penalty
;
FATAL: LXI H,76F3H ;STUFF A DI & HLT
SHLD ORIGIN ;AT THE HEAD OF CCP
LXI H,ORIGIN ;
PCHL ;HANG UP THERE UNTIL RESET
;
; FILE READ ERROR
;
RDERR: LXI B,MSG1 ;Load pointer to message
JMP CRMSG ;Display message & return
;
MSG1: DB 'READ ERROR',0
;
; FILE NOT FOUND ERROR
;
NFERR: LXI B,MSG2 ;Load pointer to message
JMP CRMSG ;Display message & return
;
MSG2: DB 'NO FILE',0
;
; DECIMAL TO BINARY CONVERSION
; Used by SAVE and USER
;
; NOTE:
;
; There is some inefficient code in this subroutine
; TRFCB will move the numbers into FCB and pad the
; rest of the block with ' '. The number can be 3
; digits long at most, so that the elaborate stepping
; past blanks, etc is not needed. Code that can be
; revised is marked by '*'
;
DECIML: CALL TRFCB ;Move numbers into FCB
LDA TRDISK ;
ORA A ;
JNZ WHAT ;
LXI H,FCB+1 ;
LXI B,11 ;8 NUMBER FILE NAME & 3 CHAR EXT
DEC1: MOV A,M ;
CPI ' ' ;
JZ DEC2 ;
INX H ;
SUI '0' ;REMOVE ASCII BIAS
CPI 10 ;
JNC WHAT ;
MOV D,A ;
MOV A,B ;*
ANI 0E0H ;*
JNZ WHAT ;*
MOV A,B ;*
RLC ;MULTIPLY BY 10
RLC ;
RLC ;
ADD B ;
JC WHAT ;Overflow
ADD B ;
JC WHAT ;Overflow
ADD D ;
JC WHAT ;Overflow
MOV B,A ;
DCR C ;*
JNZ DEC1 ;* Should be JMP DEC1
RET ;*
;
DEC2: MOV A,M ;*Step over blanks to end of block
CPI ' ' ;*
JNZ WHAT ;*
INX H ;*
DCR C ;*
JNZ DEC2 ;*
MOV A,B ;Return with binary value in (A)
RET ;
;
; BLOCK MOVE
; Source :: ((HL))
; Destination :: ((DE))
; Count :: (B)
;
MOVE3: MVI B,03H ;3 byte move
BLKMOV: MOV A,M ;Source
STAX D ;Destination
INX H ;
INX D ;
DCR B ;Counter
JNZ BLKMOV ;
RET ;
;
; SET DIRECTORY POINTER
; Point to (DDMA) + (C) + (A)
; Return with character in (A)
;
; On entry (C) contains directory entry offset
; i.e. 00H, 20H, 40H, or 60H.
; Accumulator (A) contains number of the byte
; within the directory that the pointer is to
; be set to.
;
DIRPTR: LXI H,DDMA ;Point to start of directory sector
ADD C ;Add byte number to directory offset
CALL ADDAH ; then add the result to (HL)
MOV A,M ; and get the byte pointed to.
RET ;
;
; SET TO TRANSIENT DISK
; Set the disk specified by (TRDISK) for
; reading or writing. If (TRDISK) = (CURDISK), you
; are already on the right disk, so no change is needed
;
SETTRD: XRA A ;
STA FCB ;Set first byte of FCB to 0
LDA TRDISK ;
ORA A ;Check to see if transient disk= 0
RZ ;OK- no change needed
DCR A ;
LXI H,CURDSK ;Now check current disk
CMP M ;
RZ ;OK- no change needed
JMP SELDSK ;Disk different, so go get right one
;
; SET CURRENT DISK
; Restore the current logged-in disk as the read/write
; disk. If already on the proper disk, no action is required
;
SETCUR: LDA TRDISK ;
ORA A ;
RZ ;OK- On log-in disk, no change
DCR A ;
LXI H,CURDSK ;
CMP M ;
RZ ;OK
LDA CURDSK ;On different disk.
;(NOTE: Change this command to MOV A,M and save 2 bytes)
JMP SELDSK ; so go make change
;
; START OF INTRINSIC COMMANDS
;
; *******************************
; DIR SEARCHES FILE DIRECTORY FOR
; FILES BY FILE NAME OR FILE TYPE
; ? MATCHES ANY LETTER
; * MATCHES THE WHOLE FILENAME OR
; FILETYPE
; *******************************
;
; NOTE ON SYSTEM FILES:
; 'System' type files are rendered invisible to the
; DIR command because the MSB of the second byte of
; the file ext is set. A minor change in the following
; code could make these files listable in the Directory
;
DIR: CALL TRFCB ; TRANSFER THE COMMAND LINE INTO 005CH.
CALL SETTRD ; SET TO TRANSIENT DISK
LXI H,FCB+1 ; POINT TO START OF FILE NAME
MOV A,M ; CHECK FOR A BLANK
CPI 20H ;' '
JNZ DIR1 ; NOT BLANK, GO FIND WHAT IS WANTED.
;
MVI B,11 ; BLANK ::= DISPLAY ENTIRE DIRECTORY
DIR0: MVI M,3FH ; FILL ENTIRE FILENAME/TYPE ARE WITH WILDCARD [?]'?'
INX H ;
DCR B ;
JNZ DIR0 ;
;
DIR1: MVI E,00H ; SEARCH FOR FILE.
PUSH D ;
CALL SRCHF1 ;
CZ NFERR ; <NOT FOUND> ERROR
DIR2: JZ DIR10 ;
LDA RETVAL ;Get directory entry number
RRC ;Convert to directory block pointer
RRC ;by multiplying by 32 and masking off
RRC ;the excess
ANI 60H ;
MOV C,A ;Stash block pointer
MVI A,10 ;
CALL DIRPTR ;Get character @ DDMA + block pointer + 10 = Second char of ext
RAL ;Check for MSB set :: SYSTEM file
JC DIR9 ;Skip- System files are not listed by DIR
POP D ;
MOV A,E ;
INR E ;
PUSH D ;
ANI 03H ;Check for every 4th directory item
PUSH PSW ;
JNZ DIR3 ;Not the 4th
CALL PCRLF ; 4th, so start new line and mark
PUSH B ; with disk drive letter
CALL INTDSK ;Get disk number
POP B ;
ADI 'A' ;Print the disk number & colon at the
CALL PCHAR ;start of each directory print line
MVI A,':' ;
CALL PCHAR ;
JMP DIR4 ;
;
DIR3: CALL SPACE ;Print a space/colon/space between
MVI A,':' ;directory items
CALL PCHAR ;
DIR4: CALL SPACE ;
MVI B,01H ;
;
; The code in this area is somewhat tangled
; Space could be saved by redoing some of the
; ways that pointers are set up. Currently the
; pointer to the directory entry is recomputed
; for each byte. Why not just load (HL) once
; and increment?
;
DIR5: MOV A,B ;Print the filename.ext to the console
CALL DIRPTR ;Point to byte and reurn w/ byte in (A)
ANI 7FH ;
CPI ' ' ;
JNZ DIR7 ;
POP PSW ;
PUSH PSW ;
CPI 03H ;Chack for file ext end
JNZ DIR6 ;
MVI A,09H ;Check for file name end
CALL DIRPTR ;
ANI 7FH ;
CPI ' ' ;Check for no file ext
JZ DIR8 ;
DIR6: MVI A,' ' ;Separator between filename & ext
DIR7: CALL PCHAR ;
INR B ;
MOV A,B ;
CPI 12 ;File ext end +1
JNC DIR8 ;
CPI 9 ;File name end +1
JNZ DIR5 ;
CALL SPACE ;
JMP DIR5 ;
;
DIR8: POP PSW ;
DIR9: CALL KSTAT ;CHECK KEY BOARD, ANY KEY BREAKS
JNZ DIR10 ;Exit on break
CALL SRCHNX ;Look for another directory item
JMP DIR2 ;Loop back for more
;
DIR10: POP D ;
JMP COMEND ;EXIT
;
; ******************************************************
; ERASE FILES FROM DISK
;
; '*' and '?' can be used the same as in 'DIR'
; Does not actually wipe out information from the disk,
; but merely has the first byte of the file control block
; set to 0E5H. This routine in CCP merely sets up the
; file names to be erased; the real work is done by BDOS
; ******************************************************
;
ERA: CALL TRFCB ;Set up the file control block
CPI 11 ;If 11 '?' are in the FCB then you are
JNZ ERA1 ;asking to erase the whole disk. *.*
LXI B,MSG3 ;Ask for verification before proceeding
CALL CRMSG ;
CALL CMNDIN ;Get keyboard entry
LXI H,INBLEN ;check for return without entry
DCR M ;
JNZ NXTCMD ;Abort on empty line
INX H ;
MOV A,M ;
CPI 'Y' ;Yes, proceed with erasing everything
JNZ NXTCMD ;Chickened out
INX H ;
SHLD BPTR1 ;
ERA1: CALL SETTRD ;set the transient disk
LXI D,FCB ;Point to the file control block
CALL DELETF ;Call the routine that calls th e BDOS erase
INR A ;Check the return parameter
CZ NFERR ;Warn if item not found
JMP COMEND ;EXIT
;
MSG3: DB 'ALL (Y/N)?',0
;
; ***********************************
; OUTPUT NAMED FILE TO THE CONSOLE
; File name.ext must be unconditional
; ***********************************
;
TYPE: CALL TRFCB ;Set up file name in FCB
JNZ WHAT ;Badly formed filename: ambigous
CALL SETTRD ;Set the disk
CALL OPENF1 ;Open the file for reading
JZ TYPE4 ;Error on opening
CALL PCRLF ;
LXI H,TYPCTR ;Initiate counter
MVI M,0FFH ;
TYPE1: LXI H,TYPCTR ;
MOV A,M ;Get the buffer counter
CPI 80H ;Check for end of buffer
JC TYPE2 ;Not at end, get character from buffer
;
; At end of buffer- Read from disk
;
PUSH H ;Read a sector from disk to the buffer
CALL READ1 ;
POP H ;
JNZ TYPE3 ;RETURN VALUE <> 0. End of file or bad read
XRA A ;
MOV M,A ;Zero (TYPCTR)
TYPE2: INR M ;
LXI H,DDMA ;
CALL ADDAH ;Point to character
MOV A,M ;Get the character
CPI 1AH ;Check for end of file
JZ COMEND ;EXIT
CALL OUTPUT ;Not EOF, output the byte to the console
CALL KSTAT ;Check for keyboard break
JNZ COMEND ;EXIT ON ANY KEY
JMP TYPE1 ;Back for the next letter
;
; (A) = 1 :: End of file
; (A) = 2 :: Read error
;
TYPE3: DCR A ;
JZ COMEND ;EXIT AT END OF FILE
CALL RDERR ;
TYPE4: CALL SETCUR ;This piece of code can be used to
JMP WHAT ;replace REN4 and TRANS8
;
; **********************************
; SAVE CREATES DISK FILE FROM MEMORY
; STARTING AT 0100H. 256 BYTE PAGES
; FILENAME MUST BE UNCONDITIONAL
; IF A FILE OF THE SAME NAME & TYPE
; ALREADY EXISTS, IT IS ERASED.
; **********************************
;
SAVE: CALL DECIML ;GET THE NUMBER OF PAGES TO BE SAVED
PUSH PSW ;
CALL TRFCB ;SET UP THE FILE NAME
JNZ WHAT ;Cannot be ambigous file
CALL SETTRD ;SET UP DISK
LXI D,FCB ;
PUSH D ;
CALL DELETF ;DELETE OLD FILE OF SAME NAME
POP D ;
CALL CREATE ;CREATE A NEW FILE IN DIRECTORY
JZ SAVE3 ;
XRA A ;
STA FCB+32 ;ZERO OUT SECTOR COUNTER
POP PSW ;
MOV L,A ;
MVI H,00H ;CHECK PAGE COUNTER
DAD H ;
LXI D,TPA ;
SAVE1: MOV A,H ;CHECK PAGE COUNTER
ORA L ;
JZ SAVE2 ;EXIT, LAST PAGE HAS BEEN WRITTEN
DCX H ;
PUSH H ;SAVE COUNTER
LXI H,SECLEN ;ADVANCE DMA LOCATION
DAD D ;BY ONE SECTOR
PUSH H ;
CALL STDMA1 ;SET THE DMA ADDRESS
LXI D,FCB ;
CALL WRITEF ;WRITE THE SECTOR TO DISK
POP D ;
POP H ;
JNZ SAVE3 ;ERROR CONDITION
JMP SAVE1 ;LOOP BACK TO WRITE ANOTHER SECTOR
;
SAVE2: LXI D,FCB ;CLOSE THE FILE TO MAKE IT OFFICIAL
CALL CLOSEF ;
INR A ;CHECK FOR GOOD CLOSING
JNZ SAVE4 ;OK
SAVE3: LXI B,MSG4 ;<NO SPACE> ERROR
CALL CRMSG ;
SAVE4: CALL SETDMA ;
JMP COMEND ;EXIT
;
MSG4: DB 'NO SPACE',0
;
; *******************************************
; RENAME A FILE THE FORMAT IS
; <NEW FILENAME>.<TYPE>=<OLD FILENAME>.<TYPE>
; A '_' may be substituted for the '='
; *******************************************
;
REN: CALL TRFCB ;Put new filename into FCB
JNZ WHAT ;Cannot be ambigous filename
LDA TRDISK ;Get the disk number
PUSH PSW ;
CALL SETTRD ;Log into the correct disk
CALL SRCHF1 ;Check to see if such a name exists
JNZ FEERR ;ERROR- File name already used
LXI H,FCB ;Move new filename to upper half of FCB
LXI D,FCB+16 ;
MVI B,10H ;
CALL BLKMOV ;
LHLD BPTR1 ;Back to input buffer
XCHG ;
CALL STEP ;
CPI '=' ;Check for proper separator
JZ REN1 ;OK
CPI '_' ;
JNZ REN4 ;NO GOOD
REN1: XCHG ;
INX H ;Step over separartor
SHLD BPTR1 ;Store pointer for TRFCB to use
CALL TRFCB ;Move name.type of Old file into place
JNZ REN4 ;Error- cannot be ambigous
POP PSW ;Get back disk number
MOV B,A ;
LXI H,TRDISK ;
MOV A,M ;
ORA A ;
JZ REN2 ;OK- current disk=transient disk
CMP B ;Check disk number
MOV M,B ;Save back into TRDISK
JNZ REN4 ;Error
REN2: MOV M,B ;
XRA A ;
STA FCB ;Zero in FCB :: Logged in disk
CALL SRCHF1 ;Does old file exist?
JZ REN3 ;NO- <NO FILE> Error
LXI D,FCB ;OK- Rename it
CALL RENAME ; via call to BDOS
JMP COMEND ;EXIT
;
REN3: CALL NFERR ;<NO FILE> Error
JMP COMEND ;EXIT
;
; This piece of code can be deleted, and jumps
; to it rerouted to TYPE4
;
REN4: CALL SETCUR ;Return to current disk
JMP WHAT ;??????????
;
; FILE EXISTS ERROR
;
FEERR: LXI B,MSG6 ;Load error message pointer
CALL CRMSG ;Display maessage
JMP COMEND ;EXIT
;
; NOTE:
; If these error messages ended with '$' instead of
; a null, then the BDOS string output command could
; be used instead of the PMSG subroutine.
;
MSG6: DB 'FILE EXISTS',0
;
; ************************************
; SET THE USER NUMBER FROM THE CONSOLE
; Nobody uses this in one-user CP/M
; ************************************
;
USER: CALL DECIML ;Convert number to binary
CPI 10H ;Must be 0 to 15
JNC WHAT ;Out of range
MOV E,A ;Set in place for BDOS call
LDA FCB+1 ;Check for blank 'filename'
CPI ' ' ;
JZ WHAT ;Error
CALL SETUSR ;Set via BDOS
JMP CMEND1 ;EXIT
;
; *************************************************
; TRANS LOADS A PROGRAM INTO THE TPA IF IT IS NOT A
; COMMAND NAME, AND A PROPER <FILENAME>.COM FILE
; EXISTS ON THE TRANSIENT DISK.
; *************************************************
;
TRANS: CALL CMPSER ; CHECK FOR A KOSHER SERIAL NUMBER. BOMB OUT IF NO GOOD.
LDA FCB+1 ; SPACE ::= NO FILE NAME PRESENT.
CPI ' ' ;
JNZ TRANS1 ;OK- Filename present
LDA TRDISK ;NG- Just clean things up and leave
ORA A ;
JZ CMEND1 ;EXIT IF ON THE LOGIN DISK
DCR A ;
STA CURDSK ;OTHERWISE LOG BACK TO THE LOGIN DISK
CALL LOGCUR ;
CALL SELDSK ;
JMP CMEND1 ;AND GO BACK FOR ANOTHER COMMAND
;
TRANS1: LXI D,FCB+9 ; CHECK THE FILE TYPE.
LDAX D ;
CPI ' ' ;
JNZ WHAT ; YOU'RE NOT SUPPOSED TO HAVE A FILE TYPE W/ A TRANSIENT.
PUSH D ;
CALL SETTRD ; SET TO TRANSIENT DRIVE.
POP D ;
LXI H,COM ; MOVE FILE TYPE "COM" INTO FCB.
CALL MOVE3 ;
CALL OPENF1 ; OPEN FILE FOR READING.
JZ TRANS8 ; ERROR CONDITION, FILE NOT FOUND.
LXI H,TPA ; STARTING POINT FOR PROGRAM LOADING.
;
TRANS2: PUSH H ; MOVE PROGRAM INTO TPA SECTOR BY SECTOR.
XCHG ;
CALL STDMA1 ; POINT TO LOADING LOCATION.
LXI D,FCB ;
CALL READF ; READ 1 SECTOR INTO MEMORY.
JNZ TRANS3 ; ZFLAG RESET ::= GOOD READ, GO AROUND FOR MORE.
POP H ;
LXI D,SECLEN ;
DAD D ; ADVANCE POINTER FOR NEXT SECTOR.
LXI D,ORIGIN ; CHECK FOR PROGRAM OVERLAPPING CCP AREA.
MOV A,L ;
SUB E ;
MOV A,H ;
SBB D ;
JNC LDERR ; ERROR CONDITION, PROGRAM TOO BIG FOR MEMORY.
JMP TRANS2 ; BACK FOR NEXT SECTOR.
;
TRANS3: POP H ; CLEAR STACK.
DCR A ; 1 ::= END OF FILE (GOOD) OR 2 ::= READ ERROR (BAD).
JNZ LDERR ; READ ERROR.
CALL SETCUR ; SET BACK TO CURRENT DRIVE.
CALL TRFCB ; MOVE ANY FURTHER FCB'S AT 005CH AND 006CH.
LXI H,TRDISK ;
PUSH H ;
MOV A,M ;Put transient disk number into
STA FCB ; first byte of file control block.
MVI A,16 ;Set offset for TRFCB to upper half of FCB
CALL TRFCB1 ;Move seconf file name into FCB + 16
POP H ;
MOV A,M ;Get transient disk number of second FN
STA FCB+16 ; and insert it into FCB + 16
XRA A ;Zero out sector counter
STA FCB+32 ;
LXI D,DFCB ;Move filenames to default FCB
LXI H,FCB ;
MVI B,33 ;File control block & sector counter
CALL BLKMOV ;
;
LXI H,BUFFER ;Start at the head of buffer
TRANS4: MOV A,M ;Step until blank or null found
ORA A ; i.e. step over transient name
JZ TRANS5 ;
CPI ' ' ;
JZ TRANS5 ;
INX H ;
JMP TRANS4 ;Loop
;
TRANS5: MVI B,00H ;Command line byte counter
LXI D,DDMA+1 ;
TRANS6: MOV A,M ;Move command line into default buffer
STAX D ;
ORA A ;Exit on null
JZ TRANS7 ;
INR B ;Bump counter
INX H ;
INX D ;
JMP TRANS6 ;
;
TRANS7: MOV A,B ;Put buffer counter at start
STA DDMA ; of default buffer
CALL PCRLF ;
CALL SETDMA ;Set DMA to default buffer
CALL SETLOG ; SET LOG-IN BYTE W/ CURRENT DRIVE & USER NUMBER
CALL TPA ; GO OUT AND RUN THE PROGRAM.
LXI SP,STACK ; RESTORE STACK ON RETURN.
CALL LOGCUR ;Log back to the current disk
CALL SELDSK ;
JMP NXTCMD ; READY FOR ANOTHER ASSIGNMENT.
;
; Redundant code:
; This can be deleted, and jumps to this
; point changed to TYPE4
;
TRANS8: CALL SETCUR ;Error exit
JMP WHAT ;
;
; LOAD ERROR
;
LDERR: LXI B,MSG7 ;Load the error pointer
CALL CRMSG ;Display the message
JMP COMEND ;EXIT
;
MSG7: DB 'BAD LOAD',0
;
COM: DB 'COM' ; FILE TYPE FOR TRANSIENT COMMANDS.
;
; COMMAND END
; CLEAN-UP AND EXIT POINT FOR ALL COMMANDS
; EXCEPT TRANSIENT COMMANDS.
;
COMEND: CALL SETCUR ;PRIMARY EXIT POINT
CMEND1: CALL TRFCB ;SECONDARY EXIT POINT
LDA FCB+1 ; CHECK FOR FURTHER COMMANDS.
SUI ' ' ;
LXI H,TRDISK ;
ORA M ;
JNZ WHAT ;
JMP NXTCMD ; BACK TO THE RATRACE.
;
; END OF THE PROGRAM AREA
;
; START OF PARAMETER STORAGE AREA
;
DS 16 ;STACK AREA
STACK: EQU $ ;CCP STACK
;
SUBFL: DS 1 ; SUBMIT FLAG. FLAG SET WHEN COMMANDS COME FROM $$$.SUB FILE.
SUBFCB: DB 0,'$$$ SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
FCB: DS 33 ;FILE CONTROL BLOCK FOR CCP READ/WRITE
RETVAL: DS 1 ; VALUE RETURNED IN (A) FROM CALLS TO BDOS.
CURDSK: DS 1 ; CURRENT LOGGED IN DISK.
TRDISK: DS 1 ; CURRENT TRANSIENT DISK.
TYPCTR: DS 1 ; INPUT BUFFER COUNTER USED BY 'TYPE' TO KEEP TRACK
; OF INPUT DISK BUFFER.
;
FINISH: END ORIGIN ;END OF SOURCE CODE