
; Kermit system dependent module for Heath/Zenith Z100

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, machnam, setktab, setkhlp, lclini, showkey
	include msdefs.h

false	equ	0
true	equ	1
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full.

; constants used by serial port handler

BRKBIT	EQU	048H		; Send-break bit. 

MDMCOM1	EQU	00EFH		; Address of modem port command. [19b]

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
; setktab - keyword table for redefining keys (should contain a 0 if
;    not implemented)
; setkhlp - help for setktab.

BIOS_SEG SEGMENT AT 40H		; Define segment where BIOS really is
	ORG	6*3
BIOS_AUXOUT LABEL FAR		; AUX output routine
	ORG	26*3
BIOS_AUXFUNC LABEL FAR		; AUX: function
	ORG	27*3
BIOS_CONFUNC LABEL FAR		; CON: function
BIOS_SEG ENDS

; Function codes for BIOS_xxxFUNC
CHR_WRITE	EQU	0	; Write character
CHR_READ	EQU	1	; Read character
CHR_STATUS	EQU	2	; Get status
  CHR_SFGS	EQU	0	; Get status subfunction
  CHR_SFGC	EQU	1	; Get config subfunction
CHR_CONTROL	EQU	3	; Control function
  CHR_CFSU	EQU	0	; Set new configuration parameters
  CHR_CFCI	EQU	1	; Clear input buffer


datas 	segment	public 'datas'
	extrn	drives:byte, flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

setktab	db	13
	mkeyw	'F0',96h
	mkeyw	'F1',97h
	mkeyw	'F2',98h
	mkeyw	'F3',99h
	mkeyw	'F4',9ah
	mkeyw	'F5',9bh
	mkeyw	'F6',9ch
	mkeyw	'F7',9dh
	mkeyw	'F8',9eh
	mkeyw	'F9',9fh
	mkeyw	'F10',0a0h
	mkeyw	'F11',0a1h
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Keyname: f0, ... f11, "HELP" or "SCAN" follwed by '
	db	'decimal scan code$'
brkval	db	0		; What to send for a break.
brkadr	dw	0		; Where to send it.
badbd	db	cr,lf,'Unimplemented baud rate$'
noimp	db	cr,lf,'Not implemented$'
machnam	db	'Heath-Zenith Z-100$'
crlf	db	cr,lf,'$'
delstr  db	BS,' ',BS,'$' 	; Delete string. [21d]
home	db	ESC,'H$'
eeolstr db	ESC,'K$'	; Erase to end of line
clrstr	db	ESC,'E$'	; Erase entire display
enamod	db	ESC,'x1$'	; Enable 25th line
dismod	db	ESC,'y1$'	; Disable 25th line
enascan	db	ESC,'y?$'	; Enable scan codes
disscan	db	ESC,'x?$'	; Disable scan codes
begrev	db	ESC,'p$'	; Enter reverse video
endrev	db	ESC,'q$'	; Exit reverse video
lin25	db	ESC,'Y8 $'	; Column 1 row 25
savcur	db	ESC,'j$'	; Save current cursor position
precur	db	ESC,'k$'	; Restore cursor to previous position
clrlin  db	cr,'$'		; Clear line (just the cr part).
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
tmp	db	?,'$'
temp1	dw	?		; Temporary storage.

ontab	db	02H		; Two entries.
	db	03H,'OFF$'	; Should be alphabetized. [19a]
	dw	00H
	db	02H,'ON$'
	dw	01H

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat	label	word
	dw	0		; 45.5 baud
	dw	1		; 50 baud
	dw	2   		; 75 baud
	dw	3   		; 110 baud
	dw	4   		; 134.5 baud
	dw	5   		; 150 baud
	dw	6   		; 300 baud
	dw	7   		; 600 baud
	dw	8  		; 1200 baud
	dw	9  		; 1800 baud
	dw	10 		; 2000 baud
	dw	11 		; 2400 baud
	dw	12 		; 4800 baud
	dw	13 		; 9600 baud
	dw	14  		; 19200 baud
	dw	15  		; 38400 baud

; storage for port configuration
cfginfo	struc
cfclass	db	0
cfattr	db	0
cfport	dw	0
cfbaud	db	0
cfhshk	db	0
cfbctl	db	0
cfecnt	db	0
cfncnt	db	0
cfnchr	db	0
cfres	db	6 dup(?)
cfsize	db	0
cfginfo	ends

