	TITLE	'CP/M BASIC INPUT/OUTPUT ROUTINES'
; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS)
; TARBELL ELECTRONICS
; 2.1 VERSION OF 1-9-80
; CHANGE SIGN-ON DATE FOR CP/M 2.1
;
; UPDATED BY:
; ROBERT M. WHITE
; H & W COMPUTER SYSTEMS, INC.
; 8530 STONEHAVEN
; BOISE, ID  83704
;
;
; THIS MODULE CONTAINS ALL THE INPUT/OUTPUT
; ROUTINES FOR THE CP/M SYSTEM, INCLUDING
; THE DISK ROUTINES.  It has been designed
; to provide a full implementation for a
; Tarbell controller and console configura-
; tion.  The disk drivers fully support
; various sector sizes and densities as
; documented later.  The I/O drivers fully
; support the IOBYTE as defined in the
; CP/M Alteration Manual.  Note this is an
; oversized BIOS and requires that the CCP
; be defined as a file on the default drive.
; Thus only the BDOS and BIOS are on the
; system tracks allowing a BIOS of 2.75k
; when both tracks are single density.
; Currently, a total of 4k is being allocated
; to the BIOS.  Therefore, the max CP/M
; memory size is 61k.  To calculate this,
; do as follows:
;	Max memory size+1 (64k) =10000H
;	10000H-1000H = F000H (Bios Base)
;	F000H-1600H = DA00H (CP/M Base)
;	DA00H-3400H = A600H (CBASE)
;	A600H/400H = 41
;	41+20 = 61 (Max CP/M Size)
;
; To SYSGEN, do the following:
;	1. Assemble this BIOS using MAC and
;	   creating the file, 21BIOS.HEX.
;	2. Assemble the special Boot creating
;	   the file, 21BOOT.HEX.
;	3. Create a CCP file on the
;	   new default disk as follows:
;		DDT CPMxx.COM
;		-M980,1180,100
;		-^C
;		SAVE 8 CCP.COM
;	4. Put BDOS, BOOT & BIOS on the
;	   new default disk as follows:
;		DDT CPMxx.COM
;		-M1180,1F80,980
;		-I21BOOT.HEX
;		-R900
;		-I21BIOS.HEX
;		-H1780,nnnn
;		 xxxx yyyy       (Returned by DDT)
;		-Ryyyy
;		-^C
;		SYSGEN
;			(Answer as normal).
;
; Note -- A handy way of debugging this BIOS is
; to have an operating CP/M which is 16k larger.
; Boot this bios.  To capture data areas, simply
; re-boot the other and look at them with DDT.
; This technique was used extensively to get this
; BIOS operational.
;
;
; THIS SECTION DEFINES THE I/O PORTS AND
; STATUS BITS.  BY SETTING THE PROPER VALUES
; FOR THE EQU STATEMENTS, THE I/O MAY BE
; AUTOMATICALLY RECONFIGURED TO FIT MOST
; SITUATIONS.  THE TRUE AND FALSE ONES
; CONTROL CONDITIONAL ASSEMBLIES OF DIFFERENT
; SECTIONS OF I/O ROUTINES TO FIT DIFFERENT
; INTERFACE REQUIREMENTS.

	MACLIB	SPCLMAC

TRUE	EQU  0FFFFH	;DEFINE VALUE OF TRUE.
FALSE	EQU  NOT TRUE	;DEFINE VALUE OF FALSE.
BC	EQU  B		;DOUBLE REGISTER EQUATES
DE	EQU  D
HL	EQU  H

;***************************************************
;*** THIS BEGINS THE AREA WHICH REQUIRES CHANGES ***
;***      FOR DIFFERENT CONSOLE I/O SYSTEMS      ***
;***************************************************

MSIZE	EQU  61		;MEMORY SIZE IN KBYTES.
INTRP	EQU  FALSE	;TRUE IF INTERRUPTS ALLOWED.
DFTDSK	EQU  0		;DEFAULT DISK ON BOOT (0-3)
DFTUSR	EQU  0		;DEFAULT USER ON BOOT (0-15)

OLDTARB EQU  FALSE	;TRUE IF OLD TARBELL CONTROLLER (1771)
TARBELL EQU  TRUE	;TRUE IF NEW TARBELL CONTROLLER (1791)

DEBUG	EQU  FALSE	;TRUE FOR SPECIAL DEBUGGING MESSAGES
SPOOL	EQU  FALSE	;TRUE IF USING KLH SPOOLER.
NDISK	EQU  3		;DEFINES THE NUMBER DRIVES IN SYSTEM.

CSTAT	EQU  16		;CONSOLE STATUS PORT.
CCOM	EQU  16		;CONSOLE COMMAND PORT.
CDATA	EQU  17		;CONSOLE DATA PORT.
CONUL	EQU  FALSE	;CONSOLE NULLS?
CNULL	EQU  0		;CONSOLE NULL COUNT.

LSTAT	EQU  18		;LIST STATUS PORT.
LCOM	EQU  18		;LIST COMMAND PORT.
LDATA	EQU  19		;LIST DATA PORT.
LSTNUL	EQU  FALSE	;LIST DEVICE NULLS?
LNULL	EQU  0		;LIST NULL COUNT.
LSTPAG	EQU  FALSE	;LIST DEVICE PAGING?
LINCNT	EQU  66		;LINES PER PAGE.

STPRAT	EQU  2		;RATE 1=6MS, 2=10MS, 3=20MS.
DUAL	EQU  FALSE	;TRUE IF DUAL DRIVE.
PERSCI	EQU  FALSE	;TRUE IF FAST SEEK (PERSCI).

;*******************************************************
;*** THIS IS THE END OF THE AREA WHICH NORMALLY NEED ***
;***     BE CHANGED FOR MOST CONSOLE I/O SYSTEMS     ***
;*******************************************************

DISK	EQU  0F8H	;DISK BASE ADDRESS.
DCOM	EQU  DISK	;DISK COMMAND PORT.
DSTAT	EQU  DISK	;DISK STATUS PORT.
TRACK	EQU  DISK+1	;DISK TRACK PORT.
SECTP	EQU  DISK+2	;DISK SECTOR PORT.
DDATA	EQU  DISK+3	;DISK DATA PORT.
WAIT	EQU  DISK+4	;DISK WAIT PORT.
DCONT	EQU  DISK+4	;DISK CONTROL PORT.
	IF	TARBELL
DMACHK	EQU  DISK+5	;DMA CHECK PORT.
	ENDIF

RTCNT	EQU  10		;RETRY COUNT.

CKBR	EQU  00000001B	;KEYBOARD READY BIT.
CPTR	EQU  00000010B	;PRINT READY BIT.
LKBR	EQU  00000001B	;LISTER READY BIT.
LPTR	EQU  00000010B	;LISTER READY BIT.


IOBYTE	EQU  3			;ADDRESS OF I/O BYTE.
CBASE	EQU  (MSIZE-20)*1024  	;BIAS FOR LARGER THAN 20K.
CPMB	EQU  CBASE+3400H	;START OF CPM 2.0
BDOS	EQU  CPMB+806H		;START OF BDOS 2.0.
BIOS	EQU  CPMB+1600H		;START OF CBIOS IO.
CDISK	EQU  4			;LOCATION 4 IS CURRENT DISK.
NSECTS	EQU  44			;NUMBER OF SECTORS IN IT.

	ORG  BIOS		;START OF CBIOS STRUCTURE.
