        name msxgri
; File MSXGRI.ASM
; Last modification: 20 April 1986

; Grid Compass II support for MS-DOS Kermit 2.26
;
; Jim Noble
; Planning Research Corporation
; 1500 Planning Research Drive
; Mail Stop 5S3
; McLean, VA  22102
; May, 1985
; Add global entry point vtstat for use by Status in mssset.
; Added register save/restore in procedure getbaud.
; Joe R. Doupnik 12 March 1986
; Add global procedures ihosts and ihostr to handle host initialization
; when packets are to be sent or received by us,resp. 24 March 1986
; Add global procedure dtrlow (without worker serhng) to force DTR & RTS low
; in support of Kermit command Hangup. Says Not Yet Implemented. [jrd]
; Add global procedure Dumpscr, called by Ter in file msster, to dump screen
;  to a file. Just does a beep for now. 13 April 1986 [jrd]
; In proc Outchr add override of xon from chkxon sending routine.
;  This makes a hand typed Xoff supress the xon flow control character sent
;  automatically as the receiver buffer empties. 20 April 1986 [jrd]

        page ,132

        public  serini, serrst, clrbuf, outchr, coms, vts, vtstat, 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
        public  ihosts, ihostr, dtrlow, dumpscr                 ; [jrd]
        include mssdef.h        ; (new name, changed from msdefs.h for 2.28)

false   equ     0
true    equ     1

gbuflen equ     10000           ; max bytes grid internal buffer holds
mntrgh  equ     gbuflen*1/2     ; High point = 1/2 of buffer full.

gserial equ     81h             ; grid serial port interrupt
gmodem  equ     82h             ; grid modem port interrupt
ginit   equ     0               ; function 0 - initialize port
gread   equ     1               ; function 1 - read data
gwrite  equ     2               ; function 2 - write data
gwcmd   equ     4               ; function 4 - write command
grstat  equ     5               ; function 5 - read status
gflush  equ     6               ; function 6 - buffer flush
ggbaud  equ     7               ; function 7 - get baud
gsbaud  equ     8               ; function 8 - set baud
gspar   equ     9               ; function 9 - set parity
gsdata  equ     10              ; function 10 - set data bits
gssbit  equ     11              ; function 11 - set stop bits
gbufass equ     12              ; function 12 - buffer assign
gcharto equ     13              ; function 13 - set character timeout
gbrk    equ     14              ; function 14 - break control
gcts    equ     19              ; function 19 - clear to send timeout
gbrkon  equ     gbrk*100H+00H   ; function 14 - set break on
gbrkoff equ     gbrk*100H+0ffH  ; function 14 - set break off
scnstrt equ     400h            ; starting address of screen area (page 0)
scnwrds equ     4800            ; number of words in screen memory area

datas   segment public 'datas'
        extrn   drives:byte, flags:byte, trans:byte
        extrn   portval:word, port1:byte, port2:byte, dmpname:byte ; [jrd]

machnam db      'GRID COMPASS II version A'

curini  db      0               ; [gaw@prc]
cursav  db      esc,'[s','$'    ; [gaw@prc]
curres  db      esc,'[u','$'    ; [gaw@prc]
curon   db      esc,'[3;3z','$' ; [gaw@prc]
curoff  db      esc,'[3;4z','$' ; [gaw@prc]
scrsav  dw      scnwrds DUP(?)

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$'
crlf    db      cr,lf,'$'
comphlp db      cr,lf,'1 (SERIAL)  2 (MODEM)$'          ; [19b] [gaw@prc]
hngmsg  db      cr,lf,' The phone should have hungup.',cr,lf,'$' ; [jrd]
hnghlp  db      cr,lf,' The modem control lines DTR and RTS for the current'
        db      ' port are forced low (off)'
        db      cr,lf,' to hangup the phone. Normally, Kermit leaves them'
        db      ' high (on) when it exits.'
        db      cr,lf,'$'                                       ; [jrd]
