; Date: 15 Oct 85
; HP Portable Kermit
;       for HP110 and HP Portable Plus
;       Port 1: Serial, Port 2: internal modem
;       Defaults: even parity, 9600 baud: serial, 1200 internal modem
;       Internal modem code only works on HP Portable Plus
;       15 Nov 85:
;          Added code to shut off serial port and modem when quitting Kermit
;       11 Jan 86;
;          change msdefs.h to mssdef.h for kermit 2.28 jrd
;
; Add global entry point vtstat for use by Status in mssset.
; Also trimmed off trailing commas in publics. Joe R. Doupnik 12 March 1986

                page    80,132

PLUS    equ     0

        public  serini, serrst, clrbuf, outchr, coms, vts, vtstat, dodel
        public  ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl
        public  dodisk, getbaud, beep
        public  count, xofsnt, puthlp, putmod, clrmod, poscur
        public  sendbr, term, machnam, setktab, setkhlp, showkey
        include mssdef.h

false   equ     0
true    equ     1
instat  equ     6
rddev   equ     3fH
open    equ     3dH
close   equ     3eH

; 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

ifdef   PLUS
machnam db      'HP-PLUS$'
else
machnam db      'HP-110$'
endif
erms20  db      cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40  db      cr,lf,'?Warning: Unrecognized baud rate$'
erms41  db      cr,lf,'?Warning: Cannot open com port$'
erms50  db      cr,lf,'Error reading from device$'
badbd   db      cr,lf,'Unimplemented baud rate$'
badpar  db      cr,lf,'Unimplemented parity$'
noimp   db      cr,lf,'Command not implemented.$'
shkmsg  db      'Not implemented.'
shklen  equ     $-shkmsg
baudstr db      'SB'                    ; string used in setting baud rate
baudx   db      0,';'
par_str db      'P'                     ; string used to set parity
par_x   db      0,';'
brk_on  db      'B1;'                   ; start sending breaks
brk_off db      'B0;'                   ; stop sending breaks
chk_msg db      0BFH,';'                ; check serial buffer
$m0     db      'M0;'
$m1     db      'M1;'
$off_m  db      'M3;M5;'
ini_msg db      'C2;'                   ; XON/XOFF
        db      'LI1;'                  ; DTR on
        db      'SS0;'                  ; 1 Stop Bit
        Db      'SW1;'                  ; 7 Bit Length
setktab db      0
setkhlp db      0
crlf    db      cr,lf,'$'
delstr  db      BS,BS,' ',BS,'$'
clrlin  db      cr,esc,'K$'
clreol  db      esc,'K$'
telflg  db      0               ; non-zero if we're a terminal.
xofsnt  db      0               ; Say if we sent an XOFF.
xofrcv  db      0               ; Say if we received an XOFF.
invseq  db      esc,'&dB$'      ; Reverse video
nrmseq  db      esc,'&d@$'      ; Normal video
ivlseq  db      80 dup (' '),cr,'$'     ; make line inverse video
prthnd  dw      0               ; Port handle.
tempbuf dw      10 dup(?)
prttab  dw      com2,com1
com1    db      'COM1',0
com2    db      'AUX',0
blank   db      esc,'H',esc,'J$'
movcur  db      esc,'&a'
colno   db      20 dup(?)
ten     db      10
tmp     db      ?,'$'
temp    dw      0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
rdbuf   db      20 dup(?)       ; Buffer for input.

; 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,'AUX$'
        dw      00H

;       variables for serial interupt handler

source  db      bufsiz  DUP(?)  ; buffer for data from port
bufout  dw      0               ; buffer removal pointer
count   dw      0               ; number of chars in int buffer
bufin   dw      0               ; buffer insertion pointer


ourarg  termarg <>

datas   ends

code    segment public
        extrn   comnd:near, dopar:near, prserr:near, atoi:near, prompt: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.
; Returns normally.

