; ************ START MSXAPC.ASM ***********************************

; Kermit system dependent module for NEC Advanced Personal Computer (APC)
; Ron Blanford, University of Washington, August 1984

; Modified to get key scan codes directly from fifo buffer in IO.SYS
; This version works with MS-DOS Versions 2.00 (000) and 2.11 (001); it 
; should work with future MS-DOS revisions so long as NECIS maintains
; the configuration table in IO.SYS -  the thing most apt to fail is the 
; key autorepeat because kbrepflg is furthest from a hook in config table.
; If this happens the equate for offset of kbrepflg will need adjusting. 
; Ian Gibbons, University of Hawaii, 10/26/84

; Fixed incorrect timer command port assignment so that program will run
;   under MS-DOS 2.00 as well as 2.11.
; Added autodetermination of color/mono using crttype byte in IO.SYS; this
;   works only under DOS 2.11 (and future above ?)
; IG 10/28/84
; Added direct CRT I/O handling through BIOS in order to increase speed and
;  flexibility of help menus. It gives only an imitation window but it works
;  fairly well.
; IG 10/31/84
; Added simplified mode line data to the [Connecting to host...]" line which
;  has been moved here from MSTERM in order to have more system dependent 
;  control.
; IG 11/4/84
; Make unredefined break/stop key act as a scroll/noscroll key in telnet by
;  sending alternately a ^S and a ^Q.
; IG 11/5/84
; By now code for this MSXAPC module could well be divided into separate
;  transmission and terminal modules, but I don't want to spend more time
;  on it. It's not as readable as it might be, but so far as I know
;  everything works.IG 11/6/84
 
	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, lclini, lclrst, prtchr, dobaud,
	public	discon, clearl, dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, term, machnam, setktab, setkhlp, showkey
	include msdefs.h

false	equ	0
true	equ	1

BIOS	EQU	0DCH		; NEC-APC BIOS interrupt call
fifoclr equ	6		; clears keyboard fifo buffer
crtcmd	equ	7		; Direct CRT I/O command call

; port assignments for 8251 serial controllers

;		Standard interface

mndata	equ	30H		; Data port (read/write)
mnst1a	equ	32H		; Status port (when read)
mncmda	equ	32H		; Command port (when written)
mnst2a	equ	34H		; Alternate status port (when read)
mnmska	equ	34H		; Mask port (when written)
mntdca	equ	36H		; Transmit disable port (write only)

;		Optional (H14) interface

mndatb	equ	31H		; Data port (read/write)
mnst1b	equ	33H		; Status port (when read)
mncmdb	equ	33H		; Command port (when written)
mnst2b	equ	35H		; Alternate status port (when read)
mnmskb	equ	35H		; Mask port (when written)
mntdcb	equ	37H		; Transmit disable port (write only)

; Status bits from mnst1

txrdy	equ	01H		; Bit for output ready.
rxrdy	equ	02H		; Bit for input ready.

; Command values for mncmd

ccmd	equ	37H		; RTS & DTR high, RX & TX enabled, reset ERR
cbrk	equ	08H		; break enabled
cmode	equ	40H		; enable mode reset
mmode	equ	4EH		; 16x rate, 8 data, no parity, 1 stop

; Mask values for mnmsk

txmsk	equ	01H		; disables transmit ready interrupt
rxmsk	equ	02H		; disables receive ready interrupt
tbemsk	equ	04H		; disables transmit buffer empty interrupt


; port assignments for 8253 timers

;		Standard interface

tmdata	equ	2BH		; data port
tmcmda	equ	2FH		; command port  (Was 27H Ian 10/27/84) 

;		Optional (H14) interface

tmdatb	equ	61H		; data port
tmcmdb	equ	67H		; command port

; values for tmcmd which select timer channel and mode

tmsela	equ	76H		; Channel 1, mode 3 (standard port)
tmselb	equ	36H		; Channel 0, mode 3 (optional port)

; Timer information for current port selection

tmrinfo	struc
tmdat	dw	0		; data port
tmcmd	dw	0		; command port
tmsel	db	0		; byte which selects channel and mode
tmrinfo	ends


; port assignments for 8259 interrupt controllers

;		Standard interface

intcmda	equ	20H		; Command port (master controller)
intmska	equ	22H		; Mask port
ictmsk	equ	08H		; Timer interrupt mask (to master)
icsmska	equ	02H		; Standard serial interrupt mask (to master)
icsvcta equ	11H		; Interrupt vector for standard interface

;		Optional (H14) interface

; The interrupt request vector for the optional (H14) serial interface is
; jumper-selectable to any of vectors IR2, IR5, IR8, or IR12.  NEC recommends
; that IR8 be used, so that has been selected as the default here.  To use
; any of the other vectors, set the following conditionals appropriately.
; Only one of the following should be true:

IR2	equ	false		; interrupt vector 2
IR5	equ	false		; interrupt vector 5
IR8	equ	true		; interrupt vector 8
IR12	equ	false		; interrupt vector 12

	IF IR2
intcmdb equ	20H		; Command port (master controller)
intmskb equ	22H		; Mask port
icsmskb equ	04H		; Interrupt mask
icsvctb equ	12H		; Interrupt table index
	ENDIF

	IF IR5
intcmdb equ	20H		; Command port (master controller)
intmskb equ	22H		; Mask port
icsmskb equ	20H		; Interrupt mask
icsvctb equ	15H		; Interrupt table index
	ENDIF

	IF IR8
intcmdb equ	28H		; Command port (slave controller)
intmskb equ	2AH		; Mask port
icsmskb equ	02H		; Interrupt mask
icsvctb equ	19H		; Interrupt table index
	ENDIF

	IF IR12
intcmdb equ	28H		; Command port (slave controller)
intmskb equ	2AH		; Mask port
icsmskb equ	20H		; Interrupt mask
icsvctb equ	1DH		; Interrupt table index
	ENDIF

