MS-DOS Kermit sources (Part 5 of 7)

Jim Knutson knutson at ut-ngp.UUCP
Sat Oct 6 02:08:34 AEST 1984


: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting msxhp150.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxhp150.asm
	public	serini, serrst, clrbuf, outchr, coms, vts, dodel, ctlu
	public	cmblnk, locate, prtchr, dobaud, clearl, lclini
	public	dodisk, getbaud, beep, setkhlp, setktab
	public	machnam, xofsnt, count, term, poscur
	public	clrmod, putmod, puthlp, sendbr, showkey
	include msdefs.h

false	equ	0
true	equ	1

wrdev	equ	40H
rddev	equ	3fH
open	equ	3dH
close	equ	3eH
rdchan	equ	2
e_send_break equ 6
e_ioctl	equ	44h		; MSODS io control fct

datas 	segment	public 'datas'
	extrn	drives:byte, flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte
machnam	db	'HP-150$'
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
erms41	db	cr,lf,'?Warning: Cannot open com port$'
noimp	db	cr,lf,'Command not implemented.$'
setktab	db	0
setkhlp	db	0
shkmsg	db	'Not implemented.'
shklen	equ	$-shkmsg
crlf    db      cr,lf,'$'
comphlp	db	cr,lf,'1 (COM1)   2 (COM2)$'		; [19b]
delstr  db      BS,BS,'  ',BS,BS,'$' 	; Delete string. [21d]
clrlin  db      cr,esc,'K$'
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
invseq	db	esc,'&dB$'	; Reverse video.
nrmseq	db	esc,'&d@$'	; Normal mode.
ivlseq	db	80 dup (' '),cr,'$' 	; Make a line inverse video
tmp	db	?,'$'
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.

; Entries for choosing communications port. [19b]

comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

; variables for serial interrupt handler

source	db	bufsiz DUP(?)	; Buffer for data from port.
bufout	dw	0		; buffer removal ptr
count	dw	0		; Number of chars in int buffer.
bufin	dw	0		; buffer insertion ptr
telflg	db	0		; Are we acting as a terminal. [16] [17c]
clreol	db	esc,'K$'
prttab	dw	com2,com1
com1	db	'COM1',0
com2	db	'COM2',0
blank	db	esc,'H',esc,'J$'
movcur	db	esc,'&a'
colno	db	20 dup (?)
ten	db	10
prthnd	dw	0
tempbuf dw	10 dup(?)
ourarg	termarg	<>
datas	ends

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

; See how many disk drives we have.
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				; Get number of drives in AL.
	mov drives,al
	ret
DODISK	ENDP

; Clear the input buffer before sending a packet. [20e]

CLRBUF	PROC	NEAR
	cli
	mov ax,offset source
	mov bufin,ax
	mov bufout,ax
	mov count,0
	sti
clrb1:	call prtchr		; get a character
	 jmp clrb1		; until there aren't any more
	 nop
	ret
CLRBUF	ENDP

; Common routine to clear to end-of-line. [19a]

CLEARL	PROC	NEAR
	mov dx,offset clreol
	mov ah,prstr
	int dos
	ret
CLEARL	ENDP

dobaud	proc	near
	jmp notimp
dobaud	endp

; Send the break signal out data comm.
sendbr:	mov al,e_send_break
	jmp  dc_ioctl

; Set some data comm ioctl option.  AL has function code.
dc_ioctl proc	near
	mov ah,8h
	mov tempbuf,ax
	mov dx,offset tempbuf
	mov ah,e_ioctl
	mov al,3
	mov bx,prthnd
	mov cx,2
	int 21h
	ret
dc_ioctl endp


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.
	push cx
	push bx
	cmp prthnd,0		; do we have a port handle?
	jne outch3		; yes, go on
	push ax
	call opnprt		; open the port
	pop ax
outch3: mov byte ptr temp,ah	; save character
	mov bx,prthnd
	mov ah,wrdev
	mov cx,1
	mov dx,offset temp
	int dos
	pop bx
	pop cx
	pop dx
	jmp rskp

opnprt:	mov al,flags.comflg
	mov ah,0
	mov si,ax
	shl si,1		; double index
	mov dx,prttab[si]
	mov ah,open
	mov al,2
	int dos
	jnc opnpr1
	mov ah,prstr
	mov dx,offset erms41
	int dos
	ret
opnpr1:	mov prthnd,ax
	ret

; This routine blanks the screen.

CMBLNK	PROC	NEAR		; This is stolen from the IBM example.
	mov ah,prstr
	mov dx,offset blank
	int dos
	ret
CMBLNK  ENDP

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur		; callret...
LOCATE  ENDP

GETBAUD	PROC	NEAR
	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
	push bx
	push cx
	push si
	push bp
	cmp count,0		; no characters?
	jne prtch2		; no, go fill buffer
	cmp prthnd,0		; have a handle yet?
	jne prtch1		; yes, keep going
	call opnprt
prtch1:	mov bx,prthnd
	mov al,rdchan
	mov ah,ioctl
	mov dx,offset source	; buffer to read into
	mov cx,bufsiz		; length of buffer
	int dos
	mov count,ax		; reset count
	or ax,ax
	jz prtch4		; still no chars
	mov bufout,offset source ; this is output ptr
prtch2:	dec count
	mov dx,count		; return count in dx
	mov si,bufout
	lodsb			; get character
	mov bufout,si		; update ptr
	mov bp,portval
	cmp ds:[bp].parflg,PARNON	; no parity?
	je prtch3		; then don't strip
	and al,7fh		; else turn off parity
prtch3:	pop bp
	pop si
	pop cx
	pop bx
	ret
prtch4:	pop bp
	pop si
	pop cx
	pop bx
	jmp rskp		; no chars...
PRTCHR  ENDP

; Position the cursor according to contents of DX.

POSCUR	PROC	NEAR
	mov	ax,ds
	mov	es,ax			; address data segment!!!
	cld
	mov	di,offset colno
	mov	al,dl			; column
	call	nout
	mov	al,'c'
	stosb
	mov	al,dh			; row
	call	nout
	mov	al,'Y'
	stosb
	mov	al,'$'
	stosb
	mov	dx,offset movcur
	mov	ah,prstr
	int	dos			; print the sequence
	ret
POSCUR	ENDP

NOUT	PROC	NEAR
	cbw			; extend to word
	div	byte ptr ten	; divide by 10
	or	al,al		; any quotient?
	jz	nout1		; no, forget this
	push	ax		; save current result
	call	nout		; output high order
	pop	ax		; restore
nout1:	mov	al,ah		; get digit
	add	al,'0'		; make printable
	stosb
	ret			; put in buffer and return
NOUT	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	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	mov	ah,prstr
	int	dos
	pop	dx
	int 	dos
	mov	dx,offset nrmseq ; normal videw
	int	dos
	ret			; and return
putmod	endp

; Clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	call	clearl
	ret
clrmod	endp

; Put a help message one the screen in reverse video.  Pass
; the message in AX, terminated by a null.  Returns normally.
; The message is put wherever the cursor currently is located.
puthlp	proc	near
	push ax
	mov ah,prstr		; Leave some room before the message.
	mov dx,offset crlf
	int dos
	pop si			; Put message address here.
puth0:	mov ah,prstr
	mov dx,offset invseq	; Put into reverse video.
	int dos
	mov ah,prstr
	mov dx,offset ivlseq	; Make line inverse video
	int dos
puth1:	lodsb
	cmp al,0		; Terminated with a null.
	je puth2
	mov dl,al
	mov ah,conout
	int dos	
	cmp al,lf		; Line feed?
	je puth0		; Yes, clear the next line.
	jmp puth1		; Else, just keep on writing.
puth2:	mov dx,offset crlf
	mov ah,prstr
	int dos
	mov dx,offset nrmseq	; Normal video.
	int dos
	ret
puthlp	endp

; Perform a delete.

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

; Perform a Control-U.

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

COMS	PROC	NEAR
        mov dx,offset comptab
        mov bx,offset comphlp
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp comx		;  Didn't get a confirm.
	 nop
        pop bx
        mov flags.comflg,bl     ; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	ret
comx:	pop bx
	ret
COMS	ENDP

VTS	PROC	NEAR
	jmp notimp
VTS	ENDP

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

lclini:	ret

showkey:
	mov ax,offset shkmsg
	mov cx,shklen
	ret

;  Common initialization for using serial port.

SERINI	PROC	NEAR
	call opnprt
	call clrbuf		; Clear input buffer. [20e]
	ret			; We're done. [21c]
SERINI	ENDP

SERRST	PROC	NEAR
	mov bx,prthnd
	cmp bx,0		; none there?
	je serrs1		; no, don't try to close.
	mov ah,close
	int dos			; close handle
	mov prthnd,0
serrs1:	ret			; All done. [21c]
SERRST	ENDP

; Generate a short beep.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos	
	ret
BEEP	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

term	proc	near
	mov si,ax		; this is source
	mov di,offset ourarg	; place to store arguments
	mov ax,ds
	mov es,ax		; address destination segment
	mov cx,size termarg
	rep movsb		; copy into our arg blk
