*****************************************************************
*								*
Title	'apposite designs CP/M cbios  --  vers 2.21t'
*								*
*	Copyright 1983 by	apposite designs		*
*				Berkeley, Ca.			*
*	All rights reserved					*
*								*
*	   	- biosvers.asm -				*
*								*
*****************************************************************


;  This CBIOS is designed for the following hardware -
;
;	CompuPro Floppy Disk Controller (ports 0c0h thru 0c2h)
;	CompuPro System Support Board (ports 50h thru 5fh)
;	CompuPro Interfacer 3/4 (ports 10h thru 17h)
;	CompuPro M-Drive/H (ports 0c6h thru 0c7h)
;	CompuPro 8085/88 CPU
;
;	The console is optionally (by setting i4aConInt or
;	SysConInt = True) interrupt driven with a 32 byte
;	type-ahead buffer. Console interrupts may require
;	jumper changes (see CompuPro documentation).



version		equ	22	; CP/M  version
level		equ	01	; CBIOS version
rev		equ	't'	; CBIOS revision


;	last modified>	27oct83	   joel wittenberg


; Logical Modules:
;
;	biosvers.asm	; bios version
;	system  .equ	; system equates
;	chari/o .equ	; char i/o equates
;	blocki/o.equ	; disk equates
;	coldboot.asm	; cold boot code
;	biosjump.tbl	; bios jump table
;	sysvecs .int	; 8259 interrupt vectors
;	warmboot.asm	; warm boot code
;	tables  .dsk	; dph,dpb,xlate tables
;	chari/o .asm	; char i/o code
;	utilitys.asm	; utilities
;	selfuncs.asm	; select drive,track,etc
;	deblock .asm	; deblocking routines
;	flopdrvr.asm	; flop specific code
;	messages.asm	; error messages
;	storage .dat	; data storage
page
*********************************************************
*		system equates				*
*		- system  .equ -			*
*********************************************************

false		equ	0
true		equ	not false

i4aConInt	equ	false		; buffer'd console -
SysConInt	equ	not i4aConInt	; don't set both true

HMemDrive	equ	true		; M-Drive/H option

bell		equ	07
cr		equ	0Dh
lf		equ	0Ah
sim		equ	30h		; Set Int Mask opcode
jump		equ	0c3h		; jump inst. opcode

WarmVec		equ	0000		; warm boot vector
BdosVec		equ	0005		; bdos entry vector
IntVec		equ	002ch		; rst 5.5 vector
 
RecSize		equ	128
cpmBANK		equ	0		; cp/m bank
biosBANK	equ	0		; bios bank

memsize		equ	64		; size of CP/M memory
lenBDOS		equ	0e00h
lenCCP		equ	800h		
lenCPM		equ	lenBDOS+lenCCP

lenColdBoot	set	100h
lenBios		set	0e80h

     if		HMemDrive
RecsPerBoard	equ	8
lenColdBoot	set	lenColdBoot + 100h
lenBios		set	lenBios + 080h	; room for code
lenBios		set	lenBios + 080h	; room for alloc vector
     endif		; <HMemDrive>

     if		SysConInt
lenBios		set	lenBios + 080h
     endif		; <SysConInt>

     if		i4aConInt
lenBios		set	lenBios + 080h
     endif		; <i4aConInt>

lenBios		set	(lenBios+0ffh) AND 0ff00h

adrBIOS		equ	memsize*1024-lenBios	; CP/M jump table
adrBDOS		equ	adrBIOS-(lenBDOS-6)	; CP/M bdos
adrCCP		equ	adrBIOS-(lenBDOS+lenCCP); CP/M ccp
adrColdBoot	equ	adrBIOS-lenColdBoot	; bios cold boot
TPAsize		equ	(adrBDOS-106h)/400h	; size of TPA
TPAmod		equ	(adrBDOS-106h) mod 400h	; TPA size remainder
zLinkOffset	equ	0a00h -adrColdBoot	; ddt link offset

;	BDOS constants on entry to write

WRalloc		equ	0	; write to allocated
WRdir		equ	1	; write to directory
WRunalloc	equ	2	; write to unallocated


;	-- Page Zero Definitions --

IobDefault	equ	0aah	; default iobyte
ModeDefault	equ	01	; default system mode byte
iobyte		equ	3	; address of Iobyte
ccpDSK		equ	4	; address of current disk
DeviceSelect	equ	40h	; set by cold boot loader
SysMode		equ	40h	; set in cold boot
DefaultBuf	equ	80h	; default record buffer

; 	- SysMode bit map -
; set bit(x) to enable function(x)

; bit 0	=>	- display error debug data
; bit 1 =>	- not used yet
; bit 2 =>	- not used yet
; bit 3	=>	- not used yet
; bit 4	=>	- not used yet
; bit 5 =>	- not used yet
; bit 6	=>	- not used yet
; bit 7	=>	- not used yet
page
*********************************************************
*		CHAR	I/O	EQUATES			*
*		- chari/o .equ -			*
*********************************************************

MaxConPtr	equ	31		; console buf len -1

;	CompuPro System Support equates.

SysBase		equ	50h		; Base port
SysM0pic	equ	SysBase+0	; Master PIC port 0
SysM1pic	equ	SysBase+1	; Master PIC port 1
SysS0pic	equ	SysBase+2	; Slave  PIC port 0
SysS1pic	equ	SysBase+3	; Slave  PIC port 1

SysTim0		equ	SysBase+4	; Timer number 0
SysTim1		equ	SysBase+5	; Timer number 1
SysTim2		equ	SysBase+6	; Timer number 2
SysTimCtl	equ	SysBase+7	; Timer control port

SysFpData	equ	SysBase+8	; Floating point data port
SysFpCmd	equ	SysBase+9	; Floating point command port

SysClkCmd	equ	SysBase+10	; Clock command port
SysClkData	equ	SysBase+11	; Clock data port

SysIoData	equ	SysBase+12	; Serial port data
SysIoStat	equ	SysBase+13	; Serial port Instatus
SysIoMode	equ	SysBase+14	; Serial port mode
SysIoCmd	equ	SysBase+15	; Serial port command
SysXtClear	equ	00000001b	; Transmit buf empty mask
SysRcvFull	equ	00000010b	; Data available mask
nSpecEoi	equ	20h		; non-specific end-of-int
RdIis		equ	0bh		; read int-in-service status

;	CompuPro Interfacer 4 equates.

i4con		equ	7		; select console
i4aux		equ	5		; select auxiliary device
i4list		equ	4		; select list device

i4base		equ	10h		; base port address
i4data		equ	i4base+0	; data port
i4stat		equ	i4base+1	; status port
i4mode		equ	i4base+2	; mode port
i4cmd		equ	i4base+3	; command port
i4xmtInt	equ	i4base+4	; transmit int port
i4rcvInt	equ	i4base+5	; receive int port
i4user		equ	i4base+7	; select user port

i4dsr		equ	10000000b	; data set ready
i4xtClear	equ	00000001b	; Transmit buffer empty
i4rcvFull	equ	00000010b	; Data available
i4ok2send	equ	i4xtClear+i4dsr	; use handshaking
page
*************************************************
*		disk  constants			*
*		- blocki/o.equ -		*
*************************************************

;	Disk Input / Output port assignments

HMemBase	equ	0c6h		; Base port for M-Drive/H
HMemData	equ	HMemBase	; Data port
HMemAdr		equ	HMemBase+1	; Address port

flpBase		equ	0C0h		; Base port adr for FDC
flpStatus	equ	flpBase		; Status register
flpData		equ	flpBase+1	; Data register
flpDmaAdr	equ	flpBase+2	; Dma address (when write)
flpIntPort	equ	flpBase+2	; Status Register (when read)

;	Flop Controller function definitions

flop$spec	equ	03	; Specify
flop$SDS	equ	04	; Sense Drive status
flop$WRdata	equ	05	; Write data
flop$RDdata	equ	06	; Read data
flop$recal	equ	07	; recalibrate
flop$SIS	equ	08	; Sense Interrupt status
flop$RDid	equ	10	; Read ID
flop$Seek	equ	15	; Seek
flop$MFM	equ	40h	; double density bit

len$Spec	equ	3	; length of flop commands
len$SDS		equ	2
len$Max		equ	9
len$ReCal	equ	2
len$SIS		equ	1
len$RdId	equ	2
len$Seek	equ	3
len$Status	equ	7

;	Disk drive constants

MaxRetry	equ	10	; disk retry count
MDrive		equ	'M'-'A'	; M-Drive/H number

OneSided	equ	0
TwoSided	equ	1

dsk128		equ	0	; these are the density values
dsk1024		equ	3	; returned by the NEC 765 chip

dsm128s		equ	((77-2)   *26)/08
dsm128d		equ	((77-2)*2 *26)/16
dsm1024s	equ	((77-2)   *64)/16
dsm1024d	equ	((77-2)*2 *64)/16
dsmHMem		equ	((512-4)  *08)/32	; if only 1 M-Drive/H
page
*********************************************************
*		Cold Boot CP/M 				*
*		- coldboot.asm -			*
*********************************************************

