;****************************************************************
;*								*
;*		    CP/M FILE DUMP UTILITY			*
;*		     DUAL DENSITY VERSION			*
;*								*
;****************************************************************
;
;	History :	Originally published through CPMUG as
;			version 1.2 by Sam SINGER
;			
;			Updated to version 1.3 through CPMUG
;			by Sam SINGER
;
;			Updated to version 1.4 by Dave Hatch
;			and published by GED
;
;	FEB 1,1980	Revised and extended to version 1.5
;			for double density with static density
;			allocation (two sector sizes) for CP/M
;			1.4 by Bill Bolton and published by 80AT
;
;	JULY 11,1980	Revised and extended to version 2.0
;			for double density with dynamic density
;			allocation (four sector sizes) for CP/M
;			2.2 by Bill Bolton and published by 80AT
;
;	JULY 23,1980	Minor bugs exterminated and version updated
;			to 2.1
;
;	AUGUST 26,1980	Sector translation for all disk operations
;			and reserved track offset in block dump
;			routine added, version updated to 2.2
; 
;	OCT 23,1980	Bugs in the DIR and GRP2 routines which
;			showed up when tried on an Morrow HDCA-3
;			were eliminated. Conditional assembly for
;			hard disk added. Flag for sector display
;			header and track 0 added.
;
;	FEB 10, 1981	Macro library changed to MACRO3 and DJORG
;			equate added for easier DJ2D address change,
;			version updated to 2.3A
;	
;	FEB 11, 1981	Two sided status check changed to direct
;			check on 1791 controller as Model DJ2Ds
;			do not return a side bit in the disk status
;			firmware call. Version updated to 2.4
;
;	NOV 25, 1981	Modified for use with Godbout Disk 1 and
;			Morrow HDCA-3 controller, all track number
;			references changed to 16 bit values. Version
;			updated to 2.5
;
;			--- DDH ---
;
	TITLE	'Disk 1 + HDCA-3 Disk Dump DDH Ver 2.5'
;
TRUE	EQU	0FFH
FALSE	EQU	0
;
HARD	EQU	TRUE		;TRUE FOR MORROW HARD DISK
FIRST	EQU	TRUE		;TRUE FOR MORROW HARD DISK A,B,C
				; & FLOPPY DISKS D,E,F,G
				; FALSE FOR FLOPPY DISKS A,B,C,D
				; & MORROW HARD DISK E,F,G
;
FCB	EQU	0005CH		;CP/M FILE CONTROL BLOCK LOC.
WBOOT	EQU	00000H		;CP/M WARM BOOT ENTRY LOC.
BDOS	EQU	00005H		;CP/M BDOS ENTRY LOC.
ST$OFFSET EQU	01EH		;Offset to set track routine in BIOS
;
	MACLIB	MACRO3		;INCLUDE MACRO LIBRARY
;
	ORG	100H		;SET PROG START
;
BEGIN:
	LXI	H,0
	DAD	SP		;GET STACK POINTER
	SHLD	OLDSTK
	LXI	SP,NEWSTK	;SET UP NEW STACK
	DISKIO	?DRIVE		;GET CURRENTLY LOGGED DRIVE NO
	STA	DRVNO		;SAVE FOR EXIT
	STA	NEWDRV		;ALSO SAVE IN NEW DRIVE NO
	MVI	C,12		;FIRST, CHECK VERSION NUMBER
	CALL	BDOS
	MOV	A,L		;A <--- VERSION NO.
	CPI	20H		;VERSION 2.0 OR LATER?
	JC	VERSION$ERROR	;NO, ERROR MESSAGE
	CALL	DISKDATA	;GET DISK PARAMETERS
	LDA	81H		;CONSOLE INPUT ALREADY HERE ?
	ORA	A
	JZ	SIGNON		;BUFFER EMPTY, INPUT FROM CONSOLE
	LDA	80H		;GET NO OF CHAR INPUT
	ORI	80H		;ADD 128
	MOV	L,A		;TO L
	XRA	A		;ZERO
	MOV	H,A		;HL CONTAINS ADDR OF END OF BUFFER
ZBFF:
	INR	L
	JZ	START		;REMAINDER OF BUFFER ZEROED
	MOV	M,A
	JMP	ZBFF		;LOOP
SIGNON:
	PRINT	<CR,LF,'CP/M Disk Dump Utility VERS 2.5',CR,LF,LF>
	PRINT	<'Copyright (C) 1978 By Sam   Singer',CR,LF>
	PRINT	<'Copyright (C) 1980 By Bill  Bolton',CR,LF,LF>
	PRINT	<'For Godbout Disk 1 Controller',CR,LF>

	IF	HARD
	PRINT	<'and Morrow HDCA-3 Hard Disk Controller',CR,LF>
	ENDIF

	IF	HARD AND FIRST
	PRINT	<'Hard Disk Logical Drives A,B,C',CR,LF>
	PRINT	<'Floppy Drives D,E,F,G',CR,LF>
	ELSE
	PRINT	<'Floppy Drives A,B,C,D,',CR,LF>
	ENDIF

	IF	HARD AND NOT FIRST
	PRINT	<'Hard Disk Logical Drives E,F,G',CR,LF>
	ENDIF

NEWIN:
	PRINT	<CR,LF,'*'>
	MVI	A,0FFH		;SET SWITCH TO RETURN HERE AGAIN
	STA	INFLAG
	LXI	SP,NEWSTK	;RESET STACK POINTER
	LXI	H,0
	SHLD	LINE		;SET LINE COUNT TO ZERO
	FILL	80H,0FFH	;ZERO INPUT BUFFER
	INPUT	80H		;READ FILE NAME
;
;  SELECT DISK DRIVE AND SET UP FILE CONTROL BLOCK
;
START:
	FILL	FCB,FCB+32	;ZERO FILE CONTROL BLOCK
	MATCH	82H,'A:'	;DRIVE A
	JZ	ADISK
	MATCH	82H,'B:'	;DRIVE B
	JZ	BDISK
	MATCH	82H,'C:'	;DRIVE C
	JZ	CDISK
	MATCH	82H,'D:'	;DRIVE D
	JZ	DDISK
	MATCH	82H,'E:'	;DRIVE E
	JZ	EDISK
	MATCH	82H,'F:'	;DRIVE F
	JZ	FDISK
	MATCH	82H,'G:'	;DRIVE G
	JZ	GDISK 
	JMP	GETNAM		;NO DRIVE SPECIFIED