auxconf	cfginfo	<>

; variables for serial interrupt handler

count	dw	0		; Number of chars in int buffer.

ourarg	termarg	<>

shkbuf	db	300 dup (?)	; room for definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, defkey:near
	assume	cs:code,ds:datas

; local initialization

lclini	proc	near
	mov	brkval,BRKBIT	; What to send for a break.
	mov	brkadr,MDMCOM1
 	mov	flags.vtflg,0	; Turn off true Heath mode (allows key macros)	 
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.  

DODISK	PROC	NEAR
	mov	ah,gcurdsk	; Current disk value to AL.
	int	dos
	mov	dl,al		; Put current disk in DL.
	mov	ah,seldsk	; Select current disk.
	int	dos
	mov	drives,al
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey	proc	near
	push	es
	push	ax		; save the ptr
	mov	bx,ds
	mov	es,bx		; address data segment
	cld
showk1:	mov	ah,prstr
	mov	dx,offset enascan ; enable scan codes
	int	dos
	mov	ah,0ch		; char input with buffer flush
	mov	al,7
	int	dos
;	mov	ah,chr_control
;	mov	al,chr_cfci	; clear input
;	call	bios_confunc
;	mov	ah,chr_read
;	call	bios_confunc	; read a char
	push	ax
	mov	ah,prstr
	mov	dx,offset disscan ; disable scan codes
	int	dos
	pop	ax
;	push	ax		; save the character
;	call	gss		; get shift state
;	pop	bx
	mov	ah,0		; shift state to ah
;	mov	al,bh		; scan code to al
	push	ax		; remember scan code
	mov	di,offset shkbuf
	mov	si,offset shkmsg
	mov	cx,shkmln
	rep	movsb		; copy in initial message
	call	nout		; write out scan code
	mov	si,offset shkms1
	mov	cx,shkm1ln	; second message
	rep	movsb
	pop	ax		; get scan code back
	pop	bx		; and terminal arg block
	mov	cx,[bx].klen	; and length
	jcxz	showk2		; no table, not defined
	push	di		; remember output ptr
	mov	di,[bx].ktab	; get key table
	repne	scasw		; search for a definition for this
	mov	si,di		; remember result ptr
	pop	di		; get output ptr back
	jne	showk2		; not defined, forget it
	sub	si,[bx].ktab	; compute offset from beginning
	sub	si,2		; minus 2 for pre-increment
	add	si,[bx].krpl	; get index into replacement table
	mov	si,[si]		; pick up replacement
	mov	cl,[si]		; get length
	mov	ch,0
	inc	si
	rep	movsb		; copy into buffer
showk2:	mov	ax,offset shkbuf ; this is buffer
	mov	cx,di
	sub	cx,ax		; length
	pop	es
	ret			; and return
showkey	endp

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	cli
	mov 	ah,chr_control
	mov	al,chr_cfci
	call	bios_auxfunc
	mov 	count,0
	sti
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset eeolstr	; Erase to end of line
	int	dos
	ret
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov	bp,portval
	cmp	ds:[bp].floflg,0 ; Are we doing flow control.
	je	outch2		; No, just continue.
	xor	cx,cx		; clear counter
outch1:	cmp	xofrcv,true	; Are we being held?
	jne	outch2		; No - it's OK to go on.
	loop	outch1		; held, try for a while
	mov	xofrcv,false	; timed out, force it off and fall thru.
outch2:	push	dx		; Save register.
	mov	al,ah		; Parity routine works on AL.
	call	dopar		; Set parity appropriately.
	call	bios_auxout
	pop	dx
	jmp	rskp

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset clrstr
	int	dos
	ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov	ah,prstr
	mov	dx,offset home	; Go to top left corner of screen.
	int	dos
LOCATE  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	ah,prstr
	mov	dx,offset savcur
	int	dos
	mov	dx,offset enamod
	int	dos
	mov	dx,offset lin25
	int	dos
	mov	dx,offset begrev
	int	dos
	pop	dx		; get message back
	int	dos		; write it out
	mov	dx,offset endrev
	int	dos
	mov	dx,offset precur
	int	dos
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	ah,prstr
	mov	dx,offset dismod
	int	dos
	ret
clrmod	endp