;
; I/O JUMP VECTOR
; THIS IS WHERE CPM CALLS WHENEVER IT NEEDS
; TO DO ANY INPUT/OUTPUT OPERATION.
; USER PROGRAMS MAY USE THESE ENTRY POINTS
; ALSO, BUT NOTE THAT THE LOCATION OF THIS
; VECTOR CHANGES WITH THE MEMORY SIZE.
;
	JMP  BOOT		;FROM COLD START LOADER.
WBOOTE:	JMP  WBOOT		;FROM WARM BOOT.
	JMP  CONST		;CHECK CONSOLE KB STATUS.
	JMP  CONIN		;READ CONSOLE CHARACTER.
	JMP  CONOUT		;WRITE CONSOLE CHARACTER.
	JMP  LSTOUT		;WRITE LISTING CHAR.
	JMP  PUNOUT		;WRITE PUNCH CHAR.
	JMP  RDRIN		;READ READER CHAR.
	JMP  HOME		;MOVE DISK TO TRACK ZERO.
	JMP  SELDSK		;SELECT DISK DRIVE.
	JMP  SETTRK		;SEEK TO TRACK IN REG A.
	JMP  SETSEC		;SET SECTOR NUMBER.
	JMP  SETDMA		;SET DISK STARTING ADR.
	JMP  READ		;READ SELECTED SECTOR.
	JMP  WRITE		;WRITE SELECTED SECTOR.
	JMP  LSTST		;LIST STATUS CHECK.
	JMP  SECTRAN		;SECTOR TRANSLATE ROUTINE.

	; THESE ENTRY POINTS ADDED BY TARBELL ELECTRONICS.

	IF  SPOOL		;IF USING KLH SPOOLER.
	DB   0FFH		;FLAG FOR SPOOLER.
	DW   LTBSY		;LISTER STATUS LOCATION
	DW   LTBSY		;FOR SPOOLER - -
	DW   LTBSY		;I DON'T KNOW WHY IT'S
	DW   LTBSY		;HERE 4 TIMES EITHER.
	ENDIF

;		SPECIAL ENTRY POINTS FOR DIRECT DISK I/O
	JMP  Pselct		;Select the drive in (C).
	JMP  Phome		;Home current drive.
	JMP  Pseek		;Seek track in (C).
	JMP  Pread		;Read a sector in (C), dma in (HL)
	JMP  Pwrite		;Write a sector in (C), dma in (HL)



;*****************************************************
;*                                                   *
;*      Sector Deblocking Algorithms for CP/M 2.0    *
;*                                                   *
;*****************************************************
;
;	This section of programming provides the disk
;drivers for the BIOS.  They are designed to handle
;many combinations of disk formats and sector sizes.
;Currently, the definitions of these combinations are
;defined as in DFOCO distributed by S. J. Singer via
;the CP/M User's Group.  For further information, see
;the documentation for that fine program.
;	The actual routines are divided into two sec-
;tions.  The first performs logical blocking and de-
;blocking of the host physical sectors.  The second
;section contains the actual physical routines to per-
;form the I/O functions necessary for proper operation
;of the disk drives.  Currently, these routines assume
;a Tarbell Single or Double Density Controller.  The
;original logical routines were supplied by Digital
;Research as a part of CP/M 2.0.
;
;*****************************************************
;*                                                   *
;*         CP/M to host disk constants               *
;*                                                   *
;*****************************************************
hstmax	equ	1024		;host max disk sector size
;
;*****************************************************
;*                                                   *
;*        BDOS constants on entry to write           *
;*                                                   *
;*****************************************************
wrall	equ	0		;write to allocated
wrdir	equ	1		;write to directory
wrual	equ	2		;write to unallocated



;/////////////////////////////////////////////////////
;/	   Logical Disk I/O Driver Routines	     /
;/////////////////////////////////////////////////////


;*****************************************************
;*                                                   *
;*	The BDOS entry points given below show the   *
;*      code which is relevant to deblocking only.   *
;*                                                   *
;*****************************************************
;
;	DISKDEF macro, or hand coded tables go here
;
;		* * *  Disk Parameter Header  * * *
;	In general, each disk has an associated Disk
;Parameter Header which both contains information about
;the disk drive and provides a scratchpad area for 
;certain BDOS operations.
;
dphbase	equ	$		;disk param block header
DPE0	equ	$		;** Disk A **
	dw	00000H,00000H	;XLT adr, Scratch
	dw	00000H,00000H	;Scratch, Scratch
	dw	dirbuf,00000H	;Dir Buf adr, DPB adr
	dw	CSV0,ALV0	;CSV adr, ALV adr
DPE1	equ	$		;** Disk B **
	dw	00000H,00000H	;XLT adr, Scratch
	dw	00000H,00000H	;Scratch, Scratch
	dw	dirbuf,00000H	;Dir Buf adr, DPB adr
	dw	CSV1,ALV1	;CSV adr, ALV adr
DPE2	equ	$		;** Disk C **
	dw	00000H,00000H	;XLT adr, Scratch
	dw	00000H,00000H	;Scratch, Scratch
	dw	dirbuf,00000H	;Dir Buf adr, DPB adr
	dw	CSV2,ALV2	;CSV adr, ALV adr
DPE3	equ	$		;** Disk D **
	dw	00000H,00000H	;XLT adr, Scratch
	dw	00000H,00000H	;Scratch, Scratch
	dw	dirbuf,00000H	;Dir Buf adr, DPB adr
	dw	CSV3,ALV3	;CSV adr, ALV adr
;
;		* * *  Disk Parameter Block  * * *
;	In general, each DPH, Disk Parameter Header, 
;points to a particular DPB, Disk Parameter Block.
;The DPB contains information directly relating to
;the organition of data on the disk and its hardware
;characteristics.
;
;		* *  DPB Field Displacements  * *
dpbspt	equ	0	;host CP/M sectors/track
dpbbsh	equ	2	;block shift factor
dpbblm	equ	3	;block shift mask
dpbexm	equ	4	;extent mask
dpbdsm	equ	5	;max block rcd #
dpbdrm	equ	7	;max dir mask
dpbal0	equ	9	;dir allocation vector
dpbal1	equ	10	;dir allocation vector
dpbcks	equ	11	;dir check value
dpboff	equ	13	;rsvd tracks offset
dpbdfc	equ	15	;disk format code
dpbssc	equ	16	;host sector size
dpbhbm	equ	18	;host sector mask
dpbhbs	equ	19	;host sector shift value
dpbxlt	equ	20	;xlt ptr
dpblen	equ	22	;DPB length
;
dpbbase	equ	$	;Base of Disk Parameter Blocks
dpbs00	equ	$	;** Single Density, 128 byte Sectors **
	DPB	020H,128,26,1024,243,64,64,2,xlts00
dpbs01	equ	$	;** Single Density, 256 byte Sectors **
	DPB	021H,256,16,2048,150,64,64,2,0