crtinfo struc		; Structure for color/mono formatting info
nrmseq	db	esc,'[0m$'	; Default
invseq	db	esc,'[7m$000'	; Inverse (extra space for color)
bldseq	db	esc,'[17m$'	; Bold video
nrmcrt	dw	8080H		; Normal data for direct crt i/o
bldcrt	dw	9090H		; Inverse data for direct crt i/o	

crtinfo ends


icEOI	equ	20H		; generic end of interrupt for intcmd
kbfifosiz equ	64		; size of fifo buffer in IO.SYS


; miscellaneous constants

ctrlP	equ	10H		; ^P.
ctrl_q	equ	11H		; ^Q
ctrl_s	equ	13H		; ^S
printkey equ	00FFH		; Scan code of unshifted PRINT key.
brkstp	equ	0096H		; Scan code of unshifted break/stop key.
mntrgh	equ	bufsiz*3/4	; High XON/XOFF trigger = 3/4 of buffer full.

; 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.

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

machnam	db	'NEC APC$'
nyimsg	db	cr,lf,'Not implemented$'
badbd	db	cr,lf,'Unimplemented baud rate$'
bdkscn	db	cr,lf,'Unable to install key translate table'
	db	cr,lf,'Possibly incompatible MS-DOS version',cr,lf
	db	cr,lf,'(MS-KERMIT can be used without keyboard translation'
	db	' feature)',cr,lf,'$'
nokbtr	db	cr,lf,'No key translation table is installed$'
prtmsg	db	cr,lf,'You cannot redefine the PRINT key$'
escmsg	db	cr,lf,'You cannot redefine the current CTRL-ESCAPE key$'
hngcfm	db	cr,lf,'Please confirm DISCONNECT command. (Y/N)  $'
dismsg	db	cr,lf,'Disconnecting for 3 seconds',cr,lf,'$'
rcnmsg	db	cr,lf,'Reconnected',cr,lf,'$'
cnmsg	db	cr,lf,'Connecting to host at '
cnmsgb	db	'     '
	db	' baud on port '
cnmsgp	db	' ',cr,lf,'(Type $'
cnmsg1	db	' C  to return to PC)',cr,lf,lf,lf,'$'
crlf	db	cr,lf,'$'
delstr  db      BS,' ',BS,'$'	; Delete string.
clrlin  db      cr,'$'		; Clear line (just the cr part).
ceolseq	db	esc,'[K$'	; Clear to end of line
cpseq	db	esc,'=rc'	; rc replaced by row and column before display
clrseq	db	01EH,01AH,'$'	; Home cursor and clear screen
lstpos	dw	0		; column position for printer echoing

; Storage for color and mono formatting strings

nrmcol	db	esc,'[0m$'
invcol	db	esc,'[3;21m$'	; Yellow with green overline
bldcol	db	esc,'[21m$'	; Yellow
ncrtcol	dw	8080H		; Green
bcrtcol dw	0A0A0H		; Yellow  

;nrmmon	db	esc,'[0m$'
;invmon	db	esc,'[7m$000'	; Inverse video
;bldmon	db	esc,'[17m$'	; Bold video
;ncrtmon	dw	8080H		; Normal green
;bcrtmon dw	9090H		; Inverse video	

formdat	crtinfo <>
formdtl	db	$-formdat

nocur	db	esc,'[>5h$'	; Disable cursor
recur	db	esc,'[>5l$'	; Reenable cursor
storcur	db	esc,'[s$'	; Store cursor position
rstcur	db	esc,'[u$'	; Restore cursor position
upcur	db	esc,'[A$'	; Move cursor up one line
savcur	dw	0 		; storage for cursor position
msglns	db	0		; No of lines in help message
rnoc	dw	0		; Raw NOC for screen write

; Data area for direct CRT I/O

EVEN				; Force alignment to word boundary
dispadr db	2000 dup (0)	; Storage for string data
attradr db	2000 dup (0)	; Storage for attribute data 

; Command block for direct CRT I/O

cmd	db	0		; CRT command number
la	db	0		; Line address   (0-24 binary)
ca	db	0		; Column address (0-79 binary)
noc	dw	0		; Number of chars (0-2000 binary)
dispptr dw	dispadr		; Pointer to string data block
dispseg	dw	datas		;
attrptr dw	attradr		; Pointer to attribute data block
attrseg dw	datas

ourarg	termarg	<>
modem	mdminfo	<mndata,mnst1a,mncmda,0,0,0,0>
timer	tmrinfo	<tmdata,tmcmda,tmsela>

ourflgs	db	0		; Flags for telnet options
fprint	equ	80H		;   echo screen output to printer
movcur	equ	40H		;   cursor moved - needs resetting
kbtrflg	equ	20H		;   local flag showing if kb trans.is enabled
autorepflg equ	10H		;   is this an autorepeat cycle 
inited	equ	8H		;   are we initiating first call to term
tlnxof	equ	4H		;   have we sent a ^S in telnet with brk/stp
savscn	dw	0		; save last key-in for auto repeat if needed
escscan dw	0		; scan code for current ctrl-escape key

oldsera	dw	?		; old serial handler for standard port
oldsega	dw	?		; segment of above
oldmska	db	?		; old interrupt controller mask
portina	db	0		; Has comm port been initialized.

oldserb	dw	?		; old serial handler for optional port
oldsegb	dw	?		; segment of same.
oldmskb	db	?		; old interrupt controller mask
portinb	db	0		; Has comm port been initialized.

dosseg	dw	40H		; Segment  to read IO.SYS
zero	dw	0		; Use to load segment regs for low core

; Space for addresses in IO.SYS to be calculated at run time in 'lclini'

configptr equ	3H		; Offset of pointer to base of config table
configbas dw	0		; base address of config table in IO.SYS
fifobas dw	0		; equ	0A62H for DOS 2.11

; offsets from configbas
statlnptr equ	0CH
kbinptr   equ	2CH		; kbin addr = base of fifodata area (fifobas)
crtptr    equ	48H