term1:	call prtchr
	jmp short term2		; have a char...
	nop
	nop
	jmp short term3		; no char, go on
term2:	push ax
	mov dl,al
	and dl,7fh		; mask off parity for terminal
	mov ah,dconio
	int dos			; write out the character
	pop ax
	test ourarg.flgs,capt	; capturing output?
	jz term3		; no, forget it
	call ourarg.captr	; else call the routine
term3:	mov ah,dconio
	mov dl,0ffh
	int dos
	jz term1		; no character, go on
	cmp al,ourarg.escc	; escape char?
	je term4		; yes, exit
	push ax			; save char
	mov ah,al
	or ah,80H		; turn on hi bit so DOS doesn't interfere
	call outchr		; output the character
	nop
	nop
	nop
	pop ax
	test ourarg.flgs,lclecho ; echoing?
	jz term1		; no, continue loop
	mov dl,al
	mov ah,dconio
	int dos
	jmp term1		; else echo and keep going
term4:	ret
term	endp
code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxhp150.asm
	/bin/echo -n '	'; /bin/ls -ld msxhp150.asm
fi
/bin/echo 'Extracting msxibm.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxibm.asm
; Kermit system dependent module for IBM-PC

	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	040H		; Send-break bit. 
TIMER	EQU	40H		; Use to issue short beep.
PORT_B	EQU	61H		; Port B address.
MCONF	EQU	11H		; Machine configuration. 
KEYB	EQU	16H
BIOS	EQU	10H

MDMDAT1	EQU	03F8H		; Address of modem port (data). [19b]
MDMSTS1	EQU	03FDH		; Address of modem port	status. [19b]
MDMCOM1	EQU	03FBH		; Address of modem port command. [19b]
MDMDAT2	EQU	02F8H		; Port 2 address. [19b]
MDMSTS2	EQU	02FDH		; Port 2 status. [19b]
MDMCOM2	EQU	02FBH		; Port 2 command. [19b]
MDMINP	EQU	1		; Input ready bit.

MDMINTV	EQU	0030H		; Address of modem port interrupt vector.
MDINTV2 EQU	002CH		; Address for port 2. [19b] 
MDMINTO	EQU	0EFH		; Mask to enable interrupt for modem port.
MDINTO2 EQU	0F7H		; Enable interrupt level 3. [19b]
MDMINTC	EQU	010H		; Bit to set to disable interrupts for modem.
MDINTC2 EQU	008H		; Disable IRQ3. [19b]

