        name    msxgen
; File MSXGEN.ASM
; Generic MS DOS Kermit module, does i/o via DOS calls.
; Use with file MSUGEN.ASM (Generic keyboard translator)
;
; Last edit: 8 Jan 1988
; 8 Jan 1988 Update 8 bit display in outtty. [jrd]
; 1 Jan 1988 version 2.30
; 10 Dec 1987 Add Showmodem command.
; 12 Sept 1987 version 2.30
; 18 July 1987 Remove redundant rxtable test (Tnx to Jack Bryans), pass all
;  incoming serial port chars (except flow control) when Debug is on. [jrd]
; 25 May 1987 add keyboard translator, input translation, cleanups. [jrd]
; 1 Oct 86 Version 2.29a
; 30 Sept 1986 Reject DEL char at serial port reception level to avoid
;  problems when DEL is used as a filler char (by Emacs). [jrd]
; 27 Sept 1986 Revise procedure Term to permit capturing, printer ready
;  testing, debug display. Revised other port procedures slightly too;
;  especially to set port into binary mode via ioctl. [jrd]
; 4 Sept 1986 Add Bob Goeke's change to move comms port table to a system
;  dependent module (typ msx---) to allow 3+ ports and localized idents. [jrd]
; 26 May 1986 Make default display mode be Serial. [jrd]
;
; Note: the biggest difficulty using this Generic Kermit is loss of one
; or two incoming characters when the screen must scroll. IBM PC's and
; relatives do this. If possible, replace the DOS screen write with faster
; calls specific to your system. Procedure Sleep call (uses system time of
; day clock) is commented out below for systems lacking such a clock. [jrd]

        public  serini, serrst, clrbuf, outchr, coms, vts, vtstat, dodel
        public  ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl
        public  dodisk, getbaud, beep, trnprs, pcwait, shomodem, termtb
        public  count, xofsnt, puthlp, putmod, clrmod, poscur
        public  sendbr, sendbl, term, machnam, setktab, setkhlp, showkey
        public  ihosts, ihostr, dtrlow, serhng, dumpscr, comptab
        public  chrout, cstatus, cquit, cquery, chang   ; kbd action verbs
        public  snull, kdos, klogof, klogon
        include mssdef.h

false   equ     0
true    equ     1
instat  equ     6
print_out equ   05h                     ; dos function to print to printer
prtscr  equ     80h                     ; print screen pressed

; 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, port3:byte, port4:byte
        extrn   comand:byte, dmpname:byte                       ; [jrd]
        extrn   kbdflg:byte, rxtable:byte

machnam db      'Generic MS-DOS 2.0$'
erms20  db      cr,lf,'?Warning: System has no disk drives$'
erms40  db      cr,lf,'?Warning: Unrecognized baud rate$'
erms41  db      cr,lf,'?Warning: Cannot open com port$'
erms50  db      cr,lf,'Error reading from device$'
hnd1    db      cr,lf,'Enter a file handle.  Check your DOS manual if you are '
        db      cr,lf,'not certain what value to supply (generally 3).$'
hnd2    db      cr,lf,'Handle: $'
hnderr  db      cr,lf,'Warning: Handle not known.'
deverr  db      cr,lf,'Any routine using the communications port will'
        db      cr,lf,'probably not work.$'
hndhlp  db      cr,lf,'A one to four digit file handle $'
dev1    db      cr,lf,'Device: $'
devhlp  db      cr,lf,'Name for your systems auxiliary port $'
badbd   db      cr,lf,'Unimplemented baud rate$'
noimp   db      cr,lf,'Command not implemented.$'
hngmsg  db      cr,lf,' The phone should have hungup.',cr,lf,'$'
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,'$'
msmsg1  db      cr,lf,' Communications port is not ready.$'
msmsg2  db      cr,lf,' Communications port is ready.$'
rdbuf   db      80 dup (?)      ; temp buf [jrd]

