;		ASSEMBLY LANGUAGE VERSION OF THE CP/M CONSOLE COMMAND PROCESSOR
		BIAS	EQU 800H
;		VERSION, 1.1 OCTOBER, 1976
;
;		COPYRIGHT (C) 1976
;		DIGITAL RESEARCH
;
		FALSE	EQU 8000H
		TRUE	EQU NOT FALSE
		TESTING	EQU FALSE		;TRUE IF DEBUGGING
		CCPLOC	EQU BIAS		;BASE ADDRESS OF CCP
		BDOSL	EQU 900H+BIAS	;BASE OF DOS
;
;
		IF		TESTING
		ORG		100H
		MVI		C, 0
		TRAN	EQU 81000H		;TRANSIENT PROGRAM AREA BASE (TPA)
		TRANM	EQU 1400H		;LAST TRANSIENT LOCATION + 1
		ENDIF
;
		IF		NOT TESTING
		ORG		CCPLOC
		TRAN	EQU 100H
		TRANM	EQU $
		ENDIF
;
		JMP		CCPSTART
		DB		'COPYRIGHT (C) 1976, DIGITAL RESEARCH'
;
		DISKA	EQU 0004H		;DISK ADDRESS FOR CURRENT DISK
		BDOS	EQU 0005H		;PRIMARY BDOS ENTRY POINT
		BUFF	EQU 0080H		;DEFAULT BUFFER
		FCB		EQU 005CH		;DEFAULT FILE CONTROL BLOCK
;
		RCHARF	EQU 1			;READ CHARACTER FUNCTION
		PCHARF	EQU 2			;PRINT CHARACTER FUNCTION
		PBUFF	EQU 9			;PRINT BUFFER FUNCTION
		RBUFF	EQU 10			;READ BUFFER FUNCTION
		BREAKF	EQU 11			;BREAK KEY FUNCTION
		LIFTF	EQU 12			;LIFT HEAD FUNCTION, (SHUGART SA3900 ONLY)
		INITF	EQU 13			;INITIALIZE BDOS FUNCTION
		SELF	EQU 14			;SELECT DISK FUNCTION
		CPENF	EQU 15			;OPEN FILE FUNCTION
		CLOSEF	EQU 16			;CLOSE FILE FUNCTION
		SEARF	EQU 17			;SEARCH FOR FILE FUNCTION
		SEARNF	EQU 18			;SEARCH FOR NEXT FILE FUNCTION
		DELF	EQU 19			;DELETE FILE FUNCTION
		DREADF	EQU 20			;DISK READ FUNCTION
		DWRITF	EQU 21			;DISK WRITE FUNCTION
		MAKEF	EQU 22			;FILE MAKE FUNCTION
		RENF	EQU 23			;RENAME FILE FUNCTION
		LOGF	EQU 24			;RETURN LOGIN VECTOR
		CSELF	EQU 25			;RETURN CURRENTLY SELECTED DRIVE NUMBER
		DMAF	EQU 26			;SET DMA ADDRESS
;
		CR		EQU 13			;CARRIAGE RETURN
		LF		EQU 10			;LINE FEED
		LA		EQU 5FH			;LEFT ARROW
		EOFILE	EQU 1AH			;END OF FILE
		NDISKS	EQU 2			;NUMBER OF DISKS
;
;		UTILITY PROCEDURES
PRINTCHAR:	MOV		E, A! MVI C, PCHARF! JMP BDOS
;
PRINTBC:						;PRINT CHARACTER, BUT SAVE B,C REGISTERS
		PUSH	B! CALL PRINTCHAR! POP B! RET
;
CRLF:	MVI		A, CR! CALL PRINTCHAR
		MVI		A, LF! JMP PRINTCHAR
;
PRINT:							;PRINT STRING STARTING AT B,C UNTIL NEXT DOLLAR SYMBOL
		PUSH	B! CALL CRLF! POP D! MVI C, PGUFF! JMP SDOS
;
INITIALIZE:	MVI		C, INITF! JMP BDOS
;
SELECT:	MOV		E, A! MVI C, SELF! JMP BDOS
;
OPEN:							;OPEN THE FILE GIVEN, BY D,E
		MVI		C, OPENF! CALL BDOS! STA DCNT! INR A! RET
;
OPENC:							;OPEN COMFCB
		XRA		A! STA COMREC	;CLEAR NEXT RECORD TO READ
		LXI		D, COMFCB! JMP OPEN
;
CLOSE:							;CLOSE THE  FILE GIVEN BY D,E
		MVI		C, CLOSEF! CALL BDOS! STA DCNT! INR A! RET
;
SEARCH:							;SEARCH FOR THE FILE GIVEN BY D,E
		MVI		C, SEARF! CALL BDOS! STA DCNT! INR A! RET
;
SEARCHN:						;SEARCH FOR THE NEXT OCCURRENCE OF THE FILE GIVEN BY D,E
		MVI		C, SEARNF! CALL BDOS! STA DCNT! INR A! RET
;
SEARCHCOM:						;SEARCH FOR COMFCB FILE
		LXI		D, COMFCB! JMP SEARCH