INTCONT	EQU	0021H		; Address of 8259 interrupt controller ICW2-3.
INTCON1	EQU	0020H		; Address of 8259 ICW1.
EOICOM	EQU	0064H		; End of interrupt.
EOICOM2	EQU	0063H		; End of interrupt for COM2. [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.

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

setktab	db	12
	mkeyw	'BACKSPACE',0eh
	mkeyw	'F1',3bh
	mkeyw	'F2',3ch
	mkeyw	'F3',3dh
	mkeyw	'F4',3eh
	mkeyw	'F5',3fh
	mkeyw	'F6',40h
	mkeyw	'F7',41h
	mkeyw	'F8',42h
	mkeyw	'F9',43h
	mkeyw	'F10',44h
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Keyname: backspace, f1, ... f10, or "SCAN" follwed by '
	db	'decimal scan code$'
brkval	db	0		; What to send for a break.
brkadr	dw	0		; Where to send it.
modem	mdminfo	<MDMDAT1,MDMSTS1,MDMCOM1,MDMINTO,MDMINTC,EOICOM,MDMINTV>
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
badbd	db	cr,lf,'Unimplemented baud rate$'
machnam	db	'IBM-PC$'
crlf	db	cr,lf,'$'
delstr  db	BS,' ',BS,'$' 	; Delete string. [21d]
clrlin  db	cr,'$'			; Clear line (just the cr part).
savsci	dw	?		; Save for serial port interrupt vector. [14]
savscs	dw	?		; Ditto.  [14]  
savbr1	dw	?		; "Break" interrupt vector. [25]
savbr2	dw	?		; Ditto. [25]
portin	db	0		; Has comm port been initialized. [21c]
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
tmp	db	?,'$'
temp	dw	0
temp1	dw	?		; Temporary storage.
temp2	dw	?		; Temporary storage.

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

comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat	label	word
	dw	0FFH		; 45.5 baud  -- Not supported.
	dw	900H		; 50 baud
	dw	600H		; 75 baud
	dw	417H		; 110 baud
	dw	359H		; 134.5 baud
	dw	300H		; 150 baud
	dw	180H		; 300 baud
	dw	0C0H		; 600 baud
	dw	60H		; 1200 baud
	dw	40H		; 1800 baud
	dw	3AH		; 2000 baud
	dw	30H		; 2400 baud
	dw	18H		; 4800 baud
	dw	0CH		; 9600 baud
	dw	0FFH		; 19200 baud -- Not supported.
	dw	0FFH		; 38400 baud -- Not supported.

; variables for serial interrupt handler

source	db	bufsiz DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
telflg	db	0		; Are we acting as a terminal.
mst	dw	0		; Modem status address.
mdat	dw	0		; Modem data address.
mdeoi	db	0		; End-of-Interrupt value.

rbtrn	db	7fH		; rubout

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, gss:near
	assume	cs:code,ds:datas

; local initialization

lclini	proc	near
	mov	ax,0eH		; scan code for arrow key
	mov	si,offset rbtrn	; translate to rubout
	mov	cx,1		; one char translation
	call	defkey
	mov brkval,BRKBIT	; What to send for a break.
	mov ax,modem.mdcom	; Where to send it.
	mov brkadr,ax
	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
	int mconf			; Get equipment configuration.
	mov ah,al			; Store AL value for a bit.
	and al,01H			; First, look at bit 0.
	jz dodsk0			; No disk drives -- forget it.
	mov al,ah			; Get back original value.
	mov cl,6			; Shift over bits 6 and 7.
	shr al,cl			; To positions 0 and 1.
	inc al				; Want 1 thru 4 (not 0 thru 3).
	mov drives,al			; Remember how many. 
	ret
dodsk0:	mov ah,prstr			; Print a warning message.
	mov dx,offset erms20		; I'm not sure if things will
	int dos				; work with only a cassette.
	mov drives,0			; Say there aren't any drives.
	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:	xor	ah,ah
	int	keyb		; read a char
	push	ax		; save the character
	call	gss		; get shift state
	pop	bx
	mov	ah,al		; 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 ax,offset source
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	sti
	ret
CLRBUF	ENDP

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

CLEARL	PROC	NEAR
	mov ah,3		; Clear to end of line.
	mov bh,0
	int bios		; Get current cursor position
	mov cx,dx
	mov dl,79
	mov ah,7
	mov al,0
	mov bh,7
	int bios
	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.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,modem.mdstat	; Get port status.
outch3:	in al,dx
	test al,20H		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,modem.mddat
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR		; This is stolen from the IBM example.
	mov cx,0
	mov dx,184FH
	mov bh,7
	mov ax,600H
	int bios
	ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
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	cx,1800h
	mov	dx,184fh
	mov	ax,600h		; scroll to clear the line
	mov	bh,70h		; inverse video
	int	bios
	mov	dx,1800h	; now address line 24
	call	poscur
	pop	dx		; get message back
	mov	ah,prstr
	int	dos		; write it out
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	cx,1800h
	mov	dx,184fh
	mov	ax,600h
	mov	bh,7h
	int	bios
	ret
clrmod	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	si,ax		; point to it
	mov	dh,1		; init counter
puthl1:	lodsb			; get a byte
	cmp	al,lf		; linefeed?
	jne	puthl2		; no, keep going
	inc	dh		; count it
	jmp	puthl1		; and keep looping
puthl2:	cmp	al,0		; end of string?
	jne	puthl1		; no, keep going
	mov	ax,600h		; scroll to clear window
	xor	cx,cx		; from top left
	mov	dl,4fh		; to bottom right of needed piece
	mov	bh,70h		; inverse video
	int	bios
	call	locate		; home cursor
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov	ah,14
	int	bios		; else write to screen
	jmp	puthl3		; and keep going
puthl4:	mov	dx,24 * 100H	; go to last line
	jmp	poscur		; position and return
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:	mov temp1,ax		; Remember value to output. [25]
	mov dx,modem.mdcom	; LCR -- Initialize baud rate. [19b]
	in al,dx
	mov bl,al
	or ax,80H
	out dx,al
	mov dx,modem.mddat	; [19b]
	mov ax,temp1
	out dx,al
	inc dx
	mov al,ah
	out dx,al
	mov dx,modem.mdcom	; [19b]
	mov al,bl
	out dx,al
	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 dx,modem.mdcom	; Get current Line Control Register value.
	in al,dx
	mov bl,al		; Save it.
	or ax,80H		; Turn on to access baud rate generator.
	out dx,al
	mov dx,modem.mddat	; Divisor latch.
	inc dx
	in al,dx		; Get hi order byte.
	mov ah,al		; Save here.
	dec dx
	in al,dx		; Get lo order byte.
	push ax	
	mov dx,modem.mdcom
	mov al,bl		; Restore old value.
	out dx,al
	pop ax
	cmp ax,0FFFFH		; Who knows what this is.
	je getb2
	mov bx,offset bddat	; Find rate's offset into table.
	mov cl,0		; Keep track of index.
getb0:	cmp ax,[bx]
	je getb1
	inc cl
	cmp cl,baudsiz		; At the end of the list.
	jge getb2
	add bx,2
	jmp getb0
getb1:	mov ch,0
	mov bp,portval
	mov ds:[bp].baud,cx	; Set baud rate.
	ret
getb2:	mov ah,prstr
	mov dx,offset erms40
	int dos
	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
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	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
	mov ah,2		; Position cursor.
	mov bh,0
	int bios
	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
	mov dx,offset comptab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp comx		;  Didn't get a confirm.
	 nop
	pop bx
	mov flags.comflg,bl	; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	mov modem.mddat,MDMDAT1	; Set COM1 defaults.
	mov modem.mdstat,MDMSTS1
	mov modem.mdcom,MDMCOM1
	mov modem.mddis,MDMINTC
	mov modem.mden,MDMINTO
	mov modem.mdmeoi,EOICOM
	mov modem.mdintv,MDMINTV
	mov brkadr,MDMCOM1
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	mov modem.mddat,MDMDAT2	; Set COM2 defaults.
	mov modem.mdstat,MDMSTS2
	mov modem.mdcom,MDMCOM2
	mov modem.mddis,MDINTC2
	mov modem.mden,MDINTO2
	mov modem.mdmeoi,EOICOM2
	mov modem.mdintv,MDINTV2
	mov brkadr,MDMCOM2
	ret
comx:	pop bx
	ret
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 VT52 emulation flag.
	ret
vt0:	pop bx
	ret
VTS	ENDP

; 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
	push es
	cmp portin,0		; Did we initialize port already? [21c]
	jne serin0		; Yes, so just leave. [21c]
	cli			; Disable interrupts
	cld			; Do increments in string operations
	xor ax,ax		; Address low memory
	mov es,ax
	mov bx,modem.mdintv	; Save serial card interrupt vector. [19b]
	mov ax,es:[bx]
	mov savsci,ax
	mov ax,offset serint	; And point it to my routine
	mov es:[bx],ax
	add bx,2		; Save CS register too. [19b]
	mov ax,es:[bx]
	mov savscs,ax
	mov es:[bx],cs
	mov portin,1		; Remember port has been initialize. 
	call clrbuf		; Clear input buffer. 
	mov ax,modem.mdstat
	mov mst,ax		; Use this address for status. 
	mov ax,modem.mddat
	mov mdat,ax		; Use this address for data. 
	mov al,modem.mdmeoi
	mov mdeoi,al		; Use to signify end-of-interrupt. 
	in al,21H		; Set up 8259 interrupt controller
	and al,modem.mden	; Enable INT3 or INT4. 
	out 21H,al
	mov dx,modem.mdcom	; Set up the serial card. 
	mov al,3
	out dx,al
	mov dl,0F9H
	mov al,1		; Set up interrupt enable register
	out dx,al
	mov dl,0FCH		; Enable interrupts from serial card
	mov al,0BH
	out dx,al
	sti			; Allow interrupts
	mov dl,0F8H
	in al,dx
serin0:	pop es
	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
	push es			; preserve this
	cmp portin,0		; Reset already? 
	je srst1		; Yes, just leave. 
	cli			; Disable interrupts
	mov dx,03FCH		; Disable modem interrupts
	cmp flags.comflg,1	; Using port 1 ? 
	je srst0		; Yes - continue. 
	mov dh,02		; Set for port 2. 
srst0:	mov al,3
	out dx,al
	in al,21H		; Interrupt controller
	or al,modem.mddis	; Inhibit IRQ3 or IRQ4. 
	out 21H,al
	xor bx,bx		; Address low memory
	mov es,bx
	mov bx,modem.mdintv	; Restore the serial card int vector 
	mov ax,savsci
	mov es:[bx],ax
	add bx,2		; Restore CS too. 
	mov ax,savscs
	mov es:[bx],ax
	mov portin,0		; Reset flag.
	sti
srst1:	pop es
	ret			; All done.
SERRST	ENDP

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

SERINT  PROC  NEAR
	push bx
	push dx
	push ax
	push es
	push di
	push ds
	push bp
	push cx
	cld
	mov ax,seg datas
	mov ds,ax		; address data segment
	mov es,ax
	mov di,srcpnt		; Registers for storing data.
	mov dx,mst		; Asynch status port. [19b]
	in al,dx
	test al,mdminp		; Data available?
	jz retint		; Nope.
	mov dx,mdat		; [19b]
	in al,dx
	cmp telflg,0		; File transfer or terminal mode? [17c]
	jz srint0
	and al,7FH		; Terminal mode (7 bits only). 
srint0: or al,al
	jz retint		; Ignore nulls.
	mov ah,al
	and ah,7fH		; strip parity temporarily
	cmp ah,7FH		; Ignore rubouts, too.
	jz retint
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srint2		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp al,bl		; Is it an XOFF?
	jne srint1		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp retint
srint1:	cmp al,bh		; Get an XON?
	jne srint2		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp retint
srint2:	stosb
	cmp di,offset source + bufsiz
	jb srint3		; not past end...
	mov di,offset source	; wrap buffer around
srint3:	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je retint		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je retint		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe retint		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	nop
	nop
	nop			; ignore failure.
	mov xofsnt,true		; Remember we sent it.
retint:	mov srcpnt,di
	sti
	mov al,mdeoi		; [19b]
	out intcon1,al		; Send End-of-Interrupt to 8259.
	pop cx
	pop bp
	pop ds
	pop di
	pop es
	pop ax
	pop dx
	pop bx
intret:	iret
SERINT	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov al,10110110B	; Gen a short beep (long one losses data.)
	out timer+3,al		; Code snarfed from Technical Reference.
	mov ax,533H
	out timer+2,al
	mov al,ah
	out timer+2,al
	in al,port_b
	mov ah,al
	or al,03
	out port_b,al
	sub cx,cx
	mov bl,1
beep0:  loop beep0
	dec bl		
	jnz beep0
	mov al,ah
	out port_b,al
	ret
BEEP	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
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxibm.asm
	/bin/echo -n '	'; /bin/ls -ld msxibm.asm
fi
/bin/echo 'Extracting msxrb.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxrb.asm
; Kermit system dependent module for Rainbow
; Jeff Damens, July 1984

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

; rainbow-dependent screen constants

scrseg	equ	0ee00H		; screen segment
latofs	equ	0ef4h		; ptrs to line beginnings, used by firmware
l1ptr	equ	latofs		; ptr to first line
llptr	equ	latofs+23*2	; ptr to last line
csrlin	equ	0f42h		; current cursor line.

; level 1 console definitions

fnkey	equ	100H		; function key flag
shfkey	equ	200H		; shift key
ctlkey	equ	400H		; control key
cplk	equ	800H

prvkey	equ	23H
nxtkey	equ	25H
brkkey	equ	65H
prtkey	equ	3

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

mnstata	equ	042H		;Status/command port A
mnstatb	equ	043H		;Status/command port B.
mndata	equ	040H		;Data port.
mndatb	equ	041H
mnctrl	equ	002H		;Control port.
serchn	equ	0A4H		; interrupt to use
serch1	equ	044H		; use this too for older rainbows.

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

fastcon	equ	29H		; fast console handler
firmwr	equ	18H

swidth	equ	132		; screen width
slen	equ	24		; screen length
npgs	equ	5		; # of pages to remember

stbrk	equ	15		; start sending a break
enbrk	equ	16		; stop sending break.

; 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.

; circular buffer ptr
cbuf	struc
pp	dw	?			; place ptr in buffer
bend	dw	?			; end of buffer
orig	dw	?			; buffer origin
lcnt	dw	0			; # of lines in buffer.
cbuf	ends

; answerback structure
ans	struc
anspt	dw	?			; current pointer in answerback
ansct	db	?			; count of chars in answerback
ansseq	dw	?			; pointer to whole answerback
anslen	db	?			; original length
ansrtn	dw	?			; routine to call.
ans	ends

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

setktab	db	22
	mkeyw	'F4',fnkey+5h
	mkeyw	'F5',fnkey+65h
	mkeyw	'F6',fnkey+7h
	mkeyw	'F7',fnkey+9h
	mkeyw	'F8',fnkey+0Bh
	mkeyw	'F9',fnkey+0Dh
	mkeyw	'F10',fnkey+0Fh
	mkeyw	'F11',esc
	mkeyw	'F12',bs
	mkeyw	'F13',lf
	mkeyw	'F14',fnkey+11h
	mkeyw	'F17',fnkey+13h
	mkeyw	'F18',fnkey+15h
	mkeyw	'F19',fnkey+17h
	mkeyw	'F20',fnkey+19h
	mkeyw	'FIND',fnkey+1bh
	mkeyw	'INSERTHERE',fnkey+1dh
	mkeyw	'REMOVE',fnkey+1fh
	mkeyw	'SCAN',-1
	mkeyw	'SELECT',fnkey+21h

ourflgs	db	0		; our flags
fpscr	equ	80H		; flag definitions...

crlf	db	cr,lf
setkhlp	db	' F4 ... F20 or SCAN$'
machnam	db	'Rainbow$'
nyimsg	db	cr,lf,'Not yet implemented$'
delstr  db      BS,' ',BS,'$' 	; Delete string.
clrlin  db      cr,'$'		; Clear line (just the cr part).
oldser	dw	?		; old serial handler
oldseg	dw	?		; segment of above
old1ser	dw	?		; old serial handler, alternate address
old1seg	dw	?		; segment of same.
portin	db	0		; Has comm port been initialized.
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
iobuf	db	5 dup (?)	; buffer for ioctl

phbuf	db	swidth dup (?)
gopos	db	esc,'['
rowp	db	20 dup (?)
clrseq	db	esc,'[H',esc,'[J$'
ceolseq	db	esc,'[K$'
invseq	db	esc,'[7m$'
nrmseq	db	esc,'[0m$'
ivlatt	db	swidth dup (0fH) ; a line's worth of inverse attribute

; special keys.
spckey	dw	prvkey,nxtkey,brkkey,prtkey,prtkey+ctlkey,prvkey+ctlkey
	dw	nxtkey+ctlkey,brkkey+ctlkey
spclen	equ	($-spckey)/2
; special key handlers.  Must parallel spckey
spchnd	dw	prvscr,nxtscr,sendbr,prtscr,togprt,prvlin,nxtlin,sendbr

; arrow and PF keys
arrkey	db	27H,29H,2bH,2dH,59H,5cH,5fH,62H
arrlen	equ	$-arrkey
; translations for arrow and PF keys, must parallel arrkey
arrtrn	dw	uptrn,dntrn,rgttrn,lfttrn
	dw	pf1trn,pf2trn,pf3trn,pf4trn

; keypad keys
keypad	db	2fh,32h,35h,38h,3bh,3eh,41h,44h,47h,4ah,4dh,50h,53h,56h
keypln	equ	$-keypad
; keytrn and altktrn must parallel keypad
keytrn	db	'0123456789-,.',cr
altktrn	db	'pqrstuvwxymlnM'

keyptr	dw	keytrn		; pointer to correct translation table
akeyflg	db	0		; non-zero if in alt keypad mode.

; arrow and PF key translations
uptrn	db	3,esc,'[A'
dntrn	db	3,esc,'[B'
rgttrn	db	3,esc,'[C'
lfttrn	db	3,esc,'[D'
pf1trn	db	3,esc,'OP'
pf2trn	db	3,esc,'OQ'
pf3trn	db	3,esc,'OR'
pf4trn	db	3,esc,'OS'

ourarg	termarg	<>

; variables for serial interrupt handler
source	db	bufsiz DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
telflg	db	0		; non-zero if we're a terminal. NRU.
respkt	db	10 dup (?)	; ioctl packet
ivec	dw	tranb		; transmit empty B
	dw	tranb		; status change B
	dw	tranb		; receive b
	dw	tranb		; special receive b
	dw	stxa		; transmit empty a
	dw	sstata		; status change a
	dw	srcva		; receive a
	dw	srcva		; special receive a

; multi-screen stuff

bsize	equ	swidth*slen*npgs ; # of bytes needed to store screens
tbuf	db	bsize dup (?)
bbuf	db	bsize dup (?)	; top and bottom buffers
topbuf	cbuf	<tbuf,tbuf+bsize-1,tbuf,0>
botbuf	cbuf	<bbuf,bbuf+bsize-1,bbuf,0>
tlbuf	db	swidth dup (?)	; top line temp buffer
blbuf	db	swidth dup (?)	; bottom line temp buffer
rlbuf	db	swidth dup (?)	; line temp buffer
prbuf	db	swidth dup (?)	; print temp buffer
topdwn	db	esc,'[H',esc,'M$' ; go to top, scroll down
botup	db	esc,'[24;0H',esc,'D$' ; go to bottom, scroll up
curinq	db	esc,'[6n$'	; cursor inquiry
posbuf	db	20 dup (?)	; place to store cursor position
gtobot	db	esc,'[24;0H$'	; go to bottom of screen.
ourscr	db	slen*swidth dup (?)
ourattr	db	slen*swidth dup (?) ; storage for screen and attributes
inited	db	0		; terminal handler not inited yet.
dosmsg	db	'?Must be run in version 2.05 or higher$'
anssq1	db	esc,'[c'
ansln1	equ	$-anssq1
anssq2	db	esc,'Z'
ansln2	equ	$-anssq2
eakseq	db	esc,'='
dakseq	db	esc,'>'
ansbk1	ans	<anssq1,ansln1,anssq1,ansln1,sndans> ; two answerbacks
ansbk2	ans	<anssq2,ansln2,anssq2,ansln2,sndans>
ansbk3	ans	<eakseq,2,eakseq,2,enaaky> ; enable alt keypad
ansbk4	ans	<dakseq,2,dakseq,2,deaaky> ; disable alt keypad
ansret	db	esc,'[?6c'
ansrln	equ	$-ansret
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
	assume	cs:code,ds:datas

; local initialization routine, called by Kermit initialization.

lclini	proc	near
; make sure this is DOS version 2.05 or higher...
	mov	ah,dosver
	int	dos
	xchg	al,ah		; put major version in ah, minor in al
	cmp	ax,205H		; is it 2.05?
	jae	lclin1		; yes, go on
	mov	dx,offset dosmsg
	call	tmsg
	mov	ax,4c00H	; exit(0)
	int	dos
lclin1:	mov	flags.vtflg,0	; turn off heath emulation
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  The only problem is that a value of two
; is returned for single drive systems to be consistent
; with the idea of the system having logical drives A and
; B.  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				; Get number of drives in AL.
	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
	cld
showk1:	mov	di,6		; get level one char
	int	firmwr
	cmp	cl,0ffH
	jne	showk1		; wait until char available
	mov	bx,ds
	mov	es,bx		; address data segment
	and	ax,not cplk	; no caps lock
	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 ax,offset source
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	sti
	ret
CLRBUF	ENDP

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

CLEARL	PROC	NEAR
	mov	dx,offset ceolseq	; clear sequence
	jmp	tmsg
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.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,mnstata		; port status register
outch3:	in al,dx
	test al,txrdy		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,mndata
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov	dx,offset clrseq ; clear screen sequence
	jmp	tmsg
CMBLNK  ENDP

; Locate; homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
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	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	call	tmsg
	pop	dx
	call	tmsg		; print the message
	mov	dx,offset nrmseq ; normal videw
	call	tmsg
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	call	clearl
	ret
clrmod	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
	mov	dx,slen * 100H	; go to bottom line
	call	poscur
	pop	ax
	push	es
	mov	bx,ds
	mov	es,bx		; address data segment
	mov	si,ax		; convenient place for this
	mov	bx,101H		; current line/position
puthl1:	mov	di,offset phbuf	; this is destination
	xor	cx,cx		; # of chars in the line
puthl2:	lodsb			; get a byte
	cmp	al,cr		; carriage return?
	je	puthl2		; yes, ignore it
	cmp	al,lf		; linefeed?
	je	puthl3		; yes, break the loop
	cmp	al,0
	je	puthl3		; ditto for null
	dec	cx		; else count the character
	stosb			; deposit into the buffer
	jmp	puthl2		; and keep going
puthl3:	add	cx,80		; this is desired length of the whole
	mov	al,' '
	rep	stosb		; fill the line
	push	bx
	push	si
	push	es		; firmware likes to eat this one
	mov	ax,0		; send chars and attributes
	mov	cx,80		; this is # of chars to send
	mov	dx,offset ivlatt ; this are attributes to send
	mov	si,offset phbuf	; the actual message
	mov	di,14H		; send direct to screen
	mov	bp,ds		; need data segment as well
	int	firmwr		; go send it
	pop	es
	pop	si
	pop	bx		; restore everything
	inc	bx		; next line
	cmp	byte ptr [si-1],0 ; were we ended by a 0 last time?
	jne	puthl1		; no, keep looping
	pop	es		; else restore this
	ret			; and return
puthlp	endp

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

; no baud rate.
DOBAUD	PROC	NEAR
	mov	dx,offset nyimsg
	call	tmsg
	mov	bx,portval
	mov	[bx].baud,-1		; keep baud rate unknown.
	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
	ret			; no baud rate for now.
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 have to xon the host.
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	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 an 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 bx
	push cx
	push dx
	push ax
	mov ah,ioctl
	mov al,3		; write to control channel.
	mov bx,3		; aux port handle
	mov dx,offset iobuf
	mov iobuf,stbrk		; start sending a break
	int dos
	xor cx,cx		; clear loop counter
pause:	loop pause		; Wait a while.
	mov ah,ioctl
	mov al,3
	mov bx,3
	mov dx,offset iobuf
	mov iobuf,enbrk		; stop sending the break
	int dos
	pop ax
	pop dx
	pop cx
	pop bx
	ret			; And return.
SENDBR	ENDP

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

POSCUR	PROC	NEAR
	add	dx,101H		; start at 1,1
	push	es
	push	dx
	cld
	mov	ax,ds
	mov	es,ax		; address right segment
	mov	di,offset rowp
	mov	al,dh		; row comes first
	mov	ah,0
	call	nout
	mov	al,';'
	stosb			; separated by a semi
	pop	dx
	mov	al,dl
	mov	ah,0
	call	nout
	mov	al,'H'
	stosb			; end w/H
	mov	byte ptr [di],'$' ; and dollar sign
	mov	dx,offset gopos
	call	tmsg
	pop	es
	ret
POSCUR	ENDP

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

DODEL	PROC	NEAR
	mov dx,offset delstr	; Erase weird character.
	jmp tmsg
DODEL	ENDP

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

CTLU	PROC	NEAR
	mov dx,offset clrlin	; this just goes to left margin...
	call tmsg
	jmp clearl		; now clear line
CTLU	ENDP

; set the current port.

COMS	PROC	NEAR
	mov	dx,offset nyimsg
	jmp	tmsg
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov	dx,offset nyimsg
	jmp	tmsg
VTS	ENDP

; 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
	push es
	cmp portin,0		; Did we initialize port already? [21c]
	jne serin0		; Yes, so just leave. [21c]
	cli			; Disable interrupts
	cld			; Do increments in string operations
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*serchn]	; get old serial handler
	mov oldser,ax		; save.
	mov ax,es:[4*serchn+2]	; get segment
	mov oldseg,ax		; save segment as well
	mov ax,es:[4*serch1]	; this is alternate for older rainbows
	mov old1ser,ax
	mov ax,es:[4*serch1+2]
	mov old1seg,ax		; pretty silly, huh?
	mov ax,offset serint	; point to our routine
	mov word ptr es:[4*serchn],ax ; point at our serial routine
	mov word ptr es:[4*serch1],ax ; have to set both of these
	mov es:[4*serchn+2],cs	; our segment
	mov es:[4*serch1+2],cs
	mov al,030h		;[DTR] enable RTS and DTR
	out mnctrl,al		;[DTR]
	mov portin,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer. 
	sti			; Allow interrupts