; offsets from fifobas
kbin	equ	0			; fifobas
kbout	equ	1			; fifobas + 1
kbfifo	equ	2			; fifobas + 2
kbrepflg equ	0D9H			; fifobas + 0D9H

xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.

; 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.
	dw	80 DUP (?)	; local stack for interrupt processing
mnstk	dw	?
mnsp	dw	?		; remote stack info
mnsseg	dw	?

shkbuf	db	300 dup (?)	; room to display key definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1

setktab	db	24
	mkeyw	'BACKSPACE',9CH
	mkeyw	'F1',80H
	mkeyw	'F2',81H
	mkeyw	'F3',82H
	mkeyw	'F4',83H
	mkeyw	'F5',84H
	mkeyw	'F6',85H
	mkeyw	'F7',86H
	mkeyw	'F8',87H
	mkeyw	'F9',88H
	mkeyw	'F10',89H
	mkeyw	'F11',8AH
	mkeyw	'F12',8BH
	mkeyw	'F13',8CH
	mkeyw	'F14',8DH
	mkeyw	'F15',8EH
	mkeyw	'F16',8FH
	mkeyw	'F17',90H
	mkeyw	'F18',91H
	mkeyw	'F19',92H
	mkeyw	'F20',93H
	mkeyw	'F21',94H
	mkeyw	'F22',95H
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Either keyname:  Backspace, F1, ...,F22',cr,lf
	db	      '  or   "SCAN" followed by scan code'
	db	' (given by SHOW KEY)',cr,lf,'$'

comptab	db	7
	mkeyw	'1',1
	mkeyw	'2',0
	mkeyw	'COM1',1
	mkeyw	'COM2',0
	mkeyw	'H14',0
	mkeyw	'OPTIONAL',0
	mkeyw	'STANDARD',1

bddat	label	word
	dw	0D30H		; 45.5 baud
	dw	0C00H		; 50 baud
	dw	0800H		; 75 baud
	dw	0574H		; 110 baud
	dw	0476H		; 134.5 baud
	dw	0400H		; 150 baud
	dw	0200H		; 300 baud
	dw	0100H		; 600 baud
	dw	0080H		; 1200 baud
	dw	0055H		; 1800 baud
	dw	004DH		; 2000 baud
	dw	0040H		; 2400 baud
	dw	0020H		; 4800 baud
	dw	0010H		; 9600 baud
	dw	0008H		; 19200 baud
	dw	0004H		; 38400 baud (not tested - may not work)

; some static data for mode line

unkbaud	db	' Unk '			; must be 5 chars...
baudn	db	' 45.5'			;		[g4 start]
	db	'  50 '
	db	'  75 '
	db	' 110 '
	db	' 135 '
	db	' 150 '
	db	' 300 '
	db	' 600 '
	db	' 1200'
	db	' 1800'
	db	' 2000'
	db	' 2400'
	db	' 4800'
	db	' 9600'
	db	'19200'		;		[g4 end]
baudnsiz  equ	15		; # of baud rates known (tbl size / 5)

datas	ends


code	segment	public
	extrn	comnd:near, dopar:near, escprt:near
	assume	cs:code,ds:datas

; local initialization routine, called by Kermit initialization.

LCLINI	PROC	NEAR
	cld
	mov flags.vtflg,0	; turn off heath emulation
	push es
	mov es,dosseg
	mov bx,configptr
	mov bx,es:[bx]		; Get offset of configuration table in IO.SYS
	mov configbas,bx	; store it
	mov cl,fifoclr		; Clear fifobuffer in case user has been 
	int bios		;  typing while program was loading
	mov bx,es:[bx].kbinptr	; Should be address of kbin
	mov cl,es:[bx]		; Confirm we're in right location	
	cmp cl,byte ptr es:1[bx] ; by showing kbin = kbout
	jne lclin1		; Fails for DOS 2.00 (no kbinptr)
	mov fifobas,bx		; Kbin is base of fifo area
	or ourflgs,kbtrflg	; Show we have a key tranlate table
	jmp short lclin2	; Localization finished
lclin1: mov bx,configbas	; DOS 2.00 does have pointer to status line
	mov bx,es:[bx].statlnptr ; Address of 'MS-DOS' in status line
	sub bx,6BH			; Work back to where kbin should be
	mov cl,es:[bx]			; Confirm we're in right location
	cmp cl,byte ptr es:1[bx] 	;  by showing kbin = kbout
	jnz lclin3		; Nothing else to try - report failure
	mov fifobas,bx		; We've made it!
	or  ourflgs,kbtrflg	; Show we have a key tranlate table
	xor ax,ax		; Set zero flag for mono format default
	jmp short lcli20
lclin2:	mov bx,configbas
	mov bx,es:[bx].crtptr		; Get address of crttype byte
	mov al,es:[bx]			; get the crttype byte
	test al,1			; Is it a color crt?
lcli20:	mov dx,ds			; DOS 2.00 has no crt data and 
	mov es,dx			;   and defaults to mono
	mov cl,formdtl			; Length of crt format block
	mov di,offset formdat		; Where we want data
	jnz lcli21			; No, it's color
	jmp short lclin4
lcli21: mov si,offset nrmcol		; Source of color data
	rep movsb			; Move data to working location
	jmp short lclin4
lclin3:	mov dx,offset bdkscn
	call tmsg
	and ourflgs,not kbtrflg		; Show no translate table enabled
lclin4:	mov dx,offset formdat.nrmseq	; set to our normal background color
	call tmsg
	pop es
	ret
LCLINI	ENDP

; Local reset routine, called upon exit from Kermit

LCLRST	PROC	NEAR
	ret
LCLRST	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.

; In this version, the complete untranslated key scan codes are obtained
; from the fifo buffer in IO.SYS. The fifo buffer pointers are then updated
; to show that the key has been read. Certain key scan codes which are
; intercepted in the kb interrupt routine give a blank or functional response
; to SHOW KEY  (eg FNC + CTRL + BREAK-STOP, PRINT, and CTRL + 0...9 )
; and these cannot be translated.

