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

⟦9ec3f9823⟧ TextFile

    Length: 171219 (0x29cd3)
    Types: TextFile
    Names: »msxibm.asm«

Derivation

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

TextFile

; File MSXIBM.ASM
; Kermit system dependent module for IBM-PC
; Edit History
; Last edit 2 Dec 1988
; 2 Dec 1988 Preserve UART parity etc bits in Line Control Reg.
; 30 Nov 1988 Add SET TERM CLEAR screen clear command (uses byte VTCLEAR).
; 26 Nov 1988 Add SET TERM TAB SET/CLEAR AT start-col:spacing notation.
; 21 Nov 1988 Version 2.32
; 25 Oct 1988 Add Ungermann Bass network board status check in proc chkub,
;  thanks to Fritz Buetikofer and Rene Rehmann in Switzerland.
; 9 Oct 1988 Add byte vtemu.vtchset to hold choices for VT102 emulator
;  character sets (US, UK, or Alternate-ROM). Tnx to Baruch Cochavy. The value
;  of vtemu.vtchset must match definitions in emulator file mszibm.asm.
; 5 Oct 1988 Add SET TERMINAL DIRECTION command and status display line.
; 21 August 1988 Move tests for NUL and DEL received chars to prtchr routine
;  for uniform application of tests.
; 28 July 1988 Add more UB details from Henrik Levkowetz. Double check serial
;  port address for 02f8h with COM1, for PCjr, and shift to COM2 addresses.
; 16 July 1988 Use null interrupt routine, nulint, when resetting serial port
;  because setting OUT2 low generates stray ints on some UARTs. [jrd]
; 1 July 1988 Version 2.31
; 12 June 1988 Add small changes to handle network session failures, and
;  add carry set if serini fails to init port.
; 29 May 1988 Add Ungermann Bass NetOne NETCI support from Henrik Levkowetz
;  [ohl]. Revise same to test for actual network presence and to avoid
;  interference with NetBios operations. Use "SET PORT UB-Net1"
; 23 May 1988 Hangup now resets serial port so can be re-inited with DTR high
; 21 May 1988 Tweak serrst to allow stray interrupts while resetting UART,
;  clear outstanding network requests when changing ports.
; 6 May 1988 Introduce xmtbufx for explict network double buffering of
;   xmitted data.
; 28 April 1988 fix set port bios register problem.
; 18 April 1988 Network: do another READ when char count < low water mark.
;  If BIOSn is selected skip test of Bios 40:0h for port presence.
; 3 April 1988 Ignore NUL and DEL at serial port unless DEBUG or TRANSLATE
;  INPUT are active, but pass DEL if Tek mode is active. Replace netdone
;  with new lclexit global word to shut net when Kermit exits.
; 22 March 1988 Add global byte Tekgraf which forces graphics board type
;   0=auto-sensing, 1=cga, 2=ega, 3=VGA, 5=Hercules, 5=ATT. Tekgraf stored
;   and set in this file by Set Term Graphics <board type>. [jrd]
; 6 March 1988 Ignore received XOFF's if we have already sent one. [jrd]
; 27 Feb 1988 Add global routine getmodem to return modem status in al. [jrd]
; 9 Feb 1988 Automaticallly find Interrupt ReQuest level for a port.
;   No Modem Status for network. [jrd]
; 25 Jan 1988 Revise outchr waiting on XON to use 4 millisec increments. [jrd]
; 1 Jan 1988 version 2.30
; 24 Dec 1987 Revise selection of COM1 to use COM1 name but COM2 addresses
;  if base address of 02f8 (COM2) is found in 40:00h and display notice.
;  Restore state of IRQ interrupt line when finished with serial port. [jrd]
; 31 Oct 1987 Add terminal type Tek4010, with Tek submode Tekflg. [jrd]
; 24 Oct 1987 Enhance clrbuf to empty any intermediate (net) buffers. [jrd]
; 19 Oct 1987 Fix stray tab-set at column 32. [jrd]
; 2 Oct 1987 Add PCjr baud rate table, from Ted Medin. [jrd]
; 6 Sept 1987 Allow serial port serint to send xoff when buffer fills even
;  though user may have sent xoff by hand. [jrd]
; 27 Aug 1987 Skip timeout test in OUTCHR if receive timeout is zero. [jrd]
; 23 August 1987 Add vtemu.vtflgop to hold runtime terminal settings so that
;  a reset command restores them to the Setup values, vtemu.vtflgst. Show
;  displays the vtemu.vtflgop operational values. [jrd]
; 17 August 1987 Make timing adjustments for Token Passing and single buffered
;  network adapter boards. Byte netdbfr indicates presence of double buffering;
;  it is set in chknet as a vendor option. To test your boards force dbl buf
;  then look for missing 256 byte parts of long packets sent out; missing parts
;  mean new material overwrote not-yet-sent old == single buffering. [jrd]
; 8 August 1987 Add interrupt chaining in serint. [jrd]
; 23 July 1987 Clear xofsnt and xofrcv xon/xoff flags in ihost(s/r). [jrd]
; 9 July 1987 Cure confusion about COM1/2 for IBM PCjr (address of regular
;  COM2 in 40:0h slot for COM1) with info from John Neufeld. [jrd]
; 2 July 1987 Route NetBios cancels through separate scb for systems, such
;  as Novell NetWare, which object to having active scbs touched. [jrd]
; 25 June 1987 Add trapping of Int 14H (Bios RS232 procedure) to allow
;  CTTY command to function without too much inteference from DOS. [jrd]
; 17 June 1987 Enlarge tab setting to full 132 columns at all times. [jrd]
; 11 June 1987 Add Set Term Roll on/off to control auto roll back of screen
;  when new characters are displayed; default is off (no unwinding). [jrd]
; 20 May 1987 Remove rejection of NULL and DEL chars, let callers do it. [jrd]
; 16 May 1987 Add distinction between user typed and receiver threshold
;  controlled sending of XOFF. User level overrides buffer control.[jrd]
;  Add COM3 and COM4 support: examine memory 40:00h->40:07h for selected
;  port COM1..4, resp. If word is null then set flags.comflg to 0 to say
;  undefined port. Otherwise, use that word in seg 40h as base of UART i/o
;  ports. Assume IRQ4 for COM1 and COM3 (same except for port addresses)
;  and IRQ4 for  COM2 and COM4 (again, same except for port addresses).
;  Serial port info sturcture (not values) assumed identical for all ports.
; 25 April 1987 Add Netbios compatible local area network support. [jrd]
;  Set Port command expanded to syntax SET PORT NET nodename. Use nodename
;  if acting as a client to named remote node, leave blank if running in
;  Server mode. Byte 'ttyact' is controlled by msster.asm to indicate Connect
;  mode is being used. Byte 'netdone' (stored in mssker.asm) holds offset of
;  network hangup procedure 'netclose' to be done when leaving Kermit. Hangup
;  command extended to to network hangup as well. Network uses IBM Netbios
;  standard calls (Int 5Ch) and allows for extensions of AT&T STARLAN for
;  longer node names via Int 2Bh (the later is tested before use). Virtual
;  circuits are employed. The Redirector is not necessary. Kermit can operate
;  as either a terminal (does a CALL at Connect mode startup), a file receiver
;  (does a CALL at startup), or a Kermit server (does an anonomous LISTEN at
;  startup, hence no nodename). Clients should Set Timer Off.
; Note -
; When the Bios is used for serial port i/o the modem signals DSR and CTS
; must be asserted low before the Bios will access the hardware. Jumpers
; from pin 20 (DTR) to pin 6 (DSR) and from pin 4 (RTS) to pin 5 (CTS)
; will probably be necessary.
;       From Glenn Everhart (who suggested using the Bios alternative)
;
        public  serini, serrst, clrbuf, outchr, coms, vts, vtstat
        public  dodel, ctlu, cmblnk, locate, prtchr, dobaud, clearl
        public  dodisk, getbaud, beep, termtb, shomodem, getmodem, mdmhand
        public  count, xofsnt, puthlp, putmod, clrmod, poscur, holdscr
        public  sendbr, sendbl, machnam, setktab, setkhlp, lclini, showkey
        public  ihosts, ihostr, dtrlow, serhng, comptab, pcwait
        include mssdef.h

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 buffer full

BRKBIT  EQU     040H            ; Send-break bit.
TIMERCMD EQU    43h             ; 8253/4 Timer chip command port
TIMER2DATA EQU  42h             ; 8253/4 Timer 2 data port
PPI_PORT EQU    61h             ; 8255 prog peripheral chip control port
MCONF   EQU     11H             ; Bios Machine configuration s/ware interrupt.
KEYB    EQU     16H             ; Bios keyboard software interrupt
VIDEO   EQU     10H             ; Bios Video display software interrupt
RS232   EQU     14H             ; Bios RS232 serial port s/ware interrupt

; constants used by serial port handler
;;MDMDAT1       EQU     03F8H           ; Address of port com1 (data)
;;MDMCOM1       EQU     03FBH           ; Address of port com1 (command)
;;MDMSTS1       EQU     03FDH           ; Address of port com1 (status)
;;MDMDAT2       EQU     02F8H           ; Port com2 data
;;MDMCOM2       EQU     02FBH           ; Port com2 command
;;MDMSTS2       EQU     02FDH           ; Port com2 status
MDMINTV EQU     0CH             ; IRQ4 com1/3 port interrupt vector
MDINTV2 EQU     0BH             ; IRQ3 com2/4 port interrupt vector
MDMINTO EQU     0EFH            ; Mask to enable interrupt level IRQ4
MDINTO2 EQU     0F7H            ; Mask to enable interrupt level IRQ3
MDMINTC EQU     010H            ; Bit to set to disable interrupts for IRQ4
MDINTC2 EQU     008H            ; Bit to set to disable interrupts for IRQ3
EOICOM  EQU     0064H           ; End of interrupt for IRQ4
EOICOM2 EQU     0063H           ; End of interrupt for IRQ3

INTCONT EQU     0021H           ; Address of 8259 interrupt controller ICW2-3
INTCON1 EQU     0020H           ; Address of 8259 ICW1
MDMINP  EQU     1               ; Input ready bit
MDMOVER EQU     2               ; Receiver overrun

; 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
; low_rgt - low byte = last column (typ 79), high byte = last text row
;  (typ 23) in screen coordinates (start at 0), set by msyibm.

; 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, ttyact:byte
        extrn   portval:word, port1:byte, port2:byte, port3:byte, port4:byte
        extrn   lclexit:word, rxtable:byte, decbuf:byte, taklev:byte
        extrn   refresh:byte, scbattr:byte, low_rgt:word, vtemu:byte
        extrn   vtroll:byte, tekflg:byte, tekgraf:byte, vtclear:byte

; structure for status information table sttab.
stent   struc
sttyp   dw      ?               ; type (actually routine to call)
msg     dw      ?               ; message to print
val2    dw      ?               ; needed value: another message, or tbl addr
tstcel  dw      ?               ; address of cell to test, in data segment
basval  dw      0               ; base value, if non-zero
stent   ends

setktab db      0               ; superceded by msuibm code, return 0 here
setkhlp db      '$'             ; and add empty help string
holdscr db      0               ; Hold-Screen, non-zero to stop reading
savsci  dd      ?               ; old serial port interrupt vector
sav232  dd      ?               ; Original Bios Int 14H address, in Code seg
savirq  db      ?               ; Original Interrupt mask for IRQ
savlcr  db      ?               ; Original Line Control Reg (3fbh) contents
brkval  db      BRKBIT          ; What to send for a break
brkadr  dw      0               ; Where to send it
intkind db      0               ; cause of serial port interrupt
isps2   db      0               ; non-zero if real IBM PS/2
modem   mdminfo <>
hngmsg  db      cr,lf,' The phone or network connection should have hungup'
        db      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.',cr,lf
        db      ' For networks, the active session is terminated.',cr,lf,'$'
erms40  db      cr,lf,'?Warning: Unrecognized baud rate',cr,lf,'$'
erms41  db      cr,lf,'?More parameters are needed$'
badbd   db      cr,lf,'Unimplemented baud rate$'
badprt  db      cr,lf,' Serial port $'
badprt2 db      ' is not available',cr,lf,'$'
biospt  db      'BIOS$'
compt   db      'COM$'
unkpt   db      'Unknown$'
biosmsg db      cr,lf,'This port operates through the Bios',cr,lf,'$'
msmsg1  db      cr,lf,'  Modem is not ready: DSR is off$'
msmsg2  db      cr,lf,'  Modem is ready:     DSR is on$'
msmsg3  db      cr,lf,'  no Carrier Detect:  CD  is off$'
msmsg4  db      cr,lf,'  Carrier Detect:     CD  is on$'
msmsg5  db      cr,lf,'  no Clear To Send:   CTS is off$'
msmsg6  db      cr,lf,'  Clear To Send:      CTS is on$'
msmsg7  db      cr,lf,'  Modem is not used by the Network$'

machnam db      'IBM-PC$'
crlf    db      cr,lf,'$'
delstr  db      BS,BS,'  ',BS,BS,'$'    ; Delete string
clrlin  db      cr,'$'                  ; Clear line (just the cr part)
tabmsg  db      'Tab stops ''T'' and columns -$'
clone   db      0               ; clone flag (0 = real, 'B' = system Bios,
                                ; 'N'=NetBios/network, 'U'=Ungermann/Bass net)
portin  db      0               ; Has comm port been initialized
xofsnt  db      0               ; Say if we sent an XOFF
xofrcv  db      0               ; Say if we received an XOFF
parmsk  db      0ffh            ; parity mask, 0ffh for no parity, 07fh with
flowoff db      0               ; flow-off char, Xoff or null (if no flow)
flowon  db      0               ; flow-on char, Xon or null
pcwcnt  dw      240             ; number of loops for 1 millisec in pcwait
temp    dw      0
temp2   dw      0
tempsci dw      0               ; temp storage for serint
tempdum dw      0               ; temp storage for serdum
rdbuf   db      80 dup (?)      ; temporary storage

                                        ; begin Terminal emulator data set
termtb  db      tttypes                 ; entries for Status, not Set
        mkeyw   'Heath-19',ttheath
        mkeyw   'none',ttgenrc
        mkeyw   'Tek4010',tttek
        mkeyw   'VT102',ttvt100
        mkeyw   'VT52',ttvt52

vttbl   db      18                      ; number of entries
        mkeyw   'Character-set',chaval+300h
        mkeyw   'Clear-screen',1000h    ; 1000h = marker here
        mkeyw   'Color',200H            ; screen fore/back colors; 200H=marker
        mkeyw   'Cursor-style',curval
        mkeyw   'Direction',dirval
        mkeyw   'Graphics',800h         ; Tek graphics board, 800h=marker
        mkeyw   'Heath-19',ttheath+100H ; note 100H flag for decoding here
        mkeyw   'Keyclick',keyval
        mkeyw   'Margin-bell',marval
        mkeyw   'None',ttgenrc+100H
        mkeyw   'Newline',newval
        mkeyw   'Rollback',400h         ; note 400H flag for decoding
        mkeyw   'Screen-background',scrval
        mkeyw   'Tabstops',tabval
        mkeyw   'Tek4010',tttek+100H
        mkeyw   'VT102',ttvt100+100H
        mkeyw   'VT52',ttvt52+100H
        mkeyw   'Wrap',wraval

scrtab  db      02H                     ; screen attributes
        mkeyw   'normal',00H
        mkeyw   'reverse',01H

dirtab  db      2                       ; writing direction
        mkeyw   'left-to-right',0
        mkeyw   'right-to-left',1

curtab  db      02H                     ; cursor attributes
        mkeyw   'block',00H
        mkeyw   'underline',01H

chatab  db      3                       ; character set (pound sign choice)
        mkeyw   'UK-ascii',0
        mkeyw   'US-ascii',1            ; US ASCII is default (1)
        mkeyw   'Alternate-ROM',3       ; Alternate-ROM character set

graftab db      6
        mkeyw   'auto-sensing',0        ; autosensing
        mkeyw   'CGA',1
        mkeyw   'EGA',2
        mkeyw   'VGA',3
        mkeyw   'Hercules',4
        mkeyw   'ATT',5

tabtab  db      02H                     ; label says it all!
        mkeyw   'at',0FFH               ; For setting tab stops
        mkeyw   'Clear',00H             ; For clearing tab stops

alltab  db      02H                     ; more tab command decoding
        mkeyw   'all',00H
        mkeyw   'at',01H

vtable  dw      ontab, curtab, chatab, ontab, ontab, ontab, scrtab, dirtab,0

vtsflg equ      this byte               ; define small digits xxxval
newval  equ     $-vtsflg                ; and mask for bit in byte
        db      vsnewline
curval  equ     $-vtsflg
        db      vscursor
chaval  equ     $-vtsflg
        db      vsshift3
keyval  equ     $-vtsflg
        db      vskeyclick
wraval  equ     $-vtsflg
        db      vswrap
marval  equ     $-vtsflg
        db      vsmarginbell
scrval  equ     $-vtsflg
        db      vsscreen
dirval  equ     $-vtsflg
        db      vswdir
numflgs equ     $-vtsflg
tabval  equ     $-vtsflg
        db      0
vtrtns  dw      numflgs dup (flgset), tabset ; dispatch table for vtsflg
colortb db      0,4,2,6,1,5,3,7         ; color reversed-bit setting bytes
clrset  db      ?                       ; Temp for SET Term Tabstops xxx