shkmsg  db      'Not implemented.'
shklen  equ     $-shkmsg
setktab db      0
setkhlp db      0
crlf    db      cr,lf,'$'
delstr  db      BS,BS,'  ',BS,BS,'$'    ; Delete string
; If delete code moves cursor then BS over code, BS over bad char, space
; over both to erase from screen, BS twice to restore cursor position.
clrlin  db      cr,'$'                  ; Clear line (just the cr part).
clreol  db      '^U',cr,lf,'$'          ; Clear line.
telflg  db      0               ; non-zero if we're a terminal.
argadr  dw      ?               ; address of arg blk from msster.asm
parmsk  db      ?               ; 8/7 bit parity mask, for reception
flowoff db      ?               ; flow-off char, Xoff or null (if no flow)
flowon  db      ?               ; flow-on char, Xon or null
captrtn dw      ?               ; routine to call for captured output
xofsnt  db      0               ; Say if we sent an XOFF.
xofrcv  db      0               ; Say if we received an XOFF.
count   dw      0               ; Number of chars in int buffer.
prthnd  dw      0               ; Port handle.
prttab  dw      com1,com2
com1    db      'COM1',0
com2    db      'COM2',0
tmp     db      ?,'$'
temp    dw      0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
prtstr  db      20 dup(?)       ; Name of auxiliary device

; Entries for choosing communications port
comptab db      6               ; Number of options
        mkeyw   '1',1
        mkeyw   '2',2
        mkeyw   'COM1',1
        mkeyw   'COM2',2
        mkeyw   'Device',3
        mkeyw   'File-handle',4

ourarg  termarg <>

termtb  db      tttypes                 ; entries for Status, not Set
        mkeyw   'Heath-19',ttheath
        mkeyw   'none',ttgenrc
        mkeyw   'Tek4014',tttek
        mkeyw   'VT102',ttvt100
        mkeyw   'VT52',ttvt52

datas   ends

code    segment public 'code'
        extrn   comnd:near, dopar:near, prserr:near, atoi:near, prompt:near
        extrn   sleep:near, msuinit:near, keybd:near
        assume  cs:code,ds:datas


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

DODISK  PROC    NEAR
        mov ah,gcurdsk                  ; Current disk value to AL.
        int dos
        mov dl,al                       ; Put current disk in DL.
        mov ah,seldsk                   ; Select current disk.
        int dos                         ; Get number of drives in AL.
        mov drives,al
        ret
DODISK  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.
; Do nothing since we are not interrupt driven.  Returns normally.

CLRBUF  PROC    NEAR            ; rewritten by [jrd]
        cmp prthnd,0            ; got a port handle yet?
        jne clrbu1              ; ne = yes
        ret                     ; else just return
clrbu1: call prtchr             ; read from comms port
         jmp clrbu1             ;  returns when data available
         nop
        ret                     ; skip returns on no data
CLRBUF  ENDP

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

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

shomodem proc   near
        mov     ah,cmcfm        ; get a confirm
        call    comnd
         jmp    r               ; no confirm
         nop
        cmp     prthnd,0        ; Got a handle yet?
        jne     shmod0          ; Yup just go on
        call    opnprt          ; Else 'open' the port
shmod0: mov     dx,offset msmsg1 ; say port is not ready
        mov     bx,prthnd
        mov     al,7            ; output status command
        mov     ah,ioctl        ; ask DOS to look for us
        int     dos
        jc      shmod1          ; c = call failed, device not ready
        or      al,al
        jz      shmod1          ; not ready...
        mov     dx,offset msmsg2 ; say port is ready
shmod1: mov     ah,prstr
        int     dos
        jmp     rskp
shomodem 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
        push    cx              ; save regs
        or      ah,ah           ; sending a null?
        jz      outch2          ; z = yes
        xor     cx,cx           ; clear counter
        cmp     ah,flowoff      ; sending xoff?
        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: push    dx              ; Save register.
        mov     al,ah           ; Parity routine works on AL.
        call    dopar           ; Set parity appropriately.
                                ; Begin revised output routine
        mov     byte ptr temp,al ; put data there
        cmp     prthnd,0        ; Got a handle yet?
        jne     outch3          ; Yup just go on
        call    opnprt          ; Else 'open' the port
