; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS)
;( TARBELL ELECTRONICS) CVE MOD OF 9-5-81
; 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  64		;MEMORY SIZE IN KBYTES.
INTRP	EQU  FALSE	;TRUE IF INTERRUPTS ALLOWED.

STD	EQU  FALSE	;TRUE IF STANDARD I/O OR SSM IO4.
MSIO2	EQU  FALSE	;TRUE IF MITS 2SIO.
ISIO2	EQU  TRUE	;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  7dh	;CONSOLE STATUS PORT.
	ENDIF
;changed for Z80 emulator
CCOM	EQU  7dh	;CONSOLE COMMAND PORT.
CDATA	EQU  7ch	;CONSOLE DATA PORT.
CONUL	EQU  FALSE	;CONSOLE NULLS?
CNULL	EQU  0		;CONSOLE NULL COUNT.

LSTAT	EQU  7fh	;LIST STATUS PORT.
LCOM	EQU  7fh	;LIST COMMAND PORT.
LDATA	EQU  7eh	;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  1		;RATE 1=6MS, 2=10MS, 3=20MS.
DUAL	EQU  TRUE	;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;NO INIT REQ FOR IO4, MUST BE DONE ON BOARD
	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
;changed for z80 emu so no query for no of disks occurs.
;	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.
	push	de
	lxi	de,128
	dad	de
	pop	de
	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.
	STA  DISKNO	;UPDATE OLD WITH NEW.
	XRA  A		;SET A=0 FOR NO ERRO IND.
	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:
	MOV  A,C	;GET NEW TRACK NUMBER.
	STA  TRK	;UPDATE OLD WITH NEW.
	xra	a	;set z bit
	RET
;
; 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.

; added for z80 emulator
dsk$cmd 	equ 	0D0h 
dsk$sts 	equ 	0D0h 
dsk$drv		equ	0D1h ;drv
dsk$trkL	equ	0D2h ;trkLow
dsk$trkH	equ	0D3h ;trkHigh
dsk$secL	equ	0D4h ;secLow
dsk$secH	equ	0D5h ;secHigh
dsk$dmaL	equ	0D6h ;dmaLow
dsk$dmaH	equ	0D7h ;dmaHigh
dsk$sptL	equ	0D8h ;sptLow
dsk$sptH	equ	0D9h ;sptHigh
dsk$dmaB	equ	0dah ;Disk DMA Bank
dsk$order	equ	0dbh ;track order

cmd$read	equ	1
cmd$write	equ	2

;disk executive

dsk$ex
	lda	DMAADD
	out	dsk$dmaL
	lda	DMAADD+1	
	out	dsk$dmaH
	lda	DISKNO
	out	dsk$drv
	xra	a
	out	dsk$trkH
	out	dsk$secH
	lda	TRK
	out	dsk$trkL
	lda	SECT
	out	dsk$secL
	lda	bt$cmd
	out	dsk$cmd
	in	dsk$sts
	ana	a		;set ccr
	ret

dsk$ok:	
	xra	a		;clear a to indicate OK
	ret

dsk$er:	
	mvi	a,01h		;was FF indicate ERROR
	ora	a
	ret


;
; READ THE SECTOR AT SECT, FROM THE PRESENT TRACK.
; USE STARTING ADDRESS AT DMAADD.
;
READ:	
diskrd:
	mvi	a,cmd$read
	sta	bt$cmd
	call	dsk$ex		;perform operation
	jnz	dsk$er		;exit error
	jmp	dsk$ok		;normal exit

;
; WRITE THE SECTOR AT SECT, ON THE PRESENT TRACK.
; USE STARTING ADDRESS AT DMAADD.
;
WRITE:	
diskwr:	
	mvi	a,cmd$write
	sta	bt$cmd
	call	dsk$ex		;perform operation
	jz	dsk$ok		;normal exit if write ok
	jmp	dsk$er		;exit error
;
; 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,'CVE IO4 '
	DB   MSIZE/10+'0',MSIZE MOD 10 + '0'
	DB   'K CPM V1.4 OF 7-20-79/9-5-81'
	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 	'Z80 Emulator '
;	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  'MULTI DRIVE '
	ENDIF

	IF   PERSCI	;IF PERSCI DRIVE,
	DB   'PERSCI '
	ENDIF

	DB  'VERSION.'
;changed for z80 emulator- always 4 drives. 
	db	0
;	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).
;
bt$cmd	ds	1	;command byte for disk
;
;
; 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