CLRBUF  PROC    NEAR
        cli
        mov     ax,offset source
        mov     bufin,ax
        mov     bufout,ax
        mov     count,0
        sti
        ret
CLRBUF  ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL  PROC    NEAR
        mov     ah,prstr
        mov     dx,offset clreol
        int     dos
        ret
CLEARL  ENDP

; Put the char in AH to the serial port.
; Skip returns on
; success, returns normally if the character cannot be written.

outchr: mov     bp,portval
        cmp     ds:[bp].floflg,0        ; Are we doing flow control.
        je      outch2          ; No, just continue.
        xor     cx,cx           ; clear counter
outch1: cmp     xofrcv,true             ; Are we being held?
        jne     outch2          ; No - it's OK to go on.
        loop    outch1          ; held, try for a while
        mov     xofrcv,false    ; timed out, force it off and fall thru.
outch2: push    dx                      ; Save register.
        push    cx
        push    bx
        cmp     prthnd,0        ; do we have a port handle?
        jne     outch3          ; yes, go on
        push    ax
        call    opnprt          ; open the port
        pop     ax
outch3: mov     dl,ah
        mov     ah,punout               ; Output char in DL to comm port.
        int     dos
        pop     bx
        pop     cx
        pop     dx
        jmp     rskp

; This routine blanks the screen.  Returns normally.

CMBLNK  PROC    NEAR
        mov     ah,prstr
        mov     dx,offset blank
        int     dos
        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
ifdef   PLUS
        mov     dx,1800H        ; now address line 24
else
        mov     dx,0F00H        ; now address line 15
endif
        call    poscur
        mov     dx,offset invseq        ; put into inverse video
        mov     ah,prstr
        int     dos
        pop     dx              ; get message back
        int     dos             ; print it
        mov     dx,offset nrmseq        ; normal video
        int     dos
        ret
putmod  endp

; clear the mode line written by putmod.  Returns normally.
clrmod  proc    near
ifdef   PLUS
        mov     dx,1800H
else
        mov     dx,0F00H
endif
        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    ax              ; preserve this
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        pop     si              ; point to string again
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                   ; get a byte
        cmp     al,0            ; end of string?
        je      puth2
        mov     dl,al
        mov     ah,conout
        int     dos             ; else write to screen
        cmp     al,lf           ; line feed?
        je      puth0           ; yes, clear the next line
        jmp     puth1
puth2:  mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     dx,offset nrmseq        ; normal video
        int     dos
        ret
puthlp  endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD  PROC    NEAR
        mov     bx,portval
        mov     dx,[bx].baud            ; get baud rate from table
        cmp     dx,0
        jb      bd_bd
        cmp     dx,10
        je      bd_bd
        cmp     dx,15
        ja      bd_bd
;
        cmp     dx,10
        jb      baud_4
        cmp     dx,11
        jne     baud_1
        mov     dl,'A'
        jmp     baud_5
baud_1: cmp     dx,12
        jne     baud_2
        mov     dx,'C'
        jmp     baud_5
baud_2: cmp     dx,13
        jne     baud_3
        mov     dx,'E'
        jmp     baud_5
baud_3: cmp     dx,14
        jne     bd_bd
        mov     dx,'F'
        jmp     baud_5
baud_4: add     dl,'0'                  ; make # printable
baud_5: mov     baudx,dl                ; put into baud rate message
        mov     cx,4
        mov     dx,offset baudstr       ; point to message
        cmp     prthnd,0                ; is port open
        je      Baud_R                  ; No, skip hardware set
        call    w_ioctl                 ; write to ioctl of serial port
Baud_R: ret                             ; ...and return
;
bd_bd:  mov     ah,prstr
        mov     dx,offset badbd         ; Say it's not implemented.
        int     dos
        mov     bx,portval
        mov     [bx].baud,0FFFFH        ; So it's not a recognized value.
        ret                             ; Must be set before starting Kermit.
DOBAUD  ENDP

