;-------------------------------------------------
; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS)
; TARBELL ELECTRONICS
; 2.X VERSION OF 11-4-80
; Copyright (c) 1980 Tarbell Electronics
;--------------------------------------------------
; This bios module is the CPM V2.X Auto Select Bios.
; This bios reads single or double density disk.
; The Double density disk contains 51 sectors/track,
; 77 tracks. Track 0 = single density, Tracks 1 - 76
; are double density at 51 sectors per track.
; Note:  If you leave DMACNTL false, you must have a CPU
; which runs at 4 MHz to run double density.
; This bios now supports double sided single/double density.
; 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.
;
TRUE	EQU  0FFFFH		;DEFINE VALUE OF TRUE.
FALSE	EQU  NOT TRUE		;DEFINE VALUE OF FALSE.
;
;***************************************************
;*** THIS BEGINS THE AREA WHICH REQUIRES CHANGES ***
;***      FOR DIFFERENT CONSOLE I/O SYSTEMS      ***
;***************************************************
;
MSIZE	EQU  63		;MEMORY SIZE IN KBYTES.
INTRP	EQU  FALSE	;TRUE IF INTERRUPTS ALLOWED.
TARBELL	EQU  TRUE	;TRUE IF USING THE TARBELL Z-80 CPU.
IOBASE	EQU  0		;BASE IO ADDR FOR TARBELL CPU (0 or 10 hex).
TIMER	EQU  FALSE	;TRUE IF USING CPU TIMER (Tarbell CPU board).
STD	EQU  FALSE	;TRUE IF STANDARD I/O.
MSIO2	EQU  FALSE	;TRUE IF MITS 2SIO.
VDB8024	EQU  FALSE	;TRUE IF USING VDB-8024 BOARD.
DELTA	EQU  FALSE	;TRUE IF USING DELTA PRODUCTS CPU.
ISIO2	EQU  FALSE	;TRUE IF IMSAI SIO-2.
TUART	EQU  FALSE	;TRUE IF CROMEMCO TUART.
VIDEO	EQU  FALSE	;TRUE IF USING A MEMORY MAPPED VIDEO.
OTHER	EQU  FALSE	;TRUE IF SOMETHING ELSE.
SOLOS	EQU  FALSE	;TRUE IF PROC TECH SOLOS.
DUBSID	EQU  TRUE	;TRUE FOR DOUBLE SIDED DRIVES (1 logical drive).
DMACNTL	EQU  TRUE	;TRUE IF USING DMA CONTROL.
NDISK	EQU  4		;DEFINES THE NUMBER DRIVES IN SYSTEM.
;
	IF  VIDEO	;IF USING A VIDEO BOARD
OUTADDR	EQU  0		;PUT OUTPUT ADDRESS HERE
	ENDIF
;
	IF NOT SOLOS AND NOT TARDEL	;IF NOT PROC TECH SOLOS,
CSTAT	EQU  0		;CONSOLE STATUS PORT.
CCOM	EQU  0		;CONSOLE COMMAND PORT.
CDATA	EQU  1		;CONSOLE DATA PORT.
LSTAT	EQU  2		;LIST STATUS PORT.
LCOM	EQU  2		;LIST COMMAND PORT.
LDATA	EQU  3		;LIST DATA PORT.
	ENDIF
;
CONUL	EQU  FALSE	;CONSOLE NULLS?
CNULL	EQU  16		;CONSOLE NULL COUNT.
LSTNUL	EQU  FALSE	;LIST DEVICE NULLS?
LNULL	EQU  0		;LIST NULL COUNT.
LSTPAG	EQU  FALSE	;LIST DEVICE PAGING?
LINCNT	EQU  66		;LINES PER PAGE.
HLAB	EQU  0		;8 FOR HD LD AT BEG OF SEEK.
STPRAT	EQU  0		;RATE 0=3ms,1=6MS, 2=10MS, 3=20MS.
DUAL	EQU  FALSE	;TRUE IF DUAL HEADED (2 HEADS MOVING TOGETHER).
;
;*******************************************************
;*** THIS IS THE END OF THE AREA WHICH NORMALLY NEED ***
;***     BE CHANGED FOR MOST CONSOLE I/O SYSTEMS     ***
;*******************************************************
;
RDYLO	EQU  STD OR SOLOS OR OTHER  ;STATUS READY WHEN LOW.
RDYHI	EQU  NOT RDYLO
TARDEL	EQU  TARBELL OR DELTA	;IF USING TARBELL OR DELTA CPU.
;
	IF   TARDEL	;IF USING TARBELL OR DELTA CPU
CCOM	EQU  IOBASE+1	;CONSOLE COMMAND PORT
CSTAT	EQU  IOBASE+1	;CONSOLE STATUS PORT ( CHAN A.)
CDATA	EQU  IOBASE+0	;CONSOLE DATA PORT
LCOM	EQU  IOBASE+3	;LIST COMMAND PORT
LSTAT	EQU  IOBASE+3	;LIST STATUS PORT (CHAN B.)
LDATA	EQU  IOBASE+2	;LIST DATA PORT
	ENDIF
;
	IF	TIMER AND TARBELL	;MUST BE USING TARBELL CPU.
;
; TIMER EQUATES
;
TCH0	EQU	IOBASE+4	;TIMER CHAN 0 ADDRESS
TCH1	EQU	IOBASE+5	;TIMER CHAN 1 ADDRESS
TCH2	EQU	IOBASE+6	;TIMER CHAN 2 ADDRESS
TCMND	EQU	IOBASE+7	;TIMER COMMAND PORT
IMASK	EQU	IOBASE+8	;INTERRUPT MASKING PORT
CNTR0	EQU	00000000B	;counter 0
CNTR1	EQU	01000000B	;counter 1
CNTR2	EQU	10000000B	;counter 2
RLWORD	EQU	00110000B	;read/load lsb 1st, msb 2nd.
RLHBYTE	EQU	00100000B	;read/load msb only.
RLLBYTE	EQU	00010000B	;read/load lsb only.
CNTRLT	EQU	00000000B	;counter latching operation.
BINARY	EQU	00000000B	;select binary operation.
BCD	EQU	00000001B	;select BCD operation.
MODE0	EQU	00000000B	;interrupt on terminal count.
MODE1	EQU	00000010B	;programmable One-shot.
MODE2	EQU	00000100B	;rate generator.
MODE3	EQU	00000110B	;square wave rate generator.
MODE4	EQU	00001000B	;software triggered strobe.
MODE5	EQU	00001010B	;hardware triggered strobe.
	ENDIF
;
	IF   SOLOS		;IF PROC TECH SOLOS,
CSTAT	EQU  0FAH		;CONSOLE STATUS PORT.
KBD	EQU  0C02EH		;SOLOS KEYBOARD.
CLRSCR	EQU  0C0D5H		;CLEAR SCREEN.
SCRN	EQU  0C054H		;SOLOS OUTPUT.
	ENDIF
;
	IF NOT SOLOS		;IF NOT PROC TECH SOLOS,
DMAP	EQU  0E0H		;DMA BASE ADDRESS.
DISK	EQU  0F8H		;DISK BASE ADDRESS.
	ENDIF
;
	IF SOLOS		;IF PROC TECH SOLOS,
