 	* * * * * * * * * * * * * * * * * * * * * *
	*    CBIOS FOR AMPRO CP/M 2.2 SYSTEMS     *
	*   THIS BIOS IS FOR THE USE OF OWNERS	  *
	*  OF AMPRO COMPUTER SYSTEMS. ANY OTHER   *
	*	    USE IS PROHIBITED.		  *
	* COPYRIGHT (C) 1984 AMPRO COMPUTERS, INC.*
	* * * * * * * * * * * * * * * * * * * * * *

YES	EQU	1
NO	EQU	0

ZCPR3	EQU	YES

;	purpose: the cp/m 2.2 bios for the ampro cpu. based on
;		cbios.asm and deblock.asm, modified only as
;		necessary.
;	differences from standard cp/m bios:
;
;	1) track number is a byte, not a word
;	2) keeps track of last trk accessed, seeks only when
;		necessary (and invokes idread before seeks).
;	3) prints a signon message
;	4) disk accessing for two sided disks uses a cylinder
;	   approach to minimize the need for seeks.
;	5) there is an additional entry point that unlogs all disks
;	6) support for virtual disk allows format translation
;	7) limited iobyte support has been included
;	8) support for zcpr has been included
;

VERS	EQU	16		; version of this bios

;	revision log:
;
;version 1.6  29-apr-95   RJB	Corrected BIOS overflow by optimizing
;				the IOINIT routine.
;
;version 1.5   3-mar-85	  RJB	Optimized head load delay.
;
;	      22-jan-85	  RJB	Changed con & tty status routines to
;				use "ALLSENT" bit rather than TBE.
;				Removed buffer flush on warm boot.
;
;version 1.4  24 july 84	: changed motor-on routine to wait
;	     		          1 revolution after successful 
;				  id read.  Added mask of MSB
;				  to TTY and CRT inputs.
;
;version 1.3   5 april 84      : fixed crt and tty routines
;                                modified for latest zcpr3
;
;version 1.2   5 march 84   	: optimized motor on delay
;				  changed default initialization
;				  changed to support zcpr3
;
;version 1.1  20 february 84	: added single density support for
;				   256 and 512 byte sectors  jww
;				  added one second motor on delay
;
;version 1.0  12 february 84 :  production release
;

MSIZE	EQU	61		;cp/m version memory size in kilobytes

BIAS	EQU	(MSIZE-20)*1024
CCP	EQU	3400H+BIAS	;base of ccp
BDOS	EQU	CCP+806H	;base of bdos
BIOS	EQU	CCP+1600H	;base of bios
CDISK	EQU	0004H		;current disk number 0=a,...,15=p
IOBYTE	EQU	0003H		;intel i/o byte

; Z-80 equates
OTIR80	EQU	0B3EDH		; OTIR (0edh,0b3h)

; cp/m to host disk constants

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

	ORG	BIOS		;origin of this program
NSECTS	EQU	($-CCP)/128	;warm start sector count

;	ampro hardware equates

;	port addresses

STBSET	EQU	2		;sets print strobe
STBCLR	EQU	3		;clears print strobe

PIO1	EQU	001H		;parallel printer port

CTCA	EQU	40H
CTCA0	EQU	CTCA
CTCA1	EQU	050H
CTCA2	EQU	060H
CTCA3	EQU	070H

SIO1	EQU	080H
SIODPA	EQU	SIO1
SIODPB	EQU	088H
SIOCPA	EQU	084H
SIOCPB	EQU	08CH

STAT	EQU	0C4H		;disk controller command/status register
RTRK	EQU	STAT+1		;disk controller track register
RSEC	EQU	STAT+2		;disk controller sector register
RDAT	EQU	STAT+3		;disk data port

CMND	EQU	0C0H		;disk controller command/status register
WTRK	EQU	CMND+1		;disk controller track register
WSEC	EQU	CMND+2		;disk controller sector register
WDAT	EQU	CMND+3		;disk data port

HLDELAY	EQU	35		; Head load delay

CONT	EQU	00H		;disk flag and control port

;	mask equates

TBE	EQU	04H		;dart xmit buffer empty  
RDA	EQU	01H		;dart receive data available
DCD	EQU	08H		;dart data carrier detect
CTS	EQU	20H		;dart clear to send

;	char equates

CR	EQU	0DH		;carriage return (^m)
LF	EQU	0AH		;line feed (^j)
TAB	EQU	009H		;tab (^i)
BSP	EQU	08H		;backspace (^h)
DEL	EQU	7FH		;character delete
CAN	EQU	18H		;line cancel (^x)
NAK	EQU	15H		;	     (^u)

;	misc equates

CTS	EQU	020H		;clear to send
DELSEND EQU	28
TIMEOUT EQU	100
DDLSPT	EQU	40

FRESTOR EQU	8		; spin-up disabled on all commands
FSEEK	EQU	01CH
FSTEPIN EQU	05CH
FREADS	EQU	088H
FWRITES EQU	0A8H		;write precomp on
FRDADDR EQU	0C8H

AMSSDD	EQU	086H
AMDSDD	EQU	0C6H

SSDD96	EQU	087H
DSDD96	EQU	0C7H

;	jump vectors for individual subroutines
	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		;move head 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	UNLOG		;unlog all disks
	JMP	GETEDSK 	;get pointer to e disk storage
	JMP	IOINIT		; set new i/o parameters

	ORG	BIOS+40H
CTCVAL:	DB	47H,13,47H,208,3,3,3,3	; ctc0 values
SIOAVAL:DB	4,46H,5,0EAH,3,0C1H,0,0,0,0
SIOBVAL:DB	4,86H,5,0EAH,3,0C1H,0,0,0,0
NDSKS:	DB	4		; four drives allowed
STPRAT:	DB	3,0,0,0		; step rates of four drives,
				; 2nd - 4th not used yet
IOBYT:	DB	81H
AUTOCMD:DB	7,'STARTUP',0

	ORG	AUTOCMD+10
HSA:	DB	0		;lsb: 1 = yes, 0 = no
HSB:	DB	1
VER:	DB	VERS 		; version of this bios.

	ORG	BIOS+80H