serin0:	pop es
	ret			; We're done.
SERINI	ENDP

; this is used to by serini
prtset	proc	near
	lodsb			; get a byte
	or	al,al
	jz	prtse1		; end of table, stop here
	out	dx,al		; else send it out
	jmp	prtset		; and keep looping
prtse1:	ret			; end of routine
prtset	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
	push es			; preserve this
	cmp portin,0		; Reset already? 
	je srst1		; Yes, just leave. 
	cli			; Disable interrupts
	xor ax,ax
	mov es,ax		; address segment 0
	mov ax,oldser
	mov es:[4*serchn],ax
	mov ax,oldseg
	mov es:[4*serchn+2],ax
	mov ax,old1ser
	mov es:[4*serch1],ax
	mov ax,old1seg
	mov es:[4*serch1+2],ax	; restore old handlers
	mov portin,0		; Reset flag.
srst1:	pop es
	ret			; All done.
SERRST	ENDP

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

serint	PROC  NEAR
	push bx
	push dx
	push ax
	push es
	push di
	push ds
	push bp
	push cx
	cld
	mov ax,seg datas
	mov ds,ax		; address data segment
	mov es,ax
	mov di,srcpnt		; Registers for storing data.
	mov dx,mnstatb		; Asynch status port.
	mov al,0		; innocuous value
	out dx,al		; send out to get into a known state...
	mov al,2		; now address register 2
	out dx,al
	in al,dx		; read interrupt cause
	cmp al,7		; in range?
	ja serin7		; no, just dismiss (what about reset error?)
	mov bl,al
	shl bl,1		; double for word index
	mov bh,0
	call ivec[bx]		; call appropriate handler