rdbuf   db      80 dup (?)      ; temp buf [jrd]
noimp   db      cr,lf,'?Not implemented.$'                      ; [jrd]
delstr  db      BS,' ',BS,'$'   ; Delete string. [21d]
clrlin  db      cr,esc,'[0K','$'
portin  db      0               ; has clock int vector been initialized?
xofsnt  db      0               ; Say if we sent an XOFF.
xofrcv  db      0               ; Say if we received an XOFF.
insrvc  db      0               ; Say if in service on XON/XOFF interrupt
invseq  db      esc,'[7m$'      ; Reverse video.
nrmseq  db      esc,'[0m$'      ; Normal mode.
ivlseq  db      79 dup (' '),cr,'$'     ; Make a line inverse video
tmp     db      ?,'$'
temp    dw      0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
fncerr  db      cr,lf,'Error on function '
fnctype db      'X with a status return of '
fncstat db      'Y$'

atemsg  db      'ATE1',cr
atelen  equ     (this byte) - atemsg

atvmsg  db      'ATV1',cr
atvlen  equ     (this byte) - atvmsg

argadr  dw      ?               ; address of arg blk
uptrn   db      esc,'A'
dntrn   db      esc,'B'
rgtrn   db      esc,'C'
lftrn   db      esc,'D'

; key redefinitions
ktrntab dw      ?                       ; address of translation table
krpltab dw      ?                       ; address of replacement table
tmptab  db      0eh,3bh                 ; scan code for bs, f1
ktlen   dw      ?

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$'

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

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

; 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

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

; variables for serial interrupt handler

gbuffer db      gbuflen DUP(?)  ; large internal buffer for grid [gaw@prc]

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,'[0K$'
prttab  dw      com2,com1
com1    db      'COM1',0
com2    db      'COM2',0
blank   db      esc,'[2J$'
movcur  db      esc,'['
colno   db      20 dup (?)
ten     db      10
prthnd  dw      0
tempbuf dw      10 dup(?)
ourarg  termarg <>
datas   ends

code    segment public 'code'
        extrn   comnd:near, dopar:near, prserr:near, defkey:near
        extrn   sleep:near                              ; [jrd]
        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


; Do a grid function call to the correct com port and return

GRDFNC  PROC    NEAR
        push    es              ; save es reg
        push    ds              ; then mov ds to es
        pop     es
        push    ax              ; save function call and value in al
        add     ah,"0"          ; make function code printable
        mov     byte ptr fnctype,ah     ; and save in error message
        pop     ax              ; restore ax
        cmp     flags.comflg,1  ; serial port or modem? [gaw@prc]
        jne     grdfnc1         ; if modem, do other int [gaw@prc]
        int     gserial         ; else do serial port function call [gaw@prc]
        jmp     grdfnc2         ; skip other int [gaw@prc]
grdfnc1:
        int     gmodem          ; do modem port function call [gaw@prc]
grdfnc2:
        jnc     grdfnc3         ; skip error msg if carry not set
        add     al,"0"          ; make error code printable
        mov     byte ptr fncstat,al     ; and put in error message
        mov ah,prstr
        push    dx
        mov dx,offset fncerr    ; Give an error message.
        int dos
        pop     dx
grdfnc3:
        pop     es              ; restore es
        ret
GRDFNC  ENDP

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

DOBAUD  PROC    NEAR
        push ax                 ; save regs. [jrd]
        push bx
        push bp
        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
dobd01:
        mov ax,temp1            ; Get back original value.
        mov ds:[bp].baud,ax     ; Leave baud rate as is.
        mov ah,prstr
        mov dx,offset badbd     ; Give an error message.
        int dos
        pop bp                  ; [jrd]
        pop bx
        pop ax
        ret
dobd0:
        mov     temp1,ax        ; Remember value to output. [25]
        cmp     flags.comflg,1  ; is it com1?
        je      dobd1           ; yep, skip test for 300/1200
        cmp     ax,7            ; is it 1200 on com2?
        je      dobd1           ; yep, go do it
        cmp     ax,5            ; is it 300 on com2?
        je      dobd1           ; yep, go do it
        jmp     short dobd01    ; go give unimplemented msg