;
DELETE:							;DELETE THE FILE GIVEN BY D,E
		MVI		C, DELF! JMP BDOS
;
DISKREAD:						;READ THE NEXT RECORD FROM THE FILE GIVEN BY D,E
		MVI		C, DREADF! CALL BDOS! ORA A! RET
;
DISKREADC:						;READ THE COMFCB FILE
		LXI		D, COMFCB! JMP DISKREAD
;
DISKWRITE:						;WRITE THE NEXT RECORD TO THE FILE GIVEN  BY D,E
		MVI		C, DWRITF! CALL BDOS! ORA A! RET
;
MAKE:							;CREATE THE FILE GIVEN BY D,E
		MVI		C, MAKEF! CALL BDOS! STA DCHT! RET
;
RENAM:							;RENAME THE FILE GIVEN BY D,E
		MVI		C, RENF! JMP BDOS
;
TRANSLATE:						;TRANSLATE CHARACTER IN REGISTER A TO UPPER CASE
		CPI		61H! RC			;RETURN IF BELOW LOWER CASE A
		CPI		78H! RNC		;RETURN IF ABOVE LOWER CASE Z
		ANI		5FH! RET		;TRANSLATED TO UPPER CASE
;
READCOM:						;READ THE NEXT COMMAND INTO THE COMMAND BUFFER
								;CHECK FOR SUBMIT FILE
		LDA		SUBMIT! ORA A! JZ NOSUB
								;SCANNING A SUBMIT FILE
								;CHANGE DRIVES TO OPEN AND READ THE FILE
		LDA		CDISK! ORA A! MVI A, 0! CNZ SELECT
		LDA		SUBRC! DCR A	;READ LAST RECORD(S) FIRST
		STA		SUBCR			;CURRENT RECORD TO READ
		LXI		D, SUBFCB! CALL DISKREAD	;END OF FILE IF LAST RECORD
		JNZ		NOSUB
								;DISK READ IS OK, TRANSFER TO COMBUF
		LXI		D, COMLEN! LXI H, BUFF! MVI 8, 128! CALL MOVE0
								;LINE IS TRANSFERRED, CLOSE THE FILE WITH A
								;DELETED RECORD
		LXI		H, SUBRC! DCR M	;ONE LESS RECORD
		LXI		D, SUBFCB! CALL CLOSE! JZ NOSUB
								;CLOSE WENT OK, RETURN TO ORIGINAL DRIVE
		LDA		CDISK! ORA A! CNZ SELECT
		MVI		C, PBUFF! LXI D, COMBUF! CALL BDOS
		CALL	BREAK$KEY! JZ NOREAD
		CALL	DEL$SUB! JMP CCP	;BREAK KEY DEPRESSED
;
NOSUB:							;NO SUBMIT FILE! CALL DEL$SUB
								;TRANSLATE TO UPPER CASE, STORE ZERO AT END
		MVI		C, RBUFF! LXI D, MAXLEN! CALL BDOS
NOREAD:							;ENTER HERE FROM SUBMIT FILE
								;SET THE LAST CHARACTER TO ZERO FOR LATER SCANS
		LXI		H, COMLEN! MOV B, M	;LENGTH IS IN B
READCOM0:	INX		H! MOV A, B! ORA A	;END OF SCAN?
		JZ		READCOM1! MOV A, M	;GET CHARACTER AND TRANSLATE
		CALL	TRANSLATE! MOV M, A! DCR B! JMP READCOM0
;
READCOM1:						;END OF SCAN, H,L ADDRESS END OF COMMAND
		MOV		M, A			;STORE A ZERO
		LXI		H, COMBUF! SHLD COMADDR	;READY TO SCAN TO ZERO
		RET
;
BREAK$KEY:					 	;CHECK FOR A CHARACTER READY AT THE CONSOLE
		MVI		C, BREAKF! CALL BDOS
		ORA		A! RZ
		MVI		C, RCHARF! CALL BDOS	;CHARACTER CLEARED
		ORA		A! RET
;
LIFTHEAD:						;LIFT THE HEAD ON THE SA3900
		MVI		C, LIFTF! JMP BDOS
;
CSELECT:						;GET THE CURRENTLY SELECTED DRIVE HUMBER TO REG-A
		MVI		C, CSELF! JMP BDOS
;
MOVE:							;MOVE DATA FROM D, E TO H,L FOR C BYTES
		LDAX	D! MOV M, A! INX D! INX H! DCR C! JNZ MOVE! RET
;
SETDMA:							;SET DMA ADDRESS TO D,E
		MVI		C, DMAF! JMP BDOS
;
DEL$SUB:						;DELETE THE SUBMIT FILE, AND SET SUBMIT FLAG TO FALSE
		LXI		H, SUBMIT! MOV A, M! ORA A! RZ	;RETURN IF NO SUB FILE
		MVI		M, 0			;SUBMIT FLAG IS SET TO FALSE
		XRA		A! CALL SELECT	;ON DRIVE A TO ERASE FILE
		LXI		D, SUBFCB! CALL DELETE
		LDA		CDISK! JMP SELECT	;BACK TO ORIGINAL DRIVE
