; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [34] Insert milli-second wait-loop for Break-timing - label MSWAIT:
; [33] Fix printer on hanging system problem by letting CP/M handle the
;	interrupts from the 7201 that we don't care about.  Thanks to
;	Paul Ford, U. of Chicago Graduate School of Business
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;	other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; The following is the I/O code for the DEC Rainbow.

		CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	48		;[19g]  4.8 Mhz

; Interrupt vector locations, in data segment
mnstat	EQU	042H		;Status port.
mndata	EQU	040H		;Data port.
mnctrl	EQU	002H		;Control port.

; Interrupt vector locations.  These are all in data segment 0.

mnoff	EQU	90H		;Main data port interrupt routine offset.
mnseg	EQU	92H		;Main data port interrupt routine segment.

output	EQU	04H		;Bit for output ready.
input	EQU	01H		;Bit for input ready.

outlmt	EQU	1000H		;Number of times to check output status
				; before giving up on send.		;[20d]


; Input data from port.  Preserves all ACs and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx, mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	mov	bx, offset mnchrs ;If so wrap around to the start.
inchr2:	mov	mnchop, bx	;Save the updated pointer.
	mov	al, [bx]	;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	; do flow control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx, mndata
	out	dx, al
	ret


; Test if data is available from port.

instat:	cmp	mnchrn, 0	;Any chars in the buffer?
	jnz	inst2
	 ret
inst2:	jmp	rskp


; Test if port is ready to send next char.  Returns RETSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwta		;no - go on
	cmp	xofrcv, true	;are we being held?
	jne	outwta		;no - ok go on
	ret			;held - say we're busy.	[19a] end
outwta:	push	ax
	mov	dx, mnstat
	in	al, dx
	test	al, output
	pop	ax
	jnz	outwt2
	 ret
outwt2:	jmp	rskp


; Output the character, checking first to make sure the port is clear.

prtout:	call	dopar		;[par]
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	mov	sp, offset mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax
	call	mnproc		;Process the character.
	mov	dx, mnstat	;Get the status port.
	mov	al, 38H
	out	dx, al		;Tell the port we finished with the interrupt.
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the main port
; and puts it in the infamous circular buffer.

mnproc:	mov	dx, mnstat
	in	al, dx		;Get the port status.
	test	al, input	;Any there?
	jnz	mnpro2		;Yup, go take care of it.

;[33] Begin addition
; If not a received character, simulate an interrupt transferring
; control to the CPM routine. Let it handle worrisome things like
; someone turning on the printer.

	 pushf			; Save flags, like an int.
	 callf dword ptr mnoldo	; Call CPM's routine.
	 ret			; Now back to MNINT.

;[33] End addition

mnpro2:	mov	al, 1		;Point to RR1.
	out	dx, al
	in	al, dx		;Read RR1.
	mov	ah, al		;Save it.
	mov	al, 30H		;Reset any errors.
	out	dx, al
	mov	dx, mndata
	in	al, dx		;Read the char.
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn, mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx, mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	mov	bx, offset mnchrs ;Yes, point to the start again.
mnpro3:	mov	mnchip, bx	;Save the pointer.
	mov	[bx], al	;Put the character in the buffer.
	cmp	floctl, floxon	;do flow control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[19e] start

prtbrk:				;
	mov	dx, mnstat	;status reg. address for port
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0FAH	;8 bits, TX, Break, RTS, & DTR
	out	dx, al		;Turn Break on
	mov	ax, 275		;.. for 275 millisec's		[34]
	call	mswait		;				[34]
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0EAH	;same as above without Break
	out	dx, al		;turn it off
	ret			;				 [19e] end

mswait:				;				[34] start
	mov	cx,5*clckrt	; inner loop count for 1 millisec.
mswai1:
	sub	cx,1		;** inner loop takes 20 clock cycles
	jnz	mswai1		;**
	dec	ax		; outer loop counter
	jnz	mswait		; wait another millisecond
	ret			;				[34] end
;
; Init the 7201 for 8 bits, no parity, and 1 stop bit.