;		The cold boot inits the IoByte (adr 0003),
;	ccpDSK (adr 0004), SysMode (adr 0040), and optionally
;	the console interrupt vector (adrs 002ch - 002eh when
;	using the Interfacer 4 channel A interrupts, otherwise
;	the 8259 PIC interrupt vectors); it then jumps to the
;	warm boot code.
;	    If there is a command in the ccp buffer then cp/m
;	will execute that command.
;		Note that only 1024 byte/sec MFM disks
;	are bootable.
;
;	Disk layout Definition-
;
;	Cylinder 0	Head 0
;	  128 byte sectors, FM encoded
;
;	  	Sectors-
;	  1 thru 2	Cold Boot Loader
;	  3 thru 26	CBIOS
;
;
;	Cylinder 1	Head 0
;	  1024 byte sectors, MFM encoded
;
;	  	Sectors-
;	  1 thru 2	CCP
;	  3 thru 6	BDOS
;	  7 thru 8	future CP/M expansion
;
;-------------------------------


	org	adrColdBoot
;	-------------------

;
;-------
; entry>	none  	  (DeviceSelect set by cold loader)
; exit>		 a = 0ffh (tells Warm: to exec ccp cmd)
;		 c = 0	  (tells Warm: to boot from drive A)
;		de = 0	  (tells Warm: to find drive type)
ColdBoot:	
	lxi	sp,DefaultBuf
	lxi	h,DeviceSelect	; hl = .char i/o device
	mov	a,m		;  a = default i/o board type
	mvi	m,ModeDefault	; hl = .SysMode
	lxi	h,IoByte
	mvi	m,IobDefault	; IoByte = int4 default
	cpi	02		; 2 means Sys Support board
	jnz	InitIO
;
	mvi	m,0		; IoByte = sys support default
InitIO:
	call	i4init	; setup interfacer 4 i/o
	call	SysInit	; setup system support i/o

     if		HMemDrive
	call	sizeMdrive	; 
     endif		; <HMemDrive>

     if		i4aConInt
	mvi	a,jump
	sta	IntVec	; set rst 5.5 adr
	lxi	h,i4aInterrupt
	shld	IntVec+1; adr of interrupt code
;
	mvi	a,0beh	; allow only rst 5.5
	db	sim	; Set Int Mask opcode
	ei		; ints ok now
     endif		; <i4aConInt>

	lxi	h,signON
	call	PrtMsg	; output sign on message
	lxi	d,0	; force find drive type
	xra	a
	sta	ccpDSK	; set current cpm disk
	mov	c,a	; boot from drive zero
	dcr	a	; say this is cold boot
	jmp	Warm	; read sys and exec ccp
;
;-------
; entry>	none
; exit>		none - initializes int 4 i/o
i4init:
	mvi	a,i4con		; port to init
	lxi	h,ConInit	; adr of init array
	call	doi4init

     if		i4aConInt
	mvi	a,80h		; enable console int
	out	i4rcvInt
     endif		; <i4aConInt>
;
	mvi	a,i4list	; port to init
	call	doi4init	; hl = .init string
;
	mvi	a,i4aux		; port to init
;	fall thru to doi4init	; hl = .init string
;
;-------
; entry>	 a = port to init
;		hl = adr of init string
; exit>		hl = adr of next init string
doi4init:
	out	i4user	; select uart
	mov	a,m
	out	i4mode	; mode reg 1
;
	inx	h
	mov	a,m
	out	i4mode	; mode reg 2
;
	inx	h
	mov	a,m
	out	i4cmd	; command reg
;
	inx	h	; hl = .string(next)
	ret
;
;-------
; entry>	none
; exit>		none - initializes sys support i/o
SysInit:

; 2651 UART initialization
	mvi	a,6eh	; async, 1 stop, 8 data, no parity, x16
	out	SysIoMode	; mode reg 1
;
	mvi	a,3eh	; internal clock, 9600 baud
	out	SysIoMode	; mode reg 2
;
	mvi	a,27h	; rts and dtr true, enable xmit and rcv
	out	SysIoCmd	; command reg

     if not	SysConInt
	ret
     endif		; <not SysConInt>

     if		SysConInt

; master 8259 interrupt controller initialization
	mvi	a,(low MastVecs + 0101b) OR 10h
	out	SysM0pic	; icw1 - int adr low
;
	mvi	a,high MastVecs
	out	SysM1pic	; icw2 - int adr high
;
	mvi	a,80h
	out	SysM1pic	; icw3 - IR7 slave
;
	mvi	a,10h
	out	SysM1pic	; icw4 - SFNM

; slave 8259 interrupt controller initialization
	mvi	a,(low SlaveVecs + 0100b) OR 10h
	out	SysS0pic	; icw1 - int adr low
;
	mvi	a,high SlaveVecs
	out	SysS1pic	; icw2 - int adr high
;
	mvi	a,7
	out	SysS1pic	; icw3 - slave id

; 8259 operational initialization
	mvi	a,7fh		; ocw1 - interrupt mask
	out	SysM1pic	; mask all but IR7 in master
	out	SysS1pic	; and in slave
	ei			; ints ok now
	ret
     endif		; <SysConInt>

     if		HMemDrive
sizeMdrive:
	lxi	h,4 *128	; hl = first data track
	xra	a		;  a = initial recs_per_track
	mov	b,a		;  b = initial board count
sizeloop:
	mov	c,a		; recs_per_track is never > 64
	call	ck4present	; see if a board is present
	jnz	sizeDone	; jump if no board
;
	mov	a,c
	adi	RecsPerBoard	; a = current recs_per_track
	inr	b		; b = current board count
	jmp	sizeloop
;
sizeDone:
	lxi	h,msgNoBoard	; if recs_per_track is zero
	mov	a,b		; then no M-Drive/H boards
	ora	a		; are present
	jz	msgOverlay
;
	mvi	h,0
	mov	l,c		; hl = 8,16,24,32,40,48,56, or 64
	shld	dpbHmem		; set dpb rec_per_track field
	adi	40h		; save #boards so admform.com can
	sta	dpbHmem -1	; tell what size M-Drive we have
;
	mov	a,b		; b = board count (1 - 8)
	dcr	a		; default initialization
	rz			; is for one M-Drive/H
;
	ani	7		; - just in case
	mov	b,a		; b = board index (1 - 7)

	push	b
	lxi	h,MDHsizeTable -lenMDHentry
	lxi	d,lenMDHentry
calcOvrlayAdr:
	dad	d
	dcr	b
	jnz	calcOvrlayAdr
;
	lxi	d,MDHexm	; overlay the M-Drive/H dpb with the
	lxi	b,lenMDHentry	; calculated exm, dsm, drm, and rdb
	call	MoveData
;
	pop	b
	lxi	h,msgTable -lenMessage
	lxi	d,lenMessage
calcMsgAdr:
	dad	d
	dcr	b
	jnz	calcMsgAdr
;
msgOverlay:
	lxi	d,Msize		; overlay the signon Msize
	lxi	b,lenMessage	; message with calculated size
	jmp	MoveData
;
;-------
; entry>	 a = sector
;		hl = track
; exit>		Z set if board present
ck4present:
	mov	e,a		; save the sector#
	call	sendLocation	; tell the M-Drve/H the data adr
	in	HMemData	; get a byte
	mov	d,a		; and save it
	mov	a,e
	call	sendLocation	; reset the data address
	mov	a,d
	cma
	out	HMemData	; write the inverted byte
	mov	a,e
	call	sendLocation
	in	HMemData	; get the data again
	cma			; invert again
	cmp	d		; check it against original
	rnz			; return if no match
;
	mov	a,e
	call	sendLocation
	mov	a,d		; else restore original data
	out	hMemData
	xra	a		; set Z flag for board present
	ret
;
;-------
; data storage for M-Drive/H initialization
msgTable:
	db	'1.0 Meg'
	db	'1.5 Meg'
	db	'2.0 Meg'
	db	'2.5 Meg'
	db	'3.0 Meg'
	db	'3.5 Meg'
	db	'4.0 Meg'

msgNoBoard:
	db	'with no'
lenMessage	equ	$ -msgNoBoard


MDHsizeTable:
; 1.0 meg M-Drive/H
		db	3	; extent mask
		dw	253	; disk storage maximum
		dw	127	; directory maximum
		db	80h	; reserved dir blocks
lenMDHentry	equ	$ -MDHsizeTable

; 1.5 meg M-Drive/H
		db	1	; extent mask
		dw	380	; disk storage maximum
		dw	255	; directory maximum
		db	0c0h	; reserved dir blocks
; 2.0 meg M-Drive/H
		db	1	; extent mask
		dw	507	; disk storage maximum
		dw	255	; directory maximum
		db	0c0h	; reserved dir blocks
; 2.5 meg M-Drive/H
		db	1	; extent mask
		dw	634	; disk storage maximum
		dw	383	; directory maximum
		db	0e0h	; reserved dir blocks