;
SERIALIZE:						;CHECK SERIALIZATION
		LXI		D, SERIAL! LXI H, BDOSL! MVI B, 6	;CHECK SIX BYTES
SER0:	LDAX	D! CMP M! JNZ BADSERIAL
		INX		D! INX H! DCR B! JNZ SERB
		RET						;SERIAL NUMBER IS OK
;
COMERR:							;ERROR IN, COMMAND STRING STARTING AT POSITION
								;'STADDR' AND ENDING WITH FIRST DELIMITER
		CALL	CRLF			;SPACE TO NEXT LINE
		LHLD	STADDR			;H,L ADDRESS FIRST TO PRINT
COMERR0:						;PRINT CHARACTERS UNTIL BLANK OR ZERO
		MOV		A, M! CPI ' '! JZ COMERR1	; NOT BLANK
		ORA		A! JZ COMERR1	; NOT ZERO, SO PRINT IT
		PUSH	H! CALL PRINTCHAR! POP H! INK X
		JMP		COMERR0			; FOR ANOTHER CHARACTER
COMERR1:						;PRINT QUESTION, MARK, AND DELETE SUB FILE
		MVI		A, '?'! CALL PRINTCHAR
		CALL	CRLF! CALL DEL$SUB
		JMP		CCP				;RESTART WITH NEXT COMMAND
;
								; FCB SCAN, AND FILL SUBROUTINE (ENTRY IS AT FILLFCB BELOW)
								;FILL THE COMFCB, INDEXED BY A (0 OR 16)
								;SUBROUTINES
DELIM:							;LOOK FOR A DELIMITER
		LDAX	D! ORA A! RZ	;NOT THE LAST ELEMENT
		CPI		' '! JC COMERR	;NON GRAPHIC
		RZ						;TREAT BLANK AS DELIMITER
		CPI		'='! RZ
		CPI		LA! RZ			;LEFT ARROW
		CPI		','! RZ
		CPI		','! RZ
		CPI		';'! RZ
		CPI		'<'! RZ
		CPI		'>'! RZ
		RET						;DELIMITER  NOT FOUND

DEBLANK:						;DEBLANK THE INPUT LINE
		LDAX	D! ORA A! RZ	;TREAT END OF LINE AS BLANK
		CPI		' '! RNZ! INX D! JMP DEBLANK

ADDH:							;ADD A TO H,L
		ADD		L! MOV L, A! RNC
		IHR		H! RET
;
FILLFCB0:						;EQUIVALENT TO FILLFCB(0)
		MVI		A, 0

FILLFCB:	LXI		H, COMFCB! CALL ADDH! PUSH H! PUSH H	;FCB RESCANNED AT END
		XRA		A! STA SDISK	;CLEAR SELECTED DISK (IN CASE A;...)

		LHLD	COMADDR! XCHG	;COMMAND ADDRESS IN D,E
		CALL	DEBLANK			;TO FIRST NON-BLANK CHARACTER
		XCHG!	SHLD STADDR		;IN CASE OF ERRORS
		XCHG!	POP H			;D,E HAS COMMAND, H, L HAS FCB ADDRESS
								;LOOK FOR PRECEDING FILE NAME A, B: ...
		LDAX	D! ORA A! JZ SETCUR0	;USE CURRENT DISK IF EMPTY COMMAND
		SBI		'A'-1! MOV B, A	;DISK NAME HELD IN B IF : FOLLOWS
		INX		D! LDAX D! CPI ', '! JZ SETDSK	;SET DISK NAME IF ,
;
SETCUR:							;SET CURRENT DISK
		DCX		D				;BACK TO FIRST CHARACTER OF COMMAND
SETCUR0:	LDA		CDISK! MOV M, A! JMP SETNAME
;
SETDSK:							;SET DISK TO NAME IN REGISTER B
		MOV		A, B! STA SDISK	;MARK AS DISK SELECTED
		MOV		M, B! INX D		;PAST THE ,
;
SETNAME:						;SET THE FILE NAME FIELD
		MVI		B, 8			;FILE NAME LENGTH (MAX)
SETNAM0:	CALL	DELIM! JZ PADNAME	;NOT A DELIMITER
		INX		H! CPI '*'! JNZ SET NAM1	;MUST BE 7'S
		MVI		M, '7'! JMP SETNAM2	;TO DEC COUNT
;
SETNAM1:	MOV		M, A		;STORE CHARACTER TO FCB! INX D
SETNAM2:	DCR		B			;COUNT DOWN, LENGTH! JNZ SETNAM0
;
								;END OF NAME, TRUNCATE REMAINDER
TRNAME:	CALL	DELIM! JZ SETTY	;SET TYPE FIELD IF DELIMITER
		INX		D! JMP TRNAME
;
PADNAME:	INX		H! MVI M, ' '! DCR B! JNZ PADNAME
;
SETTY:							;SET THE TYPE FIELD
		MVI		B, 3! CPI '.'! JNZ PADTY	;SKIP THE TYPE FIELD IF NO 
		INX		D				;PAST THE , TO THE FILE TYPE FIELD