DMAP	EQU  060H		;DMA BASE ADDRESS.
DISK	EQU  078H		;DIFFERENT DISK PORTS.
	ENDIF
;
ADR0	EQU  DMAP+0		;DMA ADDRESS REG PORT.
WCT0	EQU  DMAP+1		;DMA WORD COUNT REG PORT.
CMND	EQU  DMAP+8		;DMA COMMAND PORT.
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.
DMACHK	EQU  DISK+5		;DMA CHECK PORT.
RTCNT	EQU  10			;RETRY COUNT.
;
	IF   STD		;IF STANDARD I/O,
CKBR	EQU  00000001B		;KEYBOARD READY BIT.
CPTR	EQU  10000000B		;CONS OUTPUT RDY BIT.
	ENDIF
;
	IF   MSIO2		;IF MITS 2SIO,
CKBR	EQU  00000001B		;KEYBOARD READY BIT.
CPTR	EQU  00000010B		;PRINT READY BIT.
	ENDIF
;
	IF   VDB8024		;IF VDB-8024 BOARD.
CKBR	EQU  00000010B		;KEYBOARD READY BIT.
CPTR	EQU  00000100B		;CONS OUTPUT RDY BIT.
	ENDIF
;
	IF   ISIO2
CKBR	EQU  00000010B		;KEYBOARD READY BIT.
CPTR	EQU  00000001B		;PRINT READY BIT.
	ENDIF
;
	IF   TARDEL
CKBR	EQU  00000010B		;KEYBOARD READY BIT.
CPTR	EQU  00000001B		;PRINT READY BIT.
	ENDIF
;
	IF   TUART		;IF CROMEMCO TUART,
CKBR	EQU  01000000B		;KEYBOARD READY BIT.
CPTR	EQU  10000000B		;PRINT READY BIT.
	ENDIF
;
	IF   SOLOS		;IF PROC TECH SOLOS,
CKBR	EQU  00000001B		;KEYBOARD READY BIT.
CPTR	EQU  10000000B		;DUMMY EQU.
	ENDIF
;
	IF   OTHER		;IF SOMETHING ELSE,
CKBR	EQU  00000010B		;KEYBOARD READY BIT.
CPTR	EQU  10000000B		;PRINTER READY BIT.
	ENDIF
;
LRBIT	EQU  CPTR		;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  17			;NUMBER OF SECTORS IN IT.
;
	ORG  CPMB+8
;
SMSG:	DB   0DH,0AH,'Tarbell '
	DB   MSIZE/10+'0',MSIZE MOD 10+'0'
	DB   'K CPM 2.2',0DH,0AH
	DB   'Auto-Select '
	IF   DUBSID
	DB   'Double Sided '
	ENDIF
	DB   'ver of 11-4-80',0
;
; BOOT
; THIS SECTION IS EXECUTED WHENEVER RESET AND RUN
; IS PUSHED, AFTER THE COLDSTART LOADER READS IN
; THE CPM SYSTEM.
;
BOOT:	LXI  SP,80H		;SET STACK POINTER.
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	EI			;ENABLE THEM HERE.
	ENDIF
;
	IF   MSIO2		;IF MITS 2SIO,
	MVI  A,3		;INITIALIZE 2SIO.
	OUT  CCOM
	OUT  LCOM
	MVI  A,11H
	OUT  CCOM
	OUT  LCOM
	ENDIF
;
	IF   TARDEL OR ISIO2
	LXI  H,IOINIT		;point to 8251 init. bytes
	MVI  B,4		;there are 4 of them
INITI0:	MOV  A,M		;get a byte
	OUT  CCOM		;out to command port of console
	OUT  LCOM		;out to command port of lister
	INX  H			;bump pointer
	DCR  B			;decrease count
	JNZ  INITI0		;loop till done.
	ENDIF
;
	IF   TUART		;IF CROMEMCO TUART,
	MVI  A,1		;SET A = 1.
	OUT  54H		;SELECT DEVICE A.
	OUT  52H		;RESET DEVICE B.
	LXI  H,BAUDRS		;GET ADR OF BAUD RATE TABLE.
	MVI  A,11H		;OCTUPLE THE CLOCK.
IT1:	OUT  02H		;& RESET CURRENT DEV.
	MOV  A,M		;GET BAUD RATE FROM TABLE.
	INX  H			;INCREMENT POINTER.
	OUT  0			;SET BAUD RATE.
	CALL CONIN		;READ KEYBOARD.
	CALL CONIN		;READ KEYBOARD AGAIN.
	CPI  0DH		;IF NOT CARRIAGE-RETURN,
	MVI  A,1		;SLOW THE CLOCK.
	JNZ  IT1		;UNTIL A CARRIAGE-RETURN.
	ENDIF
;
	IF   SOLOS		;IF PROC TECH SOLOS,
	CALL CLRSCR		;CLEAR SCREEN.
	ENDIF
;
	IF DMACNTL
	LXI  H,RWDMA		;POINT TO DMA ROUTINE
	SHLD DMAENT+1		;MODIFY BOOT JMP ADDRESS.
	ENDIF
;
	IF   TIMER AND TARBELL	;IF USING TARBELL CPU
	MVI  A,CNTR0+RLWORD+MODE2+BINARY	;INIT 8253
	OUT  TCMND		;SEND IT TO COMMAND PORT
	LXI  B,33333		;TIME CONSTANT FOR 60 HZ
	MOV  A,C
	OUT  TCH0		;LS BYTE OF COUNT
	MOV  A,B
	OUT  TCH0		;MS BYTE OF COUNT
	ENDIF
;
	JMP  BOOTF		;FINISH BOOT
;
	IF   TARDEL OR ISIO2
IOINIT:	DB   0AAH,040H,0CEH,037H
	ENDIF
;
	IF   TUART		;IF CROMEMCO TUART,
BAUDRS:	DB   94H,0CEH,0A2H,92H,88H,84H,82H,1
	ENDIF
;
	ORG  BIOS-64		;HIDE REST OF BOOT HERE.
BOOTF:	XRA  A			;CLEAR SCRATCH AREA.
	STA  IOBYTE		;CLEAR I/O BYTE.
	STA  CDISK		;SELECT DRIVE ZERO
	MVI  B,ENDZ-STARTZ	;GET LENGTH OF ZERO AREA.
	LXI  H,STARTZ		;GET SCRATCH ADDRESS.
BOOTL:	MOV  M,A		;PUT ZERO IN MEMORY.
	INX  H			;INCREMENT POINTER.
	DCR  B			;DECREMENT COUNTER.
	JNZ  BOOTL		;LOOP TILL DONE.
	IN   CDATA		;CLEAR CONSOLE STATUS.
	LXI  H,SMSG		;POINT TO SIGN ON.
PMSG:	MOV  A,M		;GET A BYTE OF THE MESSAGE
	INX  H			;BUMP MEMORY POINTER.
	ORA  A			;IS IT A ZERO?
	JZ   GOCPM		;YES, WE ARE DONE, JMP TO CPM.
	MOV  C,A		;NOPE, PRINT MORE MESSAGE.
	CALL CONOT		;USE THE CONOT ROUTINE.
	JMP  PMSG		;AND LOOP TILL DONE.
