Files
RomWBW/Source/CPM22/CCP22.ASM
2014-09-08 04:11:55 +00:00

1327 lines
35 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; ***********************************************************
;
; 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