outch3: push    bx
        mov     bx,prthnd       ; port handle
        mov     cx,1            ; one byte to write
        mov     dx,offset temp  ; place where data will be found
        mov     ah,write2       ; dos 2 write to file/device
        int     dos
        pop     bx              ; end of revised routine
        pop     dx
        pop     cx
        jmp     rskp
OUTCHR  ENDP

; This routine blanks the screen.  Returns normally.

CMBLNK  PROC    NEAR
        push ax                 ; save some registers
        push dx
        mov ah,prstr
        mov dx,offset crlf      ; carriage return plus line feed.
        pop dx
        pop ax
        ret
CMBLNK  ENDP

; Homes the cursor.  Returns normally.

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

; Write a line 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,1800h        ; now address line 24
        call    poscur
        pop     dx              ; get message back
        mov     ah,prstr
        int     dos             ; write it out
        ret                     ; and return
putmod  endp

; clear the mode line written by putmod.  Returns normally.
clrmod  proc    near
        mov     dx,1800h
        call    poscur          ; Go to bottom row.
        call    clearl          ; Clear to end of line.
        ret
clrmod  endp

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

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

DOBAUD  PROC    NEAR
        mov ah,prstr
        mov dx,offset noimp     ; Say it's not implemented.
        int dos
        push bx                 ; save reg
        mov bx,portval
        mov [bx].baud,0FFFFH    ; So it's not a recognized value.
        pop bx
        ret                     ; Must be set before starting Kermit.
DOBAUD  ENDP

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

GETBAUD PROC    NEAR
        ret                     ; Can't do this.
GETBAUD ENDP

; Use for DOS 2.0 and above.  Check the port status.  If no data, skip
; return.  Else, read in a char and return.
; Note added by [jrd]: The test for char-at-input-port is int 21h function
; 44h (ioctl) sub function 6 (get input status). On many systems an FFH will
; be reported (meaning Ready) even though no char is available; the Ready
; indication is misleading. In such cases the system will wait for a char
; and will appear to be hung. A preferrable method is to use the ROM Bios
; call int 14H function 3 (get port status) and if the lsb of the returned
; 8 bits in AH is 1 then a char is availble at the port; this assumes that
; the machine emulates this ROM Bios operation.
; Lastly, if the current code is used and the system hangs then reboot
; and say  ECHO Hello >COM1  before running Kermit; this should make MSDOS
; truely aware of the port's actual status. Dark grey magic. Good luck! [jrd]
PRTCHR  PROC    NEAR
        push    bx
        push    cx
        cmp     prthnd,0        ; Got a handle yet?
        jne     prtch0          ; Yup just go on
        call    opnprt          ; Else 'open' the port
prtch0: call    chkxon
        mov     bx,prthnd
        mov     al,instat       ; input status command
        mov     ah,ioctl        ; see note above
        int     dos
        jc      prtch4          ; c = call failed, device not ready
        or      al,al
        jz      prtch4          ; not ready...
        mov     bx,prthnd       ; the file handle
        mov     ah,readf2       ; read file/device
        mov     cx,1            ; want just one character
        mov     dx,offset rdbuf ; where to store it
        int     dos
        jnc     prtch1          ; nc = no error
        cmp     al,5            ; Error condition.
        je      prt3x
        cmp     al,6            ; Error condition
        je      prt3x
        jmp     prtch4          ; else report no char present.
prtch1:;;;mov   count,0         ; update count (always 0 for one char reads)
        mov     dx,ax           ; needed to obey rules
        or      ax,ax           ; reading from end of file?
        jz      prtch4          ; z = yes
        mov     al,rdbuf        ; recover char
prtch3: pop     cx
        pop     bx
        ret                     ; return success (char is in al)
prt3x:  mov     ah,prstr
        mov     dx,offset erms50
        int     dos
