DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦72993dba2⟧ TextFile

    Length: 54246 (0xd3e6)
    Types: TextFile
    Names: »msxv90.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »msxv90.asm« 

TextFile

       name msxv90
; File MSXV90.ASM
     
; Kermit system dependent module for VICTOR 9000/SIRIUS
     
; Edit History
; Last edit: 12 June 1988
; 12 June 1988 Add error recovery if serial port fails to initialize. [jrd]
; 9 March 1988 Add procedure getmodem and global byte mdmhand for use by
;  scripts in sensing modem status. Add: ignore received XOFF if we have sent
;  an XOFF already (avoids lockouts from echoes). Add user/buffer xon/xoff
;  sensing. [jrd]
; 3 March 1988 Add shomodem routine to show status of DSR, CD, CTS lines
;   [bgp]
; 7 Sept 1987 Remove keep_delete, pass null, del chars to terminal section.
;   [jrd]
; 29 August 1987 Add capability for sending long break [bgp]
; 9 February 1987 Add flag for ignoring delete chars in SERINT - they must
;   be retained when emulating a Tektronix (graphics mode) - this is done
;   using global symbol keep_delete.
; 6 November 1986 Fix receiver overrun detection and miscellaneous minor
;   fixes
; 30 Sept 1986 Reject DEL char at serial port reception level to avoid
;   problems when DEL is used as a filler char (be Emacs). [jrd]
; 16 Sept 1986 Revise serial port routines prtchr, outchr, serini, serint
;   to use more efficient code from IBM version. [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 August 1986 Use parity mask when testing for nulls & Xon/Xoff in serial
;   port interrupt routine. [jrd]
; 16 August 1986 Use observed screen attributes for mode line. [jrd]
; 9 August 1986 Revise SERINT to insert control-G for overrun chars, give
;  faster return of interrupts to system, remove use of BP in code. [jrd]
;   Original version, BGP, 23 November 1985
; Add global entry point vtstat for use by Status routine in mssset.
; Cleared terminal emulation flag, flags.vtflg, in procedure lclini.
; Add register save/restore in procedure getbaud.
; Joe R. Doupnik 12 March 1986
; Add some register save/restores here and there.
; 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 to force DTR and RTS low in support of Kermit
; command Hangup.   B.G.Peterson 10 April 1986
; Add support of serial port settings through use of IOCTL DOS function
; code from Andreas Stumpf (ZRZS@DS0RUS1I), merged by B.G.Peterson 10 April 86
; Moved VTS and VTSTAT routines to MSYxxx.ASM where the terminal emulation
; is done anyway. B.G.Peterson 10 April 1986
; Last update 28 April 1986
; 30 July 1986 Corrected IHOSTS and IHOSTR to prevent sending null byte if
;  no flow control.
;  Modified SERHNG so it turns DTR and RTS off for 3 seconds and then back
;  on again so a new connection can be made easily.
;  Modified SERRST to wait until transmitter is empty before turning off
;  port.
;  Modified port controller access so that if opening it using the standard
;  Victor drivers fails, it will just go direct to the hardware. [bgp]
     
        public  serini, serrst, clrbuf, outchr, coms, dodel
        public  ctlu, cmblnk, locate, lclini, prtchr, dobaud
        public  clearl, dodisk, getbaud, beep, puthlp, putmod
        public  clrmod, poscur, sendbr, showkey, sendbl, pcwait ; [bgp]
        public  xofsnt, machnam, setktab, setkhlp, count
        public  ihosts, ihostr, dtrlow, comptab, serhng, mdmhand
        public  shomodem, getmodem
     
        include mssdef.h
     
FALSE   equ     0
off     equ     0
bufon   equ     1               ; buffer level xon/xoff on-state control flag
usron   equ     2               ; user level xon/xoff on-state control flag
MNTRGH  EQU     BUFSIZ*3/4      ; High point = 3/4 of buffer full.
MNTRGL  EQU     BUFSIZ/4        ; Low point = 1/4 of buffer full.
DEF_BAUD EQU    B1200           ; Default to 1200 baud
     
; constants used by serial port handler
     
SEG_7201 EQU    0E004H          ; Segment for 7201 serial controller
DATAA_7201 EQU  0               ; DATA A offset
STATA_7201 EQU  2               ; STATUS A offset
DATAB_7201 EQU  1               ; DATA B offset
STATB_7201 EQU  3               ; STATUS B offset
     
;   no interrupts, no waits
REG1_7201 EQU   0               ; 7201 Register 1 value
ENABLE_INT EQU  18H             ; Mask to turn on interrupts (OR)
;   non-DMA, non-vectored interrupts, priority type 1 (Ra>Rb>Ta>Tb)
REG2_7201 EQU   14H             ; 7201 Register 2 value
;   8 bits/char, no CRC, receiver enabled
REG3_7201 EQU   0C1H            ; 7201 Register 3 value
;   clock/16, 1.5 stop bit, no parity
REG4_7201 EQU   48H             ; 7201 Register 4 value
;   DTR low (active), 8 bits/char, transmitter enabled, RTS low, no CRC
REG5_7201 EQU   0EAH            ; 7201 Register 5 value
BREAK_ON EQU    10H             ; Mask to turn on break bit (OR)
DTR_RTS_OFF EQU 7DH             ; Mask to turn off DTR and RTS (AND)
     
MDMINP  EQU     1               ; Input ready bit. [jrd]
MDMOVER EQU     32              ; Receiver overrun bit. [bgp]
     
SEG_8253 EQU    0E002H          ; Segment for 8253 timer
SETA_8253 EQU   0               ; Speed for port A
SETB_8253 EQU   1               ; Speed for port B
CTRL_8253 EQU   3               ; Control for 8253 timer
     
SEG_8259 EQU    0E000H          ; Segment for 8259 int. controller
CW1_8259 EQU    0               ; Offset for 8259 register 1
CW2_8259 EQU    1               ; Offset for 8259 register 2
     
; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;     or port2)
; port1, port2 - portinfo structures for the corresponding ports
     
; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
; setktab - keyword table for redefining keys (should contain a 0 if
;     not implemented)
; setkhlp - help for setktab.
     