SHOWKEY	PROC	NEAR
	push es
	test ourflgs,kbtrflg
	jnz showk0
	push ax			; Keep stack balanced
	mov bx,ax
	and [bx].flgs,not havtt ; reset flag
	mov dx,offset nokbtr	; Inform no table installed and return
	jmp short shoerr

showk0:	push ax			; save the terminal argument block
	mov al,trans.escchr	; calculate scan code for ctrl-escape key
	add al,40H		; uncontrolify escape char
	mov ah,2		; control byte
	mov escscan,ax		; save it
showk1:	call inscan		; get key-in scan code from IO.SYS
	 jmp short showk1	; Nothing there yet - keep trying
	 nop
	call inckbo		; increment kbout pointer
	cmp al,printkey		; Is it the print key (any version) ?
	jnz show11
	mov dx,offset prtmsg	; If so complain
	jmp short shoerr
show11: cmp al,byte ptr escscan	; Is it current ctrl-escape key ?
	jnz show12
	test ah,02		; With ctrl + anything
	jz show12
	mov dx,offset escmsg	; Then complain
	jmp short shoerr
show12:	cld
	mov cx,ds
	mov es,cx
	push ax			; save scan code
	mov di,offset shkbuf	; move 'Scan code' message to buffer
	mov si,offset shkmsg
	mov cx,shkmln
	rep movsb
	call nout		; add scan code to buffer
	mov si,offset shkms1	; move 'Definition' message to buffer
	mov cx,shkm1ln
	rep movsb
	pop ax			; retrieve scan code
	pop bx			; and terminal argument block
	mov cx,[bx].klen	; length of translation table
	jcxz showk3		; no table, key not defined
	push di
	mov di,[bx].ktab	; get table address
	repne scasw		; look for scan code
	mov si,di
	pop di
	jne showk3		; not defined
	sub si,[bx].ktab	; compute entry offset in table
	sub si,2
	add si,[bx].krpl	; index to replacement
	mov si,[si]		; get its address
	mov cl,[si]		; get its length
	mov ch,0
	inc si
	rep movsb		; transfer replacement to display buffer
showk3:	mov ax,offset shkbuf	; return address of buffer in ax
	mov cx,di		; and length in cx
	sub cx,ax
	pop es
	ret
shoerr: call tmsg
	mov cx,0		; 
	pop ax			; get rid of junk
	pop es
	ret
SHOWKEY	ENDP

; copy numeric value from AX to ASCII buffer indicated by DI.  DI is updated.

NOUT	PROC	NEAR
	mov dx,0		; zero high word
	mov bx,10		; divide
	div bx
	push dx			; save remainder digit
	or ax,ax		; anything left?
	jz nout1		; no, start output phase
	call nout
nout1:	pop ax			; retrieve a digit
	add al,'0'		; make it ASCII
	stosb			; put it in buffer
	ret
NOUT	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:	pushf			; save current interrupt value
	cli			; disable interrupts while manipulating pointers
	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
	popf			; restore original interrupt flag
	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			;  ignore failure
	 nop
	 nop
	mov xofsnt,false	; remember we've sent an xon.
chkxo1:	pop bx			; restore register
	ret			; and return
CHKXON	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	PROC	NEAR
	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	sub 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	; 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,modem.mddat
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret
OUTCHR	ENDP

; Send a break out the current serial port.  Returns normally.

SENDBR	PROC	NEAR
	mov dx,modem.mdcom	; send to command port
	mov al,cbrk+ccmd	; add break to normal command
	out dx,al
	sub cx,cx		; wait a while
sndbr1:	loop sndbr1
	mov al,ccmd		; restore normal command
	out dx,al
	ret			; and return.
SENDBR	ENDP

DISCON	PROC	NEAR
	mov dx,offset hngcfm
	call besure		; Get confimation of command
	 jmp short discn1
	 nop
	mov dx,offset dismsg	; Say what we're doing
	call tmsg
	mov al,CCMD
	xor al,2		; Reset bit to drop DTR.
	mov dx,modem.mdcom
	out dx,al
	mov bx,05H		; Set outer counter  5 X --> 3sec.
pause2:	xor cx,cx
pause3:	push bx			; Waste time for 600ms.
	pop bx
	loop pause3		; Loop on inner loop.
	dec bx
	jnz pause2		; Loop on outer loop.
	or al,2			; Set bit to enable DTR again.
	out dx,al
	mov dx,offset rcnmsg
	call tmsg
discn1:	ret
DISCON	ENDP

BESURE	PROC	NEAR		; Receives addr of prompt in DX.
	call tmsg
	mov ah,conin
	int dos
	and al,137Q		; Convert to upper case if necessary. 
	cmp al,'Y'
	jz besur1		; We must return rskp for a 'Y'/'y'
	mov dx,offset crlf	; For any other character input send a cr/lf.
	call tmsg
	ret			; And return.
besur1:	jmp rskp
BESURE	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
	pushf			; save current interrupt value
	cli			; disable interrupts
	mov ax,offset source	; reset pointers to beginning of buffer
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	popf			; restore original interrupt value
	ret
CLRBUF	ENDP

; Set the baud rate for the current port, based on the value in the
; portinfo structure.  On entry, previous value of baud rate is saved in AX.
; Returns normally.

DOBAUD	PROC	NEAR
	mov bp,portval
	mov bx,ds:[bp].baud	;make sure new value is valid
	shl bx,1
	add bx,offset bddat
	cmp word ptr [bx],0FFH
	jne dobd0
	mov ds:[bp].baud,ax	;replace bad rate with previous value
	mov dx,offset badbd
	jmp tmsg
dobd0:	mov dx,timer.tmcmd	;timer command port
	mov al,timer.tmsel	;select proper channel and mode
	out dx,al
	mov ax,[bx]		;get timer initializer for this rate
	mov dx,timer.tmdat	;timer data port
	out dx,al		;output low byte
	mov al,ah
	out dx,al		;output high byte
	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,portval		; no way to determine baud rate on APC
	mov [bx].baud,B1200	;  so set default baud rate to 1200
	ret