prtch4: pop     cx
        pop     bx
        jmp     rskp            ; no chars...
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
        mov     ax,[bx].flowc   ; ah gets xon
        call    outchr          ; send it
        nop
        nop
        nop                     ; in case it skips
        mov     xofsnt,false    ; remember we've sent the xon.
chkxo1: pop     bx              ; restore register
        ret                     ; and return
chkxon  endp

; 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
        or      ah,ah           ; don't send null if flow = none
        jz      ihosts1         ; z = null
        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
; NOTE: for systems with a time-of-day clock uncomment the line below
; (call sleep) to provide an interval for the host to respond.

;;;     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
        or      ah,ah           ; don't send null if flow = null
        jz      ihostr1         ; z = null
        call    outchr          ; send it (release Host's output queue)
         nop                    ; outchr can do skip return
         nop
         nop
ihostr1: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.

SERHNG  PROC NEAR
        ret
SERHNG  ENDP

; Wait for the # of milliseconds in ax, for non-IBM compatibles.
; Based on 4.77 Mhz 8088 processor speeds.
; Thanks to Bernie Eiben for this one.
pcwait  proc    near
        mov     cx,240          ; inner loop counter for 1 millisecond
pcwai1: sub     cx,1            ; inner loop takes 20 clock cycles
        jnz     pcwai1
        dec     ax              ; outer loop counter
        jnz     pcwait          ; wait another millisecond
        ret
pcwait  endp


; Send a break out the current serial port.  Returns normally.
SENDBR  PROC    NEAR            ; Normal Break
        ret
SENDBR  ENDP
SENDBL  PROC    NEAR            ; Long Break
        ret
SENDBL  ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
POSCUR  PROC    NEAR
        ret
POSCUR  ENDP

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

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

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

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

; Set the current port.

COMS    PROC    NEAR
        mov dx,offset comptab   ; the table to examine
        mov bx,0                ; use keywords as help
        mov ah,cmkey            ; parse keyword from comptab
        call comnd
         jmp r                  ; no match
        push bx                 ; value following the located keyword
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp comx               ;  Didn't get a confirm.
         nop
        pop bx
        cmp bl,3                ; Do they want to set device name?
        je coms3                ; e = Yes go get name
        jg coms4                ; g = pick up file handle
        mov flags.comflg,bl     ; Set the comm port flag.
        cmp bl,1                ; Using Com 1?
        jne coms2               ; Nope.
        mov portval,offset port1
        ret
coms2:  cmp flags.comflg,2      ; using Com2?
        jne coms3               ; ne = no
        mov portval,offset port2
        ret
comx:   pop bx
        ret
coms3:  mov dx,offset dev1      ; Let user supply device name.
        call prompt
        mov ah,cmtxt            ; parse string, returns asciiz string
        mov bx,offset prtstr    ; Put name here
        mov dx,offset devhlp    ; help message if user types question mark
        call comnd
         jmp coms31             ; Did user type ^C.
         nop
        mov comand.cmstat,cmcfm ; simulate a confirm has been requested
        mov dx,offset prtstr    ; Point to string
        mov ah,open2            ; Open port as a file
        mov al,2                ; For reading and writing
        int dos
        jc coms31               ; c = failure
        mov portval,offset port3 ; port info structure
        mov flags.comflg,3      ; set port ident
        jmp short coms32        ; Success
coms31: mov ah,prstr
        mov dx,offset erms41
        int dos
        mov dx,offset deverr
        int dos
        ret
coms32: mov prthnd,ax           ; Save handle.
        mov ah,ioctl
        mov al,00h              ; get device info
        xor dx,dx
        mov bx,prthnd           ; port's handle
        int dos
        jc coms41               ; c = error
        or dl,20h               ; set binary mode in device info
        mov dh,0
        mov ah,ioctl
        mov al,01h              ; set device info
        int dos
        jc coms41               ; c = error
        ret
coms4:  mov dx,offset hnd2      ; Let user supply file handle.
        call prompt
        mov ah,cmtxt
        mov bx,offset rdbuf     ; Where to put input.
        mov dx,offset hndhlp    ; In case user wants help.
        call comnd
         jmp coms41             ; No go.
         nop
        cmp ah,4                ; Right amount of data?
        ja coms41               ; Too many chars.
        mov comand.cmstat,cmcfm ; simulate a confirm has been requested
        mov si,offset rdbuf
        call atoi               ; Convert to real number in ax
         jmp coms41             ; Keep trying.
         nop
        mov portval,offset port4 ; port info structure
        mov flags.comflg,4      ; set port ident
        jmp coms32              ; go complete processing
coms41: mov ah,prstr            ; Else, issue a warning.
        mov dx,offset hnderr
        int dos
        ret                     ; Yes, fail.
COMS    ENDP

; Set heath emulation on/off.

VTS     PROC    NEAR
        jmp notimp
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
        ret
DUMPSCR ENDP

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

; Initialize variables to values used by the generic MS DOS version.

lclini: mov flags.vtflg,0       ; Don't to terminal emulation.
        mov prthnd,0            ; No handle yet
        mov flags.remflg,dserial ; set serial display mode
;;      call opnprt             ; Get file handle for comm port.
        call msuinit            ; declare keyboard translator present
        ret

; Get a file handle for the communications port.  Use DOS call to get the
; next available handle.  If it fails, ask user what value to use (there
; should be a predefined handle for the port, generally 3).  The open
; will fail if the system uses names other than "COM1" or "COM2".
opnprt: mov al,flags.comflg
        dec al                  ; com1 is 1, com2 is 2, etc
        mov ah,0
        push si
        mov si,ax
        shl si,1                ; double index
        mov dx,prttab[si]       ; table of port names
        pop si
        mov ah,open2            ; open file/device
        mov al,2                ; for reading/writing
        int dos
        jnc opnpr2              ; nc = no error so far
        mov ah,prstr            ; It didn't like the string.
        mov dx,offset erms41
        int dos
        mov dx,offset hnd1
        int dos
opnpr0: mov dx,offset hnd2      ; Ask user to supply the handle.
        call prompt
        mov ah,cmtxt
        mov bx,offset rdbuf     ; Where to put input.
        mov dx,offset hndhlp    ; In case user wants help.
        call comnd
         jmp opnpr3             ; Maybe user typed a ^C.
         nop
        mov si,offset rdbuf
        call atoi               ; Convert to real number
         jmp opnpr0             ; Keep trying.
         nop
        mov prthnd,ax           ; Value returned in AX
        ret
opnpr2: mov prthnd,ax           ; Call succeeded.
        mov ah,ioctl
        mov al,00h              ; get device info
        xor dx,dx
        mov bx,prthnd           ; port's handle
        int dos
        or dl,20h               ; set binary mode in device info
        mov dh,0
        mov ah,ioctl
        mov al,01h              ; set device info
        int dos
        ret
opnpr3: cmp flags.cxzflg,'C'    ; Did user type a ^C?
        jne opnpr4              ; No, don't say anything.
        mov ah,prstr            ; Else, issue a warning.
        mov dx,offset hnderr
        int dos
opnpr4: ret                     ; Yes, fail.

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

; Initialization for using serial port.  Returns normally.
; Attempts to put port device in binary mode. [jrd]
SERINI  PROC    NEAR
        cld                     ; Do increments in string operations
        cmp     prthnd,0        ; Got a handle yet?
        jne     serin0          ; ne = yes, just go on
        push    bx
        call    opnprt          ; Else 'open' the port
        pop     bx
serin0:;;;;     call clrbuf     ; Clear input buffer.
        push    bx
        mov     bx,portval      ; get port
        mov     parmsk,0ffh     ; parity mask, assume parity is None
        cmp     [bx].parflg,parnon ; is it None?
        je      serin1          ; e = yes
        mov     parmsk,07fh     ; no, pass lower 7 bits as data
serin1: mov     bx,[bx].flowc   ; get flow control chars
        mov     flowoff,bl      ; xoff or null
        mov     flowon,bh       ; xon or null
        pop     bx
        ret                     ; We're done.
SERINI  ENDP

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

SERRST  PROC    NEAR
        ret                     ; All done.
SERRST  ENDP

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

BEEP    PROC    NEAR
        mov dl,bell
        mov ah,dconio
        int dos
        ret
BEEP    ENDP
;


; Dumb terminal emulator.  Doesn't work too well above 1200 baud (and
; even at 1200 baud you sometimes lose the first one or two characters
; on a line). Does capture (logging), local echo, debug display, tests
; for printer/logging device not ready. Uses keyboard translator
; 20 March 1987 [jrd].
term    proc    near
        mov     argadr,ax               ; save argument ptr
        mov     si,ax                   ; this is source
        mov     di,offset ourarg        ; place to store arguments
        mov     ax,ds
        mov     es,ax                   ; address destination segment
        mov     cx,size termarg
        cld
        rep     movsb                   ; copy into our arg blk
        and     ourarg.flgs,not (prtscr) ; no screen printing at startup
        mov     ax,ourarg.captr
        mov     captrtn,ax              ; buffer capture routine
        mov     parmsk,0ffh             ; parity mask, assume parity = None
        cmp     ourarg.parity,parnon    ; is parity None?
        je      term1                   ; e = yes, keep all 8 bits
        mov     parmsk,07fh             ; else keep lower 7 bits
