;
; PROGRAM:  DU2
; AUTHOR:  RICHARD CONN
; DERIVATION:  DUTIL is derived from DU Version 7.5
;		DU2 is derived from DUTIL Version 1.1
; VERSION:  1.1
; DATE:  12 Feb 83
; PREVIOUS VERSIONS:  1.0 (10 Jan 83)
; NOTE:  DU2 must be assembled using MAC (or equiv) and will only run on
;		a Z80
;
VERS	EQU	11

;
;  DU2 is derived from --
;	  DU.ASM  V7.5	Revised 1/23/81
;	DISK UTILITY - By Ward Christensen
;
;  Principal Authors of DU V7.5 are --
;	WLC   KBP   RGF   BRR
;
;  Key comments from DU V7.5 and DU2 follow --
;
;This version of DU is compatible with CP/M 1.4 and 2.x
;and does not require alteration for various hardware
;configurations.  It adjusts itself automatically to
;the correct number of sectors, tracks, directory size,
;etc.  It has been tested on 5-1/4" and 8" floppy, and
;10 megabyte hard disk systems.
;
;Because of the automatic adaption feature, no conditional
;assembly options are included.  The only alteration that
;needs to be done is to use DDT to set the byte at 103h
;to zero for systems using a 2 mHz clock or non-zero for
;4 mHz clock.  This only affects the time delay used in
;the 'sleep' command.
;
;For DU2, the additional value of PAGSIZ at 104h should
;be set for the size of the display (in lines) on the user's
;CON: device.  Under DU2, all output is paged, and this
;determines the page limit.
;
;*************************************************
;* 						 *
;*   This program has been heavily modified	 *
;* to allow it to work without modification	 *
;* on most versions of CP/M 1.4 and, hopefully,	 *
;* all versions of CP/M 2.x.			 *
;*   One known possible problem involves the	 *
;* system tracks on some systems, and results	 *
;* from the system sectors being skewed. There	 *
;* is NO way for a program executing under CP/M	 *
;* to know about this.  This program assumes the *
;* standard convention of no skew being used on	 *
;: the system tracks. This usually isn't a prob- *
;* lem because the SYSGEN program can be used to *
;* get the system from the disk so that	it can	 *
;* be modified.					 *
;*   This program should work under standard	 *
;* versions of CP/M 1.4.  The only requirement	 *
;* is that the BIOS "SETSEC" routine not modify	 *
;* the sector number passed to it in the B 	 *
;* register.  Again, system tracks with skewed	 *
;* sectors will be a problem.			 *
;*   If you add any features or make any useful	 *
;* changes to this program, please modem a copy	 *
;* to the above CBBS, so the currency of the	 *
;* program can be maintained.			 *
;* 						 *
;* 		Ron Fowler			 *
;* 						 *
;*************************************************
;
;  The last few revision notes for note are --
;

;
;System equates
;
BASE	EQU	0	;SET TO 4200H FOR HEATH OR TRS-80 ALTCPM
;
;CP/M Key Areas
;
FCB	EQU	BASE+5CH	;CP/M FCB
BDOS	EQU	BASE+5		;CP/M BDOS ENTRY POINT
TBUFF	EQU	BASE+80H	;CP/M TEMPORARY DISK I/O BUFFER
TPA	EQU	BASE+100H	;CP/M TRANSCIENT PROGRAM AREA
;
;  Some Key Variables in DU2
;
EOLCH	equ	','		;Marks logical end of line
SEPCH	equ	' '		;Argument Separator
MULCH	equ	'*'		;Multiplication Command

;
;CP/M BDOS Function Codes
;
PRINT	EQU	9
GVERS	EQU	12
RESETDK EQU	13	;RESET SYSTEM
SELDK	EQU	14	;SELECT DISK
CLOSEF	EQU	16	;CLOSE FILE
SRCHF	EQU	17	;SEARCH FIRST
DELF	EQU	19	;DELETE FILE
WRITEF	EQU	21	;WRITE BLOCK TO FILE
MAKEF	EQU	22	;CREATE FILE
SUSER	EQU	32	;SELECT USER
GETDSK	EQU	25
GETDPB	EQU	31
;
;CP/M 1.4 Offsets and Some Key Values
;
TRNOFF	EQU	15	;CP/M 1.4 OFFSET FROM BASE
			;OF BDOS TO SECTRAN ROUTINE
SKWOFF	EQU	1AH	;CP/M 1.4 OFFSET TO SKEW TABLE
S2OFF	EQU	14	;OFFSET INTO FCB FOR S2 BYTE
DPBOFF	EQU	3AH	;CP/M 1.4 OFFSET TO DPB WITHIN BDOS
S2MASK	EQU	0FH	;MASK FOR EXTENDED RC BITS OF S2
DPBLEN	EQU	15	;SIZE OF CP/M 2.x DISK PARM BLOCK
;
;Define ASCII characters
;
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
TAB	EQU	09H	;TAB
BS	EQU	08H	;BACKSPACE

;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
$-MACRO 		;FIRST TURN OFF THE EXPANSIONS
;
;	JR	- JUMP RELATIVE
;	JRC	- JUMP RELATIVE IF CARRY
;	JRNC	- JUMP RELATIVE IF NO CARRY
;	JRZ	- JUMP RELATIVE IF ZERO
;	JRNZ	- JUMP RELATIVE IF NO ZERO
;	DJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;	LDIR	- MOV @HL TO @DE FOR COUNT IN BC
;	LXXD	- LOAD DOUBLE REG DIRECT
;	SXXD	- STORE DOUBLE REG DIRECT
;	EXX	- EXCHANGE BC, DE, HL WITH BC', DE', HL'
;
;
;
;	@GENDD MACRO USED FOR CHECKING AND GENERATING
;	8-BIT JUMP RELATIVE DISPLACEMENTS
;
@GENDD	MACRO	?DD	;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS
	IF (?DD GT 7FH) AND (?DD LT 0FF80H)
	DB	100H	;Displacement Range Error on Jump Relative
	ELSE
	DB	?DD
	ENDIF		;;RANGE ERROR
	ENDM
;
;
; Z80 MACRO EXTENSIONS
;
JR	MACRO	?N	;;JUMP RELATIVE
	DB	18H
	@GENDD	?N-$-1
	ENDM
;
JRC	MACRO	?N	;;JUMP RELATIVE ON CARRY
	DB	38H
	@GENDD	?N-$-1
	ENDM
;
JRNC	MACRO	?N	;;JUMP RELATIVE ON NO CARRY
	DB	30H
	@GENDD	?N-$-1
	ENDM
;
JRZ	MACRO	?N	;;JUMP RELATIVE ON ZERO
	DB	28H
	@GENDD	?N-$-1
	ENDM
;
JRNZ	MACRO	?N	;;JUMP RELATIVE ON NO ZERO
	DB	20H
	@GENDD	?N-$-1
	ENDM
;
DJNZ	MACRO	?N	;;DECREMENT B AND JUMP RELATIVE ON NO ZERO
	DB	10H
	@GENDD	?N-$-1
	ENDM
;
LDIR	MACRO		;;LDIR
	DB	0EDH,0B0H
	ENDM
;
LDED	MACRO	?N	;;LOAD DE DIRECT
	DB	0EDH,05BH
	DW	?N
	ENDM
;
LBCD	MACRO	?N	;;LOAD BC DIRECT
	DB	0EDH,4BH
	DW	?N
	ENDM
;
SDED	MACRO	?N	;;STORE DE DIRECT
	DB	0EDH,53H
	DW	?N
	ENDM
;
SBCD	MACRO	?N	;;STORE BC DIRECT
	DB	0EDH,43H
	DW	?N
	ENDM
;
EXX	MACRO		;;EXCHANGE PRIMARY AND ALTERNATE REGISTERS
	DB	0D9H
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;


;
;Beginning of Program
;
	ORG	TPA
	jmp	start

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
;DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
;UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	000H	; EXTERNAL PATH?
MCREQD:
	DB	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	000H	; CURRENT INDIC AND DMA?
NDREQD:
	DB	000H	; NAMED DIRECTORIES?
Z2CLASS:
	DB	6	; CLASS 6
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  Extra Buffers et al
;
CLOCK:
	DB	2	;<---Put Processor Speed Here (1=1MHZ, 2=2MHZ, etc)
PAGSIZ:
	DB	24	;<---Put CRT Screen Size Here (24 Lines default)
	DB	'DU2.COM from DU.COM ver 7.5 1/23/81 by RLC'
;
;  Start of Program
;
START:
	LXI	H,0	;GET PTR TO CP/M STACK
	DAD	SP	;HL=SP
	SHLD	DUTSTK	;SAVE IT
;
	LXI	SP,DUTSTK	; SET STACK
;
	MVI	C,GVERS	;GET CP/M VERSION NR
	CALL	BDOS
	MOV	A,H	;MUST NOT BE VERSION 1.4
	ORA	L
	JRNZ	START1
	CALL	ILPRT
	DB	CR,LF,'DU2 cannot run on CP/M Version 1.4 -- Aborting',0
	JMP	EXIT1
;
;Set up local jumps to BIOS
;
START1:
	LHLD	BASE+1	;WARM BOOT POINTER
	LXI	D,3	;READY FOR ADD
	DAD	D	
	SHLD	VCONST+1	;CON: Status
	DAD	D
	SHLD	VCONIN+1	;CON: Input
	DAD	D
	SHLD	VCONOT+1	;CON: Output
	DAD	D
	SHLD	VLIST+1		;LST: Output
	DAD	D	;Skip PUNCH
	DAD	D	;Skip RDR
	DAD	D
	SHLD	VHOME+1		;Home Disk
	DAD	D
	SHLD	VSELDK+1	;Select Disk
	DAD	D
	SHLD	VSETRK+1	;Set Track
	DAD	D
	SHLD	VSTSEC+1	;Set Sector
	DAD	D
	SHLD	SETDMA+1	;Set DMA Address
	DAD	D
	SHLD	VREAD+1		;Read Block From Disk
	DAD	D
	SHLD	VWRITE+1	;Write Block To Disk
	DAD	D	;Skip LISTST
	DAD	D
	SHLD	VSCTRN+1	;CP/M 2.x Sector Translation Table
	JR	HELLO
;
;Initialization Complete -- Print Signon Message and Begin Command Processing
;
HELLO:
	CALL	GETSTP	;SET UP CP/M PARAMETERS
	CALL	INITP	;INITIALIZE BUFFER PARAMETERS
	CALL	ILPRT
	DB	CR,LF,'DU2 - Disk Utility II, Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0'
	DB	CR,LF
	DB	'Type ? for Help'
	DB	CR,LF,0
	LXI	H,TBUFF	;TO INPUT BUFF
	MOV	A,M
	ORA	A
	JRZ	PRMPTR	;NO INITIAL COMMAND FROM COMMAND LINE
;
;Got initial command, set it up
;
	MOV	B,A	;SAVE LENGTH
	DCR	B	;JUST A SPACE?
	JRZ	PRMPTR	;GOTO COMMAND PROCESSOR IF SO
	LXI	D,INBUF	;PT TO INLINE BUFFER
	INX	H	;SKIP LEN
	INX	H	;SKIP ' '
	MOV	A,M	;GET FIRST CHAR
	CPI	'/'	;IF SLASH, PRINT INITIAL HELP (TOOLSET CONVENTION)
	JZ	IHELP	;PRINT INITIAL HELP INFO
	CALL	MOVE	;COPY INPUT LINE INTO INLINE BUFFER
	MVI	A,CR	;STORE ENDING <CR>
	STAX	D
	LXI	H,INBUF	;PT TO FIRST BYTE OF INLINE BUFFER
	JR	PRMPTI	;PROCESS AS THOUGH IT WAS TYPED
;
;Input Command Line From User at Console
;
PRMPTR:
	XRA	A	;A=0
	STA	IHFLG	;Set No Initial Help
	CALL	SINBUF	;Save old INBUF into PINBUF
	CALL	RDBUF	;Read Input Line
	CALL	EXMAC	;Expand Macros
;
;Begin Processing Command Line in INBUF
;  At this point, HL points to next character to process
;
PRMPTI:
	MVI	A,0FFH	;SET INFINITE LOOP COUNT
	STA	TOGO	;LOOP COUNT FOR MULTIPLE LOOPS
	STA	TOGO+1
;
;Minor Command Loop; This is the entry point for each individual command in
;  a Command Line; Commands may be separated by semicolons in this manner
;
PROMPT	EQU	$
SETSTK:
	LXI	SP,DUTSTK	;RESET STACK
	XRA	A	;ZERO 2-UP PRINT FOR DUAL-COLUMN PRINT
	STA	TWOUP	;..SWITCH
	MVI	A,1
	STA	FTSW	;TELL SEARCH NOT TO INCR
	PUSH	H
	LXI	H,TBUFF	;SET NO-READ INPUT BUFFER ADDRESS
	SHLD	BUFAD	;FOR RDBYTE
	POP	H
	CALL	CTLCS	;ABORT?
	JRZ	PRMPTR	;..YES, READ BUFFER
;
;Do we have to position in directory after find?
;
	LDA	FINDFL
	ORA	A
	JNZ	POSDIR	;POSITION IN DIRECTORY
;
;Begin Command Evaluation -- Check for EOL and Capitalize
;
	MOV	A,M	;GET NEXT CHAR IN COMMAND LINE
	INX	H	;POINT TO FOLLOWING CHAR
	CPI	CR	;END OF LINE PHYSICALLY?
	JRZ	PRMPTR	;INPUT NEW COMMAND LINE IF SO
	CPI	EOLCH	;END OF LINE LOGICALLY?
	JRZ	PROMPT	;PROCESS NEXT ELEMENT IF SO
	CALL	UPCASE	;CAPITALIZE COMMAND
	STA	DUMTYP	;TYPE OF DUMP (A,D,H)
;
;Command dispatcher
;  If command not found, abort with error message
;  If command file, process command with HL pting to next command char and
;    A containing command letter
;
	PUSH	H	;SAVE HL
	MOV	B,A	;COMMAND IN B
	LXI	H,CMDTBL	;SCAN COMMAND TABLE FOR USER COMMAND
CMDLP:
	MOV	A,M	;GET COMMAND
	ORA	A	;0=END OF TABLE
	JZ	WHAT
	CMP	B	;COMPARE COMMAND
	JRZ	CMDGO
	INX	H	;PT TO ADR
	INX	H
	INX	H	;PT TO NEXT CMND
	JR	CMDLP
CMDGO:
	INX	H	;PT TO ADDRESS LOW
	MOV	E,M
	INX	H	;PT TO ADDRESS HIGH
	MOV	D,M
	MOV	A,B	;COMMAND BACK INTO A
	POP	H	;RESTORE HL
	PUSH	D	;PLACE ADDRESS ON STACK
	RET		;"RUN COMMAND"
;
;Macro Expansion Routine -- Expand Macros
;
EXMAC:
	LXI	H,INBUF	;PT TO INPUT LINE
	LXI	D,CTEMP	;BUILD INTO TEMPORARY BUFFER