;
ADISK:
	XRA	A		;SELECT DRIVE A
	JMP	SETDRV
;
BDISK:
	MVI	A,1		;SELECT DRIVE B
	JMP	SETDRV
;
CDISK:
	MVI	A,2		;SELECT DRIVE C
	JMP	SETDRV
;
DDISK:
	MVI	A,3		;SELECT DRIVE D
	JMP	SETDRV
;
EDISK:
	MVI	A,4		;SELECT DRIVE E
	JMP	SETDRV
;
FDISK:
	MVI	A,5		;SELECT DRIVE F
	JMP	SETDRV
;
GDISK:
	MVI	A,6		;SELECT DRIVE G
SETDRV:
	STA	NEWDRV		;STORE THE SELECTED DRIVE
	CALL	DISKDATA	;GET DISK PARAMETERS
	MOVE	82H,80H,40H	;SHIFT BUFFER DOWN TWO BYTES
;
;  SEARCH FOR DIRECT READ OF TRACK AND SECTOR OR VALIDATE
;
GETNAM:
	INSTR	82H,40H,'GROUP'
	JC	GROUP		;DISPLAY CPM ALLOCATION GROUP
	INSTR	82H,40H,'G '	;SEARCH FOR 'G'
	JC	GROUP		;DISPLAY GROUP
	INSTR	82H,40H,'MAP'	;ALLOCATION MAP
	JC	MAP		;DISPLAY GROUP ALLOCATION MAP
	INSTR	82H,40H,'DIR'	;DIRECTORY REQUEST
	JC	DIR		;DISPLAY DIRECTORY
	INSTR	82H,40H,'TRACK'	;SEARCH FOR TRACK
	JC	TRK1
	INSTR	82H,40H,'T '	;SEARCH FOR 'T'
	JNC	FILNAM		;NO TRACK GO TO READ FILE
TRK1:
	SCAN			;FIND AND CONVERT NUMBER
	DECIN
	JC	INERR		;INPUT ERROR ON CARRY
	LXI	D,0
	SHLD	TRACK		;SAVE TRACK NO.
	CPHL			;TRACK 0?
	JNZ	SECSCH		;NO, CONTINUE
	LDA	HARD$D		;ON HARD DISK?
	ORA	A
	JNZ	SECSCH		;YES, TRACK 0 SAME AS OTHERS
	MVI	A,TRUE		;NO, SET A FLAG
	STA	TK0FLAG		;  FOR TRACK 0
SECSCH:
	INSTR	82H,40H,'SECTOR';SEARCH FOR SECTOR
	JC	SEC1
	INSTR	82H,40H,'S '	;TRY 'S'
	JNC	WHLTRK		;DUMP ENTIRE TRACK
SEC1:
	SCAN	
	DECIN
	JC	INERR		;INPUT ERROR ON CARRY
	SHLD	BSEC		;BEGINNING SECTOR
	SHLD	ESEC		;SAVE IN END SECTOR ALSO
	XCHG			;SET BUFFER POINTER FOR SCAN
	SHLD	IPOINT		;SAVE BUFFER POINTER FOR EDIT
	INSTR	,40H,'-'	;SEARCH FOR '-'
	JNC	EDIT		;CHECK FOR EDIT OF SECTOR
	SCAN
	DECIN			;SCAN AND CONVERT ANOTHER NO
	JC	INERR		;ERROR IF CARRY SET
	SHLD	ESEC		;SAVE IN END SECTOR
	PUSH	H
	LHLD	BSEC
	XCHG			;DE <--- START SECTOR NO.
	POP	H		;HL <--- END SECTOR NO.
	CPHL			;COMPARE BEGIN AND END
	JP	DOREAD		;OK IF END>=BEGIN
	SHLD	BSEC		;OTHERWISE SWITCH THEM
	XCHG			;HL <--- NEW END SECTOR
	SHLD	ESEC
DOREAD:
	CALL	RDISK		;READ DIRECT
	JMP	ENDFIL		;BACK FOR MORE INPUT
;
EDIT:
	LHLD	IPOINT		;RESET BUFFER POINTER
	INSTR	,40H,'EDIT'	;CHECK EDIT FUNCTION
	JNC	DOREAD		;GO TO DISPLAY SECTOR
	CALL	RDISK		;DISPLAY SECTOR
EDIT1:
	PRINT	<CR,LF,'EDIT - '>
	FILL	INBUF,INBUF+9
	INPUT	INBUF,6		;INPUT MAXIMUM 6 CHAR
	INSTR	INBUF,8,'WRITE'	;WRITE EDITED SECTOR ON DISK?
	JC	WRTDSK		;WRITE BUFFER BACK ON DISK
	INSTR	INBUF,8,'STOP'	;STOP EDITING WITHOUT WRITING?
	JC	ENDFIL		;EXIT
	HEXIN	INBUF+2		;CONV ASCII TO HEX
	JNC	CKLIM		;IF NO ERROR, CHECK ADDR
	LDAX	D		;GET ASCII CHAR
	CPI	'.'		;CHECK FOR EXIT CHAR
	JZ	EDIT3		;BACK FOR MORE EDITING
	JMP	ADERR		;ADDRESS ERROR
CKLIM:
	LXI	D,0080H		;CHECK ADDR LIMIT
	CPHL
	JP	ADERR		;ADDRESS ERROR
	SHLD	IPOINT		;SAVE ADDRESS
	PRINT	CRLF,$
