title 'Character I/O handler for z80 chip based system'

; Character I/O for the Modular CP/M 3 BIOS

TRUE	EQU	-1
FALSE	EQU	NOT TRUE

TEST	EQU	TRUE

LOADER	EQU	FALSE

	; limitations:

	;		baud rates 19200,7200,3600,1800 and 134
	;			are approximations.

	;		9600 is the maximum baud rate that is likely
	;			to work.

	;		baud rates 50, 75, and 110 are not supported


	public	?cinit,?ci,?co,?cist,?cost
	public	@ctbl
	PUBLIC	T20MS

	EXTRN	@DATE,@SEC,@HOUR
	EXTRN	TASINI,TASABF,TAB,TABMER
	EXTRN	TASMER
	EXTRN	IVCTC3

	maclib Z80	; define Z80 op codes
	maclib ports	; define port addresses
	maclib modebaud	; define mode bits and baud equates

max$devices	equ 6

;**************************************************************
; VEREINBARUNGEN
;**************************************************************

TASTP	EQU	0FF80H
TASTPE	EQU	0FFBAH

;****************** E/A - VEREINBARUNGEN **********************

CTC	EQU	08H
CTCK3	EQU	CTC+3		; CTC-KANAL FUER UHR

SIO	EQU	0CH		; SIO-BAUSTEIN
LSTDAT	EQU	SIO+1		; LST-KANAL DER SIO
LSTCRT	EQU	SIO+3
LSTCTC	EQU	CTC+1

CRTPAR	EQU	18H		; CRT-CONTROLLER-PARAMETERREG.
CRTBEF	EQU	19H		; CRT-CONTROLLER-STEUERREGISTER
CRTBWS	EQU	34H		; BWS-ANFANGSADRESSENREGISTER

	cseg

?cinit:

IF	NOT TEST
	mov a,c ! cpi max$devices ! jz cent$init ; init parallel printer
	rnc				; invalid device
	mov l,c ! mvi h,0		; make 16 bits from device number
	push h				; save device in stack
	dad h ! dad h ! dad h		; *8 
	lxi d,@ctbl+7 ! dad d ! mov l,m	; get baud rate
	mov a,l ! cpi baud$600		; see if baud > 300
	mvi a,44h ! jnc hi$speed	; if >= 600, use *16 mode
	mvi a,0C4h			;    else, use *64 mode
hi$speed:
	sta sio$reg$4	
	mvi h,0 ! lxi d,speed$table ! dad d	; point to counter entry
	mov a,m ! sta speed		; get and save ctc count
	pop h				; recover 
	lxi d,data$ports ! dad d	; point at SIO port address
	mov a,m ! inr a ! sta sio$port	; get and save port
	lxi d,baud$ports-data$ports ! dad d	; offset to baud rate port
	mov a,m ! sta ctc$port		; get and save
	lxi h,serial$init$tbl
	jmp stream$out

cent$init:
	lxi h,pio$init$tbl

stream$out:
	mov a,m ! ora a ! rz
	mov b,a ! inx h ! mov c,m ! inx h
	outir
	jmp stream$out
ENDIF

	CALL	TASINI		; TASTATUR INIT.

IF	LOADER

	LXI	H,0F800H	; UND BILDSCHIRM
	LXI	B,800H
BSL1:	MVI	M,' '
	INX	H
	DCX	B
	MOV	A,B
	ORA	C
	JRNZ	BSL1

;**************** INITIALISIEREN CRT-CONTROLLER ***************

	mvi	a,7eh		; fuer 2.zeichensatz
;	MVI	A,3EH
	OUT	CRTBWS
	XRA	A		; STOP DISPLAY / RESET
	OUT	CRTBEF
	MVI	A,4FH		; PARAMETER 1
	OUT	CRTPAR
	MVI	A,58H		; PARAMETER 2
	OUT	CRTPAR
	MVI	A,6BH		; PARAMETER 3
	OUT	CRTPAR
	MVI	A,6DH		; PARAMETER 4
	OUT	CRTPAR
	MVI	A,20H		; START DISPLAY
	OUT	CRTBEF