;
	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.
;
DMAENT:	JMP  BOOT		;FROM SBOOT LOADER,CHANGED FOR DMA.
WBOOTE:	JMP  WBOOT		;FROM WARM BOOT.
	JMP  CONST		;CHECK CONSOLE KB STATUS.
	JMP  CONIN		;READ CONSOLE CHARACTER.
	JMP  CONOT		;WRITE CONSOLE CHARACTER.
	JMP  LIST		;WRITE LISTING CHAR.
	JMP  PUNCH		;WRITE PUNCH CHAR.
	JMP  READER		;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  PRSTAT		;LIST STATUS CHECK.
	JMP  SECTRAN		;SECTOR TRANSLATE ROUTINE.
;
	ds	256
;
; THIS SECTION DEFINES THE THE DISK PARAMETERS
;
DPBASE	EQU  $			;BASE OF DISK PARAMETER BLOCK
DPE0:	DW   XLT0,0000H		;TRANSLATE TABLE
	DW   0000H,0000H	;SCRATCH AREA
	DW   DIRBUF,SDTAB+3	;DIR BUFF, PARM BLOCK
	DW   CSV0,ALV0		;CHECK, ALLOC VECTORS
;
DPE1:	DW   XLT1,0000H
	DW   0000H,0000H
	DW   DIRBUF,DPB1
	DW   CSV1,ALV1
;
DPE2:	DW   XLT2,0000H
	DW   0000H,0000H
	DW   DIRBUF,DPB2
	DW   CSV2,ALV2
;
DPE3:	DW   XLT3,0000H
	DW   0000H,0000H
	DW   DIRBUF,DPB3
	DW   CSV3,ALV3
;
;THE FOLLOWING DESCRIBES THE DISK PHYSICAL NATURE, SUCH AS
;SECTORS/TRACK,DIRECTORY SIZE, ETC...
;THE FOLLOWING TABLE DEFINES A SINGLE DENSITY DRIVE.
;
SDTAB:	EQU  $			;ONE OF 4 DISK PARM. BLOCKS
	DB   00H		;LOG BYTE SINGLE DENSITY
	DW   XLT0		;USE SINGLE DENSITY TRANSLATE TAB.
	DW   26			;SECTORS/TRACK
	DB   3			;BLOCK SHIFT
	DB   7			;BLOCK MASK
	DB   0			;EXTNT MASK
	DW   242		;DISK SIZE - 1
	DW   63			;DIRECTORY MAX.
	DB   192		;ALLOC0
	DB   0			;ALLOC1
	DW   16			;CHECK SIZE
	DW   2			;NUMBER OF SYSTEM TRACKS
;
	IF   DUBSID		;is using double sided drives.
;
; Defines a Single density/ Double sided disk
;
	DB   02H		;log byte doub sided
	DW   XLT0
	DW   26
	DB   4
	DB   15
	DB   0
	DW   242
	DW   95			;allow 95 entrys for dir.
	DB   192
	DB   0
	DW   24
	DW   2
	ENDIF
;
;THE FOLLOWING TABLE DEFINES A DOUBLE DENSITY DRIVE.
;
DDTAB:	DB   01H		;log byte doub den/sing sided
	DW   0			;NO SECTOR TRANSLATE TABLE.
	DW   51			;51 SECTORS.
	DB   4			;BLOCK SHIFT.
	DB   15			;BLOCK MASK.
	DB   0			;EXTENT MASK.
	DW   237		;DISK SIZE -1
	DW   95			;DIRECTORY MAX.
	DB   192		;ALLOC0
	DB   0			;ALLOC1
	DW   24			;CHECK SIZE
	DW   2			;NUMBER OF SYSTEM TRACKS.
;
	IF   DUBSID		;if using double sided drives.
;
; Defines a Double density/Doub sided drive
;
	DB   03H		;log byte and dub sided
	DW   0
	DW   51
	DB   5
	DB   31
	DB   0
	DW   237
	DW   95
	DB   192
	DB   0
	DW   24
	DW   2
	ENDIF
;
;SECTOR TRANSLATION TABLE
;
XLT0	EQU  $			;START OF TRANS. TABLE
	DB   1,7,13,19,25
	DB   5,11,17,23,3
	DB   9,15,21,2,8
	DB   14,20,26,6,12
	DB   18,24,4,10,16,22
;
DPB1	EQU  SDTAB+3		;EQUIVALENT PARAMETERS
XLT1	EQU  XLT0		;SAME TRANSLATE TABLE
;
DPB2	EQU  SDTAB+3
XLT2	EQU  XLT0
;
DPB3	EQU  SDTAB+3
XLT3	EQU  XLT0
;
;DISK SET UP ROUTINE. THIS ROUTINE IS COMMON TO BOTH THE
;READ AND WRITE ROUTINES FOR DMA OPERATION. THIS ROUTINE
;MAY BE USED STAND ALONE BY PASSING PARAMETERS TO IT AND
;JUMPING TO WBOOT-3 HEX. THIS JUMP VECTOR IS CHANGED WHEN
;CP/M IS BOOTED UP.
;
;ENTERY POINT = RWDMA:
;
;USER MUST SET UP DMAADD FOR MEMORY ADDRESS AND
;USER MUST SET UP DISK SECTOR WITH 'SETSEC' ENTRY
;THE TRACK TO READ OR WRITE MUST BE SET UP USING 'SETTRK'
;BEFORE USING RWDMA ROUTINE EXTERNALLY.
;
;ENTRY PARAMETERS:
;B   = FLOPPY DISK (1793) READ/WRITE COMMAND BYTE
;C   = FLOPPY DISK (1793) FORCE INTERRUPT COMMAND BYTE
;D   = DMA (8257) READ/WRITE COMMAND + HIGH BYTE COUNT
;E   = DMA (8257) LOW BYTE COUNT (80 HEX = 128 BYTES)
;
;EXIT VALUES
;B,C = FLOPPY COMMANDS
;D,E = DMA COMMAND + BYTE COUNT.
;H,L = (H,L + D,E)
;A   = FLOPPY DISK STATUS BYTE
;
;STACK USAGE IS 1 LEVEL DEEP.
;
	IF DMACNTL		;IF USING DMA CONTROL
;
DMARW:	STA  ERCNT		;SAVE ERROR COUNT.
RWDMA:	LDA  SECT		;GET SECTOR TO READ/WRITE
	OUT  SECTP		;AND SEND IT FLOPPY CHIP.
	LHLD DMAADD		;GET CPM DMA ADDRESS.
DMARWE:	XRA  A			;CLEAR ACCUM.
	OUT  CMND		;RESET DMA CHIP.
	MOV  A,C		;FORCE INTERRUPT COMMAND BYTE
	OUT  DCOM		;SEND IT TO CONTROLLER.
	MOV  A,E		;BYTE COUNT TO TRANSFER
	DCR  A			;COUNT = COUNT - 1.
	OUT  WCT0		;SEND IT TO DMA CHIP.
	MOV  A,D		;GET READ/WRITE CODE.
	OUT  WCT0		;AND TELL DMA CHIP WHAT TO DO.
	MOV  A,L		;GET LOW ADDRESS BYTE
	OUT  ADR0		;AND SEND IT TO DMA CHIP.
	MOV  A,H		;GET HIGH ADDRESS BYTE
	OUT  ADR0		;AND SEND IT TO DMA CHIP.
	MVI  A,41H		;SET UP FOR REQUEST CH. 0
	OUT  CMND		;SEND IT TO CONTROLLER.
	CALL HDLD		;CHECK HEAD LOAD BIT.
	ORA  B			;'OR' IN THE READ/WRITE BITS.
	OUT  DCOM		;TELL FLOPPY CHIP WHAT TO DO.