; 3.0 meg M-Drive/H
		db	1	; extent mask
		dw	761	; disk storage maximum
		dw	383	; directory maximum
		db	0e0h	; reserved dir blocks
; 3.5 meg M-Drive/H
		db	1	; extent mask
		dw	888	; disk storage maximum
		dw	511	; directory maximum
		db	0f0h	; reserved dir blocks
; 4.0 meg M-Drive/H
		db	1	; extent mask
		dw	1015	; disk storage maximum
		dw	511	; directory maximum
		db	0f0h	; reserved dir blocks
     endif		; <HMemDrive>
;
;------
ConInit:
	db	6eh	; async, 1 stop, 8 data, no parity, x16
	db	3eh	; internal clock, 9600 baud
	db	27h	; rts and dtr true, enable xmit and rcv
;
;------
ListInit:
	db	6eh	; async, 1 stop, 8 data, no parity, x16
	db	3eh	; internal clock, 9600 baud
	db	27h	; rts and dtr true, enable xmit and rcv
;
;------
AuxInit:
	db	6eh	; async, 1 stop, 8 data, no parity, x16
	db	3eh	; internal clock, 9600 baud
	db	27h	; rts and dtr true, enable xmit and rcv
;
;------
SignOn:
	db	cr,lf,cr,lf
	db	memsize/10+'0',memsize mod 10 +'0'
	db	'k CP/M system - '
	db	TPAsize/10 +'0',TPAsize mod 10 +'0'
	db	'k tpa'

     if		HMemDrive
	db	', '
Msize:	db	'   512k M-Drive/H present'
     endif		; <HMemDrive>

	db	cr,lf,'BDOS vers '
	db	version/10+'0','.',version mod 10 +'0'
	db	' copyright by Digital Research'
	db	cr,lf,'BIOS vers '
	db	level + '0','.',rev
	db	' copyright by apposite designs'
	db	cr,lf,0

actualColdSize	equ	$ -ColdBoot
page
*********************************************************
*	  B I O S    J U M P   T A B L E		*
*		- biosjump.tbl -			*
*********************************************************


	org	adrBIOS
;	---------------

; standard Digital Research Bios Jump Table

	jmp	ColdBoot	; Cold boot
	jmp	WarmBoot	; Warm boot
	jmp	ConStat		; Console status (input)
	jmp	ConIn		; Console input
	jmp	ConOut		; Console output
	jmp	ListOut		; List output
	jmp	Punch		; Punch output
	jmp	Reader		; Reader input
	jmp	Home		; Flush flop bufs
	jmp	selDSK		; Select disk unit
	jmp	setTRK		; set track
	jmp	setREC		; set cp/m record
	jmp	setDMA		; set Disk Memory Address
	jmp	Read		; Read from disk
	jmp	Write		; Write onto disk
	jmp	ListStat	; List status (output)
	jmp	secTRAN		; Translate sector number

; apposite designs specific jump table

	jmp	FlushBufs	; write dirty disk buffers
	jmp	BiosVers	; return current bios version
	jmp	SetUserBank	; set user dma bank
	jmp	SetDmaBank	; set disk dma bank
page
*********************************************************
*	System Support Interrupt Vectors		*
*	------ ------- --------- -------		*
*		- sysVecs .int -			*
*********************************************************

     if		SysConInt

	org	($+1fh) AND (NOT 1fh)

; vectors must start on a 32 byte boundary and
; are 4 bytes long (set by 8259 initialization)
; Only slave vector IR7 is currently used so
; all other interrupts simply warm boot

MastVecs:
	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0


	org	($+1fh) AND (NOT 1fh)

SlaveVecs:
	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	ei		; ensure ints enabled
	rst	0	; warm boot for now
	dw	0

	jmp	SysIoInt; console interrupt

     endif		; <SysConInt>
page
*********************************************************
*	Warm boot  --  read in ccp, bdos from disk	*
*		- warmboot.asm -			*
*********************************************************

;	The warm boot code reads the CCP and BDOS from
;	the disk and sets:
;
;	adr 0000 - 0002h  =>	warm start jump vector
;	adr 0005 - 0007h  =>	BDOS entry vector
;	cp/m user dma	  =>	default record buffer
;	stack pointer	  =>	default record buffer
;
;	The first warm boot after a cold boot exits to the
;	CCP and will execute any resident CCP command; all
;	other warm boots exit to the CCP+3  and will not
;	execute resident CCP commands. All warm boots exit
;	with the C register set to the appropriate drive
;	selection value for the CCP drive login.
;

;
;-------
; entry>	de = 0  -> determine drive type
;		de = 1  -> drive type is known
; exit>		de = entry value
;		 a = 0	-> don't exec CCP commands
;		 c = 0	-> select drive A
WarmBoot:
	xra	a		; say this is warm boot
	mov	c,a		; boot from 'A' drive
;
;-------
; entry>	 a = 0  -> this is a warm boot
;		 a = ff -> this is a cold boot
;		 c = drive to select
;		de = 0  -> determine drive type
;		de = 1  -> drive type is known
; exit>		reads in bdos and ccp then jumps to ccp
Warm:
	lxi	sp,DefaultBuf	; reset stack pointer
	push	psw		; save boot type
	call	SelDsk		; select drive
	call	home		; flush bufs and home head
	lhld	cpmType		; set cpm Dens/2sided
	shld	CurType		; into Cur Dens/2sided
	call	readSYS		; read bdos and ccp
;
	lxi	b,DefaultBuf
	call	setDMA		; set dma adr to default
	mvi	a,jump
	sta	WarmVec
	sta	BdosVec
	lxi	h,adrBIOS+3	; jmp table(warm boot)
	shld	WarmVec+1	; set jump adr
	lxi	h,adrBDOS
	shld	BdosVec+1	; set jump adr
;
	lxi	h,ccpDSK
	mov	c,m		; c = cur disk
	pop	psw		; a = boot type
	ora	a
	jnz	adrCCP		; exec ccp command buf
;
	jmp	adrCCP+3	; don't exec ccp cmd buf
;
;-------
BootErr:
	call	FindErr		; parse error and retry
;	jmp	ReadSys		; func if desired
;
;-------
; entry>	none
; exit>		none  -- reads ccp and bdos
ReadSys:
	lxi	h,adrCCP	; read system into
	shld	AdrCurBuf	; memory starting at
	call	adr2fdc		; ccp base
;
	call	WarmParams	; disk, side, track, sec, dens, eot
	call	set$GPL$DTL	; gpl, dtl
	call	Seek
	mvi	c,flop$RDdata	; read ccp and most of 
	call	ExecFlop	; bdos and check results
	jnz	BootErr		; error on read
;
	lxi	h,ActBuf	; set active buf adr
	shld	AdrCurBuf	; to normal disk buf
	call	adr2fdc
;
	mvi	a,6
	sta	flopMax		; set up to read the
	sta	flopSec		; last part of bdos
	mvi	c,flop$RDdata	; read last part of bdos 
	call	ExecFlop	; and check results
	jnz	BootErr		; error
;	
	lxi	h,ActBuf	; move last part of bdos
	lxi	d,adrCCP+(5*1024)
	lxi	b,lenCPM-(5*1024)
	jmp	MoveData
;
;-------
; entry>	none
; exit>		none  --  sets disk, side, track, sec, dens, eot
warmParams:
	xra	a
	sta	flopDsk		; boot from drive 0
	sta	flopSide	; side 0 always
;
	inr	a		; system on track 1
	sta	flopTrk		; set track
	sta	flopSec		; sector 1
;
	lda	CurDens
	sta	flopDen
;
	mvi	a,5
	sta	flopMax		; set eot
	ret
page
*********************************************************
*		XTL, DPH, DPB, TABLES			*
*		- tables  .dsk -			*
*********************************************************

xltMap:	
	dw	xlt128		; FM 128  b/sec
	dw	0		; not used
	dw	0		; not used
	dw	xlt1024		; MFM 1024 b/sec



**
**		Sector Translation Tables
**		-------------------------
**

xlt128:	
	db	01,07,13,19,25,05,11,17,23,03,09,15,21
	db	02,08,14,20,26,06,12,18,24,04,10,16,22



xlt1024:	
	db	01,02,03,04,05,06,07,08
	db	25,26,27,28,29,30,31,32
	db	49,50,51,52,53,54,55,56
	db	09,10,11,12,13,14,15,16
	db	33,34,35,36,37,38,39,40
	db	57,58,59,60,61,62,63,64
	db	17,18,19,20,21,22,23,24
	db	41,42,43,44,45,46,47,48
page
**
**		Disk Parameter Headers
**		----------------------
**

ndisks	set	0	; number of disks

dphBase:

;	--------	drive A:	--------	;

	dw	xlt1024		; sector translate table adr
	dw	0,0,0		; bdos workspace
	dw	DirBuf		; dir buf adr
	dw	dpb1024s	; dpb adr
	dw	CkVec0		; dir check vector adr
	dw	AlVec0		; block allocation vector adr

