; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS)
; TARBELL ELECTRONICS
; 1.4 VERSION OF 7-20-79
; THIS BIOS CAN ALSO BE USED WITH 1.3 VERSIONS BY
; CHANGING BDOS EQU FROM CBASE+3106H TO CBASE+3206H.
; (NOTE THAT CP/M VERSION 1.3 ONLY SUPPORTS 2 DRIVES,
; WHILE CP/M VERSION 1.4 WILL SUPPORT 4 DRIVES.)
;
; THIS MODULE CONTAINS ALL THE INPUT/OUTPUT
; ROUTINES FOR THE CP/M SYSTEM, INCLUDING
; THE DISK ROUTINES.
;
; 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  48		;MEMORY SIZE IN KBYTES.
INTRP	EQU  FALSE	;TRUE IF INTERRUPTS ALLOWED.

STD	EQU  FALSE	;TRUE IF STANDARD I/O.
MSIO2	EQU  TRUE	;TRUE IF MITS 2SIO.
ISIO2	EQU  FALSE	;TRUE IF IMSAI SIO-2.
TUART	EQU  FALSE	;TRUE IF CROMEMCO TUART.
VDM	EQU  FALSE	;TRUE IF PROC TECH VDM.
FLASH	EQU  FALSE	;TRUE IF VG FLASHWRITER.
VB1	EQU  FALSE	;TRUE IF SSM VB1-B.
OTHER	EQU  FALSE	;TRUE IF SOMETHING ELSE.
SOLOS	EQU  FALSE	;TRUE IF PROC TECH SOLOS.
BACKSP	EQU  TRUE	;AUTO-BACKSPACE FOR CRT'S.
DUBSID	EQU  FALSE	;TRUE FOR DOUBLE SIDED DRIVES.

	IF  NOT SOLOS	;IF NOT PROC TECH SOLOS,
CSTAT	EQU  10H	;CONSOLE STATUS PORT.
	ENDIF

CCOM	EQU  10H	;CONSOLE COMMAND PORT.
CDATA	EQU  11H	;CONSOLE DATA PORT.
CONUL	EQU  FALSE	;CONSOLE NULLS?
CNULL	EQU  0		;CONSOLE NULL COUNT.

LSTAT	EQU  12H	;LIST STATUS PORT.
LCOM	EQU  12H	;LIST COMMAND PORT.
LDATA	EQU  13H	;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.

HLAB	EQU  0		;8 FOR HD LD AT BEG OF SEEK.
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     ***
*******************************************************

VIDEO	EQU  VDM OR FLASH OR VB1    ;TRUE FOR ANY VIDEO.
RDYLO	EQU  STD OR SOLOS OR OTHER  ;STATUS READY WHEN LOW.
RDYHI	EQU  NOT RDYLO

	IF   VDM	;IF PROC TECH VDM1,
SCREEN	EQU  0CC00H	;SCREEN PLACE IN MEMORY.
ENDSCR	EQU (SCREEN+1024) SHR 8
	ENDIF

	IF  FLASH OR VB1
SCREEN	EQU  0F800H	;SCREEN PLACE IS DIFFERENT.
	ENDIF

	IF  FLASH	;IF VG FLASHWRITTER
ENDSCR	EQU (SCREEN+2048) SHR 8
	ENDIF

	IF  VB1		;IF SSM VB1-B BOARD
ENDSCR	EQU (SCREEN+1024) SHR 8
	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,
DISK	EQU  0F8H	;DISK BASE ADDRESS.
	ENDIF

	IF SOLOS	;IF PROC TECH SOLOS,
DISK	EQU  0E8H	;DIFFERENT DISK PORTS.
	ENDIF

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.

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   ISIO2	;IF IMSAI SIO-2,
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-17)*1024  ;BIAS FOR LARGER THAN 17K.
CPMB	EQU  CBASE+2900H	;START OF CPM.
BDOS	EQU  CBASE+3106H	;START OF BDOS 1.4.
CPML	EQU  1500H	;LENGTH OF CPM SYSTEM-BIOS.
NSECTS	EQU  CPML/128	;NUMBER OF SECTORS IN IT.

	IF   DUBSID		;IF DOUBLE-SIDED,
	ORG  CPMB+083BH		;DISK PARAMETER BLOCK.
	DB   7FH		;DIRMAX.
	DB   04H		;BLKSHF.
	DB   0FH		;BLKMSK.
	;
	ORG  CPMB+1072H		;CP/M PATCH.
	CALL CPMB+0AF4H
	NOP!NOP!NOP
	MOV  A,C
	LXI  H,CPMB+14F8H
	ENDIF

	ORG  CPMB+1500H		;START OF BIOS.
;
; 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  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.
; THESE ENTRY POINTS ADDED BY TARBELL ELECTRONICS.
	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.