;
;ADJUST H,L FOR 128 BYTE INCREASE.
;
	PUSH D			;SAVE D,E
	MOV  A,D		;GET DMA COMMAND BYTE
	ANI  3FH		;STRIP OFF COMMAND BITS 7 & 6
	MOV  D,A		;NOW SET FOR H,L ADJUST.
	DAD  D			;ADD IT TO H,L.
	POP  D			;RESTORE D,E
;
;GENERAL PURPOSE WAIT ROUTINE.
;
	MVI  A,20H		;COUNT VALUE.
CNTLOOP:DCR  A
	JNZ  CNTLOOP		;LOOP TILL = ZERO.
SLOOP:	IN   DMACHK		;CHECK FOR OPERATION DONE.
	RLC			;BY LOOKING AT BIT 7.
	JC   SLOOP		;LOOP TILL BIT 7 = 0.
	IN   DSTAT		;CHECK AND RETURN DISK STATUS.
	RET			;RETURN TO CALLER.
	ENDIF
;
;Warm-boot - Read the CCP back into memory. BDOS and BIOS
;assumed still in memory. If they are not, a cold start will
;have to be done to bring them back into memory.
;
WBOOT:	LXI  SP,80H		;SET STACK POINTER.
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	EI			;ALLOW THEM HERE.
	ENDIF
;
	IF   LSTPAG		;IF LIST DEVICE PAGING,
	XRA  A			;RESET LINE-FEED COUNT.
	STA  LFCNT
	ENDIF
;
	MVI  C,0		;SELECT DISK 0.
	CALL SELDSK
	CALL HOME		;MOVE TO TRACK ZERO.
	LXI  H,0		;clear h,l
	SHLD DRVFLG		;clear drive flags
	SHLD DRVFLG+2
	MVI  B,NSECTS		;GET # SECTORS FOR CPM READ.
	MVI  C,2		;TRACK (B)=0, SECTOR (C)=2.
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	DI			;DISABLE THEM HERE.
	ENDIF
;
	LXI  H,CPMB		;GET STARTING ADDRESS.
RBLK1:	SHLD DMAADD		;SET STARTING ADDRESS.
	CALL SETSEC		;READ STARTING AT SECTOR IN C.
	PUSH B
	CALL READ		;READ A SECTOR BACK.
	POP  B
	JNZ  RDERR		;IF ERROR, PRINT MESSAGE.
	INR  C			;INCREMENT SECTOR NUMBER.
	DCR  B			;DECREMENT SECTOR COUNT.
	JNZ  RBLK1		;NOT ZERO, KEEP READING
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	EI			;ALLOW THEM AGAIN HERE.
	ENDIF
;
; SET UP JUMPS INTO CP/M IN LOWER MEMORY.
;
GOCPM:	MVI  A,0C3H		;PUT JMP TO WBOOT
	STA  0			;ADR AT ZERO.
	LXI  H,WBOOTE		;WARMBOOT ENTRY POINT
	SHLD 1			;SET IT.
	STA  5			;SET JUMP INSTRUCTION.
	LXI  H,BDOS		;PUT JUMP TO BDOS
	SHLD 6			;AT ADR 5,6,7.
	LXI  H,80H		;SET DEFAULT DMA ADR.
	SHLD DMAADD		;SAVE IT.
	LDA  CDISK		;GET DISK NUMBER TO
	MOV  C,A		;PASS TO CCP IN C.
	JMP  CPMB		;JUMP TO CCP.
;
RDERR:	CALL RECOV		;We have an error in booting.
	JMP  WBOOT		;DO A WARM BOOT.
;
; CHECK CONSOLE INPUT STATUS.
;
CONST:	CALL STATCON		;CHECK CONSOLE STATUS PORT.
CONST1:	MVI  A,0		;SET A=0 FOR RETURN.
;
	IF   RDYLO		;IF STATUS READY LOW,
	RNZ			;NOT READY WHEN NOT 0.
	ENDIF
;
	IF   RDYHI		;IF STATUS READY HIGH,
	RZ			;NOT READY WHEN ZERO.
	ENDIF
;
	CMA			;IF READY A=FF.
	RET			;RETURN FROM CONST.
;
; STATCON - CHECK KEYBOARD STATUS
;
	IF   NOT SOLOS
STATCON:IN   CSTAT		;IN STATUS PORT
	ANI  CKBR		;MASK READY BIT.
	RET
	ENDIF
;
; READ A CHARACTER FROM CONSOLE.
;
CONIN:
	IF   NOT SOLOS		;IF NOT PROC TECH SOLOS,
	CALL STATCON		;READ CONSOLE STATUS.
	ENDIF
;
	IF   SOLOS		;IF PROC TECH SOLOS,
	CALL KBD		;READ SOL KEYBOARD.
	JZ   CONIN		;READY WHEN NOT ZERO.
	ENDIF
;
	IF   RDYLO AND NOT SOLOS
	JNZ  CONIN		;LOOP UNTIL LOW.
	ENDIF
;
	IF   RDYHI		;IF READY WHEN HIGH,
	JZ   CONIN		;LOOP UNTIL HIGH.
	ENDIF
;
	IF   NOT SOLOS		;IF NOT PROC TECH SOLOS,
	IN   CDATA		;READ A CHARACTER.
	ENDIF
;
	ANI  7FH		;MAKE MOST SIG. BIT = 0.
	RET			;RETURN FROM CONIN.
;
; WRITE A CHARACTER TO THE CONSOLE DEVICE.
;
CONOT:
	IF CONUL		;IF NULLS REQUIRED,
	MVI  A,0AH		;IF IT'S A LF,
	CMP  C			;THEN HOP OUT
	JZ   CONULL		;TO NULL ROUTINE.
	ENDIF
CONOT1:
	IF NOT SOLOS AND NOT VIDEO
	IN   CSTAT		;READ CONSOLE STATUS.
	ANI  CPTR		;IF NOT READY,
	ENDIF
;
	IF RDYLO AND NOT SOLOS AND NOT VIDEO
	JNZ  CONOT1		;LOOP UNTIL LOW.
	ENDIF
;
	IF RDYHI		;IF READY WHEN HIGH,
	JZ   CONOT1		;LOOP UNTIL HIGH.
	ENDIF
;
	IF NOT SOLOS AND NOT VIDEO
	MOV  A,C		;GET CHARACTER.
	OUT  CDATA		;PRINT IT.
	RET			;RETURN.
	ENDIF