serin7:	mov dx,mnstata		; reload port address
	mov al,38H
	out dx,al		; tell the port we finished with the interrupt
	pop cx
	pop bp
	pop ds
	pop di
	pop es
	pop ax
	pop dx
	pop bx
intret:	iret

; handler for serial receive, port A
srcva:	mov dx,mnstata
	mov al,0
	out dx,al		; put into known state...
	in al,dx
	test al,rxrdy		; Data available?
	jnz srcva1		; yes, go read it
	jmp srcva7
srcva1:	mov al,30H		; reset any errors
	out dx,al
	mov dx,mndata
	in al,dx		; read the character
	cmp telflg,0		; File transfer or terminal mode?
	jz srcva2
	and al,7FH		; Terminal mode (7 bits only). 
srcva2: or al,al
	jz srcva7		; Ignore nulls.
	cmp al,7FH		; Ignore rubouts, too.
	jz srcva7
	mov ah,al
	and ah,7fH		; only consider low-order 7 bits for flow ctl.
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srcva4		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp ah,bl		; Is it an XOFF?
	jne srcva3		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp short srcva7
srcva3:	cmp ah,bh		; Get an XON?
	jne srcva4		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp srcva7
srcva4:	stosb
	cmp di,offset source + bufsiz
	jb srcva5		; not past end...
	mov di,offset source	; wrap buffer around
srcva5:	mov srcpnt,di		; update ptr
	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srcva7		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je srcva7		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe srcva7		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	nop
	nop
	nop			; ignore failure.
	mov xofsnt,true		; Remember we sent it.
srcva7:	ret

; The interrupt is for the 'B' port - transfer control to
; the original handler and hope for the best.
tranb:	pushf			; put flags on stack to simulate interrupt
	call	dword ptr [old1ser] ; call old handler
	ret			; and return

stxa:	mov	dx,mnstata
	mov	al,28H		; reset transmit interrupt
	out	dx,al
	ret

sstata:	mov	dx,mnstata
	mov	al,10H		; reset status interrupt
	out	dx,al
	ret

SERINT	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos
	ret
BEEP	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


term	proc	near
	mov si,ax		; this is source
	mov di,offset ourarg	; place to store arguments
	mov ax,ds
	mov es,ax		; address destination segment
	mov cx,size termarg
	cld
	rep movsb		; copy into our arg blk
	cmp inited,0		; inited yet?
	jz term1		; no, keep going
	test ourarg.flgs,scrsam	; do they want us to leave it alone?
	jnz term1		; yes, skip redisplay.
	call rstscr		; restore screen
term1:	mov inited,1		; remember inited
term2:	call prtchr
	jmp short term3		; have a char...
	nop
	nop
	jmp short term6		; no char, go on
term3:	and al,7fh		; turn off parity for terminal
	mov bx,offset ansbk1	; check 1st answerback
	call ansbak		; check for answerback
	mov bx,offset ansbk2	; maybe second answerback
	call ansbak
	mov bx,offset ansbk3
	call ansbak
	mov bx,offset ansbk4
	call ansbak
	cmp al,lf		; linefeed?
	jne term4		; no, keep going
	call scrprep		; need to save top line
	mov al,lf		; get char back
term4:	push ax
	int fastcon		; go print it
	pop ax
	test ourarg.flgs,capt	; capturing output?
	jz term5		; no, forget it
	push ax
	call ourarg.captr	; else call the routine
	pop ax
term5:	test ourflgs,fpscr	; print screen toggled on?
	jz term6		; no, keep going
	mov dl,al
	mov ah,lstout		; printer output
	int dos
term6:	mov di,6		; get level 1 character
	push es
	int firmwr
	pop es			; don't let firmware steal registers.
	cmp cl,0ffh		; character available?
	je term7		; no, do something else
	cmp cl,1		; maybe level 2 sequence around
	jne term2		; no, forget it
	mov di,2		; get level 2 character
	push es
	int firmwr
	pop es
	cmp cl,0ffh		; did we really get one?
	jne term2		; no, something strange happening.
	jmp term6		; else skip and keep trying.
term7:	test ax,fnkey		; function-type key?
	jnz term8		; yes, can't be escape character
	cmp al,ourarg.escc	; escape char?
	je term9		; yes, exit
term8:	call trnout		; perform necessary translations, output char
	jmp term2		; and loop around
term9:	call savscr		; save screen
	ret			; and return
term	endp

; enter with current terminal character in al, answerback ptr in bx.
; calls answerback routine if necessary.
; This can be used to make the emulator recognize any sequence.
ansbak	proc	near
	push	ax		; preserve this
	mov	si,[bx].anspt	; get current pointer
	cmp	al,[si]		; is it correct?
	jne	ansba1		; no, reset pointers and go on
	inc	[bx].anspt	; increment pointer
	dec	[bx].ansct	; decrement counter
	jnz	ansba2		; not done, go on
	push	bx
	call	[bx].ansrtn	; send answerback
	pop	bx
ansba1:	mov	ax,[bx].ansseq	; get original sequence
	mov	[bx].anspt,ax
	mov	al,[bx].anslen	; and length
	mov	[bx].ansct,al
ansba2:	pop	ax
	ret
ansbak	endp

; send the answerback message.
sndans	proc	near
	mov	si,offset ansret ; this is what we say
	mov	cx,ansrln	; length of same
sndan1:	lodsb			; get a byte
	mov	ah,al
	push	si
	push	cx
	call	outchr
	nop
	nop
	nop
	pop	cx
	pop	si
	loop	sndan1
	ret