;
; 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	;IF INTERRUPTS ALLOWED,
	EI		;ENABLE THEM HERE.
	ENDIF

	IF   STD	;IF STANDARD I/O,
	NOP!NOP!NOP!NOP	;LEAVE SPACE FOR INIT.
	NOP!NOP!NOP!NOP
	NOP!NOP!NOP!NOP
	NOP!NOP!NOP!NOP
	ENDIF

	IF   MSIO2	;IF MITS 2SIO,
	MVI  A,3	;INITIALIZE 2SIO.
	OUT  CCOM
	OUT  LCOM
	MVI  A,11H
	OUT  CCOM
	OUT  LCOM
	ENDIF

	IF   ISIO2	;IF IMSAI SIO2,
	MVI  A,0AAH	;INITIALIZE SIO 2-2.
	OUT  CCOM
	OUT  LCOM
	MVI  A,40H
	OUT  CCOM
	OUT  LCOM
	MVI  A,0CEH
	OUT  CCOM
	OUT  LCOM
	MVI  A,37H
	OUT  CCOM
	OUT  LCOM
	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

	XRA  A		;CLEAR SCRATCH AREA.
	STA  IOBYTE	;CLEAR I/O BYTE.
	MVI  C,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  C		;DECREMENT COUNTER.
	JNZ  BOOTL	;LOOP TILL DONE.
	MVI  A,0F2H	;SET LATCH CODE = F2.
	STA  LATCH
	CALL SETUP	;SET UP JUMPS.

	IF   VIDEO	;IF ANY VIDEO BOARD,
	CALL CLEAR	;CLEAR SCREEN.
	ENDIF

	IF   VDM	;IF PROC TECH VDM,
	XRA  A		;INITIALIZE.
	OUT  0C8H
	ENDIF

	IN   CDATA	;CLEAR CONSOLE STATUS.
	LXI  H,SMSG	;PRINT OPENING MESSAGE.
	CALL PMSG
	CALL CONIN	;READ # OF DISKS.
	MOV  C,A	;ECHO THE CHAR.
	CALL CONOT
	ANI  7		;LOOK AT 3 LSB'S.
	STA  NODSKS	;SAVE IT.
GOCPM:	LDA  DISKNO	;GET DISK NUMBER TO
	MOV  C,A	;PASS TO CCP IN C.
	JMP  CPMB	;JUMP TO CCP.
;
; SET UP JUMPS INTO CP/M IN LOWER MEMORY.
;
SETUP:	MVI  A,0C3H	;PUT JMP TO WBOOT
	STA  0		;ADR AT ZERO.
	LXI  H,WBOOTE
	SHLD 1
	STA  5
	LXI  H,BDOS	;PUT JUMP TO BDOS
	SHLD 6		;AT ADR 5,6,7.
	LXI  H,80H	;SET DEFAULT DMA ADR.
	SHLD DMAADD
	RET		;RETURN FROM SETUP.

	IF   TUART	;IF CROMEMCO TUART,
BAUDRS:	DB   94H,0CEH,0A2H,92H,88H,84H,82H,1
	ENDIF

;
; WARM-BOOT:  READ ALL OF CPM BACK IN
; EXCEPT BIOS, THEN JUMP TO CCP.
;
WBOOT:	LXI  SP,80H	;SET STACK POINTER.

	IF  INTRP	;IF INTERRUPTS ALLOWED,
	EI		;ALLOW THEM HERE.
	ENDIF

	IF   LSTPAG	;IF LIST DEVICE PAGING,
	XRA  A		;RESET LINE-FEED COUNT.
	STA  LFCNT
	ENDIF

	LDA  DISKNO	;SAVE DISK NUMBER.
	STA  TEMP
	MVI  C,0	;SELECT DISK ZERO.
	CALL SELDSK
	CALL HOME	;MOVE TO TRACK ZERO.
	JNZ  RDERR	;IF ERROR, PRINT MESSAGE.
	MVI  D,NSECTS	;GET # SECTORS FOR CPM READ.
	LXI  B,2	;TRACK (B)=0, SECTOR (C)=2.
	LXI  H,CPMB	;GET STARTING ADDRESS.

	IF  INTRP	;IF INTERRUPTS ALLOWED,
	DI		;DISABLE THEM HERE.
	ENDIF

RDBLK:	PUSH B		;SAVE B&C.
	MOV  C,B	;GO TO TRACK IN B.
	CALL SETTRK
	POP  B		;RESTORE B&C.
	JNZ  RDERR	;IF ERROR, PRINT MESSAGE.