ENDIF

	LXI	H,IVCTC3
	MOV	A,H		; INITIALISIERUNG INTERRUPT
	STAI
	IM2
	MVI	C,LSTCTC	; DRUCKERINITIALISIERUNG
	MVI	B,2
	LXI	H,LSTTAB
	OUTIR
	MVI	C,LSTCRT
	MVI	B,9
	OUTIR

;______________________________________________________________
; INITIALISIEREN DES UHRINTERRUPTES / ABFRAGE DER TASTATUR
;______________________________________________________________

	XRA	A
	STA	T20MS		; UHRPUFFER FREIMACHEN
	LXI	H,TASTP		; FREIMACHEN TASTATURPUFFER
	MVI	B,TASTPE-TASTP+1
SC1:	MVI	M,0
	INX	H
	DJNZ	SC1
	LXI	H,TIMINT	; EINSTELLEN INT-TABELLE KANAL 2
	SHLD	IVCTC3
	LXI	H,IVCTC3
	MOV	A,L		; INT-VEKTOR CTC LADEN
	OUT	CTC
	MVI	A,0A5H		; ZEITGEBERMODUS KANAL 2
	OUT	CTCK3
	mvi	a,240		; Zeitkonstante 25 ms
;	MVI	A,0C0H		; ZEITKONSTANTE 20 ms
	OUT	CTCK3
	RET

;**************************************************************
; INITIALISIERUNGSTABELLE FUER DRUCKER
;**************************************************************

LSTTAB:	DB	5		; CTC
	DB	1
	DB	0		; SIO
	DB	19H		; REG. 1
	DB	0		; KEIN INTERRUPT
	DB	3		; REG. 3
	DB	0C1H		; 8 BIT EMPFANGEN
	DB	4		; REG. 4
	DB	45H		; VT=16, 1 STOPBIT, UNGER. PAR.
	DB	5		; REG. 5
	DB	68H		; 8 BIT SENDEN


?ci:		; character input

IF	NOT TEST
	mov a,b ! cpi 6 ! jnc null$input ; can't read from centronics
ci1:
	call ?cist ! jz ci1		; wait for character ready
	dcr c ! inp a			; get data
	ani 7Fh				; mask parity
	ret
ENDIF

IF	TEST
	MOV A,B ! CPI 1 ! JNC NULL$INPUT
;**************************************************************
; CONSOLEINGABE
;**************************************************************

CONIN:
	LHLD	TABMER		; pruefen ob Mehrzeichencode
	MOV	A,H
	ORA	L
	JRNZ	NEXZEI		; ja, dann erst alle Zeichen
CONIN1:	LDA	TASMER
	ORA	A
	JRZ	CONIN1
	LXI	D,TASTP
	LDAX	D
	LXI	H,TASTP+1
	LXI	B,TASTPE-TASTP
	DI
	LDIR
	PUSH	PSW
	XRA	A
	STAX	D
	LDA	TASTP
	STA	TASMER
	POP	PSW
	EI
	LXI	H,TAB		; Umkodierungstabelle Tastatur
	MOV	B,A
SUCH:	MOV	A,M
	ORA	A
	JRZ	NOTFOU
	INX	H
	CMP	B
	JRZ	SUCHE
WEIT:	INX	H
	MOV	A,M
	ORA	A
	JRNZ	WEIT
	INX	H
	JR	SUCH
SUCHE:
NEXZEI:	MOV	B,M
	INX	H
	MOV	A,M
	ORA	A
	JRNZ	MEHRZE
NOTFOU:	LXI	H,0
MEHRZE:	SHLD	TABMER
	MOV	A,B
	CPI	80H
	RC
	XRA	A
	RET

ENDIF

null$input:
	mvi a,1Ah			; return a ctl-Z for no device
	ret

?cist:		; character input status

