
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*							*
*		Hard Disk Format Utility		*
*    Copyright (C) 1985, 1986 AMPRO COMPUTERS, INC.	*
*							*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *


; Assemble with asm.com or equivalent.
; All Z80 opcodes are defined with DB or DW statements.


; Revision history:
;
;  Ver	Date	Who	Description
;  ---	-----	---	------------------------------------------
;  1.5	F4.02	RJB	Added Seagate 225N.  Changed minimum bios
;			requirement to 3.7 for Seagate support.
;
;  1.4	E7.18	RJB	Added DTC format, changed minimum bios
;			requirement to 3.1 for Shugart support.
;
;  1.3	E6.27	RJB	Changed to support bios 3.0 to allow the 
;			user to manually enter the drive descriptor.
;
;  1.2  E3.14	RJB	Modified signon message to include known
;			rom revision limitations.
;
;     	E3.13	RJB	Updated XEBEC and ADAPTEC step routines.
;
;  1.1	E2.14	RJB	Added message reminding users to run the
;			FINDBAD program to find any bad sectors
;			on the disk they just formatted.
;
;	E2.12	RJB	Added message for those don't have any
;			hard disk units defined and run the hard
;			disk format program anyway.
;
;     	E1.30	RJB	Fixed Xebec directory clear, Adaptec step,
;			device select.  Added additional user
;			interface, error reporting routines.
;
;  1.0	DC.14	RLD	Original version.
;


; Program version, and current version date

VERS		EQU	15	; Current version
THIS$MONTH	EQU	4	; Today's month
THIS$DAY	EQU	02	; .       day
THIS$YEAR	EQU	86	; .       year

INT$REV		EQU	99	; Internal revision number


; TRUE and FALSE are defined here

NO		EQU	0
FALSE		EQU	0
YES		EQU	NOT FALSE
TRUE		EQU	NOT FALSE


; Operating characteristics

MIN$VERSION	EQU	37	; Minimum bios version allowed
CMD$LINE$OK	EQU	FALSE	; Allow command line input?
RTN$VIA$WB	EQU	TRUE	; Return to CP/M via warm boot?

INTERNAL	EQU	FALSE	; Internal (unreleased) revision?


; Include diagnostic messages?

TEST		EQU	NO	; TEST diagnostics?


; Current screen width and output buffer width

SWIDTH		EQU	80	; Screen width
PWIDTH		EQU	132	; Output buffer width


; Z-80 opcode equates (reversed so we can use a DW to enter them)

CPIR80		EQU	0B1EDH		; CPIR
INIR80		EQU	0B2EDH		; INIR
LDIR80		EQU	0B0EDH		; LDIR
OTIR80		EQU	0B3EDH		; OTIR

INI80		EQU	0A2EDH		; INI 
OUTI80		EQU	0A3EDH		; OUTI

SBCD80		EQU	043EDH		; LD	(dddd),BC
LBCD80		EQU	04BEDH		; LD	BC,(dddd)

SDED80		EQU	053EDH		; LD	(dddd),DE
LDED80		EQU	05BEDH		; LD	DE,(dddd)

SSPD80		EQU	073EDH		; LD	(dddd),SP
LSPD80		EQU	07BEDH		; LD	SP,(dddd)

LXIX		EQU	021DDH		; LXI	IX,dddd
SIXD80		EQU	022DDH		; LD	(dddd),IX
LIXD80		EQU	02ADDH		; LD	IX,(dddd)
POPIX		EQU	0E1DDH		; POP	IX
PUSHIX		EQU	0E5DDH		; PUSH	IX

LXIY		EQU	021DDH		; LXI	IY,dddd
SIYD80		EQU	022FDH		; LD	(dddd),IY
LIYD80		EQU	02AFDH		; LD	IY,(dddd)
POPIY		EQU	0E1FDH		; POP	IY
PUSHIY		EQU	0E5FDH		; PUSH	IY

; Bit SET/RESET/TEST Z-80 opcode equates (use DB to enter)
; Example: SET 7,D would be DB BIT,BSET+B7+ZD

BIT		EQU	0CBH		; Bit prefix

BTST		EQU	040H		; Bit test
BRES		EQU	080H		; Bit reset
BSET		EQU	0C0H		; Bit set

B0		EQU	000H		; Bit 0
B1		EQU	008H		; Bit 1
B2		EQU	010H		; Bit 2
B3		EQU	018H		; Bit 3
B4		EQU	020H		; Bit 4
B5		EQU	028H		; Bit 5
B6		EQU	030H		; Bit 6
B7		EQU	038H		; Bit 7

ZB		EQU	000H		; B Reg
ZC		EQU	001H		; C Reg
ZD		EQU	002H		; D Reg
ZE		EQU	003H		; E Reg
ZH		EQU	004H		; H Reg
ZL		EQU	005H		; L Reg
ZM		EQU	006H		; M Reg
ZA		EQU	007H		; A Reg

; Jump relative opcode equates (use DB to enter)
; Example: JR AGAIN would be DB JR,AGAIN-$-1

JR		EQU	018H		; JR addr
JRNZ		EQU	020H		; JR NZ,addr
JRZ		EQU	028H		; JR Z,addr
JRNC		EQU	030H		; JR NC,addr
JRC		EQU	038H		; JR C,addr

; IX and IY prefixes (use DB to enter)

IX		EQU	0DDH		; IX prefix
IY		EQU	0FDH		; IY prefix


; Character equates