dpbs02	equ	$	;** Single Density, 512 byte Sectors **
	DPB	022H,512,8,2048,150,64,64,2,0
dpbs03	equ	$	;** Single Density, 1024 byte Sectors **
	DPB	023H,1024,4,2048,150,64,64,2,0
dpbd00	equ	$	;** Double Density, 128 byte Sectors **
	DPB	010H,128,51,2048,239,128,128,2,xltd00
dpbd01	equ	$	;** Double Density, 256 byte Sectors **
	DPB	011H,256,26,2048,243,128,128,2,0
dpbd02	equ	$	;** Double Density, 512 byte Sectors **
	DPB	012H,512,16,2048,300,128,128,2,0
dpbd03	equ	$	;** Double Density, 1024 byte Sectors **
	DPB	013H,1024,8,2048,300,128,128,2,0
dpbnum	equ	($-dpbbase)/dpblen	;# of entries in table


;		* * *  Sector Translation Table  * * *
;	This section contains the STT, Sector Translation
;Table, for particular disk configurations (I.E.  DPB's).
;The table gives a logical to physical mapping of sector
;numbers.  Thereby, allowing different mappings for per-
;formance betterment.  Note that all entries must be rela-
;tive to zero, not one.
xltbase	equ	$	;Base of Sector Translation Tables
xlts00	equ	$	;** Single density, 128 byte sectors,
;			;   26 Sec/Trk, Skew 6 **
	db	0,6,12,18,24
	db	4,10,16,22,2
	db	8,14,20,1,7
	db	13,19,25,5,11
	db	17,23,3,9,15,21
xltd00	equ	$	;** Double density, 128 byte sectors,
;			;   51 Sec/Trk, Skew 18 **
	db	0,17,34,9,26,43,1,18,35,10,27
	db	44,2,19,36,11,28,45,3,20,37,12
	db	29,46,4,21,38,13,30,47,5,22,39
	db	14,31,48,6,23,40,15,32,49,7,24
	db	41,16,33,50,8,25,42
;
;
;*****************************************************
;*                                                   *
;*	BOOT - Cold Boot Routine.		     *
;*		Executed whenever Reset is pressed.  *
;*                                                   *
;*****************************************************

BOOT:
;		Do initialization.
	lxi	sp,080H	;set stack ptr.
	if	INTRP
	ei		;enable interrupts.
	endif

;		Initialize Mits 2SIO.
	mvi	a,3	;Issue reset.
	out	ccom
	out	lcom
	mvi	a,015H	;Set up Ports.
	out	ccom
	out	lcom
	in	cdata	;clear console port.

;		Save last six bytes of CCP.
	lxi	h,BDOS-6
	lxi	d,ccpsav
	mvi	c,6
	mov	a,m
	stax	d
	inx	h
	inx	d
	dcr	c
	jnz	$-5

;		Zero defined Scratch area.
	lxi	b,ENDZ-STARTZ
	lxi	h,STARTZ
	mvi	m,0
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	$-6

;		Set Low memory areas excluding jumps.
	mvi	a,095H	;IOBYTE := CON=CRT, LST=LPT, PUN=PTP,
	sta	IOBYTE	;	   RDR=PTR
	mvi	a,0	;Default disk = A:
	sta	CDISK

;		Issue opening message.
	lxi	h,smsg
	call	Pmsg

;		Finish by warm booting.
	jmp	WBOOT


;*****************************************************
;*                                                   *
;*	WBOOT - Warm Boot Routine.		     *
;*                                                   *
;*****************************************************

WBOOT:
;		Do initialization.
	lxi	sp,080H	;set stack ptr.
	if	INTRP
	di		;disable interrupts.
	endif

;		Set up jumps for CP/M in low memory.
	mvi	a,0C3H	;put (JMP) for WBOOT.
	sta	0
	lxi	h,WBOOTE
	shld	1
	sta	5	;put (JMP) for BDOS.
	lxi	h,BDOS
	shld	6

;		Issue loading CCP message.
	if	DEBUG
	lxi	h,lodccp
	call	pmsg
	endif

;		Zero Disk table.
	mvi	c,4*2
	lxi	h,dsktbl
	mvi	m,0
	inx	h
	dcr	c
	jnz	$-4

;		Reset Deblock fields.
	xra	a
	sta	hstact	;host buffer inactive
	sta	unacnt	;clear unalloc count.

;		Select and login default disk drive.
	mvi	c,DFTDSK	;do it.
	call	SELDSK

;		Set default user to zero for now.
	mvi	e,0
	mvi	c,020H
	call	BDOS

;		Reset disk system.
	mvi	c,00DH
	call	BDOS

;		Select default drive.
	mvi	e,DFTDSK
	mvi	c,00EH
	call	BDOS

;		Zero CCP's FCB.
	lxi	h,ccpfcb+12
	mvi	c,21
	mvi	m,0
	inx	h
	dcr	c
	jnz	$-4

;		Open CCP's FCB.
	lxi	d,ccpfcb
	mvi	c,00FH
	call	BDOS
	inr	a		;Error?
	jc	Bterr		;...yes.

;		Read in the CCP.
	mvi	c,16	;set # of sectors in CCP.
	lxi	d,128	;set sector size.
	lxi	h,CPMB	;set CCP base address.

	call	Ccpred	;Read a sector.
	dad	d	;bump ptr.
	dcr	c	;Decr count.
	jnz	$-5	;loop till done.

;		Restore CCP's last six bytes.
	lxi	h,ccpsav
	lxi	d,BDOS-6
	mvi	c,6
	mov	a,m
	stax	d
	inx	h
	inx	d
	dcr	c
	jnz	$-5

;		Issue CCP loaded Message.
	if	DEBUG
	lxi	h,ccplod
	call	Pmsg
	endif

;		Do misc initialization.
	lxi	h,080H	;set default buffer in dma.
	shld	dmaadr
	if	INTRP
	ei		;enable interrupts.
	endif

;		Set default drive and user and go to CCP.
	lda	CDISK	;get default drive.
	mov	c,a	;save it.
	mvi	a,DFTUSR ;get Default user (0-15).
	ral		;Put it in high hex digit.
	ral
	ral
	ral
	add	c	;add in default drive.
	mov	c,a	;set for CCP.
	jmp	CPMB	;go to CCP.


ccpfcb:	db	0,'CCP     ','COM',0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	db	0

;		* *  Read a sector of CCP.  * *
Ccpred:
;		Do initialization.
	push	b	;save regs.
	push	d
	push	h

;		Set DMA for read.
	xchg
	mvi	c,01AH
	call	BDOS

;		Issue the read.
	lxi	d,ccpfcb
	mvi	c,014H
	call	BDOS
	ora	a	;Error?
	jnz	Bterr	;...yes.

;		Return to caller.
	pop	h	;restore regs.
	pop	d
	pop	b
	ret


;		* *  Handle boot errors  * *
Bterr:
;		Tell operator.
	lxi	h,btmsg	;Issue Boot error msg.
	call	Pmsg
	call	CONIN	;Wait.
	jmp	WBOOT	;Try warm boot again.


;*****************************************************
;*                                                   *
;*	SELDSK - Select a Disk.			     *
;*                                                   *
;*****************************************************

SELDSK:
;		Validate the disk #.
	lxi	h,0		;set for error.
	mov	a,c		;get the disk #.
	cpi	NDISK		;Undefined drive?
	rnc			;...yes, return w/error.
	mov	a,c		;selected disk number
	sta	sekdsk		;seek disk number

;		Get ptr to disk's DPH.
	mov	l,a		;disk number to HL
	mvi	h,0
	rept	4		;multiply by 16
	dad	h
	endm
	lxi	d,dphbase	;base of parm block
	dad	d		;hl=.dph(curdsk)
	shld	sekdph		;save it.

;		Log the disk if not already logged.
	mov	a,c		;point to disk table entry.
	call	Dskadr
	mov	a,m		;get flag byte.
	ora	a		;Already logged?
	jm	$+6		;...yes.
	call	Logdsk		;...no, log it in.

;		now get address of dpb.
	lhld	sekdph		;hl = .dph(curdsk)
	lxi	d,10		;hl = .dphdpb(curdsk)
	dad	d
	mov	e,m		;hl = .dpb(curdsk)
	inx	h
	mov	d,m
	xchg
	shld	sekdpb		;save it.

;		return to caller.
	lhld	sekdph
	ret


;*****************************************************
;*                                                   *
;*	Logdsk - Login disk in (C).    		     *
;*                                                   *
;*****************************************************

Logdsk:
;		Get the format code from sector 1 track 0.
	call	Pselct	;Physically select the drive.
	call	Phome	;Home the head.
	mvi	c,0	;Seek track 0 setting density.
	call	Pseek
	mvi	c,1	;Read in first Sector.
	lxi	h,dirbuf
	call	Pread
	lda	dirbuf+07FH ;Get the format code.
	sta	dskdfc	;save it.

;		Log the disk's DPH.
	lhld	sekdph
	call	Logdph

;		Log it into the disk table.
	lda	sekdsk	;point to disk table entry.
	call	Dskadr
	lda	dskdfc	;get the density code.
	ani	010H
	rrc		;put it in low order byte.
	rrc
	rrc
	rrc
	ori	080H	;add in logged bit.
	mov	m,a	;put it in table flag byte.

;		return to caller.
	ret


;*****************************************************
;*                                                   *
;*	Logdph - Login disk parameter header.	     *
;*                                                   *
;*****************************************************

Logdph:
;		Get the right DPB.
	xchg		;de = .dph
	lda	dskdfc	;get format code for search.
	call	Srcdpb	;Search the DPB table.

;		Put the XLT address in the DPH.
	push	h	;save DPB ptr.
	rxdld	b,<>,dpbxlt ;Get XLT address.
	xchg		;put it in DPH.
	mov	m,c
	inx	h
	mov	m,b

;		Put DPB address in DPH.
	lxi	d,9	;point to dphdpb.
	dad	d
	pop	d	;get DPB address.
	mov	m,e	;put it in DPH.
	inx	h
	mov	m,d

;		return to caller.
	ret


;*****************************************************
;*                                                   *
;*	Srcdpb - Search DPB table on format code.    *
;*                                                   *
;*****************************************************

Srcdpb:
;		Do initialization.
	push	b	;save regs.
	push	d
	mov	b,a	;save format code.
	mvi	c,dpbnum ;set for # of entries.
	lxi	h,dpbbase ;point to DPB table.
	lxi	d,dpblen ;get entry length.

;		Scan the table taking default if not
;		found.
Srcdpl:
	push	h	;save current entry ptr.
	rxld	a,<>,dpbdfc ;get entry's format code.
	pop	h
	cmp	b	;Did we find it?
	jz	Srcdpf	;...yes, return w/it.
	dad	d	;bump ptr to next entry.
	dcr	c	;loop till no more entries.
	jnz	Srcdpl
	lxi	h,dpbs00 ;Default to SD/SS 128 byte.

;		Return to caller w/entry ptr.
Srcdpf:
	pop	d	;restore regs.
	pop	b
	ret


;*****************************************************
;*                                                   *
;*	HOME - Home a particular disk.		     *
;*                                                   *
;*****************************************************

HOME:
	;set track given by registers BC
	lxi	h,0
	shld	sektrk		;track to seek
	ret


;*****************************************************
;*                                                   *
;*	SETTRK - Set appropriate track.		     *
;*                                                   *
;*****************************************************

SETTRK:
	;set track given by registers BC
	mov	h,b
	mov	l,c
	shld	sektrk		;track to seek
	ret


;*****************************************************
;*                                                   *
;*	SETSEC - Set appropriate sector.	     *
;*                                                   *
;*****************************************************

SETSEC:
	;set sector given by register c 
	mov	a,c
	sta	seksec		;sector to seek
	ret


;*****************************************************
;*                                                   *
;*	SETDMA - Set Data Memory Address.	     *
;*                                                   *
;*****************************************************

SETDMA:
	;set dma address given by BC
	mov	h,b
	mov	l,c
	shld	dmaadr
	ret


;*****************************************************
;*                                                   *
;*	SECTRAN - Do Sector Translation.	     *
;*                                                   *
;*****************************************************

SECTRAN:
	;translate sector number BC
	mvi	h,0	;hl = physical sector
	mov	l,c
	mov	a,d	;if not using xlat tbl then
	ora	e	;   return.
	rz
	xchg		;hl = .xlt
	dad	b	;hl = .xlt(sector)
	mov	l,m	;hl = xlt'd sector
	mvi	h,0
	ret


;*****************************************************
;*                                                   *
;*	The READ entry point takes the place of      *
;*	the previous BIOS defintion for READ.        *
;*                                                   *
;*****************************************************
READ:
	;read the selected CP/M sector
	xra	a		;accum = 00
	sta	unacnt		;unacnt= 00
	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


;*****************************************************
;*                                                   *
;*	The WRITE entry point takes the place of     *
;*	the previous BIOS defintion for WRITE.       *
;*                                                   *
;*****************************************************
WRITE:
	;write the selected CP/M sector
	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
	rxld	a,sekdpb,dpbblm	;a = dpbblm(sekdsk) + 1
	inr	a
	sta	unacnt
	lda	sekdsk		;disk to seek
	sta	unadsk		;unadsk = sekdsk
	lhld	sektrk
	shld	unatrk		;unatrk = sectrk
	lda	seksec
	sta	unasec		;unasec = seksec
;
chkuna:
	;check for write to unallocated sector
	lda	unacnt		;any unalloc remain?
	ora	a
	jz	alloc		;skip if not
;
;	more unallocated records remain
	dcr	a		;unacnt = unacnt-1
	sta	unacnt
	lda	sekdsk		;same disk?
	lxi	h,unadsk
	cmp	m		;sekdsk = unadsk?
	jnz	alloc		;skip if not
;
;	disks are the same
	lxi	h,unatrk
	call	sektrkcmp	;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?
	push	h
	rxad	sekdpb,dpbspt
	cmp	m
	pop	h
	jc	noovf		;skip if no overflow
;
;	overflow to next track
	mvi	m,0		;unasec = 0
	lhld	unatrk
	inx	h
	shld	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


;*****************************************************
;*                                                   *
;*	Common code for READ and WRITE follows       *
;*                                                   *
;*****************************************************
rwoper:
;		convert sector to host sector # by
;		dividing it by the number of sectors
;		per host sector.
	xra	a		;zero to accum
	sta	erflag		;no errors (yet)
	lda	seksec		;compute host sector
	rxld	b,sekdpb,dpbhbs	;b = dpbhbs(sekdpb)
	dcr	b		;b = 0?
	inr	b
	jz	rwope1		;...yes, use as is.
	ora	a		;carry = 0
	rar			;shift right
	dcr	b		;decr shift count.
	jnz	$-3		;go shift it again.
rwope1:
	sta	sekhst		;host sector to seek
;
;	active host sector?
	lxi	h,hstact	;host active flag
	mov	a,m
	mvi	m,1		;always becomes 1
	ora	a		;was it already?
	jz	filhst		;fill host if not
;
;	host buffer active, same as seek buffer?
	lda	sekdsk
	lxi	h,hstdsk	;same disk?
	cmp	m		;sekdsk = hstdsk?
	jnz	nomatch
;
;	same disk, same track?
	lxi	h,hsttrk
	call	sektrkcmp	;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
	cnz	writehst	;clear host buff
;
filhst:
	;may have to fill the host buffer
	lda	sekdsk
	sta	hstdsk
	lhld	sektrk
	shld	hsttrk
	lda	sekhst
	sta	hstsec
	lhld	sekdph
	shld	hstdph
	lhld	sekdpb
	shld	hstdpb
	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
	lda	seksec		;mask buffer number
	rxad	sekdpb,dpbhbm	;hl = .dpbhbm(sekdpb)
	ana	m		;least signif bits
	mov	l,a		;ready to shift
	mvi	h,0		;double count
	rept	7		;shift left 7
	dad	h
	endm
;	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
	call	writehst
	lda	erflag
	ret


;*****************************************************
;*                                                   *
;*	Utility subroutine for 16-bit compare        *
;*                                                   *
;*****************************************************
sektrkcmp:
	;HL = .unatrk or .hsttrk, compare with sektrk
	xchg
	lxi	h,sektrk
	ldax	d		;low byte compare
	cmp	m		;same?
	rnz			;return if not
;	low bytes equal, test high 1s
	inx	d
	inx	h
	ldax	d
	cmp	m	;sets flags
	ret
;
;*****************************************************
;*                                                   *
;*	WRITEHST performs the physical write to      *
;*	the host disk, READHST reads the physical    *
;*	disk.					     *
;*                                                   *
;*****************************************************
writehst:
	;hstdsk = host disk #, hsttrk = host track #,
	;hstsec = host sect #. write "hstsiz" bytes
	;from hstbuf and return error flag in erflag.
	;return erflag non-zero if error

;		Select the drive.
	lxi	h,hstdsk	;c = disk #
	mov	c,m
	call	Pselct		;Select it.

;		Set the head on the proper track.
	lxi	h,hsttrk	;c = track #
	mov	c,m
	call	Pseek		;do it.
	sta	erflag		;save error code.
	rnz			;return if error.

;		Write the sector.
	lxi	h,hstsec	;c = sector #
	mov	c,m
	inr	c		;make it relative to one.
	lxi	h,hstbuf	;hl = .buffer
	call	Pwrite		;do it.
	sta	erflag		;save error code.
	ret


readhst:
	;hstdsk = host disk #, hsttrk = host track #,
	;hstsec = host sect #. read "hstsiz" bytes
	;into hstbuf and return error flag in erflag.

;		Select the drive.
	lda	hstdsk		;c = disk #
	mov	c,a
	call	Pselct		;Select it.

;		Set the head on the proper track.
	lxi	h,hsttrk	;c = track #
	mov	c,m
	call	Pseek		;do it.
	sta	erflag		;save error code.
	rnz			;return if error.

;		Read the sector.
	lxi	h,hstsec	;c = sector #
	mov	c,m
	inr	c		;make it relative to one.
	lxi	h,hstbuf	;hl = .buffer
	call	Pread		;do it.
	sta	erflag		;save error code.
	ret


;/////////////////////////////////////////////////////
;/                                                   /
;/	Physical Disk I/O Driver Routines	     /
;/                                                   /
;/////////////////////////////////////////////////////


;*****************************************************
;*                                                   *
;*	Pselct - Physical Device Select		     *
;*                                                   *
;*****************************************************

Pselct:
;		Trace activity.
	if	DEBUG
	push	h
	lxi	h,trcact
	mvi	m,1
	pop	h
	endif

;	Save current disk's Track #.
	lda	dskcur	;point to Disk table entry.
	call	Dskadr
	inx	h	;point to track save.
	in	TRACK	;get the track #.
	mov	m,a	;save it.

;		Get the new track #.
	mov	a,c	;get new disk #.
	sta	dskcur	;make it the current one.
	call	Dskadr	;point to disk table entry.
	inx	h	;point to track save.
	mov	a,m	;get track #.
	sta	dsktrk	;save it.
	out	TRACK	;put it in controller.

;		Set the new disk's latch.
	mov	a,c	;get the disk #.
	if	OLDTARB
	cma		;invert it.
	add	a	;Put bits 1&2 in 4&5.
	add	a
	add	a
	add	a
	ori	2	;make latch command.
	endif
	if	TARBELL
	add	a	;Put bits 1&2 in 4&5.
	add	a
	add	a
	add	a
	endif
	sta	dsklth	;save new latch.

;		Return to caller.
	ret


;*****************************************************
;*                                                   *
;*	Phome - Physical Device Home		     *
;*                                                   *
;*****************************************************

Phome:
;		Trace activity.
	if	DEBUG
	push	h
	lxi	h,trcact
	mvi	m,2
	pop	h
	endif

;		Select the drive in single density.
	lda	dsklth	;Get current latch.
	out	DCONT	;Issue it to controller.

;		Wait for device not to be busy.
	in	DSTAT	;get disk status.
	rrc		;check busy bit.
	jc	$-3	;...loop till not busy.

;		Issue Restore Command.
	mvi	a,002H	;Restore cmd - no hdld, no verify
	out	DCOM	;Issue it.
	in	WAIT	;Wait for completion.

;		Delay 200 msec.
	mvi	a,200
	call	delay

;		Check the status for track zero.
	in	DSTAT
	ani	4	;Track zero?
	jz	Phome	;...no, try again.

;		Reset track # in table.
	lda	dskcur	;point to table entry.
	call	Dskadr
	inx	h	;point to track #.
	mvi	m,0	;reset it.
	lxi	h,dsktrk ;reset current track #.
	mvi	m,0

;		Return to caller.
	ret


;*****************************************************
;*                                                   *
;*	Pseek - Physical Device Seek		     *
;*                                                   *
;*****************************************************

Pseek:
;		Trace activity.
	if	DEBUG
	push	h
	lxi	h,trcact
	mvi	m,3
	pop	h
	endif

;		Select the drive and density.
	mov	a,c	;get track #.
	sta	dsktrk	;save it.
	sta	dsktrs	;...again.
	ora	a	;Track zero?
	jz	Pseek1	;...yes, force single density.
	lda	dskcur	;point to disk table entry.
	call	Dskadr
	mov	a,m	;get flag byte.
	ani	1	;Double density?
	jz	$+5	;...no.
	mvi	a,8	;...yes, set it.
Pseek1: mov	b,a	;save density.
	lda	dsklth	;get current latch.
	ora	b	;add in density.
	out	DCONT	;put it to controller.

;		If we've changed drives since last seek,
;		force a seek to the same track to unload
;		the head.  Thus insuring that the head
;		load bit is properly set for this drive.
	lhld	dsklth	;get old and new latches.
	mov	a,l	;Are they the same?
	cmp	h
	lda	dsklth	;reset them no matter what.
	sta	dskolt
	jnz	Pseek2-2 ;...no, insure seek to same track.

;		Check to see if current track is desired one.
	in	TRACK	;get current track.
	cmp	c	;Are they the same?
	rz		;...yes, return.

;		Issue the Seek command.
	mvi	b,RTCNT	;set retry count.
Pseek2: lda	dsktrk	;get the desired track #.
	out	DDATA	;tell controller.
	in	DSTAT	;Wait till not busy.
	rrc
	jc	$-3
	mvi	a,014H+STPRAT ;seek cmd w/verify.
	out	DCOM
	in	Wait	;Wait till complete.
	in	DSTAT	;get disk status.
	if	DEBUG
	sta	trcerr
	endif
	ani	091H	;Check for error.
	rz		;...none, return.

;		Handle errors.
	dcr	b	;Decr retry count.
	jz	Pseek3	;...hard error.
	call	Phome	;home the device.
	lda	dsktrs	;restore original trk #.
	sta	dsktrk
	jmp	Pseek2	;Try seeking again.
Pseek3:
	call	Recov	;Ask about recovery.
	lda	dsktrs	;restore original trk #.
	sta	dsktrk
	jmp	Pseek2-2 ;Try it again.


;*****************************************************
;*                                                   *
;*	Pread - Physical Sector read.		     *
;*                                                   *
;*****************************************************

Pread:
;		Trace activity.
	if	DEBUG
	push	h
	lxi	h,trcact
	mvi	m,4
	pop	h
	endif

;		Save input parameters.
	shld	dskdma	;dma address
	lxi	h,dsksec ;sector #
	mov	m,c
	if	INTRP
	di		;disable interrupts.
	endif

;		Set retry count and interrupt controller.
Prdagn:
	mvi	a,RTCNT	;Set retry count.
Prdrty:
	sta	dskerr
	mvi	a,0D0H	;Issue soft interrupt.
	out	DCOM
	xthl		;Do some delay.
	xthl

;		Set the Sector.
	lda	dsksec	;get it.
	out	SECTP

;		Point to buffer.
	lhld	dskdma

;		Issue Read command to controller.
	in	DSTAT	;get head load status.
	ani	020H
	xri	020H	;invert it.
	rrc		;put it in proper bit.
	rrc
	rrc
	if	OLDTARB
	ori	088H	;Read cmd w/o hdld
	endif
	if	TARBELL
	ori	080H	;Read cmd w/o hdld
	endif
	out	DCOM	;Issue it.

;		Read the data.
Prdlop:
	in	WAIT	;Wait for INTRQ.
	ora	a	;Is it there?
	jp	Prddon	;...yes, we're done.
	in	DDATA	;get a char.
	mov	m,a	;put it in buffer.
	inx	h	;bump ptr.
	jmp	Prdlop	;go get another.

;		Analyze read status.
Prddon:
	in	DSTAT	;get it.
	if	DEBUG
	sta	trcerr
	endif
	if	INTRP
	ei		;enable interrupts.
	endif
	ani	09DH	;any errors?
	rz		;...no, return.
	call	Erchk	;...yes, insure proper track.
	lda	dskerr	;get retry count.
	dcr	a	;Decr it.
	jnz	Prdrty	;...Try reading it again.
	call	Recov	;Give possible operater cancel.
	jmp	Prdagn	;Try reading again.


;*****************************************************
;*                                                   *
;*	Pwrite - Physical Sector Write.		     *
;*                                                   *
;*****************************************************

Pwrite:
;		Trace activity.
	if	DEBUG
	push	h
	lxi	h,trcact
	mvi	m,5
	pop	h
	endif

;		Save input parameters.
	shld	dskdma	;dma address
	lxi	h,dsksec ;sector #
	mov	m,c
	if	INTRP
	di		;disable interrupts.
	endif

;		Set retry count and interrupt controller.
Pwragn:
	mvi	a,RTCNT	;Set retry count.
Pwrrty:
	sta	dskerr
	mvi	a,0D0H	;Issue soft interrupt.
	out	DCOM
	xthl		;Do some delay.
	xthl

;		Set the Sector.
	lda	dsksec	;get it.
	out	SECTP

;		Point to buffer.
	lhld	dskdma

;		Issue Write command to controller.
	in	DSTAT	;get head load status.
	ani	020H
	xri	020H	;invert it.
	rrc		;put it in proper bit.
	rrc
	rrc
	if	OLDTARB
	ori	0A8H	;Write cmd w/o hdld
	endif
	if	TARBELL
	ori	0A0H	;Write cmd w/o hdld
	endif
	out	DCOM	;Issue it.

;		Write the data.
Pwrlop:
	in	WAIT	;Wait for INTRQ.
	ora	a	;Is it there?
	jp	Pwrdon	;...yes, we're done.
	mov	a,m	;get a char from the buffer.
	out	DDATA	;put it to disk.
	inx	h	;bump ptr.
	jmp	Pwrlop	;go get another.

;		Analyze read status.
Pwrdon:
	mvi	a,1	;delay some.
	call	Delay
	in	DSTAT	;get it.
	if	DEBUG
	sta	trcerr
	endif
	if	INTRP
	ei		;enable interrupts.
	endif
	ani	0FDH	;any errors?
	rz		;...no, return.
	call	Erchk	;...yes, insure proper track.
	lda	dskerr	;get retry count.
	dcr	a	;Decr it.
	jnz	Pwrrty	;...Try reading it again.
	call	Recov	;Give possible operater cancel.
	jmp	Pwragn	;Try reading again.




;*****************************************************
;*                                                   *
;*	Erchk - Insure proper seek on read/write     *
;*                                                   *
;*****************************************************

Erchk:
;		Return if not Rcd not found error.
	push	psw	;save error flags.
	ani	010H	;Record not found?
	jnz	Erchk1	;...yes, read address and seek.
	pop	psw	;...no, return.
	ora	a
	ret

;		Read the next address.
Erchk1:
	mvi	a,0C4H	;Read address w/15ms delay
	out	DCOM
	in	WAIT	;Wait for DRQ or INTRQ.
	in	DDATA	;Read track address.
	push	psw	;save it.

;		Flush rest of address data.
Erchk2:
	in	Wait	;Wait for INTRQ.
	ora	a	;Read done?
	jp	Erchk3	;...yes.
	in	DDATA	;...no, flush rest.
	jmp	Erchk2

;		Check on Read Status.
Erchk3:
	in	DSTAT	;Get read Status.
	ora	a	;Sucessful?
	jz	Erchk4	;...yes.
	pop	psw	;...no, clean up stack.
	call	Phome	;       home head
	jmp	Erchk1	;	and try again.

;		Put track # read in controller.
Erchk4:
	pop	psw	;restore track address.
	out	TRACK	;put it in controller.

;		Seek to proper track.
	lda	dsktrk	;get the proper track #.
	mov	c,a
	call	Pseek	;do the seek.
	pop	psw	;restore error flag.
	ret



;*****************************************************
;*                                                   *
;*	Dskadr - Get disk table entry ptr.	     *
;*                                                   *
;*****************************************************

Dskadr:
	push	d	;save regs.
	mov	l,a	;hl = disp to entry.
	mvi	h,0
	dad	h
	lxi	d,dsktbl ;get base adr.
	dad	d	;add base to disp.
	pop	d
	ret


;*****************************************************
;*                                                   *
;*	Recov - Recover from read, seek and write    *
;*		errors.				     *
;*                                                   *
;*****************************************************

Recov:
	lxi	h,errmsg	;Issue error message
	call	Pmsg		;      to console.
	call	conin		;get a response from operator.
	cpi	003H		;Control C?
	jz	WBOOT		;...yes, abort.
	ret


;*****************************************************
;*                                                   *
;*	Pmsg - Print message on console.	     *
;*                                                   *
;*****************************************************

Pmsg:
	mov	a,m		;get next char of msg.
	ora	a		;if zero, eom.
	rz
	push	b		;save bc.
	mov	c,a		;print char on console.
	call	CONOUT
	pop	b		;restore bc.
	inx	h		;bump msg ptr.
	jmp	Pmsg		;loop till eom.


;*****************************************************
;*                                                   *
;*	Delay - Delay (A) msec.			     *
;*                                                   *
;*****************************************************

Delay:
	push	d	;save regs.
	push	h
Delay1: lxi	d,1
	lxi	h,65335	;Constant for 1 msec @ 4mhz
	dad	d
	jnc	$-1
	dcr	a	;Decr msec count.
	jnz	Delay1	;...loop till done.
	pop	h	;restore regs.
	pop	d
	ret


;*****************************************************
;*                                                   *
;*		    BDOS Messages		     *
;*                                                   *
;*****************************************************

btmsg:	db	'** Boot Error **',0
errmsg: db	'** BDOS Error **',0
smsg:	db	00DH,00AH,'Tarbell'
	if	OLDTARB
	db	'(1771) '
	endif
	if	TARBELL
	db	'(1791) '
	endif
	db	MSIZE/10+'0',MSIZE mod 10 + '0'
	db	'K CP/M 2.1',0
	if	DEBUG
lodccp:	db	00DH,00AH,'***Loading CCP---',0
ccplod:	db	00DH,00AH,'***CCP is loaded!',0
	endif




;****************************************************************************
;*                L-O-G-I-C-A-L  D-E-V-I-C-E  D-R-I-V-E-R-S                 *
;*	THE FOLLOWING SECTION CONTAINS THE LOGICAL DEVICE DRIVERS FOR       *
;*	EACH LOGICAL DEVICE IN THE SYSTEM.  THESE DEVICES ARE DEFINED       *
;*	BY THE IOBYTE FUNCTION FOUND IN THE CP/M ALTERATION GUIDE.   	    *
;****************************************************************************


;*************************** CON - SYSTEM CONSOLE **************************
CONST:
	LDA 	IOBYTE		;GET IOBYTE.
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYST
	DW	CRTST
	DW	RDRST
	DW	DUMST
CONIN:
	LDA 	IOBYTE		;GET IOBYTE.
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYIN
	DW	CRTIN
	DW	RDRIN
	DW	DUMIN
CONOUT:
	LDA 	IOBYTE		;GET IOBYTE.
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYOUT
	DW	CRTOUT
	DW	PUNOUT
	DW	DUMOUT


;*************************** LST - OUTPUT LIST DEVICE **********************
LSTST:
	LDA 	IOBYTE		;GET IOBYTE.
	RLC			;SHIFT BITS.
	RLC
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYST
	DW	CRTST
	DW	LPTST
	DW	DUMST
LSTOUT:
	LDA 	IOBYTE		;GET IOBYTE.
	RLC			;SHIFT BITS.
	RLC
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYOUT
	DW	CRTOUT
	DW	LPTOUT
	DW	DUMOUT


;*************************** PUN - PAPER TAPE PUNCH ************************
PUNOUT:
	LDA 	IOBYTE		;GET IOBYTE.
	RRC			;SHIFT BITS.
	RRC
	RRC
	RRC
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYOUT
	DW	PTPOUT
	DW	DUMOUT
	DW	DUMOUT


;*************************** RDR - PAPER TAPE READER ***********************
RDRST:
	LDA 	IOBYTE		;GET IOBYTE.
	RRC			;SHIFT BITS.
	RRC
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYST
	DW	PTRST
	DW	DUMST
	DW	DUMST
RDRIN:
	LDA 	IOBYTE		;GET IOBYTE.
	RRC			;SHIFT BITS.
	RRC
	CALL	IOCAL		;EXIT TO SELECTED ROUTINE.
	DW	TTYIN
	DW	PTRIN
	DW	DUMIN
	DW	DUMIN


;*************************** I/O DISPATCHER ********************************
IOCAL:
	RLC			;SHIFT BITS.
	ANI	006H		;LIMIT BITS.
	XTHL			;SAVE HL, GET TABLE ADDRESS.
	PUSH	D		;SAVE DE.
	MOV	E,A		;GET SELECTION VALUE.
	MVI	D,0
	DAD	D		;GET TABLE ENTRY.
	MOV	A,M
	INX	H
	MOV 	H,M
	MOV	L,A
	POP	D		;RESTORE DE.
	XTHL			;RESTORE HL, SAVE RTN ADDRESS.
	RET			;EXIT TO ROUTINE.




;****************************************************************************
;*              P-H-Y-S-I-C-A-L  D-E-V-I-C-E  D-R-I-V-E-R-S                 *
;*	THE FOLLOWING SECTION CONTAINS THE PHYSICAL DEVICE DRIVERS FOR      *
;*	EACH ACTUAL DEVICE IN THE SYSTEM.  FOR EACH DEVICE, THERE MUST      *
;*	EXIST AT LEAST TWO OF THREE ROUTINES, A DEVICE STATUS ROUTINE	    *
;*	AND A DEVICE INPUT OR OUTPUT ROUTINE.  THE LOGICAL PORTION OF	    *
;*	THE BIOS WILL ONLY CALL A STATUS, INPUT OR OUTPUT ROUTINE.	    *
;****************************************************************************


;*************************** DUM - DUMMY DEVICE ****************************
DUMST:
	MVI	A,0FFH		;INDICATE READY.
	RET
DUMIN:
	MVI	A,01AH		;INDICATE <EOF>.
	RET
DUMOUT:
	RET


;*************************** CRT - HIGH SPEED CONSOLE **********************
CRTST:
	IN	CSTAT		;GET CONSOLE STATUS.
	ANI	CKBR		;IF NOT READY,
	MVI	A,0
	RZ			;   RETURN.
	CMA			;RETURN 0FFH.
	RET
CRTIN:
	IN	CSTAT		;GET CONSOLE STATUS.
	ANI	CKBR		;IF NOT READY,
	JZ	CRTIN		;   LOOP.
	IN	CDATA		;READ THE CHAR.
	ANI	07FH		;MAKE MOST SIG. BIT=0.
	RET
CRTOUT:
	IN	CSTAT		;GET CONSOLE STATUS.
	ANI	CPTR		;IF NOT READY,
	JZ	CRTOUT		;   LOOP.
	MOV	A,C		;GET THE CHAR.
	OUT	CDATA		;PRINT IT.
	RET


;*************************** LPT - LINE PRINTER ****************************
;NOTE -- THIS SECTION GIVES AN LA-36 DECWRITER SOME INTELLIGENCE.
LPTST:
	IN	LSTAT		;GET PRINTER STATUS.
	ANI	LPTR		;IF NOT READY,
	MVI	A,0
	RZ			;   RETURN.
	CMA			;RETURN 0FFH.
	RET
LPTIN	EQU	DUMIN
LPTOUT:
	MOV	A,C		;GET CHAR.
	CPI	007H		;<BELL>?
	JZ	LPTOUTC		;...YES.
	CPI	008H		;<BACKSPACE>?
	JZ	LPTBS		;...YES.
	CPI	009H		;<HORIZONTAL TAB>?
	JZ	LPTHT		;...YES.
	CPI	00AH		;<LINE FEED>?
	JZ	LPTLF		;...YES.
	CPI	00CH		;<FORM FEED>?
	JZ	LPTFF		;...YES.
	CPI	00DH		;<CARRIAGE RETURN>?
	JZ	LPTCR		;...YES.

LPTCLI:
	LDA	CLCNT		;INCR COLUMN COUNT.
	INR	A
	STA	CLCNT
	MOV	B,A		;SAVE IT.

LPTOUTC:
	IN	LSTAT		;GET PRINTER STATUS.
	ANI	LPTR		;IF NOT READY,
	JZ	LPTOUTC		;   LOOP.
	MOV	A,C		;GET THE CHAR.
	OUT	LDATA		;PRINT IT.
	RET

LPTBS:
	LDA	CLCNT		;DECR COLUMN COUNT.
	DCR	A
	RZ			;IF <0 THEN RETURN W/O PRINTING.
	STA	CLCNT
	JMP	LPTOUTC

LPTHT:
	MVI	C,' '		;ISSUE <BLANK>.
	CALL	LPTCLI
	MOV	A,B		;GET COLUMN COUNT.
	ANI	8-1		;ROUND TO MOD 8.
	JNZ	LPTHT		;LOOP TO MOD 8.
	RET

LPTLF:
	LDA	LFCNT		;INCR LF COUNT.
	CPI	66		;MAX VALUE?
	JC	$+4		;...NO.
	XRA	A		;...YES, RESET IT.
	INR	A
	STA	LFCNT
	JMP	LPTOUTC

LPTFF:
	LDA	LFCNT		;TOP OF FORM?
	CPI	1
	RZ			;...YES.
	MVI	C,00AH		;ISSUE <LF>.
	CALL	LPTLF
	JMP	LPTFF

LPTCR:
	XRA	A		;ZERO COLUMN COUNT.
	STA	CLCNT
	JMP	LPTOUTC

CLCNT:	DB	0		;COLUMN COUNT
LFCNT:	DB	1		;LINE FEED COUNT


;************************ PTP - PAPER TAPE PUNCH ***************************
PTPST	EQU	DUMST
PTPIN	EQU	DUMIN
PTPOUT	EQU	DUMOUT


;************************ PTR - PAPER TAPE READER **************************
PTRST	EQU	DUMST
PTRIN	EQU	DUMIN
PTROUT	EQU	DUMOUT


;************************ TTY - SLOW SPEED CONSOLE *************************
TTYST:
	IN	LSTAT		;GET TTY STATUS.
	ANI	LKBR		;IF NOT READY,
	MVI	A,0
	RZ			;   RETURN.
	CMA			;RETURN 0FFH.
	RET
TTYIN:
	IN	LSTAT		;GET TTY STATUS.
	ANI	LKBR		;IF NOT READY,
	JZ	TTYIN		;   LOOP.
	IN	LDATA		;READ THE CHAR.
	ANI	07FH		;MAKE MOST SIG. BIT=0.
	RET
TTYOUT	EQU	LPTOUT




;*****************************************************
;*                                                   *
;*	Unitialized RAM data areas		     *
;*                                                   *
;*****************************************************
;
STARTZ:		;** Start of zeroed area **
sekdsk:	ds	1		;seek disk number
sektrk:	ds	2		;seek track number
seksec:	ds	1		;seek sector number
sekdph: ds	2		;seek .dph(sekdsk)
sekdpb: ds	2		;seek .dpb(sekdsk)
;
hstdsk:	ds	1		;host disk number
hsttrk:	ds	2		;host track number
hstsec:	ds	1		;host sector number
hstdph: ds	2		;host .dph(sekdsk)
hstdpb: ds	2		;host .dpb(sekdsk)
;
sekhst:	ds	1		;seek shr secshf
hstact:	ds	1		;host active flag
hstwrt:	ds	1		;host written flag
;
unacnt:	ds	1		;unalloc rec cnt
unadsk:	ds	1		;last unalloc disk
unatrk:	ds	2		;last unalloc track
unasec:	ds	1		;last unalloc sector
;
dskcur:	ds	1		;current disk #
dsktrk:	ds	2		;current track #
dsksec:	ds	1		;current sector #
dsktrs:	ds	1		;current trk # (saved)
dskdfc:	ds	1		;current format code
;		note - the following two latches must be kept
;			contiguous.
dsklth:	ds	1		;current disk latch
dskolt:	ds	1		;old disk latch
dskdma:	ds	2		;current dma address
dskerr:	ds	1		;current disk error count
dsktbl: ds	4*2		;disk parameter table
;				;  one entry for each
;				;  physical drive
;	db	0nnH		;flags
;				 80 - 0=not logged, 1=disk logged
;				 40 - 0=single density, 1=double
;	db	nn		;current track
ENDZ:		;** End of Zeroed Area **


	if	DEBUG
trcact:	ds	1		;last traced activity number
trcerr:	ds	1		;last traced controller error
	endif
erflag:	ds	1		;error reporting
rsflag:	ds	1		;read sector flag
readop:	ds	1		;1 if read operation
wrtype:	ds	1		;write operation type
ccpsav:	ds	6		;CCP non-128-byte boundary data.
dmaadr:	ds	2		;last dma address
ALV0:	ds	48
CSV0:	ds	32
ALV1:	ds	48
CSV1:	ds	32
ALV2:	ds	48
CSV2:	ds	32
ALV3:	ds	48
CSV3:	ds	32
dirbuf:	ds	128		;directory buffer
hstbuf:	ds	hstmax		;host buffer
	end