IF	NOT	TEST
	mov a,b ! cpi 6 ! jnc null$status ; can't read from centronics
	mov l,b ! mvi h,0		; make device number 16 bits
	lxi d,data$ports ! dad d	; make pointer to port address
	mov c,m ! inr c			; get SIO status port
	inp a				; read from status port
	ani 1			; isolate RxRdy
	rz				; return with zero
	ori 0FFh
	ret
ENDIF
IF	TEST
	MOV A,B ! CPI 1 ! JNC NULL$STATUS
;**************************************************************
; CONSOLSTATUS
;**************************************************************


CONST:
	LHLD	TABMER
	MOV	A,H
	ORA	L
	JRNZ	CONST1
	LDA	TASMER
	ORA	A
	RZ			; KEINE TASTE oder Joystick
CONST1:	MVI	A,0FFH		; TASTE DA
	RET


ENDIF

null$status:
	xra a ! ret

?co:		; character output

IF	NOT TEST
	mov a,b ! cpi 6 ! jz centronics$out
	jnc null$output
	mov a,c ! push psw		; save character from <C>
	push b				; save device number
co$spin:
	call ?cost ! jz co$spin		; wait for TxEmpty
	pop h ! mov l,h ! mvi h,0	; get device number in <HL>
	lxi d,data$ports ! dad d	; make address of port address
	mov c,m				; get port address
	pop psw ! outp a		; send data
ENDIF

IF	TEST
	MOV A,B ! CPI 1 ! JZ LIST
	CPI 0 ! JNZ NULL$OUTPUT
;**************************************************************
; CONSOLAUSGABE
;**************************************************************

CONOUT:	PUSH	PSW
	LXI	H,00FFH		; CLEAR CURSOR
	CALL	SETCU3
	LHLD	CURS
	LDA	ESCMER		; WAR ESC-FOLGE ?
	ORA	A
	JRZ	NOESC		; -> NEIN
	DCR	A
	STA	ESCMER
	JRZ	ESC2		; -> 2.BYTE DER ESC-FOLGE
	LXI	H,0F800H	; BEHANDLUNG 1.BYTE DER ESC-FO.
	MOV	A,C		; ALS ZEILENANGABE
	ANI	7FH
	ORA	A
	JRZ	SETCUR
	CPI	18H
	JRNC	SETCUR
	MOV	B,A
	LXI	D,0050H
ESC12:	DAD	D
	DJNZ	ESC12
	JR	SETCUR
ESC2:	MOV	A,C		; BEHANDLUNG 2.BYTE DER ESC-FO.
	ANI	7FH		; ALS SPALTENANGABE
	CPI	50H
	LXI	D,004FH
	JRNC	ESC22
ESC21:	MOV	E,A
ESC22:	DAD	D
	JR	SETCUR

NOESC:
	MOV	A,C		; AUSWERTUNG STEUERZEICHEN
	cpi	0c0h
	jrc	nospez
	cpi	0e0h
	jrnc	nospez
	ani	1fh
	jr	zeiums1
nospez:
	CPI	7FH
	JRNC	CRTCRTL
	CPI	20H
	JRC	CRTCRTL
CRTZEI:
	mov	b,a		; Zeichen retten
	lda	zeisat
	ora	a
	jrz	noums
	mov	a,b
	cpi	7eh
	jrz	zeiums
	ani	0dfh
	cpi	5bh
	jrc	noums
	cpi	5eh
	jrnc	noums
zeiums:
	ani	1fh
	bit	5,b
	jrnz	zeiums1
	sui	3
zeiums1:
	mov	b,a
noums:
eizeium:
	mvi	c,0ffh
	mvi	a,0ffh
	sta	eizeium+1
	xra	a
	ora	b
	ana	c
	MOV	M,A		; ZEICHEN AUF BILDSCHIRM
	CALL	CRTCRI
;
SETCUR:
	SHLD	CURS
	LDA	CURMER
	ORA	A
	JRNZ	NOSET
	CALL	SETCUS
NOSET:	POP	PSW
	RET