RBLK1:	SHLD DMAADD	;SET STARTING ADDRESS.
	CALL SETSEC	;READ STARTING AT SECTOR IN C.
	CALL READ
	JNZ  RDERR	;IF ERROR, PRINT MESSAGE.
	DCR  D		;DECREMENT SECTOR COUNT.
	JZ   ALDON	;ALL DONE WHEN D=0.
	INR  C		;INCREMENT SECTOR NUMBER.
	MOV  A,C	;IF SECTOR NUMBER
	CPI  27		;IS NOT 27,
	JC   RBLK1	;KEEP READING ON THIS TRACK.
	MVI  C,1	;OTHERWISE, RESET SECTOR=1,
	INR  B		;INCREMENT TRACK NUMBER,
	JMP  RDBLK	;AND READ NEXT TRACK.
ALDON:	LDA  TEMP	;RESTORE DISK NUMBER.

	IF  INTRP	;IF INTERRUPTS ALLOWED,
	EI		;ALLOW THEM AGAIN HERE.
	ENDIF

	STA  DISKNO
	CALL SETUP	;SET UP JUMPS.
	JMP  GOCPM	;GO BACK TO CPM.
;
RDERR:	LXI  H,BTMSG	;GET ADDRESS OF "BOOT ERROR".
	CALL PMSG	;PRINT IT.
	CALL CONIN	;READ A CHAR FROM CONSOLE.
	JMP  WBOOT	;DO A WARM BOOT.
;
; CHECK CONSOLE INPUT STATUS.
;

CONST:	IN   CSTAT	;READ CONSOLE STATUS.
	ANI  CKBR	;LOOK AT KB READY BIT.
	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.

;
; READ A CHARACTER FROM CONSOLE.
;
CONIN:

	IF NOT SOLOS	;IF NOT PROC TECH SOLOS,
	IN   CSTAT	;READ CONSOLE STATUS.
	ANI  CKBR	;IF NOT READY,
	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.

	IF BACKSP	;IF BACKSPACE ACTIVATED,
	CPI  7FH	;IS IT A RUBOUT?
	RNZ		;RETURN IF NOT.
	MVI  A,0FFH	;SET NO PRINT FLAG.
	STA  CONOTF
	MVI  A,7FH	;RESTORE RUBOUT.
	ENDIF

	RET		;RETURN FROM CONIN.
;
; WRITE A CHARACTER TO THE CONSOLE DEVICE.
;

CONOT:

	IF   BACKSP	;IF BACKSPACE ACTIVATED,
	MOV  A,C	;GET CHARACTER.
	CPI  7FH	;IS IT A RUBOUT?
	RZ		;IF SO, DON'T PRINT IT.
	LDA  CONOTF	;GET NO PRINT FLAG.
	ORA  A		;SET CPU FLAGS.
	JZ   CONOTA	;NOT SET, SO PRINT.
	XRA  A		;RESET THE FLAG
	STA  CONOTF	;TO ZERO.
	MVI  C,8	;PRINT BACKSPACE.
	CALL CONOTA
	MVI  C,20H	;PRINT SPACE.
	CALL CONOTA
	MVI  C,8	;ANOTHER BACKSPACE.
CONOTA:
	ENDIF


	IF CONUL AND NOT VIDEO  ;IF NULLS REQUIRED,
	MVI  A,0DH	;IF IT'S A CR,
	CMP  C		;THEN HOP OUT
	JZ   CONULL	;TO NULL ROUTINE.
	ENDIF
CONOT1:
	IF NOT VIDEO AND NOT SOLOS
	IN   CSTAT	;READ CONSOLE STATUS.
	ANI  CPTR	;IF NOT READY,
	ENDIF

	IF RDYLO AND NOT VIDEO AND NOT SOLOS
	JNZ  CONOT1	;LOOP UNTIL LOW.
	ENDIF

	IF RDYHI AND NOT VIDEO  ;IF READY WHEN HIGH,
	JZ   CONOT1	;LOOP UNTIL HIGH.
	ENDIF

	IF NOT VIDEO AND NOT SOLOS
	MOV  A,C	;GET CHARACTER.
	OUT  CDATA	;PRINT IT.
	RET		;RETURN.
	ENDIF

	IF CONUL AND NOT VIDEO
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

	IF   VIDEO	;IF ANY VIDEO BOARD,
;
;
;	VIDEO DRIVER FOR VB1-B BOARD OR
;	VDM 1 BOARD
;
;
;	WRITTEN BY G.W.MULCHIN
;	9-16-78
;
;
LINES15 EQU SCREEN+0040H;VIDEO+15 LINES
SCRLCNT EQU 960		;LINES TO SCROLL
SCRNTOP EQU SCREEN+SCRLCNT
;
BKSP	EQU 08H		;BACKSPACE EQUATE
LF	EQU 0AH		;LINEFEED EQUATE
CR	EQU 0DH		;CARRIAGE RET EQUATE
FF	EQU 0CH		;FORM-FEED EQUATE.
			; NORMALLY PART OF STORAGE AREA.