DPBASE: DW	XLT0,0,0,0
	DW	DIRBUF,DPARM,CSV0,ALV0

	DW	XLT0,0,0,0
	DW	DIRBUF,DPARM,CSV1,ALV1

	DW	XLT0,0,0,0
	DW	DIRBUF,DPARM,CSV2,ALV2

	DW	XLT0,0,0,0
	DW	DIRBUF,DPARM,CSV3,ALV3

	DW	XLT1,0,0,0		;the e disk
	DW	DIRBUF,EPARM,CSV4,ALV4

SPARM:			;a single-sided ampro disk
	DW	40		;sec/trk
	DB	4		;block shift
	DB	15		;block mask
	DB	1		;extent mask
	DW	94		;disk size -1
	DW	63		;directory max
	DB	128		;alloc 0
	DB	0		;alloc 1
	DW	16		;check size
	DW	2		;offset

DPARM:			;a double-sided ampro disk
	DW	40		;sec/trk
	DB	4		;block shift
	DB	15		;block mask
	DB	1		;extent mask
	DW	194		;disk size -1
	DW	127		;directory max
	DB	192		;alloc 0
	DB	0		;alloc 1
	DW	32		;check size
	DW	2		;offset

DPARM96:		; 96 tpi double sided
	DW	40		;sec/trk
	DB	4		;block shift
	DB	15		;block mask
	DB	0		;extent mask
	DW	394		;disk size -1
	DW	255		;directory max
	DB	240		;alloc 0
	DB	0		;alloc 1
	DW	64		;check size
	DW	2		;offset

XLT0:		; 48 tpi skew table
	DB	1,2,3,4,5,6,7,8,9,10

;	the following storage should not be moved for e disk to work.
;	a call to bios function getedisk returns the address of eparm.
;	the type tab entry is thus defined as the returned value -1,
;	and the translate table value as the returned value+15.

TYPETAB: DS	4		;type of disk n
				; encoding is as follows:
				;   bit 7: 0 = sd, 1 = dd
				;   bit 6: 0 = ss, 1 = ds
				;   bits 3-2: 0 = 1k alloc, 1 = 2k alloc
				;   bits 1-0: 0 = 128, 1 = 256, 2 = 512
	DB	082H		;same as kaypro
EPARM:				;kaypro disk
	DW	40		;sec/trk
	DB	3		;block shift
	DB	7		;block mask
	DB	0		;extent mask
	DW	194		;disk size -1
	DW	63		;directory max -1
	DB	240		;alloc 0
	DB	0		;alloc 1
	DW	16		;check size
	DW	1		;offset
EDSD:
	DB	1		; default drive b:

XLT1:			;translate table for e disk
	DB	0,1,2,3,4,5,6,7,8,9
	DS	10

GETEDSK:
	LXI	H,EPARM
	RET

IOINIT:	LXI	H,CTCVAL

	LXI	B,0240H		; CTC0
	DW	OTIR80

	LXI	B,0250H		; CTC1
	DW	OTIR80

	LXI	B,0260H		; CTC2
	DW	OTIR80

	LXI	B,0270H		; CTC3
	DW	OTIR80

	LXI	B,0A84H		; DART A
	DW	OTIR80

	LXI	B,0A8CH		; DART B
	DW	OTIR80

	LDA	IOBYT
	STA	IOBYTE
	RET

PUTS:			;send bytes to console until zero encountered
	MOV	A,M		;fetch char
	ORA	A		;done?
        RZ                      ;yes if it's a zero byte
	INX	H		;set up for next byte
	MOV	C,A
	CALL	CONOUT		;sent it to the console
	JMP	PUTS

WBERR:
	LXI	H,BOOTMSG
WBERR1:
	CALL	PUTS
	JMP	WBOOT

BOOTMSG: DB     CR, LF, 'Boot failed!', 7, 0

NXTTRK:
	PUSH	B
	MVI	C,1
	CALL	SETTRK
	POP	B
	MVI	C,0
	RET

WBOOT:	;simplest case is to read the disk until all sectors loaded
	; warning: this bios can only read or write single density
	; disks, not cold or warm boot from them.
	XRA	A		;0 to accumulator
; STA HSTACT and STA UNACNT were removed as they caused any sectors
; that were left in the write buffers to be thrown away on a warm
; boot.
	STA	HSTSID		;assume side zero
	LXI	SP,80H		;use space below buffer for stack
	CALL	UNLOG

;	warm boot consists of reading the ccp and bdos in from the
;	system tracks: 1600h of code. only double density disks may
;	be warm booted on an ampro system. the ten sectors
;	on logical track zero (track 0 side 0) and one track on
;	logical track one (track 0 side 1) are read to ccp.

	MVI	C,0
	CALL	SELDSK		;auto select disk type
	MOV	A,H
	ORA	L
        JZ      WBERR           ;couldn't do it
	MVI	C,0
	CALL	SETTRK
	MVI	B,NSECTS	;number of sectors to read
	MVI	C,1		;first sector
	LXI	H,CCP
WBLOOP:
	PUSH	B		;save sector count
	PUSH	H
	CALL	SETSEC		;set the sector for next read
	POP	B		;fetch dma address
	PUSH	B		;save a copy
	CALL	SETDMA
	CALL	READ		;error check?
	ORA	A
	JNZ	WBERR
	POP	H
	LXI	D,128
	DAD	D		;update dma address
	POP	B
	INR	C		;point to next sector
	MOV	A,C
	CPI	DDLSPT
	CZ	NXTTRK
	DCR	B		;decrement loop counter
	JNZ	WBLOOP		;get another sector

;	end of load operation, set parameters and go to cp/m
GOCPM:
	MVI	A,0C3H		;c3 is a jmp instruction
	STA	0		;for jmp to wboot
	LXI	H,WBOOTE	;wboot entry point
	SHLD	1		;set address field for jmp at 0
	STA	5		;for jmp to bdos
	LXI	H,BDOS		;bdos entry point
	SHLD	6		;address field of jump at 5 to bdos
	LXI	B,80H		;default dma address is 80h
	CALL	SETDMA
	EI			;enable the interrupt system
	LDA	CDISK		;get current disk number
	MOV	C,A		;send to the ccp
	JMP	CCP		;go to cp/m for further processing