DATAS   segment public 'datas'
        extrn   drives:byte, flags:byte, trans:byte
        extrn   portval:word, port1:byte, port2:byte
     
setktab db      0
setkhlp db      CR,LF,'Set Key not supported on VICTOR/SIRIUS Kermit$'
sh_key_str db   'Set Key not supported on VICTOR/SIRIUS Kermit'
sh_key_len dw   45
machnam db      'VICTOR/SIRIUS$'
clrlin  db      CR              ; clears full line with next line...
clreol  db      ESCAPE,'K$'        ; clears from cursor to end of line
home    db      ESCAPE,'H$'        ; homes cursor
clrscr  db      ESCAPE,'E$'        ; clears screen and homes cursor
delstr  db      BS,BS,'  ',BS,BS,'$' ; Delete string - why 2???
clr_25  db      ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'l',ESCAPE,'k'
        db      ESCAPE,'y1$'
        ;clr_25 does the entire operation of clearing the mode line
start_25 db     ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'p$'
        ; start_25 enables line 25 and moves to the start in reverse video
end_25  db      ESCAPE,'q',ESCAPE,'k',ESCAPE,'y1$'
        ; end_25 turns off reverse video and line 25 and returns the cursor
mov_pfx db      ESCAPE,'Y$'        ; prefix for moves
modem   mdminfo <DATAA_7201,STATA_7201,SETA_8253,0FDH,2,61H,104H>
savsci  dw      ?               ; Save for serial port interrupt vector.
savscs  dw      ?               ; Ditto.
portin  db      FALSE           ; Has comm port been initialized.
xofsnt  db      FALSE           ; Say if we sent an XOFF.
xofrcv  db      FALSE           ; Say if we received an XOFF.
parmsk  db      ?               ; parity mask, 0ffh for no parity, 07fh with.
flowoff db      ?               ; flow-off char, Xoff or null (if no flow)
flowon  db      ?               ; flow-on char, Xon or null
overrun db      ?               ; holds status of receiver overrun
mdmhand db      0               ; Modem status register, current.
erms20  db      CR,LF,'?Warning: System has no disk drives$'
badbd   db      CR,LF,'Unimplemented baud rate$'
hngmsg  db      CR,LF,' The phone should have hung up.',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 hang up the phone.  Normally, Kermit leaves them'
        db      ' high (on) when it exits.'
        db      CR,LF,' They will return high after about 3 seconds.'
        db      CR,LF,'$'                                       ; [jrd]
shmdm0  db      CR,LF,' Modem is not ready:  DSR is off$'
shmdm1  db      CR,LF,' Modem is ready:      DSR is on$'        ; [bgp]
shmdm2  db      CR,LF,' no Carrier Detect:   CD is off$'
shmdm3  db      CR,LF,' Carrier Detect:      CD is on$'
shmdm4  db      CR,LF,' no Clear to Send:    CTS is off$'
shmdm5  db      CR,LF,' Clear to Send:       CTS is on$'        ; [bgp]
tmp     db      ?,'$'
temp1   dw      ?               ; Temporary storage.
     
ontab   db      02H             ; Two entries.
        mkeyw   'OFF',00H
        mkeyw   'ON',01H
     
comptab db      0AH
        mkeyw   '1',01H
        mkeyw   '2',00H
        mkeyw   'A',01H
        mkeyw   'B',00H
        mkeyw   'COM1',01H
        mkeyw   'COM2',00H
        mkeyw   'SERIALA',01H
        mkeyw   'SERIALB',00H
        mkeyw   'TTY',01H
        mkeyw   'UL1',00H
     
; this table is indexed by the baud rate definitions given in
; pcdefs.   Unsupported baud rates should contain FF.
;   This number is determined by 78125/(baud rate) (decimal values)
     
bddat   label   word
        dw      6B4H            ; 45.5 baud
        dw      61AH            ; 50 baud
        dw      411H            ; 75 baud
        dw      2C6H            ; 110 baud
        dw      244H            ; 134.5 baud
        dw      208H            ; 150 baud
        dw      104H            ; 300 baud
        dw      82H             ; 600 baud
        dw      41H             ; 1200 baud
        dw      2BH             ; 1800 baud
        dw      26H             ; 2000 baud
        dw      20H             ; 2400 baud
        dw      10H             ; 4800 baud
        dw      8H              ; 9600 baud
        dw      4H              ; 19200 baud
        dw      2H              ; 38400 baud
     
; variables for serial interrupt handler
     
source  db      BUFSIZ DUP(?)   ; Buffer for data from port.
srcpnt  dw      0               ; Pointer in buffer (DI).
count   dw      0               ; Number of chars in int buffer.
     
; variables for accessing portinfo from serial drivers using IOCTL function
;
; Structure is defined according to "Systems Programmers Toolkit II",
; Appendix A.       Structure contains baud rate and values of control registers
; 0 through 7.
     
pval    struc
stype   dw      11H             ; port access
status  dw      (?)
blocktype dw    0               ; serial
baudr   dw      (?)             ; baud rate to set or get
CR0     db      (?)
CR1     db      (?)
CR2A    db      (?)
CR2B    db      (?)
CR3     db      (?)
CR4     db      (?)
CR5     db      (?)
CR6     db      (?)
CR7     db      (?)
pval    ends
     
erms41  db      CR,LF,'?Warning:  Cannot open com port'
        db      CR,LF,'  Going direct to serial controller hardware...$' ; [bgp]
rdbuf   db      20 dup (?)      ; input buffer
     
plength equ     17              ; length of pval structure
oldpval pval    <,,,41H,,,,,,,,,> ; default to 1200 baud
newpval pval    <,,,41H,,,,,,,,,> ; value comes from bdtab above... [bgp]
     
prttab  dw      com2,com1       ; 0=com2, 1=com1 in flags.comflg
com1    db      'SERIALA',0     ; name string for device
com2    db      'SERIALB',0
     
IOread  equ     2               ; read status block
IOwrite equ     3               ; write status block
     
prthnd  dw      0               ; handle for accessing port
     
DATAS   ends
     
CODE    segment public
        extrn   comnd:near, dopar:near, defkey:near, sleep:near
        extrn   lclyini:near
        assume  cs:code,ds:datas
     