;
;THIS ROUTINE CALLES YOUR VIDEO DRIVER ROUTINE WHICH MUST
;BE IN ROM. ALL REGISTERS MUST BE SAVED AND RESTORED BY YOUR
;VIDEO DRIVER IN ORDER TO BE COMPATIABLE WITH CPM. CPM PASSES
;THE CHAR. TO BE OUTPUT IN THE  C REGISTER. MAKE ANY CHANGES
;IN THIS ROUTINE TO PASS THE CHAR FROM REG C TO THE REGISTER
;YOUR VIDEO DRIVER EXPECTS IT TO BE IN.
;
	IF  VIDEO		;IF USING A VIDEO DRIVER IN ROM.
	MOV  A,C		;GET THE CPM CHAR INTO REG A
	CALL OUTADDR		;CALL YOUR VIDEO DRIVER.
	RET			;RETURN TO CPM.
	ENDIF
;
	IF CONUL
CONULL:	PUSH B			;SAVE B&C.
	MVI  B,CNULL+1		;GET NULL COUNT.
CONUL1:	CALL CONOT1		;PRINT CR.
	MVI  C,0		;GET NULL CHAR.
	DCR  B			;DECREMENT COUNTER.
	JNZ  CONUL1		;DO NEXT NULL.
	POP  B			;RESTORE B&C.
	MOV  A,C		;RESTORE A.
	RET			;RETURN.
	ENDIF
;
	IF   SOLOS		;IF PROC TECH SOLOS,
	PUSH B			;SAVE B&C.
	MOV  B,C		;PUT CHAR IN B REG.
	CALL SCRN		;OUTPUT CHAR TO SOLOS.
	POP  B			;RESTORE B&C.
	MOV  A,C		;PUT CHAR IN A.
	RET			;RETURN FROM CONOT.
	ENDIF
;
; SELECT DISK NUMBER ACCORDING TO REGISTER C.
;
SELDSK:	LXI  H,0		;SET UP FOR ERROR CODE
	MOV  A,C		;GET NEW DRIVE.
	CPI  NDISK		;CALLING UNDEFINED DRIVE ?
	RNC			;IF NO CY, H,L TELLS CPM YES.
	LXI  H,DISKNO		;GET OLD DRIVE NUMBER.
	MOV  A,M		;GET OLD DISK NUMBER.
;
	IF   DUAL		;IF DUAL DRIVE,
	ANI  0FEH		;CLEAR OUT BIT 0.
	ENDIF
;
	MOV  E,A		;PUT OLD DISK NO. IN D&E.
	MVI  D,0
	LXI  H,TRTAB		;GET ADDRESS OF TRACK TABLE.
	PUSH H			;SAVE ADDRESS OF TRTAB.
	DAD  D			;ADD DISK NO. TO ADDRESS.
	IN   TRACK		;READ 1771 TRACK REGISTER.
	MOV  M,A		;PUT INTO TABLE.
	MOV  A,C		;GET NEW DISK NUMBER.
;
	IF   DUAL		;IF A DUAL DRIVE,
	ANI  0FEH		;CLEAR BIT 0.
	ENDIF
;
	MOV  E,A		;PUT NEW DISK NO. IN D&E.
	POP  H			;RESTORE ADDRESS OF TRTAB.
	DAD  D			;ADD DISK NO. TO ADDRESS.
	MOV  A,M		;GET NEW TRACK NUMBER.
	OUT  TRACK		;PUT INTO 1771 TRACK REG.
	MOV  A,C		;UPDATE OLD DISK NUMBER.
	STA  DISKNO
	ADD  A			;PUT BITS 1&2 AT 4&5.
	ADD  A
	ADD  A
	ADD  A
	STA  LATCH		;SAVE NEW LATCH CODE.
DENSITY:LXI  H,DRVFLG		;POINT TO DRIVE DEN. FLAG
	MVI  B,0		;CLEAR REG B.
	DAD  B			;INDEX INTO DRIVE FLAG LOC.
	MOV  A,M		;GET THE FLAG BYTE
	ORA  A			;LOGGED IN?
	JM   LOGED		;YES, IT'S LOGGED.
	PUSH H			;NO, SAVE FLAG ADDRESS.
	LDA  LATCH		;GET LATCH CODE
	OUT  DCONT		;CHANGE LATCH OR DENSITY
;
;READ TRACK 0 SECTOR 1 FOR DENSITY BYTE AT 7E HEX.
;
	MVI  A,1		;SECTOR 1.
	STA  SECT		;SAVE THE SECTOR VALUE.
	CALL HOME		;HOME THE DRIVE.
	LHLD DMAADD		;GET CP/M DMA ADDRESS VALUE
	PUSH H			;SAVE IT ON THE STACK.
	LXI  H,DBUFF		;POINT TO THE DMA BUFFER.
	SHLD DMAADD		;SET UP READ DMA ADDRESS.
;
;READ THE DATA USING READ ROUTINE.
;
	CALL READ		;CBIOS READ ROUTINE.
;
;GET DENSITY BYTE VALUE AND DETERMINE DRIVE STATUS.
;
	POP  H			;RESTORE DMA ADDRESS FROM THE STACK.
	SHLD DMAADD		; AND RESTORE THE CP/M DMA ADDRESS.
	POP  H			;RESTORE DENSITY FLAG ADDRESS.
	LDA  DBUFF+7EH		;INDEX INTO DBUFF TO LOCATION DBUFF+7E.
	ORI  80H		;set logged in bit
	MOV  M,A		;place it in flag table.
LOGED:	LXI  B,18		;index value through drive table.
	ANI  12H		;MASK DENSITY AND SIDE BITS OUT.
	ORA  A			;SINGLE DENSITY?
	LXI  H,SDTAB		;point to start of tables
	JZ   DENSIT1		;yes, overlay param. block
;
	IF   DUBSID
	DAD  B			;no, add offset to next table
	CPI  2			;single den doub sided?
	JZ   DENSIT1		;yes
	DAD  B			;no
	CPI  10H		;doub den single sided?
	JZ   DENSIT1		;yes
	ENDIF
;
	DAD  B			;no, must be doub den , doub sided
DENSIT1:XCHG			;drive table pointer --> d,e
	LDAX D			;get log and drive type byte.
	INX  D			;bump pointer
	STA  DENS		;set current drive density.
	PUSH D			;save drive table pointer.
	CALL PARINDX		;compute parameter overlay area.
	POP  D			;restore drive table pointer.
	LXI  B,0208H		;B = 2, C = 8 (count values).
MOVE:	LDAX D			;GET XLT0 BYTE.
	MOV  M,A		;AND PUT IT INTO DW TABLE FOR DRIVE.
	INX  D			;BUMP
	INX  H			; POINTERS
	DCR  B			;DECREASE COUNT.
	JNZ  MOVE		; AND LOOP TILL ZERO.
	DAD  B			;NOW ADD INDEX INTO DPB0 AREA.
	MOV  M,E		;GET LOW POINTER BYTE.
	INX  H			;BUMP POINTER.
	MOV  M,D		;GET HIGH POINTER BYTE.
;
;SELECT DRIVE AS A FUNCTION OF H,L
;
PARINDX:LHLD DISKNO		;LOAD DISK NUMBER AND ZERO BYTE
	LXI  D,DPBASE		;POINT TO DISK PARM START.
	DAD  H			;*2
	DAD  H			;*4
	DAD  H			;*8
	DAD  H			;*16
	DAD  D			;COMPUTE INDEX FOR THE DRIVE
	XRA  A			;SET A = 0.
	RET			;RETURN FROM SELDSK.