vthlp   db      ' one of the following:',cr,lf
      db '  terminal types of:  None, Heath-19, VT52, VT102, or Tek4010',cr,lf
        db '  Newline-mode    Cursor-style        Character-set (US, UK, Alt)'
        db      cr,lf
        db '  Keyclick        Margin-bell         Screen-background'
        db      ' (normal, reverse)',cr,lf
        db '  Tabstops        Wrap (long lines)   Color (fore & background)'
        db      cr,lf,'  Clear-screen  (clears old startup screen)'
        db      cr,lf,'  Direction Left-to-right or Right-to-left'
        db      ' (screen writing direction)'
        db      cr,lf,'  Graphics  (type of display adapter when in Tek4010'
        db      ' mode)'
        db      cr,lf,'  Rollback  (undo screen roll back before writing new'
        db      ' chars, default=off)$'
clrhlp  db      ' one of the following:'
        db      cr,lf,'  AT #s  (to set tabs at column #s)    or'
        db      ' AT start-column:spacing'
        db      cr,lf,'  Clear AT #s (clears individual tabs) or'
        db      ' AT start-column:spacing'
        db      cr,lf,'  Clear ALL  (to clear all tabstops)'
        db      cr,lf,'  Ex: Set term tab at 10, 20, 34        sets tabs'
        db      cr,lf,'  Ex: Set term tab at 1:8        sets tabs at 1, 9,'
                db      ' 17,...$'
        db      cr,lf,'  Ex: Set term tab clear at 9, 17, 65   clears tabs'
        db      cr,lf,'  Ex: Set term tab clear at 1:8  clears tabs at 1, 9,'
                db      ' 17,...$'
tbserr  db      cr,lf,'?Column number is not in range 1 to screen width-1$'
colhlp  db      cr,lf,'  Set Term Color  value, value, value, ...'
        db      cr,lf,'   0 no-snow mode on an IBM CGA and white on black'
        db      cr,lf,'   1 for high intensity foreground'
        db      cr,lf,'  10 for fast CGA screen updating (may cause snow)'
        db      cr,lf,'  Foreground color (30-37) = 30 + sum of colors'
        db      cr,lf,'  Background color (40-47) = 40 + sum of colors'
        db      cr,lf,'    where colors are  1 = red, 2 = green, 4 = blue'
        db      cr,lf,'  Ex: 0, 1, 37, 44   IBM CGA(0), bright(1) white(37)'
        db      ' chars on a blue(44) field'
        db      cr,lf,'  Attributes are applied in order of appearance.$'
colerr  db      cr,lf,'?Value not in range of 0, 1, 10, 30-37, or 40-47$'
vtwrap  db      'Term wrap-lines: $'
vtbell  db      'Term margin-bell: $'
vtnewln db      'Term newline: $'
vtcur   db      'Term cursor-style: $'
vtcset  db      'Term character-set: $'
vtclik  db      'Term key-click: $'
vtscrn  db      'Term screen-background: $'
colst1  db      'Term color  foreground:3$'
colst2  db      ' background:4$'
vtgraf  db      'Term graphics: $'
vtrolst db      'Term rollback: $'
vtdir   db      'Term direction: $'
                                                        ; terminal emulator
vtstbl  stent   <srchkb,vtwrap,ontab,vswrap,vtemu.vtflgop>      ; line wrap
        stent   <srchkb,vtbell,ontab,vsmarginbell,vtemu.vtflgop>; margin bell
        stent   <srchkb,vtcur,curtab,vscursor,vtemu.vtflgop>    ; cursor type
        stent   <srchkb,vtnewln,ontab,vsnewline,vtemu.vtflgop>  ; newline
        stent   <srchkb,vtscrn,scrtab,vsscreen,vtemu.vtflgop>   ; screen
        stent   <srchkw,vtcset,chatab,vtemu.vtchset>            ; char set
        stent   <srchkb,vtdir,dirtab,vswdir,vtemu.vtflgop>      ; write direct
        stent   <colstat>                                       ; VT100 colors
        stent   <srchkb,vtclik,ontab,vskeyclick,vtemu.vtflgop>  ; keyclick
        stent   <srchkw,vtrolst,ontab,vtroll>
        stent   <srchkw,vtgraf,graftab,tekgraf>
        stent   <filler>
        stent   <tabstat>       ; VT100 tab status - needs one whole line
        dw      0               ; end of table
                                                ; end of Terminal data set

ontab   db      2                       ; Two entries
        mkeyw   'off',0
        mkeyw   'on',1

comptab db      15                      ; communications port options
        mkeyw   'Bios1','0'+1           ; '0' is to flag value as forced Bios
        mkeyw   'Bios2','0'+2
        mkeyw   'Bios3','0'+3
        mkeyw   'Bios4','0'+4
        mkeyw   'COM1',1                ; these go straight to the hardware
        mkeyw   'COM2',2
        mkeyw   'COM3',3
        mkeyw   'COM4',4
        mkeyw   '1',1                   ; straight to the hardware
        mkeyw   '2',2
        mkeyw   '3',3
        mkeyw   '4',4
        mkeyw   'NetBios','N'           ; Netbios
        mkeyw   'UB-Net1','U'           ; Ungermann Bass Net One
        mkeyw   '   ',0                 ; port is not present, for Status

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat   label   word
        dw      9E4H            ; 45.5 baud
        dw      900H            ; 50 baud
        dw      600H            ; 75 baud
        dw      417H            ; 110 baud
        dw      359H            ; 134.5 baud
        dw      300H            ; 150 baud
        dw      180H            ; 300 baud
        dw      0C0H            ; 600 baud
        dw      60H             ; 1200 baud
        dw      40H             ; 1800 baud
        dw      3AH             ; 2000 baud
        dw      30H             ; 2400 baud
        dw      18H             ; 4800 baud
        dw      0CH             ; 9600 baud
        dw      06H             ; 19200 baud
        dw      03H             ; 38400 baud
        dw      02h             ; 57600 baud
        dw      01h             ; 115200 baud
baudlen equ     ($-bddat)/2     ; number of entries above

jrbddat label   word            ; Baud rate table for IBM PCjrs [tm]
        dw      0FFH            ; 45.5 baud  -- Not supported
        dw      8bfH            ; 50 baud
        dw      5d3H            ; 75 baud
        dw      3f9H            ; 110 baud
        dw      340H            ; 134.5 baud
        dw      2e9H            ; 150 baud
        dw      175H            ; 300 baud
        dw      0baH            ; 600 baud
        dw      5dH             ; 1200 baud
        dw      3eH             ; 1800 baud
        dw      38H             ; 2000 baud
        dw      2fH             ; 2400 baud
        dw      18H             ; 4800 baud
        dw      0CH             ; 9600 baud
        dw      06H             ; 19200 baud
        dw      03H             ; 38400 baud
        dw      02h             ; 57600 baud
        dw      01h             ; 115200 baud

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
; Clone: bits are for Bios speed, no parity, 8 data bits. [jrd]
clbddat   label   word
        dw      0FFH            ; 45.5 baud  -- Not supported
        dw      0FFH            ; 50 baud
        dw      0FFH            ; 75 baud
        dw      03H             ; 110 baud
        dw      0FFH            ; 134.5 baud
        dw      23H             ; 150 baud
        dw      43H             ; 300 baud
        dw      63H             ; 600 baud
        dw      83H             ; 1200 baud
        dw      0ffH            ; 1800 baud
        dw      0FFH            ; 2000 baud
        dw      0a3H            ; 2400 baud
        dw      0c3H            ; 4800 baud
        dw      0e3H            ; 9600 baud
        dw      0FFH            ; 19200 baud
        dw      0FFH            ; 38400 baud
        dw      0FFH            ; 57600 baud
        dw      0FFH            ; 115200 baud
; variables for serial interrupt handler

source  db      bufsiz+2 DUP(?) ; Buffer for data from port (+ 2 guard bytes)
srcpnt  dw      source          ; Pointer in buffer (DI)
count   dw      0               ; Number of chars in int buffer
mst     dw      0               ; Modem status address
mdat    dw      0               ; Modem data address
mdeoi   db      0               ; End-of-Interrupt value
mdmhand db      0               ; Modem status register, current

; Information structures for IBM Netbios compatible Local Area Networks
                                ; network constants
netint  equ     5ch             ; Netbios interrupt
nadd    equ     30h             ; Add name
ncall   equ     10h             ; CALL, open a virtual circuit session
ncancel equ     35h             ; Cancel command in scb buffer
ndelete equ     31h             ; Delete Name
nhangup equ     12h             ; Hangup virtual circuit session
nlisten equ     11h             ; Listen for session caller
naustat equ     33h             ; Network Adapter Unit, get status of
nreceive equ    15h             ; Receive on virtual circuit
nreset  equ     32h             ; Reset NAU and tables
nsend   equ     14h             ; Send on virtual circuit
nsestat equ     34h             ; Session, get status of
netbrk  equ     70h             ; STARLAN Int 5bh send Break
nowait  equ     80h             ; no-wait, command modifier
npending equ    0ffh            ; Pending request
exnbios equ     0400h           ; Int 2ah exec netbios call, error retry
nbuflen equ     256             ; bytes in each network buffer (two of them)
netbios equ     8000h           ; network type bit, using NetBios
starlan equ     1               ; network type bit, AT&T STARLAN
netone  equ     02h             ; [ohl] type bit, Ungermann-Bass Net/One
;xncall equ     74h             ; [ohl] Net/One extended call function
netci   equ     6Bh             ; [ohl] Net/One command interface interrupt,
                                ; [ohl]  used for the following functions:
nciwrit equ     0000h           ; [ohl] Net/One write function
nciread equ     0100h           ; [ohl] Net/One read function
ncistat equ     0700h           ; [ohl] Net/One status function
ncicont equ     0600h           ; [ohl] Net/One control function
ncibrk  equ     02h             ; [ohl] Net/One code for a break
ncidis  equ     04h             ; [ohl] Net/One code for disconnect
ncihld  equ     06h             ; [ohl] code for placing a connection on hold
;; pcnet values:        0       no network available at all
;;                      1       network board reports itself as present
;;                      2       and session is in progress
;; extrn byte pcnet is  defined in msster.