SETTY0:							;SET THE FIELD FROM THE COMMAND BUFFER
		CALL	DELIM! JZ PADTY! INX H! CPI '*'! JNZ SETTY1
		MVI		M, '7'			;SINCE * SPECIFIED! JMP SETTY2
;
SETTY1:							;NOT A *, SO COPY TO TYPE FIELD
		MOV		M, A! INX D
SETTY2:							;DECREMENT COUNT AND GO AGAIN
		DCR		0! JNZ SETTY0
;
								;END OF TYPE FIELD, TRUNCATE
TRTYP:							;TRUNCATE TYPE FIELD
		CALL	DELIM! JZ EFILL! INX D! JMP TRTYP
;
PADTY:							;PAD THE TYPE FIELD WITH BLANKS
		INX		H! MVI M, ' '! DCR B! JNZ PADTY
;
EFILL:							;END OF THE FILENAME/FILETYPE FILL, SAVE COMMAND ADRESS
								;FILL THE REMAINING FIELDS FOR THE FCB
		MVI		B, 3
EFILL0:	INX		H! MVI M, 0! DCR B! JNZ EFILL0
		XCHG!	SHLD COMADDR	;SET NEW STARTING POINT
;
								;RECOVER THE START ADDRESS OF THE FCB AND COUNT 7'$
		POP		H! LXI B, 11	;B=0, C=8+3
SCNQ:	INX		H! MOV A, M! CPI '?'! JNZ SCNQ0
								;? FOUND, COUNT IT IN, B! INR B
;
								;NUMBER OF ?'S IN C, MOVE TO A AND RETURN, WITH FLAGS
		MOV		A, B! ORA A! RET
;
INTVEC:								;INTRINSIC FUNCTION, NAMES (ALL ARE FOUR CHARACTERS)
		DB		'DIR '
		DB		'ERA '
		DB		'TYPE'
		DB		'SAVE'
		DB		'REN '
		INTLEN	EQU ($-INTVEC)/4	;INTRINSIC FUNCTION, LENGTH
		SERIAL:	DB		0, 0, 0, 0, 0
;
;
INTRINSIC:							;LOOK FOR INTRINSIC FUNCTIONS (COMFCB HAS BEEN FILLED)
		LXI		H, INTVEC! MVI C, 0	;C COUNTS INTRINSICS AS SCANNED
INTRIN0:	MOV		A, C! CPI INTLEN	;DONE WITH SCAN?!! RNC
								;N0, MORE TO SCAN
		LXI		D, COMFCB+1		;BEGINNING OF NAME
		MVI		B, 4			;LENGTH OF MATCH IS IN B
INTRIN1:LDAX	D! CMP M   		;MATCH?
		JNZ		INTRIN2			;SKIP IF NO MATCH
		INX		D! INX H! DCR 0
		JNZ		INTRIN1			;LOOP WHILE MATCHING
;
								;COMPLETE MATCH ON NAME, CHECK FOR BLANK IN FCB
		LDAX	D! CPI ' '! JNZ INTRIN3	;OTHERWISE MATCHED
		MOV		A, C! RET		;WITH INTRINSIC NUMBER IN A
;
INTRIN2:						;MISMATCH, MOVE TO END OF INTRINSIC
		INX		H! DCR B! JNZ INTRIN2
;
INTRIN3:						;TRY NEXT INTRINSIC
		INR		C				;TO NEXT INTRINSIC NUMBER
		JMP		INTRIN0			;FOR ANOTHER ROUND
;
CHECKSUB:						;CHECK FOR A SUBMIT FILE (REGISTER A HAS DISK NAME)
		ORA		A! JNZ SETSUB0	;CLEAR SUBMIT IF NOT DISK A
								;OPEN SUBMIT FILE - IF POSSIBLE
		LXI		D, SUBFCB! CALL OPEN! JZ SETSUB0
								;SUBMIT FILE OPENED, SET SUBMIT TO TRUE
		MVI		A, 0FFH! JMP SETSUB
;
SETSUB0:	XRA		A				;CLEAR SUBMIT VARIABLE
SETSUB:	STA		SUBMIT
		RET
;
CCPSTART:						;ENTER HERE FROM BOOT LOADER
		LXI		SP, STACK! PUSH B! PUSH 8	;SAVE INITIAL DISK NUMBER
		CALL	INITIALIZE! POP B	;RECOVERED DISK NUMBER TO C
		PUSH	PSW				;SAVE $ FLAG FROM INITIALIZATION, (00 IF NO $ FILE)
		MOV		A, C			;DISK NUMBER IN ACCUMULATOR
		CALL	SELECT			;PROPER DISK IS SELECTED, NOW CHECK SUB FILES
		POP		PSW				;RECOVER $ FILE FLAG FROM INITIALIZATION
		POP		B				;ORIGINAL DISK NUMBER IS IN C
		INR		A				;00 BECOMES 01, FF BECOMES 00! ORA C ;OR DISK NUMBER
								;ACCUMULATOR CONTAINS 00 IF ON DISK 0, AND $ FILE PRESENT
								;OTHERWISE, THE ACCUMULATOR CONTAINS A NON-ZERO VALUE
		CALL	CHECKSUB