EXMAC1:
	MOV	A,M	;GET CHAR
	CPI	'0'	;SKIP IF LESS THAN '0'
	JRC	EXMAC2
	CPI	'9'+1	;CHECK FOR RANGE
	JRNC	EXMAC2
	INX	H	;PT TO NEXT CHAR
	PUSH	H	;SAVE PTR TO NEXT CHAR IN LINE
	SUI	'0'	;CONVERT TO BINARY (0-9)
	MOV	B,A	;RESULT IN B
	MVI	C,0
	LXI	H,MTABL	;PT TO BASE OF MACROS
	DAD	B	;PT TO MACRO
	CALL	COPYM	;COPY MACRO INTO LINE
	DCX	D	;BACK UP OVER <CR>
	POP	H	;GET PTR TO NEXT CHAR IN COMMAND LINE
EXMAC2:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CALL	MTEST	;TEST FOR END OF BUFFER
	CPI	CR	;DONE?
	JRZ	EXMAC3
	CPI	EOLCH	;LOGICAL EOL?
	JRNZ	EXMAC2
	JR	EXMAC1	;PROCESS NEXT COMMAND
EXMAC3:
	LXI	H,CTEMP	;COPY COMMAND LINE BACK
	LXI	D,INBUF	;INTO INBUF
	CALL	COPYCR	;COPY TO <CR>
	LXI	H,INBUF	;PT TO INBUF
	RET		;EXPANSION COMPLETE
;
;Copy Macro Into Command Line Buffer
;
COPYM:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CALL	MTEST	;CHECK FOR LIMIT
	CPI	CR	;END OF MACRO?
	JRNZ	COPYM
	RET
;
;Test for Buffer Full
;
MTEST:
	PUSH	H	;SAVE HL
	PUSH	PSW	;SAVE A
	LXI	H,CTEMPX	;CHECK FOR END OF BUFFER
	MOV	A,H	;GET PAGE
	CMP	D	;CHECK PAGE
	JRZ	MACERR
	POP	PSW	;GET A
	POP	H	;GET HL
	RET
;
;Macro Command Expansion Error
;
MACERR:
	CALL	ILPRT
	DB	CR,LF,'Error -- Macro Expanded Command Line too Long',0
	JMP	PRMPTR	;NEW COMMAND
;
;Save INBUF into PINBUF for later processing by '@' command
;
SINBUF:
	LXI	H,INBUF		;PT TO INBUF
	LXI	D,PINBUF	;PT TO PINBUF (PREVIOUS INBUF)
;
;Copy (HL) to (DE) until <CR> Encountered
;
COPYCR:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CPI	CR	;DONE?
	JRNZ	COPYCR
	RET

;
;Command Not Found Error
;
WHAT:
	POP	H	; RESTORE HL
	CALL	ILPRT
	DB	'Invalid Command at or after ',0
	MOV	A,B	;GET COMMAND LETTER
	CALL	TYPE	;PRINT IT
	JMP	PRMPTR
;
;Memory full error
;
MEMFUL:
	CALL	ILPRT
	DB	'+++ Out of memory +++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;COMMAND:  @
;Repeat Previous Command Line
;
PCMD:
	MOV	A,M	;GET NEXT CHAR
	CPI	CR	;SHOULD BE <CR>
	JRZ	PCMD1
	CALL	ILPRT
	DB	CR,LF,'Warning:  Remainder of Command Line after "@" Deleted',0
PCMD1:
	CALL	ILPRT
	DB	CR,LF,'Command --',CR,LF,0
	LXI	H,PINBUF	;GET PREVIOUS COMMAND
	LXI	D,INBUF		;COPY INTO INBUF
PCMD2:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CPI	CR	;END OF LINE?
	PUSH	PSW	;SAVE FLAG
	CALL	TYPE	;PRINT CHAR
	POP	PSW	;GET FLAG
	JRNZ	PCMD2
	MVI	A,LF	;<LF>
	CALL	TYPE
	LXI	H,INBUF	;RESTART COMMAND PROCESSING
	JMP	PRMPTI	;INCLUDE LOOP CAPABILITY
;
;COMMAND:  :
;Define or Print Macro
;:n<text> Defines Macro n, 0<=n<=9; ::n Prints Macro n, 0<=n<=9
;
MAC:
	MOV	A,M	;GET NEXT CHAR
	CALL	UPCASE	;CAPITALIZE
	CPI	'P'	;PRINT MACRO?
	JNZ	MACROD	;IF NOT, DEFINE MACRO
	INX	H	;PT TO MACRO NUMBER
	MOV	A,M	;GET IT
	CALL	UPCASE	;CAPITALIZE
	CPI	'@'	;PRINT PREVIOUS COMMAND?
	JRZ	PCPR
	PUSH	PSW	;SAVE A
	CALL	ILPRT
	DB	'Macro Definitions --',0
	POP	PSW	;GET A
	CPI	'A'	;PRINT ALL MACROS?
	JRZ	AMACPR
	CALL	MNUM	;CHECK FOR VALID NUMBER AND RETURN # IN D
	INX	H	;PT TO CHAR AFTER MACRO NUMBER
	CALL	MACPR	;PRINT MACRO WHOSE NUMBER IS IN D
	JMP	PROMPT
;
;Print Previous Command
;
PCPR:
	INX	H	;PT TO CHAR AFTER '@'
	LXI	D,PROMPT	;SET UP RET ADR
	PUSH	D	;RETURN ADR ON STACK
	PUSH	H	;SAVE PTR
	CALL	ILPRT
	DB	'Previous Command Line Definition --'
	DB	CR,LF,'@: ',0
	LXI	H,PINBUF	;PT TO PREVIOUS COMMAND
	JMP	MPRINT		;USE MACRO PRINT FACILITY
;
;Print All Macros
;
AMACPR:
	INX	H	;PT TO CHAR AFTER 'A'
	MVI	D,0	;SET FOR FIRST MACRO
AMPRL:
	CALL	MACPR	;PRINT MACRO WHOSE NUMBER IS IN D
	INR	D	;INCREMENT MACRO NUMBER
	MOV	A,D	;GET VALUE
	CPI	10	;DONE?
	JRNZ	AMPRL
	JMP	PROMPT	;CONTINUE PROCESSING
;
;Print Macro Whose Number (0-9) is in D
;
MACPR:
	PUSH	H	;SAVE PTR
	CALL	ILPRT	;PRINT HEADER
	DB	CR,LF,0
	MOV	A,D	;GET NUMBER
	ADI	'0'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT
	CALL	ILPRT
	DB	': ',0
	LXI	H,MTABL	;PT TO TABLE OF MACROS
	MVI	E,0	;PAGE OFFSET OF ZERO; MACRO NUMBER ALREADY IN D
	DAD	D	;PT TO MACRO
MPRINT:
	MOV	A,M	;GET CHAR
	INX	H	;PT TO NEXT
	CPI	CR	;END OF MACRO?
	PUSH	PSW	;SAVE FLAG
	CALL	TYPE	;PRINT CHAR
	POP	PSW	;GET FLAG
	JRNZ	MPRINT
	MVI	A,LF	;<LF>
	CALL	TYPE
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;Check char in A for valid Macro Number (0-9), print error message if
; not, return number in D if so
;
MNUM:
	SUI	'0'	;CONVERT TO 0-9
	JRC	MNERR	;ERROR IF LESS
	CPI	10	;RANGE?
	JRNC	MNERR
	MOV	D,A	;RESULT IN D
	RET
MNERR:
	CALL	ILPRT
	DB	CR,LF,'Invalid Macro Number Specified in Command',0
	JMP	PRMPTR	;NEW COMMAND
;
;Define Macro
;
MACROD:
	CALL	MNUM	;CHECK NUMBER AND RETURN IN D
	INX	H	;PT TO CHAR AFTER MACRO NUMBER
	PUSH	H	;SAVE PTR
	LXI	H,MTABL	;PT TO MACRO TABLE
	MVI	E,0	;SET EVEN PAGE
	DAD	D	;PT TO MACRO ENTRY IN HL
	XCHG		;... IN DE
	POP	H	;PT TO MACRO TEXT
	CALL	COPYCR	;COPY TO <CR>
	JMP	PRMPTR	;NEW COMMAND
;
;COMMAND:  !
;Delay for user input
;
UWAIT:
	CALL	WAIT	; USE WAIT ROUTINE
	JMP	PROMPT
;
;COMMAND:  #
;Print disk statistics
;
STATS:
	PUSH	H	;SAVE POINTER TO NEXT COMMAND
	CALL	ILPRT
	DB	'+=============================+',CR,LF
	DB	'|    -- Queue Information --  |',CR,LF
	DB	'+-----------------------------+',CR,LF
	DB	0
	CALL	QSTATS	;PRINT STATUS INFO
	CALL	ILPRT
	DB	CR,LF,CR,LF
	DB	'+=============================+',CR,LF
	DB	'|    -- Disk Information --   |',CR,LF
	DB	'+-----------------------------+',CR,LF
	DB	CR,LF,'Disk Drive:		',0
	LDA	DRIVE
	ADI	'A'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT DRIVE LETTER
	CALL	ILPRT
	DB	CR,LF,'Tracks:			',0
	LHLD	MAXTRK	;PRINT NUMBER OF TRACKS
	INX	H
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,'Sectors/Track:		',0
	LHLD	SPT	;PRINT NUMBER OF SECTORS/TRACK
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,'Group Size:		',0
	LDA	BLM	;PRINT SIZE OF A GROUP
	INR	A
	MOV	L,A
	MVI	H,0
	CALL	DEC
	CALL	ILPRT
	DB	' Blocks/Group'
	DB	CR,LF,'Total Groups:		',0
	LHLD	DSM	;PRINT TOTAL NUMBER OF GROUPS ON A DISK
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,'Directory Entries:	',0
	LHLD	DRM	;PRINT NUMBER OF DIRECTORY ENTRIES
	INX	H
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,'System Tracks:		',0
	LHLD	SYSTRK	;PRINT NUMBER OF SYSTEM TRACKS
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF
	DB	'===============================',CR,LF,0
	POP	H	;RESTORE POINTER TO NEXT COMMAND
	JMP	PROMPT
;
;COMMAND:  N
;The following command resets the disk
;system thru CP/M, and may be usable for
;changing the disk density or format.
;This can only be done if your BIOS resets
;the auto-density select parameters at
;every track-zero access.
;
NEWDSK:
	PUSH	H	;SAVE POINTER TO NEXT LETTER
	MVI	C,RESETDK	;BDOS RESET DISK FUNCTION
	CALL	BDOS
	LDA	DRIVE	;RESELECT CURRENT DRIVE
	MOV	C,A
	POP	H
	CALL	SELECT
	JMP	PROMPT
;
;COMMAND:  Q
;Queue Control
;
QUEUER:
	MOV	A,M	;GET 2ND ARGUMENT
	CALL	UPCASE	;CAPITALIZE
	CPI	EOLCH	;END OF LINE?
	JRZ	QSTAT	;STATUS REPORT
	CPI	CR	;END OF LINE?
	JRZ	QSTAT
	INX	H	;PT TO AFTER KEY CHAR
	PUSH	H	;SAVE PTR
	CPI	'Z'	;ZERO QUEUE?
	JRZ	QZERO
	CPI	'S'	;SAVE QUEUE?
	JZ	QFSAVE
	POP	H	;GET PTR
	CALL	ILPRT
	DB	'Invalid Queue Command',CR,LF,0
	JMP	PRMPTR	;ABORT LINE ON ERROR
;
;  Zero the Queue
;
QZERO:
	LXI	H,DIRECT	;ZERO QUEUE
	SHLD	QNXT	;SET NEXT
	SHLD	QLST	;SET LAST
	LXI	H,0	;ZERO COUNT
	SHLD	QCNT
	POP	H	;GET PTR AND FALL THRU TO QSTAT
;
;  Print Status of Queue
;
QSTAT:
	PUSH	H	;SAVE PTR TO NEXT CHAR
	CALL	ILPRT
	DB	'** Queue Status Summary **',CR,LF,0
	CALL	QSTATS	;PRINT STATUS
	POP	H	;RESTORE PTR
	JMP	PROMPT
QSTATS:
	LHLD	QCNT	;GET SIZE OF QUEUE
	CALL	PRQCNT	;PRINT DATA
	CALL	PRQSPAC	;PRINT SPACE AVAILABLE INFO
	CALL	ILPRT
	DB	'Address of Head of Queue: ',0
	LHLD	QNXT	;PRINT ADDRESS OF HEAD OF QUEUE
	MOV	B,H	;... ADDRESS IN BC
	MOV	C,L
	CALL	HEXB1	;PRINT IN HEX
	CALL	ILPRT
	DB	' Hex',CR,LF
	DB	'Address of Tail of Queue: ',0
	LHLD	QLST	;PRINT ADDRESS OF TAIL OF QUEUE
	MOV	B,H
	MOV	C,L
	CALL	HEXB1
	CALL	ILPRT
	DB	' Hex',CR,LF,0
	RET
;
;  Print Amount of Space Left in Queue
;
PRQSPAC:
	LXI	B,-1	;SET COUNT
	LHLD	QLST	;GET PTR TO QUEUE TAIL
QSTAT1:
	INX	B	;INCREMENT COUNT
	LXI	D,80H	;PT TO NEXT QUEUE ELEMENT
	DAD	D
	XCHG		;WRAP AROUND
	CALL	QWRAP
	LHLD	QNXT	;GET PTR TO FIRST ELEMENT
	XCHG
	MOV	A,H	;COMPARE
	CMP	D
	JRNZ	QSTAT1
	MOV	A,L
	CMP	E
	JRNZ	QSTAT1
	MOV	H,B	;HL=BLOCK COUNT
	MOV	L,C
	CALL	DEC	;PRINT AS DECIMAL
	CALL	ILPRT
	DB	' Blocks Left in Queue',CR,LF,0
	RET
;
;  Save Queue as a File
;
QFSAVE:
	MOV	A,M	;GET FIRST CHAR OF FILE NAME
	CPI	EOLCH	;EOL?
	JZ	WHAT
	CPI	CR	;EOL?
	JZ	WHAT
	LXI	D,FCB	;START TO FILL FCB
	XRA	A	;A=0
	STAX	D	;SELECT DEFAULT DRIVE
	INX	D	;PT TO FILE NAME
	MVI	B,8	;SAVE FILE NAME
	CALL	MVNAME
	MVI	B,3	;SAVE FILE TYPE
	CALL	MVNAME
	PUSH	H	;SAVE PTR TO NEXT CHAR
	LHLD	QCNT	;ANY ELEMENTS IN QUEUE?
	MOV	A,H
	ORA	L
	JZ	QEMPTY
	PUSH	H	;SAVE QUEUE COUNT
	CALL	NORITE	;CAN'T WRITE NOW
	LXI	D,FCB	;PT TO FCB
	CALL	FCBINIT	;INIT FCB
	MVI	C,DELF	;DELETE FILE
	PUSH	D	;SAVE DE
	CALL	BDOS
	POP	D
	CALL	FCBINIT	;INIT FCB AGAIN
	MVI	C,MAKEF	;CREATE FILE
	CALL	BDOS
	POP	B	;GET QUEUE COUNT IN BC
	LHLD	QNXT	;PT TO NEXT BLOCK IN QUEUE