portn   prtinfo <0FFFH,0,defpar,1,0,defhand,floxon> ; port struc for PORTN
scbst struc                     ; Session (Network) Control Block [PCnet comp]
        scb_cmd         db 0    ; command code for Netbios Int 5ch
        scb_err         db 0    ; error return or request is pending
        scb_vcid        db 0    ; virtual circuit ident number
        scb_num         db 0    ; local name-number
        scb_baddr       dw ?    ; buffer address, offset
                        dw datas ;  and segment
        scb_length      dw ?    ; length of buffer data
        scb_rname       db '*               ' ; remote name, 16 chars space
        scb_lname       db 'mskermit        ' ; local name      filled
                        db 0    ; reserved
                        db 0    ; reserved
        scb_post        dw ?    ; interrupt driven post address, offset
                        dw code ;  and segment
                        db 0    ; LAN_num (adapter #), set to zero for STARLAN
        scb_done        db 0    ; command complete status
                                ; the 14 bytes below are normally 'reserved'
                                ; STARLAN uses 5 for long/special call names
                                ;  together with STARLAN specific Int 5bh
        scb_vrname      dw 0,0  ; Variable length call name ptr offset,segment
        scb_vrlen       db 0    ; length of vrname
                        db 9 dup (0)    ; reserved
scbst   ends

rcv     scbst   <,,,,rcvbuf,,length rcvbuf,,,,,rpost>; declare scb for rcvr
xmt     scbst   <,,,,xmtbuf,,length xmtbuf,,,,,spost>;  for xmtr
lsn     scbst   <,,,,xmtbuf,,length xmtbuf,,,,,lpost>;  for server listen
can     scbst   <>                                   ;  for cancels
pcnet   db      0               ; Network is functioning
xmtbuf  db      nbuflen dup (0) ; network buffer for transmitter
xmtcnt  dw      0               ; occupancy in current output buffer
xmtbufx db      nbuflen dup (0) ; external version of xmtbuf (dbl buffers)
rcvbuf  db      nbuflen dup (0) ; network buffer for receiver
nambuf  db      65 dup (?)      ; network long name storage (STARLAN)
sposted db      0               ; send interlock, 0 if no send posted
rposted db      0               ; rcv interlock, 0 if no receive posted
lposted db      0               ; listen outstanding (if non-zero)
netdbfr db      0               ; non-zero if net board is double buffered
deflname db     'mskermit        ' ; default local name, 16 bytes
nsbrk   dw      0               ; net can send Break
nettype dw      0               ; kind of local area net (vendor bit field)
chkmsg1 db      cr,lf,' Cannot construct a local Kermit name, error = $'
chkmsg2 db      cr,lf,lf,' Name $'
chkmsg3 db      ' is already in use. Please enter another of'
        db      cr,lf,' 1 - 14 letters or numbers (or nothing to quit): $'
netmsg1 db      cr,lf,' Checking if our node name is unique ...$'
netmsg2 db      cr,lf,' The network is active, our name is $'
nonetmsg db     cr,lf,'?The network is not available$'
nethlp  db      cr,lf,' Enter node name of remote system,'
        db      cr,lf,'  or a carriage return to use current name,'
        db      cr,lf,'  or a carriage return for server mode.$'
ngodset db      cr,lf,' Connecting to network node: $'
nbadset db      bell,cr,lf,' Cannot reach network node: $'
recmsg  db      cr,lf,' Network receive failed, status = $'
sndmsg  db      cr,lf,' Network send failed, status = $'
naskpmt db      cr,lf,' A network session is active.',cr,lf
        db      ' Enter RESUME to resume it or NEW to start a new session.'
        db      cr,lf,'> $'
nettab  db      2
        mkeyw   'New',0
        mkeyw   'Resume',1
datas   ends

code    segment public 'code'
        extrn   comnd:near, dopar:near, defkey:near, lclyini:near
        extrn   sleep:near, atsclr:near, scrseg:near,scrloc:near, scrsync:near
        extrn   atoi:near, strlen:near, prtscr:near, scroff:near, scron:near
        extrn   srchkb:near, srchkw:near, prompt:near, statc:near
        assume  cs:code, ds:datas

; local initialization

lclini  proc    near
        mov     flags.comflg,1  ; assume COM1 for communications port
        call    coms2           ; setup serial port modem.info for COM1
        call    model           ; get model of IBM machine
        call    lclyini         ; let other modules initialize too...
        ret
lclini  endp

; 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
        int     mconf                   ; Get equipment configuration
        mov     ah,al                   ; Store AL value for a bit
        and     al,01H                  ; First, look at bit 0
        jz      dodsk0                  ; No disk drives -- forget it
        mov     al,ah                   ; Get back original value
        mov     cl,6                    ; Shift over bits 6 and 7
        shr     al,cl                   ; To positions 0 and 1
        inc     al                      ; Want 1 thru 4 (not 0 thru 3)
dodsk0: mov     drives,al               ; Remember how many.
        ret
DODISK  ENDP

; The IBM PC's. If jr then redo IBM baud rate with jr's values. [jrd]
model   proc    near
        mov     isps2,0                 ; PS/2 present indicator
        push    es
        push    ax                      ; get IBM model code at F000:FFFEh
        mov     ax,0f000h               ; address ROM
        mov     es,ax
        mov     al,byte ptr es:[0fffeh] ; get model id byte
        cmp     al,0fdh                 ; PC jr?
        jne     modelx                  ; ne = no
        push    ds
        pop     es                      ; set es to datas segment
        mov     si,offset bddat         ; regular IBM baud rate table
        mov     di,offset jrbddat       ; PCjr baud rate table
        mov     cl,baudlen              ; number of words to copy
        mov     ch,0
        cld
        rep     movsw                   ; copy PCjr values to IBM table
        jmp     short modelx
model2: mov     ah,0ch                  ; AT and PS/2 configuration call
        mov     al,0
        int     15h                     ; IBM Bios
        jc      modelx                  ; c = no information
        cmp     word ptr es:[bx+2],040fch ; PS/2 model 50?
        je      model3                  ; e = yes
        cmp     word ptr es:[bx+2],050fch ; PS/2 model 60?
        je      model3                  ; e = yes
        cmp     byte ptr es:[bx+2],0f8h ; PS/2 model 80?
        jne     modelx                  ; ne = no
model3: mov     isps2,1                 ; say real PS/2 for IRQ setting
modelx: pop     ax
        pop     es
        ret
model   endp

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally. Obsolete, name here for external reference only.
showkey proc    near
        ret                             ; return
showkey endp

; SHOW MODEM, displays current status of lines DSR, CD, and CTS.
; Uses byte mdmhand, the modem line status register. [jrd]
shomodem proc   near
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; no confirm
         nop
        mov     dx,offset msmsg7        ; no modem status for network
        call    getmodem                ; get modem status
        mov     mdmhand,al
        mov     ah,prstr
        mov     dx,offset msmsg1        ; modem ready msg
        test    mdmhand,20h             ; is DSR asserted?
        jz      shomd1                  ; z = no
        mov     dx,offset msmsg2        ; say not asserted
shomd1: int     dos
        mov     dx,offset msmsg3        ; CD asserted msg
        test    mdmhand,80h             ; CD asserted?
        jz      shomd2                  ; z = no
        mov     dx,offset msmsg4        ; say not asserted
shomd2: int     dos
        mov     dx,offset msmsg5        ; CTS asserted msg
        test    mdmhand,10h             ; CTS asserted?
        jz      shomd3                  ; z = no
        mov     dx,offset msmsg6        ; say not asserted
shomd3: mov     ah,prstr
        int     dos
        jmp     rskp
shomodem endp

; Get modem status and set global byte mdmhand. Preserve all registers.
getmodem proc   near                    ; gets modem status upon request
        mov     al,0                    ; assume nothing is on
        cmp     clone,'N'               ; Network?
        je      getmodx                 ; e = yes, no status
        cmp     clone,'B'               ; Bios?
        je      getmodb                 ; e = yes
        cmp     flags.comflg,0          ; null port?
        je      getmodx                 ; e = yes, no status
        mov     dx,mst                  ; hardware
        inc     dx                      ; uart status reg
        in      al,dx
        jmp     short getmodx
getmodb:mov     ah,3                    ; ask Bios for modem status into al
        push    dx
        mov     dl,flags.comflg         ; get port id
        cmp     dl,'0'                  ; ascii?
        jb      getmob1                 ; b = no
        sub     dl,'0'                  ; remove ascii bias
getmob1:int     rs232
        pop     dx
getmodx:mov     ah,0                    ; return status in al
        ret
getmodem 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     srcpnt,offset source    ; receive circular buffer
        mov     count,0                 ; receive circular buffer
        sti
        call    prtchr                  ; empty any intermediate (net) buffers
         nop                            ; got a char, clear again
         nop
         nop
        cli
        mov     srcpnt,offset source    ; receive circular buffer
        mov     count,0                 ; receive circular buffer
        mov     xmtcnt,0                ; network output buffer count
        sti
        ret
CLRBUF  ENDP

; Clear to the end of the current line.  Returns normally.
; Upgraded for Topview compatibility. [jrd]
CLEARL  PROC    NEAR
        push    ax
        push    bx
        push    dx
        mov     ah,3                    ; Clear to end of line
        mov     bh,0
        int     video                   ; Get current cursor position into dx
        mov     ax,dx                   ; Topview compatible clear line
        mov     bh,ah                   ; same row
        mov     bl,byte ptr low_rgt     ; last column
        call    atsclr                  ; clear from ax to bx, screen coord
        pop     dx
        pop     bx
        pop     ax
        ret
CLEARL  ENDP

; This routine blanks the screen.  Returns normally.
; Upgraded to Topview compatiblity. [jrd]
CMBLNK  PROC    NEAR
        push    ax
        push    bx
        xor     ax,ax                   ; from screen loc 0,0
        mov     bx,low_rgt      ; to end of text screen (lower right corner)
        inc     bh                      ; include status line
        call    atsclr               ; do Topview compatible clear, in msyibm
        pop     bx
        pop     ax
        ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

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

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
POSCUR  PROC    NEAR
        push    ax
        push    bx
        mov     ah,2                    ; Position cursor
        mov     bh,0                    ; page 0
        int     video
        pop     bx
        pop     ax
        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


BEEP    PROC    NEAR
        push    ax
        push    cx
        mov     al,10110110B    ; Gen a short beep (long one losses data.)
        out     timercmd,al     ; set Timer to to mode 3
        mov     ax,1512         ; divisor, for frequency
        out     timer2data,al   ; send low byte first
        mov     al,ah
        out     timer2data,al
        in      al,ppi_port     ; get 8255 Port B setting
        or      al,3            ; turn on speaker and timer
        out     ppi_port,al     ; start speaker and timer
        push    ax
        mov     ax,40           ; 40 millisecond beep, calibrated time
        call    pcwait
        pop     ax
        in      al,ppi_port
        and     al,0fch         ; turn off speaker and timer
        out     ppi_port,al
        pop     cx
        pop     ax
        ret
BEEP    ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod  proc    near
        push    ax              ; save regs
        push    bx
        push    cx
        push    dx              ; preserve message
        mov     bl,scbattr      ; screen attributes at Kermit init time
        and     bl,77h          ; get colors, omit bright and blink
        rol     bl,1            ; interchange fore and background
        rol     bl,1
        rol     bl,1
        rol     bl,1
        mov     bh,0            ; preset page 0
        mov     temp,bx         ; temp = page 0, reverse video
        mov     dx,low_rgt      ; ending location is lower right corner
        inc     dh              ;  of status line
        mov     cx,dx           ; start is status left side
        xor     cl,cl           ; left side
        mov     ax,600h         ; scroll to clear the line
        mov     bh,byte ptr temp ; set inverse video attributes
        int     video
        mov     dx,low_rgt      ; last text line
        inc     dh              ; status line
        xor     dl,dl           ; left side
        call    poscur
        pop     si              ; get message back
        mov     cx,1            ; only one char at a time
        xor     bh,bh           ; page 0
        cld
putmo1: lodsb                   ; get a byte
        cmp     al,'$'          ; end of string?
        je      putmo2
        push    si              ; save si
        push    ax              ; and the char
        call    poscur
        inc     dl              ; increment for next write
        pop     ax              ; recover char
        mov     ah,9            ; try this
        mov     bx,temp         ; page 0, inverse video
        int     video
        pop     si              ; recover pointer
        jmp     putmo1
putmo2: pop     cx
        pop     bx
        pop     ax
        ret
putmod  endp

; clear the mode line written by putmod.  Returns normally.
clrmod  proc    near
        push    ax              ; save regs
        push    bx
        push    cx
        push    dx
        mov     ax,600h         ; do a scroll up
        mov     dx,low_rgt      ; ending location is lower right corner
        inc     dh              ;  of status line
        mov     cx,dx           ; start is status left side
        xor     cl,cl           ; left side
        mov     bh,scbattr      ; use screen attributes at Kermit init time
        int     video
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        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    bx              ; save regs
        push    cx
        push    dx
        push    si
        push    ax              ; preserve this
        cld
        mov     bl,scbattr      ; screen attributes at Kermit init time
        and     bl,77h          ; get colors, omit bright and blink
        rol     bl,1            ; interchange fore and background
        rol     bl,1
        rol     bl,1
        rol     bl,1
        mov     bh,0            ; preset page 0
        mov     temp,bx         ; temp = page 0, reverse video

        mov     si,ax           ; point to it
        mov     dh,1            ; init counter
puthl1: lodsb                   ; get a byte
        cmp     al,lf           ; linefeed?
        jne     puthl2          ; no, keep going
        inc     dh              ; count it
        jmp     puthl1          ; and keep looping
puthl2: cmp     al,0            ; end of string?
        jne     puthl1          ; no, keep going
        mov     ax,600h         ; scroll to clear window
        xor     cx,cx           ; from top left
        mov     dl,4fh          ; to bottom right of needed piece
        mov     bh,70h          ; inverse video
        mov     bh,bl           ; inverse video
        int     video
        call    locate          ; home cursor
        mov     bx,0070h        ; bh = page 0, bl = inverse video
        mov     bx,temp
        mov     cx,1            ; one char at a time
        cld                     ; scan direction is forward
        pop     si              ; point to string again
puthl3: lodsb                   ; get a byte
        cmp     al,0            ; end of string?
        je      puthl4          ; yes, stop
        push    si              ; save around bios call
        cmp     al,' '          ; printable?
        jb      puth21          ; b = no
        mov     ah,9            ; write char at current cursor position
        int     video           ; do the Bios int 10h call
        inc     dl              ; point to next column
        jmp     puth23          ; move cursor there
puth21: cmp     al,cr           ; carriage return?
        jne     puth22          ; ne = no
        xor     dl,dl           ; set to column zero
        jmp     puth23
puth22: cmp     al,lf           ; line feed?
        jne     puth23
        inc     dh              ; go to next line
puth23: mov     ah,2            ; set cursor position to dx
        int     video
        pop     si              ; restore pointer
        jmp     puthl3          ; and keep going
puthl4: mov     dh,byte ptr low_rgt+1   ; go to last line
        inc     dh
        xor     dl,dl
        call    poscur          ; position cursor
        pop     si
        pop     dx
        pop     cx
        pop     bx
        ret
puthlp  endp
                                        ; begin Terminal set & status code

; SET Term parameters, especially for use with VT100 emulator. [jrd]
; Taken from work done originally by James Harvey IUPUI.
; VTS is called only by mssset to set terminal type and characteristics.
; Enter via direct jmp. Exit rskp for success, ret for failure.
VTS     proc    near                    ; SET TERM whatever
        mov     ah,cmkey                ; Parse another keyword
        mov     bx,offset vthlp         ; Use this help
        mov     dx,offset vttbl         ; Use this table
        call    comnd
         jmp    r
         nop
        cmp     bh,1H                   ; marker for terminal type?
        je      vsetu0                  ; e = yes
        cmp     bh,2h                   ; marker for set term color?
        jne     vset1                   ; ne = no
        jmp     vsetu2                  ; e = yes
vset1:  cmp     bh,3h                   ; marker for character set?
        je      vsetu3                  ; e = yes
        cmp     bh,4h                   ; marker for roll back control?
        je      vsetu4                  ; e = yes
        cmp     bh,10h                  ; marker for clear-screen?
        jne     vset1a                  ; ne = no
        mov     ah,cmcfm                ; yes
        call    comnd
         jmp    r
         nop
        mov     vtclear,1               ; set trigger for emulator startup
        jmp     rskp                    ; return successfully

vset1a: cmp     bh,8h                   ; marker for graphics type?
        jne     short vsetu1            ; ne = no, dispatch on bl
        jmp     vsetu8                  ; yes

vsetu0: mov     temp,bx                 ; set terminal type
        mov     ah,cmcfm
        call    comnd                   ; Get a confirm
         jmp    r                       ; Didn't get a confirm
         nop
        mov     bx,temp
        mov     flags.vtflg,bl          ; Set the terminal emulation type
        mov     tekflg,0                ; clear Tek sub mode
        jmp     rskp

vsetu1: mov     bh,0                    ; remove marker bits in bh
        shl     bx,1                    ; Make bx a word index
        jmp     vtrtns[bx]              ; Dispatch

vsetu3: mov     ah,cmkey                ; Set Term character set
        mov     bx,0                    ; Use character set table for help
        mov     dx,offset chatab        ; Use character set table
        call    comnd
         jmp    r                       ; failure
         nop
        mov     temp,bx
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    vsetu0                  ; did not get a confirm
         nop
        mov     ax,temp                 ; recover value
        mov     vtemu.vtchset,al        ; set default character set
        jmp     rskp                    ; return successfully

vsetu4: mov     ah,cmkey                ; Set Term Roll On/Off, auto roll back
        mov     bx,0                    ; Use on/off table as help
        mov     dx,offset ontab         ; Use on/off table
        call    comnd
         jmp    r                       ; bad keyword
         nop
        mov     temp,bx
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; did not get a confirm
         nop
        mov     bx,temp
        mov     vtroll,bl               ; set roll state (0=no auto rollback)
        jmp     rskp                    ; return successfully

                                     ; Set Term Color foreground, background
vsetu2: mov     ah,cmtxt                ; get number(s) after set term color
        mov     dx,offset colhlp        ; use this help
        mov     bx,offset rdbuf         ; temp buffer
        mov     rdbuf,0                 ; clear the buffer
        call    comnd
         jmp    r
         nop
        cmp     ah,0                    ; text given?
        jne     vsetu2a                 ; ne = yes
        mov     ah,prstr
        mov     dx,offset erms41        ; say need more parameters
        int     dos
        jmp     rskp
vsetu2a:mov     bx,vtemu.att_ptr        ; get address of attributes byte
        mov     bl,[bx]                 ; get attributes
        mov     byte ptr temp,bl        ; save in work temp
        mov     si,offset rdbuf         ; si = place where atoi wants text
        jmp     vsetu2b                 ; analyze

colbad: mov     ah,prstr                ; not in range - complain and exit
        mov     dx,offset colerr
        int     dos
        jmp     rskp

vsetu2x:mov     al,byte ptr temp        ; get current attributes
        mov     bx,vtemu.att_ptr        ; get address of attributes byte
        mov     [bx],al                 ; store attributes
        jmp     rskp                    ; and return normally

vsetu2b:mov     dx,si                   ; analyze values
        call    strlen                  ; current length of text to cx
        jcxz    vsetu2x                 ; z = nothing left
vsetu2c:cmp     byte ptr[si],' '        ; scan off leading spaces
        jne     vsetu2d                 ; ne = non-blank text found
        inc     si                      ; look at next char
        loop    vsetu2c                 ; cx characters to examine
        jcxz    vsetu2x                 ; z = nothing left
vsetu2d:mov     ah,cl                   ; put length where atoi wants it
        call    atoi                    ; convert text to numeric in ax
         jmp    colbad                  ; no value available
         nop
        cmp     ax,0                    ; reset all? regular IBM CGA refresh
        je      vsetu2j                 ; e = yes
        cmp     ax,1                    ; high intensity?
        je      vsetu2k                 ; e = yes
        cmp     ax,10                   ; fast refresh?
        je      vsetu2l                 ; e = yes
        cmp     ax,30                   ; check range
        jb      colbad                  ; b = too small. complain
        cmp     ax,37
        jna     vsetu2f                 ; 30-37 is foreground color
        cmp     ax,40
        jb      colbad
        cmp     ax,47                   ; compare as unsigned
        jna     vsetu2g                 ; 40-47 is background
        jmp     colbad                  ; else error

vsetu2h:inc     si                      ; skip separator
        cmp     byte ptr[si-1],0        ; ended on null?
        jne     vsetu2b                 ; ne = no, do more
        jmp     vsetu2x                 ; e = yes, exit

vsetu2f:sub     al,30                   ; remove foreground bias
        and     byte ptr temp,not 07H   ; clear foreground bits
        mov     bx,ax
        mov     al,colortb[bx]          ; get reversed bit pattern
        or      byte ptr temp,al        ; load new bits
        jmp     vsetu2h

vsetu2g:sub     al,40                   ; remove background bias
        and     byte ptr temp,not 70H   ; clear background bits
        mov     bx,ax
        mov     al,colortb[bx]          ; get reversed bit pattern
        mov     cl,4                    ; rotate 4 positions
        rol     al,cl
        or      byte ptr temp,al        ; load new bits
        jmp     vsetu2h

vsetu2j:mov     refresh,0               ; Regular (slow) screen refresh
        mov     byte ptr temp,07h       ; clear all, set white on black
        jmp     vsetu2h                 ; get next value
vsetu2k:or      byte ptr temp,08h       ; set high intensity
        jmp     vsetu2h                 ; get next value
vsetu2l:mov     refresh,1               ; Fast screen refresh
        jmp     vsetu2h

vsetu8: mov     ah,cmkey                ; Set Term graphics
        mov     bx,0                    ; Use graphics table as help
        mov     dx,offset graftab       ; Use graphics table
        call    comnd
         jmp    r                       ; bad keyword
         nop
        mov     temp,bx
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; did not get a confirm
         nop
        mov     bx,temp
        mov     tekgraf,bl              ; set Tek graphics board type
        jmp     rskp                    ; return successfully

; SET Term flags. These are the (near) equivalent of VT100 Setup mode values.

flgset: mov     word ptr rdbuf,bx       ; save index
        mov     ah,cmkey                ; Another keyword
        mov     dx,vtable[bx]           ; The table to use
        mov     bx,0                    ; Use default help
        call    comnd
         jmp    r
         nop
        mov     temp,bx                 ; Save switch value
        mov     ah,cmcfm                ; Confirm it
        call    comnd
         jmp    r
         nop
        mov     dx,temp                 ; Restore switch value
        mov     bx,word ptr rdbuf       ; And index
        shr     bx,1                    ; Make it a byte index
        mov     al,vtsflg[bx]           ; Get the flag
        cmp     dx,0                    ; Set or clear?
        je      flgse1                  ; Go clear it
        or      vtemu.vtflgst,al        ; Set the flag
        or      vtemu.vtflgop,al        ; in runtime flags too
        jmp     rskp                    ; Give good return

flgse1: not     al                      ; Complement
        and     vtemu.vtflgst,al        ; Clear the indicated setup flag
        and     vtemu.vtflgop,al        ; Clear the indicated runtime flag
        jmp     rskp                    ; Give good return

;       SET Term Tabstops Clear ALL
;       SET Term Tabstops Clear AT n1, n2, ..., nx
;       SET Term Tabstops At n1, n2, ..., nx

tabset: cld                             ; Make sure this is clear
        mov     di,offset decbuf        ; clear our temp work area here
        mov     cx,132                  ; 132 columns
        mov     al,0                    ; set "not touched" indicator
        rep     stosb                   ; in all decbuf slots
        mov     ah,cmkey                ; Parse keyword
        mov     bx,offset clrhlp        ; help text
        mov     dx,offset tabtab        ; table
        call    comnd
         jmp    r
         nop
        mov     clrset,2                ; code for set a tab
        cmp     bl,0                    ; Was it set or clear?
        jne     tabse1                  ; SET - go parse column number(s)
        mov     clrset,1                ; code for clear at/all tab(s)
        mov     ah,cmkey                ; CLEAR - parse ALL or AT
        mov     bx,offset clrhlp        ; Use this help text
        mov     dx,offset alltab        ; Parse ALL or AT
        call    comnd
         jmp    r
         nop
        cmp     bx,0                    ; ALL?
        jne     tabse1                  ; ne = AT, clear at specific places
        mov     ah,cmcfm                ; Confirm the ALL
        call    comnd
         jmp    r
         nop
        mov     al,1                    ; ALL, means clear all tab stops
        mov     cx,132                  ; use 132 columns
        mov     di,offset decbuf
        cld
        rep     stosb
        jmp     tabcpy                  ; update active & coldstart tabs

tabse1: mov     dx,offset clrhlp        ; Tell them we want a column number
        mov     ah,cmtxt                ; get text w/o white space
        mov     bx,offset rdbuf         ; temp buffer
        call    comnd
         jmp    r
         nop
        cmp     ah,0                    ; anything given?
        jne     tabse2                  ; ne = yes
        mov     ah,prstr
        mov     dx,offset erms41        ; say need more parameters
        int     dos
        jmp     rskp
tabse2: mov     si,offset rdbuf         ; si = place where atoi wants text
tabse3: mov     dx,si
        call    strlen                  ; cx = current length of text
        jcxz    tabcpy                  ; z = nothing left
        mov     ah,cl                   ; put length where atoi wants it
        call    atoi                    ; convert text to numeric in ax
         jmp    tabcpy                  ;  no number available
         nop
        mov     bx,ax                   ; for subscripting in code below
        dec     bx                      ; put column in range 0-131
        cmp     bx,0                    ; check range (1-132 --> 0-131)
        jl      tbsbad                  ; l = too small. complain
        cmp     bl,132-1                ; more than the right most column?
        jna     tabse4                  ; na = no, is ok
tbsbad: mov     ah,prstr                ; not in range - complain and exit
        mov     dx,offset tbserr
        int     dos
        jmp     rskp

tabse4: mov     al,clrset               ; get value for setting or clearing
        mov     decbuf[bx],al           ; store in tabs temp work array
        cmp     byte ptr [si],':'       ; start-column:spacing notation?
        jne     tabse3                  ; ne = no, get next tab stop
        inc     si                      ; skip colon, do start:space analysis
        mov     temp,bx                 ; save reg around atoi call
        mov     dx,si                   ; string address for strlen
        call    strlen                  ; get remaining string length into cx
        jcxz    tabcpy                  ; z = no space value, all done here
        mov     ah,cl                   ; ah = string length for atoi
        call    atoi                    ; get space value into ax
         jmp    tabcpy                  ;  no number available
         nop
        mov     bx,temp
        mov     cx,ax                   ; "space" value
        cmp     cx,0                    ; zero spacing?
        jne     tabse4a                 ; ne = no
        inc     cx                      ; don't get caught with zero spacing
tabse4a:mov     al,clrset               ; get tab set or clear indicator
tabse5: add     bx,cx                   ; new column value
        cmp     bx,132-1                ; largest tab stop
        ja      tabcpy                  ; a = done largest tab stop
        mov     decbuf[bx],al           ; store set or clear indicator
        jmp     short tabse5            ; finish spacing loop, then do tabcpy

tabcpy: mov     cx,132                  ; update all active tab stops
        mov     si,vtemu.vttbst         ; in terminal emulator's active buffer
        mov     di,vtemu.vttbs          ; and in the cold-start buffer
        mov     bx,0                    ; subscript
tabcpy1:mov     al,decbuf[bx]           ; get a table entry into al
        or      al,al                   ; what is the code?
        jz      tabcpy3                 ; z = do not touch
        cmp     al,2                    ; set a tab?
        je      tabcpy2                 ; e = set the tab
        mov     byte ptr [bx+si],0      ; clear the tab
        mov     byte ptr [bx+di],0      ; clear the tab
        jmp     short tabcpy3
tabcpy2:mov     byte ptr [bx+si],0ffh   ; set the tab
        mov     byte ptr [bx+di],0ffh   ; set the tab
tabcpy3:inc     bx                      ; inc subscript
        loop    tabcpy1
        jmp     rskp                    ; Give good return

VTS     endp                            ; end of Set Term things

              ; Terminal Status display, called within STAT0: in MSSSET
VTSTAT  proc    near                    ; enter with di within sttbuf, save bx
        push    bx
        mov     bx,offset vtstbl        ; table of things to show
        call    statc                   ; status common code, in mssset
         nop
         nop
         nop
        pop     bx
        ret                             ; return to STAT0: in MSSSET

colstat proc    near                    ; foreground/background color status
        push    si
        mov     si,offset colst1
        cld
colstd1:lodsb
        cmp     al,'$'                  ; end of string?
        je      colstd2                 ; e = yes
        stosb
        jmp     short colstd1
colstd2:mov     bx,vtemu.att_ptr        ; pointer to attributes byte
        mov     bl,byte ptr[bx]
        mov     bh,0
        push    bx
        and     bx,7                    ; get foreground set
        mov     al,colortb[bx]          ; get reversed bit pattern
        add     al,'0'                  ; add ascii bias
        stosb
        pop     bx
        mov     si,offset colst2
colstd3:lodsb
        cmp     al,'$'
        je      colstd4
        stosb
        jmp     short colstd3
colstd4:mov     cl,4                    ; rotate 4 positions
        shr     bl,cl
        and     bx,7                    ; get background set
        mov     al,colortb[bx]          ; get reversed bit pattern
        add     al,'0'                  ; add ascii bias
        stosb
        pop     si
        ret
colstat endp
                                        ; Tabs Status display
tabstat proc    near                    ; display tabs ruler for Status
        push    dx
        cld
tabstd2:mov     al,cr
        stosb
tabsta0:mov     si,vtemu.vttbst         ; active tabs address, not shadow
        mov     cl,byte ptr low_rgt     ; loop screen width-1 times
        xor     ch,ch                   ; clear high byte
        dec     si                      ; dec for inc below
        xor     ax,ax                   ; tens counter
tabsta1:mov     dl,'.'                  ; default position symbol
        inc     si                      ; start with position 1
        inc     al
        cmp     al,10                   ; time to roll over?
        jb      tabsta2                 ; b = not yet
        mov     al,0                    ; modulo 10
        inc     ah
        mov     dl,ah                   ; display a tens-digit
        add     dl,'0'
        cmp     dl,'9'                  ; larger than 90?
        jbe     tabsta2                 ; be = no
        sub     dl,10                   ; roll over to 0, 1, etc
tabsta2:cmp     byte ptr [si],0         ; is tab set?
        je      tabsta3                 ; e = no
        mov     dl,'T'                  ; yes, display a 'T'
tabsta3:push    ax
        mov     al,dl
        stosb
        pop     ax
        loop    tabsta1                 ; loop til done (cx has count)
        pop     dx
        ret
tabstat endp

filler  proc    near                    ; use space
        mov     cx,20
        mov     al,' '
        rep     stosb
        ret
filler  endp
VTSTAT  endp                            ; end of Terminal set & status code

; Compute number of iterations needed in procedure pcwait inner loop
; to do one millisecond delay increments. Uses Intel 8253/8254 timer chip
; (timer #2) to measure elapsed time assuming 1.193182 MHz clock.
; Called by serini below. For IBM PC compatible machines.
; Regs preserved. 16 April 87 [jrd]
pcwtst  proc    near
        push    ax
        push    cx
        push    dx
        mov     pcwcnt,256      ; software loop, initial value
        in      al,ppi_port     ; 8255 chip port B, 61h
        and     al,0fch         ; speaker off (bit 1), stop timer (bit 0)
        out     ppi_port,al     ; do it
  ; 10 = timer 2, 11 = load low byte then high byte, 010 = mode 2, 0 = binary
        mov     al,10110100B    ; command byte
        out     timercmd,al     ; timer command port, 43h
        xor     al,al           ; clear initial count for count-down
        out     timer2data,al   ; low order byte of count preset, to port 42h
        out     timer2data,al   ; high order byte, to the same place
        in      al,ppi_port     ; get 8255 setting
        mov     dl,al           ; remember it in dl
        and     al,0fch         ; clear our control bits
        or      al,1            ; start counter now (Gate = 1, speaker is off)
        out     ppi_port,al     ; do it, OUT goes low
                                ; this is the test loop
        mov     ax,8            ; wait 8 millisec
        call    pcwait          ; call the software timer
                                ; end test loop
        mov     al,dl           ; restore ppi port, stop timer
        out     ppi_port,al
        in      al,timer2data   ; read count down value
        xchg    al,ah           ; save low order byte
        in      al,timer2data   ; get high order byte
        xchg    ah,al           ; put in correct sequence
        neg     ax              ; subtract from zero to get elapsed tics
        xor     dx,dx           ; clear high order divisor
        add     ax,1193/2       ; round up
        adc     dx,0            ; for very very slow machines
        mov     cx,1193         ; tics per millisec
        div     cx              ; count / 1193 yields millisecs, quo=ax
        mov     cx,ax           ; retain whole number of milliseconds
        mov     ax,pcwcnt       ; get current pcwait inner loop count
        xor     dx,dx           ; clear high order field for division
        shl     ax,1
        rcl     dx,1
        shl     ax,1
        rcl     dx,1
        shl     ax,1
        rcl     dx,1            ; dx:ax = current counter times 8 loops
        cmp     cx,0            ; no millisec? (super speed machines)
        ja      pcwtst1         ; a = some
        inc     cx              ; use at least one
pcwtst1:div     cx              ; divide by observed milliseconds
        mov     pcwcnt,ax       ; store quotient as new inner loop counter
        pop     dx
        pop     cx
        pop     ax
        ret
pcwtst  endp

;; Wait for the # of milliseconds in ax, for non-IBM compatibles.
;; Thanks to Bernie Eiben for this one. Modified to use adjustable
; inner loop counter (pcwcnt, adjusted by proc pcwtst) by [jrd].
pcwait  proc    near
        push    cx
pcwai0: mov     cx,pcwcnt       ; inner loop counter for 1 ms (240 @ 4.77 MHz)
pcwai1: sub     cx,1            ; inner loop takes 20 clock cycles
        jnz     pcwai1
        dec     ax              ; outer loop counter
        jnz     pcwai0          ; wait another millisecond
        pop     cx
        ret
pcwait  endp

; set the current port.
; Note: serial port addresses are found by looking in memory at 40:00h and
; following three words for COM1..4, resp. All UARTS are assumed to follow
; std IBM addresses relative to 03f8h for COM1, and actual address are offset
; from value found in segment 40h. Global byte flags.comflg is 1,2,3,4 for
; COM1..4, and is 'N' for Network.
; If address 02f8h is found in 40:00h then name COM1 is retained but COM2
;  addressing is used to access the UART and a notice is displayed. IRQ 3
; or IRQ 4 is sensed automatically for any COMx port.
COMS    PROC    NEAR
        mov     dx,offset comptab       ; table of legal comms ports
        mov     bx,0                    ; no extra help text
        mov     ah,cmkey                ; parse key word
        call    comnd
         jmp    r                       ;  failed
        cmp     bl,'N'                  ; NetBios network?
        jne     coms0                   ; ne = no
        jmp     comsn                   ; yes, get another item for networks
coms0:  cmp     bl,'U'                  ; Ungermann Bass net?
        jne     coms1                   ; ne = no
        jmp     comsub
coms1:  mov     temp,bx
        mov     ah,cmcfm
        call    comnd                   ; Get a confirm
         jmp    r                       ;  Didn't get a confirm
         nop
        cmp     flags.comflg,'N'        ; running on network?
        jne     coms1b                  ; ne = no
                                        ; turn off sources of net interrupts
        mov     bx,offset can           ; cancel outstanding requests
        mov     can.scb_cmd,ncancel     ; set cancel op code
        cmp     lposted,1               ; listen outstanding?
        jne     coms1a                  ; ne = no
        mov     can.scb_baddr,offset lsn ; cancel listen
        call    session
        mov     lposted,0
coms1a: cmp     rposted,1               ; receive outstanding?
        jne     coms1b                  ; ne = no
        mov     can.scb_baddr,offset rcv ; cancel receive
        call    session
        mov     rposted,0               ; clear interlock flag

coms1b: call    serrst                  ; close current comms port
        mov     bx,temp                 ; get port number/letter
        mov     flags.comflg,bl         ; remember port number
COMS2:                                  ; Entry point to select comms port
        mov     bh,0
        mov     bl,flags.comflg         ; get COMx number (1-4)
        push    bx                      ; Set UART port addresses
        mov     ax,bx                   ; get COMx (1-4)
        cmp     al,'1'                  ; ascii numbered Bios port?
        jb      coms3                   ; b = no
        sub     al,'0'                  ; remove ascii bias for portinfo
coms3:  dec     ax                      ; count ports from zero
        mov     bx,type prtinfo         ; size of each portinfo structure
        mul     bx                      ; times port number
        add     ax,offset port1         ; plus start of COM1
        mov     portval,ax              ; points to our current port struct
        pop     bx                      ; restore registers
        cmp     bl,' '                  ; doing forced Bios?
        jb      coms4                   ; b = no, hardware
        mov     clone,'B'               ; set clone flag for Bios usage
        call    serini                  ; open port
        jnc     coms3a                  ; carry clear for success
        ret                             ; failure
coms3a: jmp     rskp                    ; all done

coms4:  push    bx                      ; save register
        push    es
        mov     ax,40h          ; look at RS232_base [bx] in Bios area 40:00h
        mov     es,ax
        dec     bl                      ; count com1 as bl = 0, etc
        mov     bh,0                    ; clear high byte
        shl     bx,1                    ; make bx a word index
        mov     ax,es:[bx]              ; get modem base address into ax
        pop     es
        pop     bx
        or      ax,ax                   ; is address zero?
        je      comsf                   ; e = yes, no serial port
comsc:                                  ; hardware tests
        mov     modem.mddat,ax  ; set base address (also data address) 03f8h
        add     ax,3                    ; increment to command port 03fbh
        mov     modem.mdcom,ax          ; set line control register address
        mov     brkadr,ax               ; where to send break command
        add     ax,2                    ; increment to status port 03fdh
        mov     modem.mdstat,ax         ; set line-status port address

        call    chkport                 ; get type of UART support (for Clone)
        jnc     comsu                   ; nc = has a real 8250 uart
        add     flags.comflg,'0'        ; COMn to BIOSn
        push    ax                      ; else tell user about Bios pathway
        push    dx
        mov     ah,prstr
        mov     dx,offset biosmsg
        int     dos
        pop     dx
        pop     ax
        call    serini                  ; open port
        jnc     comsc1                  ; nc = success
        ret                             ; failure
comsc1: jmp     rskp

comsu:  call    chkint                  ; find IRQ for the port
        jc      comsf                   ; c = not found, an error conditon
        call    serini                  ; open port
        jnc     comsu1                  ; clear for success report
        ret                             ; failure
comsu1: jmp     rskp

comsf:  mov     dx,offset badprt        ; say port is not available
        mov     ah,prstr
        int     dos
        mov     al,flags.comflg         ; port ident character
        cmp     al,' '                  ; binary for COM series
        jae     comsf1                  ; ae = no
        mov     dx,offset compt         ; display COM
        int     dos
        mov     ah,conout
        mov     dl,flags.comflg
        add     dl,'0'                  ; display port number
        int     dos
        jmp     comsf3
comsf1: cmp     al,'9'                  ; ascii numeric for Bios series?
        ja      comsf2                  ; a = no
        mov     dx,offset biospt        ; display BIOS
        int     dos
        mov     ah,conout
        mov     dl,flags.comflg         ; ascii port number
        int     dos
        jmp     comsf3
comsf2: mov     dx,offset unkpt         ; unknown type
        int     dos
comsf3: mov     flags.comflg,0          ; bad port, reassign to null port
        mov     ah,prstr
        mov     dx,offset badprt2       ; the rest of the message
        mov     ah,prstr
        int     dos
        stc                             ; say error
        jmp     rskp
                                        ; NetBios Network support
comsn:  mov     ah,cmfile               ; get a word (remote node name)
        mov     dx,offset nambuf        ; work buffer
        mov     word ptr nambuf,0       ; insert terminator
        mov     bx,offset nethlp        ; help message
        call    comnd                   ; get the name
         nop
         nop
         nop
        xchg    ah,al                   ; put byte count in al
        xor     ah,ah                   ; clear junk
        mov     temp,ax                 ; save number of chars entered
        mov     ah,cmcfm
        call    comnd                   ; Get a confirm
         jmp    r                       ;  Didn't get a confirm
         nop
        call    serrst                  ; reset serial port
        call    chknet                  ; start network usage
        cmp     pcnet,0                 ; is network alive (non-zero)?
        jne     comsn4                  ; ne = yes
        stc
        jmp     rskp                    ; return failure

comsn4: mov     portval,offset portn ; set Network port data structure address
        mov     flags.comflg,'N'        ; Set the comm port flag
        call    chkport                 ; set type of port support
        clc                             ; return success
        jmp     rskp                    ; End NetBios

                                        ; Ungermann-Bass terminal port [ohl +]
comsub: mov     ah,cmcfm
        call    comnd                   ; Get a confirm
         jmp    r                       ;  Didn't get a confirm
         nop
        call    serrst                  ; reset serial port
        call    chkub                   ; check UB network presence
        jnc     comsub1                 ; nc = present
        stc
        jmp     rskp                    ; return failure

comsub1:call    netclose                ; better close NetBios parts NOW!
        mov     portval,offset portn ; set Network port data structure address
        mov     flags.comflg,'U'        ; Set the comm port flag
        mov     pcnet,2                 ; network is present and active
        mov     lclexit,offset ubclose  ; address to close network
        call    chkport                 ; get type of port support
        clc                             ; return success
        jmp     rskp                    ; End Ungermann Bass    [ohl -]
COMS    ENDP

; Check which Interrupt ReQuest line the port uses. Technique: allow interrupt
; on transmitter holding register empty, test for that condition first with
; IRQ 4 and then IRQ 3. Returns with IRQ values set and carry clear if success
; or carry set if failure. [jrd]
chkint  proc    near
        cmp     flags.comflg,2          ; COM1 or COM2?
        jbe     chkin2                  ; be = yes, use standard IRQ's
        mov     modem.mddis,MDMINTC     ; IRQ 4 test. mask to disable IRQ 4
        mov     modem.mden,MDMINTO      ; mask to enable IRQ 4
        mov     modem.mdmeoi,20h        ; use general in case we guess wrong
        mov     modem.mdintv,MDMINTV    ; IRQ 4 interrupt vector (0ch)
        mov     intkind,0               ; clear interrupt cause
        call    serini                  ; setup port for IRQ 4
        jc      chkint2                 ; c = failure
        mov     dx,modem.mddat
        inc     dx                      ; interrupt enable reg (3f9h)
        mov     al,2                    ; set xmtr holding reg empty interrupt
        out     dx,al
        mov     ax,1                    ; wait one millisec for interrupt
        call    pcwait                  ;  to occur
        test    intkind,2               ; check cause of interrupt, ours?
        jz      chkint2                 ; z = no, try other IRQ
        call    serrst                  ; reset port
        mov     modem.mdmeoi,EOICOM     ; use specific EOI for IRQ4 level
        clc                             ; this setup worked
        ret
                                        ; IRQ 3 test
chkint2:call    serrst                  ; reset port
        mov     modem.mddis,MDINTC2     ; mask to disable IRQ 3
        mov     modem.mden,MDINTO2      ; mask to enable IRQ 3
        mov     modem.mdmeoi,20h        ; use general in case we guess wrong
        mov     modem.mdintv,MDINTV2    ; IRQ 3 interrupt vector
        mov     intkind,0               ; clear interrupt cause
        call    serini                  ; setup port for IRQ 3
        jc      chkin2                  ; c = failure
        mov     dx,modem.mddat
        inc     dx                      ; interrupt enable reg (3f9h)
        mov     al,2                    ; set xmtr holding reg empty interrupt
        out     dx,al
        mov     ax,1                    ; wait one millisec for interrupt
        call    pcwait                  ;  to occur
        test    intkind,2               ; check cause of interrupt, ours?
        jz      chkin2                  ; z = no, so no interrupts for port
        call    serrst                  ; reset port
        mov     modem.mdmeoi,EOICOM2    ; use specific EOI for IRQ 3 level
        clc                             ; this setup worked
        ret

chkin2: call    serrst                  ; reset port, auto test did not work
        cmp     flags.comflg,1          ; COM1?
        je      chkin4                  ; e = yes, use IRQ 4
        cmp     isps2,0                 ; IBM PS/2 Model 50 or above?
        jne     chkin3                  ; ne = yes, other COMs use IRQ 3
        cmp     flags.comflg,3          ; COM2, COM3, or COM4?
        je      chkin4                  ; e = COM3, use IRQ 4
        jmp     short chkin3            ; else COM2 or COM4, use IRQ 3
chkin4: cmp     modem.mddat,02f8h       ; really COM2 material for PCjr?
        je      chkin3                  ; e = yes, use COM2 addresses
        mov     modem.mdmeoi,EOICOM     ; use specific EOI for IRQ4 level
        mov     modem.mddis,MDMINTC     ; IRQ 4 test. mask to disable IRQ 4
        mov     modem.mden,MDMINTO      ; mask to enable IRQ 4
        mov     modem.mdintv,MDMINTV    ; IRQ 4 interrupt vector (0ch)
        jmp     short chkin5
chkin3: mov     modem.mdmeoi,EOICOM2    ; use specific EOI for IRQ 3 level
        mov     modem.mddis,MDINTC2     ; mask to disable IRQ 3
        mov     modem.mden,MDINTO2      ; mask to enable IRQ 3
        mov     modem.mdintv,MDINTV2    ; IRQ 3 interrupt vector
chkin5: clc
        ret
chkint  endp

; Test presently selected serial port for having a real 8250 UART.
; Return carry clear and clone = 0 if 8250 present,
;  else carry set and clone = 'B' for system Bios or
;  carry set and clone = 'N' for network.
; Method is to check UART's Interrupt Identification Register for high
; five bits being zero; IBM does it this way. Assumes port structure
; has been initialized with addresses of UART.  21 Feb 1987 [jrd]
; 29 May 1987 Add double check by reading Line Status Register. [jrd]

chkport proc    near
        cmp     portval,offset portn    ; network?
        je      chkporn                 ; e = yes
        push    ax
        push    dx
        cmp     flags.comflg,0          ; undefined port?
        je      chkpor1                 ; e = yes, assume Bios clone
        mov     dx,modem.mdcom  ; address of UART line control reg (3FBh/2FBh)
        sub     dx,1            ; Interupt Identification Register address
        in      al,dx                   ; read UART's IIR
        test    al,0f8h                 ; are any of high 5 bits set?
        jnz     chkpor1                 ; nz = yes, not an 8250
        mov     dx,modem.mdstat         ; line status register
        in      al,dx                ; read to clear UART BI, FE, PE, OE bits
        jmp     $+2                     ; pause, for chip access timing
        in      al,dx                   ; these bits should be cleared
        test    al,8eh                  ; are they cleared?
        jnz     chkpor1                 ; nz = no, not an 8250
        mov     clone,0                 ; clear clone flag
        pop     dx
        pop     ax
        clc                             ; clear carry (say 8250)
        ret
chkpor1:pop     dx
        pop     ax
        mov     clone,'B'               ; set clone flag
        stc                             ; set carry (say no 8250)
        ret
chkporn:push    ax                      ; Networking
        mov     al,flags.comflg         ; get port letter
        mov     clone,al
        pop     ax
        stc                             ; set carry (say no 8250)
        ret
chkport endp
;;;;;;;;;;;;;;;;;;;;;; end of part one of msxibm.asm
;;;;;;;;;;;;;;;;;;;;;; start part two of msxibm.asm

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.
; 21 Feb 1987 Add support for Bios calls (Clone) [jrd]

DOBAUD  PROC    NEAR
        push    ax                      ; save some regs
        push    bx
        push    dx
;;;     call    chkport         ; check port for real 8250 UART (clone = 0)
        cmp     clone,'N'               ; Netbios?
        je      dobd1                   ; e = yes, do nothing here
        cmp     clone,'U'               ; UB network?
        je      dobd1                   ; e = yes
        mov     bx,portval              ; pointer to port data structure
        mov     temp,ax                 ; Don't overwrite previous rate
        mov     ax,[bx].baud            ; Check if new rate is valid
        shl     ax,1                    ; make a word index
        mov     bx,offset bddat         ; Start of table
        cmp     clone,'B'               ; running on clone?
        jne     dobd0a                  ; ne = no
        mov     bx,offset clbddat       ; use Bios speed parameters for clone
dobd0a: add     bx,ax
        mov     ax,[bx]                 ; The data to output to port
        cmp     ax,0FFH                 ; Unimplemented baud rate
        jne     dobd0
        mov     ah,prstr
        mov     dx,offset badbd         ; Give an error message
        int     dos
        jmp     dobd1

dobd0:  cmp     clone,0                 ; running on a real uart?
        je      dobd2                   ; e = the real thing
        mov     dx,0            ; assume port 1 Clone: find current port
        mov     dl,flags.comflg         ; get coms port (1..4)
        or      dl,dl                   ; zero (undefined port)?
        jz      dobd1                   ; z = yes, just exit
        and     dl,7                    ; use lower three bits
        dec     dl                      ; count ports as 0..3 for Bios
        mov     ah,0                    ; set serial port
        int     rs232                   ; Bios: set the parameters
        jmp     dobd1                   ; and exit

dobd2:  mov     temp,ax                 ; Remember value to output
        mov     dx,modem.mdcom          ; LCR -- Initialize baud rate
        in      al,dx                   ; get it
        mov     bl,al                   ; make a copy
        or      ax,80H          ; turn on DLAB bit to access divisor part
        out     dx,al
        mov     dx,modem.mddat
        mov     ax,temp                 ; set the baud rate divisor, low byte
        out     dx,al
        inc     dx                      ; next address for high part
        mov     al,ah                   ; set high part of divisor
        out     dx,al
        mov     dx,modem.mdcom          ; LCR again
        mov     al,bl                   ; get original setting from bl
        out     dx,al                   ; restore it
dobd1:  pop     dx                      ; restore regs
        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.
; 21 Feb 1987 Add support for Bios calls (Clone) [jrd]

GETBAUD PROC    NEAR
;;      call    chkport                 ; check for real 8250 UART
;;      jnc     getbud                  ; nc = have one
        cmp     clone,0                 ; non-clone
        je      getbud                  ; e = yes, real thing
        ret                             ; have semi-clone, no bios feedback
getbud: push    ax                      ; save some regs
        push    bx
        push    cx
        push    dx
        mov     dx,modem.mdcom       ; Get current Line Control Register value
        in      al,dx
        mov     bl,al                   ; Save it
        or      ax,80H                ; Turn on to access baud rate generator
        out     dx,al
        mov     dx,modem.mddat          ; Divisor latch
        inc     dx
        in      al,dx                   ; Get hi order byte
        mov     ah,al                   ; Save here
        dec     dx
        in      al,dx                   ; Get lo order byte
        push    ax
        mov     dx,modem.mdcom          ; Line Control Register
        mov     al,bl                   ; Restore old value
        out     dx,al
        pop     ax
        cmp     ax,0FFFFH               ; Who knows what this is
        je      getb2
        mov     bx,offset bddat         ; Find rate's offset into table
        mov     cl,0                    ; Keep track of index
getb0:  cmp     ax,[bx]
        je      getb1
        inc     cl
        cmp     cl,baudlen              ; At the end of the list
        jge     getb2
        add     bx,2
        jmp     getb0
getb1:  mov     ch,0
        mov     bx,portval
        mov     [bx].baud,cx            ; Set baud rate
        jmp     getb3
getb2:  mov     ah,prstr
        mov     dx,offset erms40
        int     dos
getb3:  pop     dx                      ; restore regs
        pop     cx
        pop     bx
        pop     ax
        ret
GETBAUD ENDP

; Get Char from serial port buffer.
; 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]
; 21 Feb 1987 Add support for Bios calls (Clone) [jrd]
; 25 April 1987 Add Netbios support, remove test for NUL and DEL. [jrd]
PRTCHR  PROC    NEAR
        cmp     holdscr,0               ; Holdscreen in effect?
        jne     prtch0a                 ; ne = yes, do not read
        call    chkxon                  ; see if we need to xon
        cmp     clone,'B'               ; running on a semi-clone?
        je      prtch6                  ; e = yes
        cmp     clone,'N'               ; running on NetBios network?
        je      prtchn                  ; e = yes
        cmp     clone, 'U'              ; running Ungermann-Bass port? [ohl]
        jne     prtch0                  ; ne = no
prtchn: test    xofsnt,usron            ; user level xoff sent?
        jnz     prtch0a                 ; nz = yes, suppress reading here
        cmp     count,mntrgl            ; below low water mark?
        ja      prtch1                  ; a = no, read current buffer
        cmp     clone,'U'               ; UB network?
        jne     prtchn1                 ; ne = no
        call    ubrecv                  ; do a UB receive
        jmp     short prtch0
prtchn1:call    receive                 ; do a NetBios receive (asynchronous)
        jc      prtch0a                 ; c = failure
prtch0: cmp     count,0                 ; any characters available?
        jnz     prtch1                  ; nz = yes, get one
prtch0a: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 consistent
        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 buf?
        jae     prtch2                  ; ae = no
        add     si,bufsiz               ; else do arithmetic modulo bufsiz
prtch2: mov     al,byte ptr [si]        ; get a character into al
        dec     count                   ; one less unread char now
        sti                             ; interrupts back on now
        pop     si
        mov     dx,count                ; return # of chars in buffer
        jmp     prtch12                 ; screen delivered characters

prtch6:                                 ; Semi-clone, use Bios calls
        mov     dx,0            ; assume port 1 Clone: find current port
        mov     dl,flags.comflg         ; get port number (1..4)
        or      dl,dl                   ; zero (no such port)?
        jz      prtch8                  ; z = yes, don't access it
        and     dl,7                    ; use low three bits
        dec     dl                      ; address ports as 0..3 for Bios
prtch7: mov     ah,3                    ; check port status
        int     rs232                   ; Clone Bios call
        test    ah,mdminp               ; data ready?
        jnz     prtch9                  ; nz = yes, get one
prtch8: mov     dx,0                    ; return count of zero
        mov     count,dx
        jmp     rskp                    ; No data
prtch9: mov     ah,2                    ; receive a char into al
        int     rs232                   ; Clone Bios call
        test    ah,8ch                  ; timeout, framing error, parity error?
        jnz     prtch8                  ; nz = error, no char
        mov     dx,1
        mov     count,dx                ; one char received into al
        cmp     al,flowoff              ; acting on Xoff?
        jne     prtch10                 ; ne = no, go on
        cmp     xofsnt,0                ; have we sent an outstanding XOFF?
        jne     prtch8                  ; ne = yes, ignore (possible echo)
        mov     xofrcv,bufon            ; Set the flag saying XOFF received
        jmp     prtch8                  ;  and exit
prtch10:cmp     al,flowon               ; acting on Xon?
        jne     prtch12                 ; ne = no, go on
        mov     xofrcv,off              ; Clear the XOFF received flag
        jmp     short prtch8            ; no data to return
prtch12:test    flags.debug,logses      ; debug mode?
        jnz     prtch14                 ; nz = yes, pass all chars
        cmp     rxtable+256,0           ; translation turned off?
        jne     prtch14                 ; ne = table is on, pass all chars
        cmp     al,0                    ; NUL?
        je      prtch13                 ; e = yes, ignore it
        cmp     tekflg,0                ; Tek emulation active?
        jne     prtch14                 ; ne = yes, pass DEL
        cmp     al,DEL                  ; DEL char
        jne     prtch14                 ; ne = no, pass char
prtch13:mov     dx,0
        jmp     rskp                    ; no chars
prtch14:ret                             ; return char in al
PRTCHR  ENDP

; Network Receive packet routine. Request a net packet with no-wait option.
; Return carry clear if success. If failure, reset serial port (Server mode
; reinits serial port) and return carry set. No entry setup needed.
RECEIVE PROC    NEAR                    ; receive network session pkt
        cmp     pcnet,1                 ; net ready yet?
        jbe     receiv3                 ; be = no, declare a broken session
        cmp     rposted,1               ; is a request outstanding now?
        jae     receiv4                 ; ae = yes, don't do another
        mov     rposted,1               ; say posting a receive now
        mov     rcv.scb_length, length rcvbuf ; length of input buffer
        mov     rcv.scb_cmd,nreceive+nowait   ; receive, no wait
        push    bx
        mov     bx,offset rcv           ; setup pointer to scb
        call    session
        pop     bx                      ; tests below SHOULD take time
        cmp     rcv.scb_err,0           ; success?
        je      receiv4                 ; e = yes
        cmp     rcv.scb_err,npending    ; pending receive?
        je      receiv4                 ; e = yes
        push    ax                      ; wait one millisec before retesting
        push    cx
        mov     ax,1
        call    pcwait
        pop     cx
        pop     ax
        cmp     rcv.scb_err,06h         ; message incomplete?
        je      receiv4                 ; e = is ok (response posted later)
        cmp     rcv.scb_err,0        ; success now? (here for latency effects)
        je      receiv4                 ; e = yes
        cmp     rcv.scb_err,18h         ; session ended abnormally?
        jbe     receiv3                 ; e = yes, b = other normal errors
        push    ax                      ; save regs around error display
        push    dx
        mov     ah,prstr
        mov     dx,offset recmsg        ; give error message
        int     dos
        mov     al,rcv.scb_err          ; get error code
        call    hexout                  ; show error code
        pop     dx
        pop     ax
                                        ; Error return
receiv3:mov     pcnet,1                 ; say session is broken
        call    serrst                  ; reset serial port
        test    flags.remflg,dserver    ; server mode?
        jz      receiv3a                ; z = no
        call    serini                  ; reinitialize it for new session
receiv3a:stc                            ; say failure to receive
        ret
receiv4:clc                             ; carry clear = success
        ret
RECEIVE ENDP

; Network Receive post processing interrupt routine.
; Copy chars from rcvbuf to circular buffer source, act on xon/xoff,
; clear rposted interlock flag. At entry, CS is our code segment,
; es:bx points to scb, netbios stack, interrupts are off.
RPOST   PROC    NEAR            ; network receive post interrupt routine
        push    ds              ; transfers chars from net buf rcvbuf to
        push    ax              ; main circular buffer source
        push    bx
        push    cx
        push    dx
        push    si
        mov     ax,datas                ; reestablish datas segment
        mov     ds,ax
        mov     cx,rcv.scb_length       ; get returned byte count
        jcxz    rpost6                  ; z = nothing there
        mov     dh,flowon             ; flow control characters, for quick ref
        mov     dl,flowoff
        mov     si,offset rcvbuf        ; source of text
        mov     bx,srcpnt               ; address of buffer storage slot
        cld
rpost1: lodsb                           ; get byte from rcvbuf to al
        or      dx,dx                   ; doing flow control?
        jz      rpost3                  ; z = no
        mov     ah,al                   ; get copy of character
        and     ah,parmsk               ; strip parity, if any, before testing
        cmp     ah,dl                   ; acting on Xoff?
        jne     rpost2                  ; ne = Nope, go on
        cmp     xofsnt,0                ; have we sent an XOFF?
        jne     rpost5                  ; ne = yes, ignore this XOFF char
        mov     xofrcv,bufon        ; Set the flag saying buffer XOFF received
        jmp     rpost5                  ;  and skip this character
rpost2: cmp     ah,dh                   ; acting on Xon?
        jne     rpost3                  ; ne = no, go on
        mov     xofrcv,off              ; Clear the XOFF received flag
        jmp     rpost5                  ;  and skip this character

rpost3: mov     byte ptr [bx],al       ; store the new char in buffer "source"
        inc     bx
        cmp     bx,offset source + bufsiz ; beyond end of buffer?
        jb      rpost4                  ; b = not past end
        mov     bx,offset source        ; wrap buffer around
rpost4: cmp     count,bufsiz            ; filled already?
        jae     rpost5                  ; ae = yes
        inc     count                   ; no, add a char
rpost5: loop    rpost1
        mov     srcpnt,bx               ; update pointer to next free slot
rpost6: mov     rposted,0               ; clear interlock flag
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     ds
        iret                            ; return from interrupt
RPOST   endp

; Ungermann-Bass NETCI port receive characters routine.  Receive one or more
; characters.  Calls the Rpost routine to transfer character to main source
; circular buffer.  Return carry clear if success.
UBRECV  PROC    near
        push    ax
        push    bx
        push    cx
        push    es
        mov     ax, datas
        mov     es, ax                  ; es:bx will point to rcvbuf
        mov     ax, nciread             ; function 1 (receive) port 0    [ohl]
        mov     bx, offset rcvbuf
        mov     cx, length rcvbuf
        int     netci                   ; get characters                 [ohl]
        stc
        jcxz    ubrec1                  ; cx = z = nothing to do
        mov     rcv.scb_length, cx      ; prepare for rpost call         [ohl]
        mov     bx, offset rcv
        pushf                           ; simulate interrupt             [ohl]
        push    cs                      ; rpost is an interrupt routine (iret)
        call    rpost                   ; do Near call with preset stack
        clc
ubrec1: pop     es
        pop     cx
        pop     bx
        pop     ax
        ret
UBRECV  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.
; 21 Feb 1987 Add support for Bios calls (Clone) [jrd]
; 25 April 1987 Add Netbios support [jrd]
; 16 May 1987 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
outch3: cmp     clone,0                 ; real uart?
        je      outch3a                 ; e = yes
        cmp     clone,'N'               ; network?
        je      outch8                  ; e = yes, using netbios
        cmp     clone,'U'               ; Ungermann Bass network?
        je      outch8                  ; e = yes
        jmp     outch6                  ; default for others ('B' clones)
outch3a:push    cx                      ; Save registers
        push    dx
        sub     cx,cx
outch3b:mov     dx,modem.mdstat         ; Get port status
        in      al,dx
        test    al,20H                  ; Transmitter ready?
        jnz     outch4                  ; Yes
        jmp     $+2                     ; use time, prevent overdriving UART
        jmp     $+2
        loop    outch3b
         jmp    outch5                  ; Timeout
outch4: mov     al,ah                   ; Now send it out
        mov     dx,modem.mddat          ; use a little time
        jmp     $+2
        out     dx,al
        pop     dx                      ; exit success
        pop     cx
        jmp     rskp
outch5: pop     dx                      ; exit failure
        pop     cx
        ret
                                ; finish up for semi-clones, use Bios calls
outch6: push    cx                      ; Clone: find current port
        push    dx
        mov     dx,0                    ; assume port 1
        mov     dl,flags.comflg         ; get port number (1..4)
        or      dl,dl                   ; zero (no such port)?
        jz      outch5                  ; z = yes, don't access it
        and     dl,7                    ; use lower three bits
        dec     dl                      ; address ports as 0..3 for Bios
        mov     al,ah                   ; Now send it out
        mov     ah,1                    ; send char
        int     rs232                   ; Clone: bios send
        pop     dx                      ; exit success
        pop     cx
        jmp     rskp
outch8:                         ; Network sending, buffered and single char
        push    bx
        mov     bx,xmtcnt               ; count of chars in buffer
        mov     xmtbufx[bx],ah          ; put char in buffer
        pop     bx
        inc     xmtcnt                  ; count of items in this buffer
        cmp     xmtcnt,length xmtbuf    ; is buffer full now?
        jae     outch9                  ; ae = buffer is full, send it now
        cmp     ah,trans.seol           ; end of packet?
        je      outch9                  ; e = yes, send buffer
        cmp     ah,flowon               ; flow control?
        je      outch9                  ; e = yes, always expedite
        cmp     ah,flowoff              ; ditto for flow off
        je      outch9
        cmp     ttyact,0                ; are we in Connect mode?
        je      outch10                 ; e = no, wait for more before sending
outch9: cmp     clone, 'U'              ; check for UB port      [ohl]
        je      outch12                 ; e = yes                [ohl]
        call    send                    ; NetBios network send routine
        jc      outch11                 ; c = error
outch10:jmp     rskp                    ; good  exit
outch11:ret                             ; bad   exit

outch12:call    ubsend                  ; UB network send        [ohl]
        jmp     rskp                    ; good exit              [ohl]
OUTCHR  ENDP

; Network Send packet routine. Send xmt scb with no-wait option. Waits
; up to 6 seconds for current Send to complete before emitting new Send.
; Failure to Send resets serial port (Server mode allows reiniting of serial
; port). Returns carry clear for success, carry set for failure.
; Enter with xmtcnt holding length of data in xmtbuf to be sent.
SEND    PROC    NEAR                    ; Network. Send session packet
        cmp     pcnet,1                 ; network ready yet?
        ja      send0b                  ; a = net is operational
        je      send0c                  ; e = net but no session, fail
        jmp     send3a                  ; no net, fail
send0c: jmp     send3                   ; net but no session
send0b: cmp     sposted,0               ; is a send outstanding now?
        je      send1                   ; e = no, go ahead
        push    cx                      ; Timed test for old send being done
        mov     ch,trans.rtime          ; receive timeout other side wants
        mov     cl,80h                  ; plus half a second
        shl     cx,1                    ; sending timeout * 512
send0:  cmp     sposted,0               ; is a send outstanding now?
        je      send0a                  ; e = no, clean up and do send
        push    cx                      ; save cx
        push    ax                      ; and ax
        mov     ax,2                    ; wait 2 milliseconds
        call    pcwait                  ;  between retests
        pop     ax
        pop     cx                      ; loop counter
        loop    send0                   ; repeat test
        pop     cx                      ; recover cx
        jmp     send3a                  ; get here on timeout, can't send
send0a: pop     cx                      ; recover cx and proceed to send

send1:  cmp     xmtcnt,0                ; number of items to send
        jne     send1a                  ; ne = some
        clc                             ; else don't send null packets
        ret
send1a: push    bx                      ; save bx
        mov     bx,xmtcnt               ; buffer length
        mov     xmt.scb_length,bx       ; tell buffer length
        push    cx
        push    si
        push    di
        push    es
        push    ds
        pop     es                      ; set es to datas segment
        mov     si,offset xmtbufx       ; external buffer
        mov     di,offset xmtbuf        ; copy for network packets
        mov     cx,bx                   ; buffer length
        shr     cx,1                    ; divide by two (words), set carry
        jnc     send2                   ; nc = even number of bytes
        movsb                           ; do single move
send2:  cmp     cx,0
        jle     send2a                  ; le = none to do
        rep     movsw                   ; copy the data
send2a: pop     es
        pop     di
        pop     si
        pop     cx
        mov     xmtcnt,0                ; say xmtbufx is available again
        mov     xmt.scb_cmd,nsend+nowait ; send, don't wait for completion
        mov     sposted,1               ; say send posted
        mov     bx,offset xmt           ; set pointer to scb
        call    session
        pop     bx                      ; recover pointer to scb
                                        ; success or failure?
        cmp     xmt.scb_err,0           ; good return?
        je      send4                   ; e = yes
        cmp     xmt.scb_err,npending    ; pending?
        je      send4                   ; e = yes
        cmp     xmt.scb_err,18h         ; session ended abnormally?
        jbe     send3                   ; e = yes, b = other normal errors
        push    ax
        push    dx                      ; another kind of error, show message
        mov     ah,prstr
        mov     dx,offset sndmsg        ; say send failed
        int     dos
        mov     al,xmt.scb_err          ; show error code (hex)
        call    hexout
        pop     dx
        pop     ax
                                        ; Error return
send3:  mov     pcnet,1                 ; say session is broken
        call    serrst                  ; reset serial port
        test    flags.remflg,dserver    ; server mode?
        jz      send3a                  ; z = no
        call    nethangup               ; Server: purge old NAKs etc
send3a: call    serini                  ; reinitialize it for new session
        stc                             ; set carry for failure to send
        ret
send4:  clc
        ret
SEND    ENDP

; Network Send packet completion interrupt routine. At entry CS is our
; code segment, es:bx points to scb, netbios stack, interrupts are off.
SPOST   PROC    NEAR                    ; post routine for Send packets
        push    ds
        push    ax
        mov     ax,datas
        mov     ds,ax
        mov     sposted,0               ; clear send interlock
        pop     ax
        pop     ds
        iret
SPOST   ENDP

; Ungermann-Bass NETCI port send packet routine. [ohl] +++
; Enter with xmtcnt holding length of data in xmtbuf to be sent.
ubsend  proc    near
        push    ax
        push    bx
        push    cx
        push    es
        mov     cx, xmtcnt              ; number of chars                [ohl]
        jcxz    ubsend1                 ; dont send zero chars           [ohl]
        mov     bx, offset xmtbufx      ; buffer address in es:bx        [ohl]
        mov     ax, datas
        mov     es, ax
ubsend2:
        mov     ax, nciwrit             ; write function, port 0         [ohl]
        int     netci
        cmp     cx,xmtcnt               ; check that all characters sent [ohl]
        je      ubsend1                 ; e = yes                        [ohl]
        add     bx, cx                  ; point to remaining chars       [ohl]
        sub     xmtcnt,cx               ; count of remaining characters  [ohl]
        mov     cx,xmtcnt               ; need count in cx too
        jmp     short   ubsend2         ; try again to send              [ohl]
ubsend1:mov     xmtcnt,0
        pop     es
        pop     cx
        pop     bx
        pop     ax
        ret
ubsend  endp                            ;  [ohl] ---


; dispatch prebuilt network session scb, enter with bx pointing to scb.
; returns status in al (and ah too). Allows STARLAN Int 2ah for netint.
SESSION PROC    NEAR
        push    es                      ; save es around call
        push    ds
        pop     es                      ; make es:bx point to scb in datas seg
        mov     ax,exnbios              ; funct 4 execute netbios, for Int 2ah
        int     netint                  ; use network interrupt
        pop     es                      ; saved registers
        ret                             ; exit with status in ax
SESSION ENDP

; Make a virtual circuit Session, given preset scb's from proc chknet.
; For Server mode, does a Listen to '*', otherwise does a Call to indicated
; remote node. Updates vcid number in scb's. Shows success or fail msg.
; Updates network status byte pcnet to 2 if session is established.
; Does nothing if a session is active upon entry; otherwise, does a network
; hangup first to clear old session material from adapter board. This is
; the second procedure to call in initializing the network for usage.
SETNET  PROC    NEAR                    ; Network, make a connection
        cmp     lposted,1               ; Listen pending?
        je      setne0                  ; e = yes, exit now
        cmp     pcnet,1                 ; session active?
        jbe     setne1                  ; be = no
setne0: ret
                                        ; No Session
setne1: call    nethangup               ; clear old session material
        test    flags.remflg,dserver    ; Server mode?
        jz      setne2                  ; z = no, file xfer or Connect
                                        ; Server mode, post a Listen (async)
        mov     lsn.scb_rname,'*'       ; accept anyone
        mov     ax,500
        call    pcwait                  ; 0.5 sec wait
        push    bx
        mov     lposted,1               ; set listen interlock flag
        mov     lsn.scb_cmd,nlisten+nowait ; do LISTEN command, no wait
        mov     bx,offset lsn
        call    session
        pop     bx
        ret
setne2:                                 ; Non-server (Client) mode
        push    bx                      ; save reg
        test    nettype,starlan         ; STARLAN?
        jz      setne2a                 ; z = no
        cmp     xmt.scb_vrlen,0         ; yes, using long name support?
        je      setne2a                 ; e = no
        push    es                      ; save reg
        push    ds
        pop     es                      ; make es:bx point to xmt scb
        mov     bx,offset xmt           ; use xmt scb for the call
        mov     xmt.scb_cmd,ncall       ; CALL_ISN, vrname + vrlen are ready
        int     5bh                     ; STARLAN CALL Int 5bh, wait
        pop     es                      ; restore regs
        pop     bx
        jmp     short setne3            ; finish up

setne2a:                                ; Regular Netbios Call
        mov     xmt.scb_cmd,ncall       ; CALL, wait for answer
        mov     bx,offset xmt           ; setup scb pointer
        call    session
        pop     bx                      ; restore register

setne3: push    dx                      ; common Call completion, show status
        test    xmt.scb_err,0ffh        ; is there a non-zero return code?
        jnz     setne3a                 ; nz = yes, do bad return
        or      al,al                   ; check error return
        jnz     setne3a                 ; nz = bad connection
        jmp     short setne4            ; good connection so far
setne3a:mov     dx,offset nbadset       ; say can't reach remote node
        mov     ah,prstr
        int     dos
        call    saynode                 ; show remote host node name
        jmp     setne4c
                                        ; keep results of Call (vcid)
setne4: mov     al,xmt.scb_vcid         ; local session number
        mov     rcv.scb_vcid,al         ; for receiver too
        mov     can.scb_vcid,al         ; for sending Breaks
        mov     pcnet,2                 ; say session has started
        test    flags.remflg,dregular+dquiet ; regular or quiet display?
        jnz     setne4c                 ; nz = yes, show only no-connect msg
        mov     dx,offset ngodset       ; say good connection
        mov     ah,prstr
        int     dos
        call    saynode                 ; show remote host name
setne4c:pop     dx
        cmp     pcnet,1                 ; check connection again
        ja      setne5                  ; a = good so far
        stc                             ; set carry for failure
        ret
setne5: clc                             ; carry clear for success
        ret
SETNET  ENDP

saynode proc    near            ; display node name on screen, si=name ptr
        push    ax
        push    cx
        push    dx
        push    si
        mov     ah,conout
        mov     si,offset nambuf        ; remote node string
        mov     cx,64                   ; up to 64 bytes long
saynod1:cld
        lodsb                           ; get remote node name char into al
        mov     dl,al
        int     dos                     ; display it
        cmp     al,' '                  ; was it a space?
        jbe     saynod2                 ; be = yes, quit here
        loop    saynod1                 ; do all 16 chars
saynod2:mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        pop     si
        pop     cx
        pop     cx
        pop     ax
        ret
saynode endp

LPOST   PROC    FAR             ; Interrupt Post routine for Listen call
        push    ds              ; update vcid and calling node name in scb's
        push    cx
        push    es
        push    si
        push    di
        mov     cx,datas                ; reestablish datas segment
        mov     ds,cx
        mov     es,cx
        mov     si,offset lsn.scb_rname ; copy remote name to rcv and xmt scbs
        push    si
        mov     di,offset rcv.scb_rname
        mov     cx,8                    ; 16 byte field
        cld
        rep     movsw
        mov     cx,8
        pop     si
        push    si
        mov     di,offset xmt.scb_rname
        rep     movsw
        mov     cx,8
        pop     si
        mov     di,offset nambuf        ; and to nambuf for display
        rep     movsw
        mov     cl,lsn.scb_vcid         ; local session number
        mov     rcv.scb_vcid,cl
        mov     xmt.scb_vcid,cl
        mov     can.scb_vcid,cl
        mov     lposted,0               ; clear interlock flag
        mov     pcnet,2                 ; say net ready due to a Listen
        pop     di
        pop     si
        pop     es
        pop     cx
        pop     ds
        iret                            ; return from interrupt
LPOST   ENDP


NETHANGUP PROC  NEAR                 ; disconnect network session, keep names
        cmp     pcnet,0                 ; network started?
        je      nethang1                ; e = no
        cmp     clone, 'U'              ; Ungermann-Bass port? [ohl]
        je      nethang2                ; e = yes [ohl]
        push    bx                      ; NetBios network
        mov     bx,offset can
        mov     can.scb_cmd,ncancel     ; set cancel op code
        mov     can.scb_baddr,offset lsn ; cancel listens
        mov     lposted,0               ; say no listen
        call    session
        mov     can.scb_baddr,offset rcv ; cancel receives
        call    session
        mov     rposted,0               ; say no receives posted
        mov     can.scb_baddr,offset xmt ; cancel sends
        call    session
        mov     sposted,0               ; say no sends posted
        mov     xmtcnt,0                ; reset output buffer counter
        mov     xmt.scb_cmd,nhangup     ; hangup, and wait for completion
        mov     bx,offset xmt
        call    session
        pop     bx
        mov     pcnet,1                 ; say network but no session
        call    serrst                  ; reset the serial port for reiniting
nethang1:ret
                                        ; UB network [ohl] +++
nethang2:call   ubclose                 ; close connection if any [ohl]
        mov     xmtcnt,0
        mov     pcnet,1
        ret                             ;  [ohl] ---
NETHANGUP ENDP

; Ungermann Bass. Do a disconnect from the current connection.
ubclose proc    near
        push    ax
        push    cx
        cmp     nettype,netone          ; UB network has been activated?
        jz      ubclos1                 ; z = no
        mov     ax, ncistat             ; get status                     [ohl]
        int     netci
        cmp     ch, 1                   ; check if we have a connection  [ohl]
        jne     ubclos1                 ; ne = no                        [ohl]
        mov     ax, ncicont             ; control function               [ohl]
        mov     cx, ncidis              ; say disconnect                 [ohl]
        int     netci
ubclos2:call    ubrecv                  ; read response from net cmdintpr[ohl]
        jnc     ubclos2                 ; continue till no chars         [ohl]
ubclos1:and     nettype,not netone      ; remove network type
        mov     pcnet,0                 ; say no network
        pop     cx
        pop     dx
        ret
ubclose endp

; Called when Kermit exits. Name passed to mssker by proc chknet
; in word lclexit.
NETCLOSE PROC   NEAR                    ; close entire network connection
        cmp     pcnet,0                 ; network ever used?
        je      netclo1                 ; e = no, so don't touch it
        call    nethangup               ; close connections
        test    nettype,netbios         ; NetBios activated?
        jz      netclo1                 ; z = no
        push    bx
        mov     bx,offset xmt
        mov     xmt.scb_cmd,ndelete     ; delete our local Kermit name
        call    session                 ;  from net adapter board
        pop     bx
        mov     pcnet,0                 ; say no network
        and     nettype,not netbios     ; remove network kind
netclo1:ret
NETCLOSE ENDP

; Start connection process to network. Obtains Network board local name
; and appends '.K' to form Kermit's local name (removed when Kermit exits).
; If no local name is present then use name 'mskermit.K'.
; Sets local name in scb's for xmt, rcv, lsn. (Does not need DOS 3.x)
; Sets NETDONE pointer to procedure netclose for Kermit exit.
; Verifies existance of interrupt 5ch support, verifies vendor specific
; support for BREAK and other features, sets network type bit in nettype,
; sets BREAK support in nsbrk, hangsup old session if new node name given,
; fills in local and remote node names and name number in scbs (including ISN
; names for STARLAN), and sets network status byte pcnet to 0 (no net) or
; to 1 (net ready). This is the first procedure called to init network usage.
; Byte count of new host name is in temp from COMS.
chknet  proc    near
        cmp     clone,'U'               ; Ungermann Bass network?
        jne     chknea                  ; ne = no
        mov     pcnet,0                 ; force reactivation of UB net
chknea: cmp     pcnet,2                 ; session active now?
        jb      chknec                  ; b = no
        cmp     temp,0                  ; byte count of new name, if any
        je      chkneb                  ; e = none, resume old session
        mov     dx,offset naskpmt       ; prompt for New or Resume
        call    prompt
        mov     dx,offset nettab        ; table of answers
        mov     bx,0                    ; help for the question
        mov     ah,cmkey                ; get answer keyword
        call    comnd
         jmp r                          ; failed
         nop
        mov     rdbuf,bl                ; save keyword action value here
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; no confirm
         nop
        cmp     rdbuf,0                 ; New session?
        je      chkneb                  ; e = yes
        clc
        ret                             ; resume old one
chkneb: jmp     chknet1                 ; skip presence tests

chknec:                         ; setup addresses and clear junk in scb's
        cmp     pcnet,0                 ; have we been here already?
        je      chkned                  ; e = no
        jmp     chknet1                 ; yes, skip init part
chkned: mov     xmtcnt,0                ; say buffer is empty
        mov     nsbrk,0                 ; assume no BREAK across network
        and     nettype,not netbios     ; say no NetBios network yet

        push    bx
        push    es                      ; Test for Netbios presence, IBM way
        mov     ah,35h                  ; DOS get interrupt vector
        mov     al,netint               ; the netbios vector
        int     dos                     ; returns vector in es:bx
        mov     ax,es
        cmp     ax,0f000h               ; rom bios segment??
        jb      chknee                  ; b = not likely, else Bios has
        mov     ax,0                    ;  trapped this vector to dummy iret
        mov     bx,0                    ; fake null vector
chknee: or      bx,ax                   ; is vector present?
        pop     es
        pop     bx
        jz      chknet0                 ; z = no
        mov     xmt.scb_cmd,7fh ; presence test, 7fh is illegal command code
        mov     xmt.scb_err,0           ; clear response field
        push    bx
        mov     bx,offset xmt           ; address of the session control block
        call    session                 ; execute operation
        pop     bx
        mov     al,xmt.scb_err          ; get response
        cmp     xmt.scb_err,3       ; 'illegal function', so adapter is ready
        jne     chknet0                 ; ne = failure
        push    bx
        push    es                      ; Test for Netbios presence, IBM way
        mov     ah,35h                  ; DOS get interrupt vector
        mov     al,2ah                  ; the netbios vector 2ah
        int     dos                     ; returns vector in es:bx
        mov     ax,es
        cmp     ax,0f000h               ; rom bios segment??
        jb      chknef                  ; b = not likely
        mov     ax,0
        mov     bx,0                    ; fake null vector
chknef: or      bx,ax                   ; is vector present?
        pop     es
        pop     bx
        jz      chknet0                 ; z = no, no NetBios network
        or      nettype,netbios         ; say have NetBios network
                                ; AT&T STARLAN board check (0ddh=magic #)
        mov     ah,0                    ; vendor installation check on int 2ah
        mov     al,0                    ; do error retry
        int     2ah                     ; session level interrupt
        cmp     ah,0ddh                 ; 0ddh = magic number, success?
        jne     chknet1                 ; ne = no
        or      nettype,starlan         ; say using STARLAN, have int 2ah
        push    bx
        push    es                      ; Test for vector
        mov     ah,35h                  ; DOS get interrupt vector
        mov     al,5bh                  ; 5bh = STARLAN netbios ext'd vector
        int     dos                     ; returns vector in es:bx
        mov     ax,es
        or      bx,ax                   ; is vector present?
        pop     es
        pop     bx
        jz      chknet1                 ; z = no
        mov     nsbrk,1                 ; network BREAK supported
        jmp     chknet1

chknet0:mov     pcnet,0                 ; no network yet
        push    ax
        push    dx
        mov     ah,prstr
        mov     dx,offset nonetmsg      ; say network is not available
        int     dos
        pop     dx
        pop     ax
        stc                             ; set carry for failure
        ret                             ; and exit now

                                        ; net ready to operate
chknet1:cmp     temp,0                  ; byte count of new name, from COMS
        jne     chkne1e                 ; ne = new name given
        jmp     chknet2                 ; nothing, so leave names intact
chkne1e:cmp     pcnet,2                 ; is session active now?
        jb      chkne1d                 ; b = no
        call    nethangup               ; hangup net to clear old connection
chkne1d:                                ; start fresh connection
        push    si
        push    di
        push    es
        push    ds
        pop     es                      ; make es:di point to datas segment
        cld
        mov     cx,8                    ; 16 bytes for a node name
        mov     ax,'  '                 ; first, fill with spaces
        mov     di,offset xmt.scb_rname ; remote name field, clear it
        rep     stosw
        test    nettype,starlan         ; STARLAN?
        jz      chkne1b                 ; z = no
                                        ; begin STARLAN section
        mov     xmt.scb_vrname,0        ; STARLAN var length name ptr
        mov     xmt.scb_vrname+2,0      ; segement of name
        mov     xmt.scb_vrlen,0         ; and its length
        mov     cx,temp                 ; count of characters in new name
        cmp     cx,16                   ; > 16 chars in remote node name?
        ja      chkne1a                 ; a = yes, too long for Netbios
        mov     al,'/'                  ; scan for slashes in name
        mov     di,offset nambuf        ; source of text
        push    es                      ; save es around scan
        push    ds
        pop     es                      ; point es at datas segment
        cld
        repne   scasb                   ; look for the slash
        pop     es
        jne     chkne1b         ; ne = none, do regular Netbios name storage
chkne1a:                                ; STARLAN ISN long remote name support
        mov     xmt.scb_vrname,offset nambuf    ; STARLAN var length name ptr
        mov     xmt.scb_vrname+2,datas          ; segment of remote name
        mov     cx,temp                 ; get name length again (in cl)
        mov     xmt.scb_vrlen,cl        ; indicate its length
        jmp     chkne1c                 ; copy blanks in remote name field
                                        ; end STARLAN section

chkne1b:mov     cx,temp                 ; Regular Netbios form, name length
        mov     si,offset nambuf        ; source of text
        mov     di,offset xmt.scb_rname ; destination is remote name
        rep     movsb                   ; copy text to transmitter's scb
        mov     cx,16
chkne1c:mov     cx,8                    ; 8 words
        mov     si,offset xmt.scb_rname ; from here
        mov     di,offset rcv.scb_rname ; to receiver's scb also
        rep     movsw
        pop     es
        pop     di
        pop     si

chknet2:cmp     pcnet,0                 ; started net?
        je      chknet2c                ; e = no
        ret                             ; else quit here
chknet2c:
        mov     ah,prstr
        mov     dx,offset netmsg1       ; say checking node name
        int     dos
        push    word ptr xmt.scb_rname  ; save first two bytes (user spec)
        mov     byte ptr xmt.scb_rname,'*'      ; call to local name
        push    bx
        mov     xmt.scb_cmd,naustat     ; get Network Adapter Unit status
        mov     bx,offset xmt
        call    session
        pop     bx
        pop     word ptr xmt.scb_rname  ; restore remote name first two bytes
chknet2a:
        push    es                      ; save registers
        push    di
        push    si
        push    cx
        push    ds
        pop     es                      ; set es:di to datas segment
        mov     si,offset xmtbuf+60 ; where local name is returned (1st entry)
        cmp     word ptr xmtbuf+58,0    ; is local name empty?
        jne     chknet2b                ; ne = no, use name from table
        mov     si,offset deflname      ; else use default local name
chknet2b:
        mov     di,offset xmt.scb_lname ; where to put it in scb
        mov     cx,14                   ; 16 bytes minus extension of '.K'
        cld                             ; append extension of '.K' to loc name
chknet3:cmp     byte ptr[si],' ' ; find first space (end of regular node name)
        jbe     chknet4                 ; be = found one (or control code)
        movsb                           ; copy local name to scb
        loop    chknet3                 ; continue though local name
chknet4:cmp     word ptr [di-2],'K.'    ; is extension '.K' present already?
        je      chknet4a;;;5                    ; e = yes, nothing to add
        cmp     word ptr [di-2],'k.'    ; check lower case too
        je      chknet4a;;;5                    ; e = yes, nothing to add
        mov     word ptr [di],'K.'      ; append our extension of '.K'
        add     di,2                    ; step over our new extension
        sub     cx,2
                                        ; complete field with spaces
        add     cx,2                    ; 15th and 16th chars
chknet4a:jcxz   chknet5                 ; z = nothing to add
        mov     al,' '                  ; space as padding
        rep     stosb
chknet5:mov     si,offset xmt.scb_lname
        mov     di,offset rcv.scb_lname ; put in receiver scb too
        mov     cx,8
        rep     movsw
        mov     cx,8
        mov     si,offset xmt.scb_lname
        mov     di,offset lsn.scb_lname ; in Listen scb also
        rep     movsw
        pop     cx
        pop     si
        pop     di
        pop     es
chknet6:
        push    bx                      ; Put our new local name in NAU
        mov     xmt.scb_cmd,nadd        ; ADD NAME, wait
        mov     bx,offset xmt
        call    session
        pop     bx
        mov     al,xmt.scb_err          ; get error code
        cmp     al,0                    ; success?
        je      chknet7                 ; e = yes
        cmp     al,0dh                  ; duplicate name in local table?
        je      chknet6a                ; e = yes
        cmp     al,16h                  ; name used elsewhere?
        je      chknet6a                ; e = yes
        cmp     al,19h                  ; name conflict?
        je      chknet6a                ; e = yes
        push    ax
        mov     ah,prstr                ; another kind of error
        mov     dx,offset chkmsg1       ; say can't construct local name
        int     dos
        pop     ax
        call    hexout                  ; display it (in al)
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     pcnet,0                 ; say no connection today
        stc                             ; set carry for failure
        ret
chknet6a:
        mov     ah,prstr                ; ask for another name
        mov     dx,offset chkmsg2       ; prompt message
        int     dos
        mov     ah,conout               ; show name itself
        push    cx
        mov     cx,16                   ; 16 bytes in name field
        mov     si,offset xmt.scb_lname
chknet6c:
        lodsb                           ; get name char into al
        mov     dl,al
        int     dos
        mov     byte ptr[si-1],' '      ; clear old name as we go
        loop    chknet6c
        pop     cx
        mov     ah,prstr
        mov     dx,offset chkmsg3       ; rest of prompt
        int     dos

        mov     ah,0ah                  ; read buffered line from stdin
        mov     dx,offset xmtbuf+58     ; where to put text (xmtbuf+60=text)
        mov     xmtbuf+58,15            ; buf capacity, including cr at end
        mov     xmtbuf+59,0             ; say text in buffer = none
        int     dos
        jc      chknet6b                ; c = error
        cmp     xmtbuf+59,0             ; any bytes read?
        je      chknet6b                ; e = no, exit failure
        mov     ah,prstr                ; say rechecking name
        mov     dx,offset netmsg1
        int     dos
        jmp     chknet2a                ; go reinterpret name
chknet6b:
        stc                             ; set carry for failure
        ret

chknet7:mov     pcnet,1                 ; network is present (but not active)
        mov     al,xmt.scb_num          ; name number
        mov     rcv.scb_num,al
        mov     lsn.scb_num,al
        mov     lclexit,offset netclose ; address to close network
        push    ax
        push    cx
        push    dx
        mov     dx,offset netmsg2        ; say net going
        mov     ah,prstr
        int     dos
        mov     si,offset rcv.scb_lname ; display our local name
        mov     ah,conout
        mov     cx,16
        cld
chknet9:lodsb                           ; byte from si to al
        mov     dl,al
        int     dos                     ; display it
        loop    chknet9
        mov     ah,prstr
        mov     dx,offset crlf          ; add cr/lf
        int     dos
        pop     dx
        pop     cx
        pop     ax
        clc                             ; carry clear for success
        ret
chknet  endp

;                                       ; [ohl] ++++
; Verifies existance of interrupt 6Bh support, verifies vendor specific
; support for BREAK and other features, sets network type bit in nettype,
; sets BREAK support in nsbrk and sets network status byte pcnet to 0
; (no net) or to 1 (net ready). This is the first procedure called to
; init Ungermann-Bass NETCI terminal port network usage.
chkub  proc    near
        push    bx
        push    es                      ; Test for vector
        mov     ah,35h                  ; DOS get interrupt vector
        mov     al,6bh                  ; 6bh = Net/One command interpreter
                                        ;  interface, with break support
        int     dos                     ; returns vector in es:bx
        mov     ax,es                   ; is vector in rom bios??? [jrd]
        cmp     ax,0f000h               ; rom bios starts here
        jb      chkub2                  ; b = not likely
        mov     ax,0                    ; yes, say no network
        mov     es,ax                   ; fake a null vector
        mov     bx,ax
chkub2: mov     ax,es
        or      bx,ax                   ; is vector present?
        jz      chkub0                  ; z = no
        mov     al,0ffh                 ; test value (anything non-zero)
        mov     ah,2                    ; function code for testing net board
        int     netci
        or      al,al                   ; al = 0 means board is ok
        jnz     chkub0                  ; nz = not ok
        pop     es
        pop     bx
        mov     nsbrk,1                 ; network BREAK supported
        or      nettype,netone          ; say have Net/One
        clc                             ; return success
        ret

chkub0: pop     es                      ; clean stack from above
        pop     bx
        push    ax
        push    dx
        mov     ah,prstr
        mov     dx,offset nonetmsg      ; say network is not available
        int     dos
        pop     dx
        pop     ax
        stc                             ; set carry for failure
        ret                             ; and exit now
chkub  endp                             ; [ohl] ----

hexout  proc    near                    ; display byte in al as hex value
        push    ax                      ; all regs preserved
        push    cx
        push    dx
        mov     cx,2                    ; two nibbles
hexout1:push    cx                      ; save counter
        mov     cl,4                    ; high nibble
        ror     al,cl                   ; put in low order field
        mov     dl,al
        xchg    ch,al                   ; save al byte in ch
        and     dl,0fh                  ; four bits
        cmp     dl,9                    ; too big?
        jbe     hexout2                 ; be = no
        add     dl,'A'-'9'-1            ; bump up to A-F
hexout2:add     dl,'0'
        mov     ah,conout
        int     dos
        xchg    ch,al                   ; recover data byte
        pop     cx
        loop    hexout1                 ; do second nibble
        mov     dl,'H'                  ; add a final hex ident
        int     dos
        pop     ax
        pop     cx
        pop     dx
        ret
hexout  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]
; 22 June 1986 Don't send null char if not using flow control. [jrd]

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]
; 22 June 1986 Don't send null char if not using flow control. [jrd]
IHOSTR  PROC    NEAR
        push    ax              ; save regs
        push    cx
        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     cx
        pop     ax
        ret