;
CCP:							;ENTER HERE ON EACH COMMAND OR ERROR CONDITION
		LXI		SP, STACK
		CALL	CRLF			;PRINT D> PROMPT, WHERE D IS DISK NAME
		CALL	CSELECT!STA CDISK! ADI 'A'! CALL PRINTCHAR
		MVI		A, '>'! CALL PRINTCHAR
		LXI		D, BUFF! CALL SETDMA	;DEFAULT DMA ADDRESS IS AT BUFF
		CALL	READCOM! CALL FILLFCB0
		CNZ		COMERR			;THE NAME CANNOT BE AN AMBIGUOUS REFERENCE
		LDA		SDISK! ORA A! JNZ USERFUNC
								;CHECK FOR AN INTRINSIC FUNCTION
		CALL	INTRINSIC
		LXI		H, JMP TAB		;INDEX IS IN THE ACCUMULATOR
		MOV		E, A! MVI D, 0! DAD D! DAD D	;INDEX IN D,E
		MOV		A, M! INX H! MOV H, M! MOV L, A! PCHL
								;PC CHANGES TO THE PROPER INTRINSIC OR USER FUNCTION
JMPTAB:	DW		DIRECT			;DIRECTORY SEARCH
		DW		ERASE			;FILE ERASE
		DW		TYPE			;TYPE FILE
		DW		SAVE			;SAVE MEMORY IMAGE
		DW		RENAME			;FILE RENAME
		DW		USERFUNC		;USER-DEFINED FUNCTION
BADSERIAL:	LXI		H, DI OR (HLT $HL 0)
		SHLD	CCPLOC! LXI H, CCPLOC! PCHL
;
								;UTILITY SUBROUTINES FOR INTRINSIC HANDLERS
READERR:						;PRINT THE READ ERROR MESSAGE
		LXI		B, RDMSG! JMP PRINT
RDMSG:	DB		'READ ERROR$'
;
NOFILE:							;PRINT NO FILE MESSAGE
		LXI		B, NOFMSG! JMP PRINT
NOFMSG:	DB		'NOT FOUND$'
;
CONVERT:						;CONVERT THE BYTE VALUE IN COMFCB TO BINARY
		LXI		H, COMFCB+1! LXI B, 11	;(B=0, C=11)
								;VALUE ACCUMULATED IN B, C COUNTS NAME LENGTH TO ZER0
CONV0:	MOV		A, M! CPI ' '! JZ CONV1
								;MORE TO SCAN, CONVERT CHAR TO BINARY AND ADD
		INX		H! SUI '0'! CPI 10! JNC COMERR	;VALID?
		MOV		D, A			;SAVE VALUE! MOV A,B ;MULT BY 10
		ANI		1110$0000B! JNZ COMERR
		MOV		A, B			;RECOVER VALUE
		RLC!	RLC! RLC		;*8
		ADD		B! JC COMERR
		ADD		B! JC COMERR	;*8+*2 = *10
		ADD		D! JC COMERR	;+DIGIT
		MOV		B, A! DCR C! JNZ CONV0	;FOR ANOTHER DIGIT
		RET
CONV1:							;END OF DIGITS, CHECK FOR ALL BLANKS
		MOV		A, M! CPI ' '! JNZ COMERR	;BLANKS?
		INX		H! DCR C! JNZ CONV1
		MOV		A, B			;RECOVER VALUE! RET
;
MOVENAME:						;MOVE 3 CHARACTERS FROM H,L TO D,E ADDRESSES
		MVI		B, 3
MOVE0:	MOV		A, M! STAX D! INX H! INX D
		DCR		B! JNZ MOVE0
		RET
;
ADDHCF:							;BUFF + A + C TO H,L FOLLOWED BY FETCH
		LXI		H, BUFF! ADD C! CALL ADDH! MOV A, M! RET
;
SETDISK:						;CHANGE DISKS FOR THIS COMMAND, IF REQUESTED
		XRA		A! STA COMFCB	;CLEAR DISK NAME FROM FCB
		LDA		SDISK! ORA A! RZ	;NO ACTION IF NOT SPECIFIED
		DCR		A! LXI H, CDISK! CMP M! RZ	;ALREADY SELECTED
;
RESETDISK:						;RETURN TO ORIGINAL DISK AFTER COMMAND
		LDA		SDISK! ORA A! RZ	;NO  ACTION IF NOT SELECTED
		DCR		A! LXI H, CDISK! CMP M! RZ	;SAME DISK
		LDA		CDISK! JMP SELECT
;
								;INDIVIDUAL INTRINSICS FOLLOW
DIRECT:							;DIRECTORY SEARCH
		CALL	FILLFCB0		;COMFCB GETS FILE NAME
		CALL	SETDISK			;CHANGE DISK DRIVES IF REQUESTED
		LXI		H, COMFCB+1! MOV A, M	;MAY BE EMPTY REQUEST
		CPI		' '! JNZ DIR1	;SKIP FILL OF ??? IF NOT 8LANK
								;SET COMFCB TO ALL ??? FOR CURRENT DISK
		MVI		B, 11			;LENGTH OF FILL ????????,???