sndans	endp

; enable alternate keypad mode
enaaky	proc	near
	mov	akeyflg,1	; remember alternate mode
	mov	keyptr,offset altktrn ; set correct translate table
	ret
enaaky	endp

; disable alternate keypad mode
deaaky	proc	near
	mov	akeyflg,0
	mov	keyptr,offset keytrn
	ret
deaaky	endp

; enter with char and flags in ax.  Does any necessary character translations,
; then outputs character
trnout	proc	near
	and ax,not cplk		; forget about caps lock key
	test ourarg.flgs,havtt	; any translate table?
	jz trnou2		; no, just output normally
	mov cx,ourarg.klen
	mov di,ourarg.ktab	; get redefined keys
	repne scasw		; look for this one
	jne trnou2		; not found, try something else
	sub di,ourarg.ktab
	sub di,2		; get index
	add di,ourarg.krpl	; get translation address
	mov si,[di]		; this is translation
	mov cl,[si]
	inc si			; pick up length, increment past it
	mov ch,0
	jcxz trnou6		; no translation, just return
trnou1:	lodsb			; get a char
	push si
	push cx
	call sndhst		; send the character
	pop cx
	pop si
	loop trnou1		; loop thru rest of translation
	ret			; and return
trnou2:	test ax,fnkey		; function key?
	jz trnou5		; no, keep going
	and ax,not fnkey	; turn off function bit.
	mov di,offset spckey	; our special keys
	mov cx,spclen		; length of special key table
	repne scasw		; look for it in our table
	jne trnou3		; not found, maybe arrow key...
	sub di,offset spckey+2	; get index
	call spchnd[di]		; call appropriate handler
	ret			; and return
trnou3:	mov di,offset arrkey	; look for an arrow-type key...
	mov cx,arrlen		; length of arrow key table
	repne scasb		; is it an arrow key?
	jne trnou4		; no, forget it
	sub di,offset arrkey+1	; get index into table
	shl di,1		; double for word index
	mov si,arrtrn[di]	; get translation
	mov cl,[si]
	inc si
	mov ch,0
	jmp trnou1		; go send translation
trnou4:	mov di,offset keypad	; look for a keypad key.
	mov cx,keypln
	repne scasb		; is it in keypad?
	jne trnou6		; no, forget it
	sub di,offset keypad+1
	add di,keyptr		; index into correct translation table
	mov al,[di]		; get translation
	cmp akeyflg,0		; in alternate keypad mode?
	je trnou5		; no, just send the char
	push ax			; else save the character
	mov al,esc
	call sndhst
	mov al,'O'
	call sndhst		; send prefix
	pop ax			; get the character back and fall thru...
trnou5:	call sndhst		; send the character
trnou6:	ret
trnout	endp

; handle the print screen key
prtscr	proc	near
	push	ds		; save data segment
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	mov	cx,slen		; # of lines on screen
	mov	bx,0		; current line #
prtsc1:	push	cx		; save counter
	push	bx		; and line ptr
	mov	si,ds:[latofs+bx] ; get ptr to line
	mov	cx,swidth	; max # of chars/line
	mov	di,offset prbuf	; print buffer
prtsc2:	lodsb			; get a byte
	or	al,al		; is it a null?
	jne	prtsc3		; no, go on
	mov	al,' '		; yes, replace by space
prtsc3:	stosb			; drop it off
	cmp	al,' '		; is it a space?
	je	prtsc4		; yes, go on
	mov	dx,cx		; else remember count at last non-space
prtsc4:	cmp	al,0ffH		; end of line?
	loopne	prtsc2		; continue if not end
	mov	cx,dx		; count at last non-space, plus 1
	neg	cx
	add	cx,swidth+1	; figure out # of chars to print
	mov	dx,offset prbuf
	push	ds		; save this temporarily
	mov	ax,es
	mov	ds,ax		; address data segment to print
	jcxz	prtsc5		; 0 length, keep going
	mov	bx,4		; standard printer device
	mov	ah,writef2	; write call
	int	dos		; write to the printer
prtsc5:	mov	ah,writef2
	mov	bx,4
	mov	dx,offset crlf
	mov	cx,2
	int	dos		; follow line with a crlf
	pop	ds
	pop	bx
	pop	cx		; restore counters
	add	bx,2		; point to next line
	loop	prtsc1		; and keep going
	pop	ds		; restore registers
	ret			; and return
prtscr	endp

; toggle print flag...
togprt	proc	near
	xor	ourflgs,fpscr	; toggle flag
	ret			; and return
togprt	endp

; Send a character to the host, handle local echo
sndhst	proc	near
	push ax			; save the character
	mov ah,al
	call outchr
	nop
	nop
	nop
	pop ax
	test ourarg.flgs,lclecho ; echoing?
	jz sndhs2		; no, exit
	cmp al,lf		; scrolling? ***
	jne sndhs1		; no, go on
	call scrprep
	mov al,lf
sndhs1:	int fastcon
sndhs2:	ret			; and return
sndhst	endp


; print a message to the screen.  Returns normally.
tmsg	proc	near
	mov	ah,prstr
	int	dos
	ret
tmsg	endp

; save the screen for later
savscr	proc	near
	push	ds
	mov	ax,scrseg
	mov	ds,ax
	mov	cx,slen		; # of lines to do
	mov	bx,0		; current line #
	mov	di,offset ourscr ; place to save screen
	mov	dx,offset ourattr ; and to save attributes
savsc1:	push	cx		; save current count
	mov	si,ds:[latofs+bx] ; get line ptr
	mov	cx,swidth	; # of chars/line
	rep	movsb		; copy it out
	mov	si,ds:[latofs+bx]
	add	si,1000H	; this is where attributes start
	xchg	dx,di		; this holds attribute ptr
	mov	cx,swidth	; # of attrs to move
	rep	movsb
	xchg	dx,di
	pop	cx		; restore counter
	add	bx,2		; increment line ptr
	loop	savsc1		; save all lines and attributes
	pop	ds
	call	savpos		; might as well save cursor pos
	ret
savscr	endp

; restore the screen saved by savscr
rstscr	proc	near
	call	cmblnk		; start by clearing screen
	mov	si,offset ourscr ; point to saved screen
	mov	dx,offset ourattr ; and attributes
	mov	cx,slen		; # of lines/screen
	mov	bx,101H		; start at top left corner
rstsc1:	push	bx
	push	cx
	push	si		; save ptrs
	push	dx
	mov	ax,si		; this is source
	call	prlina		; print the line
	pop	dx
	pop	si
	pop	cx
	pop	bx
	add	si,swidth	; point to next line
	add	dx,swidth	; and next attributes
	inc	bx		; address next line
	loop	rstsc1		; keep restore lines
	call	rstpos		; don't forget position
	ret
rstscr	endp

; circular buffer management for screen.
; for these to work correctly, the buffer size MUST be a multiple
; of the screen width.

; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax.
putcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	mov	di,[bx].pp		; pick up buffer ptr
	add	di,swidth		; increment to next avail slot
	cmp	di,[bx].bend		; past end?
	jb	putci1			; no, leave alone
	mov	di,[bx].orig		; else start at beginning
putci1:	mov	[bx].pp,di		; update ptr
	mov	si,ax			; this is source
	mov	cx,swidth
	rep	movsb			; copy into buffer
	cmp	[bx].lcnt,npgs*slen	; can we increment it?
	jae	putci2			; no, keep going
	inc	[bx].lcnt		; else count this line
putci2:	pop	dx
	pop	cx
	pop	di
	pop	si			; restore registers
	ret
putcirc	endp

; get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; pass the buffer structure in bx, the buffer to copy the line into
; in ax.
getcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	cmp	[bx].lcnt,0		; any lines in buffer?
	jne	getci1			; yes, ok to take one out.
	stc				; else set carry
	jmp	short getcir3		; and return
getci1:	mov	si,[bx].pp		; this is source
	mov	di,ax			; this is dest
	mov	cx,swidth		; # of chars to copy
	rep	movsb
	mov	si,[bx].pp		; get ptr again
	sub	si,swidth		; move back
	cmp	si,[bx].orig		; compare to origin
	jae	getcir2			; still in range, continue
	mov	si,[bx].bend		; else use end of buffer
	sub	si,swidth-1		; minus length of a piece
getcir2:mov	[bx].pp,si		; update ptr
	dec	[bx].lcnt		; decrement # of lines in buffer
	clc				; make sure no carry
getcir3:pop	dx
	pop	cx
	pop	di
	pop	si
	ret
getcirc	endp

; prepares for scrolling by saving the top line in topbuf.
scrprep	proc	near
	push	ds		; preserve data segment
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	cmp	byte ptr [ds:csrlin],slen ; are we at the bottom?
	pop	ds		; restore in case we're returning...
	jne	scrpr1		; no, don't save it