IHOSTR  ENDP

; Send a break out the current serial port.  Returns normally.
; Do both regular and long Break. 6 March 1987 [jrd]

SENDBR  PROC    NEAR
        push    cx              ; Regular Break entry point
        mov     cx,275          ; 275 milliseconds in regular Break
        call    sendbw          ; call worker routine to do it
        pop     cx
        clc                     ; don't exit Connect mode
        ret
SENDBL: push    cx              ; Long Break entry point
        mov     cx,1800         ; 1.8 second long break
        call    sendbw          ; call worker routine to do it
        pop     cx
        clc                     ; don't exit Connect mode
        ret
                                ; worker - send Break for cx millisec
sendbw: test    clone,0ffh      ; running on a semi-clone?
        jnz     sendbw2         ; nz = yes, can't do this via Bios
        push    ax
        push    dx
        mov     dx,brkadr       ; Port address
        in      al,dx           ; Get current setting
        push    ax              ; save setting on the stack
        or      al,brkval       ; Set send-break bit(s)
        out     dx,al           ; Start the break
        mov     ax,cx           ; # of ms to wait
        call    pcwait          ; hold break for desired interval
        pop     ax              ; restore Line Control Register
        out     dx,al           ; Stop the break
        pop     dx
        pop     ax
        ret
