;***************************************************************************
;
;			C P / M    3 . 1 ( + )
;
;		Reconstructed from disk file image by SCG31.
;		All labels and comments Copyright (c) 1984 by C.C. Software.
;		Instructions Copyright (c) by Digital Research.
;
;	CP/M is a trademark of Digital Research, Inc.
;
;***************************************************************************
;
;	Define external memory references.
;
ABT$CHR	EQU	3	     ;Control-C used as an abort character.
TAB	EQU	9	     ;Tab character.
LF	EQU	10	     ;Line feed.
CR	EQU	13	     ;Carriage return characte.
CPM$DRIVE	EQU	4	     ;Current drive and user number storage byte.
CPM	EQU	5	     ;Main entry point for the BDOS functions.
TP$DRIVE	EQU	50H	     ;Transiant program loaded from this drive.
PASSWD1	EQU	51H	     ;Address of password for first file.
NCHARS1	EQU	53H	     ;Number of characters in PASSWD1.
PASSWD2	EQU	54H	     ;Address of password fo second file.
NCHARS2	EQU	56H	     ;Number of characters in PASSWD2.
DFT$FCB	EQU	5CH	     ;First default FCB.
SECOND$FCB	EQU	6CH	     ;Second default FCB.
DFT$BUFFER	EQU	80H	     ;Default daata i/o buffer.
;
;	File Control Block (FCB) definitions.
;
DR	EQU	0	     ;Drive byte code.
F1	EQU	1	     ;File name, byte 1.
F2	EQU	2	     ;File name, byte 2.
F3	EQU	3	     ;File name, byte 3.
F4	EQU	4	     ;File name, byte 4.
F5	EQU	5	     ;File name, byte 5.
F6	EQU	6	     ;File name, byte 6.
F7	EQU	7	     ;File name, byte 7.
F8	EQU	8	     ;File name, byte 8.
T1	EQU	9	     ;File type, byte 1.
T2	EQU	10	     ;File type, byte 2.
T3	EQU	11	     ;File type, byte 3.
EX	EQU	12	     ;File extent byte.
S1	EQU	13
S2	EQU	14
RCNT	EQU	15	     ;Record count, this extent.
D0	EQU	16	     ;Data block storage.
D1	EQU	17
D2	EQU	18
D3	EQU	19
D4	EQU	20
D5	EQU	21
D6	EQU	22
D7	EQU	23
D8	EQU	24
D9	EQU	25
D10	EQU	26
D11	EQU	27
D12	EQU	28
D13	EQU	29
D14	EQU	30
D15	EQU	31
REC	EQU	32	     ;Current record number.
R0	EQU	33	     ;Random record number, byte 0.
R1	EQU	34	     ;Random record number, byte 1.
R2	EQU	35	     ;Random record number, byte 2.
;
;	Include the following lines if the loader is separated from
;	the CCP which needs to reference these locations.
;
;	PUBLIC	SETUP,SET$NEXT,REMOVE,RELOCATE
;	PUBLIC	CHECKMEM,SCB$BASE,COMMON$BASE,RSX$BASE
;
;
;	The ccp is a combination of a relocatable loader (in RSX format)
;	and an absolute command processor. The first part is the loader.
;	This will be relocated to highest ram (by itself) where it will
;	process the "load overlay" bdos function #59.
;
TPA$BASE:
	JP	INIT	     ;Jump to initialize code.
RSX$INIT:NOP		     ;Changed into a jump after relocation.
	NOP	
	NOP	
	JP	XBDOS
GO$BDOS:JP	6	     ;Jump to next RSX (address filled by the loader).
PREVIOUS:DEFW	7	     ;Preious RSX address in chain.
	DEFB	0	     ;Remove flag (0=dont remove).
	DEFB	0	     ;Non-bank flag (always load).
	DEFB	'LOADER  '   ;Name of loader RSX procedure.
	DEFB	255	     ;Loader signal flag (last RSX in chain).
	DEFW	00000H
