
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; *								*
; *  Ampro Computers, Inc.			Bios version 3  *
; *								*
; *	Copyright (C) 1983,1984,1985 Ampro Computers, Inc.	*
; *			All rights reserved.			*
; *								*
; *  This bios is designed for use with Ampro hardware only.	*
; *  All other use is prohibited without prior written consent	*
; *  from Ampro Computers, Inc.					*
; *								*
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
; There have been many changes in the version 3 bios from the version
; 1 and version 2 bios.  The major enhancements are listed below:
;
; Floppy-related enhancements:
;
;   o	Automatic 48tpi.com support in a 96tpi (includes R/W for
;	Ampro formats, R/W for E-disk formats if bit 5 of BIOS+074H
;	is set, boot of DS 48tpi Ampro format in a 96tpi drive).
;	WARNING: writing to a 48tpi disk in a 96tpi drive is NOT
;	RECOMMENDED and may cause read errors later.
;
;   o	If there is not floppy diskette and/or drive present on the
;	first select after a warm or cold boot, the system will wait,
;	beeping every 5 seconds until the problem is resolved (turn
;	on the drive, put a disk in, etc.) or until a Ctrl-C is hit
;	at the keyboard (which will abort the routine with a bdos
;	error).
;
;   o	A step rate per physical drive is now supported, and 2ms and
;	3ms step rates are supported with the 1772 FDC.
;
;   o	8" data rates implemented on a per logical drive basis.
;	NOTE: Many 1770/1772 FDC chips cannot support this rate.
;
;   o	Max drive letter eliminated (left as an assembly option).
;
;   o	1 second software-timed motor on delay.
;
;
; Hard-disk related enhancements:
;
;   o	Hard disk support is SCSI generic and installed by HSYSINIT
;	as a run-time option.
;
;   o	Bus (SCSI) arbitration added as assembly-time option.  Default
;	is no arbitration.
;
;   o	Each hard disk partition has its own DPB, which allows
;	flexible partitioning of any disk drive, up to 88Mb.
;
;   o	The direct SCSI call has been changed to return better status.
;	DE points to status data on error, rather than original user 
;	data.
;
;   o	A SCSI burst/byte option has been added to allow controllers to 
;	operate at optimum rates.
;
;
; Other enhancements:
;
;   o	Initial Z3 termcap entry set up for the ADM-3A terminal.
;
;	*	*	*	*	*	*	*	*


;	*	*	*	*	*	*	*	*
;
;  Revision history:
;
;  Ver	Date	Who	Description
;  ---	-----	---	------------------------------------------
;  3.3	E8.21	RJB	Changed RESTORE logic to wait longer for
;			the FDC to setup.  Corrected sector number
;			problem with Kaypro 4/10 format.  Added
;			routine to set memory from the end of BIOS
;			thru 0FFFFH to zero in order to support
;			ZCPR3 resident system segment options (RCP
;			FCP, IOP, etc).
;
;  3.2	E7.30	RJB	Removed 2 nested "IF" statements in the 
;			hard disk section which generated extra 
;			(unused) code in the floppy-only version.
;			NO FUNCTIONAL CHANGES.
;
;  3.1	E7.02	RJB	Production release of version 3 bios.
;
;  3.0	E5.21	RJB	Beta release of version 3 bios.
;
;	*	*	*	*	*	*	*	*


; Bios version and date

VERS		EQU	33	; Current version
THIS$MONTH	EQU	8	; Today's month
THIS$DAY	EQU	21	;	  day
THIS$YEAR	EQU	85	;	  year

INT$REV		EQU	99	; Internal revision number


; System memory size

MSIZE		EQU	58
	; 60K = Floppy only (must set ZCPR3 and/or HARD$DISK to NO)
	; 59K = Floppy + 10Mb Hard
	; 58K = Floppy + 42Mb Hard
	; 57K = Floppy + 74Mb Hard
	; 56K = Floppy + 88Mb Hard


; TRUE and FALSE

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


; Operating characteristics

ZCPR3		EQU	YES		; enable ZCPR3?
HARD$DISK	EQU	YES		; enable hard disk routines?
MAX$DRIVE	EQU	NO		; enable max drive checking?
ARBITRATION	EQU	NO		; use arbitrated select?

INTERNAL	EQU	NO		; internal (unreleased) bios?

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

CPIR80		EQU	0B1EDH		; CPIR (0edh,0b1h)
INIR80		EQU	0B2EDH		; INIR (0edh,0b2h)
LDIR80		EQU	0B0EDH		; LDIR (0edh,0b0h)
OTIR80		EQU	0B3EDH		; OTIR (0edh,0b3h)

INI80		EQU	0A2EDH		; INI  (0edh,0a2h)
OUTI80		EQU	0A3EDH		; OUTI (0edh,0a3h)

SBCD80		EQU	043EDH		; SBCD (0edh,043h)
LBCD80		EQU	04BEDH		; LBCD (0edh,04bh)
SDED80		EQU	053EDH		; SDED (0edh,053h)
LDED80		EQU	05BEDH		; LDED (0edh,05bh)
SSPD80		EQU	073EDH		; SSPD (0edh,073h)
LSPD80		EQU	07BEDH		; LSPD (0edh,07bh)
SIXD80		EQU	022DDH		; SIXD (0ddh,022h)
LIXD80		EQU	02ADDH		; LIXD (0ddh,02ah)
SIYD80		EQU	022FDH		; SIYD (0fdh,022h)
LIYD80		EQU	02AFDH		; LIYD (0fdh,02ah)


; 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


; CP/M internals

BIAS		EQU	(MSIZE-20)*1024	; CP/M bias
CCP		EQU	3400H+BIAS	; CCP  starting address
BDOS		EQU	CCP+806H	; BDOS starting address
BIOS		EQU	CCP+1600H	; BIOS starting address
NSECTS		EQU	(BIOS-CCP)/128	; Warm boot sector count


; CP/M externals

CDISK		EQU	0004H		; Current disk: 0=A,...,15=P
IOBYTE		EQU	0003H		; Intel I/O byte


; CP/M to host disk constants

WRALL		EQU	0		; Write to allocated
WRDIR		EQU	1		; Write to directory
WRUAL		EQU	2		; Write to unallocated


; Ampro hardware port equates

CONT		EQU	00H		; Ampro system control port

PIO1		EQU	001H		; Parallel printer
STBSET		EQU	002H		; Set print strobe
STBCLR		EQU	003H		; Clear print strobe

CTCA0		EQU	040H		; Clock/timer channel 0
CTCA1		EQU	050H		; Clock/timer channel 1
CTCA2		EQU	060H		; Clock/timer channel 2
CTCA3		EQU	070H		; Clock/timer channel 3

SIODPA		EQU	080H		; Serial port A - data
SIOCPA		EQU	084H		; Serial port A - control
SIODPB		EQU	088H		; Serial port B - data
SIOCPB		EQU	08CH		; Serial port B - control

CMND		EQU	0C0H		; Disk controller command
WTRK		EQU	CMND+1		; Disk controller write track
WSEC		EQU	CMND+2		; Disk controller write sector
WDAT		EQU	CMND+3		; Disk controller write data

STAT		EQU	0C4H		; Disk controller status
RTRK		EQU	STAT+1		; Disk controller read track
RSEC		EQU	STAT+2		; Disk controller read sector
RDAT		EQU	STAT+3		; Disk controller read data


; DART masks

RDA		EQU	01H		; DART recieve data available
TBE		EQU	04H		; DART transmit buffer empty
DCD		EQU	08H		; DART data carrier detect
CTS		EQU	20H		; DART clear to send


; FDC masks, commands, and constants

FRESTOR 	EQU	008H		; FDC restore
FSEEKNV		EQU	018H		; FDC seek, no verify
FSEEK		EQU	01CH		; FDC seek, with verify
FREADS		EQU	088H		; FDC read sector
FWRITES 	EQU	0A8H		; FDC write sector
FRDADDR 	EQU	0C8H		; FDC read address

FNS		EQU	008H		; 0=spin-up,   1=no spin-up
FVF		EQU	004H		; 0=no verify, 1=verify
FNP		EQU	002H		; 0=precomp,   1=no precomp

FRETRY		EQU	3		; Number of floppy retries
HLDELAY		EQU	35		; Head load delay (ms)
DSBIAS		EQU	16		; Double sided sector bias

; Disk type byte definitions:

	;	   Bit: 76543210
	; Density	x	   0=single     1=double
	; Sides		 x	   0=single     1=double
	; Sector #'s	  x	   0=same       1=continuous
	; Track count	   x	   0=down       1=down front, up back
	; Alloc unit	    xx	   00=1K   01=2K   10=4K   11=8K
	; Sector size	      xx   00=128  01=256  10=512  11=1024
	;	   Bit: 76543210

SSDD48		EQU	10000110B	; DD,SS,same,down,2K,512
DSDD48		EQU	11000110B	; DD,DS,same,down,2K,512
SSDD96		EQU	10000111B	; DD,SS,same,down,2K,1024
DSDD96		EQU	11000111B	; DD,DS,same,down,2K,1024


;  NCR controller equates

NCRBASE	EQU	20H		; Base address of NCR 5380
NCRCSD	EQU	NCRBASE+0	; (R)  Current SCSI data register
NCRODR	EQU	NCRBASE+0	; (W)  Output data register
NCRICR	EQU	NCRBASE+1	; (RW) Initiator command register
NCRMR	EQU	NCRBASE+2	; (RW) Mode register
NCRTCR	EQU	NCRBASE+3	; (RW) Target command register
NCRCSBS	EQU	NCRBASE+4	; (R)  Current SCSI bus status
NCRSER	EQU	NCRBASE+4	; (W)  Select enable register
NCRBSR	EQU	NCRBASE+5	; (R)  Bus & status register
NCRSDS	EQU	NCRBASE+5	; (W)  Start DMA send
NCRIDR	EQU	NCRBASE+6	; (R)  Input data register
NCRSDTR	EQU	NCRBASE+6	; (W)  Start DMA target receive
NCRRPI	EQU	NCRBASE+7	; (R)  Reset parity/interrupt
NCRSDIR	EQU	NCRBASE+7	; (W)  Start DMA initiator receive
NCRDACK	EQU	NCRBASE+8	; (RW) DACK pseudo-DMA register

BSYBIT		EQU	08H
ERROR		EQU	02H

; Current SCSI bus status (NCRCSBS)

NCRRST		EQU	10000000B	; Reset
NCRBSY		EQU	01000000B	; Busy
NCRREQ		EQU	00100000B	; Request
NCRMSG		EQU	00010000B	; Message
NCRCD		EQU	00001000B	; Control/Data
NCRIO		EQU	00000100B	; Input/Output
NCRSEL		EQU	00000010B	; Select
NCRDBP		EQU	00000001B	; Data bus parity


; Character equates