QFS1:
	PUSH	B	;SAVE COUNT
	LXI	D,TBUFF	;COPY INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	XCHG		;PT TO NEXT QUEUE BLOCK IN DE
	CALL	QWRAP	;WRAP AROUND
	PUSH	D	;SAVE PTRS
	LXI	D,FCB	;PT TO FCB
	MVI	C,WRITEF	;WRITE BLOCK TO FILE
	CALL	BDOS
	POP	H	;GET PTR TO NEXT BLOCK
	POP	B	;GET COUNT
	DCX	B	;COUNT DOWN
	MOV	A,B	;DONE?
	ORA	C
	JRNZ	QFS1
	LXI	D,FCB	;CLOSE FILE
	MVI	C,CLOSEF
	CALL	BDOS
	CALL	ILPRT
	DB	'Queue Saved in File',CR,LF,0
	POP	H	;PT TO NEXT CHAR
	JMP	PROMPT
FCBINIT:
	PUSH	D	;SAVE PTR
	LXI	H,12	;SKIP TO EX FIELD
	DAD	D
	MVI	B,24	;ZERO 36 BYTES
	XRA	A	;A=0
FCBIN1:
	MOV	M,A	;STORE ZEROES
	INX	H
	DJNZ	FCBIN1
	POP	D	;RESTORE PTR
	RET
;
;COMMAND:  *
;Repeat buffer contents
;
REPEAT:
	CALL	DECIN	;NN SPECIFIED?
	MOV	A,D
	ORA	E
	JRZ	NNN	;NO -- SET FOR INFINITE LOOP OR SIMPLE REPEAT
	LHLD	TOGO	;LOAD LOOP FLAG
	INX	H	;TEST FOR FIRST TIME
	MOV	A,H
	ORA	L	;WAS IT 0FFFFH?; IF SO, WE HAVE NEW VALUE
	JRNZ	NNN	;NO: COUNTING
	XCHG		;GET COUNT
	SHLD	TOGO	;SET COUNT
;
NNN:
	LHLD	TOGO	;GET CURRENT COUNT
	XCHG		;DE=CURRENT COUNT, HL=COUNT LIMIT
	LXI	H,INBUF	;PT TO FIRST CHAR FOR REPEAT
	INX	D	;TEST FOR 0FFFFH
	MOV	A,D	;IF 0FFFFH, INX D MADE DE=0
	ORA	E
	JZ	PROMPT	;CONTINOUS LOOP IF 0FFFFH
	DCX	D	;COUNT DOWN
	DCX	D	;MAKE UP FOR PREV INX D
	XCHG
	SHLD	TOGO	;SET NEW COUNT (1 LESS THAN BEFORE)
	MOV	A,H	;ALL DONE?
	ORA	L
	XCHG		;GET BACK INBUF PTR IN HL
	JNZ	PROMPT	;KEEP GOING IF NOT YET ZERO
	JMP	PRMPTR	;ALL DONE
;
;COMMAND:  U
;Set CP/M 2.x user number
;
USER:
	CALL	DECIN	;GET REQUESTED USER NO.
	LDA	MUSER	;GET MAX USER
	MOV	B,A	;... IN B
	MOV	A,E
	CMP	B	;VALID?
	JNC	USRERR
	MOV	A,D	;HIGH-ORDER BYTE MUST BE ZERO FOR VALID NUMBER
	ORA	A
	JNZ	USRERR
	MOV	A,E	;SAVE USER NUMBER
	STA	UNUM
	MVI	C,SUSER	;SET USER NUMBER
	PUSH	H		;SAVE CHAR POINTER
	CALL	BDOS		;SET USER NO.
	POP	H
	JMP	PROMPT
USRERR:
	CALL	ILPRT
	DB	'User Number Out of Range',CR,LF,0
	JMP	PRMPTR
;
;COMMAND:  P
;Toggle print flag
;
PRNTFF:
	LDA	PFLAG	;TOGGLE PRINT FLAG
	XRI	1
	STA	PFLAG
	JMP	PROMPT
;
;COMMAND:  Z
;Sleep routine, in seconds
;
SLEEP:
	CALL	DECIN	;GET COUNT IF ANY
	MOV	A,E	;ANY?
	ORA	A
	JRNZ	SLEPLP
	MVI	E,1	; 1 SEC DEFAULT
;
SLEPLP:
	LDA	CLOCK	; GET CLOCK SPEED
	MOV	D,A
;
SLEEP1:
	LXI	B,41700	; APPROX 1 SEC @ 1MHz
;
SLEEP2:
	DCX	B	;COUNT DOWN FOR 1 MHz [5 CYCLES]
	MOV	A,B	;[5 CYCLES] <-- TOTAL TIME: 24 CYCLES
	ORA	C	;[4 CYCLES] <-- (24 MU-SECS AT 1MHz)
	JNZ	SLEEP2	;[10 CYCLES]
	PUSH	D
	CALL	CTLCS	;ABORT?
	POP	D
	JZ	PRMPTR
	DCR	D	;COUNT DOWN FOR CLOCK SPEED
	JNZ	SLEEP1
	DCR	E	;COUNT DOWN NUMBER OF REQUESTED SECONDS
	JNZ	SLEPLP
	JMP	PROMPT
;
;Check for control-C or S
;
CTLCS:
	CALL	CONST	;CHAR AVAILABLE?
	ORA	A
	JRNZ	GETC
	ORI	1	;NO CHAR, RETURN NZ
	RET
;
GETC:	CALL	CONIN	;INPUT CHAR
	ANI	1FH	;ALLOW ASCII
	CPI	'S'-40H	;WAIT FOR NEXT CHAR IF ^S OR S OR s
	CZ	CONIN
	CPI	'C'-40H	;CHECK FOR ^C OR C OR c
	RET		;0 SET IF CTL-C
;
;Initialize Memory Buffers
;
INITP:
	XRA	A	;A=0
	STA	HEXAD	;CLEAR ADDRESS
	STA	HEXAD+1
	STA	PFLAG	;SET NO PRINT
	STA	SAVEFL	;SET NO SAVE DONE
	STA	WRFLG	;MAY NOT WRITE
	STA	DIRPOS	;SET NO DIRECTORY POSITION
	STA	FINDFL	;SET NO POSITION
	INR	A	;A=1
	STA	FTSW	;SET SEARCH WITHOUT INCREMENT
	STA	NOTPOS	;NOT POSITIONED
	LXI	H,0	;HL=0
	SHLD	QCNT	;SET NO ELEMENTS IN QUEUE
	SHLD	MFPTR	;SET NO MULTI FILE PTR
	SHLD	CURTRK	;SET TRACK 0
	INX	H	;HL=1
	SHLD	CURSEC	;SET LOGICAL SECTOR 1
	SHLD	PHYSEC	;SET PHYSICAL SECTOR 1
	MVI	A,CR	;CLEAR PREVIOUS COMMAND
	STA	PINBUF	;SET PREVIOUS COMMAND TO NIL
	LXI	H,DIRECT	;SET FIRST AND LAST QUEUE ELEMENT PTRS
	SHLD	QNXT
	SHLD	QLST
	LXI	H,MTABL	;CLEAR MACRO TABLE
	MVI	B,10	;10 ENTRIES
INITP1:
	MVI	M,CR	;STORE <CR>
	INR	H	;PT TO NEXT PAGE
	DJNZ	INITP1
	RET
;
;Set up flags, etc, at initialization
;Find our way at initialization
;
GETSTP:
	MVI	A,CR	;INITIALIZE INPUT BUFFER
	STA	INBUF	;EMPTY BUFFER
	MVI	C,SUSER	;GET USER NUMBER
	MVI	E,0FFH	;GET USER
	CALL	BDOS
	STA	UNUM	;SET USER NUMBER
	MVI	C,GETDSK
	CALL	BDOS	;GET CURRENT DISK
	MOV	C,A	;WE HAVE TO SELECT
	JMP	SELECT	;TO GET THE DPH
;
;COMMAND:  L
;Log in the selected disk
;
LOGIN:
	CALL	DOLOG
	JMP	PROMPT
;
DOLOG:
	MOV	A,M	;DISK REQUESTED?
	LXI	D,0
	CPI	CR	;NO REQUEST OF PHYSICAL EOL
	JRZ	LGNODK
	CPI	EOLCH	;NO REQUEST IF LOGICAL EOL
	JRZ	LGNODK
	CALL	UPCASE	;CAPITALIZE
	INX	H	;POINT TO NEXT CHAR
	SUI	'A'	;CONVERT TO 0-15
	MOV	C,A	;DISK NUMBER IN C
	LDA	MDISK	;GET MAX DISK
	MOV	B,A	;... IN B
	MOV	A,C
	CMP	B
	JRC	SELECT
	CALL	ILPRT
	DB	'Disk Letter Out of Range',CR,LF,0
	JMP	PRMPTR
;
;Select Disk Whose Number is in C (A=0, B=1, etc)
;
SELECT:
	PUSH	H	;SAVE PTR TO NEXT COMMAND LETTER
	MOV	A,C
	STA	DRIVE	;REMEMBER LATER WHERE WE ARE
;
VSELDK: CALL	$-$	;ADDR FILLED IN BY 'INIT'
	MOV	A,H
	ORA	L
	JZ	WHAT	;SELECT ERROR
	MOV	E,M	;GET THE SECTOR TABLE PNTR
	INX	H
	MOV	D,M
	INX	H
	XCHG
	SHLD	SECTBL	;SET THE SECTOR TABLE PTR
	LXI	H,8	;OFFSET TO DPBPTR
	DAD	D
	MOV	A,M	;PICK UP DPB POINTER
	INX	H	;  TO USE
	MOV	H,M	;  AS PARAMETER
	MOV	L,A	;  TO LOGIT
	CALL	LOGIT
	LHLD	SYSTRK	;RESET TRACK AND SECTOR
	XCHG		;  TO DIRECTORY
	CALL	SETTRK	;  ON EVERY
	LXI	D,1	;  LOGIN
	CALL	SETSEC	;  CHANGE
	LHLD	PHYSEC	;THIS LOGIC WILL TELL
	MOV	A,H	;  IF FIRST SEC
	ORA	L	;  IS PHYSICAL 0
	STA	FIRST0
	CALL	CLCSUB	;CALCULATE WHAT GROUP/GRPDISP WE ARE IN
	POP	H	;GET PTR TO NEXT LETTER
;
LGNODK:
	CALL	NORITE	;SET NO DISK I/O DONE (NO POSITION)
	RET
;
;Read in the disk directory
;
REDDIR:
	PUSH	H	;SAVE PTR TO NEXT LETTER
	CALL	NORITE	;POSITIONING LOST
	LHLD	SYSTRK	;SAVE CURRENT TRACK
	SHLD	CURTRK
	LXI	H,1	;SET SECTOR 1
	SHLD	CURSEC
	LHLD	DRM	;GET DIR SIZE FROM DPB
	INX	H	;MAKE 1-RELATIVE
	CALL	ROTRHL
	CALL	ROTRHL	;DIVIDE BY 4 (4 NAMES/SECTOR)
	MOV	B,H	;BC=NUMBER OF BLOCKS TO READ
	MOV	C,L
	LXI	D,DIRECT	;DMA ADDR
;
;Read Disk Directory Loop
;
RDIRLP:
	PUSH	B	;SAVE REGS
	PUSH	D
	MOV	B,D	;BC=DMA ADDRESS
	MOV	C,E
	LDA	BDOS+2	;CHECK MEM AVAIL
	DCR	A	;ARE WE RNNING INTO BDOS?
	CMP	D
	JC	MEMFUL	;MEMORY FULL ERROR IF SO
	CALL	SETDMA	;SET DMA ADDRESS TO THAT IN BC
	LHLD	CURTRK	;SET TRACK
	XCHG
	CALL	SETTRK
	LHLD	CURSEC	;SET SECTOR
	XCHG
	CALL	SETSEC
	CALL	READ	;READ DIRECTORY BLOCK
	CALL	NXTSEC	;INCREMENT TO NEXT SECTOR
	POP	D
	POP	B
	LXI	H,80H	;ADVANCE TO NEXT DMA ADDRESS
	DAD	D
	XCHG		;DE=NEXT DMA ADDRESS
	DCX	B	;COUNT DOWN DIRECTORY BLOCKS
	MOV	A,B
	ORA	C
	JRNZ	RDIRLP
	LXI	B,TBUFF	;RESET DMA ADDRESS TO TBUFF
	CALL	SETDMA
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;COMMAND:  M
;Map the directory
;
MAP:
	PUSH	H	;SAVE PTR
	LHLD	QCNT	;GET COUNT
	MOV	A,H
	ORA	L
	POP	H
	JZ	MAP1	;PROCEED IF QUEUE EMPTY
	CALL	ILPRT	;PRINT ABORT MESSAGE
	DB	CR,LF,'MAP not permitted -- Block Queue would be overlaid',0
	JMP	PRMPTR
MAP1:
	CALL	PAGSET	;SET PAGING COUNTER
	XRA	A
	STA	ONLY1	;SET FLAG FOR ALL GROUPS (NOT ONLY 1)
	CALL	REDDIR	;READ IN DIRECTORY
	MVI	C,0	;INIT START GRP #
	LDA	AL0	;READ DIR GRP BITS
	CALL	COLECT	;COLLECT COUNT OF DIR GRPS..
	LDA	AL1	;..IN REGISTER C
	CALL	COLECT
	MVI	B,0	;BC NOW HAS A DEFAULT START GRP #
	CALL	HEXIN	;GET SPECIFIED GROUP IF ANY
	PUSH	H	;SAVE INBUF PTR
	MOV	A,E	;GET START
	ORA	D	;NOTHING?
	JRZ	MAPDF	;..YES, DFLT
	MVI	A,0FFH	;SET FLAG FOR ONLY 1 GROUP
	STA	ONLY1
	MOV	B,D	;GET VALUE IN BC
	MOV	C,E
;
MAPDF:
	CALL	HEXB	;PRINT FIRST GROUP NUMBER
	MVI	A,'-'	;PRINT SEPARATOR
	CALL	TYPE
	MVI	A,' '	;SET NO DUPLICATES
	STA	DUPFLG
	CALL	GETGRP	;GET GRP(C) TO HL
;
MAPCNT:
	INX	B	;NEXT GRP #
	PUSH	H
	LHLD	DSM	;GET HIGHEST GRP #
	INX	H	;PLUS 1 FOR COMPARISON
	MOV	A,L	;WHEN BC REACHES DSM+1..
	CMP	C	;..THEN WE HAVE EXCEEDED..
	JRNZ	MAPC1	;..THE DISK CAPACITY..
	MOV	A,H
	CMP	B
;
MAPC1:
	POP	H
	JRZ	MAPEND	;..AND WE ARE DONE
	PUSH	H
	CALL	GETGRP	;GET ANOTHER
	POP	D	;SEE IF SAME
	CALL	CTLCS	;ABORT?
	JRZ	MAPND2
	MOV	A,D
	CMP	H
	JRNZ	MAPDIF
	MOV	A,E
	CMP	L
	JRZ	MAPCNT	;SAME, CONTINUE
;
;Different file encountered
;
MAPDIF:
	DCX	B
	CALL	HEXB	;PRINT ENDING GROUP NUMBER
	INX	B
	XCHG
	CALL	MAPNAM	;PRINT FILE NAME
	LDA	ONLY1	;ONLY 1 NAME TO BE PRINTED?
	ORA	A	;0=NO
	JRNZ	MAPND1
	JR	MAPDF
