title 'Character I/O handler for z80 chip based system'

; Character I/O for the Modular CP/M 3 BIOS
; MARC Version 1.0
; 14.12.88

	maclib Z80	; define Z80 op codes
	maclib ports	; define port addresses
	maclib modebaud	; define mode bits and baud equates


TEST	EQU	TRUE


	; 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 staon,staoff
	public t100ms

	extrn babini,babout
	extrn zeisat
	extrn csfabr,csrafr,csctrr,stfabr

	extrn @date,@sec,@hour,@cbnk
	extrn ?bnksl
	extrn tasini,tasabf,tab
	extrn tasmer
	extrn ivctc3

	extrn motoff,motcot

max$devices	equ 6

;**************************************************************
; Vereinbarungen
;**************************************************************

tastp	equ 0ff80h
tastpe	equ 0ffbah

;**************************************************************
; Programm
;**************************************************************

	dseg
;	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 Initialisieren
	call babini		; Initialisieren CRT-Steuerung
IF NOT LOADER
	di
	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
;______________________________________________________________

	mvi	a,10		; 10 * 100 ms
	STA	t100ms		; UHRPUFFER FREIMACHEN

	call	staon

	LXI	H,TASTP		; FREIMACHEN TASTATURPUFFER
	MVI	B,TASTPE-TASTP+1
SC1:	MVI	M,0
	INX	H
	DJNZ	SC1

	call	staoff

	LXI	H,TIMINT	; EINSTELLEN INT-TABELLE KANAL 2
	SHLD	IVCTC3
	LXI	H,IVCTC3
	MOV	A,L		; INT-VEKTOR CTC LADEN
	OUT	CTC1

	mvi	a,25h		; Kanal 2 Erzeugung Takt 1 ms
	out	ctc1k2		; fuer Kanal 3
	mvi	a,100		; fuer 10,250 MHz-Quarz / 96
	out	ctc1k2
	mvi	a,0cdh		; Kanal 3 Zaehler Interrupt
	out	ctc1k3		; aller 100 ms
	mvi	a,10
	out	ctc1k3

	ei
ENDIF

	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
	db	0e8h

	cseg

?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:	CALL	CONST
;	ORA	A
;	JRZ	CONIN
;	IN	TASTE
;	RET

CONIN:
	sspd consp		; Stackrettung
	lxi sp,constk
	LHLD	TABMER
	MOV	A,H
	ORA	L
	JRNZ	NEXZEI
CONIN1:	LDA	TASMER
	ORA	A
	JRZ	CONIN1
	LXI	D,TASTP
	LXI	H,TASTP+1
	LXI	B,TASTPE-TASTP
	di

	call	staon

	ldax	d
	LDIR
	PUSH	PSW
	LDA	TASTP
	STA	TASMER
	XRA	A
	stax	d

	call	staoff

	pop	psw
	ei
	LXI	H,TAB
	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
	jrc	econin
	XRA	A
econin:
	lspd	consp
	ret

TABMER:	DW	0

	ds 32
constk:
consp:	ds 2

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:	IN	TASTST
;	CMA
;	BIT	3,A
;	MVI	A,0
;	RZ
;	MVI	A,0FFH
;	RET

CONST:
	LHLD	TABMER
	MOV	A,H
	ORA	L
	JRNZ	CONST1
	LDA	TASMER
	ORA	A
	RZ			; KEINE TASTE
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
;**************************************************************

	jmp	babout

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

; 100-ms ZAEHLER

	LXI H,t100ms ! dcr m
	JNZ TIMEND
	mvi m,10

; SEKUNDEN

	lda motcot ! ora a
	jrz secun
	dcr a ! sta motcot ! cz motoff
secun:
	LXI H,@SEC
	MOV A,M ! ora a ! 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 ! 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:
	call staon ! PUSH D ! LXI D,0ffbdh ! LXI H,@HOUR
	MVI B,2
NEXZEO:	CALL BYTOUT
	MVI A,':' ! STAX D ! INX D ! INX H
	DJNZ NEXZEO
	call bytout ! POP D
	jr timen2

timend:
	call staon
TIMEN2:	CALL TASABF

;______________________________________________________________
; ENDE TASTATURABFRAGE / UHREINSTELLUNG
;______________________________________________________________

	call staoff
timen3:
	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

;______________________________________________________________

t100ms:	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


;**************************************************************
; Einschalten Statuszeile
;**************************************************************

staon:
	lda stfabr ! out csfab
	xra a ! out csctr
	ret

;**************************************************************
; Ausschalten Statuszeile
;**************************************************************

staoff:
	lda csfabr ! out csfab
	lda csctrr ! out csctr
	ret

	end