PTX:
	HEXOUT	IPOINT+1
	HEXOUT	IPOINT		;ECHO THE ADDRESS
	PRINT	SPACE,$
	LHLD	IPOINT		;ECHO PRESENT CONTENTS
	LXI	D,080H
	DAD	D		;COMPUTE MEMORY ADDR
	MOV	A,M		;GET BYTE FROM MEMORY
	HEXOUT
	PRINT	SPACE,$
	FILL	INBUF,INBUF+5	;ZERO INPUT BUFFER
	INPUT	INBUF,4		;INPUT 4 CHAR MAX
	HEXIN	INBUF+2		;CONVERT
	JNC	EDIT2		;HEX CHAR
	LDAX	D		;GET ASCII CHAR
	CPI	'.'		;PERIOD ENDS INPUT
	JZ	EDIT3		;BACK FOR MORE EDITING
	JMP	HEXERR		;ERROR NOT HEX CHAR
EDIT2:
	LDA	INBUF+1		;LOAD NO OF CHAR TYPED
	ORA	A
	JZ	EDITX		;NO REPLACEMENT IF JUST CR
	MOV	A,L		;CONVERTED CHAR BACK TO A
	LHLD	IPOINT		;LOAD MEMORY BUFFER POINTER
	LXI	D,080H		;OFFSET
	DAD	D		;CALC MEMORY ADDR
	MOV	M,A		;STORE NEW INPUT TO MEMORY
EDITX:
	PRINT	CRLF,$
	LDA	IPOINT		;LEAST SIGNIFICANT HALF OF ADDR
	INR	A		;INCR BY ONE
	ANI	7FH		;COUNT MOD 128
	STA	IPOINT
	JMP	PTX		;INPUT MORE DATA
EDIT3:
	LXI	H,0
	SHLD	LINE		;RESET LINE NO TO ZERO
	CALL	PRTSEC		;PRINT BUFFER WITH HEADING
	JMP	EDIT1		;BACK FOR ADDITIONAL EDITING
;
WRTDSK:
	LDA	TK0FLAG		;ON TRACK 0 ?
	ORA	A
;	JNZ	WRITE$0		;YES, NEEDS SPECIAL TREATMENT
	CALLBIOS DWRITE		;WRITE BUFFER BACK ON DISK
	JMP	ENDFIL		;EXIT
;
WRITE$0:
	JMP	ENDFIL
;
;  READ TRACK AND SECTOR DIRECT
;
RDISK:
	CALL	SETUP
RDISK1:
	LDED	BSEC		;GET NEXT SECTOR TO READ
	LDA	HARD$D		
	ORA	A		;HARD DISK?
	JNZ	RDISK2		;TRACK 0 SAME AS OTHERS
	LDA	TK0$FLAG
	ORA	A		;TRACK 0 NEEDS SPECIAL TREATMENT
	JNZ	READ$0
RDISK2:
	LHLD	SPT		;GET SECTORS/TRACK FROM DPB
	SHLD	SPT$ERR
	CPHL			;SECTOR NO. > SPT?
	JC	GO$ON		;YES, ERROR
	MOV	A,D
	ORA	E		;SECTOR 0?
	JZ	BADSEC		;YES, NOT ALLOWED
	LHLD	BSEC		;NO, GET SECTOR NO.
	DCR	L		;ADJUST FOR SECTRAN
	PUSH	H
	POP	B		;BC <--- SECTOR NUMBER
	LHLD	XLT		;GET SECTOR TABLE ADDRESS
RDISK3:
	XCHG			;PUT INTO DE
	CALLBIOS DSECTRAN	;GET THE PHYSICAL SECTOR
	ORA	A		;RESET CARRY
	PUSH	H
	POP	B		;BC <--- SECTOR TO READ
	CALLBIOS DSETSEC
GO$ON:
	JC	BADSEC		;WRONG SECTOR NO
TRK2:
	LHLD	TRACK		;GET TRACK NO.
	PUSH	H
	POP	B
	LXI	H,TRK$RET
	PUSH	H		;RETURN ADDRESS ONTO STACK
	LHLD	WBOOT+1
	MVI	L,ST$OFFSET
	PCHL			;WERE OFF TO SEE THE WIZARD
TRK$RET:
	JC	BADTRK		;WRONG TRACK NO
	CALLBIOS DREAD		;READ TRACK AND SECTOR
	JMP	PRTSEC
;
READ$0:
	LXI	H,26	;SINGLE DENSITY SECTORS PER TRACK
	SHLD	SPT$ERR
	CPHL		;SECTOR NO. TOO BIG?
	JC	GO$ON	;YES, ERROR
	MOV	A,D
	ORA	E	;SECTOR 0?
	JZ	BADSEC	;YES, NOT ALLOWED
	LHLD	BSEC	;NO, GET SECTOR NUMBER
	DCR	L
	PUSH	H
	POP	B	;BC <--- SECTOR NUMBER
	LXI	H,XLT$SD ;TRANSLATE TABLE
	JMP	RDISK3
;
;  PRINT DRIVE, TRACK AND SECTOR HEADING
;
PRTSEC:
	LDA	NEWDRV		;NEW DRIVE NO
	ADI	41H		;ADD ASCII OFFSET
	PUSH	PSW
	PRINT	<CR,LF,'      Drive '>
	POP	PSW
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	PRINT	HEADER,$
	PRINT	'- Track '
	LHLD	TRACK
	DECOUT
	PRINT	'  Logical Sector '
	LXI	H,0
	LHLD	BSEC
	DECOUT
	PRINT	'  Dump Count '
	LHLD	DCOUNT
	INX	H
	SHLD	DCOUNT
	DECOUT
	PRINT	CRLF,$
	PRINT	CRLF,$
	CALL	PRTBUF		;PRINT IT
	LDA	LINE+1		;GET HI BYTE OF HEX ADDRESS
	ORA	A		;IF NOT 0
	JNZ	SECOMP		;DONT ZERO LINE COUNT
	LXI	H,0
	SHLD	LINE		;RESET HEX ADDRESS COUNT
SECOMP:
	LHLD	ESEC		;END SECTOR NUMBER
	XCHG			;DE <--- END SECTOR
	LHLD	BSEC		;SECTOR JUST READ
	CPHL			;COMPARE THEM
	RZ			;EXIT IF THEY ARE EQUAL
	INX	H		;BUMP TO NEXT SECTOR
	SHLD	BSEC
	JMP	RDISK1