GETBAUD	ENDP

; Set the mode for the current port.  This is part of the serial
; initialization routine.

DOMODE	PROC	NEAR
	mov dx,modem.mdcom	;send 3 zeros to command port to reset chip
	mov al,0
	out dx,al
	mov al,0
	out dx,al
	mov al,0
	out dx,al
	mov al,cmode		;enable mode setting
	out dx,al
	mov cx,100		;allow chip time to reset
mode1:	loop mode1
	mov al,mmode		;mode: 16x rate, 8 data, no parity, 1 stop
	out dx,al
	mov cx,100
mode2:	loop mode2
	mov al,ccmd		;RTS & DTR high, RX & TX enabled, reset errors
	out dx,al
	ret
DOMODE	ENDP

; Reassure user about connection to the host.  Tell him what escape
; sequence to use to return and the communications port and baud
; rate being used.   [19b]

DOMSG	PROC	NEAR
	mov	bx,offset ourarg	; get argument block
	mov	al,[bx].baudb		; get baud bits
	mov	si,offset unkbaud	; Assume unknown baud.
	cmp	al,baudnsiz		; too big?
	jnb	dmsg12			; yes, use default
	mov	cl,2			; each is 5 bytes long
	shl	al,cl			; 4 X
	add	al,[bx].baudb		; make 4+1 = 5
	mov	ah,0
	add	ax,offset baudn
	mov	si,ax
dmsg12:	mov	cx,5			; length of baud space
	mov	di,offset cnmsgb
	rep	movsb			; copy in baud rate
	mov	al,'1'
	cmp	ourarg.prt,1		; One means port 1
	je	dmsg15			; yes, keep going
	mov	al,'2'			; Zero means port 2
dmsg15:	mov	cnmsgp,al		; fill in port number
	mov dx,offset cnmsg
	call tmsg
	call escprt			; in MSSET
	mov dx,offset cnmsg1
	call tmsg
	ret
DOMSG	ENDP

; set the current port.

COMS	PROC	NEAR
	mov dx,offset comptab	;get port selection
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm		;get a confirmation
	call comnd
	 jmp comx
	 nop
	pop bx
	mov flags.comflg,bl	;save port selection
	cmp flags.comflg,1
	jne coms2
	mov ax,offset port1	;set to run on port 1
	mov portval,ax
	call resetb		;reset port 2, if in use
	call inita		;set up port 1
	ret
coms2:	mov ax,offset port2	;set to run on port 2
	mov portval,ax
	call reseta		;reset port 1, if in use
	call initb		;set up port 2
	ret
comx:	pop bx
	ret
COMS	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
	cmp flags.comflg,1
	jne seri2
	call resetb
	call inita
	ret
seri2:	call reseta
	call initb
	ret
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
	call reseta		;reset port 1
	call resetb		;reset port 2
	mov dx,offset recur	; Reenable cursor display
	call tmsg
	and ourflgs,not inited	; Reset init flag for term usage
	ret
SERRST	ENDP

; Local routine to initialize the standard serial port

INITA	PROC	NEAR
	cmp portina,1		; Did we initialize port already? [21c]
	je inita0		; Yes, so just leave. [21c]
	push es
	cli			; Disable interrupts
	mov ax,offset port1
	mov portval,ax
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*icsvcta]	; save standard port interrupt vector
	mov oldsera,ax
	mov ax,es:[4*icsvcta+2]
	mov oldsega,ax
	mov ax,offset serint	; point to our routine
	mov es:[4*icsvcta],ax	; point at our serial routine
	mov es:[4*icsvcta+2],cs	; our segment
	mov dx,intmska		; set up standard port...
	in al,dx
	mov oldmska,al		; save old master controller mask

;	NEC recommends that the timer interrupt be disabled during interrupt-
;	driven serial I/O, but this disables the clock display and keyboard
;	repeat.  I have not had any problems leaving it enabled, so I will
;	leave it alone here.  If problems develop, uncomment the following
;	line to disable timer interrupts. -- RonB

;	or al,ictmsk		; disable timer interrupt
	and al,not icsmska	; enable serial interrupt at master controller
	out dx,al
	mov dx,mnmska		; enable serial interrupt at port
	mov al,txmsk+tbemsk	; disable tx and tbe interrupts (enable rx)
	out dx,al
	mov dx,mntdca		; enable operation of serial port
	mov al,0
	out dx,al
	mov modem.mddat,mndata
	mov modem.mdstat,mnst1a
	mov modem.mdcom,mncmda
	mov timer.tmdat,tmdata
	mov timer.tmcmd,tmcmda
	mov timer.tmsel,tmsela
	call domode
	call dobaud
	mov portina,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer.
	sti			; Allow interrupts
	pop es
inita0:	ret
INITA	ENDP

; Local routine to initialize the optional (H14) serial port

INITB	PROC	NEAR
	cmp portinb,1		; Did we initialize port already? [21c]
	je initb0		; Yes, so just leave. [21c]
	push es
	cli			; Disable interrupts
	mov ax,offset port2
	mov portval,ax
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*icsvctb]	; save optional port interrupt vector
	mov oldserb,ax
	mov ax,es:[4*icsvctb+2]
	mov oldsegb,ax
	mov ax,offset serint	; point to our routine
	mov es:[4*icsvctb],ax	; point at our serial routine
	mov es:[4*icsvctb+2],cs	; our segment
	mov dx,intmskb		; set up optional port...
	in al,dx
	mov oldmskb,al		; save old master or slave controller mask
	and al,not icsmskb	; enable serial interrupt at controller
	out dx,al
	mov dx,mnmskb		; enable serial interrupt at port
	mov al,txmsk+tbemsk	; disable tx and tbe interrupts (enable rx)
	out dx,al
	mov dx,mntdcb		; enable operation of serial port
	mov al,0
	out dx,al
	mov modem.mdstat,mnst1b
	mov modem.mddat,mndatb
	mov modem.mdcom,mncmdb
	mov timer.tmdat,tmdatb
	mov timer.tmcmd,tmcmdb
	mov timer.tmsel,tmselb
	call domode
	call dobaud
	mov portinb,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer.
	sti			; Allow interrupts
	pop es