term1:  call    portchr         ; get char from port, apply parity mask
        jnc     short term2     ; nc = no char, go on
        call    outtty          ; display and capture char
        jmp     term1           ; do quick loop back for more
term1b: call    outtty          ; display and capture char
term2:  call    keybd           ; keyboard translator to read and send text
        jnc     term1           ; nc = do not exit Connect mode
term4:  ret
term    endp

;; keyboard translator action routines, system dependent, called from msugen.
; These are invoked by a jump instruction. Return carry clear for normal
; processing, return carry set exit Connect mode (kbdflg has transfer char).

chrout: call    outprt                  ; put char in al to serial port
        clc                             ; stay in Connect mode
        ret

trnprs: push    ax                      ; toggle Copy screen to printer
        test    ourarg.flgs,prtscr      ; are we currently printing?
        jnz     trnpr2                  ; nz = yes, its on and going off
        mov     ah,ioctl
        mov     al,7                    ; get output status of printer
        push    bx
        mov     bx,4                    ; file handle for system printer
        int     dos
        pop     bx
        jc      trnpr1                  ; c = printer not ready
        cmp     al,0ffh                 ; Ready status?
        je      trnpr2                  ; e = Ready
trnpr1: call    beep                    ; Not Ready, complain
        jmp     trnpr3                  ; and ignore request