;
;  DUMP ENTIRE TRACK IF NO SECTOR INPUT
;
WHLTRK:
	LXI	H,1		;BEGIN SECTOR
	SHLD	BSEC		;SAVE IT FOR THIS DUMP
	LDA	TK0FLAG
	ORA	A		;ON FLOPPY TRACK 0?
	LXI	H,26		;FLOPPY TRACK 0 ALWAYS HAS 26 SECTORS
	JNZ	ENDSEC		;YES, SAVE IT IMMEDIATELY
	LHLD	SPT		;NO, HIGHEST SECTOR NO. FROM DPB
ENDSEC:
	SHLD	ESEC		;SAVE IT FOR THIS DUMP
	JMP	DOREAD		;GO READ IT
;
;  FILL IN FCB FOR NAMED FILE
;
FILNAM:
	MVI	A,TRUE
	STA	FDFLAG		;SET FILE DUMP FLAG
	FILFCB	FCB,82H		;FILL IN FCB NAME FROM INPUT BUFFER
	JC	NAMERR		;ERROR IN FILE NAME
	MATCH	FCB+9,'COM'	;TEST FOR COM FILE
	JNZ	SELDR
	LXI	H,100H	
	SHLD	LINE		;SET LINE NO. TO 100
SELDR:
	LDA	NEWDRV		;SELECT NEW DRIVE
	MOV	E,A
	DISKIO	LOGIN
FILSER:
	DISKIO	SEARCH,FCB	;LOOK FOR FILE
	ORA	A		;FOUND IT?
	PUSH	PSW		;SAVE BDOS RETURN INFO
	LDA	EXFLAG		;GET EXTENT COUNT
	ORA	A		;1ST EXTENT?
	JZ	ERR1		;YES, CHECK FOR FILE NOT FOUND
	POP	PSW		;RESTORE BDOS RETURN INFO
	JM	ENDFIL		;IF NOT FOUND, BACK FOR MORE INPUT
	JMP	RDFILE		;		
;
ERR1:
	POP	PSW		;RESTORE BDOS RETURN INFO
	JM	OPNERR		;IF NOT FOUND, TELL THE USER
RDFILE:
	RRC
	RRC
	RRC			;SAME AS 5 'ADD A' INSTRUCTIONS
	ANI	60H		;MASK THE BITS OF INTEREST
	ADI	80H		;ADD BASE ADDRESS OF BUFFER
	ADI	0CH		;ADD OFFSET TO EXTENT NO.
	PUSH	PSW
	FILL	DIRBUF,DIRBUF+21,00	;INITIALISE BUFFER
	POP	PSW
	MOV	L,A
	MVI	H,0		;SET UP SOURCE ADDRESS
	LXI	D,DIRBUF	;SET UP DESTINATION ADDRESS
	MVI	C,20		;SET UP COUNT
	MVI	B,0		;DITTO
	MOVE			;DO THE MOVE
	LXI	H,RCOUNT	;POINT TO RECORD COUNT
	MOV	A,M		;GET RECORD COUNT
	CPI	128		;POSSIBLY ANOTHER EXTENT?
	JNZ	NOEXT		;NO, DUMP THE GROUPS
	LDA	EXFLAG		;YES, GET EXTENT COUNT
	INR	A		;ADD 1 TO IT
	STA	EXFLAG		;SAVE IT
NOEXT:
	PUSH	H		;TEMP SAVE H
	LHLD	DSM		;GET DSM VALUE
	LXI	D,0FF00H	;SET NO. BYTES / GROUP
	DAD	D		;IF > 255 THEN TWO BYTES
	POP	H
	JC	TWO$BYTE
ONE$BYTE:
	INX	H		;BUMP TO NEXT GROUP NO.
	PUSH	H
	MOV	A,M		;GET THE NEXT GROUP NO.
	CPI	00		;DONE LAST GROUP?
	JZ	QUIT		;YES, CHECK FOR MORE EXTENTS
	MVI	H,0
	MOV	L,A		;GROUP NO. IN HL
	CALL	GROUPS		;DISPLAY THE SECTORS
	POP	H
	JMP	ONE$BYTE
;
TWO$BYTE:
	MVI	A,8		;GROUP POSITIONS/DIR ENTRY 
	STA	P$COUNT
LOOP2:
	INX	H		;POINT TO NEXT GROUP NO.
	MOV	A,M		;GET LOW BYTE
	INX	H
	PUSH	H		;SAVE POINTER
	MOV	H,M		;GET HI BYTE
	MOV	L,A		;FORM GROUP NUMBER
	DJZ	QUIT		;QUIT IF EMPTY GROUP POSITION
	CALL	GROUPS		;DISPLAY THE SECTORS
	LDA	P$COUNT		;CHECK IF LAST GROUP NO.
	DCR	A
	JZ	QUIT		;YES, CHECK FOR NEXT EXTENT
	STA	P$COUNT		;SAVE FOR NEXT LOOP CHECK
	POP	H
	JMP	LOOP2
;
QUIT:
	POP	H		;KEEP STACK CLEAN
	XRA	A
	STA	SCOUNT		;RESET SECTOR COUNT
	LXI	H,FCB+12	;POINT TO EXTENT NO. IN FCB
	LDA	EXFLAG		;GET NEXT EXTENT NO.
	CMP	M		;ARE THEY EQUAL
	JZ	ENDFIL		;YES, NO MORE EXTENTS SO EXIT
	MOV	M,A		;NO, STUFF NEXT EXTENT NUMBER
	JMP	FILSER		;LOOK FOR NEXT EXTENT
;	
ENDFIL:
	XRA	A
	STA	TK0FLAG		;RESET TRACK 0 FLAG
	STA	EXFLAG		;RESET EXTENT COUNT
	STA	FDFLAG		;RESET FILE DUMP FLAG
	STA	SCOUNT		;RESET SECTORS DONE COUNT
	STA	DCOUNT		;RESET DUMP COUNT LOW BYTE
	STA	DCOUNT+1	;  "    "     "   HI  BYTE
	LDA	INFLAG		;SEE WHERE TO GO
	ORA	A
	JZ	MONITOR
	JMP	NEWIN