DOPARITY PROC   NEAR
        mov     bx,portval
        mov     dl,[bx].parflg          ; get parity flag
        cmp     dl,0
        jne     par_1
        mov     dx,'0'                  ; even parity
        jmp     par_3
par_1:  cmp     dl,2
        jne     par_2
        mov     dx,'4'                  ; no parity
        jmp     par_3
par_2:  cmp     dl,3
        jne     bd_par
        mov     dx,'1'                  ; odd parity
par_3:  mov     par_x,dl                ; set up string
        mov     dx,offset par_str       ; point to string
        mov     cx,3                    ; 3 lettters in string
        cmp     prthnd,0                ; is port open
        je      Par_R                   ; No, skip hardware set
        call    w_ioctl                 ; write to ioctl of serial port
Par_R:  ret                             ; ...and return
bd_par: mov     bx,portval
        mov     [bx].parflg,0
        mov     ah,prstr
        mov     dx,offset badpar
        int     dos
        ret
DOPARITY 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
;
;       write cx bytes to ioctl of serial port
;
W_IOCTL PROC    NEAR
        mov     ah,44H
        mov     al,3
        mov     bx,prthnd
        int     dos
        ret
W_IOCTL ENDP
;
;       read cx bytes from ioctl of serial port to tempbuf
;
R_IOCTL PROC    NEAR
        mov     ah,44H
        mov     al,2
        mov     bx,prthnd
        mov     dx,offset tempbuf
        int     dos
        ret
R_IOCTL ENDP
;
;       check serial port for characters and return number in al
;
CHK_BUFF PROC   NEAR
        mov     dx,offset chk_msg
        mov     cx,2
        call    w_ioctl
        mov     cx,1
        call    r_ioctl
        mov     ax,tempbuf
        ret
CHK_BUFF ENDP


; Use for DOS 2.0 and above.  Check the port status.  If no data, skip
; return.  Else, read in a char and return.
PRTCHR  PROC    NEAR
        push    bx
        push    cx
        push    si
        push    bp
        cmp     count,0                 ; any chars in buffer?
        jne     prtch2                  ; ...yes, get one
        cmp     prthnd,0                ; got a handle
        jne     prtch1
        call    opnprt                  ; open port if not
prtch1: call    chk_buff                        ; any chars at port?
        or      al,al
        jz      prtch4          ; no, go to skip return
        mov     ah,0
        mov     cx,1
        mov     count,cx
        mov     bx,prthnd
        mov     ah,rddev
        mov     dx,offset source
        int     dos
        jc      prt3x
        mov     bufout,offset source
prtch2: dec     count
        mov     si,bufout
        lodsb
        mov     bufout,si
        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
prt3x:  mov     ah,prstr
        mov     dx,offset erms50
        int     dos
prtch4: pop     bp
        pop     si
        pop     cx
        pop     bx
        jmp     rskp            ; no chars...
PRTCHR  ENDP

; Send a break out the current serial port.  Returns normally.
SENDBR  PROC    NEAR
        mov     dx,offset brk_on
        mov     cx,3
        call    w_ioctl
        mov     dx,offset brk_off
        mov     cx,3
        call    w_ioctl
        ret
SENDBR  ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
POSCUR  PROC    NEAR
        mov     ax,ds
        mov     es,ax                   ; address data segment!!
        cld
        mov     di,offset colno
        mov     al,dl                   ; column
        call    nout
        mov     al,'x'
        stosb
        mov     al,dh                   ; row
        call    nout
        mov     al,'Y'
        stosb
        mov     al,'$'
        stosb
        mov     dx,offset movcur
        mov     ah,prstr
        int     dos                     ; print the sequence
        ret
POSCUR  ENDP

NOUT    PROC    NEAR
        cbw                     ; extend the word
        div     byte ptr ten    ; divide by ten
        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                   ; put in buffer
        ret                     ; and return