;
;End of map
;
MAPEND:
	DCX	B	;GET LAST
	CALL	HEXB	;PRINT LAST GROUP NUMBER
	CALL	MAPNAM	;PRINT FILE NAME
	CALL	WAIT	;DELAY FOR USER
MAPND1:
	POP	H
	CALL	CRLF	;NEW LINE
;
;End of map - reposition to previous group
;
MAPND2:
	PUSH	H
	LHLD	GROUP	;POINT TO GROUP IN DE
	XCHG
	JMP	POSGP2
;
;Print file name pointed to by HL
;
MAPNAM:
	CALL	SPACE	;LEADING SPACE
	MOV	A,H
	ORA	L	;NONE?
	JZ	NONAME
	MOV	A,M	;SEE IF ALLOC
	CPI	0E5H	;FREE?
	MVI	A,' '	;MARK ALLOCATED
	JRNZ	MPNSP1
	MVI	A,'('	;MARK NOT ALLOCATED (ERASED FILE)
;
MPNSP1:
	CALL	TYPE	;PRINT ALLOCATION INDICATOR (SPACE OR '(')
	PUSH	H	;SAVE POINTER
	MOV	A,M
	CALL	HEX	;SHOW USER NUMBER
	CALL	SPACE
	INX	H	;SKIP USER BYTE
	PUSH	B
	MVI	B,8	;PRINT FILE NAME
	CALL	MAPN2
	MVI	A,'.'	;PRINT DECIMAL SEPARATOR
	CALL	TYPE
	MVI	B,3	;PRINT FILE TYPE
	CALL	MAPN2
	LDA	DUPFLG	;DUPLICATE?
	CALL	TYPE	;SPACE OR STAR
	POP	B
	MOV	A,M	;GET EXT
	CALL	HEX	;PRINT EXTENT NUMBER
	POP	H
	MOV	A,M
	CPI	0E5H	;DELETED ENTRY?
	MVI	A,' '	;PRINT ENDING SPACE
	JNZ	MPNSP2
	MVI	A,')'	;PRINT ALLOCATION FLAG
;
MPNSP2:
	CALL	TYPE	;")" IF ERASED FILE OR SPACE IF NOT
	JR	FLIP
;
NONAME:
	CALL	ILPRT
	DB	'    ++ Free ++      ',0
;
FLIP:
	LDA	TWOUP	;FLIP FLAG FOR TWO ENTRIES PER LINE
	XRI	1
	STA	TWOUP
	JZ	PAGER	;NEW LINE WITH PAGING IF REQUIRED
;
DELIM:
	MVI	A,':'	;PRINT DELIMITER BETWEEN ADJACENT ENTRIES ON LINE
	CALL	TYPE
	JMP	SPACE
;
;Print name pted to by HL, length in B
;
MAPN2:
	MOV	A,M
	ANI	7FH	;STRIP POSSIBLE 2.x ATTRIBUTE BIT
	INX	H
	CPI	' '	;PRINTABLE?
	JRC	MAPN2H	;..NO, IN HEX
	CPI	7EH	;7E IS LEADIN ON SOME CRTS
	JRC	MAPN2A
;
MAPN2H:
	CALL	BHEX	;PRINT A AS HEX CHARS
	JMP	MAPN2Z
;
MAPN2A:
	CALL	TYPE	;PRINT AS CHAR
;
MAPN2Z:
	DJNZ	MAPN2
	RET
;
;Find which file group (BC) belongs to
;
GETGRP:
	LHLD	DRM	;MAX DIR ENTRY #
	INX	H	;MAKE 1-RELATIVE
	SHLD	FILECT
	LXI	H,0
	SHLD	MFPTR	;SET MULTI-FILE (MORE THAN ONE USER) PTR
	LXI	H,DIRECT	;PT TO DIRECTORY
;
GETGLP:
	PUSH	H	;SAVE POINTER TO NAME
	MOV	A,M	;PICK UP DN BYTE
	CPI	0E5H	;ERASED?
	JRZ	GETGNF
	LXI	D,14	;NOW GET RECORD COUNT
	DAD	D	;  S2 PORTION ..
	MOV	A,M	;  IS 0 IN CP/M 1.4
	ANI	0FH
	MOV	E,A
	INX	H
	MOV	A,M
	ORA	E
	JZ	GETGNF
	MVI	E,16	;FIRST SET FOR 8-BIT GRPS
	LDA	DSM+1
	ORA	A
	JRZ	SMALGP
	MVI	E,8	;NOPE, BIG GROUPS
;
SMALGP:
	MOV	D,A	;SAVE GRP SIZE INDICATOR
;
GETGL2:
	INX	H	;POINTING INTO DM FIELD
	CALL	GRPCMP	;COMPARE BC GP # AGAINST 1 DM FLD
	JRNZ	NOTGOT	;JUMP IF NOT FOUND
;
;Found the file
;
	PUSH	H	;SAVE GROUP PTR
	LHLD	MFPTR
	MOV	A,H	;ANY ENTRIES?
	ORA	L
	POP	H	;GET PTR
	XTHL		;SAVE ENTRY START AND SAVE PTR
	JRZ	MPFRST	;IF ZERO, THEN FIRST ENTRY
	MVI	A,'*'	;SET MULTI FLAG
	STA	DUPFLG
MPFRST:
	SHLD	MFPTR	;SAVE POINTER
	XTHL		;RESTORE ENTRY START AND GET PTR
NOTGOT:
	DCR	E	;COUNT DOWN
	JRNZ	GETGL2	;GO TEST SOME MORE
;
GETGNF:
	POP	H	;NOT THIS ONE
	LXI	D,32	;SO GO TO NEXT
	DAD	D
	XCHG
	LHLD	FILECT	;THERE IS LIMIT TO EVERYTHING
	DCX	H
	SHLD	FILECT
	MOV	A,H
	ORA	L
	XCHG		;RE-ALIGN
	JRNZ	GETGLP
;
;Set the allocation address, if any
;
	LHLD	MFPTR	;GET ADDRESS
	RET
;
;COMMAND:  <
;Save the current sector
;	Special Form of <S saves current block onto queue
;	Special Form of <G saves indicated group onto queue
;
SAVE:
	LDA	WRFLG	;READ DONE?
	ORA	A
	JZ	BADW	;NONE TO SAVE
	MOV	A,M	;CHECK FOR 'S'
	CALL	UPCASE	;CAPITALIZE
	CPI	'B'	;BLOCK SAVE
	JRZ	QSAV	;SAVE ON STACK
	CPI	'G'	;GROUP SAVE
	JZ	SAVEG
	PUSH	H
	LXI	H,TBUFF	;COPY FROM TBUFF
	LXI	D,SAVBUF	;INTO SAVBUF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	MVI	A,1	;..SHOW
	STA	SAVEFL	;..SAVED EXISTS
	POP	H	;GET PTR TO NEXT CHAR
	JMP	PROMPT
;
;  Save Block on Queue
;
QSAV:
	INX	H	;SKIP OVER 2ND <
	PUSH	H	;SAVE PTR TO NEXT CHAR
	LHLD	QLST	;SEE IF ANOTHER SAVE WILL FILL QUEUE
	LXI	D,128	;SET HL TO PT TO END OF NEXT SECTOR IN QUEUE
	DAD	D
	XCHG		;DE PTS TO END OF NEXT BLOCK
QSAV0:
	LHLD	QNXT	;SEE IF QUEUE IS FULL NOW
	MOV	A,H	;MAY BE SAME
	CMP	D
	JRNZ	QSAV1
	MOV	A,L	;MAY NOT BE SAME
	CMP	E
	JRZ	QSAV2	;QUEUE IS FULL, SO ABORT
QSAV1:
	LHLD	QLST	;GET PTR TO LAST QUEUE ELEMENT
	XCHG		;... IN DE
	LXI	H,TBUFF	;COPY FROM TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	CALL	QWRAP	;CHECK FOR WRAP AROUND
	XCHG		;HL PTS TO NEW LAST QUEUE POSITION
	SHLD	QLST	;SAVE HL
	LHLD	QCNT	;INCREMENT SECTOR COUNT
	INX	H
	SHLD	QCNT
	CALL	PRQCNT	;PRINT QUEUE COUNT
	POP	H	;PT TO NEXT CHAR
	JMP	PROMPT
QSAV2:
	CALL	ILPRT
	DB	'Block Queue is Full -- Block Not Saved',CR,LF,0
	LHLD	QCNT	;GET COUNT
	CALL	PRQCNT	;PRINT COUNT
	POP	H	;PT TO NEXT CHAR
	JMP	PRMPTR
;
;  PRINT NUMBER OF ELEMENTS IN QUEUE
;
PRQCNT:
	CALL	DEC	;PRINT AS DECIMAL
	CALL	ILPRT
	DB	' Blocks in Queue',CR,LF,0
	RET
;
;  CHECK TO SEE IF QUEUE ELEMENT PTED TO BY DE SHOULD BE WRAPPED AROUND
;	ON EXIT, DE PTS TO QUEUE ELEMENT WITH WRAP AROUND
;
QWRAP:
	LHLD	BDOS+1	;CHECK FOR WRAP AROUND
	MOV	A,H
	SUI	10	;BELOW CCP
	CMP	D	;WRAP AROUND IF EQUAL
	RNZ
	LXI	D,DIRECT	;NEXT ELEMENT IS HERE
	RET
;
;This routine is common to Save Group (RG) and Write Group (WG); it is used
;  to extract the group number, check it, and position DU2 to it
;  On exit, GROUP = Group Number, GRPDIS = 0, and DU2 is positioned
;
COMG:
	INX	H	;PT TO CHAR AFTER 'G' OF '<G' COMMAND
	PUSH	H	;SAVE PTR TO NEXT CHAR
	MOV	A,M	;GET CHAR AFTER 'G'
	LHLD	GROUP	;GET CURRENT GROUP
	CALL	UPCASE	;CAPITALIZE
	CPI	EOLCH	;CURRENT IF LOGICAL EOL
	JRZ	COMG1
	CPI	CR	;CURRENT IF PHYSICAL EOL
	JRZ	COMG1
	CALL	HEXIN	;GET GROUP NUMBER IN HEX
	LHLD	DSM	;CHECK FOR BOUNDS ERROR
	CALL	SUBDE	;SUBTRACT GROUP NUMBER FROM DSM
	POP	H	;RESTORE PTR
	JC	OUTLIM	;LIMIT ERROR IF CARRY
	PUSH	H	;SAVE PTR AGAIN
	XCHG		;SAVE GROUP NUMBER
	SHLD	GROUP
COMG1:
	SHLD	TGRP	;TEMPORARY GROUP NUMBER
	XCHG		;GROUP NUMBER IN DE
	XRA	A	;A=0
	STA	GRPDIS	;SET GROUP DISPLACEMENT
	CALL	GTKSEC	;CONVERT GROUP NUMBER TO TRACK AND SECTOR
	CALL	SETTRK	;SET TRACK
	XCHG
	CALL	SETSEC	;SET SECTOR
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;This is the Save Group Routine; it copies the indicated group into the save
;  buffer.
;
SAVEG:
	CALL	COMG	;EXTRACT COMMON GROUP INFO -- GROUP NUMBER AND POS
	PUSH	H
	CALL	ILPRT
	DB	'Reading from Group ',0
	LHLD	GROUP	;GET CURRENT GROUP
	MOV	B,H	;VALUE IN BC
	MOV	C,L
	CALL	HEXB	;PRINT AS HEX
	CALL	ILPRT
	DB	CR,LF,0
	LHLD	QLST	;LAST PTR USED FOR READ
	SHLD	QPTR
	POP	H
	MVI	A,0	;SET COPY FUNCTION TO SAVE
	STA	CPYFCT	;0=READ, 0FFH=WRITE
;
;Group Copy Routine -- if CPYFCT = 0, Read Group; if CPYFCT = 0FFH, Write Group
;
COPYG:
	PUSH	H	;SAVE PTR TO NEXT CHAR IN COMMAND LINE
	CALL	NORITE	;POSITIONING LOST
	XCHG		;SAVE HL
	LHLD	QPTR	;PT TO NEXT QUEUE POSITION
	XCHG		;... IN DE
	LDA	BLM	;GET NUMBER OF BLOCKS/GROUP
	INR	A	; ADD 1 TO BLM FOR CORRECT COUNT
	MOV	B,A	;COUNT IN B
;
COPYGL:
	PUSH	B	;SAVE COUNT
	PUSH	D	;SAVE PTR TO NEXT BLOCK TO LOAD
	MOV	B,D	;SET BC=DE FOR SET DMA
	MOV	C,E
	CALL	SETDMA	;SET ADDRESS TO LOAD
	LDA	CPYFCT	;READ OR WRITE?
	ORA	A	;0=READ
	JRNZ	COPYGLW
	CALL	READ	;READ BLOCK
	LHLD	QCNT	;INCREMENT QUEUE ELEMENT COUNT
	INX	H
	SHLD	QCNT
	JR	COPYGL0
COPYGLW:
	LHLD	QCNT	;QUEUE EMPTY?
	MOV	A,H
	ORA	L
	JZ	QEMPTY
	CALL	PWRITE	;WRITE BLOCK (NO CHECK)
	LHLD	QCNT	;DECREMENT QUEUE ELEMENT COUNT
	DCX	H
	SHLD	QCNT
COPYGL0:
	CALL	NXTSEC	;COMPUTE NEXT SECTOR ADDRESS
	LHLD	CURTRK	;GET NEXT TRACK ADDRESS
	XCHG		;... IN DE
	CALL	SETTRK	;SET IT
	LHLD	CURSEC	;GET NEXT SECTOR ADDRESS
	XCHG		;... IN DE
	CALL	SETSEC	;SET IT
	POP	D	;GET PTR TO NEXT BLOCK
	POP	B	;GET COUNTER
	LXI	H,80H	;OFFSET TO NEXT BLOCK
	DAD	D
	SHLD	QPTR
	XCHG		;DE PTS TO NEXT BLOCK
	CALL	QWRAP	;ALLOW WRAP AROUND IN QUEUE
	LDA	CPYFCT	;0=READ
	ORA	A	;NO QUEUE OVERFLOW CHECK IF WRITE
	JRNZ	COPYGL1
	LHLD	QNXT	;CHECK FOR QUEUE OVERFLOW
	MOV	A,H	;MUST NOT BE EQUAL
	CMP	D
	JRNZ	COPYGL1
	MOV	A,L
	CMP	E
	JZ	QSAV2
COPYGL1:
	DJNZ	COPYGL	;LOOP UNTIL FINISHED
	LHLD	QCNT	;PRINT COUNT
	CALL	PRQCNT
	LHLD	QPTR	;GET QUEUE PTR
	LDA	CPYFCT	;RESET PROPER QUEUE PTR
	ORA	A	;0=READ
	JRZ	COPYGL2
	SHLD	QNXT	;NEXT PTR USED FOR WRITE
	JR	COPYGL3
COPYGL2:
	SHLD	QLST	;LAST PTR USED FOR READ
COPYGL3:
	LXI	B,TBUFF	;RESET DMA ADDRESS
	CALL	SETDMA
	XRA	A	;A=0
	STA	WRFLG	;SET NO READ DONE
	LHLD	TGRP	;GET GROUP NUMBER
	XCHG		;... IN DE
	POP	H	;GET PTR TO NEXT CHAR
	JMP	POSGRP	;POSITION TO GROUP IN DE AND CONTINUE PROCESSING