DIR0:	MVI		M, '?'! INX H! DCR B! JNZ DIR0
								;NOT A BLANK REQUEST, MUST BE IN COMFCB
DIR1:	CALL	SEARCHCOM		;FIRST ONE HAS BEEN FOUND
		CZ		NOFILE			;NOT FOUND MESSAGE
DIR2:	JZ		ENDIR
		CALL	CRLF
								;ANOTHER FCB FOUND, PRINT PROMPT
		CALL	CSELECT! ADI 'A'! CALL PRINTCHAR
		MVI		A, ':'! CALL PRINTCHAR
		MVI		A, ' '! CALL PRINTCHAR
								;COMPUTE POSITION OF NAME IN BUFFER
		LDA		DCNT			;GET THE LOCATION OF THE ELEMENT
		RRC!	RRC! RRC! ANI 110$0000B! MOV C, A
								;C CONTAINS BASE INDEX INTO BUFF FOR DIR ENTRY
		MVI		B, 1			;START WITH FIRST CHARACTER OF NAME
DIR3:	MOV		A, B! CALL ADDHCF	;BUFF+A+C FETCHED
								;DELETE TRAILING BLANKS
		CPI		' '! JNZ DIR4	;CHECK FOR BLANK TYPE
		MVI		A, 9! CALL ADDHCF	;FIRST CHAR OF TYPE
		MVI		A, ' '! CMP M! JZ DIR5	;TERMINATE
DIR4:	CALL	PRINTBC			;CHAR PRINTED
		INR		B! MOV A, B! CPI 12! JNC DIR5
								;CHECK FOR BREAK BETWEEN NAMES
		CPI		9! JNZ DIR3		;FOR ANOTHER CHAR
								;PRINT A BLANK BETWEEN NAMES
		MVI		A,' '! CALL PRINTBC! JMP DIR3
;
DIR5:							;END OF CURRENT ENTRY
		CALL	BREAK$KEY		;CHECK FOR INTERRUPT AT KEYBOARD
		JNZ		ENDIR			;ABORT DIRECTORY SEARCH
		CALL	SEARCHN! JMP DIR2	;FOR ANOTHER ENTRY
ENDIR:							;END OF DIRECTORY SCAN
		JMP		RET COM
;
;
ERASE:	CALL	FILLFCB0		;CANNOT BE ALL ???'S
		CPI		11
		JNZ		ERASEFILE
								;ERASING ALL OF THE DISK
		LXI		B, ERMSG! CALL PRINT!
		CALL	READCOM
		LXI		H, COMLEN! DCR M! JNZ CCP	;BAD INPUT
		INX		H! MOV A, M! CPI 'Y'! JNZ CCP
								;OK, ERASE THE ENTIRE DISKETTE
		INX		H! SHLD COMADDR	;OTHERWISE ERROR AT RETCOM
		LXI		H, COMFCB! MVI M, '?'

ERASEFILE:	CALL	SETDISK
		LXI		D, COMFCB! CALL DELETE
		JMP		RETCOM
;
ERMSG:	DB		'ALL FILES (Y/N)?$'
;
TYPE:							
		CALL	FILLFCB0! JNZ COMERR	;DON'T ALLOW ?'S IN FILE NAME
		CALL	SETDISK! CALL OPENC		;OPEN THE FILE
		JZ		TYPERR					;ZERO FLAG INDICATES NOT FOUND
										;FILE OPENED, READ 'TIL EOF
		CALL	CRLF! LXI H, BPTR! MVI M, 255	;READ FIRST BUFFER
TYPE0:									;LOOP ON BPTR
		LXI		H, BPTR! MOV A, M! CPI 128	;END BUFFER
		JC		TYPE1! PUSH H			;CARRY IF B, 1,...,127
										;READ ANOTHER BUFFER FULL
		CALL	DISKREADC! POP H		;RECOVER ADDRESS CF BPTR
		JNZ		TYPEOF					;HARD END OF FILE
		XRA		A! MOV M, A				;BPTR = 0
TYPE1:									;READ CHARACTER AT BPTR AND PRINT
		INR M							;BPTR = BPTR + 1
		LXI		H, BUFF! CALL ADDH		;H,L ADDRESSES CHAR
		MOV		A, M! CPI EOFILE! JZ RETCOM
		CALL	PRINTCHAR
		CALL	BREAK$KEY! JNZ RETCOM	;ABORT IF BREAK
		JMP		TYPE0					;FOR ANOTHER CHARACTER
;
TYPEOF:							;END OF FILE, CHECK FOR ERRORS
		DCR		A! JZ RETCOM
		CALL	READERR
TYPERR:	CALL	RESETDISK! JMP COMERR
;
SAVE:	CALL	FILLFCB0		;SHOULD BE NUMBER
		LDA		SDISK! ORA A! JNZ COMERR	;CANNOT BE PREFIXED
		CALL	CONVERT			;RESULTING VALUE IN REGISTER A
		PUSH	PSW				;SAVE IT FOR LATER