NOUT    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
        call    serr_x          ; force close on serial port
        mov     dx,offset comptab
        mov     bx,0
        mov     ah,cmkey
        call    comnd
        jmp     r
        push    bx
        mov     ah,cmcfm
        call    comnd              ; Get a confirm.
        jmp     comx            ;  Didn't get a confirm.
        nop
        pop     bx
        mov     flags.comflg,bl     ; Set the comm port flag.
        cmp     flags.comflg,1  ; Using Com 1?
        jne     coms0           ; Nope.
        mov     ax,offset port1
        mov     portval,ax
        ret
coms0:  mov     ax,offset port2
        mov     portval,ax
        ret
comx:   pop     bx
        ret
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

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 do terminal emulation.
        call    opnprt          ; Get file handle for comm port.
        mov     bx,offset port1
        mov     [bx].baud,B9600 ; port1 -> 9600 baud
        mov     [bx].parflg,0   ;          even parity
        mov     bx,offset port2
        mov     [bx].baud,B1200 ; port2 -> 1200 baud
        mov     [bx].parflg,0   ;          even parity
        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
        mov     ah,0
        mov     si,ax
        shl     si,1            ; double index
        mov     dx,prttab[si]
        mov     ah,open
        mov     al,2
        int     dos
        jnc     opnpr1
        mov     ah,prstr                ; It didn't like the string.
        mov     dx,offset erms41
        int     dos
        ret
opnpr1: mov     prthnd,ax               ; Call succeeded.
        ret

showkey:
        mov     ax,offset shkmsg
        mov     cx,shklen
        ret

; Initialization for using serial port.  Returns normally.
SERINI  PROC    NEAR
        cld                     ; Do increments in string operations
        cmp     prthnd,0        ; Got Handle already?
        jne     ser_x           ; ... yes, skip open
        call    opnprt          ; open handle
        mov     cx,3
        mov     ax,portval
        cmp     ax,offset port1
        jnz     seri
        mov     dx,offset $m1
        jmp     serx
seri:   mov     dx,offset $m0
serx:   call    w_ioctl
ser_x:  call    clrbuf          ; Clear input buffer.
        mov     dx,offset ini_msg
        mov     cx,15
        call    w_ioctl
        call    dobaud          ; set baud rate
        call    doparity        ; set parity
        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
        cmp     flags.extflg,0
        jne     serr_x
        ret
serr_x: mov     cx,6
        mov     dx,offset $off_m
        call    w_ioctl
        mov     bx,prthnd
        cmp     bx,0            ; none there?
        je      serr_1          ; no, don't try to close
        mov     ah,close
        int     dos             ; close handle
        mov     prthnd,0
serr_1: 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).
term    proc    near
        mov     si,ax           ; this is source
        mov     di,offset ourarg        ; place to store arguments
        mov     ax,ds
        mov     es,ax           ; address destination segment
        mov     cx,size termarg
        rep     movsb           ; copy into our arg blk
term1:  call    prtchr
        jmp     short term2             ; have a char...
        nop
        nop
        jmp     short term3             ; no char, go on
term2:  push    ax
        and     al,7fh          ; mask off parity for terminal
        mov     dl,al
        mov     ah,conout
        int     dos                     ; go print it
        pop     ax
        test    ourarg.flgs,capt        ; capturing output?
        jz      term3           ; no, forget it
        call    ourarg.captr    ; else call the routine
term3:  mov     ah,dconio
        mov     dl,0ffh
        int     dos
        jz      term1           ; no character, go on
        cmp     al,ourarg.escc  ; escape char?
        je      term4           ; yes, exit
        push    ax                      ; save char
        mov     ah,al
        or      ah,80H          ; turn on hi bit so DOS doesn't interfere
        call    outchr          ; output the character
        nop
        nop
        nop
        pop     ax
        test    ourarg.flgs,lclecho ; echoing?
        jz      term1           ; no, continue loop
        mov     dl,al
        mov     ah,dconio
        int     dos
        jmp     term1           ; else echo and keep going
term4:  ret
term    endp

; 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