sendbw2:
        cmp     clone, 'U'      ; is it an UB NETCI port?                [ohl]
        je      sendbw6         ; e = yes                                [ohl]
        cmp     clone,'N'       ; is this a NetBios network port?
        jne     sendbw4         ; ne = no
        cmp     nsbrk,0         ; is network able to send a break?
        je      sendbw4         ; e = no
        test    nettype,starlan ; STARLAN: network break supported?
        jz      sendbw4         ; z = no
        push    bx
        push    es              ; save es around call
        push    ds
        pop     es              ; make es:bx point to scb in datas segment
        mov     bx,offset can   ; use Cancel control block
        mov     can.scb_cmd,netbrk ; send net Break command
        int     5bh             ; use network Break interrupt
        pop     es              ; saved registers
        pop     bx
sendbw4:ret
sendbw6:                        ; UB port send break             [ohl] +++
        push    ax
        push    cx
        mov     ax, ncicont + 0 ; call control, use 0 for network port num [ohl]
        mov     cl, ncibrk      ; request break                          [ohl]
        int     netci           ; Net/One command interface int. (6Bh)   [ohl]
        pop     cx
        pop     ax
        ret                     ;  [ohl] ---
SENDBR  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.
;
; Revised slightly by Joe R. Doupnik 22 Dec 1985 to prevent interrupts
; being enabled until we're done, to stop interrupts from occurring when
; TX holding buffer becomes empty (a useless interrupt for us), and to
; shorten the time between enabling interrupts and our exit.
; Returns carry clear if success, else carry set.
SERINI  PROC    NEAR
        call    pcwtst                  ; recalibrate pcwait loop timer
        cmp     portin,0                ; Did we initialize port already?
        je      serin0                  ; e = no, not yet
        jmp     serin4                  ; Yes, update flow and leave