; local initialization
     
LCLINI  proc    near
        mov     flags.vtflg,TTHEATH ; BIOS does HEATH, use as default
        cmp     flags.comflg,1  ; using port 1?
        jne     lclini2         ; no...
        mov     ax,offset port1
        mov     portval,ax
        mov     modem.mddat,DATAA_7201 ; set COM1 values
        mov     modem.mdstat,STATA_7201
        mov     modem.mdcom,SETA_8253
        jmp     lclini0
lclini2:                        ; using port2
        mov     ax,offset port2
        mov     portval,ax
        mov     modem.mddat,DATAB_7201 ; set COM2 values
        mov     modem.mdstat,STATB_7201
        mov     modem.mdcom,SETB_8253
lclini0:
        call    opnprt          ; get file handle and init port
        call    lclyini         ; init term part too
        ret
LCLINI  endp
     
; procedure to get a file handle for the port.  if it fails ask the user
; for some predefined handle (3 is the usual value) (Andreas Stumpf)
; 30 July 1986 If it fails, just warn the user and go direct to the
;  hardware [bgp]
     
OPNPRT  proc    near
        cmp     prthnd,0        ; is one open?
        jle     opnprta         ; no...
        mov     bx,prthnd       ; better close this
        mov     ah,CLOSE2       ; to be sure they don't accumulate
        int     DOS
        mov     prthnd,0        ; done...
opnprta:
        mov     al,flags.comflg
        mov     ah,0
        mov     si,ax
        shl     si,1            ; double index
        mov     dx,prttab[si]
        mov     ah,OPEN2
        mov     al,2
        int     DOS             ; open port on handle
        jnc     opnprt2
        mov     ah,PRSTR        ; it didn't like the string...
        mov     dx,offset erms41
        int     DOS
        mov     prthnd,-1       ; no port is open! [bgp]
        push    es              ; better save this
        mov     bx,SEG_7201     ; point at controller
        mov     es,bx
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],1
        mov     byte ptr es:[bx],18H ; Software reset of current port
        push    ax              ; kill time for four 2.5 MHz cylces
        pop     ax              ; 8 processor cycles
        mov     bx,STATA_7201   ; First one is always port A
        mov     byte ptr es:[bx],2 ; must be first one set
        mov     byte ptr es:[bx],REG2_7201
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],4 ; must be second
        mov     byte ptr es:[bx],REG4_7201
        mov     byte ptr es:[bx],1 ; rest any order
        mov     byte ptr es:[bx],REG1_7201
        mov     byte ptr es:[bx],3
        mov     byte ptr es:[bx],REG3_7201
        mov     byte ptr es:[bx],5
        mov     byte ptr es:[bx],REG5_7201
        pop     es
        call    getbaud         ; use this to be sure right value is used
        call    dobaud          ; better set baud rate to the correct value too
        jmp     opnprt2a        ; all init done... [bgp]
opnprt2:
        mov     prthnd,ax       ; call succeeded - save handle
        mov     bx,ax
        mov     ah,IOCTL
        mov     al,IOread       ; get old values
        mov     cx,plength
        mov     dx,offset oldpval ; place for old values
        int     DOS
        mov     ah,IOCTL
        mov     al,IOread
        mov     dx,offset newpval ; one to work on
        int     DOS
     
; set registers to something neat
     
        cli                     ; avoid interrupts here
        mov     bx,offset newpval
        mov     [bx].CR1,REG1_7201
        mov     [bx].CR2A,REG2_7201
        mov     [bx].CR3,REG3_7201
        mov     [bx].CR4,REG4_7201
        mov     [bx].CR5,REG5_7201
        mov     bx,prthnd       ; get handle
        mov     ah,IOCTL
        mov     al,IOwrite      ; set new values
        int     DOS
opnprt2a:
        push    es              ; save this for a flash
        mov     bx,SEG_7201
        mov     es,bx
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],10H ; clear external/status interrupts
        mov     byte ptr es:[bx],30H ; clear special receive cond. int.
        mov     byte ptr es:[bx],38H ; set to end of interupt
        pop     es              ; back again
        sti                     ; interrupts are okay again
        ret
OPNPRT  endp
     
; this is called by Kermit initialization.    It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.
;   Since VICTOR doesn't provide any simple way to get the actual
;   number of drives, we will use the number that DOS thinks we have.
     
DODISK  proc    near
        mov     ah,GCURDSK              ; Get current disk
        int     DOS
        mov     dl,al                   ; Want to reselect that one
        mov     ah,SELDSK
        int     DOS                     ; AL now has how many drives
        cmp     al,1                    ; Make sure there's at least 1
        jl      dodsk0
        mov     drives,al               ; Remember how many.
        ret
dodsk0: mov     ah,PRSTR                ; Print a warning message.
        mov     dx,offset erms20        ; I'm not sure if things will
        int     DOS                     ; work with no disks?!?!?!
        mov     drives,0                ; Say there aren't any drives.
        ret
DODISK  endp
     
; Show the definition of a key.  Since it isn't really necessary to redefine
; keys for the VICTOR/SIRIUS (assuming that KEYGEN is available), this isn't
; implemented, and the string returned to the calling sequence merely says so.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
     
SHOWKEY proc    near
        mov     ax,offset sh_key_str
        mov     cx,sh_key_len
        ret
SHOWKEY endp
     
; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.    This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.
     
CLRBUF  proc    near
        cli
        mov     ax,offset source
        mov     srcpnt,ax
        mov     count,0
        sti
        ret
CLRBUF  endp
     
; Clear to the end of the current line.  Returns normally.
     
CLEARL  proc    near
        mov     dx,offset clreol
        mov     ah,PRSTR
        int     DOS
        ret
CLEARL  endp
     
; Put the char in AH to the serial port.  This assumes the
; port has been initialized. Skip returns on success, returns ret if the
; character cannot be written.
; Add entry point OUTCH2 for non-flow controlled sending to
; prevent confusion of flow control logic at top of outchr; used by receiver
; buffer high/low water mark flow control code. [jrd]
     