trnpr2: xor     ourarg.flgs,prtscr      ; flip the flag
trnpr3: pop     ax
        clc
        ret


klogon  proc    near                    ; resume logging (if any)
        test    flags.capflg,logses     ; session logging enabled?
        jz      klogn                   ; z = no, forget it
        or      ourarg.flgs,capt        ; turn on capture flag
klogn:  clc
        ret
klogon  endp

klogof  proc    near                    ; suspend logging (if any)
        and     argadr.flgs,not capt    ; stop capturing
klogo:  clc
        ret
klogof  endp

snull:  mov     ah,0                    ; send a null
        call    outchr                  ; send without echo or logging
         nop
         nop
         nop
        clc
        ret

kdos:   mov     al,'P'                  ; Push to DOS
        jmp     short cmdcom
cstatus:mov     al,'S'                  ; these commands exit Connect mode
        jmp     short cmdcom
cquit:  mov     al,'C'
        jmp     short cmdcom
cquery: mov     al,'?'
        jmp     short cmdcom
chang:  mov     al,'H'                  ; Hangup, drop DTR & RTS
;;;     jmp     short cmdcom
cmdcom: mov     kbdflg,al               ; pass char to msster.asm via kbdflg
        stc                             ; say exit Connect mode
        ret
                                        ;; end of action routines