;
	MOV A,C		;GET THE CHAR INTO REG A
	PUSH H		;SAVE REGISTERS
	PUSH D
	PUSH B
	PUSH PSW	;CHAR. IS IN REG A
	CALL VIDPRO	;DO VIDEO ROUTINE
	POP PSW		;RESTORE REGISTERS
	POP B		;
	POP D		;
	POP H		;
	RET		;BACK TO CALLER
VIDPRO:	LHLD VDMP	;GET SCREEN POSITION POINTER
	CPI CR		;IS IT CARRIAGE RETURN?
	JZ CARRET
	CPI LF		;IS IT LINEFEED?
	JZ LFNOT	;PUT IN A BLANK
	CPI BKSP	;IS IT A RUBOUT?
	JZ BS
	CPI  FF		;IS IT CNTL-L?
	JZ CLEAR
	MOV M,A		;IT HAS TO BE DATA
UPDATE:	INX H		;UPDATE POSITION
GONE:	MVI M,0A0H	;PUT CURSOR ON SCREEN
	JMP MAXLIN	;CHECK FOR LINE > 64
LFNOT:	MVI M,' '	;PUT IN A SPACE
	JMP UPDATE	;GET OUT NOW
BS:	MVI M,' '
	LHLD VDMP	;GET CURRENT POSITION
	DCX H
	SHLD VDMP	;SAVE CURSOR POSITION
	JMP GONE
CARRET:	MVI M,' '	;CHAR. IS A CARRIAGE RET.
	MOV A,L		;UPDATE NEXT POSITION
	ANI 0C0H
	ADI 40H		;SET UP FOR NEW LINE
	MOV L,A		;ADDRESS OF NEW LINE
	MVI A,0
	ADC H
	MOV H,A
MAXLIN:	SHLD VDMP	;SAVE POINTER FOR NEXT CHAR.
	MVI A,7FH
	ANA L
	RNZ		;EXIT BACK TO MAIN PROGRAM
	MVI M,' '
	LXI H,SCRNTOP
	SHLD VDMP
	LXI H,LINES15	;15 LINES OF SCREEN DATA
	LXI D,SCREEN	;TOP OF SCREEN. SET UP
	LXI B,SCRLCNT	; TO SCROLL 15 LINES
SCROLL:	MOV A,M		;START SCROLLLING UP
	STAX D		;STUFF REG A BY WAY OF D,E
	INX H
	INX D
	DCX B
	XRA A
	CMP B		;15 LINES YET?
	JNZ SCROLL
	CMP C
	JNZ SCROLL	;NOT DONE YET!
	LXI H,SCRNTOP
BLANK:	MVI M,' '	;PUT BLANK ON SCREEN
	INX H		;BLANK ENTIRE DATA LINE
	MOV A,L
	ANI 3FH
	JNZ BLANK
	LXI H,SCRNTOP
	MVI M,0A0H	;STUFF CURSOR BACK
	RET		;ALL DONE......!!!!
CLEAR:	LXI H,SCREEN	;CLEAR SCREEN
	MVI A,ENDSCR	;THIS IS END CHECK
CLERA:	CMP H		;IS IT END YET?
	JZ FINISH
	MVI M,' '
	INX H
	JMP CLERA	;GO BACK IF NOT DONE
FINISH:	LXI H,SCRNTOP
	MVI M,0A0H	;STUFF CURSOR BACK AGAIN
	SHLD VDMP
	RET		;ALL DONE!
	ENDIF		;END OF VDM DRIVER.

;
; SELECT DISK NUMBER ACCORDING TO REGISTER C.
;
SELDSK:	MOV  A,C	;GET NEW DISK NUMBER.
	ANI  3		;ONLY LOOK AT 2 LSB'S.
	LXI  H,DISKNO	;GET ADR OF OLD DISK NO.
	CMP  M		;NEW = OLD?
	RZ		;IF SO, RETURN.
	PUSH A		;SAVE DISK NUMBER.
	LDA  NODSKS	;GET NUMBER OF DISKS.
	DCR  A		;IF MORE THAN ONE DISK,
	JNZ  SELMOR	;TAKE CARE OF IT.
	LXI  H,MNTMSG	;GET ADR OF MOUNT MESSAGE.
	CALL PMSG	;PRINT "MOUNT ".
	POP  A		;GET DISK NUMBER.
	STA  DISKNO	;UPDATE OLD WITH NEW.
	ADI  'A'	;ADD ASCII FOR 'A'.
	MOV  C,A	;PUT INTO C.
	CALL CONOT	;PRINT IT.
	CALL CONIN	;READ A CARRIAGE RETURN.
	XRA  A		;SET A=0 FOR NO ERRO IND.
	RET		;RETURN FROM SELDSK.