serin0:;;;;     call    chkport                 ; see if have real 8250 UART
;;;;    jnc     serin2                 ; nc = the real thing (else clone != 0)
        cmp     clone,0                 ; non-clone
        je      serin2                  ; e = yes, real thing
        jmp     serin3                  ; else use Bios. Finish initialization

serin2: cmp     clone,'N'               ; Network port?
        jne     serin2a                 ; ne = no
        jmp     serin3                  ; yes
serin2a:push    bx
        push    es
        in      al,21H                  ; Interrupt controller
        mov     savirq,al               ; save state here for restoration
        or      al,modem.mddis          ; Inhibit IRQ 3 or IRQ 4
        out     21H,al
        mov     al,byte ptr modem.mdintv ; desired interrupt vector
        mov     ah,35H                  ; Int 21H, function 35H = Get Vector
        int     dos                     ; get vector into es:bx
        mov     word ptr savsci,bx    ; save address offset of original vector
        mov     word ptr savsci+2,es    ;  and its segment
        mov     al,byte ptr modem.mdintv ; interrupt number for IRQ 4 or IRQ 3
        mov     dx,offset serint        ; offset of our interrupt routine
        push    ds                      ; save ds around next DOS call
        mov     bx,cs                   ; compose full address of our routine
        mov     ds,bx                   ; segment is the code segment
        mov     ah,25H                  ; set interrupt address from ds:dx
        int     dos
        pop     ds
        mov     al,rs232                ; interrupt number for Bios serial port
        mov     ah,35H                  ; get vector into es:bx
        int     dos
        mov     word ptr sav232,bx      ; save offset
        mov     word ptr sav232+2,es    ; save segment
        mov     dx,offset serdum        ; offset of our interrupt routine
        push    ds                      ; save ds around next DOS call
        mov     bx,cs                   ; compose full address of our routine
        mov     ds,bx                   ; segment is the code segment
        mov     ah,25H                  ; set interrupt address from ds:dx
        int     dos
        pop     ds
        pop     es
        pop     bx
        mov     portin,1                ; Remember port has been initialized
        cli                             ; Disable interrupts
        cld                             ; Do increments in string operations
        mov     ax,modem.mdstat
        mov     mst,ax                  ; Use this address for status
        mov     ax,modem.mddat
        mov     mdat,ax                 ; Use this address for data.
        mov     al,modem.mdmeoi
        mov     mdeoi,al                ; Use to signify end-of-interrupt.
        in      al,21H                  ; Set up 8259 interrupt controller
        and     al,modem.mden   ; Enable INT3 or INT4. (bit=0 means enable)
        out     21H,al                  ; rewrite interrupt mask byte
        mov     dx,modem.mdcom  ; Set up the serial card Line Control Reg.
        in      al,dx                   ; get present settings
        mov     savlcr,al               ; save them for restoration
        mov     al,3                    ; 8 data bits. DLAB = 0
        out     dx,al
        mov     dx,modem.mddat     ; data and command port, read and flush any
        in      al,dx                   ; char in UART's receive buffer
        inc     dx                ; increment to interrupt enable register 3f9h
        mov     al,1                    ; Set up interrupt enable register
        out     dx,al                   ;  for Data Available only
        add     dx,3                ; increment to modem control register 3fch
        mov     al,0bh            ; assert DTR, RTS, not OUT1, and OUT2
        out     dx,al             ; OUT2 high turns on interrupt driver chip
        sti                       ; Allow interrupts (AFTER next instr)
        jmp     short serin4            ; finish up