; alternate entry that doesn't check if we're on the bottom row.
savtop:	push	es
	push	ds
	mov	ax,ds
	mov	es,ax
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	mov	si,ds:word ptr [l1ptr] ; get ptr to top line
	mov	di,offset tlbuf	; this is where it goes
	mov	cx,swidth	; # of bytes to copy
	rep	movsb		; get the top line
	pop	ds
	pop	es		; restore segments
	mov	bx,offset topbuf ; top buffer ptr
	mov	ax,offset tlbuf	; this is where line is now
	call	putcirc		; put into circular buffer
scrpr1:	ret			; and return
scrprep	endp

; get the screen's bottom line into the buffer in ax.
getbot	proc	near
	push	es
	push	ds
	push	ax
	mov	ax,ds
	mov	es,ax
	mov	ax,scrseg
	mov	ds,ax
	mov	si,ds:word ptr [llptr] ; get ptr to bottom line
	pop	di		; destination is on stack
	mov	cx,swidth	; # of bytes to copy
	rep	movsb		; get the top line
	pop	ds		; restore segments
	pop	es
	ret
getbot	endp

; handle the previous screen button...

prvscr	proc	near
	mov	ax,offset tlbuf
	mov	bx,offset topbuf
	call	getcirc
	jc	prvsc3		; no lines, forget it
	call	savpos		; save cursor position
	mov	ax,offset botbuf ; place to put screen
	mov	bx,slen		; else just use last line on screen
	mov	dx,-1		; move backwards
	call	rolscr		; save current screen
	call	cmblnk		; clear screen
	mov	cx,slen		; # of lines per screenfull
prvsc1:	mov	bl,cl		; this is current line
	mov	bh,1		; this is column
	mov	ax,offset tlbuf ; where to get the line from
	push	cx		; save count
	call	prlin		; put the line on the screen
	mov	ax,offset tlbuf
	mov	bx,offset topbuf
	call	getcirc		; get another line
	pop	cx
	jc	prvsc2		; no more, exit loop
	loop	prvsc1		; loop for all lines
prvsc2:	call	rstpos		; restore screen position
prvsc3:	ret			; and return
prvscr	endp

; handle the next screen button...

nxtscr	proc	near
	mov	ax,offset tlbuf
	mov	bx,offset botbuf
	call	getcirc		; get a line from the bottom
	jc	nxtsc3		; no lines, forget it
	call	savpos		; save cursor pos
	mov	ax,offset topbuf ; place to put screen
	mov	bx,1		; start with first line
	mov	dx,1		; move backwards
	call	rolscr		; save current screen
	call	cmblnk		; clear screen
	mov	cx,slen		; # of lines per screenfull
nxtsc1:	mov	bl,slen+1
	sub	bl,cl		; this is current line
	mov	bh,1		; this is column
	mov	ax,offset tlbuf ; where to get the line from
	push	cx		; save count
	call	prlin		; put the line on the screen
	mov	ax,offset tlbuf	; where to put the next line
	mov	bx,offset botbuf
	call	getcirc		; try to get another
	pop	cx
	jc	nxtsc2		; no more, break loop
	loop	nxtsc1		; loop for all lines
nxtsc2:	call	rstpos		; restore cursor position
nxtsc3:	ret			; and return
nxtscr	endp

; save a screen by rolling them into a circular buffer.
; enter with ax/ circular buffer ptr, bx/ first line to get
; dx/ increment

rolscr	proc	near
	shl	dx,1		; double increment for word ptr
	dec	bx		; ptr starts at 0
	shl	bx,1		; convert to word ptr
	mov	cx,slen		; # of lines to save
rolsc1:	push	cx
	push	dx
	push	bx
	push	ax
	push	ds
	mov	ax,scrseg
	mov	ds,ax		; address screen
	mov	si,ds:[latofs+bx] ; get current line
	mov	di,offset rlbuf ; place to put it
	mov	cx,swidth	; # of bytes to move
	rep	movsb		; get the lne
	pop	ds		; restore segment
	pop	bx		; this is desired circ buffer ptr
	mov	ax,offset rlbuf ; this is where the line is
	call	putcirc		; save in circular buffer
	mov	ax,bx		; put buffer ptr back where it belongs
	pop	bx		; get line pos back
	pop	dx		; and increment
	pop	cx		; don't forget counter
	add	bx,dx		; move to next line
	loop	rolsc1		; loop thru all lines
	ret			; and return
rolscr	endp

; move screen down a line, get one previous line back

prvlin	proc	near		; get the previous line back
	mov	ax,offset tlbuf	; place to put line temporarily
	mov	bx,offset topbuf ; where to get lines from
	call	getcirc		; try to get a line
	jc	prvli1		; no more, just return
	mov	ax,offset blbuf	; place for bottom line
	call	getbot		; fetch bottom line
	mov	ax,offset blbuf
	mov	bx,offset botbuf
	call	putcirc		; save in circular buffer
	call	savpos		; save cursor position
	mov	dx,offset topdwn ; home, then reverse index
	call	tmsg
	mov	ax,offset tlbuf	; point to data
	mov	bx,0101H	; print line at top of screen
	call	prlin		; print the line
	call	rstpos		; restore cursor position
prvli1:	ret			; and return
prvlin	endp

; move screen up a line, get one bottom line back
nxtlin	proc	near
	mov	ax,offset blbuf	; place to put line temporarily
	mov	bx,offset botbuf ; where to get lines from
	call	getcirc		; try to get a line
	jc	nxtli1		; no more, just return
	call	savtop		; save one line off of top
	call	savpos		; save cursor position
	mov	dx,offset botup ; go to bottom, then scroll up a line
	call	tmsg
	mov	ax,offset blbuf	; point to data
	mov	bx,0100H + slen	; print at bottom line
	call	prlin		; print the line
	call	rstpos		; restore cursor position
nxtli1:	ret			; and return
nxtlin	endp

; save cursor position
savpos	proc	near
	mov	dx,offset curinq ; where is the cursor?
	call	tmsg
	mov	posbuf,esc	; put an escape in the buffer first
	mov	di,offset posbuf+1
savpo1:	mov	ah,8		; read, no echo
	int	dos
	cmp	al,'R'		; end of report?
	je	savpo2		; yes
	stosb			; no, save it
	jmp	savpo1		; and go on
savpo2:	mov	al,'H'		; this ends the sequence when we send it
	stosb
	mov	byte ptr [di],'$' ; need this to print it later
	ret			; and return
savpos	endp

; restore the position saved by savpos
rstpos	proc	near
	mov	dx,offset posbuf
	call	tmsg		; just print this
	ret			; and return
rstpos	endp

; print a ff-terminated line at most swidth long...  Pass the line in ax.
; cursor position should be in bx.
; prlina writes attributes as well, which should be passed in dx.
prlin	proc	near
	mov	bp,2		; print characters only
	jmp	short prli1
prlina:	xor	bp,bp		; 0 means print attributes as well.
prli1:	push	es		; this trashes es!!!
	mov	si,ax		; better place for ptr
	mov	di,ax		; need it here for scan
	mov	cx,swidth	; max # of chars in line
	mov	al,0ffh		; this marks the end of the line
	repne	scasb		; look for the end
	jne	prli2		; not found
	inc	cx		; account for pre-decrement
prli2:	neg	cx
	add	cx,swidth	; figure out length of line
	jcxz	prli3		; 0-length line, skip it.
	mov	ax,2		; writing characters
	mov	bp,ds		; wants segment here
	mov	di,14H		; fast write to screen
	int	firmwr		; pos is in bx, char ptr in si
prli3:	pop	es		; restore register
	ret			; and return
prlin	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
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxrb.asm
	/bin/echo -n '	'; /bin/ls -ld msxrb.asm
fi
/bin/echo 'Extracting msxsys.doc'
sed 's/^X//' <<'//go.sysin dd *' >msxsys.doc

               SPECIFICATION FOR KERMIT SYSTEM-DEPENDENT MODULES

                      by Jeff Damens, Columbia University

All the system-independent global data structures used in Kermit-MS are defined
in the file MSDEFS.H.

The routine MSXxxx.ASM contains system-dependent support for system xxx, except
for terminal emulation, which is in MSXxxx.ASM, described below.

The  routines  in the MSX module may change any registers but the stack pointer
and segment registers, unless otherwise noted.  A routine that  returns  via  a
RET  instruction is said to return normally; a routine that skip returns is one
that returns to three bytes past the normal return address.

Global variables that must be defined in the system-dependent module:

XXOFSNT          byte.  This should be set to a non-zero value if we  are  doing
                flow  control  and  have  sent  an XOFF character to the remote
                host, zero otherwise.

MACHNAM         byte.  A  $-terminated  string  identifying  the  machine  this
                version of Kermit is for; it is printed when Kermit starts up.

SETKTAB         byte.  A keyword table associating terminal key names to 16-bit
                scan  code  values, used in the set key command.  If the kermit
                version can accept arbitrary decimal values as scan codes,  the
                word "SCAN" should appear in the table with a scan value of -1.
                If  key  redefinition is not implemented, the first byte of the
                table should be a zero.

SETKHLP         byte.  A $-terminated string to be printed when ? is  typed  in
                the  SET KEY command.  This is usually simply a list of the key
                names in  SETKTAB.    SETKHLP  must  be  defined  even  if  key
                redefinition  is not implemented, to satisfy the linker; if key
                redefinition  is  not  implemented,  SETKHLP  will   never   be
                displayed.