;
;COMMAND:  >
;Restore the current sector
;	Special Form >S gets next block from queue
;	Special Form >G gets next group from queue
;
RESTOR:
	MOV	A,M	;CHECK FOR SPECIAL FORM
	CALL	UPCASE	;CAPITALIZE
	CPI	'B'	;BLOCK SAVE?
	JRZ	QRESTOR
	CPI	'G'	;GROUP SAVE?
	JZ	RESTRG
	LDA	SAVEFL	;SAVE DONE PREVIOUSLY?
	ORA	A
	JZ	NOSAVE	;NONE TO SAVE
	PUSH	H
	LXI	H,SAVBUF	;COPY FROM SAVBUF
	LXI	D,TBUFF	;INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	POP	H	;GET PTR TO NEXT CHAR
	JMP	PROMPT
;
;  Restore Sector from Queue
;
QRESTOR:
	INX	H	;PT TO NEXT CHAR
	PUSH	H	;SAVE PTR ON STACK
	LHLD	QCNT	;GET ELEMENT COUNT
	MOV	A,H	;EMPTY?
	ORA	L
	JRZ	QEMPTY	;ABORT IF EMPTY
	DCX	H	;COUNT DOWN
	SHLD	QCNT
	CALL	PRQCNT	;PRINT COUNT
	LHLD	QNXT	;PT TO NEXT ELEMENT IN QUEUE
	LXI	D,TBUFF	;COPY INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	XCHG		;DE=PTR TO NEXT ELEMENT IN QUEUE
	CALL	QWRAP	;CHECK FOR WRAP AROUND
	XCHG		;HL PTS TO NEXT ELEMENT IN QUEUE
	SHLD	QNXT	;SAVE PTR
	POP	H	;RESTORE PTR
	JMP	PROMPT
QEMPTY:
	CALL	ILPRT
	DB	'Error -- Queue Empty',CR,LF,0
	POP	H	;RESTORE NEXT CHAR PTR
	JMP	PRMPTR
;
;Write Group Loaded in GBUFF to Disk
;
RESTRG:
	CALL	COMG	;GET GROUP NUMBER FROM COMMAND LINE AND POS
	PUSH	H
	CALL	ILPRT
	DB	'Writing to Group ',0
	LHLD	GROUP	;GET GROUP NUMBER
	MOV	B,H	;VALUE IN BC
	MOV	C,L
	CALL	HEXB	;PRINT IN HEX
	CALL	ILPRT
	DB	CR,LF,0
	LHLD	QNXT	;NEXT PTR USED FOR WRITE
	SHLD	QPTR
	POP	H
	MVI	A,0FFH	;WRITE FUNCTION
	STA	CPYFCT	;COPY FUNCTION FOR GROUP COPY ROUTINE
	JMP	COPYG	;GROUP COPY ROUTINE
;
NOSAVE:
	CALL	ILPRT
	DB	'++ No "<" Save Command Issued ++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;Move (HL) to (DE) length in B
;
MOVE:
	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DJNZ	MOVE
	RET
;
NORITE:
	XRA	A	;GET 0
	STA	WRFLG	;CAN'T WRITE NOW
	RET
;
;No match in search, try next char
;
SRNOMT:
	POP	H
	CALL	CTLCS	;ABORT?
	JRNZ	SEARCH	;..YES
	LXI	H,INBUF
	MVI	M,CR
	JMP	CLCGRP	;SHOW WHERE STOPPED
;
;COMMAND:  =
;Search for character string
;
SEARCH:
	PUSH	H	;SAVE STRING POINTER
;
SRCHL:
	CALL	RDBYTE	;GET A BYTE
	MOV	B,A	;SAVE IT
	MOV	A,M	;CHECK NEXT MATCH CHAR.
	CPI	'<'	;WILL IT BE HEX?
	MOV	A,B	;RESTORE DISK CHAR
	JRZ	SRCHL1
	ANI	7FH	;NEXT CHAR IS ASCII...STRIP BIT 7
;
SRCHL1:
	PUSH	PSW
	CALL	GETVAL	;GET SEARCH VALUE
	MOV	B,A
	POP	PSW
	CMP	B	;MATCH?
	JNZ	SRNOMT	;NO MATCH
	INX	H
	MOV	A,M	;DONE?
	CPI	CR	;END OF LINE?
	JRZ	SREQU
	CPI	EOLCH	;LOGICAL EOL?
	JRNZ	SRCHL
;
;Got match
;
SREQU:
	CALL	ILPRT
	DB	'= at ',0
	LDA	BUFAD
	ANI	7FH
	CALL	HEX
	CALL	CRLF
	JMP	CLCGRP
;
;Get value from input buffer
;
GETVAL:
	MOV	A,M	;GET NEXT CHAR
	CPI	'<'	;HEX ESCAPE?
	RNZ		;NO, RETURN
;"<<" means one "<"
	INX	H
	MOV	A,M
	CPI	'<'
	RZ
;Got hex
	PUSH	D
	CALL	HEXIN	;GET VALUE
	CPI	'>'	;PROPER DELIM?
	MOV	A,E	;GET VALUE
	POP	D
	JNZ	WHAT	;ERROR
	RET
;
;Read a byte at a time from disk
;
RDBYTE:
	PUSH	H
	LDA	FTSW	;FIRST READ?
	ORA	A
	JRNZ	READ1
	LHLD	BUFAD
	MOV	A,L
	ORA	A	;IN BUFFER?
	JM	NORD	;YES, SKIP READ
;
;Have to read
;
	CALL	NXTSEC	;ADVANCE TO NEXT BLOCK
;
READ1:
	XRA	A
	STA	FTSW	;NOT FIRST READ
	LHLD	CURSEC
	XCHG
	CALL	SETSEC
	LHLD	CURTRK
	XCHG
	CALL	SETTRK
	CALL	READ
	CALL	CLCSUB
	LXI	H,TBUFF
;
NORD:
	MOV	A,M
	INX	H
	SHLD	BUFAD
	POP	H
	RET
;
;COMMAND:  V
;View the file in ASCII starting at
;current sector, stepping thru the disk
;
VIEW:
	LDA	WRFLG
	ORA	A
	JZ	BADDMP
	CALL	DECIN	;GET DISPL IF ANY
	PUSH	H
	MOV	A,E
	ORA	A
	JRNZ	VIEWLP
	INR	E	;DFLT=1
;
VIEWLP:
	LXI	H,TBUFF	;TO DATA
;
VEWCHR:
	CALL	CTLCS	;ABORT?
	JRZ	VEWEND
	MOV	A,M	;GET NEXT CHAR
	CPI	1AH	;EOF?
	JRZ	VEWEOF
	ANI	7FH	;MASK
	CPI	7EH	;ESC CHAR FOR H1500
	JRNC	VIEWHX	;SHOW RUBOUT AND TILDE AS HEX
	CPI	' '
	JRNC	VIEWPR
	CPI	CR	;CR PASS
	JRZ	VIEWPR
	CPI	LF	;LF PASS
	JRZ	VIEWPR
	CPI	TAB	;TAB PASS
	JRZ	VIEWPR
;
VIEWHX:
	MOV	A,M	;NOT ASCII...PRINT AS <NN>
	CALL	BHEX
	JR	VIEWNP
;
VIEWPR:
	CALL	TYPE
;
VIEWNP:
	INR	L
	JRNZ	VEWCHR
	DCR	E
	JRZ	VEWEND
	PUSH	D	;SAVE COUNT
	CALL	NXTSEC
	LHLD	CURSEC
	XCHG
	CALL	SETSEC	
	LHLD	CURTRK
	XCHG
	CALL	SETTRK
	CALL	READ
	POP	D	;RESTORE COUNT
	JR	VIEWLP
;
VEWEOF:
	CALL	ILPRT
	DB	CR,LF,'	++ EOF ++',CR,LF,0
;
VEWEND:
	POP	H
	CALL	CRLF
	JMP	CLCGRP
;
;COMMAND:  A or D
;Dump in hex or ASCII
;
DUMP:
	LDA	WRFLG
	ORA	A
	JRNZ	DUMPOK
;
BADDMP:
	CALL	ILPRT
	DB	'++ Can''t dump, no sector read ++',CR,LF,0
;
EXPL:
	CALL	ILPRT
	DB	'Use G command following F,',CR,LF
	DB	'or R or S following T',CR,LF,0
	JMP	PRMPTR
;
DUMPOK:
	MOV	A,M	;GET NEXT CHAR
	CPI	EOLCH	;LOGICAL EOL?
	JRZ	DUMPDF	;DFLT
	CPI	CR	;PHYSICAL EOL?
	JRNZ	DMPNDF
;
;Use default
;
DUMPDF:
	LXI	B,TBUFF
	LXI	D,0FFH
	JR	DUMP1
;
DMPNDF:
	CALL	DISP
	MOV	B,D
	MOV	C,E
	CPI	CR
	JRZ	DUMP1
	CPI	EOLCH
	JRZ	DUMP1
	INX	H	;SKIP SEPCH
	CALL	DISP
;
;BC = start, DE = end
;
DUMP1:
	PUSH	H	;SAVE COMMAND POINTER
	MOV	H,B
	MOV	L,C
;
DUMPLP:
	MOV	A,L
	ANI	7FH
	CALL	HEX	;PRINT HEX VALUE
	CALL	SPACE
	CALL	SPACE
	LDA	DUMTYP
	CPI	'A'
	JRZ	DUMPAS
	PUSH	H	;SAVE START
;
DHEX:
	MOV	A,M
	CALL	HEX	;PRINT HEX VALUE PTED TO BY HL
	MOV	A,L
	ANI	3
	CPI	3	;EXTRA SPACE EVERY 4
	CZ	SPACE
	MOV	A,L
	ANI	7
	CPI	7	;TWO EXTRA SPACES EVERY 8
	CZ	SPACE
	MOV	A,E
	CMP	L
	JRZ	DPOP
	INX	H
	MOV	A,L
	ANI	0FH
	JRNZ	DHEX
;
DPOP:
	CALL	CTLCS	;ABORT?
	JZ	PRMPTR
	LDA	DUMTYP
	CPI	'H'
	JRZ	DNOAS	;HEX ONLY
	POP	H	;GET START ADDR
;
DUMPAS:
	CALL	ASTER	;PRINT FIRST ASTERISK TO SEPARATE TEXT
;
DCHR:
	MOV	A,M	;GET CHAR
	ANI	7FH
	CPI	' '
	JRC	DPER
	CPI	7EH	;TRAP ESC FOR H1500
	JRC	DOK
;
DPER:
	MVI	A,'.'	;PRINT PRINTING CHAR
;
DOK:
	CALL	TYPE	;PRINT CHAR
	MOV	A,E
	CMP	L
	JRZ	DEND
	INX	H
	MOV	A,L
	ANI	0FH
	JRNZ	DCHR
;
DEND:
	CALL	ASTER	;PRINT ENDING ASTERISK
	CALL	CRLF	;NEW LINE
	PUSH	D
	CALL	CTLCS	;ABORT?
	POP	D
	JZ	PRMPTR
	MOV	A,E
	CMP	L
	JRNZ	DUMPLP
	POP	H
	JMP	PROMPT
;
DNOAS:
	POP	B
	CALL	CRLF
	MOV	A,E
	CMP	L
	JRNZ	DUMPLP
	POP	H
	JMP	PROMPT
;
;COMMAND:  G
;Position
;
POS:
	PUSH	PSW
	MOV	A,M
	CPI	EOLCH	;LOGICAL EOL?
	JRZ	POSINQ
	CPI	CR	;PHYSICAL EOL?
	JRNZ	POSOK
;
POSINQ:
	POP	PSW
	JMP	INQ
;
POSOK:
	POP	PSW
	CPI	'T'	;TRACK?
	JRZ	POSTKD
	CPI	'S'	;SECTOR?
	JRZ	POSSCD
	CPI	'G'	;GROUP?
	JZ	POSGPH
	JMP	WHAT	;ERROR OTHERWISE
;
;Position to Track
;
POSTKD:
	CALL	DECIN	;GET NUMBER IN DECIMAL
;
POSTRK:
	PUSH	H
	LHLD	MAXTRK	;CHECK FOR BEYOND END OF DISK
	CALL	SUBDE
	POP	H
	JC	OUTLIM
	CALL	SETTRK	;SET TRACK
	CALL	NORITE	;TRACK DOESN'T READ
	MVI	A,1
	STA	NOTPOS	;SHOW NOT POSITIONED
	JR	CLCGRP
;
;Position to Sector
;
POSSCD:
	CALL	DECIN	;GET NUMBER IN DECIMAL
	MOV	A,D
	ORA	E
	JZ	WHAT	;DON'T ALLOW SECTOR 0
;
POSSEC:
	PUSH	H
	LHLD	SPT	;CHECK FOR WITHIN RANGE
	CALL	SUBDE
	POP	H
	JC	WHAT
	CALL	SETSEC	;SET SECTOR
	CALL	READ	;READ
	XRA	A
	STA	NOTPOS	;POSITIONED OK
;
;Calculate Group Number/Group Displacement and Print
;
CLCGRP:
	CALL	CLCSUB
	JMP	INQ
;
;Calculate group from track and sector
;  On exit, GROUP = Group Number and GRPDIS = Displacement within Group
;
CLCSUB:
	PUSH	H
	LHLD	SYSTRK
	XCHG
	LHLD	CURTRK
	CALL	SUBDE	;COMPUTE RELATIVE TRACK NUMBER (SKIP SYSTEM TRACKS)
	XCHG
	LHLD	SPT	;MULTIPLY BY NUMBER OF SECTORS/TRACK
	CALL	MULT
	XCHG		;DE=TOTAL NUMBER OF SECTORS IN TRACKS
	LHLD	CURSEC	;GET SECTOR OFFSET FROM BEGINNING OF TRACK
	DCX	H
	DAD	D	;HL=TOTAL NUMBER OF SECTORS WITH OFFSET
	LDA	BLM
	MOV	B,A
	MOV	A,L
	ANA	B
	STA	GRPDIS	;DISPLACEMENT WITHIN GROUP
	LDA	BSH
	MOV	B,A
;
CLCLOP:
	CALL	ROTRHL
	DJNZ	CLCLOP
	SHLD	GROUP	;GROUP NUMBER
	POP	H
	RET
;
;Position in the directory after a find
;(Does not work in CP/M-2.x)
;
POSDIR:
	PUSH	H	;SAVE INBUF
	LHLD	BSH
	XRA	A
	STA	FINDFL	;CANCEL POS REQ
	LDA	DIRPOS	;GET POSITION
	RAR
	RAR
	PUSH	PSW
	ANA	H
	STA	GRPDIS
	POP	PSW
;
POSDLP:
	RAR
	DCR	L
	JRNZ	POSDLP
	ANI	1	;GET GROUP
	MOV	L,A	;SETUP FOR POSGP2
	MVI	H,0
	SHLD	GROUP
	XCHG
	JR	POSGP2	;POSITION TO IT
;
;Position to Group
;
POSGPH:
	CALL	HEXIN	;GET PARAMETER
;
;Position to Group Numbered in DE and Print Position
;
POSGRP:
	PUSH	H
	LHLD	DSM	;CHECK FOR WITHIN BOUNDS
	CALL	SUBDE
	POP	H
	JC	OUTLIM
	XCHG
	SHLD	GROUP	;SET GROUP NUMBER
	XCHG
	XRA	A
	STA	GRPDIS	;SET ZERO DISPLACEMENT
	PUSH	H