serin3: cmp     flags.comflg,'N'        ; Network?
        jne     serin4                  ; ne = no
        call    setnet                  ; setup network session and pcnet flag
        jnc     serin4                  ; nc = success
        ret                             ; fail, carry set, leave portin at 0

serin4: push    bx
        mov     bx,portval              ; get port data structure
        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
        mov     xofrcv,off              ; clear xoff received flag
        pop     bx
        mov     portin,1                ; say initialized
        clc                             ; carry clear for success
        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.
; Moved push/pop es code to do quicker exit before interrupts enabled. [jrd]
; Returns normally.
; 22 June 1986 Leave OUT1 low to avoid resetting Hayes 1200B's. [jrd]
; 21 Feb 1987 Add support for Bios calls (Clone) [jrd]
; 17 May 1987 Redo for COM3/4 support [jrd]
SERRST  PROC    NEAR
        cmp     portin,0                ; Reset already?
        jne     srst3                   ; ne = no
        ret                             ; e = yes, just leave
srst3:  test    clone,0ffh              ; running on a non-compatible UART?
        jz      srst4                   ; z = no, real UART
        jmp     srst1                   ; nz = yes (Bios or Net)
srst4:  push    word ptr savsci         ; save original interrupt owner
        push    word ptr savsci+2       ; offset and segment
        mov     word ptr savsci,offset nulint ; redirect to our null routine
        mov     ax,cs                   ; segment of null routine is code
        mov     word ptr savsci+2,ax
        mov     cx,0                    ; loop counter