COUNT           word.   The number of characters in the serial input buffer, if
                known.  This is how Kermit knows to send an XON if  the  serial
                handler  has  sent an XOFF.  If the number of characters in the
                buffer isn't known, COUNT should be 0.

These are the required entry points for the system dependent  dependent  module
MSXxxx.ASM.

SERINI

Parameters      None.

Returns         Normally, no return value.

Description     Perform  any initialization that must be done before the serial
                port can  be  used,  including  setting  baud  rate,  interrupt
                vectors,  etc.  Parity and baud rate should be set according to
                the values in the PORTINFO structure.   The  external  variable
                PORTVAL  points to the PORTINFO structure for the current port.
                Calling SERINI more than once without an  intervening  call  to
                SERRST should have no effect.


SERRST

Parameters      None.

Returns         Normally, no return value.

Description     Undoes  any  initialization done by SERINI, including resetting
                the serial port, restoring any  interrupt  vectors  changed  by
                SERINI,   etc.     Calling  this  more  than  once  without  an
                intervening call to SERINI should be harmless.


CLRBUF

Parameters      None.

Returns         Normally, no return value.

Description     Remove and discard from the  serial  port's  input  buffer  any
                characters  sent by the remote host that have not yet been read
                by Kermit, and set COUNT to 0.  This  is  used  before  a  file
                transfer  to flush NAK's that accumulate in the buffer when the
                remote host is in server mode.


OUTCHR

Parameters      A character in AH.

Returns         Skip returns if the character  has  been  transmitted;  returns
                normally  if  the character can not be transmitted because of a
                hardware error.

Description     Sends the character in AH out  the  currently  selected  serial
                port.    OUTCHR  can  assume  that SERINI will have been called
                previously. OUTCHR should call the external  routine  DOPAR  to
                set  the parity of the character if the communications hardware
                doesn't automatically set  parity.    Flow  control  should  be
                honored;  the external variable PORTVAL contains a pointer to a
                PORTINFO structure (as  defined  in  MSDEFS.H)  containing  the
                current flow control definitions.


COMS

Parameters      None.

Returns         Normally   if  a  parse  error  is  encountered,  skip  returns
                otherwise.

Description     Called by the SET PORT command.  On  a  machine  with  multiple
                serial  ports,  COMS  should  parse for the name or number of a
                serial port and make that the port used by succeeding calls  to
                SERINI, PRTCHR, OUTCHR, and SERRST.  It should set the external
                variable   PORTVAL  to  point  to  one  of  the  external  port
                structures  PORT1  or  PORT2,  and  set  COMFLG  in  the  FLAGS
                structure to 1 for port one, 0 for port 2.  For implementations
                that  use  only one serial port, COMS should print a message to
                that effect and skip return.


VTS

Parameters      None.

Returns         Normally  if  a  parse  error  is  encountered,  skip   returns
                otherwise.

Description     Parses  for  an  ON  or  OFF,  sets HEATH-19 emulation while in
                terminal emulation appropriately.  The VTFLG field of the FLAGS
                structure should be set non-zero if HEATH-29 emulation  is  on,
                zero  otherwise.  If HEATH-19 emulation is not done, VTS should
                print a message and skip return.


DODEL

Parameters      None.

Returns         Normally, no return value.

Description     Erases the character immediately to the left of the cursor from
                the screen, then backs up the cursor.


CTLU

Parameters      None.

Returns         Normally, no return value.

Description     Move the cursor to the left margin, then clear the line.


CMBLNK

Parameters      None.

Returns         Normally, no return value.

Description     Clears the screen and homes the cursor.


LOCATE

Parameters      None.

Returns         Normally, no return value.

Description     Homes the cursor.


LCLINI

Parameters      None.

Returns         Normally, no return value.

Description     Performs any system-dependent initialization required  by  this
                implementation.


PRTCHR

Parameters      None.

Returns         Normally,  with  the next character from the currently selected
                serial port in AL.  Skip returns if no character is available.

Description     Reads the next character from the current serial port.   PRTCHR
                can assume SERINI has been called previously, and should handle
                flow control correctly.


DOBAUD

Parameters      None.

Returns         Normally, no return value.

Description     Sets  the baud rate for the current port.  The baud rate should
                be obtained from the BAUD  field  of  the  PORTINFO  structure,
                pointed to by the external variable PORTVAL.


CLEARL

Parameters      None.

Returns         Normally, no return value.

Description     Clears from the cursor to the end of the current line.


DODISK

Parameters      None.

Returns         Normally, no return value.

Description     Sets  the external variable DRIVES to the number of disk drives
                attached to the machine.


GETBAUD

Parameters      None.

Returns         Normally, no return value.

Description     Store current baud rate of the currently selected port  in  the
                BAUD  field of the current PORTINFO structure, which is pointed
                to by PORTVAL.  If the baud rate is to default to a  particular
                value,  this  routine  can store that value into the BAUD field
                instead.


BEEP

Parameters      None.

Returns         Normally, no return value.

Description     Rings the terminal bell.


PUTHLP

Parameters      A pointer to a string in AX.

Returns         Normally, no return value.

Description     Writes the null-terminated string given in AX to the  terminal.
                This  is used to display help and status messages.  The IBM and
                Rainbow versions write the string in a reverse video box.


PUTMOD

Parameters      A pointer to a string in AX.

Returns         Normally, no return value.

Description     Writes the null-terminated string given in AX to the last  line
                of the screen, in inverse video if possible.


CLRMOD

Parameters      None.

Returns         Normally, no return value.

Description     Clears the line written by PUTMOD.


POSCUR

Parameters      Row in DH, column in DL.

Returns         Normally, no return value.

Description     Positions  the  cursor to the row and column given in DX.  Rows
                and columns both originate at 0 (not 1!).


SENDBR

Parameters      None.

Returns         Normally, no return value.

Description     Send a break to the current serial port.


SHOWKEY

Parameters      Pointer to a terminal argument block in AX (see TERM below).

Returns         Normally, with a string pointer in AX and  the  length  of  the
                string in CX.

Description     Called  by the SHOW KEY command.  Reads a key from the terminal
                and  returns  a  string   containing   implementation-dependent
                information  about  the  key.  In  the  usual  case, the string
                contains the key's (machine-dependent) scan code, and the key's
                definition (if any) from the  terminal  argument  block.    The
                length  of  the  returned string should be returned in CX.  The
                string may contain any characters; unprintable characters  will
                be  quoted  when  the string is printed.  If the implementation
                does not support key redefinition, SHOWKEY may return a  static
                string saying so.


TERM

Parameters      Pointer to terminal argument block in AX.

Returns         Normally, no return value.

Description     Do  terminal  emulation,  based  on  argument  block  described
                below...



The terminal emulator is  supplied  in  the  file  MSYxxx.ASM.    The  terminal
argument block passed to the terminal emulator has the following fields:

FLGS            Byte containing flags.  Flags are:

                SCRSAM (80H)    If   on,   the   terminal   emulator  shouldn't
                                re-display the screen when entered.

                CAPT (40H)      Capture output.  If on, the routine  passed  in
                                field  CAPTR is called with each character sent
                                to the screen.

                EMHEATH (20H)   Emulate a Heath-19 terminal if on.

                HAVTT (10H)     A key redefinition table is present.

                TRNCTL (08H)    Print control character X  as  ^X  (useful  for
                                debugging).

                MODOFF (04H)    Do not display emulator mode line if on.

                LCLECHO (01H)   Echo  keyboard  characters  on  the  screen  in
                                addition to sending them to the port.

PRT             Port to use for terminal emulation, used  only  in  mode  line.
                This is just a copy of COMFLG in FLAGS.

COLS            Number of columns on screen.

ROWS            Number of rows on screen.

CAPTR           Routine  to  call  to with each character sent to the screen if
                CAPT flag is on.  Characters are passed in AL.

BELLD           Bell divisor (used only on IBM).

KLEN            Number of keys in key redefinition table, if HAVTT flag is on.

KTAB            Address of key redefinition table.  The key redefinition  table
                is a table of KLEN 16-bit scan codes.  Each (machine dependent)
                scan code represents a key that is redefined.

KRPL            Address  of  key  replacement table.  The key replacement table
                parallels the key redefinition table given in KTAB.  Entries in
                the replacement table are  16-bit  pointers  to  redefinitions.
                Each  redefinition  has  a  one-byte  length,  followed  by the
                definition.

ESCC            Escape character (single byte).  When this character  is  typed
                to the emulator, it should return.

BAUDB           byte.    Bits  describing the baud rate so it can be printed on
                the mode line.  This is  a  copy  of  the  BAUD  field  in  the
                PORTINFO  structure.    Currently  used  only  on the IBM.  See
                MSDEFS.H for possible values.

PARITY          byte.  Current parity to print on the mode line.    This  is  a
                copy  of PARFLG in the PORTINFO structure.  Currently used only
                on the IBM.  See MSDEFS.H for possible values.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxsys.doc
	/bin/echo -n '	'; /bin/ls -ld msxsys.doc
fi



More information about the Comp.sources.unix mailing list