;
SETCUS:
	XRA	A
	LXI	D,0F800H
	DSBC	DE		; SBC HL,DE
	MVI	C,0
	LXI	D,50H
SETCU1:
	DSBC	DE		; SBC HL,DE
	JRC	SETCU2
	INR	A
	JR	SETCU1
SETCU2:
	DAD	D
	MOV	H,A
SETCU3:
	MVI	A,80H		; CURSOR SETZEN
	OUT	CRTBEF
	MOV	A,L		; SPALTE
	OUT	CRTPAR
	MOV	A,H		; ZEILE
	OUT	CRTPAR
	RET

CRTCRTL:
	LXI	D,SETCUR
	PUSH	D
	CPI	7FH
	JZ	CRTDCH
	CPI	0DH		; STEUERZEICHEN AUSWERTEN
	JZ	CRTCR
	CPI	0AH
	JZ	CRTLF
	CPI	15H
	JZ	CRTCRI
	CPI	08H
	JZ	CRTCLE
	CPI	0CH
	JZ	CRTFF
	CPI	01H
	JZ	CRTHOM
	CPI	14H
	JZ	CRTDRB
	CPI	18H
	JZ	CRTDZE
	CPI	16H
	JZ	CRTDCZ
	CPI	1AH
	JZ	CRTCHO
	CPI	1BH
	JZ	CRTESC
	CPI	82H
	JZ	CURSON
	CPI	83H
	JZ	CURSOF
	CPI	0EH
	JZ	ascii
	CPI	0FH
	JZ	german
	cpi	8eh		; Zeichensatzumschaltung
	jz	eizeis
	RET
CRTDCH:	MVI	M,' '		; DEL
CRTCLE:	DCX	H		; BACKSPACE
	MOV	A,H
	CPI	0F7H
	RNZ
	INX	H
	RET
CRTFF:	LXI	H,0F800H	; FORM FEED
	MVI	M,' '
	LXI	D,0F801H
	LXI	B,077FH
	LDIR
CRTHOM:	LXI	H,0F800H	; HOME POSITION
	RET
CRTLF:	LXI	D,0050H		; LINE FEED
	DAD	D
	MOV	A,H
	CPI	0FFH
	RNZ
	MOV	A,L
	CPI	80H
	RC
	DSBC	DE		; SBC HL,DE
	JR	CRTPO1
CRTCRI:	INX	H		; NAECHSTE KURSORPOSITION
	MOV	A,H		; WENN NOETIG BILD ROLLEN
	CPI	0FFH
	RNZ
	MOV	A,L
	CPI	80H
	RC
	LXI	H,0FF30H
CRTPO1:	PUSH	H
	LXI	B,0730H
	LXI	H,0F850H
	LXI	D,0F800H
	LDIR
	MVI	A,' '
	MVI	B,50H
CRTPO2:	DCX	H
	MOV	M,A
	DJNZ	CRTPO2
	POP	H
	RET
CRTCR:	MOV	B,H		; CARRIAGE RETURN
	MOV	C,L
	LXI	H,0FF80H
	LXI	D,0050H
	ORA	A
CRTCR1:	DSBC	DE		; SBC HL,DE
	PUSH	H
	DSBC	BC		; SBC HL,BC
	POP	H
	RZ
	JRNC	CRTCR1
	RET

CRTESC:	MVI	A,02H		; BEHANDLUNG ESC-FOLGE
	STA	ESCMER		; BEGINNEN
	RET
CURSON:	XRA	A
CURSOF:	STA	CURMER
	RET
ascii:
	XRA	A
german:
	sta	zeisat
	ret
eizeis:	mvi	a,1fh
	sta	eizeium+1
	ret
CRTDRB:	XCHG			; LOESCHEN AB CURSOR BIS
	LXI	H,0FF7FH	; BILDENDE
	DSBC	DE		; SBC HL,DE
CRTDR0:	MOV	B,H
	MOV	C,L
CRTDR1:	MOV	H,D
	MOV	L,E
	INX	D
	MVI	M,' '
	LDIR