BEEP	PROC	NEAR
	mov	dl,07		; ASCII BEL
	mov	ah,dconio	
	int	dos             ; Ring it
	ret
BEEP	ENDP	


; put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax		; preserve this
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov	dl,al
	mov	ah,dconio
	int	dos
	jmp	puthl3		; and keep going
puthl4:	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	ret
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD	PROC	NEAR
	mov	bp,portval
	mov	temp1,ax	; Don't overwrite previous rate. [25]
	mov	ax,ds:[bp].baud	; Check if new rate is valid. [25]
	mov	tmp,2
	mul	tmp		; Get index into baud table.
	mov	bx,offset bddat	; Start of table.
	add	bx,ax
	mov	ax,[bx]		; The data to output to port.
	cmp	ax,0FFH		; Unimplemented baud rate.
	jne	dobd0
	mov	ax,temp1	; Get back orginal value.
	mov	ds:[bp].baud,ax	; Leave baud rate as is.
	mov	ah,prstr
	mov	dx,offset badbd	; Give an error message.
	int	dos
	ret
dobd0:	push	ax		; Save it
	mov	bx,ds		; Set up pointer to config info
	mov	es,bx		;  .  .  .
	mov	bx,offset auxconf ;  .  .  .
	mov	ah,chr_status
	mov	al,chr_sfgc	; get current config info
	call	bios_auxfunc
	pop	ax		; get baud back
	mov	auxconf.cfbaud,al
	mov	ah,chr_control	; Function is control
	mov	al,chr_cfsu	; Subfunction is set new config
	call	bios_auxfunc	; Set the configuration
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	mov	bx,ds
	mov	es,bx
	mov	bx,offset auxconf
	mov	ah,chr_status
	mov	al,chr_sfgc	; Status function get config info
	call	bios_auxfunc
	mov	ch,0
	mov	cl,auxconf.cfbaud
	mov	bp,portval
	mov	ds:[bp].baud,cx
	ret
GETBAUD	ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC	NEAR
	call	chkxon		; see if we need to xon
	push	bx
	mov 	ah,chr_status
	mov 	al,chr_sfgs	; Status function get status
	call	bios_auxfunc
	cmp 	bl,0
	jnz 	prtch2
	pop 	bx
	jmp 	rskp     	; No data - check console.
prtch2:	mov	dh,0
	mov	dl,bl		; Place # of chars in dx
	mov	ah,chr_read
	call 	bios_auxfunc
	dec	dl		; Decrement number of chars
	mov	count,dx	; Save count for posterity
	pop	bx
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	cmp	count,mntrgh	; below trigger?
	jae	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent the xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push	cx
	push	dx
	push	ax
	xor	cx,cx		; Clear loop counter.
	mov	dx,brkadr	; Port address.  [19b]
	in	al,dx		; Get current setting.
	or	al,brkval	; Set send-break bit(s).
	out	dx,al		; Start the break.
pause:	loop	pause		; Wait a while.
	xor	al,brkval	; Clear send-break bit(s).
	out	dx,al		; Stop the break.
	pop	ax
	pop	dx
	pop	cx
	ret			; And return.
SENDBR	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	push	dx
	mov	ah,CONOUT
	mov	dl,ESC
	int	dos
	mov	dl,'Y'
	int	dos
	pop	dx
	push	dx
	mov	dl,dh
	add	dl,' '
	int	dos
	pop	dx
	add	dl,' '
	int	dos
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset delstr ; Erase weird character.
	int	dos			
	ret
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset clrlin
	int	dos
	call	clearl
	ret
CTLU	ENDP

; set the current port.  

COMS	PROC	NEAR
	jmp	notimp
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov	dx,offset ontab
	mov	bx,0
	mov	ah,cmkey
	call	comnd
	 jmp	r
	push	bx
	mov	ah,cmcfm
	call	comnd			; Get a confirm.
	 jmp	vt0			; didn't get a confirm.
	 nop
	pop	bx
	mov	flags.vtflg,bl		; Set the Heath emulation flag
	ret
vt0:	pop	bx
	ret
VTS	ENDP

notimp:	mov	ah,prstr
	mov	dx,offset noimp
	int	dos
	jmp	rskp

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	ret			; We're done.
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	ret			; All done.
SERRST	ENDP


; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0.
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
nout	endp


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop	bp
	add	bp,3
	push	bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end