SELMOR:	POP  A		;MAKE STACK RIGHT.
	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.
	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.
	LXI  H,TRTAB	;GET ADDRESS OF TRACK TABLE.
	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
	CMA		;BITS INVERTED INTO LATCH.
	ADD  A		;PUT BITS 1&2 AT 4&5.
	ADD  A
	ADD  A
	ADD  A
	ORI  2		;MAKE LATCH COMMAND.
	STA  LATCH	;SAVE NEW LATCH CODE.
	XRA  A		;SET A = 0.
	RET		;RETURN FROM SELDSK.
;
; MOVE DISK TO TRACK ZERO.
;
HOME:	MVI  C,0	;SEEK TO TRACK ZERO.
;
; SET TRACK NUMBER TO WHATEVER IS IN REGISTER C.
; ALSO PERFORM MOVE TO THE CORRECT TRACK (SEEK).
;
SETTRK:
	IF NOT DUBSID	;IF NOT DOUBLE-SIDED,
	PUSH H		;SAVE H&L.
	LHLD LATCH	;GET NEW & OLD LATCH.
	MOV  A,L	;GET NEW LATCH.
	OUT  DCONT	;SELECT DRIVE NOW.
	STA  CLATCH	;REMEMBER CURRENT LATCH.
	CMP  H		;IS NEW SAME AS OLD?
	MVI  A,0FFH	;IF NOT, SET FLAG = FF.
	JNZ  SFLAG
	CMA		;IF NEW = OLD, FLAG = 0.
SFLAG:	STA  HLSF	;SET HEAD-LOAD/SELECT FLAG.
	POP  H		;RESTORE H&L.
	ENDIF

	MOV  A,C	;GET NEW TRACK NUMBER.

	IF   DUBSID	;IF DOUBLE-SIDED DISK,
	RRC		;SHIFT RIGHT ONCE.
	PUSH PSW	;SAVE REVISED TRACK NUMBER.
	PUSH H		;SAVE H&L.
	LHLD LATCH	;GET NEW & OLD LATCH.
	MOV  A,L	;PUT NEW LATCH IN A.
	JC   SIDE2	;IF TRACK # ODD, SIDE #2.
	ORI  40H	;CLEAR LATCH BIT FOR SIDE 1.
	JMP  SETLAT	;GO AHEAD AND SET LATCH.
SIDE2:	ANI  0B2H	;SET LATCH BIT FOR SIDE 2.
SETLAT:	OUT  DCONT	;OUTPUT TO THE LATCH.
	STA  CLATCH	;SET CURRENT LATCH CODE.
	XRA  H		;COMPARE OLD WITH NEW,
	ANI  0BFH	;IGNORE SIDE BIT.
	MVI  A,0FFH	;IF OLD NOT = NEW,
	JNZ  SETFL	;  SET FLAG = FF.
	CMA  A		;IF OLD = NEW, FLAG = 0.
SETFL:	STA  HLSF	;SET FLAG ZERO IF SAME.
	POP  H		;RESTORE H&L.
	POP  PSW	;RESTORE THE TRACK NUMBER.
	ANI  7FH	;CLEAR MSB.
	MOV  C,A	;SET C=TRACK NUMBER.
	ENDIF

	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.
	MOV  C,A	;SAVE IN C.
	MOV  A,C	;DELAY.
	CMP  B		;SAME AS NEW TRACK NO.?
	MOV  A,B	;RESTORE A FROM B.
	JNZ  NOTHR	;JUMP IF NOT THERE.
THERE:	POP  B		;RESTORE B&C.
	RET		;RETURN FROM SEEK.
NOTHR:
;
;THIS ROUTINE IS TO ALLOW TIME FOR THE DRIVE
;TUNNEL ERASE TO TERMINATE BEFORE MOVING THE
;HEAD.  THE DELAY IS APPROX. 700 MICRO-SEC. @
;4 MHZ CPU TIME, AND DOUBLE THIS FOR 2 MHZ CPU'S.
;
	PUSH  PSW	;SAVE ACCUM AND FLAGS
	MVI   A,0D0H	;DELAY COUNT = 208
BUSY1:	DCR   A		;DECREASE COUNT
	JNZ   BUSY1	;LOOP TILL DONE
	POP   PSW	;RESTORE ACCUM AND FLAGS

	IF NOT PERSCI	;IF NOT PERSCI DRIVE,
	OUT  DDATA	;TRACK TO DATA REGISTER.
BUSY:	IN   DSTAT	;READ DISK STATUS.
	RRC		;LOOK AT BIT 0.
	JC   BUSY	;WAIT TILL NOT BUSY.
	MVI  A,14H+STPRAT+HLAB  ;GET STEP RATE, DO
	OUT  DCOM	;SEEK WITH VERIFY.
	IN   WAIT	;WAIT FOR INTRQ.
	IN   DSTAT	;READ STATUS.
	ANI  91H	;LOOK AT BITS.
	JZ  THERE	;OK IF ZERO.
	ENDIF

	IF  PERSCI	;IF PERSCI DRIVE,
	MVI  A,40H+HLAB  ;IF CARRY = 1,
	JC  SDIR	;STEP IN.
	MVI  A,60H+HLAB  ;OTHERWISE, OUT.