;home the selected disk
HOME:
	MVI	C,0		;set the desired track to zero
	CALL	SETTRK
	LDA	HSTWRT	;check for pending write
	ORA	A
	JNZ	HOMED
	STA	HSTACT	;clear host active flag
HOMED:	RET

SELDSK: ;select disk
	MOV	A,C		;selected disk number
	STA	SEKDSK		;seek disk number
	STA	CPMDSK		;working variable
	CPI	4		;good disk number?
	JZ	SELEDSK 	;always works for e disk
	LXI	H,NDSKS
	CMP	M
	JNC	SELERR		;if not, return error

;	disk number is in the proper range (0..3).
;	see if disk has been accessed yet - if not, read the
;	directory track to determine if single or double sided,
;	load all disk access tables as appropriate

	MOV	A,E		; test for new mount
	RAR
	JC	SELEND		; not a new mount

;	first access, see if double sided

	CALL	GETTYPE 	;ignore result, just use pointer
	PUSH	H		;save pointer into type table
	MVI	A,3
	STA	TRIES 	;try it three times

ACCESS:

;       first assume it's a double sided double density disk
	POP	H		;get type table ptr
	PUSH	H
	MVI	M,AMDSDD	;set type = dsdd-512
        CALL    SETUP           ;select drive and side (don't care)
	CALL	RESTORE
	CPI	255
	JZ	SELERR1 	;disk timeout
	MVI	A,2		; this side
	STA	CPMTRK
	CALL	SEEK		;note seek always seeks on side 0 first.
	JNZ	TRYSS

;	check for double sided
	LDA	IDSAVE+2	; sector
	CPI	17
	JC	TRYSS
	LDA	IDSAVE+3	; size
	CPI	3		; 96 tpi has 1024 byte sectors
	JNZ	SELOK
	POP	H		; get type table pointer
	PUSH	H
	MVI	M,DSDD96
	JMP	SELOK

;	seek failed, probably an id read error. change to
;	first side and try again...

TRYSS:	POP	H
	PUSH	H
	MVI	M,AMSSDD	;set type = ssdd-512
	LXI	H,CPMTRK
	DCR	M
	CALL	SEEK
	JNZ	TRYAGN
	LDA	IDSAVE+3	; size
	CPI	3		; 96 tpi drives have 1024 byte sectors
	JNZ	SELOK
	POP	H		; get type table pointer
	PUSH	H
	MVI	M,SSDD96
	JMP	SELOK

;	seek failed, maybe a single density disk (only acceptable in e drive)
TRYAGN: LXI     H,TRIES       ;can't read side zero either?
	DCR	M
	JZ	SELERR1 	;quit if last time
	JMP	ACCESS		;else try again

;	if we get here, we had a successful id read.
;	set up tables...
SELOK:	POP	H		;get rid of saved pointer
	CALL	SELEND		;use selend as a subroutine (wow)
	LXI	D,10		;and offset to parm table entry
	DAD	D
	PUSH	H		;save pointer
	CALL	GETTYPE
	POP	H		;restore ptr
	CPI	AMSSDD		;ssdd?
	LXI	B,SPARM 	;assume single sided
	JZ	SETSSID
	CPI	AMDSDD		; double sided 48?
	LXI	B,DPARM 	; ds 48 and ss 96 share dparm
	JZ	SETSSID
	CPI	SSDD96
	JZ	SETSSID
	LXI	B,DPARM96

;	and fall through
SETSSID:
	LDAX	B		; get logical sectors per track
	STA	CPMSPT
	MOV	M,C		;and fix the table
	INX	H
	MOV	M,B		;table now set up

;	compute proper disk parameter header address

SELEND:
;	set deblocker variables
	CALL	GETTYPE
	MOV	B,A		; save it a moment
	ANI	3		; mask sector size
	STA	SECSHF		; sector shift
	MOV	C,A		; set counter
	MVI	A,1
DB0:	ADD	A
	DCR	C
	JNZ	DB0
	DCR	A
	STA	SECMSK		; sector mask
	MOV	A,B		; get type byte
	ANI	4		; test block size  0=1k, 1=2k
	MVI	A,8		; assume 1k
	JZ	DB1		; if so
	ADD	A		; must be 2k
DB1:	STA	MUNACT		; initial unacnt value

	LDA	SEKDSK		;get disk number back
GETDPT:
	MVI	H,0
	MOV	L,A		;put it in hl
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	LXI	D,DPBASE	;base of parm block
	DAD	D		;hl=.dpb(curdsk)
	RET

SELEDSK:		;log in parameters for the e disk
	LDA	EPARM-1 	;fetch type byte
	MOV	B,A		;save it in b
	ANI	03H		;mask type nibble
	ORA	A
	JZ	SELE0
	CPI	1
	JZ	SELE1
	CPI	2
	JZ	SELE2

SELE3:			; 1024 byte sectors
	MOV	A,B		; get type byte
	ANI	20H		; look for continuous sector numbering
	MVI	A,0
	JZ	SELE3X
	LDA	EPARM		; get logical sectors per track
	RAR
	RAR
	RAR
	ANI	1FH		; mask off trash
SELE3X: STA	ESECADJ
	JMP	SELEND

SELE0:			;128 byte single density
	MOV	A,B		;get type byte back
	ANI	020H		;look for continuous sector numbering
	MVI	A,0
	JZ	SELE0X		;exit if duplicate numbering
	LDA	EPARM		;fetch logical sectors per track
SELE0X:
	STA	ESECADJ
	JMP	SELEND

SELE1:			;256 byte sector, 1k allocation (whatever)
	MOV	A,B		;get type byte back
	ANI	020H		;look for continuous sector numbering
	MVI	A,0
	JZ	SELE1X		;exit if duplicate numbering
	LDA	EPARM		;fetch logical sectors per track
	RAR			;divide by 2 for physical sectors
	ANI	07FH
SELE1X:
	STA	ESECADJ
	JMP	SELEND

SELE2:			;512 byte sector, 2k allocation (ampro)
	MOV	A,B		;get type byte back
	ANI	020H		;look for continuous sector numbering
	MVI	A,0
	JZ	SELE2X		;exit if duplicate numbering
	LDA	EPARM		;fetch logical sectors per track
	RAR
	RAR			;right shift two bits
	ANI	03FH
SELE2X:
	STA	ESECADJ
	JMP	SELEND


SELERR1:		;one thing on the stack
	POP	H
SELERR: 		;disk select failed
	LXI	H,0
	RET

SETTRK:
	;set track given by register c
	MOV	A,C
	STA	SEKTRK
	STA	CPMTRK
	RET

SETSEC:
	;set sector given by register c
	MOV	A,C
	STA	SEKSEC		;sector to seek
	STA	CPMSEC
	RET

SETDMA:
	;set dma address given by bc
	MOV	H,B
	MOV	L,C
	SHLD	DMAADR
	RET

SECTRAN:
	;translate sector number bc
	MOV	H,B
	MOV	L,C
	RET

READ:
	;read the selected cp/m sector
	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:
	;write the selected cp/m sector
	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
	LDA	SEKTRK
	STA	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
	LDA	SEKTRK
	CMP	M		;sektrk = unatrk?
	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?

;	edsk: following code compares to cpmspt (40 for ampro disks)
;	make this a variable.
	PUSH	B		;save bc in case we needed it
	PUSH	PSW		;save the value to be compared
	LDA	SEKDSK
	CPI	4		;edisk?
	LDA	CPMSPT
	MOV	B,A
	JNZ	EMATCH
	LDA	EPARM		;fetch table spt (convenient, eh?)
	MOV	B,A
EMATCH:
	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
	LDA	UNATRK
	INR	A
	STA	UNATRK		;unatrk = unatrk+1

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?
	jz	samedt		; indicate not same disk
	mvi	a,0ffh
	sta	chgdsk
	jmp	nomatch
samedt:
;	same disk, same track?
	LXI	H,HSTTRK
	LDA	SEKTRK
	CMP	M		;sektrk = hsttrk?
	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	CPMDSK
	LDA	HSTTRK
	STA	CPMTRK
	CNZ	WRITEHST	;clear host buff

FILHST:
	;may have to fill the host buffer
	LDA	SEKDSK
	STA	HSTDSK
	STA	CPMDSK
	LDA	SEKTRK
	STA	HSTTRK
	STA	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	CPMDSK
	LDA	HSTTRK
	STA	CPMTRK
	CALL	WRITEHST
	LDA	ERFLAG
	RET

WRITEHST:
	;cpmdsk = 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
	STA	RWHOST
	JMP	HOSTIO

READHST:
	;cpmdsk = host disk #, cpmtrk = host track #,
        ;hstsec = host sect #. read "hstsiz" bytes
	;into hstbuf and return error flag in erflag.
	MVI	A,1
	STA	RWHOST
HOSTIO:
	CALL	SETUP		;output to drive select register
	MVI	A,10
	STA	TRIES
WMI:				;where am i?
	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 it for a moment
	LDA	CPMDSK
        CPI     'E' -41H        ; check for 'e' drive
	MOV	A,E		; restore sector number
        JZ      XYZ             ; no sector bias for 'e' drive
	PUSH	D		; save sector number in e reg.
	CALL	GETTYPE
	POP	D
	ANI	0C0H		; isolate dd and ds indicators
	CPI	0C0H		; check dd and ds
	MOV	A,E
	JNZ	XYZ		; no bias
	ADI	16		; double sided bias
XYZ:	OUT	WSEC
	CALL	SETUP		;select unit and real side
	CALL	GETBUF
	LDA	RWHOST		;see if read (1) or
	ORA	A		; write (0)
	JZ	WRDAT		;branch for writes

;	falling through implies this is a read

	MVI	A,FREADS	;read sector command
	CALL	RDATA
	JNZ	RWFAIL
	RET			;return to bdos, read ok

RWFAIL: LXI	H,TRIES
	DCR	M		;one less retry
	JZ	DABORT		;error return
	CALL	MAPTRK
	MVI	M,255		;force read address
	JMP	WMI		;try again if there

DABORT: MVI	A,01		;set error flag
	STA	ERFLAG
	RET			;return to bdos w/error

WAIT:	LXI	H,50*167	; 50 ms.
WTL:	DCX	H
	MOV	A,H
	ORA	L
	JNZ	WTL
	RET

WRDAT:	PUSH	H
	CALL	DWAIT		;wait for permission
	POP	H
	MVI	A,FWRITES	;make it a write command
	CALL	OUTCMD		;wait for cmd to start
	CALL	WR		; write the disk
	JMP	WRTERR		; check for errors and return

WR:	IN	STAT		; get fdc status
	MOV	B,A		; save it for eventual return
	RAR			; rotate busy bit into carry
	RNC			; write command is finished
	RAR			; rotate data request into carry
	JNC	WR		; wait for data request
	MOV	A,M		; get a byte
	OUT	WDAT		; give it to the fdc
	INX	H		; bump the pointer
	JMP	WR		; again

WRTERR: MOV	A,B		; get fdc status
	ANI	05CH		;check for errors
	JNZ	RWFAIL
	RET

;	rdata: read data routine. on entry, hl contains buffer,
;		a contains read command (which may be read address)

RDATA:	PUSH	PSW
	PUSH	H
	CALL	DWAIT
	POP	H
	POP	PSW
	CALL	OUTCMD		;wait for cmd to start
	CALL	RD		; read the disk
	JMP	RDERR		; check for errors and return

RD:	IN	STAT		; get fdc status
	MOV	B,A		; save status for eventual return
	RAR			; rotate busy bit to carry
	RNC			; controller has finished
	RAR			; rotate data request to carry
	JNC	RD		; wait for drq
	IN	RDAT		; get byte from fdc
	MOV	M,A		; put it in memory
	INX	H		; bump the pointer
	JMP	RD		; again

RDERR:	MOV	A,B		; fdc status
	ANI	01CH		;check for errors
	RET

OUTCMD:	CALL	MOTOR		; insure motor on
OC0:	OUT	CMND		; to fdc
	MVI	A,19		; wait 66.5 us. for command to set up
OC1:	DCR	A
	JNZ	OC1
	RET

; MOTOR-UP-TO-SPEED FOR AMPRO BIOS

MOTOR:	PUSH	H		; SAVE BUFFER POINTER
	PUSH	PSW		; SAVE COMMAND
	IN	STAT		; STATUS
	RAL			; ROTATE MOTON INTO CARRY
	JC	MOTEND		; MOTORS ALREADY ON, FORGET IT
	IN	RSEC		; GET THE SECTOR NUMBER
	STA	SECTOR		; SAVE IT
	CALL	MT2		; READ AN ID
	MOV	C,A		; SAVE ID # A MOMENT
MT3:	CALL	MT2		; READ ANOTHER ID
	CMP	C		; SAME AS THE FIRST?
	JNZ	MT3		; TIL SAME ID READ TWICE
	LDA	SECTOR		; GET SECTOR NUMBER
	OUT	WSEC		; TO 1770
MOTEND:	POP	PSW		; GET COMMAND
	POP	H		; GET POINTER
	RET

; READ AN ID AND RETURN ITS NUMBER IN A
;
MT2:	CALL	DWAIT		; WAIT FOR CONTROLLER READY
	MVI	A,FRDADDR	; READ ADDRESS COMMAND
	LXI	H,IDSAVE	; PUT IT HERE
	CALL	OC0		; READ NEXT ID FIELD
	CALL	RD		; READ THE 1770
	CALL	RDERR		; CHECK FOR GOOD READ
	ANI	0CH		; RNF OK WITH BLANK DISK
	LDA	IDSAVE+2	; ID #
	RZ			; GOOD READ
	JMP	MT2		; TRY FOR A GOOD ONE

;	procedure dwait:
;		wait for permission to write a register

DWAIT:	LXI	H,0
	MVI	A,TIMEOUT
	STA	TOCNT
DWLP:	IN	STAT
	ANI	1
	JZ	DWDONE
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	DWLP		;keep waiting
	LXI	H,TOCNT
	DCR	M
	LXI	H,0
	JNZ	DWLP
	MVI	A,0D0H		;force int
	OUT	CMND	;give up on waiting
	MVI	A,255
	RET

DWDONE: IN	STAT	;fetch status again
	RET

RESTORE:		;issue a restore command to the controller
	CALL	DWAIT
	CPI	255
        RZ                      ;timeout, don't bother
	MVI	A,FRESTOR
	LXI	H,STPRAT
	ORA	M
	CALL	OUTCMD
	CALL	DWAIT
	CALL	WAIT		; let drive settle down
	MVI	A,FSTEPIN
	LXI	H,STPRAT
	ORA	M
	CALL	OUTCMD
	CALL	WAIT
	CALL	MAPTRK
	MVI	M,0
	RET

SEEK:				;seek to alternate track
	XRA	A		;assume side zero
	STA	HSTSID
	CALL	SETUP		;select unit and side (zero is ok)
	LXI	H,IDSAVE	;use id buffer
	MVI	A,FRDADDR
	CALL	RDATA
	RNZ			;quit if error
	IN	RSEC		;fetch current track again
	OUT	WTRK		;and tell 1770 where we are
	CALL	GETTRK		;adjust if necessary (and set up side)
	OUT	WDAT		;and tell the fdc where to go
	CALL	SETUP		;set unit and real side
	CALL	DWAIT
	MVI	A,FSEEK 	;worst case seek command
	LXI	H,STPRAT
	ORA	M
	CALL	OUTCMD
	CALL	DWAIT		;wait for it to finish
	ANI	18H		;isolate possible failures
	RNZ			;seek failed, return w/error

;	seek worked, save track in table and return

	CALL	MAPTRK
	CALL	GETTRK		;get physical track
	MOV	M,A
	XRA	A		;ensure zero flag is set
	RET			;return w/success

UNLOG:			;reset logged in disks (allows changing disk type)
	LXI	H,-1
	SHLD	LTRACK
	SHLD	LTRACK+2
	RET

MAPTRK: 		;this routine returns the ltrack pointer in hl
	LDA	CPMDSK
	MOV	E,A
	MVI	D,0
	LXI	H,LTRACK
	DAD	D
	RET

SETUP:			;this routine writes to the drvsel register
	LDA	CPMDSK		;fetch unit id
	CPI	4		;e disk?
	PUSH	B		;save bc
	JZ	SETEDSK
	INR	A		;setup for decr loop
	MOV	B,A
	MVI	A,1		;selbits mask
SETUP1:
	DCR	B		;are we done?
	JZ	SETUP2
	ADD	A		;shift selbit left one
	JMP	SETUP1
SETEDSK:
	LDA	EDSD		; e disk drive
	MOV	C,A
	INR	C
	XRA	A
	STC
SEDL:	RAL
	DCR	C
	JNZ	SEDL
SETUP2: 			;selbits now in a
	ORI	040H		;turn off eprom
	LXI	H,HSTSID	;point to side select
	ORA	M		;or that in, too
	MOV	B,A
	CALL	GETTYPE
	RLC			;single density?
	JNC	SETUP3		;branch if so, else
	XRA	A		;clear accum
	JMP	SETUP4
SETUP3:
	MVI	A,20H		;load single density mask
SETUP4:
	ORA	B		;get rest of select back
	OUT	CONT
	push	psw
	lda	chgdsk
	ora	a
	jz	nochange
	mvi	c,hldelay
hlwait:
	mvi	b,189
hlwait2:
	cmp	m
	dcr	b
	jnz	hlwait2
	dcr	c
	jnz	hlwait
	mov	a,c
	sta	chgdsk
nochange:
	pop	psw
	POP	B		;restore saved reg
	RET

GETTYPE:		;get the type of a disk.
			; encoding is as follows:
			;   bit 7: 0 = sd, 1 = dd
			;   bit 6: 0 = ss, 1 = ds
			;   bits 3-2: 0 = 1k alloc, 1 = 2k alloc
			;   bits 1-0: b reg value (bytes/sector)
	LDA	CPMDSK
	MOV	E,A
	MVI	D,0
	LXI	H,TYPETAB
	DAD	D
	MOV	A,M
	RET

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
	LDA	CPMDSK
	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	CPMDSK
	CPI	4		;was it e disk?
	JNZ	GSEXIT
	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: 		;this routine converts logical to physical trk
	PUSH	H
	CALL	GETTYPE
	LXI	H,HSTSID	;point to where we keep it
	RLC
	RLC			;test for single sided
	LDA	CPMTRK		;fetch desired logical track
	JNC	SEEK1		;if single side, leave it

;	we now must convert to real sectors and sides - divide by two
;       and make the lsb be the side. the seek command doesn't know
;	about sides, and verifies track only. read and write presumably
;	should have the side select set properly (as should idread).

	RAR			;put side bit in carry
	JNC	SEEK1		;branch if side zero
	MVI	M,10H		;else set mask for later
	JMP	SEEK2
SEEK1:
	MVI	M,0		;set side zero
SEEK2:
	POP	H		;restore entry value
	ANI	7FH		;make it reasonable
	RET

;	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 output buffer status now checked
	MVI	A,01H		; Check "all sent" bit in register 1
	OUT	SIOCPA		; .
	IN	SIOCPA		; .
	ANI	01H		; .
	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 output buffer status now checked
	MVI	A,01H		; Check "all sent" bit in register 1
	OUT	SIOCPB		; .
	IN	SIOCPB		; .
	ANI	01H		; .
	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

;	the following code is one-time only

BOOT:	;cold boot parameter initialization
	MVI	A,40H		;turn off eprom
	OUT	CONT
	LDA	IOBYT		; get iobyte value
	STA	IOBYTE
	LXI	SP,80H
	CALL	IOINIT
	LXI	H,LOGMSG
	CALL	PUTS		;put sign-on msg on screen
	CALL	UNLOG

	 IF	ZCPR3

;the following code is required for zcpr3

;expath  equ	40h		;ext file search-path base
;z3whl	equ	4bh		; wheel byte
;z3cl	equ	0ff00h		;multi-cmd buffer storage
;z3cls	equ	200		;..and buffer length
;z3env	equ	0fe00h		;named directory (d/u) storage
;shstk	equ	0fd00h		;base of 32 byte shell buffers
;z3msg	equ	0fd80h		;zcpr3 messages

	JMP	ZBOOT		;go to movement routines

* SYSTEM SEGMENT:  SYSTEM.ENV
* AUTHOR:  RICHARD CONN

; program:  sysenv.asm
; author:  richard conn
; version:  1.0
; date:  22 feb 84
; previous versions:  none
;
; this version revised for 61k ampro and to assemble with asm.com
;  for addition to the ampro bios.  6 apr 84  jww
;
;	sysenv is the definition for my zcpr3 environment, and it is loaded
; as my zcpr3 environment descriptor by z3ldr.  sysenv is named to sys.env
; after assembly to permit this.
;

;
;  environment definitions
;
;	maclib	z3base
;	maclib	sysenv

;* 61k zcpr3 for ampro

;****************************************************************
;*								*
;*  z3base.lib -- base addresses for zcpr3 system on ampro	*
;*	bookshelf computer by echelon, inc.			*
;*								*
;*	these addresses are used by the following system	*
;* segments:							*
;*								*
;*	segment		function				*
;*	-------		--------				*
;*	ampboot		boot system from floppy disk		*
;*	bdosz		customized bdos				*
;*	cbiosz		customized bios				*
;*	zcpr3		zcpr3 command processor			*
;*	*.env		all environment descriptors		*
;*	*.fcp		all flow command packages		*
;*	*.iop		all input/output packages		*
;*	*.ndr		all named directory definition files	*
;*	*.rcp		all resident command packages		*
;*								*
;*								*
;* memory map of system:					*
;*								*
;*	address range	  size	function			*
;*	-------------	-------	--------			*
;*	    0 -   ff	256 b	standard cp/m buffers except	*
;*	   40 -   4a	 11 b	 for zcpr3 external path	*
;*	   4b		  1 b	wheel byte			*
;*	  100 - d7ff	 ~54  k	tpa				*
;*	 d800 - dfff	   2  k	zcpr3 command processor		*
;*	 e000 - eeff	   3.5k	bdosz				*
;*	 ef00 - fcff	   3.5k	cbiosz with buffers		*
;*	 fd00 - fd00	   0  k	resident command package	*
;*	 fd00 - fd00	   0  k flow command package		*
;*	 fd00 - fd00	  0 b	memory-based named directory	*
;*	 fd00 - fd7f	128 b	zcpr3 shell stack		*
;*	 fd80 - fdcf	 80 b	zcpr3 message buffers		*
;*				byte 0:  error flag (z/nz)	*
;*				byte 1:  if (8 levels)		*
;*				byte 2:  if active (8 levels)	*
;*				byte 3:  z3 cmd status		*
;*					00b - normal		*
;*					01b - shell		*
;*					10b - error		*
;*				bytes 4&5: error address if 10b	*
;*				byte 6: program error code	*
;*				byte 7: zex message byte	*
;*					00b - normal		*
;*					01b - z3 prompt		*
;*					10b - suspend intercept	*
;*				byte 8: zex running flag (0=no)	*
;*				bytes 9-10: address of next	*
;*					char for zex to return	*
;*				bytes 11-12: address of first	*
;*					char in zex memory-	*
;*					based file buffer	*
;*				byte 13: sh control byte	*
;*					bit 0: enable shcmt	*
;*					bit 1: enable shecho	*
;*					bit 7: enable shell	*
;*						entry wait	*
;*				bytes 14-15: shell scratch	*
;*				bytes 10h-2fh: error cmd	*
;*				bytes 30h-39h: registers	*
;*				bytes 3ah-3fh: reserved		*
;*				bytes 40h-4fh: user-defined	*
;*	 fdd0 - fdff	 48 b	zcpr3 external fcb		*
;*	 fe00 - feff	256 b	environment descriptors		*
;*				bytes 00h-7fh:  z3 parameters	*
;*				bytes 80h-ffh:  z3 terminal cap	*
;*	 ff00 - ffcf	208 b	multiple command line buffer	*
;*	 ffd0 - ffff	 48 b	zcpr3 external stack		*
;*								*
;****************************************************************

;
;  true and false
;
;false	equ	0
;true	equ	not false


;*
;*  zcpr3 base equates
;*

;
;  1. version numbers, memory size, and cp/m base address
;
;	the following equates define the version numbers of the zcpr3
; command processor and the cbiosz.  they also explicitly state the size
; of the tpa for inclusion in the cbiosz header printed at cold boot.
;
Z3REV	EQU	30	; zcpr3 rev number
;msize	equ	54	; size of tpa
;
;    base - base address of user's cp/m system (normally 0 for dr version)
;           this equate allows easy modification by non-standard cp/m (eg,h89)
;
;base	equ	0

;
;  2. processor selection
;
;	the following equate selects the use of the 8080/8085 micro or
; the z80 micro for the target for zcpr3.  note that selecting the
; 8080/8085 should be done only if you have an 8080 or 8085.  if you have
; a z80, by all means select this one since the code is much smaller and
; you can cram more features into the system as a result.
;	if the processor is an 8080 or 8085, set this equate to true.
; if the processor is a z80, set it to false.
;
;i8080	equ	false

;
;  3. external path
;
;	the following equates define the address of the zcpr3 external
; path and the number of two-byte elements contained in this path (maximum).
; if there is no zcpr3 external path, both of these values should be set to 0.
;
EXPATH	EQU	40H	; external path
EXPATHS	EQU	5	; 5 2-byte path elements
			;  (path size = expaths*2 + 1)

;
;  4. wheel byte
;
;	the following equate defines the address of the zcpr3 wheel byte.
; if there is no zcpr3 wheel byte, this value should be set to 0.
;
Z3WHL	EQU	4BH	; wheel byte address


;
;  5. ccp location
;
;	the following equate defines the address of the zcpr3 command
; processor.  this address must be supplied.
;
;ccp	equ	0d800h	; zcpr3 command processor

;
;  6. rcp location
;
;	the following equates define the address of the zcpr3 resident
; command package and its size in 128-byte blocks.  if there is no
; zcpr3 resident command package, both of these values should be 0.
;
RCP	EQU	00000H	; resident command package
RCPS	EQU	00	; 00 128-byte blocks (0k bytes)


;
;  7. iop location
;
;	the following equates define the address of the zcpr3 input/output
; package and its size in 128-byte blocks.  if there is no zcpr3 input/output
; package, both of these values should be 0.
;
IOP	EQU	00000H	; redirectable i/o package
IOPS	EQU	00	; 00 128-byte blocks (0k bytes)


;
;  8. fcp location
;
;	the following equates define the address of the zcpr3 flow command
; package and its size in 128-byte blocks.  if there is no zcpr3 flow command
; package, both of these values should be 0.
;
FCP	EQU	00000H	; flow command package
FCPS	EQU	0	; 0 128-byte blocks (0k bytes)


;
;  9. env location
;
;	the following equates define the address of the zcpr3 environment
; descriptor and its size in 128-byte blocks.  if there is no zcpr3 environment
; descriptor, both of these values should be 0.
;
Z3ENV	EQU	0FE00H	; environment descriptors
Z3ENVS	EQU	2	; size of environment descriptor in 128-byte blocks


;
; 10. shell stack
;
;	the following equates define the address of the zcpr3 shell stack,
; the number of entries permitted in the zcpr3 shell stack, and the size
; of each entry in the shell stack in terms of bytes.  if there is no zcpr3
; shell stack, all three values should be 0.
;
SHSTK	EQU	0FD00H	; zcpr3 shell stack
SHSTKS	EQU	4	; number of shsize-byte shell stack entries
SHSIZE	EQU	32	; size of a shell stack entry
			;   (stack size = shstks * shsize)


;
; 11. zcpr3 messages
;
;	the following equate defines the address of the zcpr3 message buffer.
; this buffer is always 80 bytes long.  if there is no zcpr3 message buffer,
; this address should be 0.
;
Z3MSG	EQU	0FD80H	; zcpr3 message buffer


;
; 12. external fcb
;
;	the following equate defines the address of the zcpr3 external fcb.
; this buffer is always 36 bytes long.  if there is no zcpr3 external fcb,
; this address should be 0.
;
EXTFCB	EQU	0FDD0H	; zcpr3 external fcb


;
; 13. named directory buffer
;
;	the following equates define the address and size (in terms of 18-byte
; entries) of the zcpr3 named directory buffer.  if there is no such buffer,
; both of these values should be 0.
;
Z3NDIR	EQU	00000H	; zcpr3 named directory area
Z3NDIRS	EQU	00	; 00 18-byte named directory elements permitted
			;   (ndir size = z3ndirs*18 + 1 for trailing 0)


;
; 14. command line
;
;	the following equates define the address and size (in terms of bytes)
; of the zcpr3 command line buffer (formerly called the multiple command line
; buffer under zcpr2).  if there is no such buffer, both of these values should
; be 0.
;
Z3CL	EQU	0FF00H	; zcpr3 command line buffer
Z3CLS	EQU	200	; size of command line buffer


;
; 15. external stack
;
;	the following equate defines the address of the zcpr3 external stack.
; this stack is always 48 bytes in size.  if there is no such stack, this
; value should be 0.
;
EXTSTK	EQU	0FFD0H	; zcpr3 external stack


;
; 16. user equates
;
;	the following equates are available for the implementer's target
; system.  these are implementation-defined.
;


;*
;*  end of zcpr3 base equates
;*

;
;  include environment descriptor
;
;	org	100h		; origin
ENV:	JMP	0		; leading jmp

;	sysenv

;	end
; library:  sysenv.lib
; author:  richard conn
; version:  1.0
; date:  22 feb 84
; previous versions:  none

;
;	sysenv is the definition for the ampro zcpr3 environment.
;

;sysenv	macro
;
;  environment descriptor
;	if inline, there is a leading jmp just before this
;
ENVORG1:
	DB	'Z3ENV'		; environment id
	DB	2		; class 2 environment (internal)

	DW	EXPATH		; external path address
	DB	EXPATHS		; number of 2-byte elements in path

	DW	RCP		; rcp address
	DB	RCPS		; number of 128-byte blocks in rcp

	DW	IOP		; iop address
	DB	IOPS		; number of 128-byte blocks in iop

	DW	FCP		; fcp address
	DB	FCPS		; number of 128-byte blocks in fcp

	DW	Z3NDIR		; ndr address
	DB	Z3NDIRS		; number of 18-byte entries in ndr

	DW	Z3CL		; zcpr3 command line
	DB	Z3CLS		; number of bytes in command line

	DW	Z3ENV		; zcpr3 environment descriptor
	DB	Z3ENVS		; number of 128-byte blocks in descriptor

	DW	SHSTK		; shell stack address
	DB	SHSTKS		; number of shsize-byte entires in shell stack
	DB	SHSIZE		; size of a shell stack entry

	DW	Z3MSG		; zcpr3 message buffer

	DW	EXTFCB		; zcpr3 external fcb

	DW	EXTSTK		; zcpr3 external stack

	DB	0		; quiet flag (1=quiet, 0=not quiet)

	DW	Z3WHL		; address of wheel byte

	DB	4		; processor speed in mhz

	DB	'E'-'@'		; maximum disk
	DB	31		; maximum user
	DB	1		; 1=ok to accept du, 0=not ok

	DB	0		; crt selection (0=crt 0, 1=crt 1)
	DB	0		; printer selection (n=printer n)

	DB	80		; width of crt 0
	DB	24		; number of lines on crt 0
	DB	22		; number of lines of text on crt 0

	DB	80		; width of crt 1
	DB	24		; number of lines on crt 1
	DB	22		; number of lines of text on crt 1

	DB	80		; width of printer 0
	DB	66		; number of lines on printer 0
	DB	58		; number of lines of text on printer 0
	DB	1		; form feed flag (0=can't formfeed, 1=can)

	DB	102		; width of printer 1
	DB	66		; number of lines on printer 1
	DB	58		; number of lines of text on printer 1
	DB	1		; form feed flag (0=can't formfeed, 1=can)

	DB	80		; width of printer 2
	DB	66		; number of lines on printer 2
	DB	58		; number of lines of text on printer 2
	DB	1		; form feed flag (0=can't formfeed, 1=can)

	DB	80		; width of printer 3
	DB	66		; number of lines on printer 3
	DB	58		; number of lines of text on printer 3
	DB	1		; form feed flag (0=can't formfeed, 1=can)

	DB	'SH      '	; shell variable filename
	DB	'VAR'		; shell variable filetype

	DB	'        '	; filename 1
	DB	'   '		; filetype 1

	DB	'        '	; filename 2
	DB	'   '		; filetype 2

	DB	'        '	; filename 3
	DB	'   '		; filetype 3

	DB	'        '	; filename 4
	DB	'   '		; filetype 4

	DS	128-($-ENVORG1+3)	; make exactly 128 bytes long
					; (+3 compensates for leading jmp)
;
; terminal capabilities data
;
ENVORG2:
	DB	'                '	;name of terminal (none)
	DB	'E'-'@'			;cursor up
	DB	'X'-'@'			;cursor down
	DB	'D'-'@'			;cursor right
	DB	'S'-'@'			;cursor left
	DB	00			;cl delay
	DB	00			;cm delay
	DB	00			;ce delay
	DB	0			;cl string
	DB	0			;cm string
	DB	0			;ce string
	DB	0			;so string
	DB	0			;se string
	DB	0			;ti string
	DB	0			;te string

	DS	32-($-ENVORG2)		; make exactly 32 bytes long

;
;  end of environment descriptor
;
;	endm

CMDSET: DW	Z3CL+4		;point to first char in command buf
	DB	Z3CLS		;buffer size
	DW	0		;buffer empty

PATH:   DB      '$',0           ;current drive and user 0
        DB      1,'$'           ;drive 'a' and current user
        DB      1,0             ;drive 'a' and user 0
	DB	1,15		;drive 'a' and user 15
	DB	'$',15		;current drive and user 15
	DB	0		;end of path

ZERO:	; clear (hl) for (b) bytes

	XRA	A		; clear a to zero
ZL:	MOV	M,A		; move it to memory
	INX	H		; bump pointer
	DCR	B		; count down
	JNZ	ZL		; until zero
	RET

LDIR:	; move bc bytes from (hl) to (de)

	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	LDIR
	RET	

ZBOOT:	LXI	B,3		;move data structures to storage areas
	LXI	H,CMDSET	
	LXI	D,Z3CL
	CALL	LDIR

	LXI	B,10
	LXI	H,AUTOCMD
	LXI	D,Z3CL+3
	CALL	LDIR

	LXI	B,11		; number of bytes in path
	LXI	H,PATH
	LXI	D,EXPATH
	CALL	LDIR

	LXI	H,Z3WHL		; clear wheel byte
	MVI	M,0

	LXI	H,SHSTK		; clear shell stack
	MVI	M,0

	LXI	H,Z3MSG
	MVI	B,80
	CALL	ZERO

	LXI	H,ENV
	LXI	D,Z3ENV
	LXI	B,128+32
	CALL	LDIR

	ENDIF	; zcpr3

	XRA	A		;zero in the accum
	STA	CDISK		;select disk zero
	STA	HSTACT		;host buffer inactive
	STA	UNACNT		;clear unalloc count
	STA	HSTSID		;assume side zero
	JMP	GOCPM		;initialize and go to cp/m

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

	ORG	BOOT

UNINIT	EQU	$
SEKDSK: DS	1		;seek disk number
SEKTRK: DS	1		;seek track number
SEKSEC: DS	1		;seek sector number

HSTDSK: DS	1		;host disk number
HSTTRK: DS	1		;host track number
HSTSEC: DS	1		;host sector number

CPMDSK: DS	1		;single density dsk parm
CPMTRK: DS	1		; 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	1		;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

CHGDSK:	ds	1		; changed disk flag

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

;	ampro bios-specific storage

IDSAVE: DS	6		;read address buffer area
LTRACK: DS	5		;last track accessed
TRIES:	DS	1		;number of times to do it
RWHOST: DS	1		;local read/write flag
HSTSID: DS	1		;host disk side select mask
TOCNT:	DS	1		;timeout loop counter
SECTOR:	DS	1		;temporary storage

;	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

ALV0:	DS	50
CSV0:	DS	64
ALV1:	DS	50
CSV1:	DS	64
ALV2:	DS	50
CSV2:	DS	64
ALV3:	DS	50
CSV3:	DS	64
ALV4:	DS	50
CSV4:	DS	64

ENDDAT	EQU	$
DATSIZ	EQU	$-UNINIT
RESERVE	EQU	0FD00H		; memory reserved above here
REMAINS	EQU	RESERVE-ENDDAT

	END	BIOS