dobd1:
        mov     ah,gsbaud       ; set up to set the port baudrate
        call    grdfnc          ; do a grid function call to a com port
        pop bp                  ; [jrd]
        pop bx
        pop ax
        ret
DOBAUD  ENDP

; Send a break out the current serial port.  Returns normally.
sendbr  proc    near
        push cx
        push ax
        xor cx,cx               ; Clear loop counter.
        mov ax,gbrkon           ; setup to do break on [gaw@prc]
        call    grdfnc          ; do break on
pause:  loop pause              ; Wait a while.
        mov ax,gbrkoff          ; setup to do break off [gaw@prc]
        call    grdfnc          ; do break off
        pop ax
        pop cx
        ret                     ; And return.
sendbr  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
        cmp ah,byte ptr [bp].flowc      ; sending xoff? [jrd]
        jne outch1              ; ne = no
        mov xofsnt,false        ; supress xon from chkxon buffer routine
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:
        mov     byte ptr temp,ah        ; put character in buffer
        push    cx                      ; Save register.
        push    di

        mov     cx,1            ; set up to write one char to a grid port
        mov     di,offset temp
        mov     ah,gwrite
        call    grdfnc          ; go write a character

        pop     di              ; restore saved registers
        pop     cx
        jmp rskp


; 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


; 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
        push    ax              ; save some regs. [jrd]
        push    bx              ; [jrd]
        push    cx              ; [jrd]
        push    dx              ; [jrd]
        push    bp              ; [jrd]
        mov     ah,ggbaud       ; set up to get port baud rate
        call    grdfnc          ; and go get it
        mov     al,ah           ; mov baudrate into al
        mov     ah,0            ; and zero upper part of ax

        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.
        jmp     getb3           ; [jrd]
;;;[jrd]        ret
getb2:  mov ah,prstr
        mov dx,offset erms40
        int dos
getb3:  pop     bp              ; restore regs. [jrd]
        pop     dx              ; [jrd]
        pop     cx              ; [jrd]
        pop     bx              ; [jrd]
        pop     ax              ; [jrd]
        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
prtch1:
        push    di
        push    cx
        mov     cx,bufsiz       ; set up to read from grid port buffer
        mov     di,offset source
        mov     ah,gread
        call    grdfnc
        pop     cx              ; restore saved registers
        pop     di

        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

; IHOSTS - Initialize the host by sending XON, or equivalent, and enter the
; cycle of clear input buffer, wait 1 second, test if buffer empty then exit
; else repeat cycle. Requires that the port be initialized before hand.
; Ihosts is used by the local send-file routine just after initializing
; the serial port.
; 22 March 1986 [jrd]