; put the character in al to the screen, do capture and printing,
; does translation for Set Input command.
; Adapted from msyibm.asm [jrd]
outtty  proc    near
        test    flags.remflg,d8bit      ; keep 8 bits for displays?
        jnz     outnp8                  ; nz = yes, 8 bits if possible
        and     al,7fh                  ; remove high bit
outnp8: cmp     rxtable+256,0           ; is translation off?
        je      outnp7                  ; e = yes, off
        push    bx                      ; Translate incoming char
        mov     bx,offset rxtable       ; address of translate table
        xlatb                           ; new char is in al
        pop     bx
outnp7:
        push    bx
        mov     bx,argadr               ; args from msster directly
        test    [bx].flgs,capt          ; capturing output? Can be shut off
        pop     bx                      ;  if out dev becomes not ready.
        jz      outnoc                  ; no, forget this part
        push    ax                      ; save char
        call    captrtn                 ; give it captured character
        pop     ax                      ; restore character and keep going
outnoc: test    ourarg.flgs,prtscr      ; should we be printing?
        jz      outnop                  ; no, keep going
        push    ax
        mov     ah,print_out            ; write to system printer device
        mov     dl,al
        int     dos
        pop     ax
        jnc     outnop                  ; nc = successful print
        push    ax
        call    beep                    ; else make a noise and
        call    trnprs                  ;  turn off printing
        pop     ax
outnop: cmp     flags.vtflg,0           ; emulating a terminal?
        jnz     outnop1                 ; nz = yup, go do something smart
        test    ourarg.flgs,trnctl      ; debug? if so use dos tty mode
        jz      outnp4                  ; z = no
        mov     ah,conout
        cmp     al,7fh                  ; Ascii Del char or greater?
        jb      outnp1                  ; b = no
        je      outnp0                  ; e = Del char
        push    ax                      ; save the char
        mov     dl,7eh                  ; output a tilde for 8th bit
        int     dos
        pop     ax                      ; restore char
        and     al,7fh                  ; strip high bit
outnp0: cmp     al,7fh                  ; is char now a DEL?
        jne     outnp1                  ; ne = no
        and     al,3fH                  ; strip next highest bit (Del --> '?')
        jmp     outnp2                  ; send, preceded by caret
outnp1: cmp     al,' '                  ; control char?
        jae     outnp3                  ; ae = no
        add     al,'A'-1                ; make visible
outnp2: push    ax                      ; save char
        mov     dl,5eh                  ; caret
        int     dos                     ; display it
        pop     ax                      ; recover the non-printable char
outnp3: mov     dl,al
        int     dos
        ret
outnp4: cmp     al,bell                 ; bell (Control G)?
        jne     outnp5                  ; ne = no
        jmp     beep                    ; use short beep, avoid char loss.
outnop1:
outnp5: test    flags.remflg,d8bit      ; keep 8 bits for displays?
        jnz     outnp9                  ; nz = yes, 8 bits if possible
        and     al,7fh                  ; remove high bit
outnp9: mov     ah,conout               ; dostty screen mode
        mov     dl,al                   ; write without intervention.
        int     dos                     ; else let dos display char
        ret                             ; and return
outtty  endp


; send the character in al out to the serial port; handle echoing.
; Can send an 8 bit char while displaying only 7 bits locally.
outprt  proc    near
        test    ourarg.flgs,lclecho     ; echoing?
        jz      outpr1                  ; z = no, forget it
        push    ax                      ; save char
        call    outtty                  ; print it
        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

; Get a char from the serial port manager
; returns with carry on if a character is available
portchr proc    near
        call    prtchr                  ; character at port?
         jmp    short portc1            ; yes
         nop
portc0: clc                             ; no carry -> no character
        ret                             ; and return...
portc1: and     al,parmsk               ; apply 8/7 bit parity mask
        or      al,al                   ; catch nulls
        jz      portc0                  ; z = null, ignore it
        cmp     al,del                  ; catch dels
        je      portc0                  ; e = del, ignore it
portc2: stc                             ; have a character
        ret                             ; and return
portchr 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