OUTCHR  proc    near
        cmp     flowoff,0               ; Are we doing flow control.
        je      outch2                  ; No, just continue.
        cmp     ah,flowoff              ; sending xoff?
        jne     outch1                  ; ne = no
        mov     xofsnt,usron            ; indicate user level xoff being sent
        jmp     outch1b
outch1: cmp     ah,flowon               ; user sending xon?
        jne     outch1b                 ; ne = no
        mov     xofsnt,off           ; say an xon has been sent (cancels xoff)
outch1b:cmp     xofrcv,off              ; Are we being held (xoff received)?
        je      outch2                  ; e = no - it's OK to go on.
        cmp     flags.timflg,0          ; is timer off?
        je      outch2                  ; e = yes, no timeout period
        push    cx                      ; save reg
        mov     ch,trans.rtime          ; receive timeout interval (sec)
        mov     cl,0                    ;  convert to 4 millsec increments
        jcxz    outch1c                 ; z = no timeout wanted.
     
outch1a:cmp     xofrcv,off              ; Are we being held (xoff received)?
        je      outch1c                 ; e = no - it's OK to go on.
        push    ax
        mov     ax,4                    ; 4 millisec wait loop
        call    pcwait
        pop     ax
        loop    outch1a                 ; and try it again
        mov     xofrcv,off              ; timed out, force it off and fall thru
outch1c:pop     cx                      ; end of flow control section
                     ; OUTCH2 is entry point for sending without flow control
OUTCH2: mov     al,ah                   ; Parity routine works on AL.
        call    dopar                   ; Set parity appropriately.
        mov     ah,al                   ; Don't overwrite character with status
        push    bx
        push    cx
        push    es
        xor     cx,cx
        mov     bx,SEG_7201     ; point at 7201
        mov     es,bx
        mov     bx,modem.mdstat
outch3:
        mov     al,es:[bx]
        test    al,4            ; ready?
        jnz     outch4          ; yes
        loop    outch3
        jmp     outch5          ; Timeout
outch4:
        mov     al,ah           ; Now send it out
        mov     bx,modem.mddat
        mov     es:[bx],al
        pop     es
        pop     cx
        pop     bx
        jmp     rskp
outch5:
        pop     es
        pop     cx
        pop     bx
        ret
OUTCHR  endp
     
; This routine blanks the screen.   Returns normally.
     
CMBLNK  proc    near
        mov     dx,offset clrscr
        mov     ah,PRSTR
        int     DOS
        ret
CMBLNK  endp
     
; Locate: homes the cursor.     Returns normally.
     
LOCATE  proc    near
        mov     dx,offset home
        mov     ah,PRSTR
        int     DOS
        ret
LOCATE  endp
     
; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a dollar sign.  Returns normally.
     
PUTMOD  proc    near
        push    si              ; better save this
        push    dx              ; preserve message
        mov     dx,offset start_25 ; to set up for write to 25
        mov     ah,PRSTR
        int     DOS
        mov     ah,DCONIO       ; output a char at a time
        pop     si              ; get back message
        cld                     ; better increment
putmod1:
        lodsb                   ; get byte
        cmp     al,'$'          ; is it end of string?
        je      putmod2
        mov     dl,al
        int     DOS
        jmp     putmod1
putmod2:
        mov     dx,offset end_25 ; back to normal
        mov     ah,PRSTR
        int     DOS
        pop     si              ; and restore it
        ret
PUTMOD  endp
     
; clear the mode line written by putmod.  Returns normally.
     
CLRMOD  proc    near
        mov     dx,offset clr_25 ; to clear line 25
        mov     ah,PRSTR
        int     DOS
        ret
CLRMOD  endp
     
; put a help message on the screen.     This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
     
PUTHLP  proc    near
        push    si
        mov     si,ax
        mov     ah,DCONIO       ; don't check anything...
        cld                     ; better increment on strings
puthlp1:
        lodsb                   ; get byte
        cmp     al,0            ; is it null (null-terminated string)
        je      puthlp2
        mov     dl,al
        int     DOS
        jmp     puthlp1
puthlp2:
        mov     dl,13           ; want a crlf
        int     DOS
        mov     dl,10
        int     DOS
        pop     si
        ret
PUTHLP  endp
     
; Set the baud rate for the current port, based on the value
; in the portinfo structure. Returns normally. Method of setting using
; IOCTL thanks to Andreas Stumpf
     
DOBAUD  proc    near
        push    ax              ; these too [jrd]
        push    bx
        push    cx
        push    dx
        mov     bx,portval
        mov     bx,[bx].baud
        cmp     bx,0
        jl      unk_baud        ; out of range
        cmp     bx,BAUDSIZ
        jge     unk_baud        ; out of range
        shl     bx,1            ; get index into table
        mov     ax,offset bddat ; start of table
        add     bx,ax
        mov     ax,[bx]         ; get divider
        cmp     ax,0FFH         ; unimplemented baud rate?
        je      unk_baud        ; that's right
        mov     bx,offset newpval
        mov     [bx].baudr,ax   ; set it in structure no matter what [bgp]
        cmp     prthnd,0        ; anything open?
        jge     dobaud0         ; yes, do it nice
        push    es              ; better save this...
        push    ax              ; save divider
        mov     bx,SEG_8253     ; point at timer
        mov     es,bx
        mov     bx,CTRL_8253    ; set up function first
        mov     ax,modem.mdcom  ; set up control byte
        ror     al,1            ; need port number in high bits
        ror     al,1
        and     al,0C0H         ; keep only top 2 bits
        add     al,36H          ; set both, Mode 3, binary
        mov     es:[bx],al
        mov     bx,modem.mdcom  ; Where to write the rate
        pop     ax              ; get divider back
        mov     es:[bx],al
        mov     es:[bx],ah      ; done
        pop     es
        jmp     dobaud1
dobaud0:                        ; [bgp]
        mov     ah,IOCTL
        mov     al,IOwrite
        mov     bx,prthnd       ; set the poor thing
        mov     cx,plength
        mov     dx,offset newpval
        int     DOS
dobaud1:
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
unk_baud:
        mov     ah,PRSTR
        mov     dx,offset badbd ; Give an error message.
        int     DOS
        pop     dx              ; restore regs [jrd]
        pop     cx
        pop     bx
        pop     ax
        ret
DOBAUD  endp
     
; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.   Returns normally.
; This is used during initialization.       The method of getting the baud
; rate directly from the port handler is thanks to Andreas Stumpf.
; Note that this assumes that the thing has a defined baud rate to
; start with from the initialization and the opnprt has been called on
; the current port so that the values in newpval are defined.
     
GETBAUD proc    near
        push    ax
        push    bx
        push    cx
        push    dx
        cmp     prthnd,0        ; opened?
        jne     go_gb           ; yes, get the rate
        call    opnprt          ; no, open it
go_gb:
        mov     bx,offset newpval
        mov     ax,[bx].baudr
        mov     bx,offset bddat+(BAUDSIZ-1)*2
        mov     cx,BAUDSIZ      ; length of baudtable
loop_gb:
        cmp     ax,[bx]
        je      have_gb
        dec     bx
        dec     bx
        dec     cx
        jnz     loop_gb
have_gb:
        dec     cx              ; value is one greater than desired
        mov     bx,portval      ; cx=-1 means unrecognized (dropped off bottom)
        mov     [bx].baud,cx
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
GETBAUD endp
     
; Skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
; Revised 22 May 1986, and again slightly 2 August 1986 by [jrd]
; Direct copy from msxibm.asm [jrd]
     
PRTCHR  proc    near
        call    chkxon          ; see if we need to xon
        cmp     count,0         ; any characters available?
        jnz     prtch1          ; nz = yes, get one
        mov     dx,0            ; return count of zero
        jmp     rskp            ; No data - check console.
prtch1:
        push    si              ; save si
        cli                     ; interrupts off, to keep srcpnt & count consist
ent
        mov     si,srcpnt       ; address of next available slot in buffer
        sub     si,count        ; minus number of unread chars in buffer
        cmp si,offset source    ; located before start of buffer (wrapped)?
        jae     prtch2          ; ae = no
        add     si,BUFSIZ       ; else do arithmetic module bufsiz
prtch2:
        mov     al,byte ptr [si] ; get a character into si
        dec     count           ; one less unread char now
        sti                     ; interrupts back on now.
        pop     si
        mov     dx,count        ; return # of chars in bufer
        ret
PRTCHR  endp
     
     
; local routine to see if we have to transmit an xon
chkxon  proc    near
        cmp     flowon,0                ; doing flow control?
        je      chkxo1                  ; no, skip all this
        test    xofsnt,usron            ; did user send an xoff?
        jnz     chkxo1                  ; nz = yes, don't contradict it here
        test    xofsnt,bufon            ; have we sent a buffer level xoff?
        jz      chkxo1                  ; z = no, forget it
        cmp     count,mntrgl            ; below (low water mark) trigger?
        jae     chkxo1                  ; no, forget it
        mov     ah,flowon               ; ah gets xon
        and     xofsnt,off              ; remember we've sent the xon.
        call    outch2              ; send via non-flow controlled entry point
         nop
         nop
         nop                            ; in case it skips
chkxo1: ret
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]
; 30 July 1986 Avoid sending nulls if no flow control [bgp]
     
IHOSTS  proc    near
        push    ax              ; save the registers
        push    cx
        push    dx
        mov     xofrcv,off      ; clear old xoff received flag
        mov     xofsnt,off      ; and old xoff sent flag
        mov     ah,flowon       ; put Go-ahead flow control char in ah
        or      ah,ah           ; check for null char
        jz      ihosts1         ; z=null, don't send it
        call    outchr          ; send it (release Host's output queue)
         nop                    ; outchr can do skip return
         nop
         nop
ihosts1:call    clrbuf          ; clear out interrupt buffer
        pop     dx              ; empty buffer. we are done here.
        pop     cx
        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]
; 30 July 1986 Avoid sending null if no flow control [bgp]
     