CRTDR2:	LHLD	CURS
	RET

CRTDZE:	CALL	CRTCR		; LOESCHEN KURSORZEILE UND
				; SETZEN KURSOR AN ZEILEN-
				; ANFANG
CRTDCZ:	PUSH	H
	CALL	CRTCR
	LXI	D,0050H		; LOESCHEN AB KURSORPOSITION
	DAD	D		; BIS ZEILENENDE
	POP	D
	ORA	A
	DSBC	DE
	JRZ	CRTDR2
	JR	CRTDR0
CRTCHO:	LXI	D,0050H		; KURSOR EINE ZEILE HOCH
	DSBC	DE
	MVI	A,0F7H
	CMP	H
	RC
	LHLD	CURS
	RET

CURS:	DW	0F800H		; KURSORMERKER
ESCMER:	DB	0		; ESC - MERKER
CURMER:	DB	0		; Kursordarst.-merker
zeisat:	db	0

ENDIF

null$output:
	ret

IF	NOT TEST
centronics$out:
	in p$centstat ! ani 20h ! jnz centronics$out
	mov a,c ! out p$centdata	; give printer data
	in p$centstat ! ori 1 ! out p$centstat	; set strobe
	ani 7Eh ! out p$centstat		; clear strobe
	ret
ENDIF

?cost:		; character output status
IF	NOT TEST
	mov a,b ! cpi 6 ! jz cent$stat
	jnc null$status
	mov l,b ! mvi h,0
	lxi d,data$ports ! dad d
	mov c,m ! inr c 
	inp a				; get input status
	ani 4 ! rz			; test transmitter empty

ENDIF				; IMMER READY SIMULIEREN

	ori 0FFh ! ret			; return true if ready

IF	NOT TEST
cent$stat:
	in p$centstat ! cma
	ani 20h ! rz
	ori 0FFh ! ret	

baud$ports:		; CTC ports by physical device number
	db	p$baud$con1,p$baud$lpt1,p$baud$con2,p$baud$con34
	db	p$baud$con34,p$baud$lpt2

data$ports:		; serial base ports by physical device number
	db	p$crt$data,p$lpt$data,p$con2data,p$con3data
	db	p$con4data,p$lpt2data
ENDIF

@CTBL:	DB 'TASTE '
	DB MB$INPUT
	DB BAUD$NONE
	DB 'BAB   '
	DB MB$INPUT
	DB BAUD$NONE
	DB 0		; TABENDE

IF	NOT TEST
@ctbl	db 'CRT   '	; device 0, CRT port 0
	db mb$in$out+mb$serial+mb$softbaud
	db baud$9600
	db 'LPT   '	; device 1, LPT port 0
	db mb$in$out+mb$serial+mb$softbaud+mb$xonxoff
	db baud$9600
	db 'CRT1  '	; device 2, CRT port 1
	db mb$in$out+mb$serial+mb$softbaud
	db baud$9600
	db 'CRT2  '	; device 3, CRT port 2
	db mb$in$out+mb$serial+mb$softbaud
	db baud$9600
	db 'CRT3  '	; device 4, CRT port 3
	db mb$in$out+mb$serial+mb$softbaud
	db baud$9600
	db 'VAX   '	; device 5, LPT port 1 used for VAX interface
	db mb$in$out+mb$serial+mb$softbaud
	db baud$9600
	db 'CEN   '	; device 6, Centronics parallel printer
	db mb$output
	db baud$none
	db 0			; table terminator


speed$table	db	0,255,255,255,233,208,104,208,104,69,52,35,26,17,13,7

serial$init$tbl
		db 2		; two bytes to CTC
ctc$port	ds 1		; port address of CTC
		db 47h		; CTC mode byte
speed		ds 1		; baud multiplier
		db 7		; 7 bytes to SIO
sio$port	ds 1		; port address of SIO
		db 18h,3,0E1h,4
sio$reg$4	ds 1
		db 5,0EAh 	
		db 0		; terminator