;
POSGP2:
	CALL	GTKSEC	;CONVERT GROUP TO SECTOR/TRACK
	CALL	SETTRK	;SET TRACK
	XCHG
	CALL	SETSEC	;SET SECTOR
	CALL	READ	;READ BLOCK
	XRA	A
	STA	NOTPOS	;NOW POSITIONED
	POP	H
	JMP	INQ
;
;Convert Group Number in DE to Sector and Track; also, GRPDIS = Offset in Grp
;  On exit, DE = Track Number, HL = Sector Number
;
GTKSEC:
	MOV	H,D	;HL=GROUP NUMBER
	MOV	L,E
	LDA	BSH	;GET NUMBER OF SECTORS IN GROUP
;
GLOOP:
	DAD	H
	DCR	A
	JRNZ	GLOOP
	LDA	GRPDIS	;ADD IN DISPLACEMENT WITHIN GROUP
	ADD	L	;CAN'T CARRY
	MOV	L,A
;
;Divide by number of sectors, quotient=track, remainder=sector
;
	XCHG		;DE=TOTAL NUMBER OF SECTORS
	LHLD	SPT	;GET NUMBER OF SECTORS/TRACK
	CALL	NEG	;HL = -SECTORS/TRACK
	XCHG
	LXI	B,0	;SET TRACK COUNTER TO ZERO
;
DIVLP:
	INX	B	;INCREMENT TRACK COUNT
	DAD	D	;SUBTRACT SECTORS/TRACK FROM SECTORS TOTAL
	JRC	DIVLP
	DCX	B	;ADJUST TRACK COUNT
	XCHG
	LHLD	SPT	;ADD SECTORS/TRACK BACK IN TO ADJUST
	DAD	D	;HL=NUMBER OF SECTORS ON LAST TRACK OF GROUP
	PUSH	H
	LHLD	SYSTRK	;ADD IN NUMBER OF SYSTEM TRACKS
	DAD	B
	XCHG		;DE=TRACK NUMBER
	POP	H
	INX	H	;HL=SECTOR NUMBER
	RET
;
;COMMAND:  F
;Find Directory Entry for specified file
;
POSFIL:
	CALL	NORITE
	MVI	A,1
	STA	FINDFL	;SO WE POSITION LATER
	LXI	D,FCB
	XRA	A	;LOGGED IN DISK
	STAX	D
	INX	D
	MVI	B,8
	CALL	MVNAME
	MVI	B,3
	CALL	MVNAME
	LXI	D,FCB
	MVI	C,SRCHF
	PUSH	H
	CALL	BDOS
	INR	A
	JRNZ	FLOK
	STA	DIRPOS	;GRP 0 IF NOT FOUND
	CALL	ILPRT
	DB	'++ File Not Found ++',CR,LF,0
	POP	H
	JMP	PROMPT
;
FLOK:
	DCR	A
	STA	DIRPOS	;SAVE POS. IN DIR
	ANI	3
	MOV	L,A
	MVI	H,0
	DAD	H	;X32 BYTES/ENTRY
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	LXI	D,TBUFF
	DAD	D	;HL POINTS TO ENTRY
	LXI	D,32
	XCHG
	DAD	D
	XCHG
	MVI	A,'D'
	STA	DUMTYP
	JMP	DUMPLP	;WHICH POPS H
;
MVNAME:
	MOV	A,M	;GET NEXT CHAR OF FILE NAME/TYPE
	CPI	'.'	;END OF FILE NAME?
	JRZ	MVIPAD	;PAD OUT IF SO
	CPI	CR	;END OF ENTRY?
	JRZ	PAD	;PAD OUT IF SO
	CPI	EOLCH	;END OF ENTRY?
	JRZ	PAD	;PAD OUT IF SO
	CALL	UPCASE	;CAPITALIZE
	STAX	D	;STORE
	INX	H	;PT TO NEXT
	INX	D
	DJNZ	MVNAME
	MOV	A,M	;CHECK FOR ERROR
	CPI	CR	;OK IF EOL
	RZ
	CPI	EOLCH	;OK IF LOGICAL EOL
	RZ
	INX	H
	CPI	'.'	;OK IF DECIMAL
	RZ
	JMP	WHAT
;
MVIPAD:
	INX	H
;
PAD:
	MVI	A,' '	;PRINT PADDING SPACES
	STAX	D
	INX	D
	DJNZ	PAD
	RET
;
;COMMAND:  +
;Advance to Next Logical Sector
;
PLUS:
	LXI	D,1	;DFLT TO 1 SECT
	MOV	A,M	;GET NEXT CHAR
	CPI	CR	;CR?
	JRZ	PLUSGO	;..YES, DFLT TO 1
	CPI	EOLCH
	JRZ	PLUSGO
	CALL	DECIN	;GET #
	MOV	A,D
	ORA	E
	JRNZ	PLUSGO
	LXI	D,1	;SET 1 IF VALUE OF ZERO
;
PLUSGO:
	CALL	NXTSEC	;ADVANCE TO NEXT LOGICAL SECTOR
	DCX	D	;MORE TO GO?
	MOV	A,D
	ORA	E
	JRNZ	PLUSGO	;..YES
;
;Ok, incremented to sector.  Setup and read
;
PLUSMI:
	PUSH	H
	LHLD	CURSEC
	XCHG
	CALL	SETSEC	;SET SECTOR
	LHLD	CURTRK
	XCHG
	CALL	SETTRK	;SET TRACK
	POP	H
	CALL	READ	;READ IT
	JMP	CLCGRP	;CALCULATE GROUP AND DISPLAY
;
;COMMAND:  -
;Back up to previous sector
;
MINUS:
	LXI	D,1	;SET DFLT
	MOV	A,M	;GET CHAR
	CPI	CR	;CR?
	JRZ	MINGO	;..YES, DFLT=1
	CPI	EOLCH
	JRZ	MINGO
	CALL	DECIN	;..NO, GET ##
	MOV	A,D
	ORA	E
	JRNZ	MINGO
	LXI	D,1	;ASSUME 1
;
MINGO:
	PUSH	H
	LHLD	CURSEC	;BACK UP SECTOR
	DCX	H
	MOV	A,H
	ORA	L
	JRNZ	MINOK
	LHLD	CURTRK	;BEYOND SECTOR ZERO, SO BACK UP TRACK
	MOV	A,H
	ORA	L
	JRNZ	SEASH
	LHLD	MAXTRK	;WRAP TO END OF DISK
	SHLD	CURTRK
	LHLD	MAXSEC
	JR	MINOK
;
SEASH:
	DCX	H
	SHLD	CURTRK
	LHLD	SPT	;GET NUMBER OF SECTORS/TRACK
;
MINOK:
	SHLD	CURSEC	;SET NEW CURRENT SECTOR
	POP	H
	DCX	D	;COUNT DOWN ON NUMBER OF TIMES TO BACKUP
	MOV	A,D
	ORA	E
	JRNZ	MINGO
	JR	PLUSMI	;READ BLOCK
;
;Go to next sector
;  On exit, CURSEC = Current Sector and CURTRK = Current Track
;
NXTSEC:
	PUSH	H
	PUSH	D
	LHLD	CURSEC	;INCREMENT CURRENT SECTOR
	INX	H
	XCHG
	LHLD	SPT	;CHECK TO SEE IF BEYOND END OF TRACK
	CALL	SUBDE
	XCHG
	JRNC	NEXTOK
	LHLD	CURTRK	;BEYOND END OF TRACK, SO INCR CURRENT TRACK
	INX	H
	XCHG
	LHLD	MAXTRK	;SEE IF BEYOND END OF DISK
	CALL	SUBDE
	JRNC	TRASK
	LXI	D,0	;WRAP TO START OF DISK
;
TRASK:
	XCHG
	SHLD	CURTRK	;SET NEW CURRENT TRACK
	LXI	H,1	;SET SECTOR 1
;
NEXTOK:
	SHLD	CURSEC	;SET NEW CURRENT SECTOR
	POP	D
	POP	H
	RET
;
;Tell what group, displacement, track, sector, physical sector
;
INQ:
	CALL	INQSUB
	JMP	PROMPT
;
;Position inquiry subroutine
;Executed via: G S or T (with no operands)
;
INQSUB:
	PUSH	H
	LHLD	SYSTRK	;CHECK IF IN SYSTEM TRACKS
	XCHG
	LHLD	CURTRK
	CALL	SUBDE
	JRC	NOGRP
	CALL	ILPRT	;PRINT GROUP NUMBER IF NOT IN SYSTEM TRACKS
	DB	'Group = ',0
	LHLD	GROUP
	MOV	B,H
	MOV	C,L
	CALL	HEXB	;PRINT GROUP NUMBER IN BC
	MVI	A,':'
	CALL	TYPE
	LDA	GRPDIS
	CALL	HEX	;PRINT GROUP DISPLACEMENT IN A
	MVI	A,','
	CALL	TYPE
;
NOGRP:
	CALL	ILPRT	;PRINT TRACK NUMBER
	DB	' Track = ',0
	LHLD	CURTRK
	CALL	DEC	;TRACK NUMBER IN DECIMAL
	CALL	ILPRT	;PRINT SECTOR NUMBER
	DB	', Sector = ',0
	LHLD	CURSEC
	CALL	DEC	;SECTOR NUMBER IN DECIMAL
	CALL	ILPRT	;PRINT PHYSCIAL SECTOR NUMBER
	DB	', Physical Sector = ',0
	LHLD	PHYSEC
	CALL	DEC	;PHYSICAL SECTOR NUMBER IN DECIMAL
	CALL	CRLF
	POP	H
	RET
;
;COMMAND:  C
;Change Contents of Current Block
;
CHG:
	MOV	A,M	;GET TYPE (HEX, ASCII)
	CALL	UPCASE
	PUSH	PSW	;SAVE "H" OR "A"
	INX	H
	CALL	HEXIN	;GET DISP IN HEX
	CALL	DISP1	;VALIDATE DISP TO DE
	INX	H
	LXI	B,0	;SHOW NO 'THRU' ADDR
	CPI	'-'	;TEST DELIM FR. DISP
	JRNZ	CHGNTH	;NO THRU
	PUSH	D	;SAVE FROM
	CALL	HEXIN
	CALL	DISP1	;GET THRU
	INX	H	;SKIP END DELIM
	MOV	B,D
	MOV	C,E	;BC = THRU
	POP	D	;GET FROM
	JR	CHGAH
;
CHGNTH:
	CPI	SEPCH
	JNZ	WHAT
;
CHGAH:
	POP	PSW
	CPI	'H'	;HEX?
	JRZ	CHGHEX
	CPI	'A'	;ASCII?
	JNZ	WHAT
;
;Change ASCII
;
CHGALP:
	MOV	A,M	;GET CHAR
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
;
;The following print of the deleted byte is commented out; if leading
;  semicolons are removed, deleted bytes will be printed
;
;	LDAX	D	;GET BYTE THAT IS REPLACED
;	CPI	' '
;	JC	CHGAHX
;	CPI	7EH	;DON'T PRINT ESC CHAR FOR H1500
;	JNC	CHGAHX
;	JMP	CHGA2
;
;CHGAHX:
;	CALL	BHEX
;	JMP	CHGA3
;
;CHGA2:
;	CALL	TYPE
;
;End of print of delete bytes
;
CHGA3:
	SHLD	BACK	;IN CASE "THRU"
	CALL	GETVAL	;GET ASCII OR <HEX> VALUE
	STAX	D	;UPDATE BYTE
	INX	H	;PT TO NEXT INPUT CHAR
;
;See if 'THRU' requested
;
	MOV	A,C
	ORA	A
	JRZ	CHANTH
	CMP	E	;DONE?..
	JZ	PROMPT	;..YES
	LHLD	BACK
;
CHANTH:
	INR	E
	JNZ	CHGALP
	MOV	A,M
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	JMP	WHAT
;
;Change hex
;
CHGHCM:
	INX	H
;
CHGHEX:
	MOV	A,M	;GET HEX DIGIT
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	CPI	SEPCH	;DELIM?
	JRZ	CHGHCM
	PUSH	D
	SHLD	HEXAD	;IN CASE 'THRU'
	CALL	HEXIN	;POSITIONS TO DELIM
	MOV	A,E	;GET VALUE
	POP	D	;..ADDR
;
;The following comments out the echo of the deleted byte; removing the
;  leading semicolons restores the echo
;
;	PUSH	PSW	;SAVE VALUE
;	LDAX	D	;GET OLD
;	CALL	HEX	;ECHO IN HEX
;	POP	PSW	;GET NEW
;
;End of echo of bytes
;
	STAX	D	;SAVE NEW BYTE
	MOV	A,C	;SEE IF 'THRU'
	ORA	A
	JRZ	CHHNTH	;..NO.
	CMP	E	;..YES, DONE?
	JZ	PROMPT
	LHLD	HEXAD	;..NO: MORE
;
CHHNTH:
	INR	E
	JRNZ	CHGHEX
	MOV	A,M
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	JMP	WHAT
;
;COMMAND:  R
;Read Current Block into TBUFF
;COMMAND:  RG
;Read Specified Group into GBUFF
;
DOREAD:
	LDA	NOTPOS	;POSITIONED?
	ORA	A
	JRNZ	CANTRD
	CALL	READ	;READ BLOCK
	JMP	PROMPT
;
CANTRD:
	CALL	ILPRT
	DB	'++ Can''t read - not positioned ++',CR,LF
	DB	'Position by:',CR,LF
	DB	'	Track then Sector, or',CR,LF
	DB	'	Group',CR,LF,0
	JMP	PROMPT
;
;COMMAND:  W
;Write Current Block to Disk
;COMMAND:  WG
;Write Specified Group from GBUFF
;
DORITE:
	CALL	WRITE	;DO WRITE
	JMP	PROMPT
;
;Print Byte in A as Hex Digits
;
BHEX:
	PUSH	PSW
	MVI	A,'<'
	CALL	TYPE
	POP	PSW
	CALL	HEX
	MVI	A,'>'
	CALL	TYPE
	RET
;
;Print Number in BC as Hex Digits
;  HEXB does not print MS Byte if DSM shows small disk size
;  HEXB1 prints BC regardless
;
HEXB:
	LDA	DSM+1
	ORA	A
	JZ	HEXX
HEXB1:
	MOV	A,B
	CALL	HEX
;
HEXX:
	MOV	A,C
;
;Print Byte in A as 2 Hex Digits
;
HEX:
	PUSH	PSW
	RAR		;GET HIGH NYBBLE
	RAR
	RAR
	RAR
	CALL	NIBBL	;PRINT IT
	POP	PSW	;GET LOW NYBBLE
;
NIBBL:
	ANI	0FH	;MASK LOW NYBBLE
	CPI	10	;0-9?
	JRC	HEXNU
	ADI	7	;CONVERT TO A-F
;
HEXNU:
	ADI	'0'	;CONVERT TO ASCII
	JMP	TYPE	;PRINT IT