IHOSTR  proc    near
        push    ax              ; save reg
        mov     xofrcv,off      ; clear old xoff received flag
        mov     xofsnt,off      ; and old xoff sent flag
        mov     ah,flowon       ; put Go-ahead flow control char in ah
        or      ah,ah           ; check for null char
        jz      ihostr1         ; z=null, don't send it
        call    outchr          ; send it (release Host's output queue)
         nop                    ; outchr can do skip return
         nop
         nop
ihostr1:pop     ax
        ret
IHOSTR  endp
     
; Global proc to hang up the phone by making DTR and RTS low.
     
DTRLOW  proc    near
        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
         ret
         nop                    ; dingy skip returns...
         nop
        call    serhng          ; drop DTR and RTS
        mov     ah,PRSTR        ; give a nice message
        mov     dx,offset hngmsg
        int     dos
        jmp     rskp
DTRLOW  endp
     
; SERHNG us used to hang up the phone.  This resets the port (by calling
; serrst), and then forces DTR and RTS low to terminate the connection.
; 12 April 1986 [bgp]
; 30 July 1986 Turn them back on again after 3 seconds so new connection can
;  be made [bgp]
     
SERHNG  proc    near
        call    serrst          ; reset the port to be sure
        push    dx
        push    cx
        push    bx
        push    ax
        cmp     prthnd,0        ; nice method open? [bgp]
        jge     serhng0         ; yes
        push    es              ; better save this
        mov     bx,SEG_7201     ; point at controller
        mov     es,bx
        mov     bx,modem.mdstat
        cli                     ; no interrupts please
        mov     byte ptr es:[bx],5 ; register 5
        mov     byte ptr es:[bx],REG5_7201 and DTR_RTS_OFF
        sti                     ; interrupts ok again
        mov     ax,3            ; sleep for 3 seconds
        call    sleep
        mov     bx,SEG_7201
        mov     es,bx
        mov     bx,modem.mdstat
        cli                     ; no interrupts please
        mov     byte ptr es:[bx],5
        mov     byte ptr es:[bx],REG5_7201
        sti                     ; interrupts ok again
        pop     es
        jmp     serhng1         ; [bgp]
serhng0:
        mov     bx,offset newpval
        mov     [bx].CR5,REG5_7201 and DTR_RTS_OFF
        mov     dx,bx           ; where the stuff is
        mov     cx,plength      ; how long it is
        mov     bx,prthnd       ; get handle
        mov     ah,IOCTL
        mov     al,IOwrite      ; set new values
        int     DOS
        mov     ax,3            ; sleep for 3 seconds
        call    sleep
        mov     bx,offset newpval ; turn them back on again...
        mov     [bx].CR5,REG5_7201
        mov     dx,bx
        mov     cx,plength
        mov     bx,prthnd
        mov     ah,IOCTL
        mov     al,IOwrite
        int     DOS
serhng1:
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        ret
SERHNG  endp
     
; SHOW MODEM, displays current status of lines DSR, CD, and CTS.
; Uses global byte mdmhand, the modem line status register. [jrd]
     
SHOMODEM proc   near
        mov     ah,cmcfm
        call    comnd
         ret                            ; dingy skip returns...
         nop
         nop
        mov     dx,offset shmdm0        ; assume DSR is not set
        test    mdmhand,20h             ; is bit set?
        jnz     shomodem1               ; z = no
        mov     dx,offset shmdm1        ; DSR is asserted (line active)
shomodem1:
        mov     ah,PRSTR
        int     DOS
        test    mdmhand,80h             ; test for CD
        jz      shomodem2               ; z = off
        mov     dx,offset shmdm3        ; actually have CD
shomodem2:
        int     DOS
        mov     dx,offset shmdm4        ; assume no CTS
        test    mdmhand,10H             ; test for CTS
        jz      shomodem3
        mov     dx,offset shmdm5        ; actually have CTS
shomodem3:
        int     DOS
        jmp     rskp
SHOMODEM endp
     
     
; Get modem status and set global byte mdmhand. Preserve all registers.
     
; Check the status of the return control lines from the serial port.
; Since this function doesn't care if the port has been properly
; initialized or anything else we will always go directly to the
; hardware.  This is actually quite reasonable since the values
; returned in IOCTL from earlier will have no relation to the current
; state of reality. [bgp]
; Note that the method of getting DSR is a little weird.  There are
; no connections on the 7201 for DSR so we have to get it from one
; of the 6522s where there are a few free lines (specifically, the
; one that controls the keyboard interface and the CRT brightness/
; contrast.  This one is at E8040-E804F with line PA3 for DSRA and
; PA5 for DSRB.  Note that a zero is registered for an active line
; on the DSR sense.
     
getmodem proc   near                    ; gets modem status upon request
        mov     mdmhand,0               ; assume nothing is on
        push    bx
        push    es                      ; better save these
        mov     bx,0E804H               ; segment of 6522
        mov     es,bx
        mov     bl,8                    ; assume looking at port A
        cmp     flags.comflg,1
        je      getmod1                 ; e = it is A
        mov     bl,20H                  ; actually looking at B
getmod1:test    es:[1],bl               ; is DSR bit set?
        jnz     getmod2                 ; nz = no
        or      mdmhand,20h             ; DSR is asserted (line active)
getmod2:mov     bx,SEG_7201             ; point at 7201
        mov     es,bx
        mov     bx,modem.mdstat
        test    byte ptr es:[bx],8      ; test for CD
        jz      getmod3                 ; z = off
        or      mdmhand,80h             ; actually have CD
getmod3:test    byte ptr es:[bx],20H    ; test for CTS
        jz      getmodx                 ; z = off
        or      mdmhand,10h             ; actually have CTS
getmodx:pop     es
        pop     bx
getmodem endp
     
; Send a break out the current serial port.     Returns normally.
; Changed to use IOCTL function 12 April 1986 [bgp]
; 30 July 1986 Direct to hardware if not opened right [bgp]
     
SENDBR  proc    near
        push    es
        push    dx
        push    cx
        push    bx
        push    ax
        mov     ax,250          ; break length in msec
        push    ax              ; for later use
        jmp     sendbrz
     
sendbl:
        push    es
        push    dx
        push    cx
        push    bx
        push    ax
        mov     ax,3500         ; break length in msec
        push    ax              ; for later use
sendbrz:
        cmp     prthnd,0        ; open ok? [bgp]
        jge     sendbr0         ; yes, do it nice
        mov     bx,SEG_7201     ; point at 7201
        mov     es,bx
        mov     bx,modem.mdstat
        cli                     ; no interrupts please
        mov     byte ptr es:[bx],1 ; register 1
        mov     byte ptr es:[bx],REG1_7201
        mov     byte ptr es:[bx],5
        mov     byte ptr es:[bx],REG5_7201 or BREAK_ON
        sti                     ; interrupts ok again
        pop     ax
        call    wait_msec
        cli                     ; no interrupts
        mov     byte ptr es:[bx],5
        mov     byte ptr es:[bx],REG5_7201
        mov     byte ptr es:[bx],1
        mov     byte ptr es:[bx],REG1_7201 or ENABLE_INT
        sti                     ; interrupts ok again
        jmp     sendbr1         ; [bgp]
sendbr0:
        mov     bx,offset newpval
        mov     [bx].CR1,REG1_7201 ; to disable interrupts
        mov     [bx].CR5,REG5_7201 or BREAK_ON
        mov     dx,bx           ; where the stuff is
        mov     cx,plength      ; how much there is
        mov     bx,prthnd       ; get handle
        mov     ah,IOCTL
        mov     al,IOwrite      ; set new values
        cli                     ; avoid interrupts here
        int     DOS
        mov     bx,SEG_7201     ; have to explicitly do register 1
        mov     es,bx           ; IOCTL doesn't seem to touch it
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],1 ; Register 1
        mov     byte ptr es:[bx],REG1_7201
        sti                     ; interrupts back on
        pop     ax
        call    wait_msec       ; kill time
        mov     bx,offset newpval
        mov     [bx].CR5,REG5_7201
        mov     bx,prthnd
        mov     ah,IOCTL
        mov     al,IOwrite
        cli                     ; no interrupts please
        int     DOS
        mov     bx,SEG_7201     ; Point at 7201 serial controller
        mov     es,bx
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],1 ; Register 1 must be done explicitly
        mov     byte ptr es:[bx],REG1_7201 or ENABLE_INT
        sti                     ; interrupts are okay again
sendbr1:
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     es
        clc                     ; need to stay connected
        ret                     ; And return.
SENDBR  endp
     