initb0:	ret
INITB	ENDP

; Reset standard serial port

RESETA	PROC	NEAR
	cmp portina,0		; Did we reset port already?
	je rsta0		; Yes, so just leave.
	push es
	cli			; Disable interrupts
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,oldsera		; Restore interrupt vector
	mov es:[4*icsvcta],ax
	mov ax,oldsega
	mov es:[4*icsvcta+2],ax
	mov dx,intmska		; restore old master controller mask
	mov al,oldmska
	out dx,al
	mov dx,mnmska		; disable serial interrupts at port
	mov al,txmsk+rxmsk+tbemsk
	out dx,al
	mov portina,0		; Remember port has been reset
	sti			; Allow interrupts
	pop es
rsta0:	ret
RESETA	ENDP

; Reset optional (H14) serial port

RESETB	PROC	NEAR
	cmp portinb,0		; Did we reset port already?
	je rstb0		; Yes, so just leave.
	push es
	cli			; Disable interrupts
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,oldserb		; Restore interrupt vector
	mov es:[4*icsvctb],ax
	mov ax,oldsegb
	mov es:[4*icsvctb+2],ax
	mov dx,intmskb		; restore old slave controller mask
	mov al,oldmskb
	out dx,al
	mov dx,mnmskb		; disable serial interrupts at port
	mov al,txmsk+rxmsk+tbemsk
	out dx,al
	mov portinb,0		; Remember port has been reset
	sti			; Allow interrupts
	pop es
rstb0:	ret
RESETB	ENDP


; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

SERINT	PROC  NEAR
	push ds			; save these on remote stack
	push ax
	mov ax,seg datas	; get our own data segment
	mov ds,ax
	mov mnsp,sp		; save remote stack information
	mov mnsseg,ss
	mov sp,offset mnstk	; switch to local stack
	mov ss,ax
	push es			; and save remaining registers
	push bp
	push di
	push si
	push dx
	push cx
	push bx
	mov es,ax
	call mnproc		; process the interrupt
	mov al,icEOI
	cmp flags.comflg,1	; If using standard port
	je intr1
	mov dx,intcmdb		;    or H14 vectored to master
	cmp dx,intcmda
	je intr1		;    only signal End of Interrupt to master,
	out dx,al		; otherwise signal to both slave and master.
intr1:	mov dx,intcmda
	out dx,al
	pop bx			; restore registers from stack
	pop cx
	pop dx
	pop si
	pop di
	pop bp
	pop es
	mov ax,mnsseg		; switch back to remote stack
	mov ss,ax
	mov ax,mnsp
	mov sp,ax
	pop ax
	pop ds
	iret

; handler for serial input

mnproc:	cld
	mov di,srcpnt		; get buffer pointer
	mov dx,modem.mdstat	; is data available?
	in al,dx
	test al,rxrdy
	jz mnpro7
	mov dx,modem.mddat	; read data
	in al,dx
	or al,al
	jz mnpro7		; Ignore nulls.
	cmp al,7FH		; Ignore rubouts, too.
	jz mnpro7
	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 mnpro4		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp ah,bl		; Is it an XOFF?
	jne mnpro3		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp short mnpro7
mnpro3:	cmp ah,bh		; Get an XON?
	jne mnpro4		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp mnpro7
mnpro4:	stosb
	cmp di,offset source + bufsiz
	jb mnpro5		; not past end...
	mov di,offset source	; wrap buffer around
mnpro5:	mov srcpnt,di		; update ptr
	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je mnpro7		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je mnpro7		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe mnpro7		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	 nop			;   ignore failure.
	 nop
	 nop
	mov xofsnt,true		; Remember we sent it.
mnpro7:	ret

SERINT	ENDP

; Dumb terminal emulator.  Anyone wishing to enhance it is encouraged
; to do so.

TERM	PROC	NEAR
	push es
	test ourflgs,inited	; Have we been here before
	jnz  term01		; if so, skip this stuff
	or  ourflgs,inited	; show we've been here
	test ourflgs,kbtrflg
	jnz term0
	mov bx,ax
	and [bx].flgs,not havtt ; If no table then reset flag
term0:	mov si,ax		; save argument block locally
	mov di,offset ourarg
	mov ax,ds
	mov es,ax
	mov cx,size termarg
	rep movsb
	mov al,trans.escchr	; Calculate scan code for cntrl-escape char
	add al,40H		; Uncontollify escape char
	mov ah,02H		; Control byte for escape scan code
	mov escscan,ax		; save it
	call domsg		; tell user how we're connecting.
term01: test ourflgs,movcur	 ; Do we need to reset cursor position	
	jz term1
	mov dx,offset cmd
	mov cx,savcur
	mov word ptr la,cx
	mov noc,0
	mov cmd,1		; Bios  move cursor call
	mov cl,crtcmd
	int bios
	and ourflgs, not movcur
term1:	call prtchr		; Serial port input processor
	 jmp short term2	;  ...have a char
	 nop
	jmp term4		; no char, continue
term2:	and al,7FH		; only use ASCII in terminal mode
	push ax
	mov dl,al
	mov ah,conout
	int dos			; display char
	pop ax
	test ourarg.flgs,capt	; are we capturing output?
	jz term3
	push ax
	call ourarg.captr
	pop ax
term3:	test ourflgs,fprint	; are we echoing to printer?
	jz term4
	call lstchr

term4:	test ourflgs,kbtrflg
	jz term50
	call inscan
	 jmp  short term1
	 nop
	cmp al,printkey		; All shifts of print key do special duty.
	jne term41
	cmp ah,0
	jne term40		; but toggle printer only if unshifted print
	xor ourflgs,fprint	; go toggle printer 