ndisks	set	ndisks+1



;	--------	drive B:	--------	;

	dw	xlt1024		; sector translate table adr
	dw	0,0,0		; bdos workspace
	dw	DirBuf		; dir buf adr
	dw	dpb1024s	; dpb adr
	dw	CkVec1		; dir check vector adr
	dw	AlVec1		; block allocation vector adr

ndisks	set	ndisks+1



     if		HMemDrive

;	--------	drive M:	--------	;

dphMem:
	dw	0		; no record translate table
	dw	0,0,0		; bdos workspace
	dw	DirBuf		; dir buf adr
	dw	dpbHMem		; dpb adr
	dw	0		; no dir check vector
	dw	AlVecHMem	; block allocation vector adr

     endif		; <HMemDrive>
page
**
**		Disk Parameter Blocks
**		---------------------
**

dpbMap:
	dw	dpb128s		; single sided
	dw	dpb128d		; double sided
	dw	0,0		; not using 256 b/sec
	dw	0,0		; not using 512 b/sec
	dw	dpb1024s	; single sided
	dw	dpb1024d	; double sided


; ++	Disk Parameter Tables

	; Single density, single sided --  128 b/sec
	db	OneSided	; 1 or 2 sided value
	db	dsk128		; disk density
dpb128s:
	dw	26		; records/track
	db	3,7,0		; bsh,blm,exm
	dw	dsm128s-1	; dsm
	dw	64-1		; drm
	db	0c0h,0		; size of alloc vec
	dw	(64+3)/4	; size of check vec (dir)
	dw	2		; track offset

	; Single density, double sided --  128 b/sec
	db	TwoSided	; 1 or 2 sided value
	db	dsk128		; disk density
dpb128d:
	dw	26		; records/track
	db	3,7,0		; bsh,blm,exm
	dw	dsm128d-1	; dsm
	dw	128-1		; drm
	db	0f0h,0		; size of alloc vec
	dw	(128+3)/4	; size of check vec (dir)
	dw	4		; track offset

	; Double density, single sided -- 1024 b/sec
	db	OneSided	; 1 or 2 sided value
	db	dsk1024		; disk density
dpb1024s:
	dw	8*8		; records/track
	db	4,15,0		; bsh,blm,exm
	dw	dsm1024s-1	; dsm
	dw	128-1		; drm
	db	0c0h,0		; size of alloc vec
	dw	(128+3)/4	; size of check vec (dir)
 	dw	2		; track offset

	; Double density, double sided -- 1024 b/sec
	db	TwoSided	; 1 or 2 sided value
	db	dsk1024		; disk density
dpb1024d:
	dw	8*8		; records/track
	db	4,15,0		; bsh,blm,exm
	dw	dsm1024d-1	; dsm
	dw	256-1		; drm
	db	0f0h,0		; size of alloc vec
	dw	(256+3)/4	; size of check vec (dir)
 	dw	4		; track offset

     if		HMemDrive
	; M-Drive/H, 512k total -- 128 b/rec
	; adMform overlays this dpb for multiple boards

	db	40h		; init to no boards
dpbHMem:
	dw	8		; records/track
	db	5,31		; bsh,blm
MDHexm:	db	3		; exm
	dw	dsmHMem-1	; dsm
	dw	128-1		; drm
	db	80h,0		; size of alloc vec
	dw	0		; size of check vec (dir)
 	dw	4		; track offset
     endif		; <HMemDrive>
page
*********************************************************
*		CHARACTER  I/O  ROUTINES		*
*		- chari/o.asm -				*
*********************************************************

;	++	Select an I/O Routine
;		------ -- --- -------

;	Note>	Port i4b always uses Data Set Ready
;		handshaking for char output and output
;		status. It is the only port to do so.

;
;-------
; entry>	none
; exit>		a = input status (0ffh => has data)
ConStat:
	lda	IoByte
	ani	3		; allow 4 choices
	call	execute
	dw	SysInStat	; default SysSup
	dw	i4cInStat
	dw	i4aInStat	; default int4
	dw	i4cInStat
;
;-------
; entry>	none
; exit>		a = input char
ConIn:
	lda	IoByte
	ani	3		; allow 4 choices
	call	execute
	dw	SysInPut	; default SysSup
	dw	i4cInPut
	dw	i4aInPut	; default int4
	dw	i4cInPut
;
;-------
; entry>	c = output char
; exit>		none
ConOut:
	lda	IoByte
	ani	3		; allow 4 choices
	call	execute
	dw	SysOutPut	; default SysSup
	dw	i4cOutPut
	dw	i4aOutPut	; default int4
	dw	i4cOutPut
;
;-------
; entry>	c = output char
; exit>		none
Punch:
	lda	IoByte
	rar
	rar
	ani	3		; allow 4 choices
	call	execute
	dw	i4bOutPut	; default SysSup
	dw	i4cOutPut
	dw	i4cOutPut	; default int4
	dw	i4bOutPut
;
;-------
; entry>	none
; exit>		a = input char
Reader:
	lda	IoByte
	rar
	rar
	rar
	rar
	ani	3		; allow 4 choices
	call	execute
	dw	i4bInPut	; default SysSup
	dw	i4cInPut
	dw	i4cInPut	; default int4
	dw	i4bInPut
;
;-------
; entry>	none
; exit>		a = output status (a = 0ffh => ok2send)
ListStat:
	lda	IoByte
	rlc
	rlc
	ani	3		; allow 4 choices
	call	execute
	dw	i4bOutStat	; default SysSup
	dw	SysOutStat
	dw	i4bOutStat	; default int4
	dw	i4aOutStat
;
;-------
; entry>	c = output char
; exit>		none
ListOut:
	lda	IoByte
	rlc
	rlc
	ani	3		; allow 4 choices
	call	execute
	dw	i4bOutPut	; default SysSup
	dw	SysOutPut
	dw	i4bOutPut
	dw	i4aOutPut
page
;	++	Perform the I/O
;		------- --- ---

;
;-------
; entry>	none
; exit>		a = 0ffh if data available
;		Z  reset if data available
SysInStat:

     if	not	SysConInt
	in	SysIoStat
	ani	SysRcvFull
	rz
;
	ori	0ffh
	ret
     endif		; <not SysConInt>

     if		SysConInt
	ei
	lhld	ConPtrs	; H = LastGot, L = LastPut
	mov	a,h
	inr	a	; inr in case we take char
	ani	MaxConPtr
	mov	c,a	; new LastGot mod MaxConPtr
	sub	l	; pointers equ means buf empty
	rz
;
	ori	0ffh
	ret
     endif		; <SysConInt>

;
;-------
; entry>	none
; exit>		a = 0ffh if data availble
;		Z  reset if data available
i4aInStat:

     if	not	i4aConInt
	mvi	a,i4con		; select user 7
	out	i4user
	in	i4Stat
	ani	i4rcvFull
	rz
;
	ori	0ffh
	ret
     endif		; <not i4aConInt>

     if		i4aConInt
	ei
	lhld	ConPtrs	; H = LastGot, L = LastPut
	mov	a,h
	inr	a	; inr in case we take char
	ani	MaxConPtr
	mov	c,a	; new LastGot mod MaxConPtr
	sub	l	; pointers equ means buf empty
	rz
;
	ori	0ffh
	ret
     endif		; end <i4aConInt>

;
;-------
; entry>	none
; exit>		a = 0ffh if data available
;		Z  reset if data available
i4bInStat:
	mvi	a,i4list	; select user 4
	out	i4user
	in	i4stat
	ani	i4rcvFull
	rz
;
	ori	0ffh
	ret
;
;-------
; entry>	none
; exit>		a = 0ffh if data available
;		Z  reset if data available
i4cInStat:
	mvi	a,i4aux		; select user 5
	out	i4user
	in	i4stat
	ani	i4rcvFull
	rz
;
	ori	0ffh
	ret
;
;-------
; entry>	none
; exit>		a = input char
SysInPut:
	call	SysInStat
	jz	SysInPut
;
     if not	SysConInt
	in	SysIoData
	ani	7fh
	ret
     endif		; <not SysConInt>

     if		SysConInt
	mov	a,c
	sta	LastGot
	mvi	b,0
	lxi	h,ConBufBase
	dad	b
	mov	a,m
	ret
     endif		; <SysConInt>

;
;-------
; entry>	none
; exit>		a = input char
i4aInPut:
	call	i4aInStat
	jz	i4aInPut
;
     if	not	i4aConInt
	in	i4data
	ani	7fh
	ret
     endif		; <not i4aConInt>

     if		i4aConInt
	mov	a,c
	sta	LastGot
	mvi	b,0
	lxi	h,ConBufBase
	dad	b
	mov	a,m
	ret
     endif		; <i4aConInt>

;
;-------
; entry>	none
; exit>		a = input char
i4bInPut:
	call	i4bInStat
	jz	i4bInPut
;
	in	i4data
	ani	7fh
	ret