pio$init$tbl	db	2,p$zpio$2b,0Fh,07h
		db	3,p$zpio$2a,0CFh,0F8h,07h
		db 0
ENDIF


;**************************************************************
; INTERRUPTROUTINE UHRZEIT
;**************************************************************

TIMINT:	SSPD	RETTSP
	LXI	SP,TIMSTE
	PUSH	PSW
	PUSH	H
	PUSH	B

;______________________________________________________________
; 20-ms ZAEHLER
;______________________________________________________________

;	lxi	h,t20kz
;	dcr	m
;	jnz	nokorr
;	mvi	m,235
;	inx	h
;	inr	m
;nokorr:
	LXI	H,T20MS
	INR	M
;	MVI	A,49	; 50		; 50 * 20 ms
;	mvi	a,50
	mvi	a,40		; 40 * 25 ms
	SUB	M
;	jnc	timend
	JNZ	TIMEND
;	xra	a
	MOV	M,A

;______________________________________________________________
; SEKUNDEN
;______________________________________________________________

	LXI	H,@SEC
	MOV	A,M
	INR	A
	DAA
	MOV	M,A
	SUI	60H
	JRNZ	TIMOUT
	MOV	M,A

;______________________________________________________________
; MINUTEN
;______________________________________________________________

	DCX	H
	MOV	A,M
	INR	A
	DAA
	MOV	M,A

	ani	3
	jnz	nokorr
	mvi	a,0ffh		; aller 4 Minuten 1* mehr
	sta	t20ms
nokorr:
	mov	a,m
	SUI	60H
	JRNZ	TIMOUT
	MOV	M,A

;______________________________________________________________
; STUNDEN
;______________________________________________________________

	DCX	H
	MOV	A,M
	INR	A
	DAA
	MOV	M,A
	SUI	24H
	JRNZ	TIMOUT
	MOV	M,A

;______________________________________________________________
; DATUM
;______________________________________________________________

	LHLD	@DATE
	INX	H
	SHLD	@DATE

;______________________________________________________________
; ZEIT AUSGEBEN AUF BSE
;______________________________________________________________

TIMOUT:	PUSH	D
	LXI	D,0FFBdH
	LXI	H,@HOUR
	MVI	B,3
NEXZEO:	CALL	BYTOUT
	MVI	A,':'
	STAX	D
	INX	D
	INX	H
	DJNZ	NEXZEO
	DCX	D
	MVI	A,' '
	STAX	D
	POP	D
TIMEND:	CALL	TASABF

;______________________________________________________________
; ENDE TASTATURABFRAGE / UHREINSTELLUNG
;______________________________________________________________

	POP	B
	POP	H
	POP	PSW
	LSPD	RETTSP
	EI
	RETI

;______________________________________________________________

BYTOUT:	MOV	A,M
	RRC
	RRC
	RRC
	RRC
	CALL	BYTOU1
	MOV	A,M
BYTOU1:	ANI	0FH
	ADI	'0'
	STAX	D
	INX	D
	RET

;______________________________________________________________

TIMST:	DS	20H		; STACK FUER TIMINT
TIMSTE:
RETTSP:	DW	0		; STACKPOINTER MERKEN

;______________________________________________________________

t20kz:	db	235
T20MS:	DB	0

;**************************************************************

LIST:	CALL	LISTST
	ORA	A
	JZ	LIST		; LIST NOT READY
	MOV	A,C
	ANI	7FH		; MASKIEREN ALS 7 BIT AUSGABE
	OUT	LSTDAT
	RET

;**************************************************************
; DRUCKERSTATUS
;**************************************************************

LISTST:	MVI	A,10H
	OUT	LSTCRT
	IN	LSTCRT
	CMA
	BIT	5,A		; CTS FREI ?
	JRNZ	LISTS1		; NEIN
	BIT	2,A		; SENDER FREI ?
LISTS1:	MVI	A,0
	RNZ			; LIST NOT READY
	DCR	A
	RET			; LIST READY

	END