;
; MOVE DISK TO TRACK ZERO.
;
HOME:	MVI  C,0		;SEEK TO TRACK ZERO.
	MVI  A,STPRAT		;RESTORE COMMAND
	OUT  DCOM		;TELL CONTROLLER.
;
; SET TRACK NUMBER TO WHATEVER IS IN REGISTER C.
; ALSO PERFORM MOVE TO THE CORRECT TRACK (SEEK).
;
SETTRK:
	LHLD LATCH		;get new and old latch.
	MOV  A,H		;get latch value.
	ANI  0B7H		;strip density and side bits.
	MOV  H,A		;restore it.
;
	IF   DUBSID		;if using double sided drive.
	LDA  DENS		;check if double sided.
	RRC
	RRC			;look at bit 1.
	JNC  NOTSID		;if bit 1 = 0, it's single sided.
	MOV  A,C		;it's doub sided, so get track number.
	RRC			;divide by 2.
	MOV  B,A		;save it in reg b.
	MOV  A,L		;get old latch value.
	JC   SIDE2		;change side if odd track.
	ANI  0BFH		;clear side bit from latch.
	JMP  SETLAT		;go set the latch.
;
SIDE2:	ORI  40H		;turn on side select bit.
SETLAT:	STA  CLATCH		;save it for later.
	ANI  0BFH		;clear side bit.
	CALL OLDLAT		;check for drive change.
	MOV  A,B		;restore doub sided trk number.
	ANI  7FH		;clear bit 7.
	MOV  C,A		;trk number now in reg c.
	JMP  TRKSET		;check for density of track going to.
	ENDIF
;
	IF   NOT DUBSID		;if not using double sided drive
	JMP  NOTSID		;jump around subroutine.
	ENDIF
;
OLDLAT:	CMP  H			;new = old?
	MVI  A,0FFH		;if not, set = ff
	JNZ  SFLAG
	CMA			;new = old, set = 0.
SFLAG:	STA  HLSF		;save head load select flag.
	RET
;
NOTSID:	MOV  A,L		;get latch value.
	STA  CLATCH		;save it
	CALL OLDLAT		;check for drive change.
TRKSET:	LDA  DENS		;CHECK DRIVE DENSITY FLAG.
	RRC			;IS BIT 0 = 0?
	JNC  TRKSD		;YES, WE ARE SINGLE DENSITY.
	MOV  A,C		;NO, RESTORE TRACK NUMBER.
	CPI  1			;IS IT TRACK 1?
	JC   TRKSD		;IF LESS THAN, SET SINGLE DENSITY.
	LDA  CLATCH		;GET CURRENT LATCH VALUE.
	ORI  8			;SET FOR DOUBLE DENSITY.
	JMP  TRKDD
;
TRKSD:	LDA  CLATCH		;GET CURRENT LATCH VALUE.
	ANI  0F7H		;TURN OFF BIT 4 (SINGLE DENSITY).
TRKDD:	STA  CLATCH		;SAVE NEW LATCH VALUE.
	OUT  DCONT		;SELECT DISK AND MAKE DENSITY CHANGE.
	MOV  A,C		;RESTORE TRACK VALUE
	STA  TRK		;UPDATE OLD WITH NEW.
;
; MOVE THE HEAD TO THE TRACK IN REGISTER A.
;
SEEK:	PUSH B			;SAVE B&C.
	MOV  B,A		;SAVE DESTINATION TRACK.
	MVI  A,RTCNT		;GET RETRY COUNT.
SRETRY:	STA  SERCNT		;STORE IN ERROR COUNTER.
	IN   TRACK		;READ PRESENT TRACK NO.
	CMP  B			;SAME AS NEW TRACK NO.?
	JNZ  NOTHR		;JUMP IF NOT THERE.
THERE:	POP  B			;RESTORE B&C.
	RET			;RETURN FROM SEEK.
NOTHR:	MOV  A,B		;RESTORE A FROM B.
	OUT  DDATA		;TRACK TO DATA REGISTER.
	MVI  A,14H+STPRAT+HLAB  ;GET STEP RATE, DO
	OUT  DCOM		;SEEK WITH VERIFY.
;
	IF NOT DMACNTL
	IN   WAIT		;WAIT FOR INTRQ.
	IN   DSTAT		;READ STATUS.
	ENDIF
;
	IF DMACNTL
	CALL SLOOP		;NO WAIT STATUS CHECK.
	ENDIF
;
	ANI  91H		;LOOK AT BITS.
	JZ  THERE		;OK IF ZERO.
	LDA  SERCNT		;GET ERROR COUNT.
	DCR  A			;DECREMENT COUNT.
	JNZ  SRETRY		;RETRY SEEK.
	POP  B			;RESTORE B&C.
	PUSH B			;SAVE
	CALL RECOV		;IF SEEK RETRY = 10 CHECK
	POP  B
	MOV  A,C		;RECOVER TRACK NUMBER.
	JMP  SEEK		; FOR CNTL-C FOR ABORT.
;
; SET DISK SECTOR NUMBER.
;
SETSEC:  MOV  A,C		;GET SECTOR NUMBER.
         STA  SECT		;PUT AT SECT # ADDRESS.
         RET			;RETURN FROM SETSEC.
;
;TRANSLATE THE SECTOR GIVEN B,C USING
;THE TRANSLATE TABLE;GIVEN BY D,E
;
SECTRAN:
	MOV  L,C		;GET PHYSICAL SECTOR NUMBER
	INR  L			;BUMP IT BY ONE.
	MOV  A,D		;ARE WE USING NO XLAT TABLE?
	ORA  E			;IT WILL BE ZERO IF NOT.
	RZ			;RETURN IF IT IS ZERO.
	XCHG			;H,L = TRANS
	DAD  B			;H,L = TRANS (SECTOR)
	MOV  L,M		;L   = TRANS (SECTOR)
	MVI  H,0		;CLEAR REG H
	RET			;H,L = TRANSLATED SECTOR
;
; SET DISK DMA ADDRESS.
;
SETDMA:	MOV  H,B		;MOVE B&C TO H&L.
	MOV  L,C
	SHLD DMAADD		;PUT AT DMA ADR ADDRESS.
	RET			;RETURN FROM SETDMA.
;
; HDLD - GET HEAD-LOAD BIT IF REQUIRED.
;
HDLD:	LDA  HLSF		;GET HEAD-LOAD FLAG.
	ORA  A			;IS A = ZERO?
	JZ   HDLD1		;HOP IF SO.
	CMA			;SET A = 0.
	STA  HLSF		;SET FLAG = 0 IF NOT.
;
;IF CHANGING TO A NEW DRIVE, PERFORM A SEEK TO
;THE SAME TRACK TO UNLOAD THE HEAD ON NEW DRIVE.
;
	IN   TRACK		;GET PRESENT TRACK
	OUT  DDATA		;TELL CONTROLLER.
	MVI  A,14H+STPRAT
	OUT  DCOM
;
	IF   NOT DMACNTL
	IN   WAIT		;WAIT FOR INTRQ.
	ENDIF