;
;-------
; entry>	none
; exit>		a = input char
i4cInPut:
	call	i4cInStat
	jz	i4cInPut
;
	in	i4data
	ani	7fh
	ret
;
;-------
; entry>	none
; exit>		a = output status (0ffh => ok2send)
SysOutStat:
	in	SysIoStat
	ani	SysXtClear
	mvi	a,0ffh
	rnz			; ok to send
;
	xra	a		; not ok to send
	ret
;
;-------
; entry>	none
; exit>		a = output status (0ffh => ok2send)
i4aOutStat:
	mvi	a,i4con		; select user 7
	out	i4user
	in	i4stat
	ani	i4xtClear	; ck xmit buf empty
	mvi	a,0ffh
	rnz			; ok to send
;
	xra	a		; not ok to send
	ret
;
;-------
; entry>	none
; exit>		a = output status (0ffh => ok2send)
i4bOutStat:
	mvi	a,i4list	; select user 4
	out	i4user
	in	i4stat
	ani	i4ok2send	; ck xmit buf empty
	cpi	i4ok2send	; and Data Set Ready
	mvi	a,0ffh
	rz			; ok to send
;
	xra	a		; not ok to send
	ret
;
;-------
; entry>	c = output char
; exit>		none
SysOutPut:
	call	SysOutStat
	jz	SysOutPut
;
	mov	a,c
	out	SysIoData
	ret
;
;-------
; entry>	c = output char
; exit>		none
i4aOutPut:
	call	i4aOutStat
	jz	i4aOutPut
;
	mov	a,c
	out	i4data
	ret
;
;-------
; entry>	c = output char
; exit>		none
i4bOutPut:
	call	i4bOutStat
	ora	a
	jz	i4bOutPut
;
	mov	a,c
	out	i4data
	ret
;
;-------
; entry>	c = output char
; exit>		none
i4cOutPut:
	mvi	a,i4aux		; select user 5
	out	i4user
	in	i4stat
	ani	i4xtClear
	jz	i4cOutPut
;
	mov	a,c
	out	i4data
	ret
;
;-------
; - Interrupt driven console routine
; entry>	obviously none
; exit>		char in buffer

     if		SysConInt
SysIoInt:
	push	h
	push	b
	push	psw
;
	ei		; let something else interrupt
	lhld	ConPtrs	; h = LastGot, L = LastPut
	mov	a,l
	cmp	h	; LastGot = LastPut means
	jz	NoRoom	; input buffer is full
;
	inr	a
	ani	MaxConPtr
	sta	LastPut
;
	lxi	b,ConBufBase
	mvi	h,0
	dad	b		; actual buf input adr
;
	in	SysIoData	; get input char
	ani	7fh		; strip parity
	mov	m,a		; store input char in buf
	jmp	EndInt
;
NoRoom:
	in	SysIoData	; flush char to reset Rx int
	mvi	c,bell		; alert user that buf is full
	call	SysOutPut
EndInt:
	mvi	a,nSpecEoi	; 0cw2
	out	SysS0pic	; send End-of-Int to slave
	mvi	a,RdIis		; 0cw3
	di			; ensure no reprogramming
	out	SysS0pic	; send Rd-Int-In-Service to slave
	in	SysS0pic	; read the Iis status
	ei			; ints ok now
	ora	a
	jnz	PopRegs		; slave still in int code
;
	mvi	a,nSpecEoi	; tell master slave is done
	out	SysM0pic	; with all interrupts
PopRegs:
	pop	psw
	pop	b
	pop	h
	ret
     endif		; <SysConInt>

;
;-------
; - Interrupt driven console routine
; entry>	obviously none
; exit>		char in buffer

     if		i4aConInt
i4aInterrupt:
	push	h
	push	b
	push	psw
;
	mvi	a,i4con
	out	i4user	; select channel
;
	lhld	ConPtrs	; h = LastGot, L = LastPut
	mov	a,l
	cmp	h	; LastGot = LastPut means
	jz	NoRoom	; input buffer is full
;
	inr	a
	ani	MaxConPtr
	sta	LastPut
;
	lxi	b,ConBufBase
	mvi	h,0
	dad	b	; actual buf input adr
;
	in	i4data	; get input char
	ani	7fh	; strip parity
	mov	m,a	; store input char in buf
	jmp	EndInt
;
NoRoom:
	in	i4data	; flush char to reset Rx int
	mvi	c,bell	; alert user that buf is full
	call	i4aOutPut
EndInt:
	pop	psw
	pop	b
	pop	h
	ei		; ints ok now
	ret
     endif		; <i4aConInt>
page
*********************************************************
*		bios utilities				*
*		- utilitys.asm -			*
*********************************************************

;-------
; entry>	a  = item#
;		TOS= .ExecTable(0)
; exit>		executes ith item
; used>		a,de
execute:
	mvi	d,0
	mov	e,a	; index into table
	xthl		; .execution table
	dad	d
	dad	d	; table is of words (adrs)
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a
	xthl
	ret
;
;-------
; entry>	bc = number of bytes to move
;		hl = source address.
;		de = destination address.
; exit>		data xferred
MoveData:
	mov	a,b	; check for len equ zero
	ora	c
	rz		; all xfer'd
;
	mov	a,m	; Source character
	stax	d	; to destination
	inx	h
	inx	d
	dcx	b
	jmp	MoveData; transfer not complete
;
;-------
; entry>	TOS = return address
; exit>		a = 0 if retry function is requested
wait4user:
	lxi	h,RetryCnt
	mvi	m,MaxRetry	; reset retry counter
	lxi	h,rtryMSG
	call	PrtMsg		; print '** A = abort' etc
	call	ConIn		; get user response
	ani	0dfh		; force to upper case
	cpi	'A'
	jz	WarmVec		; warm boot if 'A'
;
	cpi	'I'		; if not 'I' (Ignore error)
	jnz	ck4ret		; then check for <CR>
;
	xra	a		; return good status to cp/m
	pop	h		; Ignore error and ret
	ret			; to original caller
;
ck4ret:
	cpi	cr		; no valid response
	jnz	wait4user	; so ask again
;
	xra	a		; return a = 0 (for error)
	ret			; for those routines which care
;
;-------
; entry>	hl = adr of message string
; exit>		on null char
PrtMsg:
	mov	a,m	; get next character
	ora	a
	rz
;
	push	h
	mov	c,a
	call	conout	;Output to the console
	pop	h
	inx	h
	jmp	PrtMsg
;
;-------
; entry>	 b = len of buf
;		hl = adr of buf
; exit>		does formatted output
PrtBuf:
	push	b		; save buf len
	push	h		; save buf adr
	mvi	c,' '
	call	conout		; print 2 spaces
	mvi	c,' '
	call	conout
	pop	h		; get buf adr
	push	h		; and save it again
	mov	a,m
	call	PrtByt		; print a byte in hex
	pop	h		; get buf adr
	pop	b		; get buf len
	inx	h		; inc adr
	dcr	b		; dcr len
	jnz	PrtBuf		; jump till done
	ret

;-------
; entry>	 a = hex byte
; exit>		 none
PrtByt:
	push	psw
	rrc
	rrc
	rrc
	rrc
	call	nibble	; print high nibble
	pop	psw
 nibble:
	ani	0fh
	adi	'0'	    ; convert 0-9 to hex
	cpi	'9'+1
	jc	outBYT
;
	adi	'A'-('9'+1) ; convert 10-15 to hex
outBYT:
	mov	c,a
	jmp	conout
page
*********************************************************
*	    S E L E C T   D I S K   D R I V E		*
*		   - selfuncs.asm -			*
*********************************************************
;
;	Select the disk drive for subsequent disk transfers
;	and return the appropriate DPH address. This routine
;	determines the correct format and initializes the DPB
;	with the appropriate values for the format type.
;
; entry>	 c = drive number
;	    de lsb = 0, must find type
;	    de lsb = 1, type is known
; exit>		hl = 0 if drive not selectable
;		hl = dph(xlt) if selectable
; used>		all
selDSK:
	mov	a,c
	cpi	ndisks
	jc	getDph	; legal drive request
;
     if		HMemDrive
	cpi	MDrive	; check for M-Drive/H
	jnz	SelErr
;
	sta	cpmDisk	; save M-Drive/H request
	lxi	h,dphMem; and return the dph
	ret
     endif		; <HMemDrive>

SelErr:
	lxi	h,0	; - no such drive
	xra	a	; force drive 0
	sta	ccpDSK	; since selection
	ret		; is in error
;
getDph:
	sta	cpmDisk
	push	d	; save selection request
	mov	l,c
	mvi	h,0	
	dad	h	; *2
	dad	h	; *4
	dad	h	; *8
	dad	h	; *16
	lxi	d,dphBASE
	dad	d	; hl = .dph(xlt)