;
;
;  PRTBUF - PRINT BUFFER IN HEX AND ASCII
;
PRTBUF:
	MVI	B,8		;8 LINES
	LXI	H,080H		;INITIAL BUFFER POINTER
	SHLD	IPOINT		;STORAGE FOR POINTER
BPRN:
	LHLD	IPOINT		;LOAD POINTER
	MVI	C,16		;CHAR PER LINE
	SAVE	B,H		;PROTECT B,H DURING PRINT CALLS
	LDA	LINE+1		;GET HIGH BYTE OF HEX ADDR
	ORA	A		;IS IT 00?
	JZ	SIMPLE		;YES, PAD 1ST TWO POSITIONS
	HEXOUT	LINE+1		;PRINT HIGH BYTE OF HEX ADDR
	JMP	LOWBYT
SIMPLE:
	PRINT	'  '		;PRINT SPACES INSTEAD OF,
LOWBYT:
	HEXOUT	LINE		;PRINT LOW BYTE OF ADDRESS
	PRINT	' : '
	RESTORE	H,B
PLOOP:
	MOV	A,M		;GET A BYTE
	SAVE	B,H
	HEXOUT
	PRINT	SPACE,$
	RESTORE	H,B
	INX	H		;INCR MEMORY POINTER
	DCR	C		;DECR CHARACTER COUNT
	JZ	SKIP		;DONE 16 YET?
	MOV	A,C		;NO, SO CONTINUE
	ANI	3		;TIME FOR SPACE MARKER?
	JNZ	PLOOP		;NO, PRINT SOME MORE
	SAVE	B,H
	PRINT	SPACE,$		;YES, PRINT SPACE MARKER
	RESTORE	H,B
	JMP	PLOOP		;PRINT SOME MORE
SKIP:
	SAVE	B
	PRINT	SPACE,$
	RESTORE	B
	LHLD	IPOINT		;RESET POINTER FOR ASCII
	MVI	C,10H		;RESET CHAR COUNT
PLOOP1:
	MOV	A,M		;GET A BYTE
	ANI	7FH		;MASK OFF HIGH BIT
	CPI	7FH		;DELETE CODE
	JZ	PERIOD		;PRINT PERIOD FOR DELETE
	CPI	20H		;TEST FOR CONTROL CHAR
	JP	SKIPX		;SKIP SUBSTITUTION
PERIOD:
	MVI	A,2EH		;ASCII PERIOD
SKIPX:
	SAVE	B,H
	CHAROUT			;PRINT IT SAVE REGS
	RESTORE	H,B
	INX	H		;INCR MEMORY POINTER
	MOV	A,C
	CPI	9		;CHECK 8 CHAR
	JNZ	DECC2
	SAVE	B,H
	PRINT	SPACE,$
	RESTORE	H,B
DECC2:
	DCR	C		;DECR CHAR COUNT
	JNZ	PLOOP1		;PRINT SOME MORE
	SAVE	B
	PRINT	CRLF,$		;CARRIAGE RETURN
	CALL	PRNCON		;PRINT CONTROL?
	POP	B
	INDEX	LINE,16		;INCR LINE NO BY 16
	DCR	B		;DECR	LINE COUNT
	RZ			;RETURN IF LINE COUNT ZERO
	INDEX	IPOINT,16	;INCR POINTER BY 16
	JMP	BPRN		;LOOP BACK
;
;  PRINT CONTROL AND ESCAPE
;
PRNCON:
	MVI	C,11
	CALL	5
	ANI	1
	RZ			;RETURN
	CHARIN			;READ CONSOLE
	CPI	3		;TEST FOR CONTROL C
	JZ	ENDFIL		;EXIT IF CONTROL C
	CPI	' '		;TEST FOR SPACE
	JZ	ENDFIL		;ABORT FUNCTION
	RET
;
;    THIS SECTION DISPLAYS A CPM ALLOCATION GROUP
;
GROUP:
	SCAN			;GET THE GROUP NO
	DECIN			;CONVERT TO BINARY
	JC	INERR		;INPUT ERROR IF CARRY SET
	CALL	GROUPS		;DO THIS GROUP
	JMP	ENDFIL		;EXIT
;
GROUPS:
	SHLD	G		;SAVE GROUP NO
	XCHG			;DE <--- GROUP NO.
	LHLD	DSM		;GET MAX GROUP NO.
	CPHL			;GROUP NO. TOO BIG?
	JC	BADGRP		;YES, ERROR MESSAGE
	LXI	H,0		;NO
	SHLD	S		;SET SECTOR COUNT TO 0
GRP1:
	CALL	GRPTS		;CONVERT TO TRACK AND SECTOR
	LHLD	TRACK		;GET LAST TRACK ACCESSED
	LDED	NEW$TRACK	;GET NEXT TRACK TO ACCESS
	CPHL			;SAME TRACK ?
	XCHG
	SHLD	TRACK		;STORE TRACK NO.
	CNZ	SETUP		;NO, SET UP THE DRIVE
	CALL	RDISK1		;PRINT THE SECTOR
	LDA	FDFLAG		;FILE DUMP IN PROGRESS?
	ORA	A
	JZ	GRP2		;NO, CONTINUE
	LDA	SCOUNT		;GET SECTORS DONE COUNT
	INR	A
	STA	SCOUNT		;SAVE IT AGAIN
	LXI	H,RCOUNT
	CMP	M		;HAVE WE DONE ALL THE SECTORS
	RZ			;YES, RETURN
GRP2:
	LXI	H,BLM		;POINT TO (SECTORS/BLOCK)-1
	MOV	L,M		;GET (SECTORS/BLOCK)-1
	MVI	H,0
	XCHG			;DE <---      "      "
	LHLD	S		;GET SECTOR COUNT
	CPHL			;DONE LAST SECTOR IN BLOCK?
	RZ			;YES
	INX	H		;NO, BUMP SECTOR COUNT
	SHLD	S		;SAVE IT
	JMP	GRP1		;PRINT ANOTHER SECTOR