;
								;SHOULD BE FOLLOWED BY A FILE TO SAVE THE MEMORY IMAGE
		CALL	FILLFCB0
		JNZ		COMERR			;CANNOT BE AMBIGUOUS
		CALL	SETDISK			;MAY BE A DISK CHANGE
		LXI		D, COMFCB! PUSH D! CALL DELETE	;EXISTING FILE REMOV
		POP		D! CALL MAKE	;CREATE A NEW FILE ON DISK
		CALL	OPENC			;OPEN THE FILE
		JZ		SAVERR			;NO DIRECTORY SPACE
		POP		PSW				;#PAGES TO WRITE IS IN A, CHANGE TO #SECTORS
		MOV		L, A! MVI H, 0! DAD H!
		LXI		D, TRAN			;H,L IS SECTOR COUNT, D,E IS LOAD ADDRESS
SAVE0:							;CHECK FOR SECTOR COUNT ZERO
		MOV		A, H! ORA L! JZ SAVE1	;MAY BE COMPLETED
		DCX		H				;SECTOR COUNT = SECTOR COUNT - 1
		PUSH	H				;SAVE IT FOR NEXT TIME AROUND
		LXI		H, 128! DAD C! PUSH H	;NEXT DMA ADDRESS SAVED
		CALL	SETDMA			;CURRENT DMA ADDRESS SET
		LXI		D, COMFCB! CALL DISKWRITE
		POP		D! POP H		;DMA ADDRESS, SECTOR COUNT
		JNZ		SAVERR			;MAY BE DISK FULL CASE
		JMP		SAVE0			;FOR ANOTHER SECTOR
;
SAVERR:							;MUST BE FULL DISK
		LXI		B, FULLMSG! CALL PRINT
SAVE1:							;END OF DUMP, CLOSE THE FILE
		LXI		D, COMFCB! CALL CLOSE
		JNZ		SAVE2			;OTHERWISE CANNOT CLOSE FILE
		LXI		B, CLOSMSG! CALL PRINT
SAVE2:	JMP		RETCOM
FULLMSG:	DB		'NO SPACE$'
CLOSMSG:	DB		'CANNOT CLOSE$'
RENAME:							;RENAME A FILE ON A SPECIFIC DISK
		CALL	FILLFCB0! JNZ COMERR	;MUST BE UNAMBIGUOUS
		LDA		SDISK! PUSH PSW	;SAVE FOR LATER COMPARE
		CALL	SETDISK			;DISK SELECTED
		CALL	SEARCHCOM		;IS NEW NAME ALREADY THERE?
		JNZ		RENERR3
								;FILE DOESN'T EXIST, MOVE TO SECOND HALF OF FCB
		LXI		H, COMFCB! LXI D, COMFCB+16! MVI B, 16! CALL MOVE0
								;CHECK FOR = OR LEFT ARROW
		LHLD	COMADDR! XCHG! CALL DEBLANK
		CPI		'='! JZ REN1	;OK IF =
		CPI		LA! JNZ RENERR2
REN1:	XCHG!	INX H! SHLD COMADDR	;PAST DELIMITER
								;PROPER DELIMITER FOUND
		CALL	FILLFCB0! JNZ RENERR2
								;CHECK FOR DRIVE CONFLICT
		POP		PSW! MOV B, A	;PREVIOUS DRIVE NUMBER
		LXI		H, SDISK! MOV A, M! ORA A! JZ REN2
								;DRIVE NAME WAS SPECIFIED, SAME ONE?
		CMP		B! MOV M, B ! JNZ RENERR2
REN2:	MOV		M, B			;STORE THE NAME IN CASE DRIVES SWITCHED
		XRA		A! STA COMFCB! CALL SEARCHCOM	;IS OLD FILE THERE?
		JZ		RENERR1
;
								;EVERYTHING IS OK, ;RENAME THE FILE
		LXI		D, COMFCB! CALL RENAM
		JMP		RETCOM
;
RENERR1:						; NO FILE ON DISK
		CALL	NOFILE! JMP RETCOM
RENERR2:						; AMBIGUOUS REFERENCE/NAME CONFLICT
		CALL	rESETDISK! JMP COMERR
RENERR3:						; FILE ALREADY EXISTS
		LXI		B, RENMSG! CALL PRINT! JMP RETCOM
RENMSG:	DB		'FILE EXISTS$'
;
USERFUNC:	CALL	SERIALIZE	;CHECK SERIALIZATION
								;LOAD USER FUNCTION, AND SET UP FOR EXECLUSION
		LDA		COMFCB+1! CPI ' '! JNZ USER0
								;NO FILE NAME, BUT MAY BE DISK SWITCH
		LDA		SDISK! ORA A! JZ ENDCOM	;NO DISK NAME IF 0
		DCR		A! STA CDISK! STA DISKA
		CALL	SELECT! JMP ENDCOM
USER0:							;FILE NAME IS PRESENT
		LXI		D, COMFCB+9! LDAX D! CPI ' '! JNZ COMERR	;TYPE ' '
		PUSH	D! CALL SETDISK! POP D! LXI H, COMTYPE	;COM
		CALL	MOVENAME		;FILE TYPE IS SET TO COM
		CALL	OPENC! JZ USERER
								;FILE OPENED PROPERLY, READ IT INTO MEMORY
		LXI		H, TRAN			;TRANSIENT PROGRAM BASE