;
	lxi	d,10	; adrDPB is DPH+10
	xchg		; de = .dph(xlt)
	dad	d	; hl = .dph(dpb)
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	; hl = .dpb
	xchg		; de = .dpb, hl = .dph(xlt)
	dcx	d	; de = .dpb(type)
	ldax	d	; density
	sta	cpmDens
	dcx	d
	ldax	d	; 1 or 2 sided flag
	sta	cpm2side
	pop	d	; drive selection request
	mov	a,e
	rrc		; lsb = selection bit
	rc		; type is known so return

;
;    ++  disk type must be determined  ++
;    ------------------------------------

	push	h	; hl = .dph(xlt)
	call	FindType;  a = disk type
;
	mov	e,a
	mvi	d,0
	lxi	h,dpbMap
	dad	d
	dad	d
	mov	e,m
	inx	h
	mov	h,m
	mov	l,e	; hl = .dpb(sel)
	xchg		; de = .dpb(curType)
;
	pop	h
	push	h	; hl = .dph(xlt)
	lxi	b,10
	dad	b	; hl = .dph(dpb)
	mov	m,e
	inx	h
	mov	m,d	; set new dph(dpb)
	ani	06	; a = type, lsb = 2 sided bit
	mov	e,a
	mvi	d,0
	lxi	h,xltMap
	dad	d	; hl = adr of new xlt table
	xchg		; de = adr of new xlt table
	pop	h	; hl = .dph(xlt)
	ldax	D
	mov	m,a
	inx	h	; set new translate table adr
	inx	d	; into the DPH
	ldax	d
	mov	m,a
	dcx	h
	ret
page
;
;-------
;	set cpmTRK to zero and flush the disk buffers.
Home:
	lxi	h,0
	shld	cpmTRK
;
FlushBufs:
	call	flushACT
	lxi	h,ActDisk
	mvi	m,0ffh	; mark buffer as invalid
	ret
;
;-------
; entry>	bc = track number
; exit>		none
setTRK:
	mov	l,c
	mov	h,b
	shld	cpmTRK
	ret
;
;-------
; entry>	bc = record number
; exit>		none
setREC:
	mov	a,c
	sta	cpmREC		; sector to seek
	ret
;
;-------
; entry>	bc = dma address
; exit>		none
setDMA:
	mov	h,b
	mov	l,c
	shld	adrUSRdma
	ret
;
;-------
;	Translate sector number from logical to physical.
; entry>	de = 0, no translation required.
;		de = translation table address.
;		bc = sector number to translate.
; exit>		hl = translated sector.
secTRAN:
	mov	a,d
	ora	e
	jnz	trans
;		
	inx	b	; bdos rec# is relative to zero
	mov	l,c	; physical sectors start w/one
	mov	h,b
	ret	
;
trans:
	xchg
	dad	b
	mov	l,m
	mvi	h,0
	ret
;
;-------
BiosVers:
	lxi	h,VersLoc
	mvi	c,LenVers
	ret
;
;-------
SetUserBank:
	mov	a,c		; bios does not currently
	sta	UserBank	; make use of UserBank
	ret
;
;-------
SetDmaBank:
	mov	a,c
	sta	DmaBank
	ret
page
*********************************************************
*		deblocking routines			*
*		- deblock .asm -			*
*********************************************************
;
;-------
; entry>	none
; exit>		a  = read flag
read:
	mvi	a,0ffh	; mark transfer as read
	jmp	transfer
;
;-------
; entry>	c  = write type
; exit>		a  = write flag
write:
	mov	a,c	; bdos passes write type in 'c'
	sta	WrType
	xra	a	; mark transfer as write
;
;-------
; entry>	a = transfer direction
; exit>		xfer primitive
transfer:
	sta	RDorWR		; store transfer direction
	lxi	h,RetryCnt
	mvi	m,MaxRetry	; init retry counter
;
     if		HMemDrive
	lda	cpmDisk
	cpi	MDrive		; check for M-Drive/H
	jz	HMemTransfer	; jump if it's M-Drive/H
     endif		; <HMemDrive>
;
	call	CheckAct	; see if it's in a buffer
	jz	InActBuf
;
	call	FlushAct
	lhld	cpmType		; last selected dens/2sided
	lda	cpmTrk		; check for track zero
	ora	a
	jnz	SetType
;
	lxi	h,0		; track 0 is FM, Onesided
SetType:
	shld	CurType		; last select -> physical
	call	SetAct		; cp/m desc -> act desc
	call	qReadReq	; read/write/unallocated
	cnz	ReadFlop	; read unless prev unAlloc

InActBuf:
	lhld	AdrUsrDma
	xchg			; de = .user buffer
	call	GetBufDisp	; bc = byte offset
	lhld	AdrCurBuf	; hl = .disk buf
	dad	b		; hl = .disk buffer(offset)
	lxi	b,RecSize	; bc = record size
	lda	RDorWR		; read/write flag
	ora	a
	jz	WriteBuf

ReadBuf:
	call	MoveData
	xra	a		; return success to cp/m
	ret

WriteBuf:
				; hl = adr(user buf)
	xchg			; de = adr(disk buf)
	call	MoveData
	mvi	a,0ffh
	sta	ActDirty
	xra	a		; return success to cp/m
	ret

     if		HMemDrive
;
;-------
; entry>	none
; exit>		a = 0 for success
HMemTransfer:
	lhld	cpmTrk		; requested track
	dad	h		; * 2
	dad	h		; * 4
	dad	h		; * 8
	dad	h		; * 16
	dad	h		; * 32
	dad	h		; * 64
	dad	h		; * 128
	call	SetDataLoc	; tell M-Drive/H the location
	lhld	AdrUsrDma	; hl = .userDma
	lxi	b,(128 shl 8) + 128
	lda	RdorWr		; transfer direction flag
	ora	a
	jz	HMemWrite
;
;-------
; - transfer data from M-Drive/H
;   data location already set
; entry>	hl = .userDma
;		 b = parity byte
;		 c = data length
; exit>		 Z set if transfer good
HMemRead:
	in	HMemData	; get the data byte
	mov	m,a		; save it
	add	b		; add in parity byte
	mov	b,a		; - for next byte
	inx	h
	dcr	c
	jnz	HMemRead
;
	call	SetParityLoc
	in	HMemData	; get the parity byte
	sub	b		; b = calculated parity
	rz			; 0 = no error
;
	lxi	h,BadData	; say there was a data error
	call	PrtError
	call	wait4user	; prompt user
	jmp	HMemTransfer	; retry if user wants
;
;-------
; - transfer data to M-Drive/H
;   data location already set
; entry>	hl = .userDma
;		 b = parity byte
;		 c = data length
; exit>		 Z set always
HMemWrite:
	mov	a,m		; get byte from buf
	out	HMemData	; and send it
	add	b		; add in parity byte
	mov	b,a		; - for next byte
	inx	h
	dcr	c
	jnz	HMemWrite
;
	call	SetParityLoc	; set the parity location
	mov	a,b		; get the parity byte
	out	HMemData	; and write it
	xra	a		; return success
	ret
;
;------
; entry>	none
; exit>		hl = requested track
SetParityLoc:

	lhld	cpmTrk
;------
; entry>	hl = requested track
; exit>		none
; Set the M-Drive/H to the requested track and record
SetDataLoc:
	lda	cpmRec
	dcr	a	; make record relative to zero
sendLocation:		; used in cold boot initialization
	out	HMemAdr
	mov	a,h
	out	HMemAdr
	mov	a,l
	out	HMemAdr
	ret
     endif		; <HMemDrive>

;
;-------
; entry>	none
; exit>		Z set if request is in the buffer
CheckAct:
	lxi	h,ActDisk
	lxi	d,cpmDisk
	ldax	d	; de = adr cpm disk
	cmp	m
	rnz
;
	inx	h	; hl = adr act(x)track
	inx	d	; de = adr cpm trk(low)
	ldax	d
	cmp	m
	rnz
;
	inx	h	; hl = adr act(x)sec
	inx	d	; skip cpm track(high)
	inx	d	; de = adr cpm sec
;
	lda	CurDens
	ora	a
	ldax	d	; a = cpmSec
	jz	ckSec
;
	dcr	a	; force to 8 rec boundary
	ani	0f8h
	inr	a
;
ckSec:
	cmp	m
	ret
;
;-------
; entry>	none
; exit>		flushes all dirty bufs
FlushAct:
	lxi	h,ActDirty
	mov	a,m		; ActDirty says
	ora	a		; 'do nothing till
	rz			;  you hear from me'
;
	mvi	m,0		; clear dirty flag and
	jmp	WriteFlop	; write buffer to disk
;
;-------
; entry>	none
; exit>		Z set if read is not req'd
qReadReq:
	lda	RDorWR		; reads all require 
	ora	a		; filling the buffer
	rnz
;
	lda	curDens		; don't pre-read on
	ora	a		; single density writes
	rz
;
	lda	WrType		; don't pre-read on
	cpi	WrUnAlloc	; writes to previously
	ret			; unallocated blocks