SDIR:	OUT  DCOM	;ISSUE STEP DIRECTION.
	MVI  A,20	;DELAY LOOP COUNT.
DLOOP:	DCR  A		;DECREMENT COUNTER.
	JNZ  DLOOP
	MOV  A,C	;GET PRESENT TRACK.
	SUB  B		;FIGURE TRACKS TO STEP.
	JP   STEP	;IF NEGATIVE,
	CMA		;FIGURE THE
	INR  A		;TWO'S COMPLEMENT.
STEP:	MOV  C,A	;GET DIFFERENCE.
	MVI  A,1	;PERSCI STEP COMMAND.
STEP1:	OUT  DCONT	;STEP PERSCI (E-14).
	DCR  C		;COUNT THE STEP.
	JNZ  STEP1	;STEP UNTIL C = 0.
	IN   WAIT	;CLEAR 1771.
	IN   DSTAT
	MOV  A,B	;GET DEST. TRACK.
	OUT  TRACK	;UPDATE TRACK REG.
	LDA  CLATCH	;GET LATCH CODE.
	ANI  72H	;MAKE COMMAND TO
	OUT  DCONT	;SWITCH WAIT FOR
	IN   WAIT	;SEEK COMPLETE.
	LDA  CLATCH	;RESTORE LATCH TO
	OUT  DCONT	;OLD CODE.
	XRA  A		;MAKE GOOD RETURN.
	POP  B		;RESTORE  B&C.
	RET
	ENDIF

	IF NOT PERSCI	;IF NOT PERSCI DRIVE,
	PUSH H		;SAVE H&L.
	LXI  H,SECNT	;GET ADR OF SEEK ERR CTR.
	INR  M		;ONE MORE SEEK ERROR.
	POP  H		;RESTORE H&L.
	LDA  SERCNT	;GET ERROR COUNT.
	DCR  A		;DECREMENT COUNT.
	JNZ  SRETRY	;RETRY SEEK.
	POP  B		;RESTORE B&C.
	LXI  H,SKMSG	;PRINT "SEEK ".
	IN   DSTAT	;READ DISK STATUS.
	ANI  91H	;LOOK AT ERROR BITS.
	MOV  D,A	;PUT IN REG D.
	JMP  ERMSG	;DO COMMON ERR MESSAGES.
	ENDIF

;
; SET DISK SECTOR NUMBER.
;
SETSEC:  MOV  A,C	;GET SECTOR NUMBER.
         STA  SECT	;PUT AT SECT # ADDRESS.
         RET		;RETURN FROM SETSEC.
;
; 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 ALLOW THE HEAD TO UNLOAD.
;
	IN   TRACK	;GET PRESENT TRACK
	OUT  DDATA	;AND TELL 1771 ABOUT IT.
	MVI  A,14H+STPRAT+HLAB ;GET THE STEP RATE.
	OUT  DCOM	;SEND IT TO FLOPPY CONTROLLER.
	IN   WAIT	;WAIT FOR INTRQ.
HDLDY:	MVI  A,4	;SET BIT TO LOAD HEAD.
	RET		;RETURN FROM HDLD.
HDLD1:	IN   DSTAT	;READ 1771 STATUS.
	ANI  20H	;LOOK AT HL BIT.
	JZ   HDLDY	;LOAD IF NOT LOADED.
	XRA  A		;OTHERWISE, A=0.
	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:	STA  ERCNT	;STORE IN ERROR CTR.
	LHLD DMAADD	;GET STARTING ADR.
	MVI  A,0D0H	;CAUSE INTERRUPT.
	OUT  DCOM
	XTHL		;SOME DELAY.
	XTHL

	IF  INTRP	;IF INTERRUPTS ALLOWED,
	DI		;DISABLE THEM HERE.
	ENDIF

READ3:	LDA  SECT	;GET SECTOR NUMBER.
	OUT  SECTP	;SET SECTOR INTO 1771.
	CALL HDLD	;GET HEAD-LOAD BIT?
	ADI  88H	;ADD CODE FOR READ SECT.
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.

	IF  INTRP	;IF INTERRUPTS ALLOWED,
	EI		;ALLOW AGAIN HERE.
	ENDIF

	ANI  9DH	;LOOK AT ERROR BITS.
	RZ		;RETURN IF NONE.
	CALL ERCHK	;CHECK FOR SEEK ERROR.
	LXI  H,RECNT	;GET RD ERR COUNT ADDR.
	INR  M		;ONE MORE ERROR.
	LDA  ERCNT	;GET ERROR COUNT.
	DCR  A		;DECREMENT COUNT.
	JNZ  RRETRY	;TRY TO READ AGAIN.
	LXI  H,RDMSG	;PRINT "READ ".