;
	IF   DMACNTL
	CALL SLOOP		;CHECK DMA STATUS PORT.
	ENDIF
;
HDLD1:	IN   DSTAT		;READ 1771 STATUS.
	ANI  20H		;LOOK AT HL BIT.
	MVI  A,4
	RZ			;RETURN IF HEAD IS NOT LOADED.
	SUB  A			;HEAD IS ALREADY LOADED.
	RET			;RETURN FROM HDLD.
;
; READ THE SECTOR AT SECT, FROM THE PRESENT TRACK,
; USE STARTING ADDRESS AT DMAADD.
;
READ:	MVI  A,RTCNT		;GET RETRY COUNT.
RRETRY:
	IF DMACNTL
	LXI  B,80D0H		;FLOPPY READ, FORCE INTERRUPT.
	LXI  D,4080H		;DMA READ, DMA COUNT BYTE
	CALL DMARW		;ENTER COMMON READ/WRITE ROUTINE.
	ENDIF
;
	IF NOT DMACNTL
	MVI  B,80H		;FLOPPY READ COMMAND BYTE.
	CALL DSKSET		;SET UP DISK CONTROLLER.
	ORA  B			;'OR' IN THE READ COMMAND.
READE:	OUT  DCOM		;SEND COMMAND TO 1771.
RLOOP:	IN   WAIT		;WAIT FOR DRQ OR INTRQ.
	ORA  A			;SET FLAGS.
	JP   RDDONE		;DONE IF INTRQ.
	IN   DDATA		;READ A DATA BYTE FROM DISK.
	MOV  M,A		;PUT BYTE INTO MEMORY.
	INX  H			;INCREMENT MEMORY POINTER.
	JMP  RLOOP		;KEEP READING.
RDDONE:	IN   DSTAT		;READ DISK STATUS.
	ENDIF
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	EI			;ALLOW AGAIN HERE.
	ENDIF
;
	ANI  9DH		;LOOK AT ERROR BITS.
	RZ			;RETURN IF NONE.
	CALL ERCHK		;CHECK FOR SEEK ERROR.
	JNZ  RRETRY		;TRY TO READ AGAIN.
	CALL RECOV		;CHECK FOR ABORT OR CONTINUE
	JMP  READ		;IF NOT CNTL-C, TRY TO READ AGAIN.
;
;RECOV
;THIS ROUTINE IS CALLED BY ANY READ,WRITE,SEEK ROUTINE IF THE RETRY
;COUNT GOES TO 10. IF IT DOES,THIS ROUTINE CALLS CONIN FOR A KEY TO
;BE PUSHED. IF THE KEY IS A CNTL-C, THEN A WARMBOOT IS EXECUTED. IF
;ANY OTHER KEY IS PUSHED, THEN A RETURN IS MADE BACK TO THE CALLER
;AND THAT ROUTINE IS RETRIED FOR 10 MORE TIMES.
;
RECOV:
	MVI  C,'e'		;ERROR CODE
	CALL CONOT		;PRINT IT
	CALL CONIN		;CHECK FOR PUSHED KEY.
	CPI  03H		;IS IT A CNTL-C ?
	RNZ			;RETURN TO CALLER IF NOT.
	JMP  WBOOT		;YES, DO WARMBOOT.
;
; ERCHK - CHECK FOR RECORD NOT FOUND ERROR.
;
ERCHK:	ANI  10H		;IF RECORD NOT FOUND,
	JNZ  CHKSK		;DO A CHECK ON SEEK.
CHKOK:	LDA  ERCNT		;GET RETRYS ALLOWED
	DCR  A			;DECREASE IT,
	RET			;AND RETURN WITH NUMBER.
;
;CHECK FOR SEEK TO CORRECT TRACK,
;AND CHANGE IF NECESSARY.
;
	IF NOT DMACNTL
CHKSK:	MVI  A,0C4H		;SEND COMMAND TO 1771
	OUT  DCOM		;TO READ ADDRESS.
	IN   WAIT		;WAIT FOR DRQ OR INTRQ.
	IN   DDATA		;READ THE TRACK ADDRESS.
	PUSH PSW		;SAVE IT ON THE STACK.
CHKS2:	IN   DMACHK		;WAIT FOR INTRQ.
	ORA  A			;SET FLAGS.
	JP   CHKS3		;DONE WITH READ ADR OP.
	IN   DDATA		;READ ANOTHER BYTE.
	JMP  CHKS2		;DO IT AGAIN.
CHKS3:	IN   DSTAT		;READ DISK STATUS.
	ENDIF
;
	IF   DMACNTL
CHKSK:	LXI  H,BIOS-7		;POINT TO UNUSED SPACE
	LXI  B,0C4D0H		;READ ADDRESS, FORCE INTERRUPT CMNDS.
	LXI  D,04006H		;DMA READ, COUNT BYTE
	CALL DMARWE		;READ THE ID USING DMA CONTROL.
	ORA  A			;SET FLAGS.
	JZ   CHKS4		;READ ADR OK IF 0.
	CALL HOME		;OTHERWISE, HOME FIRST.
	JMP  CHKS5
	ENDIF
;
	IF   NOT DMACNTL
CHKS4:	POP  PSW		;UPDATE TRACK REGISTER.
	ENDIF
;
	IF   DMACNTL
CHKS4:	IN   SECTP		;GET THE TRACK BYTE
	ENDIF
;
	OUT  TRACK
CHKS5:	LDA  TRK		;GET REQUIRED TRACK NO.
	CALL SEEK		;MOVE THE HEAD TO IT.
	JMP  CHKOK		;EXIT FROM ERROR CHECK.
;
	IF   NOT DMACNTL
DSKSET:	STA  ERCNT		;STORE IN ERROR CTR.
	MVI  A,0D0H		;CAUSE INTERRUPT.
	OUT  DCOM
	XTHL			;SOME
	XTHL			; DELAY
	ENDIF
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	DI			;DISABLE THEM HERE.
	ENDIF
;
	IF   NOT DMACNTL
	LHLD DMAADD		;GET STARTING ADDR.
	LDA  SECT		;GET SECTOR NUMBER.
	OUT  SECTP		;SET SECTOR INTO 1771.
	CALL HDLD		;GET HEAD-LOAD BIT?
	RET			;RETURN TO CALLER
	ENDIF
;
; WRITE THE SECTOR AT SECT, ON THE PRESENT TRACK,
; USE STARTING ADDRESS AT DMAADD.
;
WRITE:	MVI  A,RTCNT		;GET RETRY COUNT.
WRETRY:
	IF   DMACNTL
	LXI  B,0A0D0H		;FLOPPY WRITE, FORCE INTERRUPT.
	LXI  D,08080H		;DMA WRITE, DMA COUNT BYTE.
	CALL DMARW		;ENTER COMMON READ/WRITE ROUTINE.
	ENDIF
;
	IF   NOT DMACNTL
	MVI  B,0A0H		;FLOPPY WRITE COMMAND BYTE.
	CALL DSKSET		;SET UP FLOPPY CONTROLLER.
	ORA  B			;'OR' IN WRITE COMMAND.