CTRLC		EQU	'C'-'@'		; Ctrl-C (ETX)
BS		EQU	'H'-'@'		; Ctrl-H (Backspace)
TAB		EQU	'I'-'@'		; Ctrl-I (Tab)
LF		EQU	'J'-'@'		; Ctrl-J (Line feed)
FF		EQU	'L'-'@'		; Ctrl-L (Form feed)
CR		EQU	'M'-'@'		; Ctrl-M (Carriage return)
NAK		EQU	'U'-'@'		; Ctrl-U
CAN		EQU	'X'-'@'		; Ctrl-X (Cancel)
EOF		EQU	'Z'-'@'		; Ctrl-Z (CP/M End-of-file)
ESC		EQU	1BH		; Ctrl-[ (Escape)
EOS		EQU	'$'		;	 (CP/M End-of-string)
DEL		EQU	7FH		;	 (Delete)


; bdos equates

BDOS	EQU	5		; bdos entry


;	*	*	*	*	*	*	*	*
;
;	The code starts here ...
;
;	*	*	*	*	*	*	*	*


	ORG	0100H
	JMP	START


SCREEN$WIDTH:	DB	SWIDTH-1	; 1 less than actual #
SLOW$TERM:	DB	10		; Delay (ms) for slow term
CMD$LINE$CHRS:	DB	0		; # of cmd line chrs left
CMD$LINE$PTR:	DW	0		; Ptr to next cmd line chr


NAME$MSG:
	DB	0DH,'AMPRO '
NAME:	DB	'Hard Disk Format'
	DB	' Utility',CR,LF
	DB	'Copyright (C) 1985 AMPRO Computers, Inc.',CR,LF
	DB	'Version ',VERS/10+'0','.',VERS MOD 10+'0'
	IF	INTERNAL	; Display internal revision number?
	DB	'x',INT$REV/10+'0',INT$REV MOD 10+'0'
	DB	'  [',THIS$YEAR-80+'@'
	DB	THIS$MONTH+'0'+((THIS$MONTH/10)*7)
	DB	'.',THIS$DAY/10+'0',THIS$DAY MOD 10+'0',']'
	ENDIF
	DB	CR,LF,LF,'$',CR
HELP$MSG:
	DB	'Usage: HFORMAT'
	DB	CR,LF,'$',CR,' ',EOF

SIGNON$MSG:
	DB	'The AMPRO Hard Disk Format utility is used to '
	DB	'format hard disk (winchester) drives, on the '
	DB	'Adaptec ACB-4000, '
	DB	'Data Technology 500 Series '
	DB	'(510A, 510B, 520A, 520B), '
	DB	'Shugart 1610-4, '
	DB	'Xebec 1410 (rom rev "E" or later), '
	DB	'Xebec 1410A (rom rev "D" or later) '
	DB	'controllers, or the '
	DB	'Xebec Owl or Seagate 225N combination drive/controllers.  '
	DB	'You will be asked for the controller '
	DB	'type, controller SCSI address, drive unit number, '
	DB	'and the drive descriptor information.'
	DB	CR,LF,LF,'$'
WARNING$MSG:
	DB	'CAUTION: ALL DATA WILL BE ERASED ON THE DRIVE YOU '
	DB	'SELECT TO FORMAT.',CR,LF,LF,LF
	DB	'$'

;
; Initialize the command line input pointer.
;

START: 
	IF	CMD$LINE$OK	; If we want to allow cmd line input,
	LXI	H,0080H		; .  Save the command line.
	LXI	D,INBUF		; .  .
	LXI	B,128		; .  .
	DW	LDIR80		; .  .
	LXI	H,INBUF		; .  Set up ptrs and count of chrs
	MOV	A,M		; .  .
	STA	CMD$LINE$CHRS	; .  Save count of characters,
	INX	H		; .  Bump line ptr,
	SHLD	CMD$LINE$PTR	; .  .  and save ptr to cmd line
	ENDIF
	IF	NOT CMD$LINE$OK	; If no command line input allowed,
	MVI	A,0		; .  Clear the count of characters,
	STA	CMD$LINE$CHRS	; .  .
	LXI	H,0		; .  but set the ptr up anyway.
	SHLD	CMD$LINE$PTR	; .  .
	ENDIF

	IF	NOT RTN$VIA$WB	; If we're returning without warm boot,
	LXI	H,0000H		; .  then get the old stack ptr,
	DAD	SP		; .  and
	SHLD	STACK		; .  save it for later,
	ENDIF

;
; This is where you can jump to start the program over.
;

TOP$MENU:
	LXI	SP,STACK	; Stuff SP with our stack.
	CALL	GET$BIOS$VERS	; Copy the jmp tbl to a local area

;
; Display the name and signon message
;

	IF	CMD$LINE$OK	; If cmd line input is possible 
	LDA	CMD$LINE$CHRS	; Check for any input and skip
	ORA	A		; .  the initial messages if any
	JNZ	CHECK$B$VERS	; .  chrs in the cmd line.
	ENDIF

	CALL	CLEAR$SCREEN	; Clear the screen
	LXI	D,NAME$MSG	; Display the name, version, etc.
	CALL	CENTER$OUTPUT	; .
	LXI	D,SIGNON$MSG	; and the initial message
	CALL	JUSTIFY		; .

;
; Check the version of the bios against the minimum version allowed.
; If the bios is not at least the minimum, display an error message
; and exit to the operating system.
;

CHECK$B$VERS:
	LDA	BIOS$VERSION	; Get bios version #
	CPI	MIN$VERSION	; Check against minimum version
	JNC	WHICH$BIOS	; At least minimum version . . .
	LXI	D,BIOS$PLUS	; Not minimum, display error message
	CALL	JUSTIFY		; .
	JMP	LB$WBOOT	; and exit.

BIOS$PLUS:
	DB	'This program requires AMPRO bios version '
VERSION$REQ:
	DB	MIN$VERSION/10,'.',MIN$VERSION MOD 10,' or later.'
	DB	CR,LF,'$'

;
; Perform any initialization particular to each version of the Ampro
; bios, if necessary.
;

WHICH$BIOS:
	CALL	RET$TO$CONT
	JZ	ALL$DONE


;
;  Program main loop . . .
;
AGAIN:
	CALL	CLEAR$SCREEN	; Clear the screen

	LXI	D,SCSI$ADDR$MSG	; Prompt for SCSI address
	LXI	H,SCSI$ADDR$OKC	; .
	CALL	PROMPT		; .	[ 0 1 2 3 4 5 6 7 ]
	JZ	TOP$MENU	; .
	MOV	A,B		; .
	DCR	A		; .
	CALL	BIN$TO$SCSI	; .
	STA	SCSI$ADDR	; .
	CALL	A$TO$HL$HEX	; .
	SHLD	S$ADDR$CODE	; .

	LXI	D,HDC$TYPE$MSG	; Prompt for controller type
	LXI	H,HDC$TYPE$OKC	; .
	CALL	PROMPT		; .	[ 1 2 3 4 ]
	JZ	AGAIN		; .
	MOV	A,B		; .
	DCR	A		; .
	STA	HDC$TYPE	; .

	MVI	A,0		; Set logical unit to 0 for SCSI drives
	STA	DRV$LUN		; .

	LDA	HDC$TYPE	; If Xebec OWL or Seagate 225n
	CPI	XEBEC$OWL	; .  skip the drive characteristics
	JZ	SCSI$DRIVE	; .  as these SCSI drives already
	CPI	SEAGATE$225N	; .  "know" what their characteristics
	JZ	SCSI$DRIVE	; .  are.

	LXI	D,DRV$LUN$MSG	; Prompt for drive logical unit #
	LXI	H,DRV$LUN$OKC	; .
	CALL	PROMPT		; .	[ 0 1 2 3 ]
	JZ	AGAIN		; .
	MOV	A,B		; .
	DCR	A		; .
	RRC			; Shift response into high bits
	RRC			; .
	RRC			; .
	ANI	0E0H		; .
	STA	DRV$LUN		; .

	LXI	D,DRV$INFO$MSG	; Ask for drive characteristics
	CALL	JUSTIFY		; .
	LXI	D,DRV$CYLS$MSG	; .  (Cylinders)
	CALL	PROMPT$DECIMAL	; .
	JZ	AGAIN		; .
	LXI	H,DRV$CYLS	; .
	MOV	M,D		; .
	INX	H		; .
	MOV	M,E		; .
	LXI	D,DRV$HEAD$MSG	; .  (Heads)
	CALL	PROMPT$DECIMAL	; .
	JZ	AGAIN		; .
	STA	DRV$HEAD	; .
	LXI	D,DRV$RWC$MSG	; .  (RWC)
	CALL	PROMPT$DECIMAL	; .
	JZ	AGAIN		; .
	LXI	H,DRV$RWC	; .
	MOV	M,D		; .
	INX	H		; .
	MOV	M,E		; .
	LXI	D,DRV$WPC$MSG	; .  (WPC)
	CALL	PROMPT$DECIMAL	; .
	JZ	AGAIN		; .
	LXI	H,DRV$WPC	; .
	MOV	M,D		; .
	INX	H		; .
	MOV	M,E		; .

	LDA	HDC$TYPE	; .
	CPI	SHUGART		; .
	JNZ	NOT$SHUGART	; .
	LXI	D,DRV$LZ$MSG	; .  (Landing zone)
	CALL	PROMPT$DECIMAL	; .
	JZ	AGAIN		; .
	LXI	H,DRV$LZ	; .
	MOV	M,D		; .
	INX	H		; .
	MOV	M,E		; .

NOT$SHUGART:
	CALL	GET$STEP	; Get step rate table for this HDC
	CALL	PROMPT		; Get step rate factor
	JZ	AGAIN		; .
	MOV	A,B		; .
	DCR	A		; .
	STA	STEP$RATE	; .

SCSI$DRIVE:
	LXI	D,INTL$MSG	; Prompt for interleave factor
	LXI	H,INTL$OKC	; .
	CALL	PROMPT		; .	[ 1 2 3 4 5 6 7 8 9 ]
	STA	INTL$ECHO	; .
	MOV	A,B		; .
	STA	INTERLEAVE	; .  (& save the interleave)
	JZ	AGAIN		; .  if <ESC>, back to main loop

	LXI	D,RTF$MSG	; Prompt for return key, <ESC> to
	LXI	H,RTF$OKC	; .  main menu, <CTRL-C> exits
	CALL	PROMPT		; .      [ ESC CR ]
	JZ	AGAIN		; .  if <ESC>, back to main loop

	CALL	HD$FORMAT	; Do the format routine

	LXI	D,AGAIN$MSG	; Display successful msg & prompt
	LXI	H,AGAIN$OKC	; .  for another drive to format.
	CALL	PROMPT		; .      [ ESC N Y CR ]
	JZ	ALL$DONE	; .  if <ESC>, all done
	CPI	'N'		; If yes or CR, play it again, sam,
	JNZ	AGAIN		; .  otherwise fall into ALL$DONE

;
; Restore the old stack pointer and exit the program.
;

ALL$DONE:
	JMP	LB$WBOOT	; Return via warm boot


SCSI$ADDR$MSG:	DB	'What is the SCSI address of your '
		DB	'controller (0-7)? ','$'
SCSI$ADDR$OKC:	DB	'01234567',0

HDC$TYPE$MSG:	DB	CR,LF,'Which type of controller are you using:'
		DB	CR,LF,TAB,'1  -  Adaptec ACB 4000'
		DB	CR,LF,TAB,'2  -  Shugart 1610-4'
		DB	CR,LF,TAB,'3  -  Xebec OWL (drive/controller)'
		DB	CR,LF,TAB,'4  -  Xebec 1410, 1410A'
		DB	CR,LF,TAB,'5  -  Seagate 225N (drive/controller)'
		DB	CR,LF,TAB,'6  -  Data Technology 500 Series'
		DB	CR,LF,LF
		DB	'Choose one (1-6): ','$'
HDC$TYPE$OKC:	DB	'123456',0

ADAPTEC:	EQU	0
SHUGART:	EQU	1
XEBEC$OWL:	EQU	2
XEBEC$1410:	EQU	3
SEAGATE$225N:	EQU	4
DTC$500:	EQU	5

DRV$LUN$MSG:	DB	CR,LF
		DB	'Which drive on the controller do you want '
		DB	'to format (0-3)? ','$'
DRV$LUN$OKC:	DB	'0123',0

DRV$INFO$MSG:	DB	CR,LF
		DB	'Please enter the characteristics of the '
		DB	'drive you wish to format:',CR,LF,'$'

DRV$CYLS$MSG:	DB	'      Number of cylinders: ','$'
DRV$HEAD$MSG:	DB	'          Number of heads: ','$'
DRV$RWC$MSG:	DB	'Starting cylinder for RWC: ','$'
DRV$WPC$MSG:	DB	'Starting cylinder for WPC: ','$'
DRV$LZ$MSG:	DB	'    Landing zone cylinder: ','$'

ADP$STEP$MSG:	DB	CR,LF,LF
		DB	'Adaptec step mode (choose from the '
		DB	'following table):',CR,LF
		DB	TAB,'0 -   3ms step',CR,LF
		DB	TAB,'1 -  28us buffered step',CR,LF
		DB	TAB,'2 -  12us buffered step',CR,LF
		DB	'Which step mode (0, 1, or 2)? ','$'
ADP$STEP$OKC:	DB	'012',0

DTC$STEP$MSG:	DB	CR,LF,LF
		DB	'Data Technology step mode (choose '
		DB	'from the following table):',CR,LF
		DB	TAB,'0 -   3ms step',CR,LF
		DB	TAB,'1 - 200us buffered step',CR,LF
		DB	TAB,'2 - 150us buffered step',CR,LF
		DB	TAB,'3 - 100us buffered step',CR,LF
		DB	TAB,'4 -  50us buffered step',CR,LF
		DB	'Which step mode (0, 1, 2, 3, or 4)? ','$'
DTC$STEP$OKC:	DB	'01234',0

SHU$STEP$MSG:	DB	CR,LF,LF
		DB	'Shugart step mode (choose from the '
		DB	'following table):',CR,LF
		DB	TAB,'0 -   3ms step',CR,LF
		DB	TAB,'1 -   1ms step',CR,LF
		DB	TAB,'2 - 200us buffered step',CR,LF
		DB	TAB,'3 -  70us buffered step',CR,LF
		DB	TAB,'4 -  50us buffered step',CR,LF
		DB	'Which step mode (0, 1, 2, 3, or 4)? ','$'
SHU$STEP$OKC:	DB	'012345',0

XEB$STEP$MSG:	DB	CR,LF,LF
		DB	'Xebec step mode (choose from the '
		DB	'following table):',CR,LF
		DB	TAB,'0 -   3ms step',CR,LF
		DB	TAB,'4 - 200us buffered step',CR,LF
		DB	TAB,'5 -  70us buffered step',CR,LF
		DB	TAB,'6 -  30us buffered step',CR,LF
		DB	TAB,'7 -  15us buffered step',CR,LF
		DB	'Which step mode (0, 4, 5, 6, or 7)? ','$'
XEB$STEP$OKC:	DB	'04567',0

INTL$MSG:	DB	CR,LF,'What sector interleave factor '
		DB	'(1-9, usually 2)? ','$'
INTL$OKC:	DB	'123456789',0


SCSI$ADDR:	DB	01H
HDC$TYPE:	DB	0
DRV$LUN:	DB	0
DRV$CYLS:	DW	0	; Keep these four items in order
DRV$HEAD:	DB	0	; .  (cyls, head, rwc, wpc)
DRV$RWC:	DW	0	; .
DRV$WPC:	DW	0	; .
DRV$LZ:		DW	0
STEP$RATE:	DB	0
INTERLEAVE:	DB	2

* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*							*
*	 Hard disk display & format routines . . .	*
*							*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *


GET$STEP:
	LDA	HDC$TYPE	; Get controller type
	LXI	H,H$STEP$EXE	; & step table starting addr
	JMP	GO$TABLE	; Jump to controller routine

H$STEP$EXE:
	DW	H$STEP$ADP	; Adaptec ACB 4000
	DW	H$STEP$SHU	; Shugart 1610-4
	DW	H$STEP$XEB	; Xebec Owl
	DW	H$STEP$XEB	; Xebec 1410, 1410A
	DW	H$STEP$ADP	; Seagate 225N
	DW	H$STEP$DTC	; Data Technology 500 Series

H$STEP$ADP:
	LXI	D,ADP$STEP$MSG
	LXI	H,ADP$STEP$OKC
	RET

H$STEP$DTC:
	LXI	D,DTC$STEP$MSG
	LXI	H,DTC$STEP$OKC
	RET

H$STEP$SHU:
	LXI	D,SHU$STEP$MSG
	LXI	H,SHU$STEP$OKC
	RET

H$STEP$XEB:
	LXI	D,XEB$STEP$MSG
	LXI	H,XEB$STEP$OKC
	RET


DO$SCSI:
;
; Entry:
;	HL = Command string
;	DE = Data string
;	SCSI address in SCSI$ADDR
;	Drive logical unit in DRV$LUN
;
; Exit:
;	HL = unchanged
;	DE = unchanged if no error, else ptr to error data
;	A  = SCSI error code (0 = no error)
;
	INX	H		; Point to drive unit
	LDA	DRV$LUN		; 'OR' it in
	ORA	M		; .
	MOV	M,A		; .
	DCX	H		; Set HL back
	LDA	SCSI$ADDR	; Get SCSI address
	CALL	LB$SCSIDRV	; Call SCSI driver,
	ORA	A		; set status,
	RET			; and return.


HD$FORMAT:
;
; [E6.07]
;
; Format a hard disk unit.
;
; Entry:
;	Drive format information in the following locations:
;
;	SCSI address of controller	SCSI$ADDR
;	Hard disk controller type	HDC$TYPE
;	Disk drive logical unit #	DRV$LUN
;	Interleave factor		INTERLEAVE
;
; Exit:
;	designated disk unit is formatted
;
; Modifies:
;	all
;
	LXI	D,E5BUF		; Fill first 256 bytes of buffer 
	LXI	B,00E5H		; . with 0E5H, used to clear unit
	CALL	FILL$BLOCK	; . if HDC doesn't clear by itself.
	LXI	H,E5BUF		; Fill next 8K with 0E5H.  When the
	LXI	D,E5BUF+256	; . HD$DIR$CLEAN routine is run, the
	LXI	B,8192-256	; . unit will be cleared "track" by
	DB	0EDH,0B0H	; . "track".

	LDA	HDC$TYPE	; Get controller type
	LXI	H,H$FMT$EXE	; & table starting address
	JMP	GO$TABLE	; Jump to controller routine

H$FMT$EXE:
	DW	H$DO$ADAPTEC	; Adaptec 4000
	DW	H$DO$SHUGART	; Shugart 1610-4
	DW	H$DO$OWL	; Xebec Owl
	DW	H$DO$XEBEC	; Xebec 1410 rev e, 1410A rev d
	DW	H$DO$SEAGATE	; Seagate 225N
	DW	H$DO$DTC	; Data Technology 500 Series

H$DO$UNKNOWN:
	LXI	D,NEW$CTRL	; Print error message for an unknown
	CALL	JUSTIFY		; . controller type 
	MVI	A,0FFH		; Set error status
	JMP	H$FMT$DONE	; .

H$DO$ADAPTEC:
	LXI	D,FMT$START$MSG	; Show the user we're going ...
	CALL	JUSTIFY		; .
	LXI	D,A$MD		; Where to put the fmt data
	LXI	H,DRV$CYLS	; .
	LXI	B,7		; .
	DB	0EDH,0B0H	; .  (LDIR)

	LDA	STEP$RATE	; Get step rate
	STA	A$STEP		; Save step rate

	XRA	A		; Set stop position to beyond the
	STA	A$LZ		; last track

	LDA	INTERLEAVE	; Set interleave
	STA	A$INTL		; .

	LXI	H,A$MSC		; Mode Select
	LXI	D,A$MSD		; .
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	LXI	H,A$FC		; Format unit
	LXI	D,A$FC		; . (no data required)
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

;	CALL	H$DIR$CLEAN	; Clean directory area (not needed)
;	JNZ	FMT$ERROR	; .
	JMP	H$FMT$DONE	; and jump to the end routine

A$MSC:	DB	15H,0,0		; Mode select
	DB	0,16H,0		; .
A$MSD:	DB	0,0,0,8		; Mode select data
	DB	0,0,0,0,0	; .
	DB	0,2		; . (block size = 512 bytes)
	DB	0		; .
	DB	1		; .
A$MD:	DB	0,0,0,0,0,0,0	; . (format data from bios)
A$LZ:	DB	0		; . (landing zone position)
A$STEP:	DB	0		; . (adaptec step rate)

A$FC:	DB	04H		; Format unit
	DB	06H		; . (use next byte for fill)
	DB	0E5H		; . (fill byte)
	DB	00		; .
A$INTL:	DB	00		; . (interleave)
	DB	00		; .


H$DO$DTC:
	LXI	D,FMT$START$MSG	; Show the user we're going ...
	CALL	JUSTIFY		; .

	LHLD	DRV$CYLS	; Move the data to the proper area
	SHLD	D$CYL		; .

	LDA	DRV$RWC+1	; .
	STA	D$RWC		; .

	LDA	DRV$HEAD	; .
	DCR	A		; .
	STA	D$HD		; .

	LDA	STEP$RATE	; Get step rate
	MOV	E,A		; Translate from table
	MVI	D,0		; .
	LXI	H,D$TRAN$TBL	; .
	DAD	D		; .
	MOV	A,M		; . 
	STA	D$STPR		; .

	LDA	INTERLEAVE	; Set interleave
	STA	D$INTL		; .

	LXI	H,D$MSC		; Mode Select
	LXI	D,D$MSD		; .
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	LXI	H,D$FC		; Format unit
	LXI	D,D$FC		; . (no data required)
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

;	CALL	H$DIR$CLEAN	; Clean directory area
;	JNZ	FMT$ERROR	; .
	JMP	H$FMT$DONE	; and jump to the end routine

D$TRAN$TBL:
	DB	60		; 3ms
	DB	4		; 200us
	DB	3		; 150us
	DB	2		; 100us
	DB	1		; 50us

D$MSC:	DB	0C2H,0,0	; Mode select
	DB	0,0,0		; .
D$MSD:	DB	11		; Mode select data
D$STPR:	DB	0		; . (Step rate)
	DB	0		; . (Step mode)
D$HD:	DB	0		; . (# of heads)
D$CYL:	DB	0,0		; . (# of cylinders)
D$RWC:	DB	0		; . (RWC starting cylinder)
	DB	0,0

D$FC:	DB	04H		; Format unit
	DB	0		; . 
	DB	0		; . 
	DB	0		; .
D$INTL:	DB	0		; . (interleave)
	DB	0		; .


H$DO$SHUGART:
	LXI	D,FMT$START$MSG	; Show the user we're going ...
	CALL	JUSTIFY		; .
	LHLD	DRV$CYLS	; Move the data to the proper area
	SHLD	S$CYL		; .

	LHLD	DRV$LZ		; .
	SHLD	S$LZ		; .

	LHLD	DRV$RWC		; .
	SHLD	S$RWC		; .

	LHLD	DRV$WPC		; .
	SHLD	S$WPC		; .

	LDA	DRV$HEAD	; .
	STA	S$HD		; .

	LDA	STEP$RATE	; Get step rate
	MOV	E,A		; Translate from table
	MVI	D,0		; .
	LXI	H,S$TRAN$TBL	; .
	DAD	D		; .
	DAD	D		; .
	MOV	A,M		; . 
	STA	S$STPR		; .
	INX	H		; .
	MOV	A,M		; .
	STA	S$STPR+1	; .

	LDA	INTERLEAVE	; Set interleave
	STA	S$INTL		; .

	LXI	H,S$MSC		; Mode Select
	LXI	D,S$MSD		; .
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	LXI	H,S$FC		; Format unit
	LXI	D,S$FC		; . (no data required)
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	CALL	H$DIR$CLEAN	; Clean directory area
	JNZ	FMT$ERROR	; .
	JMP	H$FMT$DONE	; and jump to the end routine

S$TRAN$TBL:
	DB	4,3		; 3ms
	DB	4,1		; 1ms
	DB	0,200		; 200us
	DB	0,70		; 70us
	DB	0,50		; 50us

S$MSC:	DB	15H,0,0		; Mode select
	DB	0,20H,0		; .
S$MSD:	DB	0,0,0,8		; Mode select data
	DB	0,0,0,0,0	; .
	DB	0,2,0		; . (Block size = 512 bytes)
	DB	80H,0		; .
S$HD:	DB	0		; . (# of heads)
S$SPW:	DB	16		; . (Step pulse width = 16us)
S$STPR:	DB	0,0		; . (Step rate)
S$CYL:	DB	0,0		; . (# of cylinders)
S$RWC:	DB	0,0		; . (RWC starting cylinder)
S$WPC:	DB	0,0		; . (WPC starting cylinder)
S$LZ:	DB	0,0		; . (Landing zone cylinder)
	DB	0,0,0,0,0,0	; .

S$FC:	DB	04H		; Format unit
	DB	0		; . 
	DB	0		; . 
	DB	0		; .
S$INTL:	DB	0		; . (interleave)
	DB	0		; .

H$DO$OWL:
	MVI	A,7		; Set step rate to '7' for OWL
	STA	STEP$RATE	; (and fall through to H$DO$XEBEC ...
H$DO$XEBEC:
	LXI	D,FMT$START$MSG	; Show the user we're going ...
	CALL	JUSTIFY		; .
	LXI	D,X$ID		; Where to put the fmt data
	LXI	H,DRV$CYLS	; .
	LXI	B,7		; .
	DB	0EDH,0B0H	; .  (LDIR)

	LDA	STEP$RATE	; Get step rate
	ORI	20H		; .  (set sector buffer bit)
	STA	X$STEP		; .
	LDA	INTERLEAVE	; Set interleave
	STA	X$INTL		; .

	LXI	H,X$IC		; Init Drive Characteristics
	LXI	D,X$ID		; .
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	LXI	H,X$WBC		; Write Sector Buffer
	LXI	D,E5BUF		; .
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	LXI	H,X$FC		; Format unit
	LXI	D,X$FC		; . (no data required)
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

;	CALL	H$DIR$CLEAN	; Clean directory area (not needed)
;	JNZ	FMT$ERROR	; .
	JMP	H$FMT$DONE	; and jump to the end routine

X$IC:	DB	0CH		; Init Drive Characteristics
	DB	0,0,0,0,0	; .
X$ID:	DB	0,0,0,0,0,0,0,0	; Init Drive data

X$WBC:	DB	0FH		; Write sector buffer
	DB	0,0,0,0,0	; .

X$FC:	DB	04H		; Format unit
	DB	0,0,0		; .
X$INTL:	DB	0		; .
X$STEP:	DB	0		; .

H$DO$SEAGATE:
	LXI	D,FMT$START$MSG	; Show the user we're going ...
	CALL	JUSTIFY		; .

	LDA	INTERLEAVE	; Set interleave
	STA	SEA$INTL	; .

	LXI	H,SEA$RSD	; Read sense data
	LXI	D,SEA$RSD	; . (to clear reset error)
	CALL	DO$SCSI		;
	JNZ	FMT$ERROR	;

	LXI	H,SEA$FC	; Format unit
	LXI	D,SEA$FC	; . (no data required)
	CALL	DO$SCSI		; .
	JNZ	FMT$ERROR	; .

	CALL	H$DIR$CLEAN	; Clean directory area
	JNZ	FMT$ERROR	; .
	JMP	H$FMT$DONE	; and jump to the end routine

SEA$RSD:
	DB	03H		; Read sense data
	DB	0,0,0,0,0	;

SEA$FC:	DB	04H		; Format unit
	DB	00H		; .
	DB	00H		; .
	DB	00		; .
SEA$INTL:
	DB	00		; . (interleave)
	DB	00		; .


H$DIR$CLEAN:
;
; This routine clears the drive by reading the capacity of the unit
; and writing sectors filled with 0E5H.  This clears all directory
; areas on the unit, no matter what partitioning is provided.  This
; clearing is done 'track by track' by sending the data in 8Kb
; blocks.
;
	LXI	D,CLR$DIR$MSG	; Tell 'em we're clearing his drive
	CALL	JUSTIFY		; .

	LXI	H,RD$CAP$CMD	; Read the capacity of the drive
	LXI	D,RD$CAP$DATA	; .
	CALL	DO$SCSI		; .
	RNZ			; Return with status if error

	; Convert from blocks to tracks by multiplying HL and
	; A by 16 and throwing away the low 8 bits (which is
	; the same as dividing by 16, only this is easier).

				;   HL reg pair    C  A reg
	ORA	A		; ---------------- - --------
	LDA	RD$CAP$DATA+1	; ---------------- - --------
	MOV	H,A		; xxxxFEDC-------- - --------
	LDA	RD$CAP$DATA+2	; xxxxFEDC-------- - --------
	MOV	L,A		; xxxxFEDCBA987654 - --------
	LDA	RD$CAP$DATA+3	; xxxxFEDCBA987654 - 3210xxxx
	ADD	A		; xxxxFEDCBA987654 3 210xxxx-
	DB	0EDH,06AH	; xxxFEDCBA9876543 - 210xxxx-
	ADD	A		; xxxFEDCBA9876543 2 10xxxx--
	DB	0EDH,06AH	; xxFEDCBA98765432 - 10xxxx--
	ADD	A		; xxFEDCBA98765432 1 0xxxx---
	DB	0EDH,06AH	; xFEDCBA987654321 - 0xxxx---
	ADD	A		; xFEDCBA987654321 0 xxxx----
	DB	0EDH,06AH	; FEDCBA9876543210 - xxxx----

	; HL now contains the number of 'tracks'

CLR$NEXT$TRACK:
	DCX	H		; Decrement the track count
	SHLD	CUR$TRACK	; Save current track

	XRA	A		; Convert back to 20-bit
				; A reg     C  HL reg pair
				; --------  -  FEDCBA9876543210
	DAD	H		; --------  F  EDCBA9876543210-
	RAL			; -------F  -  EDCBA9876543210-
	DAD	H		; -------F  E  DCBA9876543210--
	RAL			; ------FE  -  DCBA9876543210--
	DAD	H		; ------FE  D  CBA9876543210---
	RAL			; -----FED  -  CBA9876543210---
	DAD	H		; -----FED  C  BA9876543210----
	RAL			; ----FEDC  -  BA9876543210----

	XCHG			; Save block # in write command
	LXI	H,WRITE$BLK$CMD	; .
	INX	H		; .
	MOV	M,A		; Move high byte
	INX	H		; .
	MOV	M,D		; Move middle byte
	INX	H		; .
	MOV	M,E		; Move low byte

	LXI	H,WRITE$BLK$CMD	; Write 8K block to disk at this blk
	LXI	D,E5BUF		; .
	CALL	DO$SCSI		; .
	RNZ			; Return with status if error
	LHLD	CUR$TRACK	; Get track number back
	MOV	A,H		; See if we're done
	ORA	L		; .
	JNZ	CLR$NEXT$TRACK	; Non-zero means more to do . . .

	XRA	A		; Set A-OK status
	RET

RD$CAP$CMD:
	DB	025H,0		; Read capacity command
	DB	0,0,0,0,0,0,0,0	; .
RD$CAP$DATA:
	DB	0,0,0,0,0,0,0,0	; Read capacity data area

WRITE$BLK$CMD:
	DB	00AH,0		; Write data cmd
	DB	0,0,16,0	; .

CUR$TRACK:
	DW	0		; Current track data


FMT$ERROR:
	LXI	D,NO$RESPONSE	; 
	CPI	0FFH		; Timeout error?
	JZ	FMT$TIMEOUT	; Yes, print timeout msg
	MOV	A,M		; No, Get last command
	CALL	A$TO$HL$HEX	; Convert to hex
	SHLD	ERR$CMD		; Save in output line
	LXI	H,FMT$ERR$CMD	; Get status from controller
	LXI	D,FMT$ERR$DATA	; .
	CALL	DO$SCSI		; .
	LDAX	D		; Get error code
	CALL	A$TO$HL$HEX	; Convert to hex
	SHLD	ERR$CODE
	LXI	D,FORMAT$ERROR	; Print error message
FMT$TIMEOUT:
	CALL	JUSTIFY		; .
	MVI	A,0FFH		; Set error status
	JMP	H$FMT$DONE	; and return

FMT$ERR$CMD	DB	3,0,0	; Request sense
		DB	0,4,0
FMT$ERR$DATA	DB	0,0,0,0	; Sense data

H$FMT$DONE:
	ORA	A		; Set status
	LXI	D,FMT$ERR$MSG	; Print "Errors occurred during
	PUSH	PSW		; .  formatting" if NOT ZERO
	CNZ	JUSTIFY		;
	POP	PSW		;
	LXI	D,FMT$OK$MSG	; Print "Format complete.  No
	CZ	JUSTIFY		; .  errors" if ZERO
	RET			;


* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*							*
*		Library routines . . .			*
*							*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *


A$TO$HL$HEX:
;
; [DC.27]
;
; Converts the number in A to the hex digits in HL
;
; Entry:
; 	A  = number to convert
;
; Exit:
;	HL = the hex equivalent of the number (L=high, H=low)
;		(use shld to store the converted number)
;
; Modifies: none
;
	PUSH	PSW		; Save original number
	RRC			; Get high nybble
	RRC			; .
	RRC			; .
	RRC			; .
	ANI	0FH		; .
	CALL	A$TO$HEX	; Convert to hex
	MOV	L,A		; Save in L register
	POP	PSW		; Get original number back
	PUSH	PSW		; Save again for later
	ANI	0FH		; Get low nybble
	CALL	A$TO$HEX	; Convert to hex
	MOV	H,A		; Save in H register
	POP	PSW		; Get original number back
	RET			; and return

A$TO$HEX:			; Convert A to a hex digit
	CPI	0AH		; If 0-9, we don't need to 
	JM	A$TO$HEX$2	; .  add any offset
	ADI	07H		; Offset for A-F
A$TO$HEX$2:			; .
	ADI	30H		; ASCII bias
	RET			; and return


BIN$TO$SCSI:
; Converts binary 0-7 to SCSI address 
;
; Entry:
;	A  = number to convert (0-7)
;
; Exit:
;	A  = converted SCSI address    [00H = error]
;
; Modifies: B
;
	ANI	07H		; Mask out all but addrs 0-7
	INR	A		; Bump A to shift at least one
	MOV	B,A		; .  bit and move to B register
	XRA	A		; Clear A register
	STC			; Set carry for shift
BIN$NEXT$BIT:
	RAL			; Shift left one bit
	DCR	B		; Decrement count
	JNZ	BIN$NEXT$BIT	; Zero means all done
	RET


CENTER$OUTPUT:
;
; [DC.20]
;
; Automatically centers the output line(s) based on the line width
; stored in SCREEN$WIDTH.  Each line is delimited with CR+LF.  This
; routine will return to the caller when it encounters the string
; terminator, '$'.  Any additional LF characters after a CR+LF pair
; will be passed through.
;
; Entry:
;	DE = Pointer to output string(s), terminated with CR+LF.
;
; Exit:
;	The output string(s) are sent to the screen
;
; Modifies: DE
;
	PUSH	PSW		; Save registers
	PUSH	B		; .
	PUSH	H		; .
CENTER$NEXT$LN:
	CALL	GET$STRLEN	; Get length to next CR or '$' in B
	MOV	A,B		; Check for zero length
	ORA	A		; .
	JZ	NEXT$DELIM	; If so, output the CR, LF, etc.
	LDA	SCREEN$WIDTH	; Compute offset needed to center line
	STC			; .
	SBB	B		; .  (if there are too many chrs, just
	JC	NO$BLANKS	; .   print the line as is . . . )
	ANI	0FEh		; Clear least significant bit
	RRC			; .  and rotate to divide by two
	MVI	C,' '		; Output enough blanks to center line
	CNZ	CON$CHR$AC	; .  (only if count is non-zero)
NO$BLANKS:
	MOV	A,M		; Save CR for later
	MVI	M,'$'		; Plug position with '$' for cp/m
	CALL	CON$MSG		; .  print string function
	MOV	M,A		; Restore saved CR
NEXT$DELIM:
	MOV	A,M		; Get character
	CPI	CR		; Print it if CR
	JZ	OUTPUT$DELIM	; .
	CPI	LF		; Print it if LF
	JZ	OUTPUT$DELIM	; .
	CPI	EOS		; Stop processing if EOS ('$')
	JZ	CENTER$DONE	; .
	XCHG			; Put new pointer in DE
	JMP	CENTER$NEXT$LN	; Go & do the next line
OUTPUT$DELIM:
	CALL	CON$CHR		; Output delimiter
	CPI	LF		; Wait 10ms if we have a line feed
	CZ	WAIT		; .
	INX	H		; Point to next chr
	JMP	NEXT$DELIM	; and check that one, also
CENTER$DONE:
	POP	H		; Restore registers
	POP	B		; .
	POP	PSW		; .
	RET			; and return


CLEAR$SCREEN:
;
; [E1.28]
;
; This routine clears the screen by calling DO$CRLF 26 times.
;
; Entry:
;	None
;
; Exit:
;	The screen is cleared
;
; Modifies:
;	None
;
	PUSH	PSW		; Save just in case
	MVI	A,26		; 26 CRLF's
C$NEXT$LINE:
	CALL	DO$CRLF		; Next line
	DCR	A		; Done?
	JNZ	C$NEXT$LINE	; Nope.
	POP	PSW		; Restore original AF
	RET			; and return


CON$CHR:
;
; [DC.20]
;
; This routine sends the character in the "A" register to the console
; through the BDOS conout call.
;
; Entry:
;	A  = character to send
;
; Exit:
;	character is sent to the console
;
; Modifies:
;	None
;
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,2
	MOV	E,A
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET


CON$CHR$AC:
;
; [DC.20]
;
; This routine sends the character in the C register to the console
; the number of times in the A register.
;
; Entry:
;	A  = Number of times to send character
;	C  = Character to send
;
; Exit:
;	Same
;
; Modifies:
;	None
;
	PUSH	PSW		; Save all registers
	PUSH	B		; .
	PUSH	D		; .
	PUSH	H		; .
	MOV	B,A		; Move data to accomodate CP/M
	MOV	A,C		; .
NEXT$CHR$OUT:
	CALL	CON$CHR		; Send 1 chr
	DCR	B		; Decrement counter
	JNZ	NEXT$CHR$OUT	; Done?
	POP	H		; Restore all registers
	POP	D		; .
	POP	B		; .
	POP	PSW		; .
	RET			; and return


CONIN$NE$XC:
;
; [E1.28]
;
; Console input, no echo, exit on ctrl-c
;
; Entry:
;	none
;
; Exit:
;	A  = character from console, except for ctrl-c, which causes
;	     an immediate jump to ALL$DONE
;
; Modifies:
;	A
;
	CALL	LB$CONIN
	CPI	CTRLC
	JZ	ALL$DONE
	RET


CON$MSG:
;
; [DC.20]
;
; Console message 
;
; Entry:
;	DE = pointer to message string, terminated with '$'
;
; Exit:
;	message printed on console
;
; Modifies: A, BC
;
	PUSH	PSW		; Save registers
	PUSH	B		; .
	PUSH	D		; .
	PUSH	H		; .
	MVI	C,9		; BDOS print string command
	CALL	BDOS		; .
	POP	H		; Restore registers
	POP	D		; .
	POP	B		; .
	POP	PSW		; .
	RET			; and return


DO$CRLF:
;
; [DC.27]
;
; This routine sends a carriage return and a line feed to the terminal,
; and then waits 'SLOW$TERM' ms for a slow terminal to catch up.
;
; Entry:
;	none
;
; Exit:
;	CR + LF is sent to the screen.
;
; Modifies:
;	none
;
	PUSH	PSW		; Save AF
	MVI	A,0Dh		; Send carriage return
	CALL	CON$CHR		; .
	MVI	A,0Ah		; and line feed
	CALL	CON$CHR		; .
	LDA	SLOW$TERM	; Check slow flag
	ORA	A		; .
	CNZ	WAIT		; wait for the s-l-o-w terminals
	POP	PSW		; recover original AF
	RET			; and return


FILL$BLOCK:
;
; [E1.29]
;
; Fills the buffer pointed to by DE with the character in C for
; a length of B bytes (max 256).
;
; Entry:
;	B  = Length to fill
;	C  = Character to fill with
;	DE = Start of buffer
;
; Exit:
;	Buffer filled
;
; Modifies:
;	Only buffer area affected
;
	PUSH	B		; Save registers
	PUSH	D		; .
	XCHG			; Set up M register use
F$NEXT:
	MOV	M,C		; Stuff a char
	INX	H		; Bump pointer
	DCR	B		; Bounce counter
	JNZ	F$NEXT		; Do it again if not done
	XCHG			; We are done.  restore old HL
	POP	D		; Restore other registers
	POP	B		; .
	RET			; and return


GET$BIOS$VERS:
;
; [DC.20]
;
; Get bios version -- Copies the current BIOS jump tables (starting
; at warm boot) to a local area for ease of utility access.  If the
; BIOS is version 2.0 or greater, the secondary jump table is copied
; also.
;
; Entry:
;	none
;
; Exit:
;	Z  = bios 1.0 - 1.4 (floppy only bios)
;	NZ = bios 2.0 or greater (floppy & fixed disk bios)
;
; Modifies: All registers
;
	LHLD	1		; Get start of bios jump table
	LXI	D,LB$BIOS$TBL	; Move bios to local storage
	MVI	B,LB$LEN	; .  (length of bios area)
	CALL	MOVE$BLOCK	; .  (move routine)
	MVI	A,0		; Test CP/M version
	CALL	LB$GETNXT	; Get next jump table
	STA	BIOS$VERSION	; Save bios version
	INX	H		; See if HL is 0FFFFh
	MOV	A,H		; .
	ORA	L		; .
	RZ			; If so, then old version
	DCX	H		; Fix HL as it has the table addr
	LXI	D,LB$XTBL	; Move extra table to local storage
	MVI	B,LB$XLEN	; .  (length of extra table)
	CALL	MOVE$BLOCK	; .  (move routine)
	MVI	A,0FFH		; Set NZ to indicate bios
	ORA	A		; ... version 2.1+
	RET			; ... and return.


GET$HL$PTR:
;
; [DC.20]
;
; Gets the pointer pointed to by HL and puts it in HL
;
; Entry:
;	HL = pointer to put in HL
;
; Exit:
;	HL = pointer
;
; Modifies: none
;
	PUSH	PSW		; Save A register
	MOV	A,M		; Get low byte of pointer
	INX	H		; .
	MOV	H,M		; Get high byte of pointer
	MOV	L,A		; Pointer is now together
	POP	PSW		; Restore A register
	RET			; and return


GET$STRLEN
; Searches the string pointed to by HL and returns the string length
; to the next carriage return.  The length is returned in B.
	PUSH	D		; Save start of string
	MVI	B,0		; Clear counter
TRY$NEXT$CHR:
	LDAX	D		; Get character
	CPI	CR		; Is it CR?
	JZ	EOS$FOUND	; .
	CPI	'$'		; Is it '$'?
	JZ	EOS$FOUND	; .
	INR	B		; No -- increment count and
	INX	D		; .  point to the next character
	JMP	TRY$NEXT$CHR	; .
EOS$FOUND:
	POP	H		; CR or '$' found, recall orig ptr
	XCHG			; DE=orginial, HL=current
	RET			; and return


GO$TABLE:
;
; [E2.05]
;
; Jump to a routine based on a table of pointers
;
; Entry:
;	A  = index into table
;	HL = table base address
;
; Exit:
;	Routine at (A*2)+HL is executed
;
; Modifies:
;	B, HL
;
	LXI	B,2		; Compute offset to table of routines
	CALL	INDEX$TABLE	; .
	CALL	GET$HL$PTR	; .
	PCHL			; Jump to proper routine


INDEX$TABLE:
;
; [E1.30]
;
; Computes offset to table given base address, entry length, and entry
; requested.
;
; Entry:
;	A  = entry #
;	BC = table entry length
;	HL = base address
;
; Exit:
;	HL = address to entry
;
; Modifies:
;	A, BC, HL
;
	ORA	A		; Set up flags for first check
I$TBL$ADD:
	RZ			; If A=0, we're done
	DAD	B		; Otherwise add length to base,
	DCR	A		; .  decrement counter,
	JMP	I$TBL$ADD	; .  and check again.


IS$IT$OK:
;
; [E1.28]
;
; Check the character in A against the list of "OK" chrs pointed
; to by HL
;
; Entry:
;	A  = character to check
;	HL = pointer to list of "OK" characters
;
; Exit:
;	A  = original character if ok, 0ffh if not in list
;	B  = position of character in list
;
; Modifies:
;	BC
;
	PUSH	H		; Save original "OK" pointer
	MOV	C,A		; Save chr to check against
	MVI	B,0		; Clear counter
	CPI	ESC		; If chr is <ESC>
	JZ	I$CHR$OK	; .  then automatically ok
	INR	B		; .  otherwise start counting at 1
I$CHK$NEXT:
	MOV	A,M		; Get chr to check against
	ORA	A		; End of table?
	JNZ	I$NOT$EOT	; No, check chr
	DCR	A		; Decrement to get 0ffh
	MOV	B,A		; Stuff for later move
	JMP	I$CHR$OK	; And exit
I$NOT$EOT:
	CMP	C		; Chrs match?
	JZ	I$CHR$OK	; . Yes, return
	INX	H		; . No, bump pointer
	INR	B		; . . and bump counter
	JMP	I$CHK$NEXT	; . . and check next chr
I$CHR$OK:
	MOV	A,B		; Set status based on
	ORA	A		; .  position counter
	MOV	A,C		; Get user chr back
I$DONE:
	POP	H		; and original "OK" pointer
	RET			; and return


JUSTIFY:
;
; [E6.11]
;
; This routine will send a data stream to the console, with each line
; justified based on the SCREEN$WIDTH value.  The stream must terminate
; with the CP/M end of string character ($) and may contain imbedded
; CR,LF pairs to separate paragraphs.
;
; NOTE:  To insure proper operation, the LF character should only follow
; a CR character or another LF character, as the CR character is used to
; flush the current line without justification.
;
; Two entry points are provided:
;	JUSTIFY		Justify output, flush right
;	JUSTIFY$RAGGED	Justify output, ragged right
;
; As of E6.11, the flush right routine was not installed, so either
; entry point will provide the same results.
;
; Entry:
;	DE = pointer to line(s) to output
;
; Exit:
;	The data is sent to the screen.
;
; Modifies: All registers
;
	MVI	A,80H		; Set flush right mode
	DB	JR,E$JUSTIFY-$-1; Jump to routine entry
JUSTIFY$RAGGED:
	MVI	A,00H		; Set ragged right mode
E$JUSTIFY:
	STA	J$MODE		; Save mode byte
	MVI	A,'$'		; Mark start of buffer
	STA	OUTBUF-1	; .
	XCHG			; DE is usually print source
J$NEXT$LINE:
	XRA	A		; Clear character counter
	STA	BLANK$LEN	; .
	MOV	B,A		; .
	LXI	D,OUTBUF	; Set up buffer pointer
J$CHECK$CHR:
	MOV	A,M		; Get character
	CPI	CR		; CR?
	DB	JRNZ,J$NO$R-$-1	; .
	CALL	J$FLUSH$LINE	; .  Flush output line,
	MVI	A,CR		; .  Output CR,
	CALL	CON$CHR		; .  .
	INX	H		; .  bump ptr & check next
	DB	JR,J$NEXT$LINE-$-1 and 255
J$NO$R:
	CPI	LF		; LF?
	DB	JRNZ,J$NO$L-$-1	; .
	CALL	CON$CHR		; .  Output LF,
	INX	H		; .  bump ptr & check next
	DB	JR,J$CHECK$CHR-$-1 and 255
J$NO$L:
	CPI	FF		; FF?
	DB	JRNZ,J$NO$F-$-1	; .
	CALL	CLEAR$SCREEN	; .  Clear screen,
	INX	H		; .  bump ptr & check next
	DB	JR,J$CHECK$CHR-$-1 and 255
J$NO$F:
	CPI	'$'		; End of string?
	DB	JRNZ,J$NO$S-$-1	; .
	CALL	J$FLUSH$LINE	; .  Flush output line, and
	RET			; .  return to caller.
J$NO$S:
	STAX	D		; Not a special chr, save in buffer

	CPI	' '		; Blank?
	DB	JRNZ,J$NO$B-$-1	; .  No, don't save position
	SHLD	BLANK$POS	; Save position for later
	XCHG			; and save corresponding position
	SHLD	OUTBUF$BLANK	; .  of the blank we just saved
	XCHG			; .  in the output buffer
	MOV	A,B		; .
	STA	BLANK$LEN	; Save current length also
J$NO$B:
	INR	B		; Increment counter
	INX	H		; .  and input pointer
	INX	D		; .  and output pointer
	LDA	SCREEN$WIDTH	; Compare counter against screen width
	SUB	B		; .
	JP	J$CHECK$CHR	; And continue checking if not past end
;
; Screen width exceeded, send this line to the screen.
;
	LHLD	OUTBUF$BLANK	; Get pos of last blank in output buf
	MVI	M,'$'		; and plug with eos ('$')
	LDA	J$MODE		; Justify right edge only if the
	ORA	A		; .  right-justify flag is non-zero
	CM	J$ADD$BLANKS	; .
	CALL	J$SEND$BUFFER	; Output the line to the screen
	CALL	DO$CRLF		; and a CR / LF
	LHLD	BLANK$POS	; Get pointer to where we left off
J$SKIP$BLANKS:
	INX	H		; Bump pointer past blank(s)
	MOV	A,M		; .
	CPI	' '		; .
	DB	JRZ,J$SKIP$BLANKS-$-1 and 255
	JMP	J$NEXT$LINE	; and check next segment

J$FLUSH$LINE:			; Flush line when CR or EOS encountered
	MVI	A,'$'		; Plug current position with EOS ($)
	STAX	D		; .
	CALL	J$SEND$BUFFER	; Send this line of data
	RET			; and return

J$ADD$BLANKS:
	RET			; At a later time, this routine will
				; justify the right margin by inserting
				; extra blanks in the output line.

J$SEND$BUFFER:
	MOV	A,B		; If line to output is of zero length,
	ORA	A		; .  then don't output the line.
	RZ			; .
	LXI	D,OUTBUF	; Get address of output buffer
	CALL	CON$MSG		; and call our print message routine
	RET			; return

J$MODE		DB	0	; Current right justify mode
BLANK$POS	DW	0	; Last blank on this line
OUTBUF$BLANK	DW	0	; Last blank in the output buffer
BLANK$LEN	DB	0	; Length of line to the blank

; NOTE: OUTBUF is defined to be after the stack and before the heap.

; end of justify$output data area


MOVE$BLOCK:
;
; [DC.20]
;
; Move a block of memory (up to 256 bytes)
;
; Entry:
;	HL = source
; 	DE = destination
; 	B  = count
; Exit:
;	data moved
;
; Modifies: 
;	none
;
	PUSH	PSW		; Save registers
	PUSH	B		; .
	PUSH	D		; .
	PUSH	H		; .
M$NEXT$CHR:
	MOV	A,M		; Get next byte,
	STAX	D		; Save in destination.
	INX	H		; Increment pointer to source.
	INX	D		; Increment pointer to dest.
	DCR	B		; Decrement counter.
	JNZ	M$NEXT$CHR	; Loop back if we're not done.
	POP	H		; Restore registers
	POP	D		; .
	POP	B		; .
	POP	PSW		; .
	RET			; No more to do -- return.


PROMPT:
;
; [E1.30]
;
; Prompt the user or the command line for input.
;
; Two entry points are provided:
;	PROMPT		standard entry, CRLF after chr from user
;	PROMPT$NOLF	special entry, No CRLF after chr from user
;
; Entry:
;	DE = pointer to prompt string 
;	HL = pointer to list of valid chars (terminated with 00H)
;
; Exit:
;	A  = char from the user
;	B  = position of this character (0, 1, 2, ... n)
;
;	Z  = char was the escape key
;	NZ = char was not the escape key
;
; Modifies:
;	A, BC
;
	MVI	A,01H		; Set CRLF after chr
	JMP	E$PROMPT	; Jump to entry point
PROMPT$NOLF:
	MVI	A,00H		; Set no CRLF after chr
E$PROMPT:
	STA	PROMPT$MODE	; Save prompt mode flag
	LDA	CMD$LINE$CHRS	; Are there any characters left from
	ORA	A		; .  the command line?
	JZ	P$DISP		; No -- display & get chr from bios
	PUSH	H		; Yes, save oklist pointer
	LHLD	CMD$LINE$PTR	; and get chr from command line
	DCR	A		; .  Reduce count of chrs by one
	STA	CMD$LINE$CHRS	; .  .
	MOV	A,M		; .  Get command line character
	INX	H		; .  Increment pointer
	SHLD	CMD$LINE$PTR	; .  .
	POP	H		; .  Restore oklist pointer
	CPI	','		; Change ',' to CR
	JNZ	P$NOT$COMMA	; .
	MVI	A,CR		; .
P$NOT$COMMA:
	CPI	'.'		; Change '.' to ESC
	JNZ	P$NOT$DOT	; .
	MVI	A,ESC		; .
P$NOT$DOT:
	CPI	'_'		; Underline means prompt & get chr
	JZ	P$DISP		; . from bios anyway (user input)
	CALL	TO$UPPER	; Convert the chr to upper case
	CALL	IS$IT$OK	; Check the chr against the ok list
	RP			; If ok, return
	XRA	A		; Otherwise, cancel the cmd line
	STA	CMD$LINE$CHRS	; .  buffer and fall through to p$disp
P$DISP:
	PUSH	H		; Save pointer to ok-chrs
	CALL	JUSTIFY		; and call justify routine
	POP	H		; .
P$TRY$AGAIN:
	CALL	CONIN$NE$XC	; Console input, no echo, except ^C
	CALL	TO$UPPER	; Convert the chr to upper case
	CALL	IS$IT$OK	; If the character is not "OK"
	JM	P$TRY$AGAIN	; . get another
	CNZ	CON$CHR		; . otherwise display it
	PUSH	PSW		; Check mode flag in case we need
	LDA	PROMPT$MODE	; .  to send a CR+LF after the
	ANI	01H		; .  user's input
	CNZ	DO$CRLF		; .
	POP	PSW		; .
	RET			; and return

PROMPT$MODE:	DB	0	; Prompt mode flag


PROMPT$DECIMAL:
;
; [E6.11]
;
; Prompt the user for a decimal input of up to 5 digits.  The Z flag
; indicates the termination character: Z = ESC key, results may not
; be valid; NZ = RETURN key, results valid.
;
; Entry:
;	DE = pointer to prompt string
;
; Exit:
;	DE = Value entered by the user, 0-0FFFFH
;	A  = Low byte of value entered by the user, 0-0FFH
;
;	Z  = ESC key pressed, ignore results
;	NZ = RETURN key pressed, results valid
;
; Modifies:
;	A, PSW
;
	MVI	A,01H		; Set CRLF after entry
	DB	JR,E$PR$DEC-$-1	; Jump to entry point
PROMPT$DEC$NOLF:
	MVI	A,00H		; Set no CRLF after entry
E$PR$DEC:
	STA	PROMPT$D$MODE	; Save prompt mode flag
	PUSH	H		; Save original HL reigster
	PUSH	B		; .  and the BC register, too.
	XRA	A		; Clear the current digit pointer
	MOV	C,A		; Setup count in C register
	LXI	H,SCRATCH	; Initialize the scratch pointer
NEXT$DIGIT:
	PUSH	B		; Save digit counter
	PUSH	H		; .  and string pointer
	LXI	H,DEC$INPUT$OKC	; Get current 'ok' chrs
	CALL	PROMPT$NOLF	; Prompt for digit
	POP	H		; Get string pointer back
	POP	B		; .  along with digit counter
	DB	JRZ,ESCRTN-$-1	; Return if ESC key hit
	CPI	CR		; Check for return
	JZ	CONVERT$STRING	; Convert string to decimal, if so
	CPI	BS		; Check for backspace
	JZ	BACKUP$DIGIT	; Back up 1 digit, if we can

	MOV	B,A		; Save character for a moment
	MOV	A,C		; Check digit count
	CPI	5		; .
	JC	ADD$DIGIT	; 5 digits or less, add to string
	LXI	D,BLOT		; More than 5 digits, blot this one
	CALL	CON$MSG		; .
	JMP	DE$FOR$NEXT	; .
ADD$DIGIT:
	INR	C		; Otherwise bump digit count
	MOV	M,B		; Save digit
	INX	H		; Bump digit pointer
	JMP	DE$FOR$NEXT	; Setup DE for next prompt

BACKUP$DIGIT:
	MVI	A,' '		; Bump forward to clear digit
	CALL	CON$CHR		; .
	XRA	A		; Are we at the beginning?
	CMP	C		; .
	JZ	DE$FOR$NEXT	; Yes, don't backup
	MVI	A,BS		; Backup to correct position
	CALL	CON$CHR		; .
	DCX	H		; Backup pointer 1 chr
	DCR	C		; Push count 1 back, also
DE$FOR$NEXT:
	LXI	D,DEC$INPUT$MSG	; Setup for next prompt
	JMP	NEXT$DIGIT	; .

CONVERT$STRING:
	MVI	M,0		; Mark end of string
	LXI	H,SCRATCH	; Get beginning of string
	CALL	STR$TO$DE	; Convert string to DE register
	MVI	A,0FFH		; Insure NZ flag
	ORA	A		; .
	MOV	A,E		; Move low byte to A
	PUSH	PSW		; Check mode flag to see if a
	LDA	PROMPT$D$MODE	; .  CR+LF should be sent after
	ORA	A		; .  the user's input
	CNZ	DO$CRLF		; .
	POP	PSW		; .
ESCRTN:
	POP	B		; Get old BC register back
	POP	H		; .  and original HL, as well.
	RET			; and return with result in HL & A

DEC$INPUT$MSG:	DB	'$'		; Decimal input message
DEC$INPUT$OKC:	DB	'0123456789'	; Decimal input 'ok' chrs
		DB	BS,CR,0		; .
BLOT:		DB	BS,' ',BS,'$'	; Blot out digit
SCRATCH:	DB	'     ',0	; Max 5 digits
PROMPT$D$MODE:	DB	0		; CRLF flag


RET$TO$CONT:
;
; [E2.19]
;
; Prompts and waits for the RETURN key to be pressed.
;
; Entry:
;	none
;
; Exit:
;	Display message and wait for a RETURN key.
;
; Modifies:
;	all
;
	LXI	D,RTC$MSG	; Press RETURN to continue ...
	CALL	CENTER$OUTPUT	; .
	LXI	D,NO$MSG	; .
	LXI	H,RTC$OKC	; .
	CALL	PROMPT		; .	[ CR ]
	RET

RTC$MSG:	DB	'Press the RETURN key to continue ... '
NO$MSG:		DB	'$'
RTC$OKC:	DB	CR,0

; end of RET$TO$CONT routine


SCSI$TO$BIN:
;
; [DC.27]
;
; Converts SCSI address to binary 0-7
;
; Entry:
;	A  = SCSI address to convert
;
; Exit:
;	A  = converted address (0-7)    [0FFH = error]
;
; Modifies: B
;
	MVI	B,0FFH		; Set up B register for possible error
	ORA	A		; If A=0, error
	JZ	SCSI$CONVERTED	; .
SCSI$NEXT$BIT:
	INR	B		; Increment count
	RRC			; Shift address right 1 bit
	JNC	SCSI$NEXT$BIT	; Bit shifted to cary means we're done
SCSI$CONVERTED:
	MOV	A,B		; Move converted addr to A
	RET


STR$COMP:
;
; [E1.18]
;
; Compare two strings
;
; Entry:
;	HL = source
; 	DE = destination
; 	B  = count
; Exit:
;	Z  = two strings equal
;	M  = source <  dest
;	P  = source >= dest
;
; Modifies: all
;
	PUSH	D
	PUSH	H
NEXT$COMPARE:
	MOV	C,M
	INX	H
	LDAX	D
	INX	D
	CMP	C
	JNZ	NOT$EQUAL
	DCR	B
	JNZ	NEXT$COMPARE
NOT$EQUAL:
	POP	H
	POP	D
	RET


STR$SWAP:
; Swap two strings
;
; [E1.18]
;
; Entry:
;	HL = source
; 	DE = destination
; 	B  = count
; Exit:
;	data moved
;
; Modifies: all
;
	MOV	C,M
	LDAX	D
	MOV	M,A
	MOV	A,C
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	STR$SWAP
	RET


STR$TO$DE:
;
; [E5.26]
;
; Converts the string pointed to by HL to a number in the DE reg.
; The conversion will continue until the first non-numeric chr
; found.
;
; NOTE: a test for register overflow is not made.  If HL points
; to a string whose numerical value is greater than 65535 (64K),
; inaccurate results will occur.
;
; Entry:
;	HL = ptr to string
;
; Exit:
;	DE = value of string in HL
;	HL = next character to process
;
; Modifies:
;	DE, HL
;
;
	PUSH	PSW		; We need the A register
	PUSH	B		; .  and the BC register
	LXI	D,0		; Clear totals
STN$NEXT$CHR:
	PUSH	D		; Save results so far . . .
	POP	B		; .
	MOV	A,M		; Get chr
	CPI	'0'		; Less than '0'?
	DB	JRC,NO$DIG-$-1	; Yes, finished
	CPI	'9'+1		; Greater than '9'
	DB	JRNC,NO$DIG-$-1	; Yes, finished
	SUI	'0'		; No,  convert to 0-9
	MOV	E,A		; Save this digit
	MVI	D,0		; .
	PUSH	H		; Save ptr to input string
	LXI	H,0		; Multiply previous by 10
	DAD	B		; x1
	DAD	H		; x2
	DAD	H		; x4
	DAD	B		; x5
	DAD	H		; x10
	DAD	D		; Add in new digit
	XCHG			; Save results back to DE
	POP	H		; Get input string ptr back
	INX	H		; Bump to next chr
	JMP	STN$NEXT$CHR	; Go back for another digit
NO$DIG:
	POP	B		; Get old BC reg back
	POP	PSW		; .  and old A reg as well.
	RET			; Return with results in DE.


TO$UPPER:
;
; [E1.08]
;
; Convert the character in A to upper case.
;
; Entry:
;	A  = character to convert
;
; Exit:
;	A  = upper case character (if alpha)
;
; Modifies:
;	A
;
	CPI	'z'+1		; Convert to upper case
	JP	UPPER$ALREADY	; .
	CPI	'a'		; .
	JM	UPPER$ALREADY	; .
	ANI	5FH		; .
UPPER$ALREADY:
	RET			; and return


WAIT:
; Wait A ms
;
; Entry:
;	milliseconds in A
;
; Exit:
;	time waited
;
; Modifies: A
;
	PUSH	PSW
	MVI	A,221
WAIT$2:	DCR	A
	JNZ	WAIT$2
	POP	PSW
	DCR	A
	JNZ	WAIT
	RET


* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*							*
*		Data area . . .				*
*							*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *

; Replicated BIOS to make direct calls easier . . .

LB$BIOS$TBL:
LB$WBOOT	DS	3	; Warm boot
LB$CONST	DS	3	; Console status
LB$CONIN	DS	3	; Console input
LB$CONOUT	DS	3	; Console output
LB$LISTOUT	DS	3	; List output
LB$PUNCH	DS	3	; Punch output
LB$READER	DS	3	; Reader input
LB$HOMDSK	DS	3	; Home disk (move to track 00)
LB$SELDSK	DS	3	; Select disk drive
LB$SETTRK	DS	3	; Select track number
LB$SETSEC	DS	3	; Select sector number
LB$SETDMA	DS	3	; Set DMA address
LB$DSKREAD	DS	3	; Disk read
LB$DSKWRITE	DS	3	; Disk write
LB$LISTST	DS	3	; List status
LB$SECTRN	DS	3	; Sector translate routine
; AMPRO-specific BIOS calls
LB$GETNXT	DS	3	; Get bios ver & next tbl address
LB$GETEDSK	DS	3	; Get pointer to E-disk storage
LB$IOINIT	DS	3	; Set new I/O parameters
LB$SCSIDRV	DS	3	; SCSI direct driver
LB$LEN	EQU	$-LB$WBOOT	; Length of bios table

LB$XTBL:			; 'Extra' table definitions ...
LB$SWAP$DRV	DS	3	; Swap two logical drives
LB$GET$WDP	DS	3	; Set/get win drive parameters
LB$PHYTAB	DS	3	; Set/get phytab access
LB$GET$LDTE	DS	3	; Get logical device table entry
LB$RESERVED	DS	3	; Reserved entry
LB$XLEN	EQU	$-LB$XTBL	; Length of extra table

BIOS$VERSION	DB	0	; Bios version number


YOU$ARE$ABT	DB	'You are about to format the following '
		DB	'drive with an interleave of ('
INTL$ECHO	DB	'1):',CR,LF,LF,'$'

RTF$MSG		DB	CR,LF
		DB	'Press <RETURN> to format, '
		DB	'<ESC> to start over, '
		DB	'<CTRL-C> to quit: ','$'
RTF$OKC		DB	CR,0

FMT$START$MSG	DB	CR,LF,LF,'Formatting . . . ','$'

CLR$DIR$MSG:	DB	CR,'Setting drive data to 0E5H . . . ','$'

FMT$OK$MSG	DB	CR,'Drive formatted -- no errors.  '
		DB	'Be sure to use FINDBAD on each of the CP/M '
		DB	'letters listed above to find any bad sectors '
		DB	'in the drive you just formatted.',CR,LF
		DB	'Example: FINDBAD F:; FINDBAD G:',CR,LF,LF,'$'

FMT$ERR$MSG	DB	'Drive NOT FORMATTED due to error(s).'
		DB	CR,LF,LF,'$'

NO$RESPONSE	DB	CR,'FORMAT ERROR: your controller '
		DB	'is not responding.  Possible causes:',CR,LF,LF
		DB	TAB,'Controller set to wrong SCSI address',CR,LF
		DB	TAB,'Controller malfunctioning or not connected',CR,LF
		DB	TAB,'SCSI host adapter malfunctioning',CR,LF
		DB	TAB,'Cables (SCSI to controller or controller '
		DB	'to drive) are bad',CR,LF,LF,'$'

FORMAT$ERROR	DB	CR,'FORMAT ERROR: Error code '
ERR$CODE	DB	'xx (hex), SCSI command '
ERR$CMD		DB	'xx (hex).',CR,LF
		DB	'SCSI address bit code '
S$ADDR$CODE	DB	'xx (hex).',CR,LF
		DB	'(See your hard disk controller '
		DB	'manual for details).',CR,LF,LF,'$'

AGAIN$MSG	DB	CR,LF,'Do another (Y/N)? ','$'
AGAIN$OKC	DB	'NY',CR,0

NEW$CTRL	DB	CR,LF,'Cannot format -- you have an '
		DB	'unknown or unsupported hard disk '
		DB	'controller.',CR,LF,LF,'$'


OLDSP:	DS	2		; old stack pointer
STACK:	EQU	OLDSP+64	; 31-level stack

INBUF:	EQU	STACK+1		; Command line input buffer
INBUFL:	EQU	0		; Input buffer length

OBPLUG:	EQU	INBUF+INBUFL	; Start of outbuf ('$')
OUTBUF:	EQU	OBPLUG+1	; Output buffer
OBUFL:	EQU	PWIDTH		; Output buffer length

HEAP:	EQU	OUTBUF+OBUFL	; Next available area for data

E5BUF:	EQU	HEAP

	END