ERMSG:	CALL PMSG	;PRINT ORIGIN MESSAGE.
ERMSG1:
	IF NOT VIDEO	;NEED MORE ROOM?
	MOV  A,D	;GET ERROR BITS.
	ANI  80H	;IF BIT 7 HIGH,
	LXI  H,NRMSG	;"NOT READY".
	CNZ PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  10H	;IF BIT 4 IS HIGH,
	LXI  H,RNMSG	;PRINT "RECORD NOT FOUND"
	CNZ PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  8H		;IF BIT 3 IS HIGH,
	LXI  H,CRCMSG	;PRINT "CRC ERROR".
	CNZ PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  4H		;IF BIT 2 IS HIGH,
	LXI  H,LDMSG	;PRINT "LOST DATA".
	CNZ PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  1		;IF BIT 1 IS HIGH,
	LXI  H,BSYMSG	;PRINT "BUSY".
	CNZ  PMSG
	ENDIF

	LXI  H,ERRMSG	;PRINT "ERROR."
	CALL PMSG
	MVI  A,1	;SET FOR PERM ERR MSG.
	ORA  A		;SET FLAGS.
	RET
;
; ERCHK - CHECK FOR RECORD NOT FOUND ERROR.
;
ERCHK:	MOV  D,A	;SAVE ERROR BITS IN D.
	ANI  10H	;IF RECORD NOT FOUND,
	JNZ  CHKSK	;DO A CHECK ON SEEK.
	MOV  A,D	;OTHERWISE RESTORE BITS
	ORA  A		;SET FLAGS,
	RET		;AND RETURN.
;CHECK FOR SEEK TO CORRECT TRACK,
;AND CHANGE IF NECESSARY.
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.
	MOV  B,A	;SAVE IN REGISTER B.
CHKS2:	IN   WAIT	;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.
	ORA  A		;SET FLAGS.
	JZ   CHKS4	;READ ADR OK IF 0.
	CALL HOME	;OTHERWISE, HOME FIRST.
	JMP  CHKS5
CHKS4:	MOV  A,B	;UPDATE TRACK REGISTER.
	OUT  TRACK
CHKS5:	LDA  TRK	;GET REQUIRED TRACK NO.
	CALL SEEK	;MOVE THE HEAD TO IT.
	MOV  A,D	;GET ERROR BITS.
	ORA  A		;SET FLAGS.
	RET		;RETURN FROM ERCHK.
;
; WRITE THE SECTOR AT SECT, ON THE PRESENT TRACK.
; USE STARTING ADDRESS AT DMAADD.
;
WRITE:	MVI  A,RTCNT	;GET RETRY COUNT.
WRETRY:	STA  ERCNT	;STORE IN ERROR COUNTER.
	LHLD DMAADD	;GET STARTING ADR.
	MVI  A,0D0H	;STATUS INTERUPT FOR 1771.
	OUT  DCOM	;COMMAND 1771.
	XTHL		;WAIT FOR STATUS.
	XTHL		;CHANGE IT BACK.

	IF  INTRP	;IF INTERRPUTS ALLOWED,
	DI		;DISABLE THEM HERE.
	ENDIF

WRITE3:	LDA  SECT	;GET SECTOR NUMBER.
	OUT  SECTP	;SET THE SECTOR INTO 1771.
	CALL HDLD	;GET HEAD LOAD BIT?
	ADI  0A8H	;ADD CODE FOR WRITE.
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.

	IF  INTRP	;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.
	LXI  H,WECNT	;GET ADR OF WRITE ERR CTR.
	INR  M		;ONE MORE WRITE ERROR.
	LDA  ERCNT	;GET ERROR COUNT.
	DCR  A		;DECREMENT COUNT.
	JNZ  WRETRY	;TRY TO WRITE AGAIN.
	LXI  H,WTMSG	;PRINT "WRITE ".

	IF NOT VIDEO	;NEED MORE ROOM?
	CALL PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  40H	;LOOK AT BIT 6.
	LXI  H,WPMSG	;PRINT "PROTECT ".
	CNZ  PMSG
	MOV  A,D	;GET ERROR BITS.
	ANI  20H	;LOOK AT BIT 5.
	LXI  H,WFMSG	;PRINT "FAULT ".
	CNZ  PMSG
	JMP  ERMSG1	;DO COMMON MESSAGES.
	ENDIF

	IF   VIDEO	;WE NEED A RETURN.
	JMP  ERMSG
	ENDIF