;
;Decimal output routine
;  Print Number in HL as decimal digits
;
DEC:
	PUSH	B
	PUSH	D
	PUSH	H
	XRA	A	;SET NO LEADING DIGIT
	STA	DDIG
	LXI	B,10000
	CALL	DPRT
	DAD	B
	LXI	B,1000
	CALL	DPRT
	DAD	B
	LXI	B,100
	CALL	DPRT
	DAD	B
	LXI	B,10
	CALL	DPRT
	DAD	B
	MOV	A,L	;ALWAYS PRINT LSD
	ADI	'0'	;ASCII
	CALL	TYPE
	POP	H
	POP	D
	POP	B
	RET
DPRT:
	PUSH	B	;SAVE BC
	MVI	D,0FFH	;SET -1
DPRTL:
	INR	D	;ADD 1 TO OUTPUT DIGIT
	MOV	A,L	;L-C
	SUB	C
	MOV	L,A
	MOV	A,H	;H-B
	SBB	B
	MOV	H,A
	JRNC	DPRTL
	POP	B	;RESTORE BC
	LDA	DDIG	;GET LEADING DIGIT FLAG
	ORA	D	;CHECK FOR ZERO STILL
	STA	DDIG	;SET FLAG
	MOV	A,D	;GET DIGIT TO PRINT
	RZ		;ABORT IF BOTH ZERO
	ADI	'0'	;ASCII
	JMP	TYPE
DDIG:	DS	1	;TEMP FOR DEC USE ONLY
;
;Print <SP>
;
SPACE:	MVI	A,' '
	JMP	TYPE
;
;Print '*'
;
ASTER:	MVI	A,'*'
	JMP	TYPE
;
;Inline print routine
;  Print Chars ending in 0 pted to by Return Address; return to byte after
;
ILPRT:
	XTHL		;GET PTR AND SAVE HL
;
ILPLP:
	CALL	CTLCS	;ABORT?
	JZ	PRMPTR
	MOV	A,M	;GET CHAR
	CPI	1	;PAUSE? -- ^A
	JRNZ	ILPOK
	CALL	CONIN	;WAIT FOR ANY CHAR
	CPI	3	;ABORT?
	JZ	PRMPTR
	JR	ILPNX
;
ILPOK:
	CALL	TYPE	;PRINT CHAR
;
ILPNX:
	INX	H	;PT TO NEXT
	MOV	A,M	;GET IT
	ORA	A	;DONE?
	JRNZ	ILPLP
	INX	H	;PT TO BYTE AFTER ENDING 0
	XTHL		;RESTORE HL AND RET ADR
	RET
;
;DISP calls DECIN, and validates a sector
;displacement, then converts it to an address
;
DISP:
	CALL	DECIN
DISP1:
	PUSH	PSW	;SAVE DELIMITER
	MOV	A,D
	ORA	A
	JRNZ	BADISP
	MOV	A,E
	ORA	A
	JM	BADISP
	ADI	80H	;TO POINT TO BUFFER AT BASE+80H
	MOV	E,A
	MVI	D,BASE/256
	POP	PSW	;GET DELIM
	RET
;
BADISP:
	CALL	ILPRT
	DB	'++ Bad Displacement (Not 0-7FH) ++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;Input Number from Command Line -- Assume it to be Hex
;  Number returned in DE
;
HEXIN:
	LXI	D,0
	MOV	A,M
	CPI	'#'	;DECIMAL?
	JRZ	HDIN	;MAKE DECIMAL
;
HINLP:
	MOV	A,M	;GET CHAR
	CALL	UPCASE	;CAPITALIZE
	CPI	CR	;EOL?
	RZ
	CPI	EOLCH	;EOL?
	RZ
	CPI	SEPCH
	RZ
	CPI	'-'	;'THRU'?
	RZ
	CPI	'>'
	RZ
	INX	H	;PT TO NEXT CHAR
	CPI	'0'	;RANGE?
	JC	WHAT
	CPI	'9'+1	;RANGE?
	JRC	HINNUM
	CPI	'A'	;RANGE?
	JC	WHAT
	CPI	'F'+1	;RANGE?
	JNC	WHAT
	SUI	7	;ADJUST FROM A-F TO 10-15
;
HINNUM:
	SUI	'0'	;CONVERT FROM ASCII TO BINARY
	XCHG
	DAD	H	;MULT PREVIOUS VALUE BY 16
	DAD	H
	DAD	H
	DAD	H
	ADD	L	;ADD IN NEW DIGIT
	MOV	L,A
	XCHG
	JR	HINLP
;
HDIN:
	INX	H	;SKIP '.'
;
;Input Number in Command Line as Decimal
;  Number is returned in DE
;
DECIN:
	LXI	D,0
	MOV	A,M	; GET 1ST CHAR
	CPI	'#'	; HEX?
	JRNZ	DINLP
	INX	H	; PT TO DIGIT
	JR	HINLP	; DO HEX PROCESSING
;
DINLP:
	MOV	A,M	;GET DIGIT
	CALL	UPCASE	;CAPITALIZE
	CPI	'0'	;RANGE?
	RC
	CPI	'9'+1	;RANGE?
	RNC
	SUI	'0'	;CONVERT TO BINARY
	INX	H	;PT TO NEXT
	PUSH	H
	MOV	H,D
	MOV	L,E
	DAD	H	;X2
	DAD	H	;X4
	DAD	D	;X5
	DAD	H	;X10
	ADD	L	;ADD IN DIGIT
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	XCHG		;RESULT IN DE
	POP	H
	JR	DINLP
;
;Read in a console buffer
;
RDBUF:
	CALL	ILPRT	;PRINT PROMPT
	DB	CR,LF,'DU2  ',0
	LDA	DRIVE	;GET DRIVE NUMBER
	ADI	'A'	;CONVERT TO ASCII
	CALL	TYPE
	LDA	UNUM	;DISPLAY USER NUMBER
	MOV	L,A	;VALUE IN HL
	MVI	H,0
	CALL	DEC	;PRINT IN DECIMAL
	CALL	ILPRT	;PRINT PROMPT
	DB	'? ',0
	LXI	D,INBUF-2	;USE CP/M READLN
	MVI	C,10
	CALL	BDOS
	LDA	INBUF-1	;GET CHAR COUNT
	MOV	B,A	;CHAR COUNT IN B
	LXI	H,INBUF	;STORE ENDING <CR>
	ADD	L	;ADD CHAR COUNT TO HL
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	MVI	A,CR	;STORE ENDING <CR>
	MOV	M,A	;SAVE IT
	CALL	TYPE	;ECHO IT
	MVI	A,LF	;ECHO..
	CALL	TYPE	;..LF
	LXI	H,INBUF	;SET PTR TO FIRST CHAR IN LINE
	RET
;
;Set paging flag for page routine
;
PAGSET:
	LDA	PAGSIZ	;GET SIZE OF PAGE
	STA	PAGFLG	;SET FLAG
	RET
;
;Page output
;
PAGER:
	LDA	PAGFLG	;GET FLAG
	CPI	2	;2 LINES LEFT?
	JRZ	WAIT	;SAME AS USER DELAY
	DCR	A	;COUNT DOWN
	STA	PAGFLG
	JMP	CRLF
;
;Delay Routine
;
WAIT:
	PUSH	H
	CALL	ILPRT
	DB	CR,LF,'Type Any Character to Continue or ^C to Abort - ',0
	POP	H
	CALL	CONIN	;GET RESPONSE
	CPI	'C'-40H	;^C?
	JRZ	WAIT1
	CALL	CRLF	;NEW LINE
	CALL	PAGSET	;RESET PAGE COUNT
	RET
WAIT1:
	LDA	IHFLG	;INITIAL HELP?
	ORA	A	;0=NO
	JZ	PRMPTR	;ABORT TO COMMAND PROMPT
	JMP	EXIT1	;ABORT TO CP/M
;
;CRLF Routine
;
CRLF:
	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
	JMP	TYPE
;
;Convert to Upper Case
;
UPCASE:
	ANI	7FH	;MASK OUT MSB
	CPI	60H	;LESS THAN SMALL A?
	RC		;RETURN IF SO
	ANI	5FH	;MAKE UPPER CASE
	RET
;
;CON: Status Routine
;
CONST:
	PUSH	B
	PUSH	D
	PUSH	H
VCONST:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;CON: Input Routine
;
CONIN:
	PUSH	B
	PUSH	D
	PUSH	H
VCONIN:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;Console out with TAB expansion
;  Char in A
;
TYPE:
	PUSH	B	;SAVE REGS
	PUSH	D
	PUSH	H
	MOV	C,A	;FOR OUTPUT ROUTINE
	CPI	TAB
	JRNZ	TYPE2
;Tabulate
TYPTAB:
	MVI	A,' '	;PRINT SPACE
	CALL	TYPE
	LDA	TABCOL	;GET COL COUNT
	ANI	7	;DONE?
	JRNZ	TYPTAB
	JR	TYPRET
;
;Filter out control characters to
;prevent garbage during view of file
;
TYPE2:
	CPI	' '
	JRNC	TYPEQ
	CPI	CR
	JRZ	TYPEQ
	CPI	LF
	JRNZ	TYPNCR
;
TYPEQ:
;
;CON: Output Routine
;
VCONOT:	CALL	$-$	;ADDR FILLED IN BY 'INIT'
;
;Update column used in tab expansion
;
	MOV	A,C	;GET CHAR
	CPI	CR
	JRNZ	TYPNCR
	MVI	A,0	;RESET TAB COLUMN IF <CR>
	STA	TABCOL
	JR	TYPLST
;
TYPNCR:
	CPI	' '	;CTL CHAR?
	JRC	TYPLST	;..NO CHANGE IN COL
	LDA	TABCOL	;INCR TAB COUNT
	INR	A
	STA	TABCOL
;
TYPLST:
	LDA	PFLAG	;CHECK FOR PRINTER OUTPUT
	ANI	1
	CNZ	LIST	;FROM C REG
;
TYPRET:
	POP	H	;RESTORE REGS
	POP	D
	POP	B
	RET
;
;LST: Output Routine
;  Char in C
;
LIST:
	PUSH	B	;SAVED REGS
	PUSH	D
	PUSH	H
VLIST:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;Home Disk Routine
;
HOME:
	PUSH	H
VHOME:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	RET
;
;Set track # in DE
;
SETTRK:
	PUSH	H
	LHLD	MAXTRK	;CHECK FOR WITHIN BOUNDS
	CALL	SUBDE	;IF TRACK # IN DE > MAX, THEN ERROR
	POP	H
	JC	OUTLIM
	XCHG		;RESET CURRENT TRACK
	SHLD	CURTRK
	XCHG
	MOV	B,D	;BC=TRACK NUMBER
	MOV	C,E
	PUSH	H
;
VSETRK:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	RET
;
;Set Sector Number in DE
;
SETSEC:
	PUSH	H
	PUSH	D
	LHLD	SYSTRK	;GET NUMBER OF SYSTEM TRACKS
	XCHG
	SHLD	CURSEC	;SET CURRENT SECTOR
	LHLD	CURTRK	;GET CURRENT TRACK
	CALL	SUBDE	;SEE IF WE ARE IN THE SYSTEM TRACKS
	POP	B	;BC=SECTOR NUMBER
	MOV	H,B	;HL=SECTOR NUMBER
	MOV	L,C
	JRNC	NOTSYS	;IF NO CARRY FOR SUBDE, WE ARE NOT IN SYSTEM TRACKS
	LDA	FIRST0	;SEE IF FIRST SEC 0
	ORA	A
	JRNZ	GSTSEC	;NO, JUMP AWAY
	DCX	H	;YES, SO DECREMENT
	JR	GSTSEC	;REQUESTED, THEN GO
;
;Not in System Tracks, so Skew Factor is effective
;
NOTSYS:
	LHLD	SECTBL	;GET PTR TO SECTOR TABLE
	XCHG		;... IN DE
	DCX	B	;DECREMENT SECTOR NUMBER BY 1
;
VSCTRN:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	LDA	SPT+1	;IF SPT<256 (HI-ORD = 0)
	ORA	A	; THEN FORCE 8-BIT TRANSLATION
	JRNZ	GSTSEC	; ELSE KEEP ALL 16 BITS
	MOV	H,A
GSTSEC:
	SHLD	PHYSEC
	MOV	B,H
	MOV	C,L
;
VSTSEC:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H	;RESTORE PTR TO NEXT CHAR
	RET
;
;Out of Disk Track Limit
;
OUTLIM:
	CALL	ILPRT
	DB	'++ Not Within Tracks 0-',0
	PUSH	H
	LHLD	MAXTRK	;PRINT MAX TRACK NUMBER
	CALL	DEC
	POP	H
	CALL	ILPRT
	DB	' ++',CR,LF,0
	CALL	NORITE	;NOT POSITIONED
	JMP	PRMPTR
;
;Set DMA Address
;
SETDMA:
	JMP	$-$	;ADDR FILLED IN BY 'INIT'
;
;Read Next Block into DMA Address
;
READ:
	MVI	A,1	;SET FLAG
	STA	WRFLG
	PUSH	H	;SAVE PTR TO NEXT CHAR
;
VREAD:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	ORA	A	;ERROR?
	JRZ	READOK
	CALL	ILPRT
	DB	'++ READ Failed, Sector may be Invalid ++'
	DB	CR,LF,0
;
READOK:
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;Write Block in DMA Address to Disk
;
WRITE:
	LDA	WRFLG	;READ ALREADY PERFORMED?
	ORA	A	;ERROR IF NOT
	JRNZ	PWRITE
;
BADW:
	CALL	ILPRT
	DB	'++ Cannot Write Unless Read Issued ++'
	DB	CR,LF,0
	JMP	EXPL
;
;Do Write
;
PWRITE:
	PUSH	H	;SAVE PTR TO NEXT CHAR
	MVI	C,1	;FORCE WRITE TYPE 1 IN CASE 2.x DEBLOCK USED
;
VWRITE:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	ORA	A	;ERROR?
	JRZ	WRITOK
	CALL	ILPRT
	DB	'++ WRITE Failed ++',CR,LF,0
;
WRITOK:
	POP	H
	RET
;
;Help; HELP is entry point for HELP (?) command, HELP1 is entry point for
; Initial Help Command, and IHELP is entry point for HELP (/) from command
; line
;
IHELP:
	CALL	ILPRT
	DB	'Introductory HELP on DU2 (Disk Utility)',CR,LF
	DB	'  The DU2 program is designed to provide the user with'
	DB	CR,LF
	DB	'the ability to manipulate information on the disk as easily'
	DB	CR,LF
	DB	'as the DDT and SID utilities allow the user to manipulate'
	DB	CR,LF
	DB	'information in memory.',CR,LF
	DB	'  The following is a summary of the commands available to'
	DB	CR,LF
	DB	'the DU2 user.  This same list is invoked internally by the'
	DB	CR,LF
	DB	'? Command of DU2.  For additional information on disk'
	DB	CR,LF
	DB	'structures and how to use DU2 in general, refer to the'
	DB	CR,LF
	DB	'files DU2.DOC and DU2.HLP.',CR,LF,0
	MVI	A,0FFH	;A=0FFH
	STA	IHFLG	;SET INITIAL HELP
	CALL	WAIT
	JR	HELP1	;PROCESS NORMALLY
HELP:
	XRA	A	;A=0
	STA	IHFLG	;SET NO INITIAL HELP
