;TS1.MAC contains the input and output subroutines
; See list of entry points for names.

	PAGE 70
	.Z80
	TITLE TS1

	SUBTTL I/O SUBROUTINES

;CP/M specific constants
	EXTERNAL BDOS	;address of BDOS jump

	ENTRY CHKKEY,INKEY,XONXOF,CRNLFD,MESS,DIMON,DIMOFF
	ENTRY OUTSTR,OUTPUT,OUTCON,DELOUT,CLRTAB

CHKKEY:	;check keyboard for key hit
	LD C,6
	LD E,0FFH
	CALL BDOS
	OR A
	RET

INKEY:	;waits for one input char from the keyboard and returns it in A
	CALL CHKKEY
	JR Z,INKEY
	RET	

XONXOF:	;checks the keyboard if XON/XOFF (Ctrl/S / Ctrl/Q) was hit.
	;On XOFF the subroutine waits for either XON or another XOFF 
	;or for a Ctrl/C in which case it jumps to the BREAK point
	;in the main algorithm.
	EXTERNAL BREAK
	CALL CHKKEY	;check keyboard for key hit
	RET Z
	CP 13H		;Ctrl/S ?
	RET NZ
XOFF:	CALL CHKKEY
	JP Z,XOFF
	CP 03H		;Ctrl/C ?
	JP Z,BREAK
	CP 13H		;2nd Ctrl/S ?
	RET Z
	CP 11H		;Ctrl/Q ?
	JP NZ,XOFF
	RET

CRNLFD:	;outputs a carriage return and line feed
	EXTERNAL CRN,LFD
	LD A,(CRN)
	CALL OUTPUT
	LD A,(LFD)
	CALL OUTPUT
	RET

MESS:	;outputs the error message for char in A on the console
	CALL DIMON
	LD HL,MESSC
	LD (HL),A
	LD DE,MESSG
	LD C,9
	CALL BDOS
	CALL DIMOFF
	CALL XOFF
	CALL CRNLFD
	RET
MESSG:	DB 0DH,0AH
	DB '<*** Error '
MESSC:	DB ' '
	DB ' - Press Ctrl/S,Q,C ***> '
	DB '$'
			
DIMON:	;select reduced screen brightness
	EXTERNAL DIMON$
	LD HL,DIMON$
	CALL SCCOD	;output screen control code sequence
	RET
DIMOFF:	;select normal screen brightness
	EXTERNAL DIMOF$
	LD HL,DIMOF$
	CALL SCCOD
	RET

SCCOD:	;outputs screen control code sequences at HL
	DEVCON	EQU 0	;code for console device
	EXTERNAL OUTCHL
	LD A,(OUTCHL)	;check if console is output device
	CP DEVCON
	RET NZ
	LD A,(HL)	;get no. of bytes
	OR A
	RET Z
	LD B,A
	LD C,6
SCCOD1:	INC HL		;get next byte
	LD A,(HL)
	LD E,A
	PUSH BC
	PUSH HL
	CALL BDOS	;output
	POP HL
	POP BC
	DJNZ SCCOD1	;until done
	RET	

OUTSTR:	;outputs AUXST via the current output channel
	EXTERNAL AUXST,$AUXST
	LD DE,(AUXST)
OUTST1:	LD HL,($AUXST)
	SCF
	CCF
	SBC HL,DE
	JR C,OUTST2
	PUSH DE
	LD A,(DE)
	CALL OUTPUT
	POP DE
	INC DE
	JR OUTST1
OUTST2:	RET

OUTPUT:	;outputs char in A via current output channel
	EXTERNAL OUTCHL,CUA,CLCCNT
	PUSH AF
	LD A,(OUTCHL)
	ADD A,A
	LD E,A
	XOR A
	LD D,A	
	POP AF
	LD HL,OUTBL
	ADD HL,DE
	LD E,(HL)
	INC HL
	LD D,(HL)
	EX DE,HL
	JP (HL)
OUTBL:
	IRP CH,<OUTCON,OUTCON,OUTPUN,OUTLST,OUTDSK>
	DW CH
	ENDM
	
OUTCON:	CP 20H		;output via console
	JR C,OUTCO2
OUTCO1:	CP 7FH
	CALL C,OUTCCH	;is char printable?
	JP OUTCO9
OUTCO2:	LD HL,OUCTBL	;compare control char
	LD BC,OUCJPS-OUCTBL	;with table
	CPIR
	JR Z,OUTCO3
	PUSH AF		;if not found output ^x
	LD A,(CUA)
	CALL OUTCCH
	POP AF
	OR 40H
	CALL OUTCCH
	JP OUTCO9
OUTCO3:	LD HL,OUCJPS	;if found get jump address
	ADD HL,BC	;from table
	ADD HL,BC
	LD E,(HL)
	INC HL
	LD D,(HL)
	EX DE,HL
	JP (HL)		;and jump to code segment
OUCTBL:			;code table
	IRP CH,<09H,0DH,0AH,0CH>
	DB CH
	ENDM
OUCJPS:			;note: address table is inverse
	IRP CH,<OUTCOD,OUTCOA,OUTCOD,OUTCOI>
	DW CH
	ENDM