WRITE2:	OUT  DCOM
WLOOP:	IN   WAIT		;WAIT FOR READY.
	ORA  A			;SET FLAGS.
	JP   WDONE		;HOP OUT WHEN DONE.
	MOV  A,M		;GET BYTE FROM MEM.
	OUT  DDATA		;WRITE ONTO DISK.
	INX  H			;INCREMENT MEM PTR.
	JMP  WLOOP		;KEEP WRITING.
WDONE:	IN   DSTAT		;READ DISK STATUS.
	ENDIF
;
	IF INTRP AND NOT DMACNTL;IF INTERRUPTS ALLOWED,
	EI			;ENABLE AGAIN HERE.
	ENDIF
;
	ANI  0FDH		;LOOK AT THESE BITS.
	RZ			;RETURN IF NO ERR.
	CALL ERCHK		;CHECK/CORRECT SEEK ERR.
	JNZ  WRETRY		;TRY TO WRITE AGAIN.
	CALL RECOV		;CHECK FOR ABORT
	JMP  WRITE		;RETRY WRITE AGAIN.
;
;LIST STATUS CHECK ROUTINE
;
PRSTAT:	CALL PSTAT		;CHECK PRINTER STATUS PORT.
;
	MVI  A,0		;RETURN STATUS ACTIVITY.
;
	IF   TARDEL OR RDYLO
	RNZ
	ENDIF
;
	IF   RDYHI
	RZ
	ENDIF
;
	CMA			;INVERT IT
;
; PUNCH AND READER ARE NOT SUPPORTED.
;
PUNCH:
READER:	RET
;
;PSTAT - PRINTER STATUS CHECK ROUTINE.
;
PSTAT:	IN   LSTAT		;READ PRINTER STATUS PORT.
;
	IF   NOT TARDEL
	ANI  LRBIT
	ENDIF
;
	IF   TARDEL
	ANI  81H		;MASK READY BITS
	XRI  81H
	ENDIF
;
	RET			;RETURN TO CALLER
;
; WRITE A CHARACTER ON LISTING DEVICE.
;
LIST:
	IF   LSTNUL  		;IF NULLS OR PAGING,
	MVI  A,0DH		;IF IT'S A CR,
	CMP  C			;THEN HOP OUT TO
	JZ   LINUL		;NULL ROUTINE.
	ENDIF
;
	IF   LSTPAG		;IF PAGING
	MVI  A,0AH		;GET A LINEFEED
	CMP  C			;DOES IT MATCH?
	JZ   LINUL3
	MOV  A,C
	CPI  0CH
	RZ
	ENDIF
;
LTBSY:	CALL PSTAT		;READ LISTER STATUS.
;
	IF   TARDEL OR RDYLO
	JNZ  LTBSY		;LOOP TILL LOW.
	ENDIF
;
	IF   NOT TARDEL AND RDYHI
	JZ   LTBSY		;LOOP TILL HIGH.
	ENDIF
;
	MOV  A,C		;GET DATA BYTE.
	OUT  LDATA		;PRINT IT.
	RET			;RETURN FROM LIST.
;
	IF   LSTNUL		;IF LIST NULLS
LINUL:	PUSH B			;SAVE B&C.
	MVI  B,(LNULL AND 0FFH)+1 ;GET NULL COUNT
LINUL1:	CALL LTBSY		;PRINT (CR FIRST).
	MVI  C,0		;GET NULL CHAR.
	DCR  B			;DECREMENT COUNTER.
	JNZ  LINUL1		;DO NEXT NULL.
	JMP  LINUL2		;EXIT THE ROUTINE.
	ENDIF
;
	IF   LSTPAG		;IF LIST DEV. PAGING,
LINUL3:	PUSH B			;SAVE B,C PAIR
	LDA  LFCNT		;GET LINE-FEED COUNT.
	INR  A			;INCREMENT IT.
	STA  LFCNT		;SAVE IT BACK.
	CPI  LINCNT-(LINCNT/11) ;END OF PAGE?
	MVI  B,1		;SET UP FOR 1 LF.
	JNZ  NOTEOP		;HOP IF NOT END.
	XRA  A			;SET LF COUNT = 0.
	STA  LFCNT
	MVI  B,(LINCNT/11)+1	;BETWEEN PAGES.
NOTEOP:	MVI  C,0AH		;GET LINE-FEED CODE.
LSTPA1:	CALL LTBSY		;PRINT LINE-FEED.
	DCR  B			;DECREMENT LF COUNTER.
	JNZ  LSTPA1		;DO NEXT LINE FEED?
	ENDIF
;
	IF   LSTNUL OR LSTPAG	;IF NULLS OR PAGING,
LINUL2:	POP  B			;RESTORE B&C.
	MOV  A,C		;RESTORE A.
	RET			;RETURN FROM LIST.
	ENDIF
;
ENDPROG	EQU	$-1		;ENDING ADDRESS.
;
;NOTE:  AS THERE ARE ONLY SIX (6) SECTORS AVAILABLE FOR CBIOS ON
;THE SECOND SYSTEM TRACK (1), THE LAST ADDRESS BEFORE THIS POINT
;SHOULD BE NO GREATER THAN THE CBIOS STARTING ADDRESS + 037F (HEX).
;THIS WILL NORMALLY BE XD7F (HEX).
;
; BIOS SCRATCH AREA.
;
TRK:	DS   1			;CURRENT TRACK NUMBER.
SECT:	DS   1			;CURRENT SECTOR NUMBER.
DMAADD:	DS   2			;DISK TRANSFER ADDRESS.
;
; THE NEXT SEVERAL BYTES, BETWEEN STARTZ AND
; ENDZ, ARE SET TO ZERO AT COLD BOOT TIME.
;
STARTZ:				;START OF ZEROED AREA.
;
DISKNO:	DS   2			;DISK NUMBER
;
; SPECIAL FLAGS.
;
HLSF:	DS   1			;HEAD-LOAD SELECT FLAG.
LFCNT:	DS   1			;PAGING LINE-FEED COUNT.
;
; TRTAB - DISK TRACK TABLE - PRESENT POSITION OF
;	HEADS FOR UP TO 4 DRIVES.
;
TRTAB:	DS   4
DRVFLG:	DS   4			;DRIVE DENSITY FLAGS.
DENS:	DS   1			;CURRENT DRIVE DENSITY VALUE.
ERCNT:	DS   1			;ERROR COUNT FOR RETRIES.
SERCNT:	DS   1			;SEEK RETRY COUNTER.
LATCH:	DS   1			;NEW CODE FOR LATCH.
CLATCH:	DS   1			;CURRENT CODE IN LATCH.
;
ENDZ	EQU  $
;
BEGDAT	EQU  $
DIRBUF:	DS   128		;DIRECTORY BUFFER
ALV0:	DS   31
CSV0:	DS   24
ALV1:	DS   31
CSV1:	DS   24
ALV2:	DS   31
CSV2:	DS   24
ALV3:	DS   31
CSV3:	DS   24
ENDDAT	EQU  $
DATSIZ	EQU  $-BEGDAT		;TOTAL SIZE OF DISK PARM STORAGE.
;
DBUFF:	DS   128		;128 BYTE DENSITY SELECT BUFFER.
;
	ORG  ENDPROG		;SHOW ACTUAL ENDING ADDRESS OF BIOS
	END