IHOSTS  PROC    NEAR
        push    ax              ; save the registers
        push    bx
        push    cx
        push    dx
        mov     bx,portval      ; port indicator
        mov     ax,[bx].flowc   ; put Go-ahead flow control char in ah
        call    outchr          ; send it (release Host's output queue)
         nop                    ; outchr can do skip return
         nop
         nop
ihosts1:call    clrbuf          ; clear out interrupt buffer
        mov     ax,1            ; sleep for 1 second
        call    sleep           ; procedure sleep is in msscom.asm
        call    prtchr          ; check for char at port
         jmp    ihosts1         ; have a char in al, repeat wait/read cycle
         nop                    ; prtchr does skip return on empty buffer
        pop     dx              ; empty buffer. we are done here.
        pop     cx
        pop     bx
        pop     ax
        ret
IHOSTS  ENDP

; IHOSTR - initialize the remote host for our reception of a file by
; sending the flow-on character (XON typically) to release any held
; data. Called by receive-file code just after initializing the serial
; port.         22 March 1986 [jrd]
IHOSTR  PROC    NEAR
        push    ax              ; save regs
        push    bx
        push    cx
        mov     bx,portval      ; port indicator
        mov     ax,[bx].flowc   ; put Go-ahead flow control char in ah
        call    outchr          ; send it (release Host's output queue)
         nop                    ; outchr can do skip return
         nop
         nop
        pop     cx
        pop     bx
        pop     ax
        ret
IHOSTR  ENDP

DTRLOW  PROC    NEAR            ; Global proc to Hangup the Phone by making
                                ; DTR and RTS low.
        mov ah,cmtxt            ; allow text to be able to display help
        mov bx,offset rdbuf     ; dummy buffer
        mov dx,offset hnghlp    ; help message
        call comnd              ; get a confirm
         jmp r
; not yet imp.  call serhng             ; drop DTR and RTS
        mov ah,prstr            ; give a nice message
; not yet imp.  mov dx,offset hngmsg
        mov dx,offset noimp     ; for now
        int dos
        jmp rskp
DTRLOW  ENDP

; Hang up the Phone. Similar to SERRST except it just forces DTR and RTS low
; to terminate the connection. 29 March 1986 [jrd]
; Calling this twice without intervening calls to serini should be harmless.
; Returns normally.
; SERHNG is Not Yet Implemented.


; Position the cursor according to contents of DX.

POSCUR  PROC    NEAR
        push    ax                      ; save regs. [jrd]
        push    dx
        push    es
        push    di
        mov     ax,ds
        mov     es,ax                   ; address data segment!!!
        cld
        mov     di,offset colno
        mov     al,dh                   ; row
        inc     al                      ; adjust offset from 1 instead of 0
        mov     ah,0                    ; zero up half of reg containing number
        call    nout
        mov     al,';'
        stosb
        mov     al,dl                   ; column
        inc     al                      ; adjust offset from 1 instead of 0
        mov     ah,0                    ; zero up half of reg containing number
        call    nout
        mov     al,'H'
        stosb
        mov     al,'$'
        stosb
        mov     dx,offset movcur
        mov     ah,prstr
        int     dos                     ; print the sequence
        pop     di                      ; [jrd]
        pop     es
        pop     dx
        pop     ax
        ret
POSCUR  ENDP

; put the number in ax into the buffer pointed to by di.  Di is updated
nout    proc    near
        push    dx              ; save registers
        push    bx
        push    ax

        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
        pop     ax              ; restore all registers
        pop     bx
        pop     dx
        ret                     ; and return
nout    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,26 * 100H    ; line 26
        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,26 * 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

; 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

VTSTAT  PROC    NEAR    ; For Status display [jrd]
        ret             ; no emulator status to display
VTSTAT  ENDP

; Save the screen to a buffer and then append buffer to a disk file. [jrd]
; Default filename is Kermit.scn; actual file can be a device too. Filename
; is determined by mssset and is passed as pointer dmpname.

DUMPSCR PROC    NEAR    ; Dumps screen contents to a file. Just Beeps here
        call beep       ; [jrd]
        ret
DUMPSCR ENDP


; local initialization

lclini  proc    near
        mov     flags.vtflg,0   ; turn off terminal emulation [gaw@prc]
        mov     ax,0eH          ; scan code for arrow key
        mov     si,offset rbtrn ; translate to rubout
        mov     cx,1            ; one char translation
        call    defkey
        ret
lclini  endp

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey proc    near
        push    es
        push    ax              ; save the ptr
        mov     bx,ds
        mov     es,bx           ; address data segment
        cld
showk1:
        mov     dl,0ffh         ; wait for input but don't echo
        mov     ah,coninq
        int     dos
        or      al,al           ; extended code?
        mov     ah,0
        jnz     noalt           ; no, skip getting 2nd char

        mov     dl,0ffh         ; get 2nd char
        mov     ah,coninq
        int     dos
        mov     ah,08h          ; fake, alt key in ah

;       xor     ah,ah
;       call    prtchr          ; read a char
;       nop
;       nop
;       nop
;       or      ax,ax           ; get one?
;       jz      showk1          ; nope, wait

;       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

noalt:
        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


;  Common initialization for using serial port.

SERINI  PROC    NEAR
        push    es
        cmp portin,0            ; Did we initialize interrupt already?
        je      serin0          ; No, skip exit
        jmp     serin1          ; Yes, so just leave. [21c]
serin0:
        cli                     ; Disable interrupts
        cld                     ; Do increments in string operations

        xor     ax,ax           ; get vector of clock tick int at 1CH
        mov     es,ax
        mov     bx,1Ch*4
        mov     ax,es:[bx]
        mov     cs:savclko,ax
        add     bx,2
        mov     ax,es:[bx]
        mov     cs:savclks,ax

;-> all because cli doesn't disable clock tick interrupt!

        push    es              ; save pointer to segment part of vector
        push    bx
        mov     es,ax           ; point to location with offset replaced
        mov     bx,offset serint
        mov     cl,es:[bx]      ; save value in cl
        mov     al,0CFh         ; load al with IRET instruction
        mov     es:[bx],al      ; put at location with offset replace
        pop     bx
        pop     es
        sub     bx,2            ; now point to offset part of vector
        mov     ax,offset serint
        mov     es:[bx],ax      ; and load with offset of serint
        add     bx,2            ; now point to segment part of vector
        mov     ax,cs
        mov     es:[bx],ax      ; and load with code segement address
        mov     ax,cs:savclks   ; now restore byte at offset of serint
        mov     es,ax
        mov     bx,offset serint
        mov     es:[bx],cl

;<-

;       mov     ax,cs           ; set vector at 1CH to point to serint
;       mov     es:[bx],ax
;       sub     bx,2
;       mov     ax,offset serint
;       mov     es:[bx],ax

        mov portin,1            ; Remember interrupt has been initialize.
        sti                     ; turn interrupts back on

;       mov     ah,ginit        ; reset serial port to default values
;       call    grdfnc

        call    dobaud          ; reset baud just incase modem hung up

        xor     ax,ax
        mov     ah,gspar        ; parity = none
        call    grdfnc

        mov     al,1            ; stopbit = 1
        mov     ah,gssbit
        call    grdfnc

        mov     al,8            ; data bits = 8
        mov     ah,gsdata
        call    grdfnc

        mov     dx,1            ; timeout on char read wait = 1ms
        mov     ah,gcharto
        call    grdfnc

        cmp     flags.comflg,1  ; Using Com 1?
        jne     skipcts         ; Nope.

        mov     dx,0            ; timeout on cts = 0
        mov     ah,gcts
        call    grdfnc

skipcts:
        mov     ax,ds           ; point to large buffer
        mov     es,ax
        mov     di,offset gbuffer
        mov     cx,gbuflen
        mov     ah,gbufass
        call    grdfnc

        mov     ah,gflush       ; flush grid input buffer
        call    grdfnc

;;      cmp     flags.comflg,1  ; Using Com 1?
;;      je      skipcmd         ; Yep, skip commands to modem
;;
;;      push    di
;;
;;      lea     di,atemsg       ; send an ATE=1
;;      mov     cx,atelen
;;      mov     ah,gwrite
;;      call    grdfnc
;;
;;      lea     di,atvmsg       ; send an ATV=1
;;      mov     cx,atvlen
;;      mov     ah,gwrite
;;      call    grdfnc
;;
;;      pop     di
;;
;;skipcmd:
        call clrbuf             ; Clear input buffer. [20e]
serin1:
        pop es
        ret                     ; We're done.
SERINI  ENDP

SERRST  PROC    NEAR
;       mov     ah,ginit        ; reset serial port to default values
;       call    grdfnc

        cmp portin,0            ; Did we initialize interrupt already?
        je serrst0              ; No, skip resetting clock vector
        cli                     ; Disable interrupts

;-> all because cli doesn't disable clock tick interrupt!

        push    es              ; restore vector of clock tick int at 1CH
        mov     ax,cs:savclks   ; get pointer to location when segment replaced
        mov     es,ax
        mov     bx,offset serint
        mov     cl,es:[bx]      ; save value from there in cl
        mov     al,0CFh         ; load location with IRET
        mov     es:[bx],al
        push    es              ; save segment pointer for later
        push    bx
        xor     ax,ax           ; point to segment part of vector
        mov     es,ax
        mov     bx,(1Ch*4)+2
        mov     ax,cs:savclks   ; replace with original value
        mov     es:[bx],ax
        sub     bx,2            ; now replace offset with original value
        mov     ax,cs:savclko
        mov     es:[bx],ax
        pop     bx              ; now restore location holding temporary IRET
        pop     es
        mov     es:[bx],cl
        pop     es              ; and clean up stack

;<-

;       push    es              ; restore vector of clock tick int at 1CH
;       xor     ax,ax
;       mov     es,ax
;       mov     bx,1Ch*4
;       mov     ax,cs:savclko
;       mov     es:[bx],ax
;       add     bx,2
;       mov     ax,cs:savclks
;       mov     es:[bx],ax
;       pop     es

        mov portin,0            ; Remember interrupt has been reset.
        sti                     ; turn interrupts back on

serrst0:
        ret                     ; All done. [21c]
SERRST  ENDP

; Comm port interrupt service routine to prevent grid buffer overflow
; sends Xoff if necessary if activated
;
SERINT  PROC    NEAR
        push ax
        push bx
        push cx
        push dx
        push ds
        push es
        push si
        push di
        push bp
        pushf
        cld
        mov ax,seg datas
        mov ds,ax               ; address data segment
        mov es,ax

        mov bp,portval
        cmp ds:[bp].floflg,0    ; Doing flow control?
        je retint               ; No, just leave.

        jmp     retint  ; <<===<< temporary stub until grid problem resolved

        cmp     insrvc,true     ; are we already doing interrupt service?
        je      retint          ; yes, then skip service until this one done
        mov     insrvc,true     ; nope, set in service flag
        sti                     ; flag set enable interrupts

        mov     ah,grstat       ; get the buffer count
        call    grdfnc

        cmp xofsnt,true         ; Have we sent an XOFF?
        je serint1              ; Yes.

        cmp cx,mntrgh           ; Past the high trigger point?
        jbe intdone             ; No, we're within our limit.
        mov ah,XOFF             ; Get the XOFF.

        mov     byte ptr temp,ah        ; put character in buffer

        mov     cx,1            ; set up to write one char to a grid port
        mov     di,offset temp
        mov     ah,gwrite
        call    grdfnc          ; go write a character

;       call outchr             ; Send it.
;       nop
;       nop
;       nop                     ; ignore failure.

        mov xofsnt,true         ; Remember we sent it.
        jmp     intdone

serint1:
        cmp     cx,mntrgh       ; below the high trigger point?
        ja      intdone         ; no, don't send XON
        mov     ah,XON          ; get the XON

        mov     byte ptr temp,ah        ; put character in buffer

        mov     cx,1            ; set up to write one char to a grid port
        mov     di,offset temp
        mov     ah,gwrite
        call    grdfnc          ; go write a character

;       call    outchr          ; send it
;       nop                     ; ignore failure
;       nop
;       nop

        mov     xofsnt,false    ; remember we sent it

intdone:
        mov     insrvc,false    ; set in service flag to false
retint:
        sti
        popf
        pop bp
        pop di
        pop si
        pop es
        pop ds
        pop dx
        pop cx
        pop bx
        pop ax
        jmp     dword ptr cs:savclko

savclko dw      ?               ; save clock tick interrupt vector offset
savclks dw      ?               ; save clock tick interrupt vector segment


SERINT  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

; send the character in al out to the serial port
; handle echoing also...
outprt  proc    near
        test    flags,lclecho           ; echoing?
        jz      outpr1                  ; no, forget it
        push    ax                      ; save char

        mov dl,al
        and dl,7fh              ; mask off parity for terminal
        mov ah,dconio
        int dos                 ; write out the character

        pop     ax                      ; restore
outpr1: mov     ah,al                   ; this is where outchr expects it
        call    outchr                  ; output to the port
         nop
         nop
         nop                            ; skip returns...
        ret
outprt  endp

argini  proc    near                    ; read passed arguments
        mov     bx,argadr               ; base of argument block
        mov     al,[bx].flgs            ; get flags
        and     al,capt+emheath+havtt+trnctl+lclecho  ; +modoff
        mov     flags,al                ; mask for allowable and save
;       and     flags1,not (prtscr)     ; these are allowable
                                        ; (others remain).
        mov     al,[bx].prt
;       cmp     al,portno               ; using same port?
;       je      argin1                  ; yes, go on
;       and     flags1,not inited       ; else re-init stuff
argin1:
;       mov     portno,al               ; update port number
;       mov     ax,[bx].captr
;       mov     captrtn,ax              ; buffer capture routine
;       mov     ax,[bx].belld
;       mov     beldiv,ax               ; bell divisor

        mov     ax,[bx].klen
        mov     ktlen,ax                ; length of key redef tbl
        mov     ax,[bx].ktab
        mov     ktrntab,ax              ; save key translation table
        mov     ax,[bx].krpl
        mov     krpltab,ax

;       mov     al,[bx].escc
;       mov     esc_ch,al
        ret                             ; that's it
argini  endp

term    proc    near

        push    ax
        push    es

        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

        pop     es
        pop     ax

        mov     argadr,ax               ; save argument ptr
        push    es                      ; save caller's extra segment address
        mov     ax,seg datas
        mov     es,ax

        call    argini                  ; init options from arg address

        cmp     curini,0                ; have we been in here before[gaw@prc]
        je      term1                   ; if not skip restoring cursor[gaw@prc]
        call    restscr                 ; restore screen
        cmp     flags.vtflg,0           ; are we in emulation mode[gaw@prc]
        jne     term1           ; if we are skip restoring cursor[gaw@prc]
        mov     ah,prstr                ; send '<esc>[u' to ansi.sys[gaw@prc]
        mov     dx,offset curres        ; [gaw@prc]
        int     dos                     ; [gaw@prc]

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

        or      al,al           ; extended code?
        mov     bl,0
        jnz     no2nd           ; no, skip getting 2nd char

        mov     dl,0ffh         ; get 2nd char
        mov     ah,coninq
        int     dos
        mov     ah,al           ; duplicate char in ah for "call turnout"
        mov     bl,08h          ; fake, alt key in bl for "call turnout"
no2nd:
        call    trnout          ; translate if necessary

;       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:
        cmp     flags.vtflg,0           ; are we in emulation mode[gaw@prc]
        jne     term5                   ; if we are skip saving cursor[gaw@prc]
        mov     ah,prstr                ; send '<esc>[s' to ansi.sys[gaw@prc]
        mov     dx,offset cursav        ; [gaw@prc]
        int     dos                     ; [gaw@prc]
        mov     byte ptr curini,1       ; now we've saved the cursor[gaw@prc]
term5:                                  ; [gaw@prc]
        call    savescr                 ; save screen [gaw@prc]
        mov     al,flags
        mov     bx,argadr
        mov     [bx].flgs,al            ; update flags in arg block
        pop     es                      ; restore segment register
        ret                             ; and return to caller

term    endp

savescr proc near
        push    es                      ; move ds base address to es
        push    ds
        pop     es
        mov     di,offset scrsav        ; point to start of screen save area
        push    ds
        xor     ax,ax                   ; point to base page with ds
        mov     ds,ax
        mov     si,scnstrt              ; point to screen memory area
        mov     cx,scnwrds              ; setup word count
        rep     movsw                   ; transfer image to save area
        pop     ds                      ; restore registers and return
        pop     es
        ret
savescr endp

restscr proc near
        push    es                      ; point to base page with es

        mov dx,offset curoff            ; turn cursor off
        mov ah,prstr
        int dos

        mov dx,offset blank             ; clear screen to get rid of cursor
        mov ah,prstr
        int dos

        xor     ax,ax                   ; restore screen
        mov     es,ax
        mov     di,scnstrt              ; point to screen memory area
        mov     si,offset scrsav        ; point to start of screen save area
        mov     cx,scnwrds              ; setup word count
        rep     movsw                   ; transfer image to screen

        mov dx,offset curon             ; turn cursor back on
        mov ah,prstr
        int dos

        pop     es                      ; restore register and return
        ret
restscr endp

; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout  proc    near

;       cmp     ah,4eh                  ;*** plus key thing?
;       je      trnmod                  ; yes, go toggle mode line

trnou1:
        test    flags,havtt             ; translate table given?
        jz      trnou3                  ; no, just output character
        push    ax                      ; save original value
        mov     al,ah                   ; put scan code into ah
        mov     ah,bl                   ; shift state into top half.
        mov     di,ktrntab              ; pick up translate tbl
        mov     cx,ktlen                ; length of tbl
        repne   scasw                   ; look for our key
        pop     ax                      ; recover character
        jne     trnou3                  ; not found, forget it
        sub     di,ktrntab              ; get index into tbl
        sub     di,2                    ; (minus 2 for pre-increment)
        mov     bx,krpltab              ; get replacement table
        mov     si,[bx][di]             ; and addr of replacement
        mov     cl,[si]                 ; get first byte (length)
        xor     ch,ch                   ; clear high-order byte
        inc     si                      ; point to translation string
trnou2: lodsb                           ; get a byte
        push    si
        push    cx                      ; save important registers
        call    outprt                  ; send to port
        pop     cx
        pop     si
        loop    trnou2                  ; send all chars
        ret                             ; and return
trnou3:
;       cmp     al,0                    ; is it a special code?
;       jne     trnou4                  ; no, don't do this
;       mov     al,ah                   ; get scan code
;       mov     cx,lckeys               ; length of table
;       mov     di,offset ckeys         ; table address
;       repne   scasb
;       mov     al,0                    ; ascii code was 0...
;       jne     trnou4                  ; not found, keep going
;       sub     di,offset ckeys+1       ; get table offset
;       shl     di,1                    ; shift for word offset
;       jmp     ckacts[di]              ; jump to appropriate routine
;trnou4:
        call    outprt                  ; just output single char
        ret                             ; and return

;trnmod:        test    flags,modoff            ; mode line already off?
;       jnz     trnm1                   ; yes, go turn on
;       call    clrmod                  ; no, clear mode line here
;       or      flags,modoff            ; turn on flag
;       ret                             ; and return
;trnm1: call    modlin                  ; turn on mode line
;       and     flags,not modoff        ; clear flag
;       ret                             ; and return
;
;trnbrk:        mov     ah,dconio
;       mov     dl,0ffH
;       int     dos                     ; read the bogus ^C DOS gets.
;       call    sendbr
;       ret
;trnprs:        xor     flags1,prtscr           ; flip the flag
;       and     flags,not modoff        ; turn on mode line
;       mov     si,offset prton
;       mov     cx,prtnlen
;       test    flags1,prtscr           ; did it go on?
;       jnz     trnpr1                  ; yes, say so
;       mov     si,offset prtoff
;       mov     cx,prtflen
;trnpr1:        call    modwrt                  ; write into mode line
;       ret                             ; and return

; common entry for arrow keys
trnarr: mov     cx,2                    ; length is always 2
        jmp     trnou2                  ; go send definition

trnupw: mov     si,offset uptrn
        jmp     trnarr

trndnw: mov     si,offset dntrn
        jmp     trnarr

trnlfw: mov     si,offset lftrn
        jmp     trnarr

trnrgw: mov     si,offset rgtrn
        jmp     trnarr

trnout  endp

code    ends
        end