HELP1:
	CALL	ILPRT
	DB	'=======================================================',CR,LF
	DB	'		   -- Command Summary --		',CR,LF
	DB	'-------------------------------------------------------',CR,LF
	DB	CR,LF
	DB	'Operands in brackets [...] are optional'
	DB	CR,LF,CR,LF
	DB	'@	Repeat Previous Non-@ Command Line'
	DB	CR,LF
	DB	'+[nn]	Step In [nn (decimal)] Sectors;	-[nn]  Step Out '
	DB	'Sectors'
	DB	CR,LF
	DB	'	Note:  + or - need not be followed by a "," to '
	DB	'delimit commands.'
	DB	CR,LF
	DB	'#	Print Disk Parameters for Current Drive'
	DB	CR,LF
	DB	'=xxx	Search for ASCII xxx from Current Sector'
	DB	CR,LF
	DB	'	Note:  upper/lower case matters.  Use <xx> for hex:'
	DB	CR,LF
	DB	'	  To find "IN 0" use: =<db><0>     or'
	DB	CR,LF
	DB	'	  "(tab)H,0(CR)(LF)" use: =<9>H,0<D><A>'
	DB	CR,LF
	DB	'*[nn]	Repeat [nn (decimal) times];	!	Pause for User'
	DB	CR,LF
	DB	':ntext	Define ''text'' to be Macro n;	n	Perform Macro'
	DB	' n, 0<=n<=9'
	DB	CR,LF
	DB	':Pn	Print Macro n, 0<=n<=9'
	DB	CR,LF
	DB	':Px	Print All Macros if x=A or Print Prev Line if x=@'
	DB	CR,LF,CR,LF,0
	CALL	WAIT
	CALL	ILPRT
	DB	'-------------------------------------------------------',CR,LF
	DB	'A[ff,tt]	ASCII Dump'
	DB	CR,LF
	DB	'C	Change:'
	DB	CR,LF
	DB	'	 CHaddr byte byte... (hex)'
	DB	CR,LF
	DB	'	 or CAaddr data...  (Ascii)'
	DB	CR,LF
	DB	'	 	<xx> Allowed for imbedded hex.'
	DB	CR,LF
	DB	'	 or CHfrom-thru byte  e.g. ch0-7f e5'
	DB	CR,LF
	DB	'	 or CAfrom-thru byte'
	DB	CR,LF
	DB	'D[ff,tt]	Dump (Hex and ASCII)'
	DB	CR,LF
	DB	'Ffn.ft	Find File'
	DB	CR,LF
	DB	'Gnn	CP/M Allocation Group nn (hex)'
	DB	CR,LF
	DB	'H[ff,tt]	Hex Dump'
	DB	CR,LF
	DB	'L[d]	Log in Current Drive or Drive d'
	DB	CR,LF
	DB	'M[nn]	Map [from group nn (hex)]'
	DB	CR,LF,CR,LF,0
	CALL	WAIT
	CALL	ILPRT
	DB	'-------------------------------------------------------',CR,LF
	DB	CR,LF
	DB	'N	Load New Disk;	P	Toggle Printer Switch'
	DB	CR,LF
	DB	'Q	Queue Status;	QZ	Zero (Empty) Queue'
	DB	CR,LF
	DB	'QSfn.ft Save Queue as a File on Disk'
	DB	CR,LF
	DB	'<	Save Current Block into Temp;	>	Restore Temp '
	DB	'Block'
	DB	CR,LF
	DB	'<B	Save Block into Queue;		>B	Restore Queue '
	DB	'Block'
	DB	CR,LF
	DB	'<G[n]	Save Group into Queue;		>G[n]	Restore Queue '
	DB	'Group'
	DB	CR,LF
	DB	'Snn	Sector nn (decimal)'
	DB	CR,LF
	DB	'Tnn	Track nn (decimal)'
	DB	CR,LF
	DB	'Unn	Set User nn (decimal) for Find command (CP/M-2 only)'
	DB	CR,LF
	DB	'V[nn]	View [nn (decimal)] ASCII Blocks'
	DB	CR,LF
	DB	'R	Read Current Block;	W	Write Current Block'
	DB	CR,LF
	DB	'X	Exit Program'
	DB	CR,LF
	DB	'Z[nn]	Sleep [nn (decimal) seconds]'
	DB	CR,LF,CR,LF,0
	CALL	WAIT
	CALL	ILPRT
	DB	'-------------------------------------------------------',CR,LF
	DB	CR,LF
	DB	'Command Line is of the form:  DU2 du?',CR,LF
	DB	'	"d" is Logged-In Disk, "u" is Current User',CR,LF
	DB	CR,LF
	DB	'Cancel a function with C or Ctrl-C.'
	DB	CR,LF
	DB	'Suspend output with S or Ctrl-S.'
	DB	CR,LF
	DB	'Separate commands with ",".'
	DB	CR,LF
	DB	'	Example: g0'
	DB	CR,LF
	DB	'	+,d,z2,*'
	DB	CR,LF
	DB	'	 would step in, dump, sleep 2 sec, '
	DB	CR,LF
	DB	'	 and repeat until control-c typed.'
	DB	CR,LF
	DB	'"nn" usage varies with command as follows:',CR,LF
	DB	'	+, -, *, T, S, U, V, Z	nn in Decimal',CR,LF
	DB	'				(use #nn for Hex)',CR,LF
	DB	'	G, M			nn in Hexadecimal',CR,LF
	DB	'				(use #nn for Decimal)'
	DB	CR,LF
	DB	'"ff" and "tt" are in Hexadecimal (use #ff or #tt for Decimal)'
	DB	CR,LF,CR,LF
	DB	'=======================================================',CR,LF
	DB	0
	CALL	WAIT
	CALL	ILPRT
	DB	'=======================================================',CR,LF
	DB	'DU2 Status Information',CR,LF
	DB	'-------------------------------------------------------',CR,LF
	DB	'Processor Speed:  ',0
	LDA	CLOCK	;GET CLOCK SPEED
	ADI	'0'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT
	CALL	ILPRT
	DB	' MHz',CR,LF
	DB	'Number of Lines on CON:  ',0
	LDA	PAGSIZ	;GET PAGE SIZE
	MOV	L,A	;NUMBER IN HL
	MVI	H,0
	CALL	DEC	;PRINT NUMBER IN DECIMAL
	CALL	ILPRT
	DB	CR,LF,'Group Save Buffer Address:  ',0
	LXI	B,GBUFF	;BC=ADDRESS
	CALL	HEXB1	;PRINT AS HEX
	CALL	ILPRT
	DB	' Hex',CR,LF
	DB	'=======================================================',CR,LF
	DB	0
	LDA	IHFLG	;INITIAL HELP?
	ORA	A	;0=NO
	JRNZ	EXIT1	;RETURN TO CP/M IF SO
	JMP	PRMPTR	;NEW LINE INPUT IF NOT
;
;COMMAND:  X
;Exit to CP/M
;
EXIT:
	CALL	ILPRT	;PRINT
	DB	CR,LF,'Exit to CP/M -- Do you wish to Warm Boot (Y/N/<CR>=N)?'
	DB	' ',0
	CALL	CONIN	;GET RESPONSE
	CALL	UPCASE	;CAPITALIZE
	CPI	'Y'	;YES?
	JZ	BASE	;WARM BOOT IF SO
;
;Quick Exit to CP/M
;
EXIT1:
	LHLD	DUTSTK	;GET CP/M STACK PTR
	SPHL		;SET SP
	RET

;
;********************************
;*				*
;*    Utility Subroutines	*
;*				*
;********************************
;
GRPCMP:
	MOV	A,C
	INR	D
	DCR	D
	JRZ	CMP8
	CMP	M
	INX	H
	RNZ
	MOV	A,B
;
CMP8:
	CMP	M
	RET
;
;2's complement HL ==> HL
;
NEG:
	MOV	A,L
	CMA
	MOV	L,A
	MOV	A,H
	CMA
	MOV	H,A
	INX	H
	RET
;
;HL/2 ==> HL
;
ROTRHL:
	ORA	A
	MOV	A,H
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	RET
;
;Collect the number of '1' bits
;in A as a count in C
;
COLECT:
	MVI	B,8	;NUMBER OF BITS
;
COLOP:
	RAL
	JRNC	COSKIP
	INR	C
;
COSKIP:
	DCR	B
	JRNZ	COLOP
	RET
;
;HL-DE ==> HL
;  Carry Flag is Significant
;
SUBDE:
	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	RET
;
;Quick Kludge multiply
;HL*DE ==> HL
;
MULT:
	PUSH	B
	PUSH	D
	XCHG
	MOV	B,D
	MOV	C,E
	MOV	A,B
	ORA	C
	JRNZ	MULCON
	LXI	H,0	;FILTER SPECIAL CASE
	JR	MLDONE	;  OF MULTIPLY BY 0
;
MULCON:
	DCX	B
	MOV	D,H
	MOV	E,L
;
MULTLP:
	MOV	A,B
	ORA	C
	JRZ	MLDONE
	DAD	D
	DCX	B
	JR	MULTLP
;
MLDONE:
	POP	D
	POP	B
	RET
;
;Routine to fill in disk params
;with every drive change
;
LOGIT:
	LXI	D,DPB	;   THEN MOVE TO LOCAL
	MVI	B,DPBLEN ;  WORKSPACE
	CALL	MOVE
	LXI	H,GRPDIS
	MOV	A,M
	PUSH	PSW
	LDA	BLM
	MOV	M,A
	PUSH	H
	LHLD	DSM
	XCHG
	CALL	GTKSEC
	SHLD	MAXSEC
	XCHG
	SHLD	MAXTRK
	POP	H
	POP	PSW
	MOV	M,A
	RET

;***********************************
;
;  DU2 Command Table
;
;***********************************
CMDTBL:
	DB	':'
	DW	MAC
;
	DB	'@'
	DW	PCMD
;
	DB	'+'	
	DW	 PLUS
;
	DB	'-'
	DW	MINUS
;
	DB	'='
	DW	SEARCH
;
	DB	'<'
	DW	SAVE
;
	DB	'>'
	DW	RESTOR
;
	DB	'#'
	DW	STATS
;
	DB	'?'
	DW	HELP
;
	DB	MULCH
	DW	REPEAT
;
	DB	'!'
	DW	UWAIT
;
	DB	'A'
	DW	DUMP
;
	DB	'C'
	DW	CHG
;
	DB	'D'
	DW	DUMP
;
	DB	'F'
	DW	POSFIL
;
	DB	'G'
	DW	POS
;
	DB	'H'
	DW	DUMP
;
	DB	'L'
	DW	LOGIN
;
	DB	'M'
	DW	MAP
;
	DB	'N'
	DW	NEWDSK
;
	DB	'P'
	DW	PRNTFF
;
	DB	'Q'
	DW	QUEUER
;
	DB	'R'
	DW	DOREAD
;
	DB	'S'
	DW	POS
;
	DB	'T'
	DW	POS
;
	DB	'U'	;******CP/M 2.x ONLY******
	DW	USER
;
	DB	'V'
	DW	VIEW
;
	DB	'W'
	DW	DORITE
;
	DB	'X'
	DW	EXIT
;
	DB	'Z'
	DW	SLEEP
;
	DB	0	; End of Table
;*************************************

;
;Temporary storage area
;
	DS	100	;50-ELT STACK
DUTSTK:
	DS	2	;OLD CP/M STACK POINTER; TOP OF DU2 STACK
BUFAD:
	DS	2	;FORCES INITIAL READ
QCNT:
	DS	2	;NUMBER OF SECTORS IN QUEUE
QNXT:
	DS	2	;PTR TO NEXT SECTOR IN QUEUE
QLST:
	DS	2	;PTR TO LAST SECTOR IN QUEUE
QPTR:
	DS	2	;G-P QUEUE PTR
HEXAD:
	DS	2	;TO RE-FETCH A VALUE
TOGO:
	DS	2	;REPEAT COUNT (FFFF=CONT)
TWOUP:
	DS	1
UNUM:
	DS	1	;NUMBER OF CURRENT USER
ONLY1:
	DS	1	;FLAG TO PRINT ONLY 1 MAP ENTRY (0=NO)
MFPTR:
	DS	2	;MULTI FILE PTR FOR GETGRP
PAGFLG:
	DS	1	;LINE COUNTER FOR PAGING
PFLAG:
	DS	1	;1=PRINT
GROUP:
	DS	2	;GROUP NUMBER
GRPDIS:
	DS	1	;DISPLACEMENT INTO GROUP
SAVEFL:
	DS	1	;SAVE FLAG
CURTRK:
	DS	2	;CURRENT TRACK NUMBER
CURSEC:
	DS	2	;CURRENT SECTOR NUMBER
PHYSEC:
	DS	2	;CURRENT PHYSICAL SECTOR NUMBER
TABCOL:
	DS	1	;TAB COLUMN
CPYFCT:
	DS	1	;GROUP COPY FUNCTION; 0=READ, 0FFH=WRITE
FILECT:
	DS	2	;FILE COUNT
DIRPOS:
	DS	1	;POSITION IN DIRECTORY
FINDFL:
	DS	1	;1=MUST POSITION AFTER FIND
FTSW:
	DS	1	;SEARCH W/O INCREMENT
NOTPOS:
	DS	1	;INITIALLY NOT POSITIONED
WRFLG:
	DS	1	;MAY NOT WRITE UNTIL '+', '-',
;			 OR 'G' COMMAND
TGRP:
	DS	2	;TEMPORARY GROUP FLAG
FIRST0:
	DS	1	;SETS TO 0 IF FIRST SEC # IS 0
DRIVE:
	DS	1	;DRIVE NUMBER
MAXTRK:
	DS	2	;MAX TRACK NUMBER
MAXSEC:
	DS	2	;MAX SECTOR NUMBER
SECTBL:
	DS	2	;POINTER TO SECTOR SKEW TABLE
;
IHFLG:
	DS	1	;0=NOT AT INITIAL HELP, 0FFH=AT INITIAL HELP
DUPFLG:
	DS	1	;SPACE OR STAR TO INDICATE MULTIPLE USERS
BACK:
	DS	2	;TO BACK UP IN "CA0-7F,X"
DUMTYP:
	DS	1
;
;The disk parameter block
;is moved here from CP/M
;
DPB	EQU	$	;DISK PARAMETER BLOCK (COPY)
SPT:
	DS	2
BSH:
	DS	1
BLM:
	DS	1
EXM:
	DS	1
DSM:
	DS	2
DRM:
	DS	2
AL0:
	DS	1
AL1:
	DS	1
CKS:
	DS	2
SYSTRK:
	DS	2
;
;End of disk parameter block
;
SAVBUF:
	DS	128+2+2
;
;Set INBUF to a Page Boundary
;
	ORG	$/100H*100H+100H-2
	DB	126	; SIZE OF BUFFER FOR CP/M
	DS	1
INBUF:
	DS	400H	;EXTRA SPACE FOR MACRO EXPANSION
PINBUF:
	DS	400H	;PREVIOUS CONTENTS OF INPUT BUFFER
CTEMP:
	DS	400H	;BUILD NEW COMMAND LINE BUFFER
CTEMPX	EQU	$	;END OF CTEMP
;
;Directory read in here; also loaded group area and Macros
;
MTABL:	DS	100H*10			;10 PAGES FOR 10 MACROS
GBUFF	EQU	$
DIRECT	EQU	$
;
	END