;
;   GRPTS  CONVERT CPM GROUP AND SECTOR NUMBER TO TRK AND SEC
;
GRPTS:
	LHLD	SPT
	XCHG			;DE <--- SECTORS/TRACK
	LXI	H,0
	DSUB			;FORM DIVISOR
	SHLD	DIVISOR		;SAVE IT
	LHLD	G		;GET GROUP NO.
	LDA	BSH		;GET BLOCK SHIFT FACTOR
SHIFT$LOOP:
	DAD	H		;SHIFT LEFT ONE
	DCR	A		;ENOUGH SHIFTS ?
	JNZ	SHIFT$LOOP	;NO DO IT AGAIN
	PUSH	H		;TEMP SAVE HL
	LHLD	S		;GET SECTOR NO.
	XCHG			;DE <--- "   "
	POP	H
	DAD	D		;HL NOW HAS G*(BLOCK SIZE)+S
	PUSH	H		;TEMP SAVE HL
	LHLD	DIVISOR
	XCHG			;DE <--- DIVISOR
	LHLD	OFF		;GET NO. OF RESERVED TRACKS
	PUSH	H
	POP	B		;BC <--- NO. OF RESERVED TRACKS
	POP	H		;HL <--- DIVIDEND
	DCX	B		;ADJUST FOR LOOP ENTRY
DIVIDE:
	DAD	D		;SUBTRACT DIVISOR
	INX	B		;ADJUST TRACK NO.
	JC	DIVIDE		;LOOP TILL MINUS
	PUSH	H
	PUSH	B
	POP	H
	SHLD	NEW$TRACK	;STORE TRACK NO.
	LHLD	SPT		;INDEX INTO TABLE
	XCHG			;DE <--- INDEX
	POP	H
STORE:
	DAD	D		;FORM LOGICAL SECTOR NUMBER
	INR	L		;ADJUST FOR SECTRAN LATER
	SHLD	BSEC		;SAVE IN BEGINNING SECTOR
	SHLD	ESEC		;SAVE IN END SECTOR TOO
	RET
;
;    THIS ROUTINE DISPLAYS THE DISK SECTOR ALLOCATION MAP
;
MAP:
	LDA	NEWDRV
	MOV	E,A
	DISKIO	LOGIN		;LOG IN SELECTED DRIVE
	DISKIO	?ALLOC		;GET POINTER TO ALLOCATION MAP
	MOV	H,B
	MOV	L,A		;HL <--- POINTER TO MAP
	SHLD	IPOINT		;SAVE IT
	LXI	H,0
	SHLD	G		;ZERO COUNT OF UNUSED GROUPS
	PRINT	<CR,LF,LF,'                          '>
	PRINT	<'GROUP ALLOCATION MAP - DRIVE '>
	LDA	NEWDRV		;NEW DRIVE NO
	ADI	41H		;ADD ASCII OFFSET
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	PRINT	<CR,LF,LF>
	XRA	A
	LHLD	DSM		;GET NO. BLOCKS (GROUPS)
	INX	H
	SHLD	MAP$COUNT	;SAVE FOR BLOCKS+1
	LXI	D,-48		;48 BITS MAPPED/LINE
MAP1:
	DAD	D		;SUBTRACT A LINE
	INR	A		;BUMP LINE COUNT
	JC	MAP1
	MOV	D,A		;D <--- NO. LINE TO DISPLAY
	LHLD	IPOINT		;POINTER TO DISK ALLOCATION MAP
MAP2:
	MVI	C,6		;WORDS PER LINE
MAPX:
	SAVE
	PRINT	'                '
	RESTORE
MAP3:
	MVI	B,8		;BITS PER WORD
	MOV	A,M		;GET A BYTE FROM ALLOC MAP
MAP4:
	RAL			;SHIFT LEFT THRU CARRY
	SAVE	B,D,H,PSW
	JC	MAP5		;PRINT A ONE
	PRINT	'0'		;PRINT A ZERO
	LHLD	G		;UNUSED GROUPS
	INX	H		;ADD 1
	SHLD	G		;STORE IT BACK
	JMP	MAP6
MAP5:
	PRINT	'1'		;PRINT A ONE
MAP6:
	RESTORE	PSW,H,D,B
	SAVE	PSW,H		;SAVE BIT MAP BYTE
	LHLD	MAP$COUNT	;GET BYTES REMAINING
	DCX	H		
	SHLD	MAP$COUNT	;SAVE NEW BYTES LEFT
	DJZ	FINISHED	;QUIT IF DONE
	MOV	A,B		;BIT COUNT
	CPI	1		;LAST BIT OF BYTE?
	JZ	BITS		;YES
	DCR	B
	RESTORE H,PSW
	JMP	MAP4
;
BITS:
	RESTORE H
	MOV	A,C		;GET WORD COUNT
	CPI	1		;LAST WORD OF LINE?
	JZ	NEW$LINE	;YES
	DCR	C		;ADJUST WORD COUNT
	INX	H		;BUMP POINTER TO NEXT BYTE		
	RESTORE PSW
	JMP	MAP3
;
NEW$LINE:
	INX	H		;BUMP BYTE POINTER
	SAVE	
	PRINT	CRLF,$		;NEXT LINE ON SCREEN
	RESTORE 
	RESTORE PSW
	JMP	MAP2
;
FINISHED:
	RESTORE PSW
	PRINT	<CR,LF,LF,'                      '>
	DECOUT	G		;PRINT NO OF UNUSED BLOCKS/GROUPS
	PRINT	<' GROUPS REMAINING ON DISK OUT OF '>
	DECOUT	DSM		;PRINT MAX NUMBER OF BLOCKS/GROUPS
	PRINT	<CR,LF>
	JMP	ENDFIL		;EXIT
;
;    THIS ROUTINE DUMPS THE DIRECTORY GROUPS
;
DIR:
	LXI	D,0FFFFH	;SET UP DE
	LDA	AL0		;GET FIRST DIR ALLOC MAP