; Wait for the # of milliseconds in ax.  The delay is set for a 5 MHz
; clock rate.       Actual delay for ax=1 is 1.007 msec, plus 1.005 msec
; for each increment in ax.
     
WAIT_MSEC proc  near
pcwait:                         ; entry point for the outside world...
        push    cx              ; 10 cycles
        mov     cx,ax           ; 2 cycles
wait_msec1:
        push    cx              ; 10 cycles
        mov     cx,294          ; 4 cycles
wait_msec2:
        loop    wait_msec2      ; 5+17*(CX-1) cycles
        pop     cx              ; 8 cycles
        loop    wait_msec1      ; 17 cycles if jump, 5 cycles if no jump
        pop     cx              ; 8 cycles
        ret
WAIT_MSEC endp
     
; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
     
POSCUR  proc    near
        push    ax
        push    dx              ; save this
        mov     dx,offset mov_pfx ; move prefix string
        mov     ah,PRSTR
        int     DOS
        pop     dx
        push    dx
        mov     dl,dh
        add     dl,' '          ; this is the row
        mov     ah,DCONIO       ; no checking please
        int     DOS
        pop     dx
        push    dx
        add     dl,' '          ; this is the column
        int     DOS
        pop     dx
        pop     ax
        ret
POSCUR  endp
     
; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.
     
DODEL   proc    near
        mov     dx,offset delstr ; Erase weird character.
        mov     ah,PRSTR
        int     DOS
        ret
DODEL   endp
     
; Move the cursor to the left margin, then clear to end of line.
; Returns normally.
     
CTLU    proc    near
        mov     dx,offset clrlin
        mov     ah,PRSTR
        int     DOS
        ret
CTLU    endp
     
; set the current port.
     
COMS    proc    near
        mov     dx,offset comptab
        mov     bx,0
        mov     ah,CMKEY
        call    comnd
         ret
         nop
         nop
        push    bx
        mov     ah,CMCFM
        call    comnd           ; Get a confirm.
         jmp    comx            ;   Didn't get a confirm.
         nop
        pop     bx
        mov     flags.comflg,bl ; Set the comm port flag.
        cmp     flags.comflg,1  ; Using Com 1?
        jne     coms0           ; Nope.
        mov     ax,offset port1
        mov     portval,ax
        mov     modem.mddat,DATAA_7201 ; Set COM1 defaults.
        mov     modem.mdstat,STATA_7201
        mov     modem.mdcom,SETA_8253
        call    opnprt          ; open the handle
        ret
coms0:
        mov     ax,offset port2
        mov     portval,ax
        mov     modem.mddat,DATAB_7201 ; Set COM2 defaults.
        mov     modem.mdstat,STATB_7201
        mov     modem.mdcom,SETB_8253
        call    opnprt
        ret
comx:
        pop     bx
        ret
COMS    endp
     
; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.     Modified to IOCTL function 12 April 1986 [bgp]
     
SERINI  proc    near
        push    es
        push    dx
        push    cx
        push    bx
        push    ax
        cmp     portin,FALSE    ; Did we initialize port already?
        je      serin0
        jmp     serin2          ; Yes, just leave
serin0:
        cli                     ; Disable interrupts
        xor     ax,ax           ; Address low memory
        mov     es,ax
        mov     bx,modem.mdintv
        mov     ax,es:[bx]
        mov     savsci,ax
        mov     ax,offset serint ; Point at our routine
        mov     es:[bx],ax
        add     bx,2            ; Now for CS value
        mov     ax,es:[bx]
        mov     savscs,ax
        mov     es:[bx],cs
        mov     portin,1        ; Note that we are initialized
        mov     ax,offset source
        mov     srcpnt,ax
        mov     count,0
        mov     ax,SEG_8259     ; Point at 8259 interrupt controller
        mov     es,ax
        mov     bx,CW2_8259     ; Control word 2
        mov     al,es:[bx]
        and     al,modem.mden   ; Enable INT1 (all from 7201)
        mov     es:[bx],al      ; Save it
        mov     bx,CW1_8259     ; Control word 1
        mov     al,modem.mdmeoi ; Clear any outstanding requests
        mov     es:[bx],al
     
;  Note that access to the serial controller here is only to register 1 which
;  must be done explicitly anyway, so there is no reason to care about
;  whether a port is open [bgp]
     
        mov     bx,SEG_7201     ; Point at 7201 serial controller
        mov     es,bx
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],1 ; Register 1 must be done explicitly
        mov     byte ptr es:[bx],REG1_7201 or ENABLE_INT
        mov     byte ptr es:[bx],10H ; Clear external/status interrupts
        mov     byte ptr es:[bx],30H ; Clear special receive cond. int.
        mov     byte ptr es:[bx],38H ; Set to end of interrupt
        sti                     ; Allow interrupts
        mov     bx,portval      ; get port [jrd]
        mov     parmsk,0FFH     ; parity mask, assume parity is None. [jrd]
        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
serin2: pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     es
        clc                     ; carry clear for success
        ret
SERINI  endp
     
; Reset the serial port.  This is the opposite of serini.   Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.     Modified to use IOCTL function 12 April 1986 [bgp]
; 30 July 1986 Make it go direct to hardware if handle open failed [bgp]
;   Also, don't reset the port until the transmit buffer is empty
     
SERRST  proc    near
        push    es              ; preserve this
        push    dx
        push    cx
        push    bx
        push    ax
        cmp     portin,FALSE    ; Reset already?
        je      srst0           ; Yes, just leave.
        mov     bx,SEG_7201     ; point at 7201 [bgp]
        mov     es,bx
        mov     bx,modem.mdstat
sersta:
        cli
        mov     byte ptr es:[bx],1 ; want status register 1
        mov     al,es:[bx]      ; get status
        sti
        test    al,1            ; all sent? (transmitter and shift reg. empty)
        jz      sersta          ; no, wait...
        cli                     ; Disable interrupts
        mov     bx,SEG_8259     ; Point at 8259 interrupt controller
        mov     es,bx
        mov     bx,CW2_8259
        mov     al,es:[bx]
        or      al,modem.mddis  ; Turn off INT1
        mov     es:[bx],al
        xor     bx,bx           ; Address low memory
        mov     es,bx
        mov     bx,modem.mdintv ; Restore the serial card int vector
        mov     ax,savsci
        mov     es:[bx],ax
        add     bx,2            ; Restore CS too.
        mov     ax,savscs
        mov     es:[bx],ax
     