;
;-------
; entry>	none
; exit>		none
SetAct:
	lhld	cpmDisk
	shld	ActDisk	; set disk and track
	lxi	h,cpmRec
	lda	CurDens
	ora	a	; check current density
	mov	a,m	; a = cpmRec
	jz	SetSec	; if single density
;
	dcr	a	; if double density then
	ani	0f8h	; set to 8 rec boundary
	inr	a
;
SetSec:
	sta	ActRec	; set sec
	ret
;
;------
; entry>	none
; exit>		bc = byte offset into disk buffer
GetBufDisp:
	lxi	h,ActRec
	lda	cpmRec
	sub	m
	rar
	mov	b,a
	mvi	c,0
	rnc
;
	mvi	c,80h
	ret
page
*************************************************
*	CompuPro Disk1 specific routines	*
*	      - flopdrvr.asm -			*
*************************************************

;-------
; entry>	none
; exit>		retry upon error
ReadFlop:
	call	StartFlop	; setup and seek
	mvi	c,flop$RdData
	call	ExecFlop
	rz			; function successful
;
	call	FindErr		; parse error
	jmp	ReadFlop
;
;-------
; entry>	none
; exit>		retry upon error
WriteFlop:
	call	StartFlop	; setup and seek
	mvi	c,flop$WrData
	call	ExecFlop
	rz			; function successful
;
	call	FindErr		; parse error
	jmp	WriteFlop
;
;-------
; entry>	c = read/write opcode
;		used by warm,Read,Write
; exit>		Z set if transfer good
ExecFlop:
	lda	curDens
	ora	a
	mov	a,c
	jz	setOpCode
;
	ori	flop$MFM
setOpCode:
	lxi	h,flopCmd
	mov	m,a
	mvi	b,len$Max
	call	command	; send flop disk command
	call	wait4int; wait for status available
	call	results	; and get the results
;
	lda	flopStat
	ani	0f8h
	cpi	40h	; return if not possibly
	rnz		; an End of Track error 
;
	lda	flopStat+1
	cpi	80h	; xfer was good if err
	ret		; is end of track error
;
;-------
; entry>	none
; exit>		none
StartFlop:
	call	adr2fdc	; set Disk1 dma address
	call	SetParams
;			; fall through to seek
;-------
; entry>	none
; exit>		none
Seek:
	lxi	h,flopCmd
	mvi	m,flop$Seek
	mvi	b,len$Seek
	call	command
	call	sense	; rets a = status code
	ani	0f8h	; mask head and unit
	cpi	20h	; seek done bit
	rz		; seek successful
;
	call	ReHome	; recalibrate
	jmp	Seek	; re-seek
;
;-------
; entry>	none
; exit>		none
adr2fdc:
	lda	dmaBANK
	out	flpDmaAdr	; msb first
	lhld	AdrCurBuf
	mov	a,h		; middle next
	out	flpDmaAdr
	mov	a,l		; lsb last
	out	flpDmaAdr
	ret
;
;-------
; entry>	none
; exit>		none
SetParams:
	lxi	h,ActTrk	; hl = .ActTrk
	lda	cur2side	; 1 or 2 sided flag
	ora	a
	mov	a,m		; a = ActTrk
	mvi	b,0		; default to side 0
	jz	SetTrack	; not double-sided
;
	rar			; flopTrk = cpmTRK/2
	jnc	SetTrack	; side = 0 if track even
;
	inr	b		; side = 1 if track odd
SetTrack:
	sta	flopTrk		; set track
	mov	a,b
	sta	flopSide	; set side
;
	rlc
	rlc			; head bit of drive byte
	dcx	h		; hl = .ActDsk
	ora	m		;  a = Side <OR> ActDsk
	sta	flopDsk		; flopDsk = ActDsk <OR> Side
	inx	h
;
	inx	h		; hl = .ActRec
	lda	curDens		; get current density (0-3)
	sta	flopDen		; set flop cmd(density)
	ora	a
	mov	a,m
	jz	SetFlopSec
;
	dcr	a		; 8 cp/m records
	rar			; per physical sector
	rar			; so divide cpmRec by 8
	rar			; - cpmRec starts w/1
	inr	a		; due to SecTrans
;
SetFlopSec:
	ani	1fh
	sta	flopSec
	sta	flopMax
;				; fall thru to GPL,DTL
;-------
; entry>	none
;		- used by warm boot code
; exit>		none
set$GPL$DTL:
	lxi	h,Gap$Data$Len
	lda	curDens
	mov	e,a
	mvi	d,0
	dad	d
	dad	d
	lxi	d,flopGap
	mov	a,m
	stax	d	; GPL
	inx	d
	inx	h
	mov	a,m
	stax	d	; DTL
	ret
;
;-------
;	recalibrate selected drive
; entry>	none
; exit>		retry upon error
ReHome:
	lxi	h,flopDsk
	lda	cpmDisk
	mov	m,a
	dcx	h		; hl = .flopCmd
	mvi	m,flop$ReCal
	mvi	b,len$ReCal
	call	command		; send calibrate cmd
	call	sense		; rets a = status code
	ani	0f8h		; mask head and unit
	cpi	20h		; seek done bit
	rz			; reCalibrate was good
;
	lxi	h,RetryCnt
	dcr	m		; try again for 
	jnz	ReHome		; MaxRetry times
;
	lxi	h,badHOME	; admit failure
	lda	flopDsk
	adi	'A'		; make drive# printable
	sta	HomeDsk
	call	DoPrompt
	jmp	ReHome
;
;-------
; entry>	none
; exit>		a = flop status
Sense:
	call	wait4int	; no status till int
	lxi	h,flopCmd
	mvi	m,flop$SIS
	mvi	b,len$SIS
	call	command		; send sense int cmd
	call	results		; get the results
;
	lda	flopStat
	mov	b,a		; save st0 (status byte)
	lxi	h,flopDsk	; ensure status is
	xra	m		; for current disk
	ani	3		; only check unit#
	jnz	sense		; retry if not current
;
	mov	a,b		; restore st0
	ani	0d8h		; check for sense error
	mov	a,b		; restore st0 for caller
	rz
;
	ani	10h		; ret err if drive or
	rnz			; recalibrate failure
;
	mov	a,b
	ani	08		; check for drive not ready
	jz	NotKnown	; give a generic error message
;				; unless the drive is not ready
;-------
DrvNotReady:
	lda	flopDsk
	adi	'A'
	sta	NRdsk
	lxi	h,NotReady
	jmp	DoPrompt
;
;-------
; entry>	none
; exit>		a = curTYPE
FindType:
	call	SenseDrive	; sets cpm2side byte
	call	ReHome		; recalibrate drive
;
	mvi	a,1
	sta	flopTrk
	call	Seek
	call	ReadId
	lda	flopStat+6	; density, 0-3
	sta	cpmDens
	rlc
	lxi	h,cpm2side
	ora	m		; ret a = dens/side
	ret
;
;-------
; entry>	none
; exit>		sets cpm double-sided flag
SenseDrive:
	call	IsDrvReady	; the disk1/nec 765/qume
	cz	IsDrvReady	; sometimes falsely reports
	jnz	get2sided	; not ready the 1st time
;
	call	DrvNotReady
	jmp	SenseDrive
;
get2sided:
	mov	a,b
	ani	08		; 2 sided bit
	rrc
	rrc
	rrc
	sta	cpm2side
	ret
;
;-------
; entry>	none
; exit>		Z set if drive is not ready
IsDrvReady:
	lxi	h,flopDsk
	lda	cpmDisk
	mov	m,a
	dcx	h
	mvi	m,flop$SDS
	mvi	b,len$SDS
	call	command
	call	results
	lda	flopStat
	mov	b,a
	ani	20h		; drive ready bit
	ret
;
;-------
; entry>	none
; exit>		flopStat buf contains ID info
; Note that for some reason you must try FM mode first
; - else MFM is ok but FM hangs in FDC wait4int
ReadId:
	lxi	h,flopCmd
	mvi	m,flop$RdId	; try single density first
	call	GetId		; read the ID field
	rz			; return if this read is good
;
	lxi	h,flopCmd	; try double density next
	mvi	m,(flop$RdId OR flop$MFM)
	call	GetId		; read the ID field
	rz			; return if this read good
;
	call	FindErr		; tell user about error
	jmp	ReadId		; retry if user requests
;
;-------
; entry>	hl = adr of command string
; exit>		Z set if Id read is good
GetId:
	mvi	b,len$RdId
	call	command
	call	wait4int
	call	results
	lda	flopStat+1
	ani	05	; no (data/ID adr mark) mask
	ret
;
;-------
; entry>	hl = adr of command string
;		 b = len of command string
; exit>		sends cmd to flop disk controller
command:
	mov	d,h
	mov	e,l	; save cmd str adr
	push	b	; save command length
start:
	call	wait2xfer
	jc	CmdErr	; FDC wants to send data
;
	mov	a,m
	out	flpData	; send byte to fdc
	inx	h
	dcr	b
	jnz	start
;
	pop	b	; didn't need B again
	ret