CTRLC		EQU	'C'-'@'		; Ctrl-C (Break, abort)
BELL		EQU	'G'-'@'		; Ctrl-G (Bell)
BSP		EQU	'H'-'@'		; Ctrl-H (Backspace)
TAB		EQU	'I'-'@'		; Ctrl-I (Tab)
LF		EQU	'J'-'@'		; Ctrl-J (Line feed)
CR		EQU	'M'-'@'		; Ctrl-M (Carriage return)
NAK		EQU	'U'-'@'		; Ctrl-U
CAN		EQU	'X'-'@'		; Ctrl-X (Cancel)
ESC		EQU	1BH		; Ctrl-[ (Escape)
DEL		EQU	7FH		; 	 (Delete)


; Other equates

DELSEND EQU	28
DDLSPT	EQU	40


; ZCPR3 equates

		IF	ZCPR3
Z3REV		EQU	30		; ZCPR 3.0

Z3ENV		EQU	0FE00H		; Z3 environment descriptor
Z3ENVS		EQU	2		; Size in 128-byte blocks

RCP		EQU	00000H		; Resident command package
RCPS		EQU	0		; Size in 128-byte blocks

IOP		EQU	00000H		; Redirectable I/O package
IOPS		EQU	0		; Size in 128-byte blocks

FCP		EQU	00000H		; Flow command package
FCPS		EQU	0		; Size in 128-byte blocks

Z3NDIR		EQU	00000H		; Named directory area
Z3NDIRS		EQU	0		; Size in 18-byte blocks

Z3CL		EQU	0FF00H		; Z3 command line buffer
Z3CLS		EQU	200		; Size in bytes

SHSTK		EQU	0FD00H		; Shell stack
SHSTKS		EQU	4		; Number of shell elements
SHSIZE		EQU	32		; Size of a shell entry

EXTSTK		EQU	0FFD0H		; Z3 external stack

EXPATH		EQU	40H		; Location of exernal path
EXPATHS		EQU	5		; 5 2-byte path elements

Z3WHL		EQU	4BH		; Wheel byte location

Z3MSG		EQU	0FD80H		; Z3 message buffers

EXTFCB		EQU	0FDD0H		; Z3 external FCB
		ENDIF


;	*	*	*	*	*	*	*	*
;
;		C A V E A T    E M P T O R
;
;  WARNING: The address offsets from BIOS thru BIOS+017FH must remain
;  the same.  Any changes in these offsets will cause incompatibility
;  with the AMPRO system utilities.
;
;		  YOU HAVE BEEN WARNED ...
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS		; Bios starting location

	JMP	BOOT		; Cold start
WBOOTE: JMP	WBOOT		; Warm start
	JMP	CONST		; Console status
	JMP	CONIN		; Console character in
	JMP	CONOUT		; Console character out
	JMP	LIST		; List character out
	JMP	PUNCH		; Punch character out
	JMP	READER		; Reader character in
	JMP	HOME		; Seek to home position
	JMP	SELDSK		; Select disk
	JMP	SETTRK		; Set track number
	JMP	SETSEC		; Set sector number
	JMP	SETDMA		; Set DMA address
	JMP	READ		; Read disk
	JMP	WRITE		; Write disk
	JMP	LISTST		; Return list status
	JMP	SECTRAN 	; Sector translate

; Ampro specific bios calls

	JMP	GETTBL		; Point to more jumps
	JMP	GETEDSK 	; Get ptr to E-disk table
	JMP	IOINIT		; Set new I/O parameters
	IF	HARD$DISK
	JMP	SCSI		; SCSI direct driver
	ENDIF
	IF	NOT HARD$DISK
	MVI	A,0FFH		; Dummy SCSI direct driver
	RET
	ENDIF

;	*	*	*	*	*	*	*	*
;
;  Current system disk
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+03FH
SYSDSK  DB 0			; Assume A: for now

 
; BIOS+040H thru BIOS+06EH modified by CONFIG


;	*	*	*	*	*	*	*	*
;
;  Initialization parameters for the CTC and DART/SIO
;
;  These values are set by the CONFIG program, and specify the
;  CTC and DART/SIO initialization parameters.  Each CTC channel
;  requires two bytes of initialization, while each DART/SIO 
;  channel may have up to 10 bytes of initialization information.
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+040H
CTCVAL:
	DB	47H,13,47H,208,3,3,3,3		; CTC0,CTC1,CTC2,CTC3
SIOAVAL:
	DB	4,46H,5,0EAH,3,0C1H,0,0,0,0	; DART/SIO channel A
SIOBVAL:
	DB	4,86H,5,06AH,3,0C1H,0,0,0,0	; DART/SIO channel B


;	*	*	*	*	*	*	*	*
;
;  Maximum drive letter available:
;
;  This value is installed by the CONFIG program, and is used by
;  the SELECT routine (optionally) to avoid selecting any disk 
;  units above the maximum letter.  This variable was previously
;  the NDSKS (# of floppy disks available) parameter in the 1.x
;  and 2.x bios.
;
;  Note: Although this parameter is set with the CONFIG utility,
;  it is ignored unless the MAX$DRIVE parameter is set to YES.
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+05CH
MAX$DRV$LTR:
	DB	'P'-'A'		; Allow drives A-P


;	*	*	*	*	*	*	*	*
;
;  Step rates for the four floppy drives.
;
;  These values are set by the CONFIG program.
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+05DH
STPRAT:
	DB	0,0,0,0		; Initial rates = 6ms


;	*	*	*	*	*	*	*	*
;
;  Initial IOBYTE value
;
;  This value is set by the CONFIG program, and is used as the
;  initial value for the IOBYTE (location 0003H) on cold boot.
;
;  CP/M defines the following IOBYTE devices:
;
;  Bit: 76543210
;	      xx  Console (0=TTY:, 1=CRT:, 2=BAT:, 3=UC1:)
;	    xx	  Reader  (0=TTY:, 1=PTR:, 2=UR1:, 3=UR2:)
;	  xx	  Punch   (0=TTY:, 1=PTP:, 2=UP1:, 3=UP2:)
;	xx	  List    (0=TTY:, 1=CRT:, 2=LPT:, 3=UL1:)
;
;  In the AMPRO 3.0 bios, the devices are mapped to the serial
;  and parallel ports as follows:
;
;  CRT: Serial Port A		All other devices are undefined.
;  TTY:	Serial Port B		    UC1:  PTR:  UR1:  UR2:  
;  LPT: Parallel port		    UL1:  PTP:  UP1:  UP2:  
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+061H
IOBYT:	DB	81H	; 10 00 00 01	Initial values:
			; || || || ||
			; || || ||  \= CON: Serial port A (CRT:)
			; || ||  \==== RDR: Serial port B (TTY:)
			; ||  \======= PUN: Serial port B (TTY:)
			;  \========== LST: Parallel port (LPT:)


;	*	*	*	*	*	*	*	*
;
;  AutoStart command
;
;  This value is set by the CONFIG program, and is used as the
;  initial command to ZCPR3.  This command is ignored in the CP/M
;  CCP.
;
;  The format of this region is:
;
;  Command length -- 1 byte
;  Command text   -- 8 bytes (max)
;  Trailing zero  -- 1 byte
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+062H
AUTOCMD:
	DW	0,0,0,0,0	; Cmd length + cmd + 0 terminator


;	*	*	*	*	*	*	*	*
;
;  Handshake required flags for DART/SIO channels A and B
;
;  These values are set by the CONFIG program, and indicate if
;  hardware handshaking is required for each of the two serial
;  channels.  Bit zero (0) of each flag should be set to a one
;  to indicate hardware handshaking.
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+06CH
HSA:	DB	0		; Handshake flag for channel A
HSB:	DB	1		; Handshake flag for channel B


;	*	*	*	*	*	*	*	*
;
;  Current BIOS version
;
;  This value is returned in the A register on a call to GETTBL
;  (at BIOS+030H) and is the BIOS version number, in decimal.
;  For example, BIOS version 3, revision 0 would be returned in
;  the A register as 1EH (30 decimal).
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+06EH
VER:	DB	VERS 		; VERSION OF THIS BIOS.


;	*	*	*	*	*	*	*	*
;
;  Double step write enable flag
;
;  This byte is used to determine if a write to a floppy will be
;  allowed if the drive is set to "double step" mode (48tpi disk
;  in a 96tpi drive).  If this byte is set equal to 0DEH (Double
;  step Enable), writes on "double step" drives will be allowed.
;  If this byte is not equal to 0DEH, then a write will return a
;  "write protected" result ("bad sector" on many implementations
;  of CP/M) and the write will not occur.
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+06FH
DS$WRITE$OK:
	DB	0DEH		; Allow writes on double-step


;	*	*	*	*	*	*	*	*
;
;  Drive select bytes
;
;  These values are modified by the floppy drive select code, the
;  warm boot code, and several "E" utilities.  The format of each
;  select byte is as follows:
;
;		  Bit:	76543210
;       Speed select	x		0=normal,  1=double
;       Step select	 x		0=single,  1=double
;       Disk density	  x		0=double,  1=single
;       Side select	   x		0=Side 0,  1=Side 1
;       Drive unit 4	    x		.
;       Drive unit 3	     x		.  Select drive
;       Drive unit 2	      x		.  unit 1,2,3,4
;       Drive unit 1	       x	.
;		  Bit:  76543210
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+070H
DRIVE$TYPES:
	DB	01H		; Drive A:  DD, Unit 1
	DB	02H		; Drive B:  DD, Unit 2
	DB	04H		; Drive C:  DD, Unit 3
	DB	08H		; Drive D:  DD, Unit 4
	DB	02H		; Drive E:  DD, Unit 2 (for now)
	DB	03H		; Drive ?:  DD, Unit 3 (for now)


;	*	*	*	*	*	*	*	*
;
;  SCSI ID (used in SCSI arbitration)
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+076H
MY$ID:
	DB	0		; Indicate no ID at present
	DB	0		; (Spare)


;	*	*	*	*	*	*	*	*
;
;  Misc pointers 
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+078H
	DW	0		; Pointer 0
	DW	0		; Pointer 1
	DW	0		; Pointer 2
	DW	0		; Pointer 3


;	*	*	*	*	*	*	*	*
;
;  Disk drive parameter headers
;
;  Physical drives A,B,C,D	Floppy drives
;
;  Physical drive  E		Special E-disk
;
;  Physical drives F,G,H,I
;		   J,K,L,M
;		   N,O,P	Hard disk drives
;
;	*	*	*	*	*	*	*	*

	ORG	BIOS+080H
DPBASE:
	DW	XLTSS,0,0,0,DIRBUF,SPARM,CSVA,ALVA
	DW 	XLTSS,0,0,0,DIRBUF,SPARM,CSVB,ALVB
	DW 	XLTSS,0,0,0,DIRBUF,SPARM,CSVC,ALVC
	DW 	XLTSS,0,0,0,DIRBUF,SPARM,CSVD,ALVD

	DW 	XLTE1,0,0,0,DIRBUF,EPARM,CSVE,ALVE

	IF	HARD$DISK
	DW 	0,0,0,0,DIRBUF,FPARM,CSVF,ALVF
	DW 	0,0,0,0,DIRBUF,GPARM,CSVG,ALVG
	DW 	0,0,0,0,DIRBUF,HPARM,CSVH,ALVH
	DW 	0,0,0,0,DIRBUF,IPARM,CSVI,ALVI
	DW 	0,0,0,0,DIRBUF,JPARM,CSVJ,ALVJ
	DW 	0,0,0,0,DIRBUF,KPARM,CSVK,ALVK
	DW 	0,0,0,0,DIRBUF,LPARM,CSVL,ALVL
	DW 	0,0,0,0,DIRBUF,MPARM,CSVM,ALVM
	DW 	0,0,0,0,DIRBUF,NPARM,CSVN,ALVN
	DW 	0,0,0,0,DIRBUF,OPARM,CSVO,ALVO
	DW 	0,0,0,0,DIRBUF,PPARM,CSVP,ALVP
	ENDIF

	IF	NOT HARD$DISK
	DW 	0,0,0,0,DIRBUF,SPARM,CSVF,ALVF
	DW 	0,0,0,0,DIRBUF,SPARM,CSVG,ALVG
	DW 	0,0,0,0,DIRBUF,SPARM,CSVH,ALVH
	DW 	0,0,0,0,DIRBUF,SPARM,CSVI,ALVI
	DW 	0,0,0,0,DIRBUF,SPARM,CSVJ,ALVJ
	DW 	0,0,0,0,DIRBUF,SPARM,CSVK,ALVK
	DW 	0,0,0,0,DIRBUF,SPARM,CSVL,ALVL
	DW 	0,0,0,0,DIRBUF,SPARM,CSVM,ALVM
	DW 	0,0,0,0,DIRBUF,SPARM,CSVN,ALVN
	DW 	0,0,0,0,DIRBUF,SPARM,CSVO,ALVO
	DW 	0,0,0,0,DIRBUF,SPARM,CSVP,ALVP
	ENDIF


SPARM:				; Ampro single sided 48tpi
	DW	40		; Sectors/track
	DB	4		; Block shift
	DB	15		; Block mask
	DB	1		; Extent mask
	DW	94		; Disk size -1
	DW	63		; Directory max
	DB	128		; Allocation 0
	DB	0		; Allocation 1
	DW	16		; Check size
	DW	2		; Offset       

DPARM:				; Ampro double sided 48tpi
				; -or-  single sided 96tpi
	DW	40		; Sectors/track
	DB	4		; Block shift  
	DB	15		; Block mask  
	DB	1		; Extent mask  
	DW	194		; Disk size -1 
	DW	127		; Directory max
	DB	192		; Allocation 0 
	DB	0		; Allocation 1 
	DW	32		; Check size   
	DW	2		; Offset       

QPARM:				; Ampro double sided 96tpi
	DW	40		; Sectors/track
	DB	4		; Block shift  
	DB	15		; Block mask  
	DB	0		; Extent mask  
	DW	394		; Disk size -1 
	DW	255		; Directory max
	DB	240		; Allocation 0 
	DB	0		; Allocation 1 
	DW	64		; Check size   
	DW	2		; Offset       

XLTSS:				; Single sided format skew table
	DB	1,2,3,4,5,6,7,8,9,10
XLTDS:				; Double sided format skew table
	DB	17,18,19,20,21,22,23,24,25,26

;	*	*	*	*	*	*	*	*
;
;  Special user-defined drive		"E-disk"
;
;  NOTE: The order of the following table must not be changed,
;	 since many existing utilities depend on this order.
;
;  A call to the BIOS function GETEDISK (BIOS+030H) returns the
;  address of EPARM.  The type table entry is defined as the re-
;  turned value -1, and the translate table value as the returned
;  value +15.  The translate table has space for 20 entries in
;  bios version 1 or 2, 26 entries in bios version 3+.
;
;	*	*	*	*	*	*	*	*
	
ETYPE	DB	82H		; ETYPE must be at EPARM - 1
EPARM:				;Set for:	KAYPRO 
  	DW 	40		;SEC/TRK   	KAYPRO IS 40
  	DB 	3		;BLOCK SHIFT
  	DB 	7		;BLOCK MASK
  	DB 	0		;EXTENT MASK
  	DW 	194		;DISK SIZE -1 	KAYPRO IS 194
  	DW 	63		;DIRECTORY MAX -1
  	DB 	240		;ALLOC 0 	KAYPRO 240
  	DB 	0		;ALLOC 1
  	DW 	16		;CHECK SIZE
  	DW 	1		;OFFSET 	KAYPRO IS 1

EDSD:
  	DB 	1		;DEFAULT DRIVE B:

XLTE1:		;TRANSLATE TABLE FOR E DISK
  	DB 	0,1,2,3,4,5,6,7,8,9
  	DW	0,0,0,0,0,0,0,0		; 26 entries max in XLT1

;  End of E-disk user defined table


;	*	*	*	*	*	*	*	*
;
;  GETTBL	GET NEXT JUMP TABLE ADDRESS
;
;  While this routine returns the address of NXTTBL, it can also
;  be used to identify the BIOS version level.
;
;  For pre-2.0 systems, the A register is returned unchanged,
;  and the HL register pair returned 0FFFFH.
;
;  For 2.0 and higher, HL returneds the address of NXTTBL (never
;  equal to 0FFFFH), and A contains the assembly version of this
;  bios code.
;
;	*	*	*	*	*	*	*	*

GETTBL:
	LXI	H,NXTTBL	; Get address of jump table
	MVI	A,VERS		; .  and version number
NXTJMP:				; Label for dummy routines
	RET

NXTTBL:
	JMP	SWAP		; Swap two logical drives
	JMP	HD$INFO		; Get HD table information
	JMP	PHTBAC		; Get/set PhyTab access
	JMP	PAGET		; Get PhyTab entry address
	JMP	NXTJMP		; RESERVED ENTRY


;	*	*	*	*	*	*	*	*
;
;  GETEDSK	Get E-disk pointer
;
;	*	*	*	*	*	*	*	*

GETEDSK:
  	LXI 	H,EPARM
  	RET


;	*	*	*	*	*	*	*	*
;
;  IOINIT
;
;  Initialize the CTC and DART comm devices according to the
;  information provided at BIOS + 40 hex.
;
;  Entry from BOOT and also via the JMP IOINIT from the BIOS jump
;  table.
;
;	*	*	*	*	*	*	*	*

IOINIT:
	LXI	H,CTCVAL

	LXI	B,0240H		; Initialize CTC0
	DW	OTIR80

	LXI	B,0250H		; Initialize CTC1
	DW	OTIR80

	LXI	B,0260H		; Initialize CTC2
	DW	OTIR80

	LXI	B,0270H		; Initialize CTC3
	DW	OTIR80

	LXI	B,0A84H		; Initialize DART channel A
	DW	OTIR80

	LXI	B,0A8CH		; Initialize DART channel B
	DW	OTIR80

	LDA	IOBYT		; Initialize IOBYTE
	STA	IOBYTE
	RET


;	*	*	*	*	*	*	*	*
;
;  SWAP		Swap logical drives
;
;  Entry here causes the two LOGICAL drives identified in B and C
;  to be swapped in position.  This alters the current LOGICAL
;  name of the target drive for all subsequent disk operations.
;
;  For example, assume the CP/M logical drive A: is the left
;  floppy disk drive, and the CP/M logical drive F: is the first
;  hard disk partition.  If on entry to the SWAP routine the B
;  register contained 0 (logical drive A:) and the C register
;  contained 5 (logical drive F:), after SWAP, all accesses to
;  CP/M drive A: will be to the first hard disk partition, while
;  all accesses to CP/M drive F: would be to the left floppy 
;  drive.
;
;  		* * * * *   WARNING   * * * * * 
;
;  To maintain compatibility with all utilities, and also for
;  some special case coding within this BIOS, the user definable
;  E-DISK must remain as Drive E logical.
;
;	*	*	*	*	*	*	*	*

SWAP:
	MOV	A,B		; Get first drive logical #
	CALL	PAGET		; Compute current address in table
	PUSH	H		; Save this address
	MOV	A,C		; Get second drive logical #
	CALL	PAGET		; Compute current address in table
	POP	D		; Get pointer to first back in D
	MVI	B,4		; Set up to move 4 bytes
PATMV:
	MOV	C,M		; Get a byte from the first str
	LDAX	D		; Get a byte from the second str
	MOV	M,A		; Swap the bytes
	MOV	A,C		; .
	STAX	D		; .
	INX	D		; Bump pointers
	INX	H		; .
	DCR	B		; Decrement count
	JNZ	PATMV		; More bytes to do?
	RET			; No -- all done


;	*	*	*	*	*	*	*	*
;
;  HD$INFO	Return HD information.
;
;  Returns a pointer to various HD pointers.
;
;	*	*	*	*	*	*	*	*

HD$INFO:
	LXI	H,HD$ALV$AVAIL	; Get pointer to available area
	RET			; Provide addr if ptr zero

HD$ALV$AVAIL:
	DW	HD$CURRENT	; Current BIOS buffer area value
	DW	HD$VECTORS	; Hard disk allocation vector base
	DW	HD$BYTE$BLOCK	; Location of SCSI byte/block flag
	DW	0		; Reserved
	DW	0		; Reserved
	DW	0		; Reserved


;	*	*	*	*	*	*	*	*
;
;  PHTBAC	PHYSICAL TABLE ACCESS
;
;  On entry, if the HL register pair is 0000H, the address of the
;  Logical vs Physical table is returned in HL, with the length 
;  of that table in the A register.
;
;  If on entry, the HL register pair is non-zero, the current
;  table is replaced with the user data starting at the location
;  pointed to by HL for the length of the table.
;
;  Note:  When updating the table, ALL bytes are re-written. No
;  length parameter is accepted from the user, and no alternate
;  starting address is recognized.
;
;  Refer to the PHYTAB for byte definitions...
;
;	*	*	*	*	*	*	*	*

PHTBAC:
	XCHG			; Save ptr in case we need it
	MOV	A,D		; Test former HL
	ORA	E		; 
	MVI	A,PHYEND-PHYTAB	; Get length of table
	LXI	H,PHYTAB	; .  and address, too.
	DB	JRNZ,CPY$TBL-$-1; Copy new table if ptr non-zero
	RET			; Provide addr if ptr zero


;	*	*	*	*	*	*	*	*
;
;  PAGET	GET DRIVE LOGICAL INFORMATION
;
;  Entry here from within the BIOS, as well as from the NXTTBL
;  BIOS Jump entry.
;
;  Enter with the Drive Logical address in the A reg.
;
;  Exits with the Logical Table entry address in the HL reg pair.
;
;  NOTE: Destroys DE and A
;
;	*	*	*	*	*	*	*	*

PAGET:
	LXI	H,PHYTAB	; Get base of physical table
	ADD	A		; Compute offset to the descriptor
	ADD	A		; .
	MOV	E,A		; .
	MVI	D,0		; .
	DAD	D		; .
	RET			; Return with ptr in HL


CPY$TBL:			; Copy from DE to HL, length A
	XCHG			; .  (exchange DE and HL)
	MOV	C,A		; .  (put length info in BC)
	MVI	B,0		; .  (now it's a-ok for LDIR)
	DW	LDIR80		; .  (use LDIR to move table)
	RET			; and return to caller


;	*	*	*	*	*	*	*	*
;
;  PHYSICAL DRIVER TABLE
;
;  This table contains information to identify the Logical vs
;  Physical drives requested, and also the disk driver required 
;  for the particular drive.
;
;  The table is built as 16 groups of 4-bytes each, arranged in
;  the logical order of drives A thru P for a total of 64 bytes.
;
;  The four bytes defining each Logical drive are as follows:
;
;  +0	Disk Driver ID number, 0=reserved for error, Max=7
;
;  +1	Physical offset from DPBASE in DPH table, and the drive
;	unit number.
;
;	Bits   7654:	Offset to DPBASE 0 - F
;
;	Bits   3210:	Physical device address to be passed
;			to the respective driver.
;
;			For floppies, this is the drive unit #.
;			For hard disks, these are reserved.
;
;  +2	Type identifier for this drive 
;
;	Floppy usage:
;
;		   Bit: 76543210
;	Density		x	   0=single     1=double
;	Sides		 x	   0=single     1=double
;	Sector #'s	  x	   0=same       1=continuous
;	Track count	   x	   0=down       1=down front, up back
;	Alloc unit	    xx	   00=1K   01=2K   10=4K   11=8K
;	Sector size	      xx   00=128  01=256  10=512  11=1024
;		   Bit: 76543210
;
;	Hard disk usage:
;
;		   Bit: 76543210
;	LUN		xxx	   Logical unit number (0-7)
;	Reserved	   x	   
;	Alloc unit	    xx	   00=1K   01=2K   10=4K   11=8K
;	Sector size	      xx   00=128  01=256  10=512  11=1024
;		   Bit: 76543210
;
;	Note:	Allocation unit and sector size usage are the same
;		for floppy and hard disk.
;
;
;  +3	00 for floppy or SCSI bus address for hard disk.
;
;	For normal SCSI operations, only one bit may be set.
;
;		   Bit: 76543210
;	SCSI address 0	       x	This is the actual
;	SCSI address 1	      x		bit pattern supplied
;	SCSI address 2	     x		during the SCSI
;	SCSI address 3	    x		select routine.  No
;	SCSI address 4	   x		internal address
;	SCSI address 5	  x		translation or bit
;	SCSI address 6	 x		scaling is done.
;	SCSI address 7	x		
;
;  +4	Duplicated for drives B thru P
; thru
; +63
;
;  Disk Driver Linkage  +64 thru +79
;
;  Each disk driver is described by 2 bytes which point to the
;  driver to be used, with driver 0 reserved for the SELECT ERROR
;  trap for unused or deselected drive letters.
;
;	*	*	*	*	*	*	*	*

PHYTAB:
	DB	1,000H, SSDD48, 0H	; Floppy drive A
	DB	1,011H, SSDD48, 0H	; Floppy drive B
	DB	1,022H, SSDD48, 0H	; Floppy drive C
	DB	1,033H, SSDD48, 0H	; Floppy drive D

	DB	2,044H, 0H, 0FFH	; Special floppy drive E

	DB	0,050H,0,0		; Hard disk drive F
	DB	0,060H,0,0		; Hard disk drive G
	DB	0,070H,0,0		; Hard disk drive H
	DB	0,080H,0,0 		; Hard disk drive I

	DB	0,090H,0,0		; Hard disk drive J
	DB	0,0A0H,0,0		; Hard disk drive K
	DB	0,0B0H,0,0		; Hard disk drive L
	DB	0,0C0H,0,0		; Hard disk drive M

	DB	0,0D0H,0,0		; Hard disk drive N
	DB	0,0E0H,0,0		; Hard disk drive O
	DB	0,0F0H,0,0		; Hard disk drive P

DRVRADR:				; Driver table
	DW	SELERR			; Driver 0 - select error
	DW	FLOPPY			; Driver 1 - floppy
	DW	DISKE			; Driver 2 - E-disk
	IF	HARD$DISK
	DW	DRVR3			; Driver 3 - SCSI hard disk
	ENDIF
	IF	NOT HARD$DISK
	DW	SELERR			; Driver 3 - not defined
	ENDIF
	DW	SELERR			; Driver 4 - not defined
	DW	SELERR			; Driver 5 - not defined
	DW	SELERR			; Driver 6 - not defined
	DW	SELERR			; Driver 7 - not defined

PHYEND	EQU	$


;	*	*	*	*	*	*	*	*
;
;  HOME		Home the selected disk
;
;  Entry via the bios jump table HOME
;
;	*	*	*	*	*	*	*	*

HOME:
	LXI	B,0		; Set the current track to zero
	CALL	SETTRK		; .
	LDA	HSTWRT		; Check for pending write
	ORA	A		; .
	DB	JRNZ,HOMED-$-1	; Pending write, leave active alone
	STA	HSTACT		; No writes, clear host active
HOMED:
	RET


;	*	*	*	*	*	*	*	*
;
;  SETTRK	Set track (BIOS JMP table entry)
;
;	*	*	*	*	*	*	*	*

SETTRK:				; Set track given by BC
	DW	SBCD80,SEKTRK
	DW	SBCD80,CPMTRK
	RET


;	*	*	*	*	*	*	*	*
;
;  SETSEC	Set sector (BIOS JMP table entry)
;
;	*	*	*	*	*	*	*	*

SETSEC:				; Set sector given by reg C
	MOV	A,C
	STA	SEKSEC
	STA	CPMSEC
	RET


;	*	*	*	*	*	*	*	*
;
;  SETDMA	Set DMA (BIOS JMP table entry)
;
;	*	*	*	*	*	*	*	*

SETDMA:				; Set DMA address given by BC
	MOV	H,B
	MOV	L,C
	SHLD	DMAADR
	RET


;	*	*	*	*	*	*	*	*
;
;  SECTRAN	Sector translation (BIOS JMP table entry)
;
;  NOTE: No sector translation is done for the AMPRO disk formats.
;
;	*	*	*	*	*	*	*	*

SECTRAN:			; Translate sector number in BC
	MOV	H,B
	MOV	L,C
	RET


;	*	*	*	*	*	*	*	*
;
;  SELECT	Select disk routine
;
;  Entry via the BIOS Jump Table SELDSK
;
;  Entry:
;	C  CP/M Logical drive
;	E  Bit zero is non-zero if this is the first select of the
;	   drive since warm or cold boot.
;
;	*	*	*	*	*	*	*	*

SELDSK:
	MOV	A,C		; Get logical disk requested
	STA	LOGDSK		; Save it
	IF	MAX$DRIVE	; Is the max drive checking enabled?
	LDA	MAX$DRV$LTR	; .  Yes -- Is the letter requested
	CMP	C		; .  .  greater than the max letter?
	JC	SELERR		; .  Yes -- return w/select error
	ENDIF			; .  .
	MVI	A,0FFH		; No to either -- set SELECT cmd

; NOTE:  we fall through to PDRVR

;	*	*	*	*	*	*	*	*
;
;  PDRVR	Physical driver linkage
;
;  Enter with the Logical Disk Drive saved at (LOGDSK) and the 
;  Physical Command in the A reg as:
;
;	FF	Select Disk Routine
;	00	Write Disk
;	01 	Read  Disk
;
;  The proper driver is computed and jumped to as needed.
;
;	*	*	*	*	*	*	*	*

PDRVR:
	PUSH	D		; Protect first select status
	STA	PHYCMD		; Save cmd for later
	LDA	LOGDSK		; Get logical drive requested
	CALL	PAGET		; Get table address for this drive
	SHLD	PHYTAG		; Save index to logical descriptor
	PUSH	H		; Save start address
	LXI	D,TAGDRV	; Move the four tag bytes
	LXI	B,4		; .
	DW	LDIR80		; (LDIR)
	POP	H		; Restore starting address
	POP	D		; Restore first select status
	MOV	A,M		; Get driver number
	ORA	A		; .  If zero, then select error
	JZ	SELERR		; .
	PUSH	PSW		; Save drive index
	INX	H		; Bump to next position
	MOV	A,M		; Get unit # and DPH offset
	ANI	0FH		; Mask all but unit #
	STA	PHYDRV		; Save unit #
	MOV	A,M		; Get unit # and DPH offset again
	ANI	0F0H		; Mask all but DPH offset
	STA	DPHDRV		; Save DPH offset
	LDA	PHYCMD		; Get command back
	INR	A		; Select command?
	JNZ	PDRVR1		; No  - don't update disk address
	LDA	LOGDSK		; Yes - Set SEKDSK = LOGDSK
	STA	SEKDSK		; .
PDRVR1:	
	POP	PSW		; Get driver number back
	ADD	A		; .
	MOV	C,A		; Compute ptr within driver table
	MVI	B,0		; .
	LXI	H,DRVRADR	; .
	DAD	B		; .
	MOV	A,M		; Get ptr to driver in HL
	INX	H		; .
	MOV	H,M		; .
	MOV	L,A		; .
	LDA	PHYCMD		; Get cmd back in the A reg
	PCHL			; Jump to the proper driver


;	*	*	*	*	*	*	*	*
;
;  FLOPPY	Floppy disk dispatcher -- driver code (01)
;
;  Enter with operation code in the A register as:
;
;	FF	Select Disk
;	00	Write Disk
;	01	Read Disk
;
;	*	*	*	*	*	*	*	*

FLOPPY:
	INR	A		; Code = select?
	JZ	SELFLP		; Yes, perform a floppy select
	JMP	FLOPPYIO	; No,  perform host read or write


;	*	*	*	*	*	*	*	*
;
;  DISKE	E-disk dispatcher -- driver code (02)
;
;  Enter with the operation code in the A register as:
;
;	FF	Select Disk
;	00	Write Disk
;	01	Read Disk
;
;	*	*	*	*	*	*	*	*

DISKE:
	PUSH	PSW		; Save command
	PUSH	D		; .  and first-select status
	LDA	EDSD		; Get E-disk unit
	MOV	E,A		; Get type byte for the drive
	MVI	D,0		; .  we want to be the 'E'
	LXI	H,DRIVE$TYPES	; .  disk
	DAD	D		; .
	MOV	A,M		; .
	ANI	00FH		; Strip all but select bit
	MOV	B,A		; Save select bit
	LXI	H,DRIVE$TYPES+4	; Point to the E-disk drive byte
	MOV	A,M		; Get the E-disk drive byte
	ANI	0F0H		; Strip select bits
	ORA	B		; OR in proper select bits
	MOV	M,A		; Save new E-disk drive byte
	PUSH	H		; Save pointer to E-disk drive byte
	LDA	EPARM-1		; Get type byte
	LHLD	PHYTAG		; Save as PHYTAB type byte
	INX	H		; .
	INX	H		; .
	MOV	M,A		; .
	STA	TAGTYP		; and as current type byte
	POP	H		; Get E-disk drive byte ptr back
	DB	BIT,BRES+B5+ZM	; Indicate double density
	RLC			; See if E-disk is single density
	DB	JRC,E$IS$DD-$-1	; No,  leave bit alone
	DB	BIT,BSET+B5+ZM	; Yes, indicate single density
E$IS$DD:
	POP	D		; Recover first select status
	POP	PSW		; .  and command
	INR	A		; Select?
	JZ	SELEDSK		; Yes, perform select
	JMP	FLOPPYIO	; Otherwise use R/W floppy host


PUTS:				; Send zero-terminated str to con:
	MOV	A,M		; Get char
	INX	H		; Bump pointer
	ORA	A		; Zero is end-of-string
        RZ                      ; Yes -- return
	MOV	C,A		; Otherwise move to C register
	CALL	CONOUT		; .  and send to console
	JMP	PUTS		; Repeat for remaining bytes


;	*	*	*	*	*	*	*	*
;
;  WBOOT	Warm boot system
;
;  This routine reloads the CCP and BDOS from the current logical
;  disk defined as SYSDSK.  While SYSDSK is normally logical drive
;  A:, any other drive may be referenced by setting SYSDSK to the
;  desired logical drive address (0-15).
;
;  The warm boot consists of reading 1600 hex bytes of code, from
;  CP/M track 0, sector 1, for 44 CP/M records.  The code is loaded
;  starting with the CCP address, which is defined at the start of
;  this BIOS listing.
;
;  The disk currently assigned to SYSDSK must have a standard AMPRO
;  system image residing on the boot tracks (tracks 0 and 1).
;
;  If SYSDSK is not logical drive A:, the BDOS will still access the
;  current logical drive A: after the GOCPM entry in order to build
;  the directory allocation vector table for drive A: prior to the
;  selection of the current disk.
; 	
;	*	*	*	*	*	*	*	*

WBOOT:
	LXI	SP,80H		; Set stack to below default DMA

	LDA	SYSDSK		; Get current system disk
	MOV	C,A		; .
	MVI	E,0		; Set autoselect bit
	CALL	SELDSK		; Select the system disk
	MOV	A,H		; Check for a select error
	ORA	L		; .
        JZ	WBERR		; Select error -- try again

	LXI	B,0		; Start reading from track 0
	CALL	SETTRK		; .
	MVI	B,NSECTS	; Set the # of sectors to read
	MVI	C,1		; Start from sector 1 of track 0
	LXI	H,CCP		; Where we want the data to go
WBLOOP:
	PUSH	B		; Save sector count
	PUSH	H		; and DMA address
	CALL	SETSEC		; Set the sector to read next
	POP	B		; Get the DMA address
	PUSH	B		; Save it back
	CALL	SETDMA		; Set the current DMA
	CALL	READ		; Read the sector
	ORA	A		; Check for errors
	JNZ	WBERR		; Disp msg & try again on error

	POP	H		; Get DMA address
	POP	B		; Get count & current sector
	LXI	D,128		; Update DMA address
	DAD	D		; .
	INR	C		; Bump sector count
	LDA	CPMSPT		; Get sectors/track for this disk
	CMP	C		; Are we at the end of track yet?
	DB	JRNZ,NXTSEC-$-1	; No,  just get the next sector
	PUSH	B		; Yes, save count & current sector
	DW	LBCD80,CPMTRK	; Bump current track
	INX	B		; .
	CALL	SETTRK		; .
	POP	B		; Restore count
	MVI	C,0		; Reset sector to 0
NXTSEC:
	DCR	B		; Decrement loop counter
	JNZ	WBLOOP		; 

; NOTE: WBOOT falls through to GOCPM

;	*	*	*	*	*	*	*	*
;
;  GOCPM	Setup CP/M vectors and jump to CP/M
;
;	*	*	*	*	*	*	*	*

GOCPM:
	MVI	A,0C3H		; Setup the warm boot vector
	STA	0		; .
	LXI	H,WBOOTE	; .
	SHLD	1		; .
	STA	5		; and the BDOS vector
	LXI	H,BDOS		; .
	SHLD	6		; .
	LXI	B,80H		; Setup the default DMA
	CALL	SETDMA		; .
	EI			; Enable interrupts
	LDA	CDISK		; Get current disk/user
	MOV	C,A		; .
	JMP	CCP		; And go to CP/M ...


;	*	*	*	*	*	*	*	*
;
;  WBERR	Display error msg on warm boot error and try again.
;
;	*	*	*	*	*	*	*	*

WBERR:
	LXI	H,BOOTMSG	; Warm boot error -- display
	CALL	PUTS		; .  boot failed message and
	JMP	WBOOT		; .  try again ...
BOOTMSG:
	DB	CR,LF,'BOOT FAILED!',0


;	*	*	*	*	*	*	*	*
;
;  SELFLP	Floppy disk select routine
;
;  See if the disk has been accessed since the last warm or cold
;  boot (bit 0 of the E register is non-zero).  If not, seek to
;  track 2 and read the ID to determine if the disk is SS or DS,
;  48tpi or 96tpi, and in a 48tpi or 96tpi drive.  Set up the disk
;  access tables as appropriate.
;
;	*	*	*	*	*	*	*	*

SELFLP:
	DB	BIT,BTST+B0+ZE	; Check for new mount since boot
	JNZ	SELEND		; Not new, no need to determine type
	CALL	MAPTYPE		; Clear double step bit
	DB	BIT,BRES+B6+ZM	; .
	CALL	GETTYPE		; Set type byte to SS48tpi
	MVI	M,SSDD48	; .
	CALL	SETUP		; Setup parameters for SSDD
	CALL	RESTORE		; Restore floppy to track 00
	JNZ	SELERR		; Error on restore, give select err
	MVI	A,2		; Seek to track 2
	STA	CPMTRK		; .
	CALL	SEEK		; .
	LDA	IDSAVE+3	; Get sector size (bytes/sector)
	DB	BIT,BTST+B1+ZA	; Bit 1 non-zero means 512 or 1024
	JZ	SELERR		; 128,256 = err for AMPRO fmt
	ANI	01H		; Reset all but bit 0
	ADD	A		; *2 ... now 0=48tpi, 2=96tpi
	MOV	C,A		; Save in C reg for the moment
	LDA	IDSAVE+2	; Get sector #
	CPI	DSBIAS+1	; Compare to DS bias (16+1)
	JC	SELSS		; Carry = SS, No carry = DS
	INR	C		; Bump pointer if DS
SELSS:	MVI	B,0		; BC now contains offset to type
	CALL	GETTYPE		; Get ptr to type byte
	XCHG			; Save ptr in DE
	LXI	H,TYPTBL	; Get type table base
	DAD	B		; Add offset
	MOV	A,M		; Get actual type byte
	STAX	D		; Save in PHYTAB

	CALL	GETDPT		; Get ptr to DPT
	LXI	D,10		; Bump by 10 to get ptr to DPB
	DAD	D		; .
	XCHG			; Save ptr in DE
	LXI	H,DPBTBL	; Get DPB ptr base
	DAD	B		; Add offset
	DAD	B		; Add offset again (word ptr)
	MOV	A,M		; Get first byte of DPB ptr
	STAX	D		; Save it
	INX	D		; Bump pointers
	INX	H		; .
	MOV	A,M		; Get second byte of DPB ptr
	STAX	D		; Save it

	LDA	IDSAVE		; Get track number
	DCR	A		; Track=1 means set double step
	JZ	SET$DSTEP	; .
	DCR	A		; Track=2 means normal step
	JZ	SELEND		; .  so just finish the select
	JMP	SELERR		; Any other value = select err

SET$DSTEP:
	CALL	MAPTYPE		; Map byte for this drive,
	DB	BIT,BSET+B6+ZM	; set the double step bit.

; NOTE: We fall through to SELEND ...

;	*	*	*	*	*	*	*	*
;
;  SELEND	Compute proper deblocker variables and DPT addr
;
;  Driver types 1, 2, and 3 all re-enter here.
;
;	*	*	*	*	*	*	*	*

SELEND:
	CALL	GETTYPE		; Get current type byte
	MOV	B,A		; Save for a moment
	ANI	3		; Mask all but sector shift bits
	STA	SECSHF		; Save sector shift value
	MOV	E,A		; Get sector shift byte from table
	MVI	D,0		; .
	LXI	H,SEC$MSK$TBL	; .
	DAD	D		; .
	MOV	A,M		; .
	STA	SECMSK		; Save it

	MOV	B,A		; Get type byte back
	RAR			; Shift over to move alloc size
	RAR			; .  to the proper position
	ANI	3		; Mask all but alloc bits
	MOV	E,A		; Get initial unalloc from table
	MVI	D,0		; .
	LXI	H,M$UNACT$TBL	; .
	DAD	D		; .
	MOV	A,M		; .
	STA	MUNACT		; Save it

; NOTE: We fall through to GETDPT . . .

;	*	*	*	*	*	*	*	*
;
;  GETDPT	Calculate the DPH address for the current logical disk
;
;  Entry with the DPH vector in DPHDRV, exits with the DPH address in
;  the HL register pair, and the current DPB saved at CURDPB (primarily
;  for the warm boot routine).
;
;	*	*	*	*	*	*	*	*

GETDPT:
	LDA	DPHDRV		; Get DPH pointer
	MOV	E,A		; .
	MVI	D,0		; .
	LXI	H,DPBASE	; .
	DAD	D		; .
	PUSH	H		; save it (need to return it in HL)
	LXI	D,10		; Add offset to DPB
	DAD	D		; .
	MOV	A,M		; Copy DPB ptr to HL
	INX	H		; .
	MOV	H,M		; .
	MOV	L,A		; .
	SHLD	CURDPB		; Save current DPB pointer
	MOV	A,M		; Get sectors-per-track
	STA	CPMSPT		; Save it
	POP	H		; Get DPH pointer
	RET			; Leave with DPH pointer in HL

TYPTBL:				; Drive type bytes
	DB	SSDD48
	DB	DSDD48
	DB	SSDD96
	DB	DSDD96

DPBTBL:				; DBP pointers
	DW	SPARM
	DW	DPARM
	DW	DPARM
	DW	QPARM

SEC$MSK$TBL:			; Sector mask table
	DB	00H
	DB	01H
	DB	03H
	DB	07H

M$UNACT$TBL:			; Initial unalloc count table
	DB	8
	DB	16
	DB	32
	DB	64


;	*	*	*	*	*	*	*	*
;
;	SELECT THE E-DISK DRIVE
;
;	Builds the appropriate tables, and attempts to re-zero the
;	drive. Returns with Select Error if can't re-zero.
;
;	*	*	*	*	*	*	*	*

SELEDSK:
	DB	BIT,BTST+B0+ZE	; Check for new mount since boot
	JNZ	SELEND		; Not new, no need to determine type
	CALL	GETTYPE		; Get type byte & pointer
	DB	BIT,BTST+B5+ZA	; Continuous sector numbers?
	DB	JRZ,NOTCONT-$-1	; No,  don't bother computing E-adj
	ANI	03H		; Yes, compute E-adj from sector
	DW	LBCD80,EPARM-1	; .  size and sectors per track
	MVI	C,0FFH		; .  (A=count,B=SPT,C=mask)
SELESHIFT:			; .
	ORA	A		; .  Test if done & clear carry
	DB	JRZ,SDONE-$-1	; .  Yes, save parameter
	DB	0CBH,019H	; .  RR C  (Rotate C right)
	DB	0CBH,018H	; .  RR B  (Rotate B right)
	DCR	A		; .  Decrement count
	JMP	SELESHIFT	; .  Next shift ...
NOTCONT:
	LXI	B,0		; Not continuous, set adj to zero
SDONE:
	MOV	A,B		; Shifts done, get computed E-adj
	ANA	C		; Mask extra bits off
	STA	ESECADJ		; Save as proper adj
	CALL	SETUP		; Setup parameters for the E-disk
	CALL	RESTORE		; Restore floppy to track 00
	INR	A		; Check for error during restore
	JZ	SELERR		; Error code, return with select err
	JMP	SELEND		; No error, finish the select


;	*	*	*	*	*	*	*	*
;
;  SELERR	Indicate select error to the BDOS
;
;	*	*	*	*	*	*	*	*

SELERR:
	LXI	H,0		; Zero indicates select failed
	RET


;---------------------------------------------------------------
;
;	READ CP/M SECTOR FROM THE CURRENTLY SELECTED DISK
;
;	ENTRY VIA THE BIOS JUMP TABLE
;
;--------------------------------------------------------------

READ:
	CALL	GETTYPE
	ANI	3		; SIZE
	JZ	READHST		; IF 128 BYTES
	XRA	A
	STA	UNACNT
	MVI	A,1
	STA	READOP		;READ OPERATION
	STA	RSFLAG		;MUST READ DATA
	MVI	A,WRUAL
	STA	WRTYPE		;TREAT AS UNALLOC
	JMP	RWOPER		;TO PERFORM THE READ


;---------------------------------------------------------------
;
;	WRITE CP/M SECTOR FROM THE CURRENTLY SELECTED DISK
;
;	ENTRY VIA THE BIOS JUMP TABLE
;
;--------------------------------------------------------------

WRITE:
	CALL	GETTYPE
	ANI	3
	JZ	WRITEHST	; IF 128 BYTES
	XRA	A		;0 TO ACCUMULATOR
	STA	READOP		;NOT A READ OPERATION
	MOV	A,C		;WRITE TYPE IN C
	STA	WRTYPE
	CPI	WRUAL		;WRITE UNALLOCATED?
	JNZ	CHKUNA		;CHECK FOR UNALLOC

;	WRITE TO UNALLOCATED, SET PARAMETERS
;	FOR EDSK, WE NEED TO CHECK TO SEE IF SEKDSK IS THE E DISK.
;	IF SO, THEN UNACNT WILL BE SET ACCORDING TO THE DISK TYPE.
;	FOR AMPRO DISKS, ITS ALWAYS BLKSIZ/128.

	LDA	MUNACT
	STA	UNACNT
	LDA	SEKDSK		;DISK TO SEEK
	STA	UNADSK		;UNADSK = SEKDSK
	LHLD	SEKTRK
	SHLD	UNATRK		;UNATRK = SECTRK
	LDA	SEKSEC
	STA	UNASEC		;UNASEC = SEKSEC

CHKUNA:
	;CHECK FOR WRITE TO UNALLOCATED SECTOR
	LDA	UNACNT		;ANY UNALLOC REMAIN?
	ORA	A
	JZ	ALLOC		;SKIP IF NOT

;	MORE UNALLOCATED RECORDS REMAIN
	DCR	A		;UNACNT = UNACNT-1
	STA	UNACNT
	LDA	SEKDSK		;SAME DISK?
	LXI	H,UNADSK
	CMP	M		;SEKDSK = UNADSK?
	JNZ	ALLOC		;SKIP IF NOT

;	DISKS ARE THE SAME

	LXI	H,UNATRK
	CALL	TRKCMP
	JNZ	ALLOC		;SKIP IF NOT

;	TRACKS ARE THE SAME

	LDA	SEKSEC		;SAME SECTOR?
	LXI	H,UNASEC
	CMP	M		;SEKSEC = UNASEC?
	JNZ	ALLOC		;SKIP IF NOT

;	MATCH, MOVE TO NEXT SECTOR FOR FUTURE REF

	INR	M		;UNASEC = UNASEC+1
	MOV	A,M		;END OF TRACK?

;	WATCH FOR E-DISK - IF SO, USE DIFFERENT SPT VARIABLE

	PUSH	B		;SAVE BC IN CASE WE NEEDED IT
	PUSH	PSW
	LDA	PHYDRV		; Test for E-disk
	CPI	04H		; .
	LDA	CPMSPT		; Use normal SPT if not e-disk
	JZ	EMATCH		; .
	LDA	EPARM		; Else use E-disk SPT
EMATCH:
	MOV	B,A
	POP	PSW
	CMP	B		;COUNT CP/M SECTORS
	POP	B		;RESTORE BC REGISTER
	JC	NOOVF		;SKIP IF NO OVERFLOW

;	OVERFLOW TO NEXT TRACK

	MVI	M,0		;UNASEC = 0
	LHLD	UNATRK
	INX	H
	SHLD	UNATRK

NOOVF:
				;MATCH FOUND, MARK AS UNNECESSARY READ
	XRA	A		;0 TO ACCUMULATOR
	STA	RSFLAG		;RSFLAG = 0
	JMP	RWOPER		;TO PERFORM THE WRITE

ALLOC:
				;NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ
	XRA	A		;0 TO ACCUM
	STA	UNACNT		;UNACNT = 0
	INR	A		;1 TO ACCUM
	STA	RSFLAG		;RSFLAG = 1

RWOPER:
				;ENTER HERE TO PERFORM THE READ/WRITE
	XRA	A		;ZERO TO ACCUM
	STA	ERFLAG		;NO ERRORS (YET)
	PUSH	B		;SAVE IN CASE
	LDA	SECSHF
	MOV	B,A
	LDA	SEKSEC		;COMPUTE HOST SECTOR

SLOOP:
	ORA	A		;CARRY = 0
	RAR			;SHIFT RIGHT
	DCR	B
	JNZ	SLOOP
	POP	B		;RESTORE
	STA	SEKHST		;HOST SECTOR TO SEEK

;				ACTIVE HOST SECTOR?
	LXI	H,HSTACT	;HOST ACTIVE FLAG
	MOV	A,M
	MVI	M,1		;ALWAYS BECOMES 1
	ORA	A		;WAS IT ALREADY?
	JZ	FILHST		;FILL HOST IF NOT

;				HOST BUFFER ACTIVE, SAME AS SEEK BUFFER?
	LDA	SEKDSK
	LXI	H,HSTDSK	;SAME DISK?
	CMP	M		;SEKDSK = HSTDSK?
	JNZ	NOMATCH		; No.

;	SAME DISK, SAME TRACK?

	LXI	H,HSTTRK
	CALL	TRKCMP
	JNZ	NOMATCH

;				SAME DISK, SAME TRACK, SAME BUFFER?
	LDA	SEKHST
	LXI	H,HSTSEC	;SEKHST = HSTSEC?
	CMP	M
	JZ	MATCH		;SKIP IF MATCH

NOMATCH:
;				PROPER DISK, BUT NOT CORRECT SECTOR
	LDA	HSTWRT		;HOST WRITTEN?
	ORA	A
	LDA	HSTDSK		;SELECT HOST AS DISK TO WORK ON
	STA	LOGDSK		;SET LOGICAL DRIVE = BUFFERS DRIVE
	LHLD	HSTTRK
	SHLD	CPMTRK
	CNZ	WRITEHST	;CLEAR HOST BUFF

FILHST:
;				MAY HAVE TO FILL THE HOST BUFFER
	LDA	SEKDSK
	STA	HSTDSK
	STA	LOGDSK		;UPDATE LOGICAL DRIVE 
	LHLD	SEKTRK
	SHLD	HSTTRK
	SHLD	CPMTRK
	LDA	SEKHST
	STA	HSTSEC
	LDA	RSFLAG		;NEED TO READ?
	ORA	A
	CNZ	READHST 	;YES, IF 1
	XRA	A		;0 TO ACCUM
	STA	HSTWRT		;NO PENDING WRITE

MATCH:
;				COPY DATA TO OR FROM BUFFER

;	EDSK: SECTOR MASK (SECMSK) MUST BE A VARIABLE FOR EDISK AND
;	IS CALCULATED BY (HSTSIZ/128)-1. THIS IS THREE FOR AMPRO,
;	KAYPRO, AND OTHER 512-BYTE SECTORS, AND 1 FOR 256 BYTE SECTORS.

	PUSH	B		;SAVE REG
	LDA	SECMSK
	MOV	B,A
	LDA	SEKSEC		;MASK BUFFER NUMBER
	ANA	B		;LEAST SIGNIF BITS
	POP	B		;RESTORE BC CONTENTS
	MOV	L,A		;READY TO SHIFT
	MVI	H,0		;DOUBLE COUNT
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	DAD	H

;				HL HAS RELATIVE HOST BUFFER ADDRESS
	LXI	D,HSTBUF
	DAD	D		;HL = HOST ADDRESS
	XCHG			;NOW IN DE
	LHLD	DMAADR		;GET/PUT CP/M DATA
	MVI	C,128		;LENGTH OF MOVE
	LDA	READOP		;WHICH WAY?
	ORA	A
	JNZ	RWMOVE		;SKIP IF READ

;				WRITE OPERATION, MARK AND SWITCH DIRECTION
	MVI	A,1
	STA	HSTWRT		;HSTWRT = 1
	XCHG			;SOURCE/DEST SWAP

RWMOVE:
;				C INITIALLY 128, DE IS SOURCE, HL IS DEST
	LDAX	D		;SOURCE CHARACTER
	INX	D
	MOV	M,A		;TO DEST
	INX	H
	DCR	C		;LOOP 128 TIMES
	JNZ	RWMOVE

;				DATA HAS BEEN MOVED TO/FROM HOST BUFFER
	LDA	WRTYPE		;WRITE TYPE
	CPI	WRDIR		;TO DIRECTORY?
	LDA	ERFLAG		;IN CASE OF ERRORS
	RNZ			;NO FURTHER PROCESSING

;				CLEAR HOST BUFFER FOR DIRECTORY WRITE
	ORA	A		;ERRORS?
	RNZ			;SKIP IF SO
	XRA	A		;0 TO ACCUM
	STA	HSTWRT		;BUFFER WRITTEN
	LDA	HSTDSK
	STA	LOGDSK
	LHLD	HSTTRK
	SHLD	CPMTRK
	CALL	WRITEHST
	LDA	ERFLAG
	RET

TRKCMP:	XCHG
	LXI	H,SEKTRK
	LDAX	D		; LOW BYTE COMPARE
	CMP	M		; SAME?
	RNZ			; RETURN IF NOT
	INX	D		; NEXT..
	INX	H		; ..BYTES
	LDAX	D		; HIGH ORDER COMPARE
	CMP	M		; SETS FLAGS
	RET			; ZERO FLAG IF EQUAL

WRITEHST:
	;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #,
        ;HSTSEC = HOST SECT #. WRITE "HSTSIZ" BYTES
	;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG.
	;RETURN ERFLAG NON-ZERO IF ERROR

	XRA	A
	JMP	HOSTDO

READHST:
	;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #,
        ;HSTSEC = HOST SECT #. READ "HSTSIZ" BYTES
	;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG.

	MVI	A,1
HOSTDO:
	STA	RWHOST
HOSTIO:
  	JMP	PDRVR		;SELECT NEXT ROUTINE VIA PDRVR
;
;--------------------------------------------------------------------
;
;	FLOPPY DISK READ/WRITE HOST ROUTINES
;
;-------------------------------------------------------------------


FLOPPYIO:
	CALL	SETUP		;OUTPUT TO DRIVE SELECT REGISTER
        MVI 	A,FRETRY
        STA 	TRIES
WMI:
  	CALL	MAPTRK
  	CALL	GETTRK		;WHAT TRACK DO WE WANT?
  	CMP	M
  	CNZ	SEEK		;IF NOT THERE, GO THERE
  	JNZ	RWFAIL		;IN CASE SEEK FAILS

;	TRACK SHOULD BE OK, BUT WE NEED TO SEND IT ANYWAY IN
;       CASE THE LAST UNIT WAS DIFFERENT (THERE'S ONLY ONE TRACK
;	REGISTER). THEN LOAD SECTOR AND DO IT...

	CALL	GETTRK
	OUT	WTRK
	CALL	GETSEC

; CHECK FOR DSDD TO ADD SECTOR BIAS IF NOT 'E' DRIVE

	MOV	E,A		; Save sector # for a moment
	LDA	PHYDRV		; See if this is the E-disk
	CPI	04H		; .  (E-disk is drive "4")
	MVI	A,0		; Restore sector #
        DB	JRZ,NOBIAS-$-1	; No sector bias if 'E' drive
	PUSH	D		; Get current media type
	CALL	GETTYPE		; .
	POP	D		; .
	ANI	0C0H		; Isolate DD, DS indicators
	CPI	0C0H		; Check DD and DS
	MVI	A,0		; .
	DB	JRNZ,NOBIAS-$-1	; Not DSDD, no bias
	ADI	DSBIAS		; Add double sided bias
NOBIAS:
	ADD	E		; Add sector # to bias
	OUT	WSEC		; Send sector # to FDC
	CALL	SETUP		; Select unit and real side
	CALL	GETBUF		;
	MVI	A,2		; Set up internal retry counter
	STA	INT$RETRIES	; .
	LDA	RWHOST		; Test for read (1) or write (0)
	ORA	A		; .
	JNZ	RDS		; .

	CALL	WRDAT		; Write sector
	JNZ	RWFAIL		; Try again if failed
	RET			; Otherwise return

RDS:
	MVI	A,FREADS	; Indicate read sector
	CALL	RDATA		; Read sector
	JNZ	RWFAIL		; Try again if failed
	RET			; Otherwise return

RWFAIL:
	CALL	RESTORE		; Restore to track 00 on failure
	LXI	H,TRIES		; Point to retry counter
	DCR	M		; Bump it closer to doom
	JNZ	WMI		; and try again if we can . . .

	MVI	A,01		; No more tries left
	STA	ERFLAG		; Set error flag
	RET			; and return to BDOS w/error


;	*	*	*	*	*	*	*	*
;
;  WRDAT	Floppy disk write data routine
;
;  This routine writes the data pointed to by the HL register to
;  the currently selected floppy.
;
;  The number of bytes written is determined by the FDC controller
;  and the ID string currently active for this sector.
;
;  Multiple sector transfers are not supported.
;
;  NOTE:  The fastest way to test for busy and DRQ is to shift the
;  status bit right into the carry bit.  Therefore, when busy drops,
;  the ending status is still shifted right.  The checks for lost
;  data, write protect, record not found, and crc error must take
;  this bit shift into account.
;
;	*	*	*	*	*	*	*	*

WRDAT:
	SHLD	FRWPTR		; Save data ptr in case intr
	CALL	DWAIT		; Wait until the FDC is ready
WRDAT2:
	MVI	A,FWRITES	; Set up write sector command
	LHLD	FRWPTR		; .
	CALL	OUTCMD		; Send the command to the FDC
WR:
	IN	STAT		; Get FDC status
	RAR			; Test busy bit
	JNC	WRDONE		; Exit if no busy bit
	RAR			; Test DRQ bit
	JNC	WR		; Test status again if no DRQ
	MOV	A,M		; Busy & DRQ ==> OK to write data
	OUT	WDAT		; .
	INX	H		; Bump pointer to next byte
	JMP	WR		; Repeat as long as busy is active
WRDONE:
	DB	BIT,BTST+B1+ZA	; Test for lost data
	JNZ	WRDAT2		; Lost data -- Try write cmd again
WRNLD:
	ANI	02CH		; Test for WP, RNF, or CRC
	RZ			; All ok if zero
	LDA	INT$RETRIES	; Get # of internal retries left
	DCR	A		; Bump count one closer to doom!
	STA	INT$RETRIES	; Save count
	JP	WRDAT2		; Try again if 0, 1, or 2
	RET			; Otherwise return with NZ status


;	*	*	*	*	*	*	*	*
;
;  RDATA	Floppy disk read data routine
;
;  This routine reads the data from the currently selected floppy
;  into the area pointed to by the HL register.  This data is from
;  a read address or read data command.
;
;  The number of bytes read is determined by the 1770 controller
;  and the ID string currently active for this sector.
;
;  Multiple sector transfers are not supported.
;
;  NOTE:  The fastest way to test for busy and DRQ is to shift the
;  status bit right into the carry bit.  Therefore, when busy drops,
;  the ending status is still shifted right.  The checks for lost
;  data, record not found, and crc error must take this bit shift
;  into account.
;
;	*	*	*	*	*	*	*	*

RDATA:
	STA	FRWCMD		; Save command and data pointer
	SHLD	FRWPTR		; .  (in case we're interrupted)
	CALL	DWAIT		; Make sure FDC is ready
RDATA2:
	LDA	FRWCMD		; Get command and data pointer
	LHLD	FRWPTR		; .
	CALL	OUTCMD		; Send command to FDC
RD:
	IN	STAT		; Get FDC status
	RAR			; Test busy bit
	JNC	RDDONE		; Exit if no busy bit
	RAR			; Test DRQ bit
	JNC	RD		; Test status again if no DRQ
	IN	RDAT		; Busy & DRQ ==> OK to read data
	MOV	M,A		; .
	INX	H		; Bump pointer to next byte
	JMP	RD		; Repeat as long as busy is active
RDDONE:
	DB	BIT,BTST+B1+ZA	; Test for lost data
	JNZ	RDATA2		; Lost data -- try read again
RDNCD:
	ANI	0CH		; Test for RNF or CRC
	RZ			; All ok if zero
	LDA	INT$RETRIES	; Get # of internal retries left
	DCR	A		; Bump count one closer to doom!
	STA	INT$RETRIES	; Save count
	JP	RDATA2		; Try again if 0, 1, or 2
	RET			; Otherwise return with NZ status


;	*	*	*	*	*	*	*	*
;
;  OUTCMD	Issue a cmd to the 1770 floppy controller
;
;	*	*	*	*	*	*	*	*

OUTCMD:
	CALL	MOTOR		; Insure motor on
OC0:	OUT	CMND		; Send command to FDC
	MVI	A,19		; Wait 66.5 us for cmd to set up
OC1:	DCR	A		; .
	JNZ	OC1		; .
	RET


;	*	*	*	*	*	*	*	*
;
;  MOTOR	Start floppy  drive motor
;
;  This routine starts the target motor by executing a read address
;  command.  The xTRK, xSEC, and xDAT registers of the 1770 must be
;  saved as the read address command alters their contents.  After
;  the read address command, we have 2 seconds (10 index pulses) to
;  execute the next command before the motor shuts off.
;
;	*	*	*	*	*	*	*	*

MOTOR:
	PUSH	H		; Save buffer pointer
	PUSH	PSW		; .  and command
	IN	STAT		; Check motor on bit
	RAL			; .
	JC	MOTOROK		; Motor already on ...
	IN	RTRK		; Save 1770 trk, sec, & dta regs
	MOV	B,A		; .
	IN	RSEC		; .
	MOV	C,A		; .
	IN	RDAT		; .
	MOV	D,A		; .
	CALL	DWAIT		; Wait for the 1770 to get ready
	XRA	A		; Setup seek to same track cmd
	OUT	WTRK		; .
	OUT	WDAT		; .
	MVI	A,FSEEKNV	; .  (seek, no verify)
	OUT	CMND		; Send the cmd

	PUSH	B		; Save BC reg for wait cmd
	MVI	A,4		; Wait 4 * 250ms = 1 second
WAITMORE:
	PUSH	PSW		; Wait 250ms
	MVI	A,250		; .
	CALL	WAIT		; .
	POP	PSW		; .
	DCR	A		; More waiting?
	DB	JRNZ,WAITMORE-$-1 and 255
	POP	B		; Get BC reg back

	MOV	A,D		; Restore 1770 registers
	OUT	WDAT		; .
	MOV	A,C		; .
	OUT	WSEC		; .
	MOV	A,B		; .
	OUT	WTRK		; .

MOTOROK:
	POP	PSW		; Get cmd and buffer ptr back
	POP	H		; .
	RET			; and return

 	
;	*	*	*	*	*	*	*	*
;
;  DWAIT	Wait for permission to write a register
;
;  This routine will wait up to 5 seconds for permission to write
;  to one of the FDC registers.  After 5 seconds, a FORCE INTERRUPT
;  command will be issued to the FDC.
;
;	*	*	*	*	*	*	*	*

DWAIT:
	LXI	H,TIMEOUT	; Point to timeout location
	MVI	M,6		; Set 6 major loops

DLOOP:
	IN	STAT		; Get FDC status
	DB	BIT,BTST+B0+ZA	; Test bit 0 (BUSY), return with
	RZ			; .  zero status if busy non-active
	DCX	H		; See if enough minor loops
	MOV	A,H		; .  (Approx 58,000 times)
	ORA	L		; .
	JNZ	DLOOP		; Not done with minor loop
	LXI	H,TIMEOUT	; Decrement major loop counter
	DCR	M		; .  (6 times)
	JNZ	DLOOP		; .
	MVI	A,0D0H		; Give up on waiting; issue a FORCED
	OUT	CMND		; .  INTERRUPT to the FDC
	XRA	A		; Set A to 0FFH and status to NZ
	DCR	A		; .
	RET			; Return to caller


;	*	*	*	*	*	*	*	*
;
;  RESTORE	Restore a floppy drive to track 00
;
;  This routine issues a re-zero command to the 1770 which causes
;  the drive indicated by LOGDSK to slowly find its way back to 
;  the track zero (00) sensor.
;
;	*	*	*	*	*	*	*	*

RESTORE:			; Restore the disk head to track 00
	CALL	DWAIT		; Wait for the 1770 to be ready
        RNZ			; Return to caller if timeout
	CALL	GETSTEP		; Get the step rate for this drive
	ORI	FRESTOR		; Add restore command to step rate
	CALL	OUTCMD		; Send the cmd to the 1770
	CALL	DWAIT		; Wait for the cmd to finish
	MVI	A,50		; Wait 50 ms for the drive to settle
	CALL	WAIT		; .
	CALL	MAPTRK		; Set this drive's last track
	MVI	M,0		; .  to track 00

NEXT$BLIP:
	MVI	A,FRDADDR	; Send the READ ADDRESS cmd to the
	CALL	OUTCMD		; .  FDC and see if we get anything
	CALL	DWAIT		; Return if cmd finished (implies
	RZ			; .  both disk & drive present)
	MVI	C,BELL		; Beep if no disk
	CALL	CONOUT		; .
	CALL	CONST		; Check console status
	CNZ	CONIN		; Get char if one is there
	CPI	CTRLC		; Was the char a ctrl-C?
	DB	JRZ,UABORT-$-1	; Yes, abort with error
	CPI	ESC		; Was the char an ESC?
	JNZ	NEXT$BLIP	; No, try again
UABORT:
	XRA	A		; Return with error
	DCR	A		; .
	RET			; .


;	*	*	*	*	*	*	*	*
;
;  SEEK		Seek to track (floppy only)
;
;  Moves the floppy head to the track indicated by CPMTRK if not
;  already there.
;
;	*	*	*	*	*	*	*	*


SEEK:
	CALL	MOTOR		; Make sure the motor is on
	CALL	GETTRK		; Get track we want
	PUSH	PSW		; Save track #
	CALL	SETUP		; Set up unit and real side
	POP	PSW		; Get track # back
	CALL	MAPTYPE		; Check double step bit
	DB	BIT,BTST+B6+ZM	; .
	DB	JRZ,NODS1-$-1	; Skip double step if bit clear
	ADD	A		; Otherwise multiply by 2
NODS1:	
	OUT	WDAT		; Tell the 1770 where we want to go

	CALL	MAPTRK		; Get the current track #
	MOV	A,M		; .
	CALL	MAPTYPE		; Check double step bit
	DB	BIT,BTST+B6+ZM	; .
	DB	JRZ,NODS2-$-1	; Skip double step if bit clear
	ADD	A		; Otherwise multiply by 2
NODS2:
	OUT	WTRK		; Tell the 1770 where we are
	CALL	GETSTEP		; Get the step rate for this drive
	ORI	FSEEKNV		; OR in seek without verify cmd
	PUSH	PSW		; Save A reg as DWAIT destroys it
	CALL	DWAIT		; Wait until 1770 is ready
	POP	PSW		; Get A reg back (1770 cmd)
	CALL	OUTCMD		; Send the cmd
	CALL	DWAIT		; Wait for the cmd to finish
	ANI	18H		; Isolate possible failures
	RNZ			; Seek failed, return error

	MVI	A,FRDADDR	; Read current track info
	LXI	H,IDSAVE	; .
	CALL	RDATA		; .
	CALL	MAPTRK		; Get ptr to track save area
	CALL	GETTRK		; Get track we were seeking
	MOV	B,A		; Save it for a moment
	IN	RSEC		; Get track # from the FDC
	CMP	B		; Compare to what we wanted
	MOV	M,A		; Save FDC track # in either case
	RET			; Z=ok, NZ=error


;	*	*	*	*	*	*	*	*
;
;  GETSTEP	Get the step rate for a particular drive
;
;  Sets the HL register pair to the location which contains the 
;  step rate for the drive indicated by PHYDRV, and returns the
;  actual step bits in the A register.
;
;  Registers modified:	A, PSW, DE, HL
;
;	*	*	*	*	*	*	*	*

GETSTEP:
	LDA	PHYDRV		; Get physical drive unit
	CPI	04		; E-disk?
	DB	JRNZ,GET$AD-$-1	; No,  use normal unit #
	LDA	EDSD		; Yes, use E-disk unit #
GET$AD:
	MOV	E,A		; Get step rate from table
	MVI	D,0		; .  (based on physical unit)
	LXI	H,STPRAT	; .
	DAD	D		; .
	MOV	A,M		; .
	ANI	03H		; Mask off junk, just in case
	RET			; And return data in A and HL


;	*	*	*	*	*	*	*	*
;
;  MAPTYPE	Get pointer to physical type byte
;
;  Sets the HL register pair to the location which contains the 
;  type byte for the physical drive indicated by PHYDRV.
;
;  The format of the physical type bytes can be found in the 
;  description of DRIVE$TYPES at BIOS+070H.
;
;  Registers modified:	DE, HL
;
;	*	*	*	*	*	*	*	*

MAPTYPE:
	PUSH	PSW		; Save entry A reg and flags
	LDA	PHYDRV		; Get physical drive unit
	MOV	E,A		; Compute pointer to physical
	MVI	D,0		; .  type byte
	LXI	H,DRIVE$TYPES	; .
	DAD	D		; .
	POP	PSW		; Get A reg and flags back
	RET			; And return with ptr in HL


;	*	*	*	*	*	*	*	*
;
;  MAPTRK	Return pointer to current track
;
;  Sets the HL register pair to the location which contains the 
;  current track for the drive indicated by PHYDRV.
;
;  Registers modified:	A, PSW, DE, HL
;
;	*	*	*	*	*	*	*	*

MAPTRK:
	LDA	PHYDRV		; Get physical drive unit
	CPI	04		; E-disk?
	DB	JRNZ,MAP$AD-$-1	; No,  use normal unit #
	LDA	EDSD		; Yes, use E-disk unit #
MAP$AD:
	MOV	E,A		; Compute ptr to current track
	MVI	D,0		; .
	LXI	H,LTRACK	; .
	DAD	D		; .
	RET			; Return with pointer in HL


;	*	*	*	*	*	*	*	*
;
;  SETUP	Setup the system control register for a drive select
;
;  This routine writes to the system control port register at I/O
;  00H, causing the associated drive to be selected, along with
;  the head select signal line.
;
;  NOTE:  This routine always turns the internal system rom OFF,
;  without any regard for those who just may have the rom enabled
;  prior to entry.
;
;  NOTE:  This routine modifies all registers
;
;	*	*	*	*	*	*	*	*

SETUP:
	CALL	MAPTYPE		; Get pointer to drive type byte
	MOV	A,M		; Get byte
	ORI	040H		; Turn off EPROM
	LXI	H,HSTSID	; Point to side select
	ORA	M		; Include proper side select

	MOV	C,A		; Save control byte
	ANI	1000$1111B	; Mask out eprom, density, & side
	LXI	H,CHGDSK	; Compare with previous results
	CMP	M		; .
	MOV	M,A		; .  (save this result anyway)
	DB	JRZ,SETUP2-$-1	; Skip split & delay if same

	MOV	A,C		; Get control byte back
	ANI	0F0H		; Mask out drive bits
	OUT	CONT		; Send speed only to control port
	MOV	A,C		; Get control byte back
	OUT	CONT		; Send full byte to the control port

	MVI	A,HLDELAY	; Delay HLDELAY ms (usually 30ms)
	CALL	WAIT		; .
	RET
SETUP2:
	MOV	A,C		; Get control byte back
	OUT	CONT		; Send to control port
	RET


;	*	*	*	*	*	*	*	*
;
;  WAIT		Wait "A" ms
;
;  Modifies:	A,PSW  (A = 0, Z flag set)
;
;	*	*	*	*	*	*	*	*

WAIT:
	PUSH	B
WAIT1:
	MVI	B,199
WAIT2:	CMP	M
	DB	10H,WAIT2-$-1 and 255	; DJNZ WAIT1
	DCR	A
	DB	JRNZ,WAIT1-$-1 and 255	; JRNZ WAIT2
	POP	B
	RET


;	*	*	*	*	*	*	*	*
;
;  GETTYPE	Get the type byte and ptr for a drive unit
;
;  Returns the current disk type identifier in the A reg, and the
;  address of the entry in the HL reg pair.
;
;  See PHYTAB (driver table) for the definition of the type byte.
;
;  Modifies:	A, DE, HL
;
;	*	*	*	*	*	*	*	*

GETTYPE: 
	LDA	LOGDSK		; Use logical disk entry value
	CALL	PAGET		; Get ptr to table entry
	INX	H		; Type byte is at table+2
	INX	H		; .
	MOV	A,M		; Get type byte
	RET			; Return type in A, ptr in HL


GETSEC: 		;CONVERT LOGICAL SECTOR TO PHYSICAL SECTOR
	PUSH	H
	CALL	GETTYPE
	ANI	3		; 128 BYTE SECTOR?
	LDA	CPMSEC		;NOPE, DO MAPPING
	JZ	GOTSDS
	LDA	HSTSEC
GOTSDS:
	PUSH	PSW		;SAVE SECTOR
	CALL	GETDPT		;FETCH POINTER TO XLT TABLE
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG			;TRANSLATE TABLE NOW IN HL
	POP	PSW		;RESTORE DESIRED SECTOR
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	A,M		;GET PHYSICAL SECTOR FROM TABLE
	MOV	E,A		;SAVE IT
	LDA	PHYDRV
	CPI	04H		;WAS IT E DISK?
	DB	JRNZ,GSEXIT-$-1	;NO, THEN NO ADJUST
	LDA	HSTSID		;WHICH SIDE?
	ORA	A
	JZ	GSEXIT		;SIDE ZERO, IGNORE
	LDA	ESECADJ 	;ELSE GET ADJUSTMENT
	ADD	E
	MOV	E,A		;PUT IT BACK FOR MOVE
GSEXIT:
	MOV	A,E		;MOVE REAL SECTOR TO A
	POP	H
	RET

GETBUF: 		;GET THE DMA ADDRESS
	CALL	GETTYPE
	ANI	3		; 128 BYTE SECTORS?
	LXI	H,HSTBUF	;USED FOR DD
	RNZ
	LHLD	DMAADR		;ELSE USE REAL DMA ADDRESS...
	RET

;	*	*	*	*	*	*	*	*
;
;  GETTRK	Convert logical to physical track
;
;  This routine converts the logical track stored in CPMTRK to
;  the actual physical track and side to be used.
;
;  The physical track is returned in the A register, and the side
;  to be used is returned in HSTSID.
;
;	*	*	*	*	*	*	*	*

GETTRK:	PUSH	H		; Save HL register
	CALL	GETTYPE		; Get the type byte for this disk
	LXI	H,HSTSID	; Point to the side flag
	RAL			; Determine SS or DS
	RAL			; .
	LDA	CPMTRK		; Get track #
	JNC	GETTRK1		; Not DS -- leave track alone
	RAR			; Divide track by 2 to get side
	JNC	GETTRK1		; No carry means side 0
	MVI	M,10H		; Indicate side 1
	JMP	GETTRK2		; And finish up
GETTRK1:
	MVI	M,0		; Indicate side 0
GETTRK2:
	POP	H		; Get HL register back
	ANI	07FH		; Mask high bit, just in case
	RET			; ... and return


;	LOGICAL DEVICE	  PHYSICAL DEVICE ASSIGNMENTS
;	--------------	  ---------------------------
;	CON:		  CRT: OR TTY:
;	READER: 	  TTY:
;	PUNCH:		  TTY:
;	LIST:		  CRT: OR TTY: OR LPT:


CONST:	;CONSOLE STATUS, RETURNS 0FFH IF CHARACTER READY, ELSE 00H
 
	LDA	IOBYTE		;CHECK DEVICE ASSIGNMENT
	ANI	11B		;KEEP CON BITS ONLY
	JZ	TTYIST		;CHECK APPROPRIATE INPUT STATUS
	JMP	CRTIST		;NO THIRD OR FOURTH CHOICES

CONIN:	;CONSOLE CHARACTER INTO REGISTER A
	LDA	IOBYTE
	ANI	11B
	JZ	TTYIN
	JMP	CRTIN

CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C
	LDA	IOBYTE
	ANI	11B
	JZ	TTYOUT
	JMP	CRTOUT

READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE
	JMP TTYIN		;NO CHOICES SUPPORTED

PUNCH:	;PUNCH CHARACTER FROM REGISTER C
	JMP TTYOUT		;NO CHOICES SUPPORTED

LISTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H
	LDA	IOBYTE		;CHECK DEVICE ASSIGNMENT
	ANI	11000000B	;KEEP LST BITS ONLY
	JNZ	LSTST1		;TTY?
	JMP 	TTYOST		;CHECK TTY OUTPUT STATUS
LSTST1:	ANI	01000000B	;CRT?
	JZ	LPTST		;NO OTHER CHOICE BUT LPT
	JMP	CRTOST		;CHECK CRT OUTPUT STATUS

LIST:	;LIST CHARACTER OUTPUT FROM REGISTER C
	CALL	LISTST		;WAIT TILL READY TO SEND
	JZ	LIST
	LDA	IOBYTE		;CHECK DEVICE ASSIGNMENT
	ANI	11000000B	;KEEP LST BITS ONLY
	JZ	TTYOUT
	CPI	01000000B
	JZ	CRTOUT
	JMP	LPTOUT		;NO FOURTH CHOICE

CRTOST: ; CRT OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT
	; CRT "ALL SENT" STATUS NOW CHECKED

	; Note:  This former routine (IN SIOCPA   ANI TBE   RZ) requires
	; autoenables to be set to function correctly.  The present
	; routine uses the "ALL SENT" bit to determine if the previous
	; byte has been sent from the DART.  Under the previous routine,
	; there is a possibility of a character getting into the output
	; buffer when there is no ready signal.

	MVI	A,01H		; Check "all sent" bit in register 1
	OUT	SIOCPA		; .
	IN	SIOCPA		; .
	ANI	01H		; "ALL SENT" is bit 0
	RZ			;TRANSMIT BUFFER NOT READY
	LDA	HSA		;SEE IF CTS H/S REQUIRED
	ORA	A		
	JZ      CRTRDY		;IF 0, NO H/S NEEDED
	;CRT CTS HANDSHAKE SIGNAL STATUS NOW CHECKED
	MVI	A,10H
	OUT	SIOCPA		;UPDATE UART STATUS
	IN	SIOCPA		;FETCH STATUS
	ANI	CTS
	RZ			;CTS NOT ACTIVE
CRTRDY:	ORI	255		;SHOW READY TO END
	RET

CRTIST: ;CRT INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT
	IN	SIOCPA		;FETCH STATUS
	ANI	RDA
	RZ			;NOT READY
	ORI	255		;GOT SOMETHING, SIGNAL ITS PRESENCE
	RET

CRTOUT:	CALL    CRTOST  	;OK TO SEND?
        JZ	CRTOUT		;NOPE, WAIT
	MOV	A,C		;CHARACTER TO REGISTER A
	OUT	SIODPA
	RET

CRTIN:	CALL	CRTIST		;READY?
	JZ	CRTIN		;NOPE, WAIT
	IN	SIODPA		;ELSE FETCH IT
	ANI	7FH		;STRIP PARITY BIT
	RET

LPTOUT: ;PRINTER CHARACTER OUTPUT FROM REGISTER C
	CALL	LPTST		;PRINTER READY?
	JZ	LPTOUT		;NOPE, WAIT
	MOV	A,C
	OUT	PIO1		;SET UP THE DATA
	OUT	STBSET		;SEND A DATA STROBE
        OUT     STBCLR          ;  (DATA DOESN'T MATTER)
	RET

LPTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H
	MVI	A,10H		;UPDATE UART STATUS
	OUT	SIOCPB
	IN	SIOCPB		;READ PRINTER BUSY SIGNAL
	ANI	10H
	RZ			;NOT READY
	ORI	255		;SHOW READY
	RET

TTYOST: ;TTY OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT
	;TTY "ALL SENT" STATUS NOW CHECKED

	; Note:  This former routine (IN SIOCPB   ANI TBE   RZ) requires
	; autoenables to be set to function correctly.  The present
	; routine uses the "ALL SENT" bit to determine if the previous
	; byte has been sent from the DART.  Under the previous routine,
	; there is a possibility of a character getting into the output
	; buffer when there is no ready signal.

	MVI	A,01H		; Check "all sent" bit in register 1
	OUT	SIOCPB		; .
	IN	SIOCPB		; .
	ANI	01H		; "ALL SENT" is bit 0
	RZ			;TRANSMIT BUFFER NOT READY
	LDA	HSB		;SEE IF CTS H/S REQUIRED
	ORA	A		
	JZ      TTYRDY		;IF 0, NO H/S NEEDED
	;TTY CTS HANDSHAKE SIGNAL STATUS NOW CHECKED
	MVI	A,10H
	OUT	SIOCPB		;UPDATE UART STATUS
	IN	SIOCPB		;FETCH STATUS
	ANI	CTS
	RZ			;CTS NOT ACTIVE
TTYRDY:	ORI	255		;SHOW READY TO SEND
	RET

TTYIST: ;TTY INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT
	IN	SIOCPB		;FETCH STATUS
	ANI	RDA
	RZ			;NOT READY
	ORI	255		;GOT SOMETHING, SIGNAL ITS PRESENCE
	RET

TTYOUT:	CALL	TTYOST		;OK TO SEND?
	JZ	TTYOUT		;NOPE, WAIT
	MOV	A,C		;CHARACTER TO REGISTER A
	OUT	SIODPB
	RET

TTYIN:	IN	SIOCPB
	ANI	RDA		;CHAR READY?
	JZ	TTYIN		;NOPE, WAIT
	IN	SIODPB		;ELSE FETCH IT
	ANI	7FH		;STRIP PARITY BIT
	RET

MAINEND	EQU	$		; End of main body of code

	IF	HARD$DISK

;	ORG	($+127)/128*128	; ORG HD stuff on sector boundry
	
;	*	*	*	*	*	*	*	*
;
;	HARD DISK Disk parameter blocks
;
;	A system configuration entry, or a user defined assembly
;	is to be used to set up the track offset entry to create
;	various hard disk partitions. This is done to support drives
;	with greater than 20Mbyte storage.
;
;	*	*	*	*	*	*	*	*

;		# of  --mask--  disk  # of   rsv'd  chk  trk
;		sect  bs bm em  size  d ent  block  siz  ofs  x

HD$DPB$BASE:	EQU	$

FPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
GPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
HPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
IPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
JPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
KPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
LPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
MPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
NPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
OPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0
PPARM:	DB	64,0,  5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0

HD$DPB$LEN:	EQU	$-HD$DPB$BASE


DRVR3:
;	*	*	*	*	*	*	*	*
;
;  Hard disk driver (Driver #3)
;
;	FF	Select Disk
;	00	Write Disk
;	01	Read Disk
;
;	*	*	*	*	*	*	*	*

	PUSH	PSW		; Save command
	LXI	H,TAGPHY	; Get address & step rate
	MOV	A,M		; .
	ANI	0FH		; Mask off address
	STA	STEP$RATE	; Store step rate in SCSI cmd
	INX	H		; Bump ptr to unit #
	MOV	A,M		; Get unit # & type 
	ANI	0E0H		; Mask off type
	STA	LOGUNIT		; Save unit #
	INX	H		; Bump ptr to SCSI addr
	MOV	A,M		; Get SCSI address
	STA	TARGET		; Save as target address
	POP	PSW		; Restore command

	CPI	0FFH		; Select command?
	JNZ	SCSI$IO		; If not, use common R/W
	JMP	SELEND		; Otherwise finish select code


;	*	*	*	*	*	*	*	*
;
;  DIRECT SCSI DRIVER  
;
;  Entry here via the BIOS Jump tables as JMP SCSI
;
;  Enter with the following registers set:
;
;	A   SCSI target address to be used
;	HL  Pointer to SCSI Command block
;	DE  Pointer to SCSI Data Block
;
;  Exits with HL preserved.  DE is preserved if the operation is 
;  successful, otherwise DE contains the sense data (error bytes).
;  The A regisner contains the ending status of the SCSI command.
;
;  NOTE: This routine saves and restores the previous TARGET
;	address for normal operations.
;
;	*	*	*	*	*	*	*	*

SCSI:
	MVI	C,1		; Set C register for 1 byte block
SCSI$HSPEED:
	STA	TARGET		; Save target address
 	PUSH	H		; Save entry command pointer
	PUSH	D		; .  and entry data pointer
	SHLD	CMDPTR		; Set CMDPTR to what HL is pointing to
	XCHG			; Set the data pointer to HL

	CALL	SCSI$CMD	; Do the direct routines

	JZ	SCSI$END$OK	; If ok, restore cmd & data ptrs
	POP	H		; Otherwise, throw away data ptr
	JMP	SCSI$END$ERR	; .  by popping cmd pointer twice
SCSI$END$OK:
	POP	D		; Restore data pointer
SCSI$END$ERR:
	POP	H		; Restore command pointer
	RET			; All done


;
;  SCSI return sense data command (Cmd 03)
;

SCSI$STAT$CMD:
  	DB 	3		; 00 - REQUEST SENSE COMMAND
  	DB 	0		; 01 - LOGICAL UNIT
  	DB 	0		; 02 - RESERVED
  	DB 	0		; 03 - RESERVED
  	DB 	4		; 04 - NUMBER OF BYTES
  	DB 	0		; 05 - RESERVED

;
;  SCSI read/write command (Cmd 08/0A)
;

SCSI$RD$CMD:	EQU	08H		; 08 IS READ DATA
SCSI$WR$CMD:	EQU	0AH		; 0A IS WRITE DATA

SCSI$RW$CMD:	DB 	SCSI$RD$CMD	; 00 - 08=Read, 0A=Write
HIGH$ADDR: 	DB 	0		; 01 - High address
MED$ADDR:	DB 	0		; 02 - Middle address
LOW$ADDR:	DB 	0		; 03 - Low address
  		DB 	1		; 04 - Number of sectors
STEP$RATE:	DB 	0		; 05 - Step rate (Xebec)


;	*	*	*	*	*	*	*	*
;
;  SCSI READ/WRITE HOST ROUTINES
;
;  Entry here from the HOSTIO section of the blocking/deblock
;  routines. Assumes that the TARGET address has been pre-set.
;
;  Exits with the error code in (A), and the sense status saved
;  at SCSI$STAT$DAT if an error occurs.
;
;	*	*	*	*	*	*	*	*

SCSI$IO:
  	LDA 	RWHOST
	CPI 	0
  	JZ 	SCSI$WR

;
;  Read from the hard disk described by (TARGET) and (LOGUNIT)
;

SCSI$RD:
	MVI	A,SCSI$RD$CMD	; Set read command
	JMP 	SCSI$DO$RW

;
;  Write to the hard disk described by (TARGET) and (LOGUNIT)
;

SCSI$WR:
	MVI	A,SCSI$WR$CMD	; Set write command 

;
;  Do the rest of the setup for the read/write command
;

SCSI$DO$RW: 
	LXI	H,SCSI$RW$CMD	; Get command string
	MOV	M,A		; Plug the SCSI command 
	SHLD	CMDPTR		; Save the command pointer
	CALL	BLD$SCSI$SCTR	; Build SCSI sector address
  	LXI 	H,HSTBUF	; Get pointer to host buffer
	LDA	HD$BYTE$BLOCK	; Get SCSI byte/block mode value
	MOV	C,A		; Stuff in C register

;
;  Enter here from the SCSI direct driver routine
;
;  Exits with status in A.  0FFH = timeout error
;

SCSI$CMD:
	SHLD 	DATPTR 		; Save the data pointer
	LXI	H,SCSI$IO$COUNT	; And the # of bytes to transfer
	MOV	M,C		; .  at one time (each block)
	CALL 	SELECT 		; Perform the SCSI operation
	LDA 	STATUS		; Get the return status
	CPI	0FFH		; Timeout?
	JZ	SCSI$DONE	; Yes, go save timeout status
	ANI 	2		; Check for SCSI error status
	JZ	SCSI$DONE	; No error -- finish up
	PUSH	PSW		; Save previous command ending status
	LXI	H,SCSI$STAT$CMD	; Set up data and command pointers
	LXI	D,SCSI$STAT$DAT	; .  for request sense command
	LDA	LOGUNIT		; Get logical unit number
	INX	H		; Stuff in proper location in
	MOV	M,A		; .  SCSI command
	DCX	H		; .
	LDA	TARGET		; Reclaim target address
	CALL	SCSI		; Execute request sense command
	POP	PSW		; Get previous command status back
SCSI$DONE:
	STA 	ERFLAG		; Save ending status
	ORA	A		; Set Z/NZ for user
	RET			; and return

;	*	*	*	*	*	*	*	*
;
;  Build 3-byte SCSI sector number and logical unit number
;
;  NOTE:  This routine assumes 16 sectors per track.
;
;  It is safe to assume 16 sectors per track even though some hard
;  disk controllers format more (Xebec=17, Adaptec=18, etc.) as the
;  read and write commands use a block number, rather than a track
;  and sector number to move to the correct block to read or write.
;
;	*	*	*	*	*	*	*	*

BLD$SCSI$SCTR:

	XRA	A		; Clear A & Carry

				; A reg     C  HL reg pair
	LHLD	CPMTRK		; --------  -  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----
	MOV	B,A		; 
	LDA	LOGUNIT		; 
	ORA	B		; Or in logical unit number
	STA	HIGH$ADDR	; Save unit number & high byte
	MOV	A,H		; 
	STA	MED$ADDR	; Save middle byte
	LDA	HSTSEC		; Get sector number
	ORA	L		; Or in low byte
	STA	LOW$ADDR	; Save new low byte
	RET	


;
;  Select controller, and fall through to phase if selected ok.
;

busbsy:	equ	40h

SELECT:
	ENDIF
	IF	HARD$DISK AND ARBITRATION
	xra	a
	out	ncricr		; Clear initiator command register
	out	ncrmr		; .  and mode register
	out	ncrtcr		; .  and target command register

clear$arbit:
	in	ncrmr		; Reset arbitration bit
	ani	0feh		; .
	out	ncrmr		; .

arbitrate:
	lda	my$id		; Assert my ID (the initiator)
	out	ncrodr		; .
	in	ncrmr		; Set arbitration bit
	ori	01h		; .
	out	ncrmr		; .

in$progress:
	in	ncricr		; Wait for "arbitration in 
	ani	40h		; .  progress" bit
	jz	in$progress
	nop			; Arbritration delay
	nop

	in	ncricr		; Check for lost arbitration
	ani	20h		; .
	jnz	clear$arbit	; We lost -- start over

	lda	my$id
	mov	b,a
	in	ncrcsd		; See if we're the highest priority
	sub	b		; .  remove my addr
	sub	b		; .  compare my addr to bus data
	jm	i$win		; We win if result < 0
	jmp	clear$arbit	; .  otherwise we lose -- start over

i$win:
	in	ncricr		; Check again for lost arbitration
	ani	20h		; .  (just in case)
	jnz	clear$arbit	; We lost -- start over

	mvi	a,0ch		; Assert SEL and BSY
	out	ncricr		; .
	nop			; Wait 1 bus clear delay

	lda	my$id		; Select target: get our ID,
	mov	b,a		; .
	lda	target		; .  or in target ID
	ora	b		; .
	out	ncrodr		; .  and send to NCR chip

	mvi	a,0dh		; Assert data bus
	out	ncricr		; .  (along with SEL & BSY)

	in	ncrmr		; Reset arbitration bit
	ani	0feh		; .
	out	ncrmr		; .

	mvi	a,05h		; Release BSY, keep SEL	
	out	ncricr		; .  and assert data bus
	nop

	lxi	b,6000h		; 250 ms loop (1M cycles)
stim:
	in	ncrcsbs		; Wait for BSY
	ani	busbsy		; .
	jnz	select$ok	; Got him!

	dcr	c
	jnz	stim		; inner loop:  41*256 = 10496 cycles
	dcr	b
	jnz	stim		; outer loop: 10510*96 = 1M cycles

	xra	a		; Select timeout -- clear bus
	out	ncrodr
	mvi	c,20		; 200 us loop
last$chance:
	in	ncrcsbs		; Wait for BSY
	ani	busbsy		; .
	jnz	select$ok	; Just in time!

	mvi	a,0ffh		; Select failed -- set error status
	jmp	all$done	; and clear the registers

select$ok:
	xra	a		; Set good status
	ENDIF

	IF	HARD$DISK AND (NOT ARBITRATION)
	xra	a
	out	ncrtcr		; Clear target cmd register
	lda	target
	out	ncrodr		; Set target ID
	mvi	a,01h
	out	ncricr		; Enable data bus
	mvi	a,05h
	out	ncricr		; Enable select, also
	push	d		; Save data pointer
	lxi	d,1000h		; Set time out counter
wait$bsy$loop:
	dcx	d		; We only wait so long ...
	mov	a,d		; .
	ora	e		; .
	jz	wait$timeout	; Too late -- return timeout status
	in	ncrcsbs		; Get current SCSI bus status
	ani	ncrbsy		; Check for BUSY status
	jz	wait$bsy$loop	; Not BUSY yet -- try again
	pop	d		; Restore data pointer
	xra	a
	jmp	all$done	; Selected ok -- clean up
wait$timeout:
	pop	d		; Restore data pointer
	dcr	a		; Was zero, now make 0FFH
	sta	status		; Save in current status
	ENDIF
	IF	HARD$DISK	; continue hard disk code

all$done:
	sta	status		; Save status for PWIDIR routine
	push	psw
	mvi	a,01h		; Release SEL
	out	ncricr		; .
	xra	a		; Release data bus
	out	ncricr		; .
	pop	psw		; Get status back
	ora	a		; Set status
	rnz

; * * * * *  
; *  --------\	NOTE: we fall through to PHASE if we
; *  --------/	successfully selected the controller!!
; * * * * *

;
;  Next SCSI bus phase operator
;

PHASE:
  	XRA 	A 
	OUT 	NCRMR		; Clear mode register
	OUT 	NCRICR		; .  and initiator command register

;  Wait for REQ active, watch for BSY loss.
;  The new phase is not valid until REQ occurs.

PH1:
  	IN 	NCRCSBS		; Get current bus status
	MOV 	B,A		; .  (Save in B)
	ANI 	NCRBSY		; Make sure we still have BSY
  	RZ			; If BSY drops, get out.
    	MOV 	A,B		; .  (Get status back from B)
    	ANI 	NCRREQ		; REQ active?
    	JZ 	PH1		; No -- wait for REQ active
      	MOV 	A,B		; Update phase
      	ANI 	00011100B	; Mask all but phase bits
      	RAR			; Rotate over for target
	MOV	E,A		; . (Save for use with jump table)
      	RAR			; .
      	ANI 	0FH		; Mask off carry, just in case
      	OUT 	NCRTCR		; Set phase
      	MOV 	A,B		; Get status back from B again
      	MVI 	D,0		; E is already set (6 ins ago)
      	LXI 	H,PHASE$TABLE	; Get phase jump table base
      	DAD 	D		; Add offset for this phase
	MOV	A,M		; Get phase pointer into HL
	INX	H		; .
	MOV	H,M		; .
	MOV	L,A		; Pointer is now together
      	PCHL			; Go to it!

PHASE$TABLE:
  	DW	PHASE0
  	DW	PHASE1
	DW	PHASE2
  	DW	PHASE3
	DW 	PHASE4
	DW	PHASE5
	DW	PHASE6
	DW	PHASE7


PHASE0:				; Data out phase ...
	LXI	H,SCSI$IO$COUNT	; Point to byte/block transfer mode
	MOV	E,M		; Get transfer mode value
  	LHLD 	DATPTR		; Use data pointer
  	JMP 	WSCSI		; Execute SCSI write routine

PHASE1:				; Data in phase ...
	LXI	H,SCSI$IO$COUNT	; Point to byte/block transfer mode
	MOV	E,M		; Get transfer mode value
  	LHLD 	DATPTR		; Use data pointer
  	JMP 	RSCSI		; Execute SCSI read routine

PHASE2:				; Command out phase ...
  	LHLD 	CMDPTR		; Use command pointer
  	MVI 	E,1		; Set mode to byte transfer
  	JMP 	WSCSI		; Execute SCSI write routine

PHASE3:				; Status in phase ...
  	LXI 	H,STATUS	; Use status pointer
  	MVI 	E,1		; Set mode to byte transfer
  	JMP 	RSCSI		; Execute SCSI read routine

PHASE7:				; Message in phase ...
  	LXI 	H,MESSAGE	; Use message pointer
  	MVI 	E,1		; Set mode to byte transfer
  	JMP 	RSCSI		; Execute SCSI read routine

; Currently unused phases

PHASE4:
PHASE5:
PHASE6:
  	RET

; Generalized SCSI write routine

WSCSI:
  	MVI 	A,1		; Assert data bus
  	OUT 	NCRICR
  	MVI 	A,00000010B	; Set DMA mode
  	OUT 	NCRMR		
  	MVI 	D,01000000B	; DMA request mask (for use below)
  	OUT 	NCRSDS		; Start DMA send

; Wait for DMA request, keeping an eye on phase.  Note that the NCR
; will not issue an ACK, nor will it generate DMA requests once the
; phase changes, so it is best to treat DMA request checking as a
; higher priority than phase change checking.

WSCSI1:
	MOV	A,E		; Are we in byte mode (CMD XFER)?
	ORA	A		; .
	JNZ	WSCI11		; Yes, HL = cmd string pointer
	SHLD	DATPTR		; Save (new) data ptr for multi-sector
WSCI11:
  	IN 	NCRBSR
  	MOV 	C,A		; Save status for use below
  	ANA 	D          	; Check for DMA request
  	JZ 	WSCSI2

; This is the heart of the pseudo-DMA transfer.  On entry, HL points
; to the data buffer and B should be 0 for 256 byte block transfer,
; or 1 for byte-by-byte transfer.  NOTE: Use block transfer only if
; you can be sure the controller can buffer 256 bytes of data and
; can transfer at 5.25 us per byte.  Extra DACK's after the last REQ
; will do no harm.
;
; OTIR register use: H = memory pointer, C = I/O port, B = counter
; 
    	MOV 	B,E		; Set up loop count
    	MVI 	C,NCRDACK	; Set up destination port address
    	DB  	0EDH   		; ED B3 = OTIR instruction op code
    	DB  	0B3H
    	JMP 	WSCSI1		; Write more bytes until phase changes

; This code skipped when data is being transferred ...
WSCSI2:
    	MOV 	A,C		; Check phase
    	ANI 	00001000B       ; .
    	JNZ 	WSCSI1		; .
    	JMP 	PHASE		; Exit when phase changes


; Generalized SCSI read routine

RSCSI:
; INITIATOR COMMAND REG IS ALREADY INITIALIZED, BY PHASE
	MVI 	A,00000010B	; Set DMA mode
	OUT 	NCRMR		
	MVI 	D,01000000B	; DMA request mask (for use below)
	OUT 	NCRSDIR		; Start DMA initiator receive

; Wait for DMA request, keeping an eye on phase.  Note: we must do
; a check for DMA request before checking for a phase change, since
; a byte may be queued up waiting to be DACKed prior to the phase
; change.

RSCSI1:
	SHLD	DATPTR		; Save (new) data ptr for multi-sector
  	IN 	NCRBSR
  	MOV 	C,A		; Keep for phase change checking
  	ANA 	D          	; Mask for DMA request
  	JZ 	RSCSI2

; This is the heart of the pseudo-DMA transfer.  On entry, HL points
; to the data buffer and B should be 0 for 256 byte block transfer,
; or 1 for byte-by-byte transfer.  NOTE: Use block transfer only if
; you can be sure the controller can buffer 256 bytes of data and
; can transfer at 5.25 us per byte.  Extra DACK's after the last REQ
; will do no harm.
;
; INIR register use: H = memory pointer, C = I/O port, B = counter
; 
    	MOV 	B,E		; Set up loop count
    	MVI 	C,NCRDACK	; Set up source port address
    	DB  	0EDH   		; ED B2 = INIR instruction op code
    	DB  	0B2H

    	JMP 	RSCSI1		; Read more bytes until phase changes
				; Be sure and check phase if no DMA
				; request, since NCR won't issue any
				; unneeded DACKs

; This code skipped when data is being transferred ...
RSCSI2:
	MOV 	A,C		; Check phase
	ANI 	00001000B     	; .
	JNZ 	RSCSI1		; .
	JMP 	PHASE 		; Exit when phase changes

	ENDIF

HDCODE	EQU	$		; End of hard disk code

;	*	*	*	*	*	*	*	*
;
;	COLD BOOT ENTRY
;
;	NOTE: The following code does not stay resident. It is 
;	overlayed for use by all bios system variables.
;
;	*	*	*	*	*	*	*	*

BOOT:
	MVI	A,41H		; Turn off EPROM, but leave drive 0
	OUT	CONT		; . selected (1770 turns it off)
	LDA	IOBYT		; Setup initial IOBYTE value
	STA	IOBYTE		; .
	LXI	SP,80H		; Initialize DART, CTC, etc.
	CALL	IOINIT		; .

	IF	HARD$DISK	; Init HD parameters, if present
	XRA	A		; Set SCSI byte/block mode to block
	STA	HD$BYTE$BLOCK	; .

	IN	029h		; Get ID value
	ANI	07H		; Mask off extra bits
	PUSH	PSW		; . (Put ID in inital message)
	ADI	'0'		; . .
	STA	SCSI$ID$FOUND	; . .
	POP	PSW		; . .
	INR	A		; Bump to make 1 - 8
	MOV	B,A		; Convert to SCSI address
	XRA	A		; .  (Clear receiving register)
	STC			; .  (Carry = bit to rotate)
NEXT$BIT:			; .
	RAL			; .  (Rotate left through carry)
	DCR	B		; .  (Decrement counter)
	JNZ	NEXT$BIT	; .  (Not done -- repeat)
	STA	MY$ID		; Save ID value

	CPI	1000$0000B	; Perform SCSI reset if this is ID # 7
	DB	JRNZ,NOT7-$-1	; .
	MVI	A,80H		; Set reset line high
	OUT	NCRICR		; .
HOLD$RESET:
	DCR	A		; Hold reset high for at least 50us
	DB	JRNZ,HOLD$RESET-$-1 and 255
	OUT	NCRICR		; Clear reset line
	IN	NCRRPI		; .  and interrupt line

NOT7:
  	XRA 	A		; Clear 5380 registers (SCSI init)
  	OUT 	NCRICR		; .
  	OUT 	NCRMR		; .
  	OUT 	NCRTCR		; .
  	OUT 	NCRSER		; .
	ENDIF			; End of HD initialization

	LXI	H,LOGMSG	; Display signon message
	CALL	PUTS	


	IF	ZCPR3		; Init ZCPR3 stuff if present

	LXI	H,LAST$SG	; Set up HL and DE to clear memory
	LXI	D,LAST$SG+1	; from the end of boot to 0ffffh
	MVI	M,0
	MOV	A,H
	CMA
	MOV	B,A
	MOV	A,L
	CMA
	MOV	C,A
	DW	LDIR80

ZBOOT:	LXI	B,3		; Set up the ZCPR3 command line
	LXI	H,CMDSET	; .  pointers
	LXI	D,Z3CL		;
	DW	LDIR80		;

	LXI	B,10		; Move the automatic command to
	LXI	H,AUTOCMD	; .  the ZCPR3 command line
	LXI	D,Z3CL+3	;
	DW	LDIR80		;

	LXI	B,11		; Move the initial path descriptor
	LXI	H,PATH		; .  to the proper location
	LXI	D,EXPATH	;
	DW	LDIR80		;

	LXI	H,Z3WHL		; Turn the wheel byte on
	MVI	M,0FFH		;

	LXI	H,ENV		; Move environment and TCAP to the
	LXI	D,Z3ENV		; .  proper location
	LXI	B,ENVEND-ENV+1	; .
	DW	LDIR80		;
	ENDIF			; ZCPR3 init

	XRA	A		; 
	STA	CDISK		; Indicate disk 0 selected
	STA	HSTACT		; Set host buffer inactive
	STA	UNACNT		; Clear unalloc count
	STA	HSTSID		; Assume side zero
	JMP	GOCPM		; Initialize & jump to CP/M

LOGMSG:	DB	CR,LF,LF
	DB	MSIZE/10 +'0',MSIZE MOD 10 +'0','k CP/M vers 2.2'
        DB      CR,LF,'AMPRO BIOS VERS ',VERS/10+'0','.'
	DB	VERS MOD 10+'0'

	IF	INTERNAL	; Display internal revision #
	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,'COPYRIGHT (C) 1983,1984,1985 '
	DB	'AMPRO COMPUTERS, INC.'

	IF	HARD$DISK	; Display SCSI ID for this unit
	DB	CR,LF,LF,'SCSI initiator ID = '
SCSI$ID$FOUND:
	DB	'x'
	ENDIF

	DB	CR,LF,0

	IF	ZCPR3		; Include ZCPR3 definitions?
ENV:
	JMP	0		; Leading JMP
ENV1:				; ZCPR3 enviornment descriptor ...
	DB	'Z3ENV'		; . Environment ID
	DB	1		; . Class 1 environment (external)
	DW	EXPATH		; . External path (PATH)
	DB	EXPATHS		; 
	DW	RCP		; . Resident command package (RCP)
	DB	RCPS		; 
	DW	IOP		; . Input/output package (IOP)
	DB	IOPS		; 
	DW	FCP		; . Flow command package (FCP)
	DB	FCPS		; 
	DW	Z3NDIR		; . Named directories (NDR)
	DB	Z3NDIRS		; 
	DW	Z3CL		; . Command line (CL)
	DB	Z3CLS		; 
	DW	Z3ENV		; . Environment (ENV)
	DB	Z3ENVS		; 
	DW	SHSTK		; . Shell stack (SH)
	DB	SHSTKS		; 
	DB	SHSIZE		; 
	DW	Z3MSG		; . Message buffer (MSG)
	DW	EXTFCB		; . External FCB (FCB)
	DW	EXTSTK		; . External stack (STK)
	DB	0		; . Quiet flag (1=quiet, 0=not quiet)
	DW	Z3WHL		; . Wheel byte (WHL)
	DB	4		; . Processor speed (Mhz)
	DB	'P'-'@'		; . Max disk letter
	DB	31		; . Max user number
	DB	1		; . 1=ok to accept DU:, 0=not ok
	DB	0		; . CRT selection
	DB	0		; . Printer selection
	DB	80		; . CRT 0: Width
	DB	24		; 	 # of lines
	DB	22		; 	 # of text lines
	DB	132		; . CRT 1: Width
	DB	24		; 	 # of lines
	DB	22		; 	 # of text lines
	DB	80		; . PRT 0: Width
	DB	66		; 	 # of lines
	DB	58		; 	 # of text lines
	DB	1		; 	 FF flag (1=can form feed)
	DB	96		; . PRT 1: Width
	DB	66		; 	 # of lines
	DB	58		; 	 # of text lines
	DB	1		; 	 FF flag (1=can form feed)
	DB	132		; . PRT 2: Width
	DB	66		; 	 # of lines
	DB	58		; 	 # of text lines
	DB	1		; 	 FF flag (1=can form feed)
	DB	132		; . PRT 3: Width
	DB	88		; 	 # of lines
	DB	82		; 	 # of text lines
	DB	1		; 	 FF flag (1=can form feed)
	DB	'SH      '	; . Shell variable filename
	DB	'VAR'		; . Shell variable filetype
	DB	'        '	; . File 1
	DB	'   '		; 
	DB	'        '	; . File 2
	DB	'   '		; 
	DB	'        '	; . File 3
	DB	'   '		; 
	DB	'        '	; . File 4
	DB	'   '		;
	DS	128-($-ENV)	; Make ENV 128 bytes long

ENV2:				; Terminal capabilities data
	DB	'ADM-3A  '	; . Name of terminal (ADM 3A)
	DB	'        '	; .
	DB	'K'-'@'		; . Cursor up
	DB	'J'-'@'		; . Cursor down
	DB	'L'-'@'		; . Cursor right
	DB	'H'-'@'		; . Cursor left
	DB	00		; . Clear screen delay
	DB	00		; . Cursor motion delay
	DB	00		; . Clear to EOL delay
	DB	1BH,'*',0	; . (CL) Clear screen string
	DB	1BH,'=%+ %+ ',0	; . (CM) Cursor motion string
	DB	1BH,'T',0	; . (CE) Clear to EOL string
	DB	1BH,')',0	; . (SO) Start hilite string
	DB	1BH,'(',0	; . (SE) End hilite string
	DB	0		; . (TI) Terminal init string
	DB	0		; . (TE) Terminal de-init string
	DB	0		;
ENVEND:

; End of environment and TCAP descriptors

CMDSET: DW	Z3CL+4		; Point to first chr in cmd line buf
	DB	Z3CLS		; Command line buffer size

PATH:				; Initial path description
	DB	'$',0		; .  Current drive, user 0
	DB	'$',15		; .  Current drive, user 15
	DB	1,'$'		; .  Drive A:, current user
	DB	1,0		; .  Drive A:, user 0
	DB	1,15		; .  Drive A:, user 15
  	DB	0		; (end of path)

	ENDIF			; ZCPR3 data

LAST$SG	EQU	$

	ORG	BOOT

UNINIT		EQU	$
SEKDSK:		DS	1	;SEEK DISK NUMBER
SEKTRK:		DS	2	;SEEK TRACK NUMBER
SEKSEC:		DS	1	;SEEK SECTOR NUMBER

HSTDSK:		DS	1	;HOST DISK NUMBER
HSTTRK:		DS	2	;HOST TRACK NUMBER
HSTSEC:		DS	1	;HOST SECTOR NUMBER

CPMDSK:		DS	1	;SINGLE DENSITY	DSK PARM
CPMTRK:		DS	2	; AND TRK
CPMSEC:		DS	1	; AND SECTOR

SEKHST:		DS	1	;SEEK SHR SECSHF
HSTACT:		DS	1	;HOST ACTIVE FLAG
HSTWRT:		DS	1	;HOST WRITTEN FLAG

UNACNT:		DS	1	;UNALLOC REC CNT
UNADSK:		DS	1	;LAST UNALLOC DISK
UNATRK:		DS	2	;LAST UNALLOC TRACK
UNASEC:		DS	1	;LAST UNALLOC SECTOR

CPMSPT:		DS	1	; LOGICAL SECTORS PER TRACK
SECMSK:		DS	1	; SECTOR MASK
SECSHF:		DS	1	; SECTOR SHIFT
MUNACT:		DS	1	; UNALLOCATED COUNT VALUE

INT$RETRIES:	DS	1	; Internal retry counter for RD & WR
ERFLAG:		DS	1	;ERROR REPORTING
RSFLAG:		DS	1	;READ SECTOR FLAG
READOP:		DS	1	;1 IF READ OPERATION
WRTYPE:		DS	1	;WRITE OPERATION TYPE
DMAADR:		DS	2	;LAST DMA ADDRESS
HSTBUF:		DS	1024	;HOST BUFFER
STATUS:		DS	1
MESSAGE:	DS	1
CMDPTR		DS	2
DATPTR		DS	2
LOGDSK		DS	1
PHYDRV		DS	1
DPHDRV		DS	1
PHYTAG		DS	2
PHYCMD		DS	1
TAGDRV		DS	1
TAGPHY		DS	1
TAGTYP		DS	1
TAGCTL		DS	1
SCSI$IO$COUNT	DS	1
SCSI$STAT$DAT	DS	4
TARGET		DS	1
LOGUNIT		DS	1
CURDPB		DS	2
HD$BYTE$BLOCK	DS	1

;	AMPRO BIOS-SPECIFIC STORAGE

IDSAVE:		DS	6		;READ ADDRESS BUFFER AREA
LTRACK:		DS	5		;LAST FLOPPY TRACK ACCESSED
LDISK:		DS	1		;LAST DISK SELECTED
TRIES:		DS	1		;NUMBER OF TIMES TO DO IT
RWHOST:		DS	1		;LOCAL READ/WRITE FLAG
HSTSID:		DS	1		;HOST DISK SIDE SELECT MASK
TIMEOUT:	DS	1		;TIMEOUT LOOP COUNTER
SECTOR:		DS	1		;TEMPORARY STORAGE

CHGDSK:		DS	1		; Flag to tell if we changed drives
FRWCMD:		DS	1		; FDC command
FRWPTR:		DS	2		; FDC data pointer

;	SPECIAL E DISK PARAMETERS - FILLED IN WHEN E DISK IS
;	FIRST SELECTED, AND USED IN DEBLOCKING.

ESECADJ:	DS	1		;SECTOR NUMBER ADJUST FOR SIDE 1

DIRBUF: 	DS	128		;DIRECTORY ACCESS BUFFER


;	*	*	*	*	*	*	*	*
;
;  Floppy drive directory check vector storage
;
;  The length of these vectors allows the use of up to 256
;  directory entries, all of which are checked by BDOS.
;
;  Note that these are used for removable floppy media only.
;
;  Do not change QPARM to increase the number of directory
;  entries without adjusting the variable FD$CKS.
;
;	*	*	*	*	*	*	*	*

FD$CKS	EQU	(255/4)+1	; maximum of 256 directory entries 

CSVA:	DS	FD$CKS
CSVB:	DS	FD$CKS
CSVC:	DS	FD$CKS
CSVD:	DS	FD$CKS
CSVE:	DS	FD$CKS

;	*	*	*	*	*	*	*	*
;
;  Floppy drive allocation vector storage
;
;  This area is used by BDOS to map all CP/M blocks for the
;  target disk drive, and is maintained to indicate which blocks
;  on the disk are in use.
;
;  The length of the Floppy area is set to 50 Bytes, which is
;  enough to handle a 96 TPI DS/DD disk as described in QPARM.
;
;  Do not change QPARM to increase storage without adjusting
;  the variable FD$ALV.
;
;	*	*	*	*	*	*	*	*

FD$ALV		EQU	(394/8)+1	; maximum of 395 disk blocks

ALVA:		DS	FD$ALV
ALVB:		DS	FD$ALV
ALVC:		DS	FD$ALV
ALVD:		DS	FD$ALV
ALVE:		DS	FD$ALV

FDATAEND	EQU	$


HD$VECTORS:	EQU	$	; HD check & allocation vectors 
HD$CURRENT	EQU	$	; Current Bios Buffer Area ptr

;	*	*	*	*	*	*	*	*
;
;  Hard disk directory check vector storage
;
;	No Check storage is required - just an address
;
;	*	*	*	*	*	*	*	*

CSVF:		DS	0
CSVG:		DS	0
CSVH:		DS	0
CSVI:		DS	0
CSVJ:		DS	0
CSVK:		DS	0
CSVL:		DS	0
CSVM:		DS	0
CSVN:		DS	0
CSVO:		DS	0
CSVP:		DS	0

;	*	*	*	*	*	*	*	*
;
;  Hard disk allocation vector storage
;
;	*	*	*	*	*	*	*	*

		IF	HARD$DISK
HD$ALV		EQU	(1279/8)+1	; Maximum of 1280 disk blocks
ALVF:		DS	0
ALVG:		DS	0
ALVH:		DS	0
ALVI:		DS	0
ALVJ:		DS	0
ALVK:		DS	0
ALVL:		DS	0
ALVM:		DS	0
ALVN:		DS	0
ALVO:		DS	0
ALVP:		DS	0
		ENDIF

		IF	NOT HARD$DISK
ALVF:		DS	0	; If this is a floppy only system,
ALVG:		DS	0	; define the hard disk vectors (to
ALVH:		DS	0	; prevent assembly errors).
ALVI:		DS	0
ALVJ:		DS	0
ALVK:		DS	0
ALVL:		DS	0
ALVM:		DS	0
ALVN:		DS	0
ALVO:		DS	0
ALVP:		DS	0
		ENDIF

ENDDATA		EQU	$

					; Mark the last avail. byte
		IF	ZCPR3		;
RESERVE:	EQU	0FD00H		; (0FD00H if ZCPR3)
		ENDIF			;
		IF	NOT ZCPR3	;
RESERVE:	EQU	00000H		; (00000H if no ZCPR3)
		ENDIF			;


FDCSIZE:	EQU	(MAINEND-BIOS) + (LAST$SG-HDCODE)
FDDSIZE:	EQU	FDATAEND-LAST$SG

HDCSIZE:	EQU	HDCODE-MAINEND
HDDSIZE:	EQU	ENDDATA-FDATAEND


;	*	*	*	*	*	*	*	*
;
;  Show the available free space and the number of sectors needed
;  to hold a SYSGEN image of CP/M with this BIOS:
;
;	*	*	*	*	*	*	*	*

FREEMEM:	EQU	RESERVE-ENDDATA		; Free memory left

SGSIZE:		EQU	(LAST$SG-CCP+127)/128	; Sysgen size
			; Number of sectors -- Must be 50H or less!

	END	BIOS