OUTCOI:			;expand TAB
	LD HL,(TABSTK)
	INC HL
	LD A,(HL)
	CP 0FFH		;end of TAB-expansion stack reached?
	JP Z,OUTCOK
	LD (TABSTK),HL
	LD A,(CLCCNT)
	LD (HL),A
OUTCOJ:	LD A,20H
	CALL OUTCCH
	LD A,(CLCCNT)
	AND 07H
	JR NZ,OUTCOJ
	JP OUTCO9

OUTCOK:	INC HL		;if end of TAB-stack reached
	INC (HL)	;increment counter for non-expanded TABs
	LD A,20H	;and substitute TAB by Blank
	CALL OUTCCH
	JP OUTCO9

TABSTK:	DW TABSTK+2	;Stack for TAB expansion counts
	DW 0		;(depth for 20 TABs should be enough)
	DW 0
	DW 0
	DW 0
	DW 0
	DW 0
	DW 0
	DW 0
	DW 0
	DW 0
	DB 0FFH		;end of stack marker
	DB 0		;counter for non-expanded TABs

OUTCOD:	CALL OUTCHA	;output CR or FF and reinit counter
	XOR A
	LD (CLCCNT),A
	JP OUTCO9
OUTCOA:	CALL OUTCHA	;output LF
	JP OUTCO9

OUTPUN:	JP OUTCON	;not yet implemented
OUTLST:	JP OUTCON
OUTDSK:	JP OUTCON
OUTCO9:	RET		;exit of subroutine OUTPUT

OUTCCH:	;outputs char in A via console and increments the 
	;char-per-line counter.
	;If the maximum char-per-line count is greater 0
	;CR/LF will be inserted when the maximum count is exceeded 
	;thus resetting the char-per-line to zero.
	EXTERNAL CLCMAX,CLCCNT
	CALL OUTCHA
	LD HL,CLCCNT
	INC (HL)
	LD A,(CLCMAX)
	OR A
	RET Z
	CP (HL)
	RET NZ
	LD (HL),0
	LD A,(CRN)
	CALL OUTCHA
	LD A,(LFD)
	CALL OUTCHA
	RET

OUTCHA:	;outputs char in A on console by calling BDOS function #6
	;and checks the console for XON/XOFF input.
	LD C,6
	LD E,A
	CALL BDOS
	CALL XONXOF
	RET

DELOUT:	;deletes the char in A on the console.
	;If the maximum char-per-line count is greater 0
	;the character-per-line counter is dwecremented.
	;Regular printable characters are blanked out with a 
	;Backspace-Blank-Backspace (Hex 082008) sequence.
	;TAB is blanked out back to the previous TAB position.
	;LF and CR are not treated on the screen although they are deleted 
	;from VALST thus restrincting the out-blanking of characters only to 
	;the beginning of the current line. For further deletions, though,
	;the cursor can be positioned to the end of the previous line 
	;by re-printing the input line with Ctrl/R.
	CP 20H
	JR C,DELOU2	;control char?
DELOU1:	CP 7FH
	CALL C,OUTBS	;printable char?
	JP DELOU9
DELOU2:	LD HL,OUCTBL	;compare control char
	LD BC,OUCJPS-OUCTBL	;with table
	CPIR
	JR Z,DELOU3
	CALL OUTBS	;if not found 2 backspaces
	CALL OUTBS
	JP DELOU9
DELOU3:	LD HL,DELJPS	;if found get jump address
	ADD HL,BC	;from table
	ADD HL,BC
	LD E,(HL)
	INC HL
	LD D,(HL)
	EX DE,HL
	JP (HL)		;and jump to code segment
DELJPS:			;note: address table is inverse
	IRP CH,<DELOUD,DELOUD,DELOUD,DELOUI>
	DW CH
	ENDM

DELOUI:	LD HL,(TABSTK)	;make previous TAB expansion unhappen
	INC HL
	LD A,(HL)
	CP 0FFH		;had end of TAB-stack been reached?
	JP Z,DELOUK
DELOUM:	DEC HL
	LD A,(HL)
	DEC HL
	LD (TABSTK),HL
DELOUJ:	PUSH AF
	CALL OUTBS
	POP AF
	INC A
	AND 07H
	JP NZ,DELOUJ	
	JP DELOU9

DELOUK:	INC HL		;if end of TAB-stack had been reached
	XOR A		;check if all substituted Blanks have been
	CP (HL)		;blanked out.
	JP Z,DELOUL
	DEC (HL)
	CALL OUTBS
	JP DELOU9
DELOUL:	DEC HL		;if so proceed with TAB-stack
	JP DELOUM

DELOUD:			;no action on CR, LF or FF
	JP DELOU9

DELOU9:	RET		;exit of subroutine DELOUT

OUTBS:	;delete last char on console by backspacing and out-blanking
	EXTERNAL CLCCNT
	LD A,(CLCCNT)	;but only if not at beginning of line
	OR A
	RET Z
	DEC A		;decrement char-per-line counter
	LD (CLCCNT),A
	LD A,08H
	CALL OUTCHA
	LD A,20H
	CALL OUTCHA
	LD A,08H
	CALL OUTCHA
	RET

CLRTAB:	;re-init the TAB-expansion stack
	;This subroutine is called whenever a new output sequence is started
	;(like in primitive function RS).
	LD HL,TABSTK+2
	LD (TABSTK),HL
	RET

	END