;
CmdErr:
	call	results	; get data from FDC
	xchg		; retrieve cmd string adr
	pop	b	; this is why we saved B
	jmp	command	; restart command
;
;-------
; entry>	none
; exit>		FDC status into flopStat
Results:
	lxi	h,flopStat	; adr of result buf
	push	h
	mvi	b,len$Status	; len of buf
ResFill:
	mvi	m,0ffh		; set buf to all 0ffh
	inx	h
	dcr	b
	jnz	ResFill
;
	pop	h		; adr of buf again
more:
	call	wait2xfer	; wait till FDC ready
	rnc			; no more status bytes
;
	in	flpData		; get a status byte
	mov	m,a
	inx	h
	jmp	more
;
;-------
; entry>	none
; exit>		c set if FDC wants to send data
;		reset if FDC ready to accept data
wait2xfer:
	push	h	; min 12 uSec delay from fdc
	dad	h	; data i/o till valid status
	pop	h
;
	in	flpStatus
	rlc		; bit 7 set if ok to transfer
	jnc	wait2xfer
;
	rlc		; bit 6 determines direction
	ret
;
;-------
; entry>	none
; exit>		none
wait4int:
	in	flpIntPort
	rlc		; wait for interrupt
	jnc	wait4int; status bit to be set
	ret
;
;-------
; entry>	none
; exit>		to DoPrompt
FindErr:
	lxi	h,RetryCnt
	lda	flopStat+2
;
	rrc
	jc	NoDataMark ; no data adr mark (st2,b0)
;
	rrc
	jc	BadTrk	; bad track (st2,b1)
;
	rrc
	rrc
	rrc
	jc	BadTrk	; wrong track (st2,b4)
;
	rrc
	jc	dataCRC	; bad data crc (st2,b5)
;
	lda	flopStat+1
;
	rrc
	jc	NoIdMark; no id adr mark (st1,b0)
;
	rrc
	jc	WrProt	; cant write (st1,b1)
;
	rrc
	jc	NoSec	; bad sector (st1,b2)
;
	rrc
	rrc
	jc	OvRun	; over run (st1,b4)
;
	rrc
	jc	idCRC	; bad id crc (st1,b5)
;
NotKnown:
	lxi	h,UnKnown	; should get here only
	jmp	DoPrompt	; from Sense routine
;
;-------
; entry>	hl = .flop retry counter
; exit>		hl = .no data mark string
NoDataMark:
	dcr	m
	rnz
;
	lxi	h,DataMark
	jmp	DoPrompt

;-------
; entry>	hl = .flop retry counter
; exit>		hl = .BadTrack string
BadTrk:
	push	h
	call	ReHome	; recalibrate drive
	pop	h
;
	dcr	m
	rnz
;
BadCyl:
	lxi	h,BadTrack
	jmp	DoPrompt

;-------
; entry>	hl = .flop retry counter
; exit>		hl = .BadData string
dataCRC:
	dcr	m
	rnz
;
	lxi	h,BadData
	jmp	DoPrompt

;-------
; entry>	none used
; exit>		hl = .wrong density string
NoIdMark:
	lda	flopStat+3
	cpi	77		; last legal track +1
	jnc	BadCyl		; illegal track request
;
	lxi	h,IdMark
	jmp	DoPrompt
;
;-------
; entry>	none used
; exit>		hl = .Protected string
WrProt:
		; no point in retrying
	lda	flopDsk
	adi	'A'
	sta	ProtDSK
	lxi	h,Protected
	jmp	DoPrompt
;
;-------
; entry>	none used
; exit>		hl = .BadSec string
NoSec:
	dcr	m
	rnz
;
	lxi	h,BadSec
	jmp	DoPrompt

;-------
; --	this is a fatal error	--
; entry>	none used
; exit>		no exit
OvRun:
	lxi	h,overrun
	call	PrtError
	call	PrtStat
	jmp	$	; spin till cold boot

;-------
; entry>	hl = .flop retry counter
; exit>		hl = .badID string
idCRC:
	dcr	m
	rnz
;
	lxi	h,badID
;			; fall thru to DoPrompt
;-------
; entry>	hl = adr of error str
; exit>		query user
DoPrompt:
	call	PrtError; print error string
	lda	SysMode	; check for debug print flag
	rrc		; bit0 is flag
	cc	PrtStat	; print flop statistics
	jmp	wait4user
;
;-------
; entry>	hl = .error string
; exit>
PrtError: 
	push	h	; save error str adr
	lxi	h,ErrHdr
	call	PrtMsg	; print error header
	pop	h	; restore str adr
	jmp	PrtMsg	; print error string
;
;-------
; entry>	none
; exit>		none
PrtStat:
	lxi	h,FlopMsg
	call	PrtMsg	; print 'Flop> ' header
	lxi	h,flopCmd
	mvi	b,len$Max+len$Status
	jmp	PrtBuf	; print flop cmd/status bufs
page
*************************************************
*		M E S S A G E S			*
*		- messages.dat -		*
*************************************************

FlopMsg:
	db	cr,lf,'Flop> '
	db	'Cmd Dsk Trk Hd  Sec Den Max Gap '
	db	'Len |-------  Status  -------|'
	db	cr,lf,'    ',0

RtryMsg:
	db	cr,lf,'** A = abort, '
	db	'I = ignore, <CR> = retry ',0

ErrHdr:
	db	cr,lf,'** ',0

IdMark:
	db	'wrong Density',0

DataMark:
	db	'no Data Adr Mark',0

BadTrack:
	db	'Track not found',0

BadSec:
	db	'Sector not found',0

BadData:
	db	'bad Data crc',0

badID:
	db	'bad Id crc',0

Protected:
	db	'disk '
ProtDsk:db	0ffh,' is write Protected',0

OverRun:
	db	'DMA error '
	db	'- Cold Boot system',0

NotReady:
	db	'drive '
NRdsk:	db	0ffh,' not Ready',0

BadHome:
	db	'cannot Home drive '
HomeDsk:db	0ffh,0

UnKnown:
	db	'Unknown error',0
page
*********************************************************
*	D A T A    S T O R A G E    A R E A		*
*		- storage .dat -			*
*********************************************************

;	floppy disk command table

flopCmd:	db	0	; opCODE
flopDsk:	db	0	; disk
flopTrk:	db	0	; track
flopSide:	db	0	; side
flopSec:	db	0	; sec
flopDen:	db	0	; den
flopMax:	db	0	; eot
flopGap:	db	0	; gpl
flopLen:	db	0	; dtl

flopStat:	ds	7	; result buffer

RetryCnt:	db	MaxRetry    ; retry 10 times

AdrUsrDma:	dw	DefaultBuf  ; lsw of cp/mDMA adr
UserBank:	db	0	    ; msb of cp/mDMA adr
AdrCurBuf:	dw	ActBuf	    ; lsw of flopDMA adr
DmaBank:	db	0	    ; msb of flopDMA adr


;	Command buffer disk type dependent values.

Gap$Data$Len:  ; GPL  DTL
	db	007h,080h	; Single density  128 b/sec
	db	00eh,0ffh	; Double density  256 b/sec
	db	01bh,0ffh	; Double density  512 b/sec
	db	035h,0ffh	; Double density 1024 b/sec

VersLoc:	db	version
		db	level
		db	rev
lenVers		equ	$-VersLoc


;   ++	BDOS disk, track, record, type  ++

cpmDisk:	db	0
cpmTrk:		dw	0
cpmRec:		db	0
cpmType:	
cpm2side:	db	0	; SelDsk 2sided value
cpmDens:	db	0	; SelDsk density value

ActDirty:	db	0	; are any bufs dirty
RDorWR:		db	0	; type of transfer
WrType:		db	0	; passed by bdos on writes

curType:
cur2side:	db	0	; Current 2sided value
curDens:	db	0	; Current density value

; ++	actual buffer description	++

ActDisk:	db	0	; disk
ActTrk:		db	0	; track
ActRec:		db	0	; sec

     if		SysConInt OR i4aConInt
ConPtrs:
LastPut:	db	0
LastGot:	db	MaxConPtr
     endif		; <SysConInt> OR <i4aConInt>

zGenByte	equ	$ - 1	; last sysgen image byte

     if		SysConInt OR i4aConInt
ConBufBase:	ds	MaxConPtr+1
     endif		; <SysConInt> OR <i4aConInt>


; ++	Disk transfer buffers	++

CkVec0:		ds	(256/4)		; dir check storage
CkVec1:		ds	(256/4)

AlVec0:		ds	(dsm1024d+7)/8	; alloc vector storage
AlVec1:		ds	(dsm1024d+7)/8

     if		HMemDrive
AlVecHMem:	ds	((dsmHMem*8)+7)/8
     endif		; <HMemDrive>

DirBuf:		ds	128	; Directory buffer

ActBuf:		ds	1024	; flop disk buffer

zMemByte	equ	$ - 1	; last mem loc used

*********************************************************
*							*
*	end of apposite designs bios modules		*
*							*
*********************************************************