LOAD0:	PUSH	H				;SAVE DMA ADDRESS
		XCHG!	CALL SETDMA
		LXI		D, COMFCB! CALL DISKREAD! JNZ LOAD1
								;SECTOR LOADED, SET NEW DMA ADDRESS AND COMPARE
		POP		H! LXI D, 128! DAD D
		LXI		D, TRANM		;HAS THE LOAD OVERFLOWED?
		MOV		A, L! SUB E! MOV A, H! SBB D! JNC LOADERR
		JMP		LOAD0			;FOR ANOTHER SECTOR
;
LOAD1:	POP		H! DCR A! JNZ LOADERR	;END FILE IS 1
		CALL	RESETDISK		;BACK TO ORIGINAL DISK
		CALL	FILLFCB0! LXI H, SDISK! PUSH H
		MOV		A, M! STA COMFCB	;DRIVE NUMBER SET
		MVI		A,16! CALL FILLFCB	;MOVE ENTIRE FCB TO MEMO
		POP		H! MOV A, M! STA COMFCB+16
		XRA		A! STA COMREC	;RECORD NUMBER SET TO ZERO
		LXI		D, FCB! LXI H, COMFCB! MVI B, 33! CALL MOVE0
								;MOVE COMMAND LINE TO BUFF
		LXI		H, COMBUF
BMOVE0:	MOV		A, M! ORA A! JZ BMOVE1! CPI ' '! JZ BMOVE1
		INX		H! JMP BMOVE0	;FOR ANOTHER SCAN
								;FIRST BLANK POSITION FOUND
BMOVE1:	MVI		B, 0! LXI D, BUFF+1!	;READV FOR THE MOVE
BMOVE2:	MOV		A, M! STAX D! ORA A! JZ BMOVE3
								;MORE TO MOVE
		INR		B! INX H! INX D! JMP BMOVE2
BMOVE3:							;B HAS CHARACTER COUNT
		MOV		A, B! STA BUFF
		CALL	CRLF
									;NOW GO TO THE LOADED PROGRAM
		LDA		CDISK! STA DISKA	;CURRENT DISK AT DISKA
		CALL	LIFTHEAD! LXI D, BUFF! CALL SETDMA
		CALL	TRAN				;GONE TO THE LOADED PROGRAM
		LXI		SP, STACK			;MAY COME BACK HERE
		LDA		CDISK! CALL SELECT
		JMP		CCP
;
USERER:							;ARRIVE HERE ON COMMAND ERROR
		CALL	RESETDISK! JMP COMERR
;
LOADERR:						;CANNOT LOAD THE PROGRAM
		LXI		B, LOADMSG! CALL PRINT
		JMP		RETCOM
LOADMSG:	DB		'LOAD ERROR$'
COMTYPE:	DB		'COM'	   	;FOR COM FILES
;
;
RETCOM:							;RESET DISK BEFORE END OF COMMAND CHECK
		CALL	RESETDISK
;
ENDCOM:							;END OF INTRINSIC COMMAND
		CALL	FILLFGB0		;TO CHECK FOR GARBAGE AT END OF LINE
		LDA		COMFCB+1! SUI ' '! LXI H, SDISK! ORA M
								;0 IN ACCUMULATOR IF NO DISK SELECTED, AND BLANK FCB
		JNZ		COMERR
		JMP		CCP
;
;
;
;		DATA AREAS
		DS		16				;8 LEVEL STACK
STACK:
;
								;'SUBMIT' FILE CONTROL BLOCK
SUBMIT:	DB		0				;00 IF NO SUBMIT FILE, FF IF SUBMITTING
SUBFCB:	DB		0, '$$$ '		;F1LE NAME IS $$$
		DB		'SUB', 0, 0, 0	;FILE TYPE IS SUB
SUBRC:	DS		1				;RECORD COUNT FILED
		DS		16				;DISK MAP
SUBCR:	DS		1				;CURRENT RECORD TO READ
;
								;COMMAND FILE CONTROL BLOCK
COMFCB:	DS		32				;FIELDS FILLED IN LATER
COMREC:	DS		1				;CURRENT RECORD TO READ/WRITE
;
								;COMMAND BUFFER
MAXLEN:	DB		1 7				;MAX BUFFER LENGTH
COMLEN:	DB		0				;COMMAND LENGTH (FILLED IN BY DOS)
COMBUF:	DS		128				;COMMAND BUFFER
COMADDR:DS		2				;ADDRESS OF NEXT TO SCAN
STADDR:	DS		2				;STARTING ADDRESS OF CURRENT F1LLFCB REQUEST
;
DCNT:	DS		1				;DISK DIRECTORY COUNT (USED FOR ERROR CODES)
CDISK:	DS		1				;CURRENT DISK
SDISK:	DS		1				;SELECTED DISK FOR CURRENT OPERATION
								;NONE=0, A=1, B=2 ...
SPTR:	DS		1				;BUFFER POINTER
		END		CCPLOC