srst2:  mov     dx,modem.mdstat         ; status register
        in      al,dx
        jmp     $+2                     ; chip access delay
        test    al,40h                  ; both xmtr output registers empty?
        loopz   srst2                   ; z = no, wait for them a while
        mov     dx,modem.mddat          ; modem base address 3f8h
        add     dx,1                    ; point at int enable reg 3f9h
        mov     al,0
        out     dx,al                   ; disable interrupts from this source
        jmp     $+2                     ; let stray interrupts occur now
        jmp     $+2
        add     dx,2                    ; point at Line Control Register 3fbh
        mov     al,savlcr               ; saved bit pattern
        and     al,not 80h              ; force DLAB bit to 0
        out     dx,al                   ; restore line control state
                ; clear modem's delta status bits and reassert DTR etc
        inc     dx              ; increment to modem control register 3fch
        mov     al,03h          ; reassert DTR,RTS,but not OUT1 and not OUT2
        out     dx,al           ;  OUT2 low to turn off interrupt drivers
        jmp     $+2             ; pause, in case stray interrupt is generated
        jmp     $+2             ; which is more than likely, hence nulint.
        add     dx,2                    ; modem status register 3feh
        in      al,dx                   ; clear status register by reading it
        jmp     $+2
        mov     mdmhand,al              ; save here for Show Modem
        cli                             ; Disable interrupts
        in      al,21H                  ; Interrupt controller
        or      al,modem.mddis          ; Inhibit IRQ 3 or IRQ 4
        pop     word ptr savsci+2       ; recover original int owner's addr
        pop     word ptr savsci
        sti                             ; replace original IRQ intrpt vector
        push    bx
        mov     al,byte ptr modem.mdintv ; vector number to do
        mov     dx,word ptr savsci      ; offset part
        push    ds
        mov     bx,word ptr savsci+2    ; segment part
        mov     ds,bx                   ; ds:dx has interrupt vector
        mov     ah,25H                  ; set interrupt vector
        int     dos                     ; replaced
        pop     ds
        mov     al,rs232        ; Bios serial port interrupt vector to restore
        mov     dx,word ptr sav232      ; offset part
        push    ds
        mov     bx,word ptr sav232+2    ; segment part
        mov     ds,bx
        mov     ah,25h                  ; set interrupt vector
        int     dos
        pop     ds
        pop     bx
        cli
        mov     ah,savirq               ; saved Interrupt state
        and     ah,modem.mddis          ; pick out our IRQ bit
        in      al,21h                  ; get current intrpt controller state
        jmp     $+2
        xor     al,modem.mddis          ; remove our IRQ bit
        or      al,ah                   ; set previous state
        out     21h,al                  ; reset IRQ 3 or 4 to original state
        sti
                                        ; non-UART processes
srst1:  cmp     pcnet,0                 ; a network active?
        je      srst9                   ; e = no
        cmp     flags.comflg,'N'        ; NetBios network?
        jne     srst9                   ; ne = no
        cmp     rposted,0               ; receive outstanding?
        je      srst9                   ; e = no
        mov     can.scb_baddr,offset rcv ; cancel receives
        push    bx
        mov     bx,offset can
        call    session
        pop     bx
        mov     ax,1
        call    pcwait                  ; wait one millisec
        cmp     rcv.scb_err,0           ; success?
        jne     srst9                   ; ne = no, leave interlock set
        mov     rposted,0               ; say no receives posted
srst9:  mov     portin,0                ; Reset flag
        ret                             ; All done
SERRST  ENDP

; Null interrupt routine, to handle strays
nulint  proc    near
        push ax
        push dx
        mov al,20h                      ; general EOI
        mov dx,intcon1                  ; to 8259 interrupt controller
        out dx,al                       ; clear controller chip
        pop dx
        pop ax
        iret
nulint  endp

; Dummy Interrupt 14H to defeat DOS interference with serial port when CTTY
; and Kermit use the port simultaneously. If ports differ then chain DOS to
; original Int 14H Bios code. Else return dummy status=ok reports and
; Backspace for Read, ignore char for Write.
; Entered with AH = function request, AL = char to be sent, DX = com port num
; CS is our code segment, DS is DOS's, SS is ours or DOS's, interrupts off.
; 25 June 1987 [jrd]
SERDUM  PROC    FAR
        push    ds                      ; preserve all registers
        push    ax
        mov     ax,seg datas            ; get our data segment
        mov     ds,ax
        mov     al,flags.comflg         ; get port id (COM1 = 1, COM2 = 2)
        and     al,7                    ; use lower three bits
        dec     al                      ; DOS counts COM1 as 0, etc
        cmp     dl,al           ; referencing same port as Kermit is using?
        pop     ax                      ; recover request parameters
        jne     serdu1                  ; ne = no, chain to Bios routine
        pop     ds
        cmp     ah,0                    ; initialization request?
        je      serdu3                  ; e = yes, return dummy status=ok rpt
        cmp     ah,1                    ; send char in al?
        jne     serdu2                  ; ne = no
        mov     ah,60h                  ; yes, set line status=ok in ah
        iret
serdu2: cmp     ah,2                    ; receive char (and wait for it)?
        jne     serdu3                  ; ne = no, return dummy report
        mov     al,bs                   ; yes, return ascii BS to DOS
        mov     ah,0                    ; ah = errors (none here)
        iret
serdu3: mov     ax,60b0h                ; dummy status report:xmtr empty, CD,
        iret                            ;  DSR, and CTS are on

serdu1: pop     tempdum                 ; save old ds
        push    word ptr sav232+2       ; push Bios int 14H handler segment
        push    word ptr sav232         ; push Bios int 14H handler offset
        push    tempdum                 ; recover old ds
        pop     ds
        ret                             ; do a ret far (chain to Bios)
SERDUM  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.
; 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.
; Upgraded to read cause of interrupt from interrupt ident reg (accepts only
;  data ready), chain to old interrupt if source is not our device.
; 9 Feb 1988 Add storage of interrupt cause in intkind. [jrd]

SERINT  PROC  FAR
        push    ax                      ; save registers
        push    dx                      ;
        push    ds
        mov     ax,seg datas
        mov     ds,ax                   ; address data segment
        mov     dx,mdat                 ; modem base address
        add     dx,2            ; increment to Interrupt Identification Reg
        in      al,dx                   ; get interrupt cause
        mov     intkind,al              ; save cause here
        test    al,1            ; interrupt available if this bit is zero
        jz      srintc                  ; z = interrupt is from our source
        pop     tempsci                 ;  save old ds
        pop     dx              ;  clean the stack and prepare for ret far
        pop     ax              ;  to old int handler (same as a jump there)
        push    word ptr savsci+2       ; old handler segment
        push    word ptr savsci         ; old handler offset
        push    tempsci                 ; recover old ds
        pop     ds
        ret                             ; do far return (chain to old handler)
srintc: mov     al,mdeoi                ; allow interrupt controller to run
        out     intcon1,al              ; Send End-of-Interrupt to 8259
        test    intkind,4               ; data ready?
        jnz     srint0a                 ; nz = yes, else ignore
srint0: sti                             ; else turn on interrupts
        jmp     retint                  ;  and exit now (common jump point)

srint0a:mov     dx,mst                  ; Asynch status port
        in      al,dx
        and     al,mdmover              ; select overrun bit
        mov     ah,al                   ; save it for later
        mov     dx,mdat
        in      al,dx                   ; read the received character into al
        mov     dh,al              ; dh = working copy. Check null, flow cntl
        and     dh,parmsk               ; strip parity temporarily, if any
;;;;;   jz      srint0                  ; if null ignore char
srint0b:cmp     flowoff,0               ; flow control active?
        je      srint2                  ; e = no
        cmp     dh,flowoff              ; acting on Xoff?
        jne     srint1                  ; ne = Nope, go on
        cmp     xofsnt,0                ; have we sent an outstanding XOFF?
        jne     srint0                  ; ne = yes, ignore (possible echo)
        mov     xofrcv,bufon            ; Set the flag saying XOFF received
        jmp     srint0                  ;  and exit
srint1: cmp     dh,flowon               ; acting on Xon?
        jne     srint2                  ; ne = no, go on
        mov     xofrcv,off              ; Clear the XOFF received flag
        jmp     srint0                  ;  and exit
srint2: push    bx                      ; save register
        or      ah,ah                   ; overrun?
        jz      srint2a                 ; z = no
        mov     ah,al                   ; yes, save present char
        mov     al,bell                 ; insert control-G for missing char
srint2a: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      srint3                  ; b = not past end
        mov     srcpnt,offset source    ; wrap buffer around
srint3: cmp     count,bufsiz            ; filled already?
        jae     srint4                  ; ae = yes
        inc     count                   ; no, add a char
srint4: or      ah,ah                   ; anything in overrun storage?
        jz      srint4a                 ; z = no
        mov     al,ah                   ; recover any recent char from overrun
        xor     ah,ah                   ; clear overrun storage
        jmp     srint2a                 ; yes, go store real second char
srint4a:pop     bx                      ; restore reg
        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 nul 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
        xor     cx,cx                   ; loop counter
srint5: mov     dx,modem.mdstat         ; Get port status
        in      al,dx
        test    al,20H                  ; Transmitter ready?
        jnz     srint6                  ; nz = yes
        jmp     $+2                     ; use time, prevent overdriving UART
        loop    srint5                  ; else wait loop, cx times
         jmp    srint7                  ; Timeout
srint6: mov     al,ah                   ; Now send out the flow control char
        mov     dx,modem.mddat
        jmp     $+2
        out     dx,al
        mov     xofsnt,bufon       ; Remember we sent an XOFF at buffer level
srint7: pop     cx                      ; restore reg
retint: pop     ds
        pop     dx
        pop     ax
        iret
SERINT  ENDP

DTRLOW  PROC    NEAR            ; Global proc to Hangup the Phone or Network
                                ; by making DTR and RTS low (phone). [jrd]
        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
         nop
        cmp     clone,'0'               ; running on a semi-clone?
        jb      dtrlow2                 ; b = no
        cmp     clone,'4'
        jb      dtrlow1                 ; b = yes, can't access modem lines
dtrlow2:call    serhng                  ; drop DTR and RTS
        cmp     taklev,0                ; in a Take file or macro?
        jne     dtrlow1                 ; ne = yes, no message
        mov     ah,prstr                ; give a nice message
        mov     dx,offset hngmsg
        int     dos
dtrlow1: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]
; 5 April 1987 Add 500 millisec wait with lines low before returning. [jrd]
; Calling this twice without intervening calls to serini should be harmless.
; If network then call nethangup procedure to hangup the session without
; losing local name information.
; Returns normally.

serhng  proc    near    ; clear modem's delta status bits and lower DTR & RTS
        cmp     clone,'U'               ; Ungermann Bass network?
        je      shng2                   ; e = yes
        cmp     clone,'N'               ; network?
        jne     shng1                   ; ne = no
shng2:  cmp     pcnet,0                 ; network operational?
        je      shng1                   ; e = no
        call    nethangup               ; break the session
        call    serrst                  ; reset port so can be opened again
        ret
shng1:  call    serrst                  ; reset port so serini can set DTR
        cli                             ; Disable interrupts
        push    ax
        push    dx
        mov     dx,modem.mddat          ; serial port base address
        add     dx,4                    ; increment to control register
        mov     al,08h                 ; reassert OUT2, un-assert DTR,RTS,OUT1
        out     dx,al
        jmp     $+2
        add     dx,2                    ; increment to modem status register
        in      al,dx                   ; Clear Status reg by reading it
shngx:  sti                             ; Enable interrupts
        mov     ax,500                  ; 500 millisec, for pcwait
        call    pcwait              ; keep lines low for at least 500 millisec
        pop     dx
        pop     ax
        clc
        ret
serhng  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