;
;***************************************************************************
;
;	System loader. This resides in highest ram to process the load-overlay
;	function. This is a special RSX procedure and can load a COM or PRL
;	type file and associated RSX procedures (relocating as necessary).
;
;***************************************************************************
;
;	Filter BDOS calls. Look for LOAD OVERLAY function (#59) and process
;	this ourselves.
;
;	This represents the system loader. The CCP will move this into
;	highest ram memory and there it resides until the next warm boot.
;	The code from 100 hex to 394 hex is a standard PRL type file in
;	RSX format (the relocation bit map immediately follows the code).
;
XBDOS:	LD	A,C
	CP	59	     ;Load overlay (#59)?
	JP	NZ,GO$BDOS   ;Nope, just let the BDOS handle it.
	POP	BC	     ;Okay, get return address.
	PUSH	BC
	LD	HL,0
	ADD	HL,SP	     ;Place it on our stack.
	LD	SP,TMPSTACK
	LD	(OLDSTACK),HL;Save users stack pointer.
	PUSH	BC
	EX	DE,HL
	LD	(FCB$ADR),HL ;Store the address of the fcb to load.
	LD	A,H	     ;If zero, then remove all RSX's.
	OR	L
	PUSH	AF
	CALL	Z,REMOVE
	POP	AF
	CALL	NZ,LOADER    ;Otherwise, load this new procedure.
	POP	DE
	LD	HL,TPA$BASE  ;Check for an RSX header (pg-19).
	LD	A,(HL)
	CP	0C9H	     ;Yes if just a return is present.
	JP	Z,LOAD$NEXT
	LD	A,D	     ;Check return address.
	DEC	A	     ;0100 hex?
	OR	E
	JP	NZ,XRET
	LD	A,(TPA$BASE+13);Yes...
	OR	A
	JP	NZ,XRET
	LD	HL,(GO$BDOS+1);Reset the tpa space for this extra procedure.
	LD	(6),HL
	LD	(RSX$BASE),HL
	CALL	SET$TPA
XRET:	LD	HL,(OLDSTACK);Restore users stack pointer.
	LD	SP,HL
	XOR	A	     ;Clear result registers and return.
	LD	L,A
	LD	H,A
	RET	
;
;	Set error return code.
;
LOAD$ERR:LD	DE,0FEH
;
;	Return location. Enter here with return code in (de).
;
LDR$RET:LD	HL,(OLDSTACK)
	LD	SP,HL
	POP	HL
	PUSH	HL
	DEC	H	     ;Is there a valid return address?
	LD	A,H	     ;Note that 0100 would not be valid.
	OR	L
	EX	DE,HL
	LD	A,L
	LD	B,H
	RET	NZ
;
;	Load error. Program will not fit into memory.
;
TOOBIG:	LD	C,9	     ;Can not load program (not enough memory).
	LD	DE,BADLOAD
	CALL	CPM
	JP	0
;
;	Load an RSX procedure.
;
LOAD$RSX:
	INC	HL	     ;Extract length of RSX.
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	LD	A,(COMMON$BASE);Check common memory base (banked system?).
	OR	A
	JP	Z,INSTALL
	INC	HL	     ;Yes, do we load this RSX?
	INC	(HL)
	JP	Z,SKIP$RSX
;
;	Load RSX into highest memory.
;
INSTALL:PUSH	DE
	CALL	CHECKMEM     ;Make sure it will fit.
	POP	HL
	CALL	RELOCATE     ;Move it into position and fit addresses.
	CALL	SETUP	     ;Setup pointers to next RSX.
SKIP$RSX:POP	HL
;
;	Load next RSX procedure.
;
LOAD$NEXT:LD	DE,16	     ;Get address of PRL portion of proedure.
	ADD	HL,DE
	PUSH	HL
	LD	E,(HL)
	INC	HL
	LD	D,(HL)	     ;Is there another RSX to handle?
	LD	A,E
	OR	D
	JP	NZ,LOAD$RSX  ;Yes...
	CALL	RSX$INIT     ;Done, some kind of initialization?
	LD	A,(TPA$BASE+100H);Look for a null COM file.
	CP	0C9H	     ;They start with a RET instruction.
	JP	NZ,LOAD01
	LD	HL,(SCB$BASE);No COM file, set flag bit #1
	LD	L,0B3H	     ;such that the CCP does not remove this
	LD	A,(HL)	     ;when we return.
	OR	2
	LD	(HL),A
LOAD01:	LD	HL,(TPA$BASE+1);The PRL file starts at 200, move it down
	LD	B,H	     ;to 100 hex.
	LD	C,L	     ;Byte count.
	LD	HL,200H	     ;Source.
	LD	DE,100H	     ;Destination.
	CALL	BLKMOVE	     ;Move it.
	JP	XRET	     ;All done.
;
;	Setup the chain bytes (and flag bytes) for an RSX procedure.
;	Each RSX has address pointers to the procedures in front and in
;	back.
;
SETUP:	
	LD	HL,(6)	     ;Set the first 6 bytes (this was the
	LD	L,0	     ;the serial number area.
	LD	BC,6	     ;Move 6 bytes from (hl) to (de).
	CALL	BLKMOVE
	LD	E,018H	     ;Clear loader flag byte.
	LD	(DE),A
	LD	E,CR	     ;Set address of previous RSX (if any).
	LD	(DE),A
	DEC	DE
	LD	A,7
	LD	(DE),A
	LD	L,E	     ;Fill in previous address to point to
	LD	E,0BH	     ;this RSX.
	LD	(HL),E
	INC	HL
	LD	(HL),D
SET$NEXT:
	EX	DE,HL	     ;Fill in pointer to next RSX (or the BDOS
	LD	(HL),D	     ;if this is the last one).
	DEC	HL
	LD	(HL),6
SET$BASE:LD	L,6	     ;Set low address to point back to current
	LD	(6),HL	     ;RSX procedure.
	LD	(RSX$BASE),HL
SET$TPA:LD	DE,SCBPBLDR  ;Store base of TPA in BDOS table (SCB).
ACC$SCB:LD	C,49
	JP	CPM
;
;	Remove all completed RSX procedures. Note that only the
;	lowest ones can actually be removed from the memory list.
;
REMOVE:	LD	HL,(6)
	LD	B,H
REMOVE1:LD	H,B	     ;Check the LOADER flag.
	LD	L,018H
	INC	(HL)	     ;<>0 and we reached the last one.
	DEC	(HL)
	RET	NZ
	LD	L,0BH	     ;Get page of next RSX.
	LD	B,(HL)
	LD	L,0EH	     ;Check the REMOVE flag byte.
	LD	A,(HL)	     ;=0 and we will not remove it.
	OR	A
	JP	Z,REMOVE1
	LD	L,0CH	     ;Okay, get address of previous RSX.
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	LD	A,B	     ;Make it point to the next one (this
	LD	(DE),A	     ;effectively removes the present RSX).
	DEC	DE
	LD	A,6
	LD	(DE),A
	INC	DE	     ;Make the next RSX point backward to the
	LD	H,B	     ;presious one. This completes the pointer
	LD	L,0CH	     ;chain.
	LD	(HL),E
	INC	HL
	LD	(HL),D
	LD	A,D	     ;Is this the bottom one?
	OR	A
	PUSH	BC
	CALL	Z,SET$BASE   ;Yes, set the upper TPA address.
	POP	BC
	JP	REMOVE1
;
;	File loader. This will read the file into memory and then relocate
;	it (PRL files only). Note that this will determine the maximum
;	number of sectors that can be read each time to speed the loading.
;
LOADER:	PUSH	HL
	LD	DE,GET$DMA   ;Get current DMA address.
	CALL	ACC$SCB
	EX	DE,HL
	POP	HL
	PUSH	HL
	LD	BC,REC	     ;Set record number in fcb to 0.
	ADD	HL,BC
	LD	(HL),0
	INC	HL
	LD	C,(HL)	     ;Get load address.
	INC	HL
	LD	H,(HL)
	LD	L,C
	DEC	H	     ;Less than 100 hex?
	INC	H
	JP	Z,LOAD$ERR   ;Yes, this is not allowed.
	PUSH	HL
	PUSH	DE
	PUSH	HL
	CALL	SET$ONE	     ;Get current multi-sector count
	POP	HL
	PUSH	AF	     ;and save it so we can restore this value
	LD	E,080H	     ;when we finish. Prepare for large blocks.
LD$NXT01:LD	A,(7)	     ;Compute max number of sectors to be read
	DEC	A	     ;at any one time.
	SUB	H
	JP	C,SMALL$SIZE ;Only 1 page available?
	INC	A
	CP	64	     ;Space >64 pages (16k)
	JP	NC,LD$NXT03
	RLCA		     ;Nope, compute max number of sectors to
	LD	E,A	     ;read.
	LD	A,L
	OR	A	     ;Load address on a page boundry?
	JP	Z,LD$NXT03
	LD	B,2	     ;Nope, adjust for this address.
	DEC	A
	JP	M,LD$NXT02
	DEC	B	     ;Only one sector off.
LD$NXT02:LD	A,E	     ;Reducce the secor count in (e) by (b).
	SUB	B
	JP	Z,SMALL$SIZE ;Only a small area available?
	LD	E,A	     ;Reset sector count.
LD$NXT03:PUSH	DE
	PUSH	HL
	CALL	SET$MULTI    ;Set sector count to (e) and read the file.
	POP	HL
	PUSH	HL
	CALL	RDNEXT	     ;Read next record(s).
	POP	HL
	POP	DE
	PUSH	AF	     ;Save status.
	LD	A,E	     ;Update memory pointer by (e) sectors.
	INC	A
	RRA	
	ADD	A,H
	LD	H,A
	LD	(FSIZE),HL   ;Save next available address.
	POP	AF
	JP	Z,LD$NXT01   ;Continue until end or error occurs.
LD$NXT04:POP	BC	     ;Note that we pushed (a) above.
	DEC	A	     ;Set zero on end of file.
	LD	E,B	     ;Reset multi-sector count to what user had
	CALL	SET$MULTI    ;prior to loading this last file.
	LD	C,26	     ;Set DMA address to what we had originally.
	POP	DE
	PUSH	AF	     ;Save read status flag.
	CALL	CPM
	POP	AF
	LD	HL,(LDR$STAT);Get error return code.
	EX	DE,HL
	JP	NZ,LDR$RET   ;Return on a read error.
	POP	DE	     ;Read was okay, check for a PRL file.
	POP	HL
	LD	BC,9	     ;Check extension, return if not PRL.
	ADD	HL,BC
	LD	A,(HL)
	AND	07FH
	CP	'P'	     ;A "P"?
	RET	NZ
	INC	HL
	LD	A,(HL)
	AND	07FH
	CP	'R'	     ;An "R"?
	RET	NZ
	INC	HL
	LD	A,(HL)
	AND	07FH
	SUB	'L'	     ;Then an "L"?
	RET	NZ
	LD	A,E	     ;Check load address, it must be a page boundry.
	OR	A
	JP	NZ,LOAD$ERR
	LD	H,D	     ;All was okay. Relocate this file
	LD	L,E	     ;from low memory to high and change all
	INC	HL	     ;needed addresses via bit map.
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	LD	L,E
;
;	Relocation routine. Memory from (hl)+100 will be moved
;	to memory at (de), (bc) number of bytes. Immediately
;	following the original code is bit map table. Starting
;	from bit 7 to 0, each on bit represents a byte that must
;	be adjusted (the upper half of an address). This is a cute
;	routine.
;
RELOCATE:
	INC	H	     ;Bump source address by 100h.
	PUSH	DE
	PUSH	BC
	CALL	BLKMOVE	     ;Move the code now.
	POP	BC
	POP	DE
	PUSH	DE
	LD	E,D	     ;Compute relocation offset value.
	DEC	E
	PUSH	HL
	LD	H,E	     ;(h) is the offset value to use.
;
;	Relocate the code that (de) points to. The address
;	of the bit map table is on the stack. The code is (bc)
;	bytes long.
;
	LD	E,0	     ;Point to start of code.
RELMEM:	LD	A,B	     ;More bytes to relocate?
	OR	C
	JP	Z,RELM03
	DEC	BC	     ;Yes, need to get next byte from bit map?
	LD	A,E
	AND	7
	JP	NZ,RELM01
	EX	(SP),HL	     ;Yes, move next byte into register (l).
	LD	A,(HL)	     ;Note that pointer to bit map is on the stack.
	INC	HL
	EX	(SP),HL
	LD	L,A
RELM01:	LD	A,L	     ;Check next bit.
	RLA	
	LD	L,A
	JP	NC,RELM02    ;Need to relocate this byte?
	LD	A,(DE)	     ;Yes, offset is in (h).
	ADD	A,H
	LD	(DE),A
RELM02:	INC	DE	     ;Move on to next byte.
	JP	RELMEM
RELM03:	POP	DE	     ;Restore (de) only, note that (bc) is zero,
	POP	DE	     ;(h) is offset.
	RET	
;
;	Load file via normal DMA buffer area (there was not enough room
;	in memory for fast procedure).
;
SMALL$SIZE:
	CALL	SET$ONE	     ;Set multi-sector count to (1).
	LD	HL,DFT$BUFFER;Read into memory here.
	CALL	RDNEXT
	JP	NZ,LD$NXT04  ;End of file or error.
	LD	HL,0FEH	     ;Sorry, file won't fit.
	LD	(LDR$STAT),HL
	JP	LD$NXT04
;
;	Check for enough memory below the BDOS to hold a program
;	that is (bc) bytes long. Return with (de) containing the
;	start of free memory that is available. Note that if
;	there is not enough memory, a message is printed and a
;	normal return is not executed. Besides room for the code,
;	extra space must be available (15 pages minimum or what is
;	specified with the file).
;
CHECKMEM:
	LD	A,(7)
	DEC	A
	DEC	BC
	SUB	B	     ;Get lowest page requested.
	INC	BC
	CP	15	     ;Must be above ourselves of course.
	JP	C,TOOBIG
	LD	HL,(FSIZE)   ;Check additional space required.
	CP	H
	JP	C,TOOBIG
	LD	D,A	     ;Set (de) to the number of available
	LD	E,0	     ;pages of ram.
	RET	
;
;	Move a block of memory from (HL) to (DE) that is (BC)
;	bytes long.
;
BLKMOVE:LD	A,B	     ;Test for done.
	OR	C
	RET	Z
	DEC	BC	     ;Move next byte and adjust registers.
	LD	A,(HL)
	LD	(DE),A
	INC	DE
	INC	HL
	JP	BLKMOVE
;
;	Set the multi sector count to (1) and also get the previous
;	value into (a).
;
SET$ONE:LD	E,1
;
;	Set multi-sector count to (e) and get previous value into (a).
;
SET$MULTI:LD	HL,(SCB$BASE);Value is stored in the sysstem tables.
	LD	L,0E6H
	LD	A,(HL)	     ;Get previous value.
	LD	(HL),E	     ;And set new value.
	RET	
;
;	Read the next record from file into memory area
;	pointed to by (DE). Return with zero flag set if okay.
;
RDNEXT:	EX	DE,HL	     ;Set DMA address from M.
	LD	C,26
	PUSH	HL
	CALL	CPM
	LD	C,20	     ;Read next record from file.
	LD	HL,(FCB$ADR) ;Get FCB of file.
	EX	DE,HL
	CALL	CPM
	LD	(LDR$STAT),HL
	POP	DE
	OR	A
	RET	Z
	LD	E,H
	RET	
BADLOAD:DEFB	CR,LF,'Cannot load Program 221282  COPYR ',39,'82 DRI '
	DEFB	0,0,0,0,0,0,0,0,0,0,0,0,0,0
SCB$BASE:DEFB	0,0
COMMON$BASE:DEFB	0
GET$DMA:DEFB	60,0
SCBPBLDR:DEFB	98,254
;
;	The following 37 bytes contain a bit map for the code
;	from 100 to 394. For every bit that is set, the corresponding
;	byte must be relocated. In addition, this area is used as
;	data storage space after the code is relocated (saves space).
;
;	Note: The contents of this area are set by RMAC and if any changes
;	are made to the loader code, this area has to change also. The
;	loader section can be separated from the CCP portion and assembled
;	independantly. Then use DDT to combine the sections together again.
;
RSX$BASE:
	DEFB	0,128
FSIZE:	DEFB	0,0
FCB$ADR:DEFB	128,72
OLDSTACK:DEFB	65,16
LDR$STAT:DEFB	4,18,36,18,64,8,0,16,0,136,68,72,0,32,4,128
	DEFB	0,9,0,32,0,0,1,32,0,0,16,0,0,17,18,0,0,65
TMPSTACK:DEFB	0,16,64,130,8,33,0,34,8,1,16,0,0,0,32,1,0,4
	DEFB	8,1,2,8,36,18,0,36,64,0,132,0,2,4,0,0,0,0,0,0
	DEFB	0,0,0
;
;	End of bit-map data. Fill remainder of page with EOF (1AH).
;
	DEFB	26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26
	DEFB	26,26,26,26,26,26,26,26,26
;
;***************************************************************************
;
;	Console Command Processor (CCP) for CP/M 3.1(+)
;
;	This will handle all console interaction, load and execute user
;	programs and built-in commands. Submit jobs and program chain
;	functions are also processed here.
;
;***************************************************************************
;
;
;	Include the following lines if the loader is separated from
;	the CCP which needs to reference these locations.
;
;	EXTERNAL	SETUP,SET$NEXT,REMOVE,RELOCATE
;	EXTERNAL	CHECKMEM,SCB$BASE,COMMON$BASE,RSX$BASE
;
;	Start of the CCP program. Start off with a fake PRL prefix
;	of 26 bytes.
;
;	ORG	200H		;Include this line for separate loader.
CCP:	
	DEFB	0,0,0,0,0,0,0,0,0,0,0,0,0
	DEFB	0,0,0,0,0,0,0,0,0,0,0,0,0
;
;	Start of the command file processor. This is absolute code (normal
;	COM type file).
;
INIT:	LD	SP,OURSTK    ;Use our own stack area.
	LD	HL,MAINRET   ;Install our own return address.
	PUSH	HL
	LD	DE,GET$SCB   ;Get the address of the SCB table in the BDOS.
	LD	C,49
	CALL	CPM
	LD	(SCB$BASE),HL
	LD	L,0FAH	     ;Set base address of common memory. This
	LD	A,(HL)	     ;is zero if in non-banked mode.
	LD	(COMMON$BASE),A
	LD	L,099H	     ;Get base page of BDOS.
	LD	A,(HL)
	LD	(BASEPAGE),A
	LD	A,(7)
	SUB	(HL)
	JP	NZ,INIT01
	LD	BC,0294H     ;See if there is enough room to move the
	CALL	CHECKMEM     ;loader into high mem.
	LD	H,E	     ;Okay, clear (hl). Note (de) is addr of
	LD	L,E	     ;available mem (e)=0.
	CALL	RELOCATE     ;Move and relocate the loader code.
	LD	HL,(6)	     ;Install the first 6 bytes from the base of
	LD	L,E	     ;the BDOS to the start of the loader code
	LD	C,6	     ;(just extends the BDOS area).
	CALL	MOVE$C	     ;Move (c) bytes from (hl) to (de).
	LD	E,0BH	     ;Reset the jump address at relative byte (e) 
	CALL	SET$NEXT     ;to BDOS+6 and reset the TPA base address.
;
;	Initialize our table parameters and flag bytes.
;
INIT01:	LD	C,98	     ;Return all free blocks (unclosed files)
	CALL	CPM	     ;to the system for re-use.
	LD	B,0B6H	     ;Get relative parameter #182 from table.
	CALL	GET$PARM     ;Get some system parameters.
	INC	A	     ;Get console screen width (zero relative).
	RRCA		     ;Make absolute, divide by 16. This becomes the
	RRCA		     ;number of directory names that can be printed
	RRCA		     ;per line.
	RRCA	
	AND	0FH
	LD	DE,DIRSIZE
	LD	(DE),A
	LD	L,0B8H	     ;Get lines per screen parameter.
	LD	A,(HL)
	DEC	A
	INC	DE
	LD	(DE),A
	XOR	A	     ;Set "page" output mode.
	INC	DE
	LD	(DE),A
	LD	A,'$'	     ;Add a delimitor.
	INC	DE
	LD	(DE),A
	LD	L,0D3H	     ;Set this as the standard delimitor also.
	LD	(HL),A
	LD	L,0E6H
	LD	(HL),1	     ;Set multi-sector count to (1).
	INC	HL
	XOR	A	     ;Set BDOS error mode to (0).
	LD	(HL),A
	LD	L,0CFH	     ;Set console mode to (1).
	LD	(HL),1
	INC	HL
	LD	(HL),A	     ;Set RSX input mode to true.
	LD	L,0A1H
	LD	(HL),31H     ;Set BDOS version number.
	LD	L,0B4H	     ;Do we have to reset the disks?
	LD	A,(HL)
	AND	020H	     ;Check bit 5.
	LD	C,13
	PUSH	HL
	CALL	NZ,CPM	     ;Yes, then do it.
	POP	HL
	LD	L,0B3H	     ;Do we have to remove all completed RSX's?
	LD	A,(HL)
	AND	2	     ;Check bit 1.
	PUSH	HL
	CALL	Z,REMOVE     ;Yes, then do this also.
	POP	HL
	LD	A,(HL)	     ;And say that they have been removed.
	AND	0FDH
	LD	(HL),A
	AND	040H	     ;Check bit 6, if set then the default
	PUSH	HL	     ;user number will be used. Otherwise the
	LD	L,0B0H	     ;previous number will be used.
	LD	BC,USR$NUM
	LD	D,H
	LD	E,0E0H	     ;Get default user number.
	LD	A,(DE)
	LD	(BC),A	     ;Install it.
	LD	A,(HL)	     ;Get previous user number.
	JP	NZ,INIT02
	LD	(BC),A	     ;Use it instead.
INIT02:	LD	(DE),A	     ;Reset default user number.
	INC	BC
	LD	E,0DAH	     ;Get current disk.
	LD	A,(DE)
	JP	NZ,INIT03
	LD	A,0FFH	     ;Use special drive (search chain?).
INIT03:	LD	(BC),A	     ;Save disk number.
	DEC	HL
	INC	BC	     ;Get disk to access.
	LD	A,(HL)
	LD	(BC),A
	LD	(DE),A	     ;Set previous disk.
	LD	L,0ECH	     ;Get disk for temp files.
	INC	BC
	LD	A,(HL)	     ;Save it.
	LD	(BC),A
	POP	HL
	LD	A,(HL)	     ;Check for a program chain request.
	AND	80H
	JP	Z,INIT05
	LD	HL,DFT$BUFFER;Yes, next command must already be here.
;
;	Fill input buffer from a previous chain request or use the
;	standard auto-start program name.
;
INIT04:	LD	DE,IN$BUFF+1
	LD	C,127	     ;Set data length.
	LD	A,C
	LD	(DE),A
	INC	DE
	CALL	MOVE$C	     ;Move new buffer into place.
	JP	EXECUTE	     ;Process this line.
INIT05:	LD	L,0B5H	     ;Do we exucte built-in command?
	LD	A,(HL)
	AND	2
	JP	NZ,CMDRET
	LD	A,(HL)	     ;Yes, but only once.
	OR	2
	LD	(HL),A
	LD	(AS$MODE),A
	LD	HL,AUTO$START;This is the command (what sets it?).
	JP	INIT04
AUTO$START:DEFB	'PROFILE.S',0
;
;	Command error return location. Turn off various other processing
;	(submit or next command on single line).
;
CMDRET:	CALL	SET$BC
	CALL	CRLF
;
;	Main command return location. Check for more submit data.
;
MAINRET:LD	HL,MAINSTK
	LD	SP,HL
	XOR	A
	LD	(ROW$NUM),A  ;Set row number to zero.
	LD	HL,MAINRET   ;Establish return location (normal).
	PUSH	HL
	CALL	SET$BC
	DEC	HL	     ;Processing a submit file?
	LD	A,(HL)
	AND	1	     ;Test bit 0.
	JP	Z,GET$COMMAND
	LD	DE,IN$BUFF+1 ;Nope, get next line from user.
	CALL	SETDMA	     ;Okay, set dma address.
	LD	C,15	     ;Open temp submit file ($$$.SUB).
	CALL	TMP$FILE
	LD	C,11	     ;Check keyboard, quit on ^C.
	CALL	Z,TMP$FILE   ;Note that this just checks console.
	JP	NZ,SUB$EOF   ;Delete file and continue.
	LD	HL,TEMP$SUB+R2;Clear random record number (upper 2 bytes).
	LD	(HL),A
	DEC	HL
	LD	(HL),A
	DEC	HL
	PUSH	HL
	LD	A,(TEMP$SUB+RCNT);Decrement record number.
	DEC	A
	LD	(HL),A
	LD	C,33	     ;Read next record (if any remain).
	CALL	P,TMP$FILE
	POP	HL
	DEC	(HL)	     ;Did we complete this file?
	LD	C,19
	CALL	M,TMP$FILE   ;Yes, delete it.
	OR	A	     ;Good read?
;
;	Done reading, if zero flag is set then we will process this last
;	line of data. Otherwise, we had an error and we will delete the
;	temp file and ask for the next command from the console.
;
SUB$EOF:PUSH	AF	     ;Save error code.
	LD	C,99	     ;Truncate the temp file.
	CALL	TMP$FILE
	POP	AF
	JP	Z,EXECUTE    ;Did we have an error?
	LD	BC,0B301H    ;Yes, clear the submit file flag bit
	CALL	CLR$BITS
	LD	C,19	     ;and delete the temp file.
	CALL	TMP$FILE
;
;	GEt the next command line from the console.
;
GET$COMMAND:
	LD	A,(USR$NUM)  ;Print user number if not zero.
	OR	A
	CALL	NZ,DECOUT
	CALL	PRTLOG	     ;Print drive name (A-P).
	LD	A,'>'	     ;Add prompt (>).
	CALL	PAGE$OUT
	LD	DE,0B1BAH    ;Extract address of continued input data.
	CALL	MOVE2
	OR	A	     ;Set zero flag if nothing is present.
	PUSH	AF
	LD	BC,0B480H
	CALL	NZ,CLR$BITS  ;Clear bit 7 of (B4) if more input is available.
	CALL	GETLINE	     ;Get input line from console.
	CALL	CLR$BC	     ;Now clear bits 6,7 of (B4).
	POP	AF
	CALL	NZ,CONTINUE  ;Adjust system table parameters.
;
;	Execute command line from buffer.
;
EXECUTE:
	CALL	GET$MODE     ;Use current or previous page mode?
	JP	NZ,EXEC01
	LD	L,0C9H	     ;Get previous page mode byte.
	LD	A,(HL)
	DEC	HL
	LD	(HL),A	     ;Reinstate this mode.
EXEC01:	LD	L,0C8H	     ;Set page mode flag per BDOS direction.
	LD	A,(HL)
	LD	(PAGE$FLG),A
	CALL	SCANLINE     ;Scan input line, make UC and look for "[".
	RET	Z	     ;Nothing entered?
	LD	DE,USR$BLK
	CALL	CHKFNAME     ;Separate drive name and user number.
	LD	A,(FCB+T1)   ;Any extension entered?
	CP	020H
	JP	NZ,EXEC04    ;Yes, go try to execute.
	LD	HL,USR$BLK
	LD	A,(HL)	     ;If a user number or drive was specified,
	INC	HL	     ;just execute the program given. Otherwise
	OR	(HL)	     ;we will check for a built-in command first.
	INC	HL
	LD	A,(HL)
	JP	NZ,EXEC05
	LD	HL,CMDTBL    ;Check for a built-in command.
	LD	DE,FCB+1     ;(FCB+1 is start of name).
	LD	A,(FCB+3)
	CP	021H	     ;Is name at least 3 chars long?
	CALL	NC,COMPARE   ;Yes, search for it.
	JP	NZ,EXEC02    ;Match not found?
	LD	A,(OPTIONS)  ;Okay, command #(b) found, were any options
	OR	A	     ;specified in initial command line?
	LD	A,B
	LD	HL,(INP$PTR) ;Save current pointer address incase of an
	LD	(TEMP$PTR),HL;error. Then we can write bad file name.
	LD	HL,ADRTBL
	JP	Z,TBL$JUMP   ;Nope, we can execute it then.
	CP	4	     ;Options specified, is it command # 0-3?
	JP	C,GET$ERR    ;Yes, just execute command file of same name.
	LD	HL,FCB+4
	JP	NZ,EXEC02    ;Was it #4 (DIRS)?
	LD	(HL),' '     ;Yes, change name to DIR for loading.
EXEC02:	LD	BC,0B418H
	CALL	TST$BC	     ;Test relative byte (b4) anded with (18).
	JP	Z,EXEC04
	LD	B,8	     ;Set default extension.
	SUB	B
	JP	Z,EXEC03
	LD	B,0
EXEC03:	PUSH	BC	     ;Set ext, (a)=0 COM, (a)=8 SUB, (a)=16 PRL.
	CALL	SET$EXT
	CALL	PROCESS
	POP	AF
	CALL	SET$EXT
EXEC04:	CALL	PROCESS	     ;Process command (only returns on an error).
	JP	WHAT?	     ;Illegal extension given.
EXEC05:	CP	' '
	JP	NZ,EXEC02
	CALL	MORE
	LD	A,(USR$BLK)
	SUB	1
	JP	C,LOGINDSK
;
;	Set user number from (a).
;
SET$USR:LD	(USR$NUM),A  ;Save number for us
	LD	B,0B0H	     ;and for the system.
	CALL	SET$PARM
	CALL	SET$USER     ;Put into SCB for other programs also.
;
;	Login the specified disk if it is not the default disk.
;
LOGINDSK:LD	A,(FCB)	     ;Get disk drive.
	DEC	A	     ;Default reference?
	RET	M	     ;Yes, ignore.
	PUSH	AF	     ;Nope, select it.
	CALL	SELDSK
	POP	AF
	LD	(LOGDRIVE),A ;Then save it for ourselves.
	LD	B,0AFH
	JP	SET$PARM     ;Also put into system tables.
;
;	Built-in command table. All entries must end with a space.
;	The end of the table is with a null.
;
CMDTBL:	DEFB	'DIR '
	DEFB	'TYPE '
	DEFB	'ERASE '
	DEFB	'RENAME '
	DEFB	'DIRSYS '
	DEFB	'USER '
	DEFB	0
;
;	Address table for above commands. These must be in the
;	same order.
;
ADRTBL:	DEFW	DIR,TYPE
	DEFW	ERASE,RENAME
	DEFW	SYSDIR,USER
;
;	Drive print functions.
;
PRTDRIVE:LD	A,(DFT$FCB)  ;Get drive name from fcb.
PRTDRV$A:DEC	A	     ;Use loged-in drive or (a)?
	JP	P,PRTDR1
PRTLOG:	LD	A,(LOGDRIVE) ;Get loged-in drive.
PRTDR1:	ADD	A,'A'	     ;Make (A) into drive name.
	JP	CONSOLE
;
;***************************************************************************
;
;	Directory commands, the first entry (DIR) prints non-
;	system names and the second entry SYSDIR prints all
;	names.
;
;***************************************************************************
;
DIR:	LD	C,0	     ;Clear system flag bit 7.
	LD	DE,SYSFILES  ;Load address of warning message.
	JP	PRTDIR
SYSDIR:	LD	C,080H	     ;Set system flag bit 7.
	LD	DE,NONSYS    ;Load address of warning message.
PRTDIR:	PUSH	DE
	CALL	PRINTDIR     ;Print full directory.
	POP	DE
	JP	Z,NONE	     ;No files printed?
	LD	A,L	     ;End last line if required.
	CP	B
	CALL	NC,CRLF
	LD	HL,SKIPED    ;Did we skip over any filenames?
	DEC	(HL)
	INC	(HL)
	RET	Z
	DEC	(HL)	     ;Yes, reset flag and display appropriate
	JP	PRT$CRLF     ;message.
;
;	Print directory now in pages. Note that (de) points to a
;	warning message to be displayed if some file names are
;	not listed. Register (c), bit 7 indicates what type of
;	files will be printed (0=normal, 1=system).
;	Return with zero flag set if no names are listed at all.
;
PRINTDIR:PUSH	BC
	CALL	DFTDMA
	CALL	PARSEINP     ;Parse input file descriptor.
	LD	DE,DFT$FCB+1 ;All blanks?
	LD	A,(DE)
	CP	' '
	LD	B,11	     ;Set string length.
	CALL	Z,FILLSTR    ;Yes, fill field with "?".
	CALL	MORE	     ;A second argument is not allowed.
	CALL	GETFIRST     ;Look for first name.
	POP	BC
	RET	Z	     ;None?
	LD	A,(DIRSIZE)  ;Get # of directory names per line allowed.
	LD	L,A
	LD	B,A
	INC	B
PRTD01:	PUSH	HL	     ;Look at system bit.
	LD	HL,10
	ADD	HL,DE
	LD	A,(HL)
	POP	HL
	AND	080H
	CP	C	     ;Do we show this file?
	JP	Z,PRTD02
	LD	A,1	     ;No, say we skiped some names.
	LD	(SKIPED),A
	JP	PRTD03
PRTD02:	DEC	B	     ;Reached end of line?
	CALL	Z,CRLFX	     ;Yes, start new line and then print drive name.
	LD	A,B
	CP	L
	CALL	Z,PRTDRIVE   ;Put drive name at start of line.
	LD	A,':'	     ;Add colon now.
	CALL	CONSOLE
	CALL	SPACE
	CALL	PRTFNAME     ;Print filename and extension.
	CALL	SPACE
PRTD03:	PUSH	BC	     ;Move on to the next name.
	PUSH	HL
	CALL	CHKCON	     ;We will quit on any key.
	CALL	GETNEXT	     ;Lookup next name.
	POP	HL
	POP	BC
	JP	NZ,PRTD01    ;More names to print?
	INC	A	     ;No, clear zero flag and return.
	RET	
;
;***************************************************************************
;
;	TYPE command processor.
;
;***************************************************************************
;
TYPE:	LD	HL,CMDRET    ;Set return address.
	PUSH	HL
	CALL	PARSEINP     ;Parse input file name.
	LD	A,07FH	     ;Set buffer length to 128 bytes for TYPE.
	LD	(BUFPNTR),A
	LD	C,15	     ;Open the file now.
	CALL	GETFILE	     ;Note, this just prints 'file reqd' if none
;				;found.
TYPE01:	CALL	CHKCON	     ;Check console and abort on any key.
	CALL	FREAD	     ;Read next byte from file.
	RET	NZ	     ;Done if none left or end-of-file reached.
	CP	01AH
	RET	Z
	CALL	PAGE$OUT     ;Print next character and continue.
	JP	TYPE01
;
;***************************************************************************
;
;	USER command processing. Set desired user number.
;
;***************************************************************************
;
USER:	LD	DE,ASKUSER   ;Ask for user number.
	CALL	INP$NUM
	CALL	DEC2BIN	     ;Convert number to binary, return in (a).
	RET	Z	     ;Nothing entered?
	JP	SET$USR	     ;Set new user number and return.
;
;***************************************************************************
;
;	Erase a specified file (or files). If an ambigeous name
;	was entered, ask for confirmation.
;
;***************************************************************************
;
ERASE:	CALL	PARSEINP     ;Parse input file name.
	JP	Z,DEL$FILE   ;No file given (note zero flag setting)?
	CALL	WILDS	     ;Any wild cards used in name?
	JP	NZ,DEL$FILE
	LD	DE,ERASE?    ;Yes, request confirmation first.
	CALL	PRINT
	LD	HL,(LINE$PTR);Print file specification up to a space.
	LD	C,' '
	CALL	LINEOUT
	LD	DE,YES$NO
	CALL	PRTCHK	     ;Print '(Y/N)' and get a single char response.
	CALL	CRLF
	LD	A,L	     ;If 'Y' was typed, then erase the file(s).
	AND	05FH	     ;Note that this converts (a) to upper case
	CP	'Y'	     ;only when we are interested in a single
	RET	NZ	     ;letter comparison. 'Y' typed?
	OR	A	     ;Yes, clear zero flag.
DEL$FILE:LD	C,19	     ;Set 'delete file' function. Note status of
	JP	GETFILE	     ;of zero flag.
;
;***************************************************************************
;
;	RENAME command processor.
;
;***************************************************************************
;
RENAME:	CALL	PARSEINP     ;Parse input file specification.
	PUSH	AF	     ;Note zero set if none (save this).
	LD	HL,16	     ;Point to second name.
	ADD	HL,DE
	EX	DE,HL
	PUSH	DE
	PUSH	HL
	LD	C,16	     ;Move it into fcb+16
	CALL	MOVE$C
	CALL	PARSEINP     ;Parse it also.
	POP	HL
	POP	DE
	CALL	DRIVECHK     ;Check drives specified (must be the same).
	LD	C,23	     ;Set "rename" function.
	POP	AF
;
;	Execute file bdos fuction (c). Note that if the zero flag
;	is set, then "file required" will be printed instead.
;
GETFILE:PUSH	AF	     ;Save zero flag.
	CALL	NZ,MORE	     ;Anything remaning on input line is an error.
	POP	AF
	LD	DE,DFT$FCB
	LD	B,0FFH
	LD	H,1	     ;Set (h) to error byte.
	CALL	NZ,ACCESS    ;Access the file according to (c).
	RET	NZ	     ;Return if successful.
	DEC	H	     ;File not found?
	JP	M,NONE
	LD	HL,(TEMP$PTR);Print file name then...
	LD	(INP$PTR),HL
GET$ERR:CALL	PROCESS
	CALL	PRTFNAME
	LD	DE,REQD	     ;..."required" message.
	JP	PRT$ERR
;
;	Check that the specified drives (including defaults) have
;	been specified consistantly (the same drive referenced).
;
DRIVECHK:LD	A,(DE)	     ;Get first drive.
	CP	(HL)	     ;Same as second?
	RET	Z	     ;Yes, then all is okay.
	OR	A	     ;Nope, was second un-specified?
	RET	Z	     ;Yes, this is okay too.
	INC	(HL)	     ;Nope, but was the other specified?
	DEC	(HL)
	JP	NZ,WHAT?     ;Nope. This is an error.
	LD	(HL),A	     ;Okay, make both the same.
	RET	
;
;***************************************************************************
;
;	General purpose subroutine and processing section.
;
;***************************************************************************
;
;	Search up to 11 bytes for a wild card character (?). Set
;	the zero flag if one was found.
;
WILDS:	LD	B,11	     ;Set search limit to 11.
;
;	Search file specification (de) for wild cards. Only look
;	through (b) bytes. Set the zero flag if found.
;
XWILDS:	INC	DE	     ;Get next byte.
	LD	A,(DE)
	CP	'?'	     ;A wild card?
	RET	Z	     ;Yes, return on zero.
	DEC	B	     ;Not yet, more to look at?
	JP	NZ,XWILDS
	DEC	B	     ;None found, clear zero flag and
	RET		     ;return.
;
;	Input a user number from the console if one was not
;	included with the USER command.
;
INP$NUM:CALL	NONBLANK     ;Was a number entered?
	RET	NZ	     ;Yes, just use it.
	CALL	PRINT	     ;Print 'enter user #' message.
	CALL	GETLINE	     ;Read line in response.
	JP	SCANLINE     ;Scan this line for special chars and return.
;
;	File accessing error messages.
;
NONE:	LD	DE,NO$FILE   ;A file was not found with indicated name.
PRT$ERR:CALL	PRT$CRLF     ;Print message (de) and carriage return.
	JP	MAINRET	     ;Error is fatal, just return.
;
;	Define SUBMIT file command processor to handle .SUB files.
;
SUBMIT:	DEFB	0,'SUBMIT  COM'
;
;	A file was specified with a .SUB extension. Enter the
;	name SUBMIT.COM in front to simulate a submit job.
;
SUB$FILE:LD	A,(DE)	     ;Save drive to	re-select.
	LD	B,0ABH
	CALL	SET$PARM
	LD	HL,SUBMIT    ;Move submit file name into place.
	LD	C,12
	CALL	MOVE$C
	LD	HL,IN$BUFF+1 ;Add separating space.
	LD	(HL),' '
	INC	HL	     ;Pretend this was typed (in case of errors).
;
;	Process the command line. Assume that a file name was
;	given that we should try to load.
;
	LD	(INP$PTR),HL
PROCESS:LD	DE,FCB+T1    ;Check extension against our table.
	LD	HL,EXT$TBL
	CALL	COMPARE
	RET	NZ	     ;Not found, just an error.
	LD	DE,USR$BLK   ;Was user number specified?
	LD	A,(DE)
	OR	A
	RET	NZ
	INC	DE	     ;Nope, check first byte of FCB. If no drive
	LD	A,(DE)	     ;was specified, enable drive search chain.
	LD	C,A
	PUSH	BC
	LD	C,0	     ;Flag to disable search.
	OR	A
	JP	NZ,DRVS03    ;Nope, use loged in drive only.
	LD	BC,0E704H    ;Access search chain(4 items max).
	LD	A,(LOGDRIVE) ;Get default drive.
	INC	A
	LD	H,A
	LD	L,1	     ;One drive only.
DRV$SRCH:INC	B
	DEC	C	     ;More drives to search for?
	LD	A,C
	PUSH	HL
	CALL	P,GET$PARM   ;If yes, then extract next into (a).
	POP	HL
	OR	A	     ;Done with chain?
	JP	M,NO$MORE
	JP	Z,DRVS01     ;Nope, but use default drive instead?
	CP	H	     ;Nope, check to see if we are going to use
	JP	NZ,DRVS02    ;the default drive anyway.
;
DRVS01:	LD	A,H	     ;Use default drive.
	DEC	L	     ;Only once though.k
	JP	M,DRV$SRCH
;
;	Look for the file on drive (a). (de) contains the address of the
;	FCB.
;
DRVS02:	LD	(DE),A
DRVS03:	PUSH	BC
	PUSH	HL
	CALL	FOPEN	     ;Open the file now.
	POP	HL
	POP	BC
	JP	Z,DRV$SRCH   ;Can't?
	LD	BC,0B403H    ;Okay, do we print message to this effect?
	CALL	TST$BC
	JP	Z,EXT$JUMP
	LD	A,(DE)	     ;Yes, print "d:filename (user)".
	CALL	PRTDRV$A     ;Print drive name.
	LD	A,03AH	     ;Print colon next.
	CALL	CONSOLE
	PUSH	DE	     ;Print filename.
	CALL	PRTFNAME
	POP	DE
	PUSH	DE
	LD	HL,8	     ;Check the 'f8' bit. If it is set, then
	ADD	HL,DE	     ;"(user 0)" is also printed. This indicates
	LD	A,(HL)	     ;that the file was found on user zero and
	AND	080H	     ;will become read-only.
	LD	DE,USER0
	CALL	NZ,PRINT
	CALL	CRLF
	POP	DE	     ;Restore fcb pointer.
;
;	Jump to section to handle files with one of the supported
;	extension types.
;
EXT$JUMP:POP	AF
	LD	HL,EXT$ADR   ;Address of procedure for each type.
;
;	Table directed procedures. Enter here with the relative
;	table address in (a) and the beginning of the table in (hl).
;	This will jump to the indicated entry (note that no range
;	checking is performed).
;
TBL$JUMP:ADD	A,A	     ;Convert entry number to byte count (*2).
	CALL	ADDHL
	PUSH	DE
	LD	E,(HL)	     ;Extract address from table.
	INC	HL
	LD	D,(HL)
	EX	DE,HL
	POP	DE
	JP	(HL)	     ;Execute routine now.
;
;	Table of possible extensions. Each must end in a space. The
;	table must end with a null byte.
;
EXT$TBL:DEFB	'COM '
	DEFB	'SUB '
	DEFB	'PRL '
	DEFB	0	     ;End of table.
;
;	Addresses to handle the above file types.
;
EXT$ADR:DEFW	COM$PRL
	DEFW	SUB$FILE
	DEFW	COM$PRL
;
NO$MORE:POP	BC
	LD	A,C
	LD	(DE),A
	RET	
;
;	Set the default extension type to that speccified in (a).
;	(a)=0 COM, (a)=8 SUB, (a)=16 PRL.
;
SET$EXT:RRCA		     ;Divide by 2 (change into byte count).
	LD	HL,EXT$TBL   ;Get start of extension table.
	CALL	ADDHL	     ;Index into table.
	LD	DE,FCB+T1    ;Move extension into place now (3 bytes).
	LD	C,3
	JP	MOVE$C
;
;	Process a COM or PRL type file.
;
COM$PRL:LD	HL,100H
	LD	(PARSE$FCB),HL
	LD	HL,(BUFPNTR)
	DEC	H
	LD	L,0C0H
	PUSH	HL
	LD	A,(DE)	     ;Save drive that program comes from.
	LD	(TP$DRIVE),A
	EX	DE,HL
	LD	C,35
	CALL	MOVE$C
	LD	HL,AS$MODE
	INC	(HL)
	LD	HL,(INP$PTR) ;Move input line down to 80h.
	DEC	HL
	LD	DE,81H
	EX	DE,HL
	LD	(INP$PTR),HL
	CALL	MOVSTRNG
	LD	(DFT$BUFFER),A;Add character count.
	CALL	PARSEINP     ;Parse input filename.
	LD	(PASSWD1),HL ;Store address of first password.
	LD	A,B
	LD	(NCHARS1),A  ;And it length.
	LD	DE,SECOND$FCB;Parse second filename (if present).
	CALL	PARSEIT
	LD	(PASSWD2),HL ;Store address of second password.
	LD	A,B
	LD	(NCHARS2),A  ;And its length.
	LD	HL,DSK$NUM
	LD	A,(HL)
	OR	A
	CALL	P,SELDSK     ;Select specified disk.
	LD	A,(USR$NUM)  ;And user number.
	CALL	SET$USER
	ADD	A,A	     ;Move user number into high nybble.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	LD	L,0DAH	     ;Add in current drive.
	OR	(HL)
	LD	(CPM$DRIVE),A;Store here for CP/M 2.2 compatibility.
	POP	DE
	LD	HL,(BASEPAGE-1)
	XOR	A
	LD	L,A
	LD	SP,HL	     ;Setup stack and return locations.
	LD	H,A
	PUSH	HL	     ;Setup return addresses, first 0000
	INC	H
	PUSH	HL	     ;then 0100 hex.
	LD	(DFT$FCB+REC),A;Set record number in FCB to zero.
	LD	B,0CFH
	CALL	SET$PARM     ;Set console mode to normal (low byte=0).
	LD	L,090H	     ;Interesting, this sets the SCB -9,10,11,12
	LD	(HL),A	     ;to zero. What is here?
	INC	HL
	LD	(HL),A
	INC	HL
	LD	(HL),A
	INC	HL
	LD	(HL),A
	LD	L,0B3H	     ;Check to see if we are progress of chaining.
	LD	A,(HL)
	AND	080H
	JP	NZ,BEGIN     ;Yes, leave return code alone.
	LD	L,0ACH	     ;Nope, clear return code.
	LD	(HL),A
	INC	HL
	LD	(HL),A
BEGIN:	LD	A,(HL)	     ;Clear the chain flag bit.
	AND	03FH
	LD	(HL),A
	LD	C,59	     ;Call the loaded to load the program and
	JP	CPM	     ;start execution.
;
;	Output (a) to the console in "page" mode. If this fills
;	the screen, we will wait for a key before continuing.
;
PAGE$OUT:CP	LF	     ;Line feed?
	JP	NZ,CONOT2
	LD	HL,SCRNSIZE
	LD	A,(HL)	     ;Get screen row limit.
	INC	HL
	INC	(HL)	     ;Count this last line.
	SUB	(HL)	     ;Reached limit?
	JP	NZ,LINEFEED
	LD	(HL),A	     ;Yes, reset current row.
	INC	HL
	LD	A,(HL)	     ;Do we wait between pages?
	OR	A
	LD	DE,WAITING
	CALL	Z,PRTCHK     ;Print wait message?
	CP	ABT$CHR	     ;Do we abort?
	JP	Z,CMDRET     ;Yes, do it then.
	LD	E,CR	     ;Okay, move to the start of this line.
	CALL	CONOT3	     ;And continue with the listing.
LINEFEED:LD	A,LF	     ;Output one line feed character.
;
;	Output (a) to the console without further interogation.
;
CONOT2:	LD	E,A
CONOT3:	LD	C,2	     ;Just output (E) to the console now.
	JP	CPM
;
;	Print message and get a single char in response.
;
PRTCHK:	CALL	PRINT	     ;Print message and check keyboard.
	LD	C,1
	JP	CPM
;
;	Print message pointed to by (de).It must end with a '$'.
;
PRINT:	LD	C,9	     ;Print string function.
	JP	CPM
;
;	Get a line of data from the console. This executes the
;	buffered read function such that normal correction keys
;	will be recognized.
;
GETLINE:LD	HL,IN$BUFF   ;Input a line from the console.
	LD	(HL),0E7H    ;Set max line length to 231.
	EX	DE,HL
	LD	C,10	     ;Read a line into buffer pointed to by (de).
	CALL	CPM
	LD	HL,IN$BUFF+1 ;Check number of chars in line just entered.
	LD	A,(HL)
	INC	HL
	CALL	ADDHL	     ;Add trailing null byte.
	LD	(HL),0
	JP	CRLF	     ;Output end of line.
;
;	Check the console for a key. If one is ready, then just
;	abort current operation (jump to return address). The
;	zero flag is set if nothing entered (any one care?).
;
CHKCON:	CALL	CONSTAT	     ;Check console, abort on any key.
	RET	Z
	JP	CMDRET
;
;	Interogate the console keyboard. Return with zero set
;	if nothimng is ready. Otherwise return with character
;	in register (a) with zero cleared.
;
CONSTAT:LD	C,11	     ;Check console keyboard.
	CALL	BDOS	     ;This will set flags on (a) before returning.
	RET	Z	     ;Just return on zero (nothing ready).
	LD	C,1	     ;Okay, get character and return with it in (a).
	JP	BDOS	     ;Note that this will clear the zero flag if
;				;char is not null.
;
;	Set the default DMA address to 80 hex.
;
DFTDMA:	LD	DE,DFT$BUFFER
;
;	Set the data transfer address (or DMA address) to (de).
;
SETDMA:	LD	C,26
	JP	CPM
;
;	Select the drive as specified in register (a).
;
SELDSK:	LD	E,A
	LD	C,14
	JP	CPM
;
;	Set the current user number to (a). This directly accesses
;	the System Control Block (SCB).
;
SET$USER:LD	B,0E0H
	JP	SET$PARM
;
;	Open the file specified at FCB. The proper drive will
;	be loged in.
;
FOPEN:	LD	BC,15	     ;Set b=0 (special?), c=15 (open file function).
	LD	DE,FCB	     ;Get fcb address.
;
;	Access the file specified by (de). The access function
;	must be in (c). Register (b) is used as drive mask byte
;	possibly to get/set/ignore time and date stamps?
;
ACCESS:	LD	HL,REC	     ;Clear record number byte in fcb.
	ADD	HL,DE
	LD	(HL),0
	PUSH	BC
	PUSH	DE
	LD	A,(DE)	     ;Get drive specified in fcb.
	AND	B	     ;And with specified mask byte (reason?).
	DEC	A	     ;Select this drive if it is not the current
	CALL	P,SELDSK     ;drive (0).
	LD	DE,TEMPDMA   ;Set the dma address in case file i/o is
	CALL	SETDMA	     ;involved in the specified function.
	POP	DE
	POP	BC
	PUSH	DE
	LD	HL,(SCB$BASE)
	LD	L,0E7H
	LD	(HL),B	     ;Save (b) in system tables before access.
	PUSH	HL
	CALL	CPM	     ;Perform desired file function.
	POP	DE
	XOR	A	     ;Reset system table byte (function?).
	LD	(DE),A
	LD	A,(LOGDRIVE) ;Reset loged in drive.
	LD	E,0DAH	     ;Put in BDOS table (SCB).
	LD	(DE),A
	PUSH	HL
	CALL	DFTDMA	     ;Reset dma address.
	POP	HL	     ;Restore (hl) from return from access function.
	INC	L	     ;Depending on access function, this may be an error flag.
	POP	DE
	RET	
;
;	Look for a file in the directory. Either search
;	for the first name or the next name.
;	Return with the zero flag set if not found.
;
GETFIRST:LD	C,011H
	JP	GET01
GETNEXT:LD	C,18
GET01:	LD	DE,DFT$FCB   ;Lookup file in directory.
	CALL	CPM
	INC	A
	RET	Z	     ;Not there?
	DEC	A	     ;Point to extent entry.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	LD	HL,80H
	CALL	ADDHL
	EX	DE,HL
	XOR	A	     ;Clear zero flag.
	DEC	A
	RET	
RDNXTREC:XOR	A
	LD	(BUFPNTR),A
	LD	C,20	     ;Read next record.
	LD	DE,DFT$FCB
;
;	Call bdos function in (c) and set flags on (a) upon return.
;
BDOS:	CALL	CPM
	OR	A
	RET	
;
;	Access the submit temporary file "$$$.sub".
;
TMP$FILE:LD	DE,TEMP$SUB
	JP	BDOS
;
;	Scan the input line for special characters. Note that this
;	stops on a semi-colon or exclamation mark and if an
;	option lead-in is found ([), a flag is set.
;
SCANLINE:CALL	CLR$BC
	EX	DE,HL
	XOR	A	     ;Clear "option included" flag byte.
	LD	(OPTIONS),A
	LD	HL,IN$BUFF+2 ;Get next non-blank character.
	CALL	NBLANK1
	EX	DE,HL
	CP	';'	     ;Ignore lines that start with a semi-colon.
	RET	Z
	CP	'!'	     ;Ignore leading exclamation point also.
	JP	Z,SCAN01
	CP	':'	     ;Is first character a colon?
	JP	NZ,SCAN02
	LD	L,0ACH	     ;Yes, check return code.
	INC	(HL)
	INC	(HL)
	JP	Z,SCAN01     ;0fe hex mean no error.
	INC	HL
	INC	(HL)	     ;A high byte id 0ff hex is an error.
	RET	Z	     ;So skip this command line.
SCAN01:	INC	DE	     ;Skip this character.
SCAN02:	EX	DE,HL
	LD	(INP$PTR),HL
SCAN03:	LD	A,(HL)
	CP	'['	     ;Start of option?
	JP	NZ,SCAN04
	LD	(OPTIONS),A  ;Yes, set flag byte non-zero.
SCAN04:	CP	'a'	     ;Convert (A) to upper case.
	JP	C,SCAN05
	CP	'{'
	JP	NC,SCAN05
	SUB	020H
	LD	(HL),A
SCAN05:	CP	'!'	     ;Multiple command separator?
	CALL	Z,MULTIPLE   ;Yes, move the remainder of the line into
	INC	HL	     ;a safe area.
	OR	A
	JP	NZ,SCAN03
;
;	Find location of argument after command. Set zero flag if
;	there is none. This will skip intervening blanks and tabs.
;
NONBLANK:LD	HL,(INP$PTR) ;Get pointer into command line.
NBLANK1:LD	(INP$PTR),HL ;Save current pointer.
	LD	(LINE$PTR),HL
	LD	A,(HL)	     ;Get next character.
	OR	A	     ;End of line?
	RET	Z
	CP	' '	     ;Nope, a space?
	JP	Z,NBLANK2    ;Yes, skip to next char.
	CP	TAB	     ;How about a tab?
	RET	NZ	     ;Nope, return with this character.
NBLANK2:INC	HL	     ;Bump to next character.
	JP	NBLANK1
;
;	Multiple command line found, create a special RSX procedure to
;	hold the remaining data (just below the lowest existing RSX).
;	Then tell the BDOS about it (so we can find it next time).
;
MULTIPLE:
	LD	E,L
	LD	D,H
	INC	DE
	LD	A,(DE)	     ;Check for 2 in a row.
	CP	'!'
	PUSH	AF
	PUSH	HL
	CALL	Z,MOVSTRNG   ;Yes, just eliminate the first one.
	POP	HL
	POP	AF
	RET	Z	     ;And return, we will get back here shortly.
	LD	(HL),0	     ;Place a null in its place.
	EX	DE,HL
	LD	HL,(6)	     ;Get low RSX address.
	DEC	H	     ;Move to one page before it.
	LD	L,018H	     ;Put the remaining line from here to the end.
	LD	(HL),A
	PUSH	HL
MULTI01:INC	HL	     ;Moveline while replacing all marks with
	INC	DE	     ;carriage returns. The BDOS will return the
	LD	A,(DE)	     ;next line up to (cr).
	LD	(HL),A
	CP	'!'	     ;Separator?
	JP	NZ,MULTI02
	LD	(HL),CR	     ;Yes, change to a (cr).
MULTI02:OR	A	     ;End of line?
	JP	NZ,MULTI01
	LD	(HL),CR	     ;Yes, append a (cr).
	INC	HL
	LD	(HL),A	     ;Then a null byte.
	LD	L,6
	LD	(HL),0C3H    ;Put jump instruction from xx06 to xx09
	INC	HL	     ;(this RSX performs no other action).
	LD	(HL),9
	INC	HL
	LD	(HL),H
	INC	HL
	LD	(HL),0C3H    ;At nine, insert another jump instruction.
	LD	L,0EH
	LD	(HL),A	     ;Zero REMOVE flag so this is not removed.
	LD	L,A	     ;Point to start of procedure
	EX	DE,HL
	CALL	SETUP	     ;let SETUP fix all address pointers.
	LD	HL,(SCB$BASE);Put address of this data line into the
	LD	L,0B1H	     ;SCB tables for the BDOS to use.
	POP	DE
	INC	DE
	LD	(HL),E
	INC	HL
	LD	(HL),D
	LD	L,0AEH	     ;Set flag saying the information is present.
	LD	(HL),D
	XOR	A	     ;Set end-of-line indication.
	RET	
;
;	Process a continuation line. This will check for an abort request
;	(^C) or end of data (in which case the RSX is marked as removable).
;
CONTINUE:
	LD	DE,0BAB1H    ;Move address of next character.
	CALL	MOVE2
	OR	A	     ;High byte zero?
	LD	DE,0BCB1H
	CALL	Z,MOVE2	     ;Yes, then move data from here instead.
	PUSH	HL
	CALL	CONSTAT	     ;Check for an abort request.
	POP	HL
	LD	L,0B1H
	JP	NZ,CMD$END   ;Yes, clear flags to stop processing.
	LD	E,(HL)	     ;Get address of remainder of line.
	INC	HL
	LD	D,(HL)
	INC	(HL)	     ;Is there any?
	DEC	(HL)
	DEC	HL
	JP	Z,CMD$END    ;Nope, clear flags.
	LD	A,(DE)	     ;Yep, check next character for end mark
	OR	A	     ;(null).
	RET	NZ
;
;	Remove the RSX associated with the multiple command processing.
;
CMD$END:XOR	A	     ;Clear the address pointer.
	LD	(HL),A
	INC	HL
	LD	(HL),A
	LD	L,0AEH	     ;Get address of REMOVE flag.
	LD	H,(HL)
	LD	L,0EH
	DEC	(HL)	     ;Turn 0 into 0ff hex and then remove it.
	JP	REMOVE
;
;	Parse the input file specifications. Note that this routine is
;	very strange. It is not consistant with the fine code throughout
;	the rest of the CCP and loader. In particular are the references
;	to 0B28 hex. I don't know what this is all about. It seems like
;	nonsense.
;
PARSEINP:LD	DE,DFT$FCB   ;Parse next filename and put fcb here.
PARSEIT:CALL	NONBLANK     ;Skip over to first non-blank.
	PUSH	AF	     ;Save zero flag (z=none).
	CALL	PARSE
	POP	AF
	RET	
PARSE:	LD	(INP$PTR),HL ;Save input pointer temporailly.
	LD	(LINE$PTR),HL
	PUSH	DE
	LD	DE,INP$PTR   ;Now let the bdos parse the filename.
	LD	C,152
	CALL	CPM
	POP	DE
	LD	A,H	     ;(hl) will be zero if end of string found.
	OR	L
	LD	B,(HL)	     ;Get next non-blank char (maybe).
	INC	HL	     ;(hl) is returned as ffff if error occured.
	JP	NZ,PARSE01
	LD	HL,0B28H     ;What???
PARSE01:LD	A,H	     ;Did an error occur?
	OR	L
	JP	NZ,PARSE02
	LD	HL,0B28H     ;Yes, but what is this statement doing here?
	CALL	WHAT?	     ;Print error message.
PARSE02:LD	A,B	     ;Check terminating character. This is a
	CP	'.'	     ;terminator and must be skipped if it is
	JP	NZ,PARSE03   ;not a period.
	DEC	HL	     ;Don't skip.
PARSE03:LD	(INP$PTR),HL ;Update address for next time.
	LD	C,16
	LD	HL,PARSE$FCB+2
	PUSH	DE
	CALL	MOVE$C	     ;Move FCB into users place (first 16 bytes).
	LD	DE,TEMPDMA   ;Move password here.
	LD	C,10
	CALL	MOVE$C
	POP	DE
	LD	A,(HL)	     ;Get length of password.
	LD	HL,0	     ;Set in case there was none.
	OR	A
	LD	B,A
	JP	Z,PARSE05    ;Why not just RET Z here? Very strange...
	LD	HL,(LINE$PTR);Okay,look for start of passord.
PARSE04:LD	A,(HL)	     ;It follows a semi-colon.
	CP	';'
	INC	HL
	JP	NZ,PARSE04
PARSE05:RET	
;
;	Check file name specified for a leading user number and
;	drive name. These must be seen within the first 4 bytes.
;	(de) points to a 2 byte data storage area for parameters.
;
CHKFNAME:PUSH	DE	     ;Save pointer to data area.
	XOR	A
	LD	(DE),A	     ;Clear user number storage spot.
	INC	DE
	LD	(DE),A	     ;And drive name spot.
	INC	DE
	CALL	NONBLANK     ;Skip over leading spaces or tabs.
	LD	HL,(INP$PTR)
	POP	DE
	PUSH	DE
	LD	B,4	     ;Search up to 4 bytes for a colon or a null.
CKNAM01:LD	A,(HL)
	CP	':'	     ;A colon?
	JP	Z,CKNAM04
	OR	A	     ;A null (end-of-line)?
	JP	Z,CKNAM02
	DEC	B
	INC	HL
	JP	NZ,CKNAM01   ;Only search (b) bytes.
CKNAM02:POP	DE	     ;No drive name found, clear any suspected user
	XOR	A	     ;number.
	LD	(DE),A
	LD	HL,(INP$PTR)
CKNAM03:INC	DE
	LD	A,(DE)
	PUSH	AF
	CALL	PARSE
	POP	AF
	LD	(DE),A
	RET	
CKNAM04:LD	HL,(INP$PTR)
	LD	A,(HL)
CKNAM05:CP	030H	     ;Look for leading user number.
	JP	C,CKNAM06
	CP	03AH
	JP	NC,CKNAM06
	CALL	CVRTUSR	     ;Extract user number.
	POP	DE
	PUSH	DE
	LD	A,(DE)	     ;Already seen one?
	OR	A
	JP	NZ,CKNAM02   ;Yes, error.
	LD	A,B	     ;Nope, store it (plus one).
	INC	A
	LD	(DE),A
	JP	CKNAM07
CKNAM06:CP	041H	     ;Look for possible drive name.
	JP	C,CKNAM02
	CP	051H
	JP	NC,CKNAM02
	POP	DE
	PUSH	DE
	INC	DE
	LD	A,(DE)	     ;Already seen one?
	OR	A
	JP	NZ,CKNAM02
	LD	A,(HL)	     ;Nope, convert to binary drive and store.
	SUB	040H
	LD	(DE),A
	INC	HL
CKNAM07:LD	A,(HL)	     ;Does colon follow name (required)?
	CP	03AH
	JP	NZ,CKNAM05   ;Nope, error.
	INC	HL	     ;Okay, continue scan.
	POP	DE
	JP	CKNAM03
;
;	Move 2 bytes from system parameters, relavive byte (d)
;	into relative byte (e).
;
MOVE2:	LD	HL,(SCB$BASE)
	LD	L,D
	LD	D,H
	LD	C,2
;
;	Move a block of memory (c) bytes long from (hl) to (de).
;
MOVE$C:	LD	A,(HL)
	LD	(DE),A
	INC	HL
	INC	DE
	DEC	C
	JP	NZ,MOVE$C
	RET	
;
;	Move a string from (DE) to M and count bytes
;	moved in (C). String ends in a null.
;
MOVSTRNG:LD	C,0
MOVEIT:	LD	A,(DE)	     ;Move next byte.
	LD	(HL),A
	OR	A	     ;Last byte moved?
	LD	A,C
	RET	Z	     ;Yes, return byte count in (A).
	INC	HL	     ;Bump pointers.
	INC	DE
	INC	BC
	JP	MOVEIT
;
;	Read one byte from file (fcb is at 5c hex). Set zero
;	flag if data in (a) is okay. Clear zero on eof or err.
;
FREAD:	XOR	A
	LD	HL,BUFPNTR   ;Bump buffer pointer. Need to read in the next record?
	INC	(HL)
	CALL	M,RDNXTREC   ;Okay to continue?
	OR	A
	RET	NZ	     ;Nope, return with error code (zero cleared).
	LD	A,(BUFPNTR)  ;Okay, extract next byte.
	LD	HL,80H
	CALL	ADDHL
	XOR	A	     ;Clear zero flag (data is valid).
	LD	A,(HL)
	RET	
GET$MODE:LD	BC,0B440H
;
;	Test parameter in relative byte (b) by masking it with
;	register (c). Return with (hl) pointing to it.
;
TST$BC:	LD	HL,(SCB$BASE)
	LD	L,B
	LD	A,(HL)
	AND	C
	RET	
SET$BC:	LD	BC,0B4A0H
	CALL	TST$BC
	LD	A,C
	OR	(HL)
	LD	(HL),A
	RET	
CLR$BC:	LD	BC,0B4A0H
CLR$BITS:CALL	TST$BC
	LD	A,C
	CPL	
	AND	(HL)
	LD	(HL),A
	RET	
;
;	Set/get relative parameter (b) from/to (a).
;
SET$PARM:LD	HL,(SCB$BASE)
	LD	L,B
	LD	(HL),A
	RET	
GET$PARM:LD	HL,(SCB$BASE)
	LD	L,B
	LD	A,(HL)
	RET	
PRT$CRLF:CALL	PRINT	     ;Print message but does this set (l) to
;				;anything???
CRLFX:	LD	B,L
;
;	Output a carriage return and line feed to the console.
;
CRLF:	LD	A,CR	     ;Note registers are preserved.
	CALL	CONSOLE
	LD	A,LF
	JP	CONSOLE
;
;	Output (A) as a two digit decimal number.
;
DECOUT:	SUB	10	     ;Less than 10?
	JP	C,DECOT2
	LD	E,'0'	     ;No, compute left digit.
DECOT1:	INC	E
	SUB	10
	JP	NC,DECOT1
	PUSH	AF
	CALL	CONOT3
	POP	AF
DECOT2:	ADD	A,'9'+1	     ;Make (A) ascii and print
	JP	PAGE$OUT
;
;	Output string pointed to by (hl) on the console.
;	It ends with either a null or same character as (C).
;
LINEOUT:LD	A,(HL)	     ;Terminator reached?
	OR	A
	RET	Z
	CP	C
	RET	Z
	CALL	CONSOLE	     ;Nope, continue printing.
	INC	HL
	JP	LINEOUT
MORE:	CALL	NONBLANK
	RET	Z
WHAT?:	LD	HL,AS$MODE   ;If bits 6,7 <> 0 then this error will be
	LD	A,(HL)	     ;skipped one time only.
	OR	A
	LD	(HL),0	     ;Always becomes zero.
	RET	NZ	     ;... just skip this error.
	LD	HL,(LINE$PTR);Print file name.
	LD	C,' '	     ;Output up until a space.
	CALL	LINEOUT
	LD	A,'?'	     ;Follow by this.
	CALL	PAGE$OUT
	JP	CMDRET	     ;And return.
;
;	Convert the next characters from ascii digits to a binary number.
;
DEC2BIN:CALL	NONBLANK
	LD	HL,(INP$PTR)
	LD	(LINE$PTR),HL
	RET	Z
	LD	A,(HL)	     ;Is this a decimal digit?
	CP	'0'
	JP	C,WHAT?
	CP	'9'+1
	JP	NC,WHAT?
	CALL	CVRTUSR	     ;Yes, convert number.
	LD	(INP$PTR),HL ;Save pointer into line.
	OR	1	     ;Clear carry and zero flags.
	LD	A,B	     ;Get number into (a) and return.
	RET	
;
;	Convert the ascii decimal number pointed to by (hl) into binary.
;	Return result in (b) and (hl) pointing to next character.
;
CVRTUSR:LD	B,0	     ;Clear result.
CVUSR1:	LD	A,(HL)	     ;Get next character.
	SUB	'0'	     ;Convert to binary.
	RET	C	     ;Non-digit?
	CP	10
	RET	NC
	PUSH	AF	     ;Save for later.
	LD	A,B	     ;Multiply previous sum by 10.
	ADD	A,A
	ADD	A,A
	ADD	A,B
	ADD	A,A
	LD	B,A
	POP	AF	     ;Pop new digit.
	INC	HL	     ;Bump pointer.
	ADD	A,B	     ;Add in new digit.
	LD	B,A	     ;Put result back into (b).
	CP	16	     ;Greater than 16?
	JP	C,CVUSR1
	JP	WHAT?	     ;Yes, error.
;
;	Print filename and extension. (de) points to fcb.
;
PRTFNAME:INC	DE	     ;Skip drive byte.
	LD	H,8	     ;Print 8 char name.
	CALL	PRINT$H
	CALL	SPACE	     ;Followed by a space then the 3 char extension.
	LD	H,3	     ;Set length count.
;
;	Print (h) characters from (de) on the console.
;
PRINT$H:LD	A,(DE)
	AND	07FH	     ;Strip bit 7 always.
	CALL	CONSOLE
	INC	DE
	DEC	H
	JP	NZ,PRINT$H
	RET	
;
;	Output a single space to the console.
;
SPACE:	LD	A,' '
;
;	Output (A) to the console and preserve all
;	registers.
;
CONSOLE:PUSH	BC
	PUSH	DE
	PUSH	HL
	CALL	PAGE$OUT
	POP	HL
	POP	DE
	POP	BC
	RET	
;
;	Add (hl) and (a). Return result in (hl).
;
ADDHL:	ADD	A,L	     ;Add into low byte.
	LD	L,A
	RET	NC	     ;Carry generated?
	INC	H	     ;Yes, increment high byte.
	RET	
;
;	Fill string (DE) with "?". It is (B) characters long.
;
FILLSTR:LD	A,'?'	     ;Set fill character.
FILLST1:LD	(DE),A	     ;Stuff one more.
	INC	DE
	DEC	B
	JP	NZ,FILLST1   ;Continue for (b) characters.
	OR	A	     ;Clear zero flag then return.
	RET	
;
;	Compare the name from (de) with the table of names at (hl).
;	The table ends with a null byte and each name ends with a
;	space character. Blank chars are not compared.
;	Set the zero flag on a match and return match number in (a).
;
COMPARE:LD	BC,0FFH	     ;Set (b)=0, (c)=-1 as counters.
COMP01:	PUSH	DE
	PUSH	HL
COMP02:	LD	A,(DE)
	AND	07FH
	CP	' '+1	     ;Check this byte?
	JP	C,COMP03
	CP	(HL)	     ;Yes, match?
	JP	NZ,COMP04
COMP03:	INC	DE	     ;So far so good.Move on to next byte.
	INC	C	     ;Update string length count.
	LD	A,' '	     ;End of string?
	CP	(HL)
	INC	HL
	JP	NZ,COMP02
	POP	HL	     ;Yes, move (c) byte string into place now.
	POP	DE
	CALL	MOVE$C
	LD	A,B	     ;Get match number into (a) and return with
	RET		     ;zero flag set.
COMP04:	LD	A,' '	     ;Compare failed, look for end of string.
COPM05:	CP	(HL)
	INC	HL
	JP	NZ,COPM05
	POP	DE
	POP	DE
	INC	B	     ;Bump to next name.
	LD	C,0FFH
	LD	A,(HL)
	SUB	1	     ;End of names? Note that this will clear the
	JP	NC,COMP01    ;zero flag.
	RET	
;
;	Data storage area.
;
ASKUSER:DEFB	'Enter User #: $'
NO$FILE:DEFB	'No File$'
REQD:	DEFB	' required$'
ERASE?:	DEFB	'ERASE $'
YES$NO:	DEFB	' (Y/N)? $'
WAITING:DEFB	cr,lf,cr,lf,'Press RETURN to Continue $'
USER0:	DEFB	'  (User 0)$'
SKIPED:	DEFB	0
NONSYS:	DEFB	'NON-'
SYSFILES:DEFB	'SYSTEM FILE(S) EXIST$'
AS$MODE:DEFB	0,0,0	     ;Auto-start mode flag.
GET$SCB:DEFB	3AH,0
INP$PTR:DEFB	0,0
	DEFW	PARSE$FCB+2
USR$NUM:DEFB	0
DSK$NUM:DEFB	0
LOGDRIVE:DEFB	0
TEMP$SUB:DEFB	1,'$$$     SUB'
	DEFB	0
;
;	End of initialized code area. The remaining labels are defined
;	by address only (no contents assumed).
;
DIRSIZE	EQU	$+23
SCRNSIZE	EQU	$+24
ROW$NUM	EQU	$+25
PAGE$FLG	EQU	$+26
LINE$PTR	EQU	$+27
TEMP$PTR	EQU	$+29
BUFPNTR	EQU	$+31
BASEPAGE	EQU	$+32
OPTIONS	EQU	$+33
TEMPDMA	EQU	$+34
USR$BLK	EQU	$+44
FCB	EQU	$+45
PARSE$FCB	EQU	$+78
IN$BUFF	EQU	$+116
MAINSTK	EQU	$+427
OURSTK	EQU	$+429
;
	END	