DIR$TEST:
	RAL			;IS BIT SET?
	JNC	DIR$DUMP2	;NO, DUMP THE DIRECTORY
	INX	D		;YES, BUMP BLOCK COUNT
	PUSH	PSW
	MVI	A,8		;NO. OF BITS IN A BYTE
	CMP	E		;DONE 8 BITS?
	JZ	NEXT$BYTE	;YES, TTEST AL1
	MVI	A,16		;MAX NO. OF BITS
	CMP	E		;DONE 16 BITS?
	JZ	DIR$DUMP1	;YES, DUMP DIRECTORY	
	POP	PSW 
	JMP	DIR$TEST	;TEST NEXT BIT
;
NEXT$BYTE:
	POP	PSW		;KEEP STACK STRAIGHT
	LDA	AL1		;GET AL1
	JMP	DIR$TEST	;GO TEST IT
;
DIR$DUMP1:
	POP	PSW		;KEEP STACK STRAIGHT
DIR$DUMP2:
	LXI	H,0		;START WITH GROUP 0
DIR$LOOP:
	SAVE	H,D
	CALL	GROUPS		;DO THE DUMP
	RESTORE D,H
	CPHL			;DONE ALL THE DIR GROUPS?
	INX	H		;BUMP GROUP NO.
	JNZ	DIR$LOOP	;YES, NO DO IT AGAIN
	JMP	ENDFIL		;EXIT
;
SETUP:
	LDA	NEWDRV
	MOV	E,A
	DISKIO	LOGIN		;SELECT NEW DRIVE (IF REQUESTED)
	CALLBIOS DHOME		;HOME SELECTED DRIVE
	RET
;
;	FIND THE SECTOR SIZE AND NUMBER OF SIDES OF THE MEDIA
;
DISKDATA:
	CALL	SETUP		;LOG DRIVE INTO BDOS
	LDA	NEWDRV		;GET DRIVE NO.

	IF	HARD AND FIRST
	CPI	3		;HARD DISK?
	JNC	FLOPPY		;NO
	MVI	A,TRUE		;SET A FLAG
	STA	HARD$D
	JMP	NEXT
	ENDIF

	IF	HARD AND NOT FIRST
	CPI	2		;HARD DISK?
	JC	FLOPPY		;NO
	MVI	A,TRUE		;SET A FLAG
	STA	HARD$D
	JMP	NEXT
	ENDIF
	
FLOPPY:
	XRA	A
	STA	HARD$D	
NEXT:
	LXI	B,NEWSTK	;END OF THIS PROGRAM
	CALLBIOS DSETDMA
	LDA	NEWDRV		;NOW GET THE
	MOV	C,A		;  XLT LOCATION
	CALLBIOS DSELDSK	;SELECT THE DRIVE
	MOV	A,M		;GET XLT ADDR LO BYTE
	INX	H		;POINT TO XLT HI BYTE
	MOV	H,M		;GET XLT ADDR HI BYTE
	MOV	L,A		;FORM ADDRESS
	SHLD	XLT		;SAVE XLT ADDRESS
	MVI	C,1		;SET TO TRACK OTHER THAN 0
	CALLBIOS DSETTRK	;DO IT
	MVI	C,1
	CALLBIOS DSETSEC	;SET THE SECTOR
	CALLBIOS DREAD		;READ THE DISK
	MVI	C,31		;GET DISK PARAMETER BLOCK
	CALL	BDOS		;HL POINTS TO BIOS DPB ON RETURN
	SHLD	DPB$ADDR	;SAVE DPB ADDRESS
	LXI	D,DPB		;POINT TO PROGRAM DPB STORAGE AREA
	LXI	B,15		;LENGTH TO MOVE
	MOVE			;COPY DPB FROM BIOS
	LDA	HARD$D
	ORA	A		;ON HARD DISK?
	CALL	HARDWARE	;NO, HARDWARE SPECIFIC ROUTINE
	LXI	B,0080H		;RESET DMA ADRESS
	CALLBIOS DSETDMA
	RET
;
;
;****************************************************************
;*								*
;*	HARDWARE						*
;*								*
;*	This is routine has to be provided by the user		*
;*	to suit their disk controller hardware. It must		*
;*	set up the display header to suit the types		*
;*	of drive you are using. This is up to you		*
;*	but I would suggest something informative		*
;*	about the disk format, i.e. single/double		*
;*	sided, single/double density, sector size		*
;*	etc. as in the DJ2D example below.			*
;*								*
;****************************************************************
;
HARDWARE:
	LXI	H,HEAD$HARD	;POINTER TO HEADER FOR HARD DISK
	JNZ	MOVER		;DISPLAY ONLY IF HARD FLAG SET
	LHLD	DPB$ADDR	;GET DPB START
	DCX	H		;GODBOUT HAS DISK TYPE BEFORE XLT
	MOV	A,M		;A <---- DISK TYPE	
	LXI	H,HEAD$TABLE
	RAL			;MULTIPLY DISK TYPE BY 2
	MVI	D,0
	MOV	E,A		;DE <---- OFFSET INTO TABLE
	DAD	D		;HL <---- POINTS TO HEADER ADDRESS
	CONTENTS		;HL <---- POINTS TO HEADER
MOVER:
	LXI	D,HEADER	;BUFFER FOR DISPLAY HEADER
	LXI	B,14		;LENGTH OF MESSAGE
	MOVE			;MOVE IT
	RET
;
HEAD$TABLE:
	DW	HEAD$128S
	DW	HEAD$128D
	DW	HEAD$256S
	DW	HEAD$256D
	DW	HEAD$512S
	DW	HEAD$512D
	DW	HEAD$1024S
	DW	HEAD$1024D
;
HEAD$128S:
	DB	' : 1S/1D/128  '
;
HEAD$256S:
	DB	' : 1S/2D/256  '
;
HEAD$512S:
	DB	' : 1S/2D/512  '
;
HEAD$1024S:
	DB	' : 1S/2D/1024 '
;
HEAD$128D:
	DB	' : 2S/1D/128  '
;
HEAD$256D:
	DB	' : 2S/2D/256  '
;
HEAD$512D
	DB	' : 2S/2D/512  '