;
; PRINT THE MESSAGE AT H&L UNTIL A ZERO.
;
PMSG:    MOV  A,M	;GET A CHARACTER.
         ORA  A		;IF IT'S ZERO,
         RZ		;RETURN.
         MOV  C,A	;OTHERWISE,
         CALL CONOT	;PRINT IT.
         INX  H		;INCREMENT H&L,
         JMP  PMSG	;AND GET ANOTHER.
;
; CBIOS MESSAGES
;

	IF NOT VIDEO	;NEED MORE ROOM?
NRMSG:	DB   'NOT READY ',0
RNMSG:	DB   'RECORD NOT FOUND ',0
CRCMSG:	DB   'CRC ',0
LDMSG:	DB   'LOST DATA ',0
BSYMSG:	DB   'BUSY ',0
WPMSG:	DB   'PROTECT ',0
WFMSG:	DB   'FAULT ',0
	ENDIF

BTMSG:	DB   'BOOT '
ERRMSG:	DB   'ERROR.',0
RDMSG:	DB   0DH,0AH,'READ ',0
WTMSG:	DB   0DH,0AH,'WRITE ',0
SKMSG:	DB   0DH,0AH,'SEEK ',0
MNTMSG:	DB   0DH,0AH,'MOUNT ',0
SMSG:	DB   0DH,0AH,'TARBELL '
	DB   MSIZE/10+'0',MSIZE MOD 10 + '0'
	DB   'K CPM V1.4 OF 7-20-79'
	DB   0DH,0AH

	IF  STD		;IF STANDARD I/O,
	DB   'STANDARD '
	ENDIF

	IF  MSIO2	;IF MITS 2SIO,
	DB   '2SIO '
	ENDIF

	IF  ISIO2	;IF IMSAI SIO-2,
	DB  'SIO-2 '
	ENDIF

	IF  TUART	;IF TUART,
	DB  'TUART '
	ENDIF

	IF  SOLOS	;IF PROC TECH SOLOS,
	DB  'SOLOS '
	ENDIF

	IF   VDM	;IF PROC TECH VDM,
	DB   'VDM '
	ENDIF

	IF   FLASH	;IF VG FLASHWRITER,
	DB   'FLASHWRITER '
	ENDIF

	IF   VB1	;IF SSM VB1-B,
	DB   'VB1 '
	ENDIF

	IF  DUBSID	;IF DOUBLE-SIDED,
	DB   'DOUBLE-SIDED '
	ENDIF

	IF  DUAL	;IF DUAL DRIVE,
	DB  'DUAL '
	ENDIF

	IF   PERSCI	;IF PERSCI DRIVE,
	DB   'PERSCI '
	ENDIF

	DB  'VERSION.'
	DB   0DH,0AH,'HOW MANY DISKS? ',0
;
; 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
	ENDIF

LTBSY:	IN   LSTAT	;READ LISTER STATUS.
	ANI  LRBIT	;LOOK AT READY BIT.

	IF   RDYLO	;IF READY WHEN LOW,
	JNZ  LTBSY	;LOOP TILL LOW.
	ENDIF

	IF   RDYHI	;IF READY WHEN HIGH,
	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

;
; PUNCH PAPER TAPE.
;
PUNCH:
	RET		;RETURN FROM PUNCH.
;
;  NORMALLY USED TO READ PAPER TAPE.
;
READER:
	RET		;RETURN FROM READER.

;NOTE:  AS THERE ARE ONLY NINE 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 + 047F (HEX).
;THIS WILL NORMALLY BE XE7F (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   1		;DISK NUMBER (TO CP/M).

;
; ERROR COUNTS.  THESE LOCATIONS KEEP TRACK OF THE
; NUMBER OF ERRRS THAT OCCUR DURING READ, WRITE,
; OR SEEK OPERATIONS.  THEY ARE INITIALIZED ONLY
; WHEN A COLD-START IS PERFORMED BY THE BOOOTSTRAP.
;
RECNT:	DS   1		;READ ERROR COUNT.
WECNT:	DS   1		;WRITE ERROR COUNT.
SECNT:	DS   1		;SEEK ERROR COUNT.
; SPECIAL FLAGS.
CONOTF:	DS   1		;NO-PRINT FLAG (WHEN FF).
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

ENDZ:			;END OF ZEROED AREA.
;
; VDM SCRATCH AREA.
;

	IF   VIDEO	;IF VIDEO BOARD IN,
VDMP:	DS   2		;VIDEO CURSOR POSITION.
	ENDIF

NODSKS:	DS   1		;NUMBER OF DISKS.
ERCNT:	DS   1		;ERROR COUNT FOR RETRIES.
SERCNT:	DS   1		;SEEK RETRY COUNTER.
TEMP:	DS   1		;TEMPORARY STORAGE.
LATCH:	DS   1		;NEW CODE FOR LATCH.
CLATCH:	DS   1		;CURRENT CODE IN LATCH.
         END