serini:	call	ansmod		;Switch from VT52 to ANSI mode	;[30c]

	mov	ax, ds
	mov	cs:mndseg, ax	;Save the data segment somewhere in CSEG.
	push	ds		;Save the data segment.
	mov	ax, 0
	mov	ds, ax		;We want DSEG = 0.
	cli			;Turn off interrupts.
	mov	bx, .mnoff	;[33] Get original interrupt offset.
	mov	es, .mnseg	;[33] Get original interrupt segment.
	mov	ax, offset mnint;Point to the interrupt routine offset.
	mov	.mnoff, ax	;Put in the main port interrupt offset addr.
	mov	ax, cs		;Get our code segment.
	mov	.mnseg, ax	;Put in the main port interrupt segment addr.
	sti			;Restore interrupts.
	pop	ds		;Restore data segment.

	mov	mnoldo, bx	;[33] Stash original serial interrupt offset.
	mov	mnolds, es	;[33] Stash original segment.

	mov	dx, mnstat	;Point to status port.
	mov	al, 18H
	out	dx, al		;Reset the port.
	mov	al, 14H
	out	dx, al		;Select register 4.
	mov	al, 44H		;16X clock, 1 stop bit, no parity.
	out	dx, al
	mov	al, 13H
	out	dx, al		;Select register 3.
	mov	al, 0C1H	;8 bits/char, RX enable.
	out	dx, al
	mov	al, 15H
	out	dx, al		;Select register 5.
	mov	al, 0EAH	;8 bits/char, TX enable, RTS and DTR.
	out	dx, al
	mov	al, 11H
	out	dx, al		;Select register 1.
	mov	al, 18H
	out	dx, al		;Enable interrupt processing on this port.
	mov	dx, mnctrl	;point to comm control port
	mov	al, 0F0H	;set RTS & DTR high
	out	dx, al
	ret

serfin:	
	call	clrscr		;[19b] clear screen		;[30c]

	ret			;Nothing to deinitialize on Rainbow.

; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, offset mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, offset mnchrs-1+mnchnd ;Reset output pointer.
	ret

	DSEG $

mnchnd	equ	256		;[19a] Size of circular buffer.
mntrg1	equ	64		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	192		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

mnchrn	DW	0		;[19a] Number of chars in the buffer.
mnchrs	RB	mnchnd		;Circular character buffer for input.
mnchip	DW	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	DW	mnchrs-1+mnchnd	;Output pointer into character buffer.

mnoldo	RW	1		;[33] CPM's 7201 interrupt vector offset
mnolds	RW	1		;[33] and segment.

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]


	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand (not implemented in Rainbow)

bdset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in Rainbow)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

shobd:	ret			;Baud rate selection not implemented.

shoprt:	ret			;Port selection not implemented.




; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	cl, 10
	mov	al, [bx]	;Get row value
	sub	ah, ah
	div	cl		;units digit in ah, tens digit in al
	add	ax, '00'	;Convert both to ASCII
	mov	word ptr anspos+2, ax	;Save reversed (al,ah)
	mov	al, 1[bx]	;Do same for column value
	sub	ah, ah
	div	cl
	add	ax, '00'
	mov	word ptr anspos+5, ax
	mov	dx, offset anspos ;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	mov	dx, offset anscls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	mov	dx, offset ansclr ;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	mov	dx, offset ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	mov	dx, offset ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	mov	dx, offset ansbon
	call	tmsg
	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	mov	dx, offset ansbof
	call	tmsg
	ret

; ANSMOD - enters ANSI mode from VT52 mode

ansmod:	mov	dx, offset ansion
	call	tmsg
	ret

	DSEG $

anspos	db	esc,'[00;00H$'		;Position cursor to row and column
anscls	db	esc,'[H',esc,'[J$'	;Home cursor and clear screen
ansclr	db	esc,'[K$'		;Clear from cursor to end of line
ansron	db	esc,'[7m$'		;Turn on reverse video
ansrof	db	esc,'[m$'		;Turn off reverse video
ansbon	db	esc,'[1m$'		;Turn on bold (highlight) display
ansbof	db	esc,'[m$'		;Turn off bold display
ansion	db	esc,'<$'		;Enter ANSI mode

; Here tab expansion is done if necessary.  If not, just return retskp.

	CSEG $
dotab:	jmp rskp

	DSEG $
delstr	db	' ',10O,10O,'$'	;Delete string.

system	db	'                DEC Rainbow-100$'