term40:	call inckbo		; increment kbout pointer
	jmp term1
term41:	cmp al,byte ptr escscan ; Is it current escape key
	jne  term42
	test ah,02		;  with ctrl + anything ?
	jz  term42
	call inckbo
	mov cx,100		; Delay for memory stability
trm420:	loop trm420
	jmp short termx		; it's ctrl-escape key so just return	
term42:	call trnout		; Returns rskp if char not sent.
	 jmp short term1	; Translation found and already sent.
	 nop			; no translation so move char via DCONIO
	cmp ax,brkstp		; is it unredefined/unshifted break/stop key
	jnz term50		; if not, just continue
	mov ax,ctrl_s		; get a ^S ready
	test ourflgs,tlnxof	; have we already sent a ^S ?
	jz term43		; no, we can just set flag and send the ^S
	mov ax,ctrl_q		; yes, so now we need a ^Q
term43: xor ourflgs,tlnxof	; change the flag in either case
	call sndhst		; send our  ^S/ ^Q - IO.SYS 2.11 won't do it 
	mov ah,dconio		; do a dummy read to clear IO.SYS flush flag
	mov dl,0FFH
	int dos
	jmp term1		; and go back for more
term50:	mov ah,dconio		; Keyboard input processor
	mov dl,0FFH
	int dos			; check console
	jnz term51
	jmp term1		; no char, continue .Too far for rel jmp.
term51:	cmp al,ourarg.escc	; is it the escape char?
	je termx		; allows use of unredef left arrow key to esc
	call sndhst		; no translation, just send it out
	jmp term1		; and go back for more
termx:	pop es
	ret


; do appropriate translations on input key, and transmit
; if translation entry found it sends char(s) to sndhst and returns normally 
; if no translation entry is found, returns rskp with unsent scan code in ax
;  so that CONIN in IO.SYS can do its translation if neessary.
 
trnout:	test ourflgs,kbtrflg	; is there a translation table?
	jz trnou3
	mov cx,ourarg.klen	; get table length and origin
	mov di,ourarg.ktab
	push es
	mov bx,ds
	mov es,bx
	jcxz trnou3		; Needed for case of table zero length
	repne scasw		; look for key
	jne trnou3		; if not found, return rskp
	sub di,ourarg.ktab	; reset to offset of replacement
	sub di,2
	add di,ourarg.krpl
	mov si,[di]
	mov cl,[si]		; get length of replacement
	mov ch,0
	jcxz trnou3		; if length is zero, send nothing
	inc si
trnou1:	lodsb			; get replacement character
	push si
	push cx
	call sndhst		; send it to port
	pop cx
	pop si
	loop trnou1		; continue until translation complete
trnou2:	call inckbo		; increment kbout pointer
	pop es
	ret			; return after translating and sending
trnou3:	pop es
	jmp rskp		; plain characters return rskp

; get key-in scan code from fifo buffer in IO.SYS
; if gets a key-in, skip returns with scan code in ax
; returns normally if no key-in

inscan:	push es
	mov es,dosseg		; Address IO.SYS segment with es
	and ourflgs,not autorepflg	; Reset auto repeat flag
	mov bx,fifobas 		; Offset of fifobas in IO.SYS (from LCLINI)
insca1:	mov al,es:[bx].kbout	; Get value of kbin pointer
	cmp al,es:[bx].kbin	; Compare value of kbout pointer
	jz  insca2		; If equal, no key-in yet so exit
	sub ah,ah
	add bx,ax		; Calculate address pointed to (-2)
	mov ax,es:2[bx]		; Get scan code pointed to by kbout.
	xchg ah,al		; Get control byte -> ah, data byte -> al.
	mov savscn,ax		; Save scan data in case key repeat needed.
	pop es
	jmp rskp
insca2: mov bx,fifobas
	cmp byte ptr es:[bx].kbrepflg,0	 ; Is it time to repeat last key-in
	jz inscax		 	; Nope so exit
	mov ax,savscn			; Get last key-in
	or ourflgs,autorepflg		; Show we are on auto repeat cycle
	pop es				; And make it look like new
	jmp rskp
inscax:	pop es
	ret

; increments kbout pointer (with reset to zero) and returns normally
;   (on auto repeat cycles resets kbrepflg and returns )
 
inckbo:	push es
	mov es,dosseg
	mov bx,fifobas
	test ourflgs,autorepflg	; new key-in or autorepeat
	jnz inckb2
	mov cl,0		; Update kbout pointer to fifo 
	cli
	cmp byte ptr es:[bx].kbout,kbfifosiz-2		; End of fifo buff ?
	je  inckb1		; Yes, so start back at beginning
	mov cl,es:[bx].kbout	; No, just update our place
	add cl,2
inckb1:	mov es:[bx].kbout,cl	; Write back new pointer value
	sti
	jmp short inckbx
inckb2: mov byte ptr es:[bx].kbrepflg,0	; Reset autorepeat flag in IO.SYS
inckbx:	pop es
	ret

; send character in AL to port, with possible local echo

sndhst:	push ax
	mov ah,al
	call outchr		; send char to port
	 nop			;  ...don't care if it fails
	 nop
	 nop
	pop ax
	test ourarg.flgs,lclecho ; doing local echo?
	jz sndhs2
	mov dl,al
	mov ah,conout
	int dos			;  if so, display char
sndhs2:	ret

; send character to printer.  The only special case is the tab, which must
; be expanded to spaces because MS-DOS doesn't.

lstchr:	cmp al,tab
	jne lstch2
	mov ax,lstpos		; current column position
	mov cx,8		; # of spaces = 8 - (column % 8)
	div cl
	sub cl,ah
	add lstpos,cx		; update the column position
	mov al,' '
lstch1:	call lstch4		; print all the spaces
	loop lstch1
	ret
lstch2:	cmp al,cr		; CR returns column count to zero
	jne lstch3
	mov lstpos,0
lstch3:	cmp al,' '		; only printable characters are counted
	jb lstch4
	cmp al,del
	je lstch4
	inc lstpos