;
HEAD$1024D:
	DB	' : 2S/2D/1024 '
;
HEAD$HARD:
	DB	' : Hard Disk  '
;
;  ERROR AND EXIT ROUTINES
;
;
INERR:
	PRINT	<CR,LF,'INPUT ERROR'>
	JMP	ENDFIL
;
BADSEC:
	PRINT	<CR,LF,'INCORRECT SECTOR NUMBER, 0 OR GREATER THAN '>
	DECOUT	SPT$ERR
	JMP	ENDFIL
;
BADTRK:
	PRINT	<CR,LF,'INCORRECT TRACK NUMBER'>
	JMP	ENDFIL
;
BADGRP:
	PRINT	<CR,LF,'INCORRECT GROUP NUMBER, GREATER THAN '>
	DECOUT	DSM
	RET
;
OPNERR:
	PRINT	<CR,LF,'NO FILE BY THAT NAME ON DRIVE '>
	LDA	NEWDRV		;NEW DRIVE NO
	ADI	41H		;ADD ASCII OFFSET
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	JMP	ENDFIL
;
RDERR:
	PRINT	<CR,LF,'DISK READ ERROR'>
	JMP	MONITOR
;
NAMERR:
	PRINT	<CR,LF,'ERROR IN FILE NAME'>
	JMP	ENDFIL
;
ADERR:
	PRINT	<CR,LF,LF,'ADDRESS ERROR'>
	JMP	EDIT1		;ADDRESS ERROR ON EDIT
;
HEXERR:
	PRINT	<CR,LF,'  ERROR - HEX INPUT ONLY',CR,LF>
	JMP	PTX
;
VERSION$ERROR:
	PRINT	<'Sorry, you need CP/M version 2.0 or later',CR,LF>
	PRINT	<'to run this disk dump program'>
;
MONITOR:
	PRINT	CRLF,$
	LDA	DRVNO		;RESTORE LOGGED DRIVE NO
	MOV	E,A
	DISKIO	LOGIN
	LHLD	OLDSTK
	SPHL			;RESET OLD STACK POINTER
	RET
;
;
;   DATA ALLOCATIONS
;
SPACE:	DB	' $'		;ASCII SPACE
CRLF:	DB	0DH,0AH,24H	;ASCII CR LF
XLT:	DW	00		;SECTOR TRANSLATE TABLE ADDDR
DIVISOR:DW	00		;USED IN GROUP CALCS
SEC$INDEX:DW	00		;DITTO
I:	DW	00		;PSEUDO INDEX REGISTER
LINE:	DW	00		;LINE NUMBER FOR LISTING
IPOINT:	DW	00		;VARIABLE BUFFER POINTER
LASTIN:	DB	0		;LAST CONSOLE INPUT CHAR
INFLAG:	DB	0		;FLAG, RET FOR MORE CONSOLE INPUT
DRVNO:	DB	0		;STORAGE FOR ORIGINALLY LOGGED DRIVE
NEWDRV:	DB	0		;STORAGE FOR NEW DRIVE NO
TK0FLAG:DB	0		;DRIVE ON TRACK 0
HARD$D:	DB	0		;HARD DISK FLAG
TRACK:	DW	0		;SELECTED TRACK
NEW$TRACK:DW	0		;NEXT TRACK TO ACCESS
BSEC:	DW	00		;SELECTED BEGINNING SECTOR
ESEC:	DW	00		;SELECTED ENDING SECTOR
DCOUNT:	DW	00		;SECTORS DUMPED COUNT
SCOUNT:	DB	0		;SECTORS DUMPED IN CURRENT EXTENT
P$COUNT:DB	0		;GROUPS DONE COUNT
G:	DW	00		;CPM GROUP NO
S:	DW	00		;SECTOR NO WITHIN GROUP G
MAP$COUNT:DW	00		;GROUPS REMAINING TO MAP
OLDSTK:	DW	00		;STORAGE FOR OLD STACK POINTER
INB:	DW	00		;STORES POINTER TO INPUT BUFFER AREA
OUTB:	DW	00		;STORES POINTER TO DIRECTORY BUFFER AREA
FDFLAG:	DB	0		;FILE DUMP IN PROGRESS FLAG
EXFLAG:	DB	0		;EXTENT CHECK FLAG AND COUNT
SPT$ERR:DW	0		;MAX SECTOR NO. FOR BADSEC ERROR
;
DPB$ADDR:
	DW	0		;START OF DPB IN BIOS
DPB:				;START OF DISK PARAMETER BLOCK
SPT:	DW	00		;TOTAL NUMBER OF SECTORS/TRACK
BSH:	DB	0		;DATA ALLOC BLOCK SHIFT FACTOR
BLM:	DB	0		;CP/M DOCUMENTATION STRIKES AGAIN
EXM:	DB	0		;EXTENT MASK
DSM:	DW	00		;MAXIMUM DATA BLLOCK (GROUP) NUMBER
DRM:	DW	00		;TOTAL NO. OF DIRECTORY ENTRIES
AL0:	DB	0		;BIT MAP OF THE NO. OF BLOCKS (GROUPS)
AL1:	DB	0		;  RESERVED FOR THE DIRECTORY
CKS:	DW	00		;DIRECTORY CHECK VECTOR
OFF:	DW	00		;NUMBER OF RESERVED TRACKS
;
HEADER:	DS	14		;DISPLAY HEADER MESSAGE BUFFER
	DB	'$'
;
INBUF:	DS	10		;USED AS CONSOLE INPUT BUFFER
DIRBUF:	DS	03		;BUFFER FOR FILE GROUP ALLOCATION INFO
RCOUNT:	DS	01		;NO. OF RECORDS IN CURRENT EXTENT
ALLOC:	DS	18		;GROUP ALLOCATION TABLE	
;
XLT$SD:
	DB	00,06,12,18,24,04,10,16,22,02,08,14,20
	DB	01,07,13,19,25,05,11,17,23,03,09,15,21
;
ENDSTK:	DS	64		;STACK SPACE ALLOCATION
NEWSTK:				;SP LOAD POINT

	END	BEGIN