; As in SERINI, the only access to the serial controller is to register 1
; which must be done explicitly anyway, so we don't care about the handle [bgp]
     
        mov     bx,SEG_7201     ; Point at 7201 serial controller
        mov     es,bx
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],1 ; Register 1 has to be done explicitly
        mov     byte ptr es:[bx],REG1_7201
        mov     portin,FALSE    ; Reset flag.
        sti
srst0:
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     es              ; All done.
        ret                     ; All done.
SERRST  endp
     
; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.
; Revised on 22 May 1986, again 2 August 1986 to run at 38.4kb on PC's. [jrd]
; Srcpnt holds offset, within buffer Source, where next rcv'd char goes.
; Count is number of chars now in buffer, and oldest char is srcpnt-count
; done modulo size of Source.  All pointer management is handled here.
; Control-G char substituted for char(s) lost in overrun condition. [jrd]
; Adapted from msxibm.asm code. [jrd]
; Fixed test for receiver overrun. [bgp]
     
SERINT  proc      near
        push    ax              ; [jrd]
        push    ds              ; do ds and ax first [jrd]
        push    es
        push    bx
        push    dx
        mov     ax,seg datas
        mov     ds,ax           ; address data segment
        mov     bx,SEG_7201     ; point at 7201
        mov     es,bx
        mov     bx,modem.mdstat
        mov     al,es:[bx]      ; get status (register 0)
        mov     byte ptr es:[bx],1 ; want status register 1 [bgp]
        mov     ah,es:[bx]      ; get status [bgp]
        mov     bx,STATA_7201   ; this one is always channel A
        mov     byte ptr es:[bx],38H ; Notify 7201 of end of interrupt
        mov     bx,SEG_8259     ; point at 8259
        mov     es,bx
        mov     bx,CW1_8259
        mov     dl,modem.mdmeoi
        mov     es:[bx],dl      ; Clear interrupt
        test    al,MDMINP       ; anything there (status register 0)?
        jnz     srint0          ; nz = yes
        jmp     retint          ; and exit now (common jump point)
srint0:
        mov     bx,SEG_7201
        mov     es,bx
        and     ah,MDMOVER      ; select overrun bit (status register 1)[jrd]
        mov     overrun,ah      ; save it for later [jrd]
        jz      srint0a         ; no overrun
        mov     bx,modem.mdstat
        mov     byte ptr es:[bx],30H ; Clear overrun error status
srint0a:
        mov     bx,modem.mddat
        mov     al,es:[bx]      ; get data byte
        cmp     flowoff,0       ; flow control active?
        je      srint4          ; e = no
        mov     ah,al           ; ah = working copy. Check flow cntl.
        and     ah,parmsk       ; strip parity temporarily, if any. [jrd]
        cmp     ah,flowoff      ; acting on Xoff?
        jne     srint3          ; ne = Nope, go on
        cmp     xofsnt,0        ; have we sent an outstanding XOFF? [jrd]
        jne     srint1          ; ne = yes, ignore (possible echo)
        mov     xofrcv,bufon    ; Set the flag saying XOFF received
srint1: jmp     retint          ; and exit
srint3:
        cmp     ah,flowon       ; acting on Xon?
        jne     srint4          ; ne = Nope, go on
        mov     xofrcv,off      ; Clear the XOFF received flag.
        jmp     retint          ; and exit
srint4:
        mov     ah,overrun      ; get overrun flag [jrd]
        or      ah,ah           ; overrun?
        jz      srint5          ; z = no
        mov     ah,al           ; yes, save present char
        mov     al,BELL         ; insert control-G for missing character
srint5:
        mov     bx,srcpnt       ; address of buffer storage slot
        mov     byte ptr [bx],al ; store the new char in buffer "source"
        inc     srcpnt          ; point to next slot
        inc     bx
        cmp     bx,offset source+BUFSIZ ; beyond end of buffer?
        jb      srint6          ; b = not past end
        mov     srcpnt,offset source ; wrap buffer arount
srint6:
        cmp     count,BUFSIZ    ; filled already?
        jae     srint7          ; ae = yes
        inc     count           ; no, add a char
srint7:
        or      ah,ah           ; anything in overrun storage?
        jz      srint8          ; z = no
        mov     al,ah           ; recover any recent char from overrun
        xor     ah,ah           ; clear overrun storage
        jmp     srint5          ; yes, go store real second char
srint8:
        sti                     ; ok to allow interrupts now, not before
        cmp     count,MNTRGH    ; past the high trigger point?
        jbe     retint          ; be = no, we're within our limit
        test    xofsnt,bufon    ; Has an XOFF been sent by buffer control?
        jnz     retint          ; nz = Yes.
        mov     al,flowoff      ; get the flow off char (Xoff or null)
        or      al,al           ; don't send null chars
        jz      retint          ; z = null, nothing to send
        call    dopar           ; set parity appropriately.
        mov     ah,al           ; Don't overwrite character with status
        push    cx              ; save reg [jrd]
        xor     cx,cx           ; loop counter [jrd]
        mov     bx,SEG_7201     ; point at 7201
        mov     es,bx
        mov     bx,modem.mdstat
srint9:
        mov     al,es:[bx]
        test    al,4            ; ready?
        jnz     srint10         ; yes
        loop    srint9
        jmp     srint11         ; Timeout
srint10:
        mov     al,ah           ; now send it out
        mov     bx,modem.mddat
        mov     es:[bx],al
        mov     xofsnt,bufon       ; Remember we sent an XOFF at buffer level
srint11:
        pop     cx              ; [jrd]
retint:
        sti                     ; be sure that this made it on
        pop     dx
        pop     bx
        pop     es
        pop     ds
        pop     ax              ; [jrd]
        iret
SERINT  endp
     
; Produce a beep.   Returns normally.
     
BEEP    proc    near
        mov     dl,BELL
        mov     ah,DCONIO       ; No checks, just do it
        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
     
CODE    ends
     
        end