lstch4: mov dl,al		; print the character in any case
	mov ah,lstout
	int dos
	ret

TERM	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov dx,offset nyimsg
	jmp tmsg
VTS	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	push si
	cmp dh,25		; out of range just assumes high value
	jb poscu1
	mov dh,24
poscu1:	cmp dl,80
	jb poscu2
	mov dl,79
poscu2:	add dx,2020H		; add offset for ADM cursor addressing
	mov cpseq+2,dh
	mov cpseq+3,dl
	mov si,offset cpseq	; print sequence (ESC=rc)
	mov cx,4
posc1:	lodsb
	mov dl,al
	mov ah,conout
	int dos
	loop posc1
	pop si
	ret
POSCUR	ENDP

; Locate; homes cursor position and disables its display. Returns normally.

LOCATE  PROC	NEAR
	mov dx,offset nocur	; Disable cursor
	call tmsg
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	cmp al,del		; Del character needs extra backspace
	jne dodel1
	mov dl,bs
	mov ah,conout
	int dos
dodel1:	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

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov dx,offset ceolseq	; clear sequence
	jmp tmsg
CLEARL	ENDP

; This routine blanks the screen and homes the cursor.  Returns normally.

CMBLNK	PROC	NEAR
	mov dx,offset clrseq	; clear screen and home cursor sequence
	jmp tmsg
CMBLNK  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 formdat.invseq	; put into inverse video
	call tmsg
	pop dx			; print the message
	call tmsg
	mov dx,offset formdat.nrmseq	; normal video
	jmp tmsg		; Jump to return
PUTMOD	ENDP

; clear the mode line written by putmod.  Returns normally.

CLRMOD	PROC	NEAR
	mov dx,24*100H
	call poscur
	mov dx,offset ceolseq
	jmp tmsg
CLRMOD	ENDP

; Put a help message in a box at the top of the screen. 
; This one uses inverse video (or yellow if color)
; Pass the message in ax, terminated by a null.  Returns normally.

PUTHLP	PROC	NEAR
	cld
	mov dx,ax		; Prepare to pass message to 'getnoc'
	call getnoc		; 
	mov rnoc,cx		; This is unformatted NOC in message
	mov  cx,5		; Calculate formatted area needed (in words)
	rol  bx,cl		; BX is no of lf's.
	mov  NOC,bx
	mov cx,2
	ror bx,cl
	add NOC,bx		; This is Lines X 40
	add NOC,80		; Current line + one more
	mov cx,NOC
	mov si,dx		; Source of message  given us
	mov di,attrptr		; Pointer to screen attrib area
	mov ax,formdat.bldcrt	; Need bold attribute
	rep stosw		; Cover attribute area needed
	mov ax,formdat.nrmcrt	; Rest of screen needs normal color
	mov cx,1000		; Whole screen
	sub cx,NOC		; Attribute area left to cover
	rep stosw		; Do it
	mov cx,1000		; Fill screen data area with null bytes
	mov di,dispptr		; Pointer to screen data area
	xor ax,ax		; This is source of null bytes
	rep stosw		; Prepare clean screen data area
	mov cx,rnoc		; No of unformatted data bytes 
	mov di,dispptr		; Destination is screen data area
pthlp0:	push di			; Save start of current line
pthlp1:	lodsb			; Load data bytes 1-by-1
	cmp al,cr		; Is this one an eol?
	jz pthlp2		; Yep - handle cr/lf's ourselves
	stosb			; No - move character
	loop  pthlp1		; And go back for next
	jmp short pthlp3	; Finished moving chars, so exit.
pthlp2: dec cx			; Account for cr and lf
 	dec cx
	inc si			; Skip the lf
	pop di			; Get start of current line.
	add di,80		; Adjust pointer to next line
	jmp short pthlp0	; And go back for more
pthlp3:	pop di
	mov noc,2000		; We are going to write over whole screen
	test ourflgs,movcur	; Save cursor only if not previously saved
	jnz pthlp4
	mov cmd,2		; Get cursor position before writing
	mov dx,offset cmd
	mov cl,crtcmd
	int bios		; Get cursor call
	mov cx,word ptr la
	mov savcur,cx		; Store cursor position
pthlp4:	mov la,24		; Prepare to roll down 24 lines
	mov ca,0
	mov cmd,3		; Screen roll down command
	mov dx,offset cmd
	mov cl,crtcmd
	int bios		; Do it!
	mov la,0		; Begin our msg on top line of screen
	mov cmd,1		; String write command
	int bios		; Write it.
	mov cx,1000		; Delay for video memory stability
pthlp5:	loop pthlp5
	mov dx,offset upcur
	call tmsg
	mov dx,offset upcur	; Put cursor up onto clean screen
	call tmsg
	or  ourflgs,movcur	; Tell that we've moved the cursor
	ret
PUTHLP	ENDP

; Receives message pointer in DX, terminating character in CL
; Returns with message ptr in DX, NOC in CX, and number of lf's in BX.

GETNOC	PROC	NEAR
	cld
	mov di,dx
	mov al,cl			; Move terminator to AL
	mov cx,2000			; Longest acceptible message.
	repnz scasb			; Look for terminator
	jz gtnoc1
	xor cx,cx
	ret				; Error return
gtnoc1: sub di,dx			; Calculate NOC
	mov cx,di			; Move it to counter
	dec cx				; Discount terminator
	push cx				; Save NOC
	mov di,dx
	mov al,lf
	xor bx,bx
gtnoc2:	repnz scasb			; Now count lf's
	jcxz gtnoc3			; If the counter ran out
	inc bx
	jmp short gtnoc2
gtnoc3:	pop cx				; Recover NOC
	ret
GETNOC	ENDP



; Produce a short beep.  Returns normally.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos
	ret
BEEP	ENDP

; Prints $-terminated message in dx, for local use only

TMSG	PROC	NEAR
	mov ah,prstr
	int dos
	ret
TMSG	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

; END OF MSXAPC.ASM
