; File MSXIBM.ASM ; Kermit system dependent module for IBM-PC include mssdef.h ; Edit History ; Last edit 16 Jan 1990 ; 30 Sept 1989 Incorporate Opennet networking material from Fred Richter. ; 30 Sept 1989 Fred Richter (FGR) Intel Corp, Hauppauge NY ; polyof!inthap!fred or fred@polyof.poly.edu ; Added support for Intel Opennet Networking Virtual Terminal Services ; added "set port opennet xxx", also modified (slightly) ; the Netbios code (rpost) to always have the next Netbios receive posted ; as a small efficiency gain. Added error checking for received Netbios ; packets (there was none for async receives), also fix problem when the ; receives are canceled, where the data was being accepted anyway. ; 3 Sept 1989 Add DECnet CTERM and LAT support, plus Novell Int 6Bh. ; 9 July 1989 Add automatic usage of National Semiconductor 16550A UART in ; receiver fifo mode. Thanks to Dan Norstedt, danne@kicki.stacken.kth.se, ; and Campbell Scientific, Logan Utah for urging and supporting information. ; 17 June 1989 Add transparent interrupt driven support for IBM's Extended ; Bios via SET PORT BIOSn. ; 8 June 1989 Add Wyse-700 support from (Mikko Laanti (mjl). ; 6 March 1989 Add SET TERMINAL REPLAY FILESPEC command. ; 21 Jan 1989 Avoid initializing a port until the port is accessed for regular ; use, baud rate setting, or modem status. portin=-1 means unaccessed. ; 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 . [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 public dodel, ctlu, cmblnk, locate, prtchr, baudst, clearl public getbaud, beep, 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 public portval, port1, port2, port3, port4, bdtab, ubhold public dupflg 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 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: ; 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. data segment public 'data' extrn flags:byte, trans:byte, ttyact:byte, comand:byte extrn lclsusp:word, lclrest:word, lclexit:word, rxtable:byte extrn rdbuf:byte, taklev:byte, tekflg:byte, scbattr:byte extrn low_rgt:word, diskio:byte, crt_cols:byte, repflg:byte ; Modem information mdminfo struc mddat dw 0 ; data register mdiir dw 0 ; interrupt identification register mdstat dw 0 ; line status register mdcom dw 0 mden db 0 mddis db 0 mdmeoi db 0 mdintv dw 0 mdminfo ends modem mdminfo <> 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 dupflg db 0 ; full (0) or half (1) duplex on port 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 erms40 db cr,lf,'?Warning: Unrecognized Speed',cr,lf,'$' badbd db cr,lf,'Unimplemented speed$' badprt db cr,lf,'?Warning: no hardware for this serial port$' biosmsg db cr,lf,'?This port will be operated through the Bios as BIOS$' 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$' hngmsg db cr,lf,' The phone or network connection should have hung up' 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,'$' machnam db 'IBM-PC$' crlf db cr,lf,'$' delstr db BS,BS,' ',BS,BS,'$' ; Delete string clrlin db BS,BS,' ',cr,'$' ; Clear line (just the cr part) portin db -1 ; Has comm port been initialized, -1=not used 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 comptab db 19 ; 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 '3Com(BAPI)','C' ; 3Com BAPI interface mkeyw 'DECnet','D' ; DECnet-DOS LAT and CTERM mkeyw 'NetBios','N' ; Netbios mkeyw 'Novell(NASI)','W' ; Novell NetWare NASI/NACS mkeyw 'OpenNET','O' ; Intel OpenNET support (FGR) mkeyw 'UB-Net1','U' ; Ungermann Bass Net One mkeyw ' ',0 ; port is not present, for Status portmax equ 4 ; number of predefined ports port1 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> port2 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> port3 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> port4 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> rept portmax-4 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> endm portval dw port1 ; Default is to use port 1 bdtab db 18 ; Baud rate table mkeyw '45.5',0 mkeyw '50',1 mkeyw '75',2 mkeyw '110',3 mkeyw '134.5',4 mkeyw '150',5 mkeyw '300',6 mkeyw '600',7 mkeyw '1200',8 mkeyw '1800',9 mkeyw '2000',10 mkeyw '2400',11 mkeyw '4800',12 mkeyw '9600',13 mkeyw '19200',14 mkeyw '38400',15 mkeyw '57600',16 mkeyw '115200',17 ; this table is indexed by the baud rate definitions given in bdtab. ; Unsupported baud rates should contain 0FFh. 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. ; 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 miir dw 0 ; modem interrupt ident register 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) ; DEC-LAT requires 259 (256 here + 3 extra) 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 bapi equ 10h ; 3Com BAPI, nettype bapiint equ 14h ; 3Com BAPI, interrupt (Bios replacment) bapicon equ 0a0h ; 3Com BAPI, connect to port bapidisc equ 0a1h ; 3Com BAPI, disconnect bapiwrit equ 0a4h ; 3Com BAPI, write block bapiread equ 0a5h ; 3Com BAPI, read block bapibrk equ 0a6h ; 3Com BAPI, send short break bapistat equ 0a7h ; 3Com BAPI, read status (# chars to be read) bapihere equ 0afh ; 3Com BAPI, presence check bapieecm equ 0b0h ; 3Com BAPI, enable/disable ECM char bapiecm equ 0b1h ; 3Com BAPI, trap Enter Command Mode char ;; 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,0> ; 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 data ; and segment scb_length dw ? ; length of buffer data scb_rname db '* ' ; remote name, 16 chars space scb_lname db ' ' ; 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 ; DECnet material decneth dw 0 ; session handle decseg dw 0 ; segment of SCB memory block decint equ 69h ; CTERM interrupt decnet equ 4 ; for DECnet/CTERM nettype declat equ 8 ; for LAT nettype dpresent equ 100h ; Installation check for CTERM dsend equ 101h ; CTERM send byte dread equ 102h ; CTERM read byte dcstat equ 103h ; CTERM status ddstat equ 104h ; CTERM Decnet status dopen equ 105h ; CTERM open session dclose equ 106h ; CTERM close session dgetscb equ 10ah ; CTERM get SCB size latint equ 6ah ; LAT interrupt latsrv equ 0d500h ; LAT Get next service name latopen equ 0d0ffh ; LAT open latclose equ 0d000h ; LAT close latsend equ 1 ; LAT send byte latread equ 2 ; LAT read byte latstat equ 3 ; LAT status latbreak equ 0d100h ; LAT send a BREAK lcbst struc ; LAT control block structure service db 18 dup (0) ; service db 18 dup (0) ; node, for future use db 18 dup (0) ; port, for future use stopped dd 0 ; session stopped post routine addr overflow dd 0 ; service table overflow post addr xnotify dd 0 ; transmit post routine addr rnotify dd 0 ; receive post routine addr sstatus dw 0 ; session status dw 0 ; slot state, LAT driver use db 0 ; local credits, LAT driver use dw 0 ; vcb offset, LAT driver use dw 0 ; vcb segment, LAT driver use dw 0 ; back slot, LAT driver use dw 0 ; forward slot, LAT driver use db 0 ; rem slot id, LAT driver use db 0 ; loc slot id, LAT driver use db 0 ; slot byte count, LAT driver use db 0 ; remote credits, LAT driver use xdata db 255 dup (0) ; transmitted data slot slotqty db 2 ; number receive data slots slotused db 0 ; number occupied slots slotnr db 0 ; index of next rcv slot to use slotcur db 0 ; index of current rcv slot slotptr dw 0 ; ptr to first received char dw 0 ; ptrs to slots dw 0 ; reuse NetBios buffers xmtbufx, rcvbuf for slot1, slot2, resp. ; init these address in proc chkdec below. lcbst ends lat lcbst <> ; LAT data structure decmsg1 db cr,lf,'Cannot create DECnet session.$' decmsg3 db ' DECnet Error $' decmsg4 db cr,lf,'DECnet CTERM session started.$' decmsg5 db cr,lf,'DECnet LAT session started.$' ; end of DECnet 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+3 dup (0) ; external version of xmtbuf (dbl buffers) rcvbuf db nbuflen+3 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.K ' ; default local name, 16 bytes ivt1str db 'iVT1',0 ; FGR - OpenNet VT handshake string inettyp db 0 ; FGR - network type 'N' or 'O' 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 $' netmsg3 db cr,lf,' NetBios Local name: $' netmsg4 db ' Remote host: $' netmsg5 db cr,lf,' DECnet host: $' nonetmsg db cr,lf,'?The network is not available$' noname db cr,lf,'?No name exists for the remote host.$' nethlp db cr,lf,' node name of remote system,' db cr,lf,' or press ENTER to use current name,' db cr,lf,' or press ENTER for server mode (NetBios only).$' dnethlp db cr,lf,' node name of remote system,' db cr,lf,' or press ENTER to use current name,' db cr,lf,' or * to see a list of LAT service names.$' nethlp2 db cr,lf,' Optional LAT password, if using a LAT connection$' dnetsrv db cr,lf,' Available LAT service names:',cr,lf,'$' 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:',0 nettab db 2 mkeyw 'New',0 mkeyw 'Resume',1 biosbuf db 20 dup (0) ; IBM EBIOS receive buffer biosblen equ $-biosbuf ; its length data ends code segment public 'code' extrn comnd:near, prompt:near, dopar:near, lclyini:near, atsclr:near extrn strcpy:near, strlen:near,decout:near, prtasz:near, prtscr:near extrn kbsusp:near, kbrest:near ; in msuibm.asm assume cs:code, ds:data, es:nothing ; 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 mov lclsusp,offset suspend ; call this when suspending to DOS mov lclrest,offset restore ; call this when returning from DOS mov lclexit,offset finexit ; call this when exiting Kermit call lclyini ; let other modules initialize too... ret lclini endp ; Call these routines when suspending Kermit to go to DOS suspend proc near call kbsusp ; DEC LK250 keyboard, set back to DOS mode call ihosts ; suspend the host call serrst ret suspend endp ; Call these routines when returning to Kermit from DOS restore proc near call kbrest ; DEC LK250 keyboard, set back to DEC mode call serini ; reinit serial port call ihostr ; resume the host ret restore endp ; Call these routines when doing final exit of Kermit finexit proc near call netclose ; close network connections call kbsusp ; DEC LK250 keyboard, set back to DOS mode call serrst ; reset serial port ret finexit 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 data 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 xor ch,ch cld rep movsw ; copy PCjr values to IBM table jmp short modelx mov ah,0ch ; AT and PS/2 configuration call xor al,al 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,cmeol ; get a confirm call comnd jc shomd5 ; c = failure 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 cmp rcv.scb_lname,' ' ; have NetBios name yet? jbe shomd4 ; be = no, skip this part mov dx,offset netmsg3 ; local name int dos mov cx,16 mov di,offset rcv.scb_lname call prtscr mov ah,prstr mov dx,offset netmsg4 ; remote name int dos mov cx,16 mov di,offset rcv.scb_rname call prtscr shomd4: cmp lat.service,0 ; DECnet name available? je shomd5 ; e = no mov ah,prstr mov dx,offset netmsg5 int dos mov dx,offset lat.service ; network host name, asciiz call prtasz clc shomd5: ret shomodem endp ; Get modem status and set global byte mdmhand. Preserve all registers. getmodem proc near ; gets modem status upon request cmp portin,-1 ; done any port yet? jne getmod1 ; ne = yes mov bl,flags.comflg ; pass current port ident cmp bl,'4' ; above UART and Bios? ja getmod1 ; a = yes, do not start the port call comstrt ; do SET PORT command now jnc getmod1 ; nc = success ret ; failed to set port getmod1:xor al,al ; assume nothing is on cmp flags.comflg,'1' ; UART? jae getmod2 ; ae = no 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 getmod2:cmp flags.comflg,'4' ; above Bios? (networks) ja getmodx ; a = yes, no status mov ah,3 ; ask Bios for modem status into al push dx mov dl,flags.comflg ; get port id sub dl,'1' ; remove ascii bias (BIOS1 -> 0) int rs232 pop dx getmodx:xor ah,ah ; return status in al clc 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 cli mov srcpnt,offset source ; receive circular buffer mov count,0 ; receive circular buffer mov xmtcnt,0 ; network output buffer count sti clc cmp flags.comflg,'0' ; using Bios? jb clrbuf3 ; b = no cmp flags.comflg,'4' ; using Bios? ja clrbuf3 ; a = no call chkbios ; check for IBM EBIOS and set dl jc clrbuf3 ; c = not present mov ah,0f9h ; do a Regain Control operation int rs232 clrbuf1:mov ax,0fd02h ; see if buffer has chars already int rs232 jcxz clrbuf2 ; z = no, ok to reset buffering mov ah,0fch ; do receive /nowait to clear buff int rs232 jmp short clrbuf1 ; repeat til empty clrbuf2:mov bx,offset biosbuf ; receive buffer for EBIOS push ds pop es ; set es:bx to the buffer address xor cx,cx ; set cx to zero to terminate buffering mov ax,0ff02h ; reset buffered mode for receiving int rs232 clrbuf3:ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. ; Upgraded for Topview compatibility. CLEARL PROC NEAR push ax push bx push dx mov ah,3 ; Clear to end of line xor bh,bh 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 xor dx,dx ; 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 xor bh,bh ; 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 jmp clearl 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 clc 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 si 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 xor bh,bh ; 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 cmp dl,crt_cols ; beyond physical right border? jb putmo1 ; b = no putmo2: pop si 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 xor bh,bh ; 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: or al,al ; end of string? jnz puthl1 ; nz = 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 or al,al ; end of string? jz puthl4 ; z = 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 ; 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 jcxz pcwtst1 ; z = no millisec (super speed machines) use 1 div cx ; divide by observed milliseconds pcwtst1: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 NetBios. ; 'O' is for Intel Opennet Network (FGR) ; 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 xor bx,bx ; no extra help text mov ah,cmkey ; parse key word call comnd jnc coms0a ; nc = success ret ; failure coms0a: mov temp,bx cmp bl,'D' ; DECnet? je comstrt ; e = yes cmp bl,'N' ; NetBios network? je comstrt ; yes, get another item for networks cmp bl,'O' ; Opennet network? je comstrt ; yes, get another item for networks cmp bl,'U' ; Ungermann Bass net? je comstrt ; e = yes cmp bl,'W' ; Novell NASI net? je comstrt ; e = yes mov ah,cmeol ; non-network call comnd ; Get a confirm jnc coms0b ; nc = success ret ; failure coms0b: mov bx,temp COMSTRT:mov temp,bx ; port ident is in BL cmp bl,'N' ; NetBios network? jne comst2a ; ne = no jmp comsn ; yes, get another item for networks comst2a:cmp bl,'O' ; Opennet network? jne comst2 ; ne = no jmp comso ; yes, get another item for networks comst2: cmp bl,'U' ; Ungermann Bass net? jne comst3 ; ne = no jmp comsub comst3: cmp bl,'D' ; DECnet? jne comst4 ; ne = no jmp comsd ; do DECnet startup comst4: cmp bl,'W' ; Novell NASI? jne comst5 ; ne = no jmp comsub comst5: cmp bl,'C' ; 3Com BAPI? jne comst5a ; ne = no jmp comsbapi comst5a:cmp flags.comflg,'N' ; have been running on NetBios? je coms1c ; eq = yes cmp flags.comflg,'O' ; have been running on Opennet? jne coms2 ; ne = no ; turn off sources of net interrupts coms1c: 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? mov rposted,0 ; clear interlock flag no matter what jne coms2 ; ne = no mov can.scb_baddr,offset rcv ; cancel receive call session coms2: call serrst ; close current comms port mov al,byte ptr temp ; get COMx (1-4) mov flags.comflg,al ; remember port ident cmp al,'1' ; Bios? jb coms2a ; b = no, hardware sub al,'0' ; remove ascii bias for portinfo coms2a: dec al xor ah,ah ; count ports from 0 push bx ; set UART port addresses 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 flags.comflg,'1' ; Bios path? jb coms4 ; b = no, check hardware clc ret coms4: cmp portin,-1 ; serial port touched yet? jne coms4a ; ne = yes, else avoid init looping mov portin,0 ; say serial port is being touched coms4a: push bx ; save register push es mov ax,40h ; look at RS232_base [bx] in Bios area 40:00h mov es,ax mov bx,temp ; get desired port xor bh,bh dec bl ; count com1 as bl = 0, etc 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 comsnh ; e = yes, no serial port hardware comsc: ; hardware tests mov modem.mddat,ax ; set base address (also data address) 03f8h add ax,2 mov modem.mdiir,ax ; interrupt identification reg 03fah inc ax ; 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 jc comsnu ; c = not a real 8250 class UART call chkint ; find IRQ for the port jc comsnu ; c = not found, an error condition ret ; success comsnh: mov ah,prstr ; no port address for hardware mov dx,offset badprt int dos ; no port address or no UART or no IRQ comsnu: mov ah,prstr ; tell user about Bios pathway mov dx,offset biosmsg int dos mov dl,byte ptr temp ; selected hardware port add dl,'0' ; map to Bios mov flags.comflg,dl mov ah,conout ; say port number int dos stc ; say error ret ; Opennet Network support (FGR) comso: mov inettyp,'O' ; remember OpenNet type network jmp comso2 ; do generic network code ; NetBios Network support comsn: mov inettyp,'N' ; remember Netbios type network comso2: mov ah,cmword ; get a word (remote node name) mov dx,offset rdbuf ; work buffer mov word ptr rdbuf,0 ; insert terminator mov bx,offset nethlp ; help message call comnd ; get the name, ignore errors xchg ah,al ; put byte count in al xor ah,ah ; clear junk mov temp,ax ; save number of chars entered mov ah,cmeol call comnd ; get a confirm jc comsn2 ; c = failure call serrst ; reset serial port cmp temp,0 ; any name given? je comsn1 ; e = no, use current name mov si,offset rdbuf mov di,offset nambuf ; copy to here call strcpy comsn1: call chknet ; start network usage cmp pcnet,0 ; is network alive (non-zero)? jne comsn4 ; ne = yes comsn2: stc ret ; failure comsn4: mov portval,offset portn ; set Network port structure address mov portn.portrdy,1 ; say the comms port is ready mov al,inettyp ; FGR - get saved network type mov flags.comflg,al ; Set the Netbios port flag call chkport ; set type of port support clc ; return success ret ; End NetBios ; Ungermann-Bass terminal port [ohl +] comsub: push bx ; save net type U or W mov ah,cmeol call comnd ; get a confirm jc comsub0 ; c = failure call serrst ; reset serial port call chkub ; check UB network presence pop bx ; recover net type U or W jnc comsub1 ; nc = present comsub0:ret ; return failure comsub1:push bx call netclose ; better close NetBios parts NOW! pop bx mov portval,offset portn ; set Network port data structure address mov portn.portrdy,1 ; say the comms port is ready mov flags.comflg,bl ; set the comm port flag mov pcnet,2 ; network is present and active clc ; return success ret ; End Ungermann Bass/ Novell comsd: ; DECnet mov ah,cmword ; get a word (remote node name) mov dx,offset rdbuf ; work buffer mov word ptr rdbuf,0 ; insert terminator mov bx,offset dnethlp ; help message call comnd ; get the name xchg ah,al ; put byte count in al xor ah,ah ; clear junk mov temp,ax ; save number of chars entered mov ah,cmword ; get optional LAT service name mov dx,offset rdbuf+80 ; work near end of this buffer mov word ptr rdbuf+80,0 ; insert terminator mov bx,offset nethlp2 ; help message mov comand.cmblen,17 ; length of buffer (password = 16 chr) call comnd ; get the name, ignore if absent mov ah,cmeol call comnd ; get a confirm jnc comsd3 ret ; did not get a confirm comsd3: cmp temp,0 ; any node name? je comsd8a ; e = no mov si,offset rdbuf ; the node name, make upper case add si,temp ; and add '::' if absent cmp byte ptr [si],':' ; ended on colon? jne comsd4 ; ne = no, append them cmp byte ptr [si-1],':' ; first colon present? je comsd5 ; e = yes dec si ; one colon, back up to it dec temp comsd4: mov word ptr [si+1],'::' mov byte ptr [si+3],0 ; terminator add temp,2 ; include new bytes comsd5: mov si,offset rdbuf ; match spelling of old and new nodes mov di,offset lat.service mov cx,temp sub cx,2 ; ignore trailing '::' here cld comsd6: lodsb ; si = new node name mov ah,[di] ; di = old node name inc di and ax,not 2020h ; move both to upper case xor ah,al ; compare bytes loope comsd6 ; repeat while same spelling je comsd8 ; e = same throughout mov al,flags.comflg ; save kind of port call nethangup ; different, clear current connection mov si,offset rdbuf mov di,offset lat.service ; copy new name for use by chkdec call strcpy comsd8: cmp pcnet,2 ; session active? jb comsd8a ; b = no test nettype,decnet+declat ; and of DEC kind? jz comsd8a call chknew ; session exists, Resume or start new? jc comsd8a ; c = resume cmp rdbuf,0 ; New session? jne comsd8a ; ne = no, resume current call nethangup ; hangup current session comsd8a:call serrst ; reset serial port (not used here) jmp chkdec ; make DECnet session with host comsbapi:mov ah,bapihere ; 3Com BAPI presence check xor al,al mov bx,0aaaah ; undocumented magic int bapiint cmp ax,0af01h ; signature jne comsbap1 ; ne = not present call serrst ; close current port mov ah,bapieecm ; disable Enter Command Mode char xor al,al int bapiint mov portval,offset portn ; set Network port data structure address mov portn.portrdy,1 ; say the comms port is ready mov nettype,bapi ; indentify network type mov flags.comflg,'C' ; set the comm port flag mov pcnet,2 ; network is present and active clc ; success ret comsbap1:mov ah,prstr mov dx,offset nonetmsg ; say no network int dos comsbap3:stc ; say failure ret ; end 3Com BAPI COMS ENDP ; SETUP DECNET port. Host name is in lat.service. Try LAT then try CTERM. ; Return carry clear if success, or carry set if failure. chkdec proc near cmp pcnet,2 ; net active now? jb chkde4 ; b = no test nettype,declat ; LAT support active? jz chkde9 ; z = not LAT cmp decneth,0 ; valid LAT handle? je chkde4 ; e = invalid handle mov portval,offset portn ; set Network port data structure address mov flags.comflg,'D' ; set the comm port flag mov portn.portrdy,1 ; say the comms port is ready clc ret ; return to active session chkde9: cmp decneth,0 ; valid CTERM handle? jle chkde4 ; le = none, start new session jmp chkde6 ; resume old session chkde4: cmp lat.service,0 ; node name present? jne chkde4a ; ne = yes mov ah,prstr mov dx,offset noname ; say host name is required int dos stc ret chkde4a:xor ax,ax ; status of service not available mov es,ax ; refer to segment 0 mov si,latint*4 ; LAT 6Ah interrupt location les si,es:[si] ; get LAT driver entry point mov ax,es cmp ax,0 ; undefined interrupt? je chkde4h ; e = yes cmp ax,0f000h ; points at ROM Bios? je chkde4h ; e = yes cmp word ptr es:[si-3],'AL' ; preceeding 3 bytes spell 'LAT'? jne chkde4h ; ne = no, so no LAT, try CTERM cmp byte ptr es:[si-1],'T' je chkde4i ; e = a match for LAT chkde4h:jmp chkde4e ; no LAT, try CTERM chkde4i:push ds pop es mov bx,offset lat ; LAT session control block mov ax,offset xmtbufx ; initialize receive slot pointers mov [bx].slotptr+2,ax add ax,4 mov [bx].slotptr,ax mov ax,offset rcvbuf ; use this as second buffer mov [bx].slotptr+4,ax cmp lat.service,'*' ; just show known LAT names? jne chkde4j ; ne = no call latlst ; show LAT names stc ; do not make a connection ret chkde4j:mov ax,latopen ; open a LAT session mov di,offset rdbuf+80 ; optional asciiz LAT password ptr cmp byte ptr [di],0 ; any name entered? je chkde4d ; e = no mov cx,16 ; 16 chars required, blank padded chkde4b:cmp byte ptr [di],0 ; found terminator? je chkde4c ; yes, proceed to pad with spaces inc di ; no, point to next character loop chkde4b ; and loop jmp short chkde4g ; in case 16 byte password chkde4c:mov byte ptr [di],' ' ; insert padding inc di ; point to next loop chkde4c ; and loop chkde4g:mov byte ptr [di],0 ; null terminator mov di,offset rdbuf+80 ; point es:di to password field push ds pop es and al,0fh ; open as AX = 0d0fh if with password chkde4d:mov dh,0ffh int latint or ah,ah ; status byte jnz chkde4e ; nz = failure, try CTERM mov dh,0ffh ; success mov decneth,dx ; store handle returned in dl or nettype,declat ; say LAT session is active mov pcnet,2 mov portval,offset portn ; set Network port data structure address mov flags.comflg,'D' ; set the comm port flag mov portn.portrdy,1 ; say the comms port is ready mov ah,prstr mov dx,offset decmsg5 ; show status int dos clc ; carry clear means status = success ret chkde4e:xor ax,ax ; CTERM check mov es,ax ; refer to segment 0 mov si,decint*4 ; CTERM 69h interrupt location les si,es:[si] ; get CTERM driver entry point mov ax,es cmp ax,0 ; undefined interrupt? je chkde4f ; e = yes cmp ax,0f000h ; points at ROM Bios? je chkde4f ; e = yes mov ax,dpresent ; CTERM installation call int decint cmp al,0ffh ; CTERM installed? je chkde5 ; e = yes chkde4f:mov ah,prstr mov dx,offset decmsg1 ; no CTERM yet int dos jmp chkde8 ; exit with no net chkde5: mov ax,decseg ; scb memory segment, if non-zero or ax,ax ; allocated already? jnz chkde5a ; nz = yes, segment is in ax mov ax,dgetscb ; get CTERM SCB size int decint add ax,15 ; round up byte count shr ax,1 ; bytes to paragraphs shr ax,1 shr ax,1 shr ax,1 mov bx,ax mov temp,ax ; save requested paragraphs mov ah,alloc int dos ; bx gets # paragraphs allocated jc chkde8 ; c = failure mov decseg,ax ; store address of memory block cmp temp,bx ; wanted vs allocated paragraphs jb chkde7 ; b = not enough chkde5a:mov bx,offset lat.service ; ds:bx = node name mov es,ax xor dx,dx ; es:dx = SCB address mov ax,dopen ; open session int decint cmp ax,0 ; > 0 means session handle, else error jle chkde7 ; le = error mov decneth,ax ; store handle mov ah,prstr mov dx,offset decmsg4 ; say CTERM connection is ready int dos chkde6: or nettype,decnet ; network type is DECnet mov pcnet,2 ; say net is present and active mov portval,offset portn ; set Network port structure address mov flags.comflg,'D' ; set the comm port flag mov portn.portrdy,1 ; say the comms port is ready clc ret ; CTERM startup failure chkde7: push ax ; save error number in ax mov ax,decseg ; allocated memory segment mov es,ax mov ah,freemem ; free allocated memory at es: int dos ; free the block mov decseg,0 ; clear remembered segment address mov ah,prstr mov dx,offset decmsg1 ; cannot create session int dos mov dx,offset decmsg3 ; DEC Error # int dos pop ax ; recover error number (negative) neg ax call decout ; error number chkde8: mov pcnet,0 ; no net stc ; status is error ret chkdec endp ; Display list of LAT service names. Presumes LAT presence checks have passed latlst proc near push es push bx mov ah,prstr mov dx,offset dnetsrv ; header int dos push ds pop es mov si,2 ; chars in line counter latlst1:mov bx,offset rdbuf+2 ; es:bx = temp buffer for a name mov word ptr [bx-2],' ' ; indent mov byte ptr [bx],0 ; and a null terminator mov ax,latsrv ; get next LAT service name mov dh,0ffh int latint or ah,ah ; check status jnz latlst2 ; nz = done (no more names) mov dx,offset rdbuf ; name ptr is in es:bx (our buffer) call prtasz ; show asciiz name call strlen ; get current length add si,cx ; count chars on this line cmp si,60 ; enough on line already? jbe latlst1 ; be = no mov ah,prstr ; break the screen line mov dx,offset crlf int dos mov si,2 ; reset line count jmp short latlst1 ; do it again latlst2:pop bx pop es ret latlst 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? ja chkin1 ; a = no jmp chkin2 ; be = yes, use standard IRQ's chkin1: 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) cli mov al,2 ; set xmtr holding reg empty interrupt out dx,al jmp $+2 out dx,al ; again, because first may be missed sti 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) cli mov al,2 ; set xmtr holding reg empty interrupt out dx,al jmp $+2 out dx,al ; again, because first may be missed sti 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 if 8250 present, ; else carry set and flags.comflg in ascii digits for system Bios or ; carry set 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 flags.comflg,4 ; non-UART port? ja chkporx ; a = yes cmp flags.comflg,0 ; undefined port? je chkporx ; e = yes push ax push dx mov dx,modem.mdiir ; UART Interrupt Ident reg (3FAh/2FAh) in al,dx ; read UART's IIR test al,30h ; are these bits set? jnz chkpor1 ; nz = yes, not an 8250/16450/16550A 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/16450/16550A pop dx pop ax clc ; clear carry (say 8250/etc) ret chkpor1:pop dx pop ax add flags.comflg,'0' ; set Bios usage flag (ascii digit) chkporx:stc ; set carry (say no 8250/etc) ret chkport endp ; Check for presence of IBM EBIOS and set dl to Bios port value ; Returns carry clear if EBIOS present, else carry set chkbios proc near xor dx,dx ; assume port 1, find current port mov dl,flags.comflg ; get port number (1..4) or dl,dl ; zero (no such port)? jz chkbios1 ; z = yes, don't access it and dl,7 ; use low three bits dec dl ; address ports as 0..3 for Bios mov ax,0f4ffh ; IBM EBIOS presence check int rs232 jc chkbios1 ; c = failure or ax,ax ; returns ax = 0 if present jnz chkbios1 ; nz = not present clc ret chkbios1:stc ; IBM EBIOS not present, dl = port ret chkbios 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 carry clear. BAUDST PROC NEAR mov dx,offset bdtab ; baud rate table, ascii xor bx,bx ; help is the table itself mov ah,cmkey ; get keyword call comnd jc baudst1 ; c = failure push bx ; save result mov ah,cmeol ; get confirmation call comnd pop bx jc baudst1 ; c = failure mov si,portval mov ax,[si].baud ; remember original value mov [si].baud,bx ; set the baud rate call dobaud ; use common code clc baudst1:ret BAUDST ENDP DOBAUD PROC NEAR cmp portin,-1 ; port used yet? jne dobd3 ; ne = yes, go get rate mov bl,flags.comflg ; pass current port ident cmp bl,4 ; upper limit on UARTs ja dobd4 ; a = not a UART, no baud rate call comstrt ; do SET PORT command now jnc dobd3 ; nc = success dobd4: stc ret ; failure dobd3: push ax ; save some regs push bx push cx push dx cmp flags.comflg,'4' ; UART or Bios? ja dobd1 ; a = no, networks call chkport ; check port for real 8250 UART 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 flags.comflg,'0' ; Bios? jb dobd0a ; b = no, UART mov bx,offset clbddat ; use Bios speed parameters dobd0a: add bx,ax mov ax,[bx] ; 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 flags.comflg,'0' ; running on a real uart? jb dobd2 ; b = the real thing xor dx,dx ; assume port 1, 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 xor ah,ah ; set serial port int rs232 ; Bios: set the parameters jmp dobd1 ; and exit dobd2: mov temp,ax ; UART, 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 cx pop bx pop ax clc 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. GETBAUD PROC NEAR push bx mov bx,portval mov [bx].baud,0ffh ; set baud rate to unknown cmp flags.comflg,4 ; UART? ja getb3 ; a = no, Bios or Networks cmp portin,-1 ; port unused? jne getbud ; ne = no, used, go get rate mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT command now jnc getbud ; nc = success getb3: pop bx ret ; failure getbud: push ax ; save some regs 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 high order byte mov ah,al ; save here dec dx in al,dx ; get low 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 xor cl,cl ; keep track of index getb0: cmp ax,[bx] je getb1 inc cl cmp cl,baudlen ; at the end of the list? jge getb2 ; ge = yes add bx,2 jmp getb0 getb1: xor ch,ch mov bx,portval mov [bx].baud,cx ; set baud rate getb2: pop dx ; restore regs pop cx pop ax pop bx clc ret GETBAUD ENDP ; Get Char from serial port buffer. ; returns carry set if no character available at port, otherwise ; returns carry clear with char in al, # of chars in buffer in dx. 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 repflg,0 ; REPLAY? je prtchn1 ; e = no jmp prtch30 ; yes, do replay file reading prtchn1:mov al,flags.comflg ; get kind of port cmp al,'0' ; Bios or network? jb prtch0 ; b = no, UART cmp al,'4' ; Bios? jbe prtch6 ; be = yes, else networks prtchn: cmp count,bufsiz-nbuflen ; room left for more network data? ja prtch1 ; a = no, read current buffer cmp al,'U' ; UB network? je prtchn2 cmp al,'C' ; 3Com BAPI interface? je prtchn2 ; e = yes cmp al,'W' ; Novell network? jne prtchn3 ; ne = no prtchn2:call ubrecv ; do a UB receive jmp short prtch0 prtchn3:cmp al,'D' ; DECnet? jne prtchn4 ; ne = no jmp decrcv ; DECnet receive char prtchn4:call receive ; start next NetBios receive (async) prtch0: cmp count,0 ; any characters available? jnz prtch1 ; nz = yes, get one prtch0a:xor dx,dx ; return count of zero stc ; say no data ret 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: ; Bios calls call chkbios ; set dl to port check IBM EBIOS jc prtch7 ; c = EBIOS not present mov ah,0fch ; IBM EBIOS receive /no wait int rs232 ; does line check then char ready chk test ah,8ch ; timeout, framing error, parity error? jnz prtch8 ; nz = error, no char jz prtch9a ; z = success, process char prtch7: mov ah,3 ; check port status, std Bios calls int rs232 ; Bios call test ah,mdminp ; data ready? jnz prtch9 ; nz = yes, get one prtch8: xor dx,dx ; return count of zero stc ; say no data ret prtch9: mov ah,2 ; receive a char into al int rs232 ; Bios call test ah,8ch ; timeout, framing error, parity error? jnz prtch8 ; nz = error, no char prtch9a: ; single char read final filter 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:xor dx,dx stc ; no data ret prtch14:clc ; return char in al ret prtch30:push bx ; REPLAY, read char from a file push cx xor dx,dx test xofsnt,usron ; user level xoff sent? jz prtch32 ; z = no pop cx ; suppress reading here pop bx clc ; return with no char ret prtch32:cmp tekflg,0 ; doing Tek plots? jne prtch32a ; ne = yes, do not insert pauses mov ax,100 mov bx,1 jmp $+2 ; flush lookahead buffer div bx ; burn some cpu cycles jmp $+2 ; because a 1 ms wait is too long div bx jmp $+2 div bx prtch32a:mov ah,readf2 mov bx,diskio.handle ; file handle mov cx,1 ; read one char mov dx,offset rdbuf ; to this buffer int dos jc prtch31 ; c = read failure cmp ax,cx ; read the byte? jne prtch31 ; ne = no pop cx pop bx mov al,rdbuf ; get the char into al mov dx,1 ; external char count clc ret ; return it prtch31:call beep mov ax,40 ; wait 40 millisec call pcwait call beep mov ah,coninq ; wait for a keystroke int dos or al,al ; null? jnz prtch33 ; nz = no mov ah,coninq int dos prtch33:pop cx mov bx,portval mov [bx].portrdy,0 ; say port is not ready pop bx xor dx,dx stc ; say no char ret PRTCHR ENDP ; DECnet receive routine DECRCV PROC NEAR mov dx,decneth ; DEC handle test nettype,declat ; LAT interface? jz decrcv1 ; z = not LAT test lat.sstatus,08h ; status: stop slot received? jnz decrcv4 ; nz = yes, no valid session mov ah,latread ; read char via LAT int latint test ah,80h ; char available? jz decrcv2 ; z = yes test lat.sstatus,0ch ; status: stop slot or circuit failure jnz decrcv4 ; nz = yes, no valid session stc ; return no char mov count,0 ret decrcv1:mov ax,dcstat ; CTERM status int decint test ah,0c0h ; no session, DECnet error? jnz decrcv4 ; nz = yes, stop here ; test ah,1 ; data available? ; jz decrcv3 ; z = no ; data available test fails under flow control, maybe a Cterm bug. jrd mov ax,dread ; read char via CTERM int decint test ah,80h ; char received? jnz decrcv3 ; nz = no decrcv2:jmp prtch9a ; use common completion code decrcv3:stc ; return no char mov count,0 ret decrcv4:call nethangup test flags.remflg,dserver ; server mode? jz decrcv5 ; z = no call serini ; reinitialize it for new session decrcv5:stc ; say failure to receive ret DECRCV ENDP ; NetBios Receive packet routine. If a new packet has been received unpack ; the data and request a new one with no-wait option. If a receive request is ; still outstanding just return with no new data. ; 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? je receiv4 ; e = yes (1), don't do another jb receiv1 ; b = no (0), do one now call receiv2 ; have new pkt, unpack, do new recv jnc receiv1 ; nc = success ret ; else return carry set receiv1:mov rposted,1 ; say posting a receive now mov rcv.scb_length,nbuflen ; 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 ret receiv2:mov al,rcv.scb_err ; returned status or al,al ; success? jz receiv5 ; z = yes, get the data cmp al,npending ; pending receive? je receiv4 ; e = yes cmp al,6 ; message incomplete? je receiv5 ; e = yes, get what we have anyway cmp al,0bh ; receive cancelled? je receiv4 ; e = yes cmp al,18h ; session ended abnormally? jbe receiv3 ; e = yes, b = other normal errors 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 ; Error return receiv3:mov pcnet,1 ; say session is broken mov rposted,0 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 ; shared by NetBios, Novell, Opennet, Ungerman Bass, 3ComBAPI receiv5:push bx ; new packet has been received push cx ; copy contents to circ buf source push dx push si mov dh,flowon mov dl,flowoff mov si,rcv.scb_baddr ; source of text (es:bx is scb ptr) mov bx,srcpnt ; address of destination buffer slot receiv6:mov cx,rcv.scb_length ; get remaining returned byte count jcxz receiv13 ; z = nothing there mov ax,offset source+bufsiz ; end of destination buffer+1 sub ax,bx ; space remaining at end of buffer jns receiv7 ; should never be negative neg ax ; but if so invert receiv7:cmp ax,cx ; buffer ending vs incoming byte count jge receiv8 ; ge = enough for this pass mov cx,ax ; limit this pass to end of the buffer receiv8:sub rcv.scb_length,cx ; deduct chars done in this pass add count,cx ; add them to the count cld ; inner loop "block" transfer receiv9:lodsb ; get byte from rcvbuf to al or dx,dx ; doing flow control? jz receiv11 ; 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 receiv10 ; ne = no cmp xofsnt,0 ; have we sent an XOFF? jne receiv12 ; ne = yes, ignore this XOFF char mov xofrcv,bufon ; set flag saying buffer XOFF received jmp short receiv12 ; and skip this character receiv10:cmp ah,dh ; acting on Xon? jne receiv11 ; ne = no, go on mov xofrcv,off ; clear the XOFF received flag jmp short receiv12 ; and skip this character receiv11:mov [bx],al ; store new char in buffer "source" inc bx receiv12:loop receiv9 ; bottom of inner loop ; update buffer pointer for wrapping cmp bx,offset source+bufsiz ; pointing beyond end of buffer? jb receiv6 ; b = no, do next pass mov bx,offset source ; wrap pointer, modulo bufsiz jmp short receiv6 ; do next pass receiv13:mov srcpnt,bx ; update pointer to next free slot cmp count,bufsiz ; count more that buffer size? jbe receiv14 ; be = no mov count,bufsiz ; limit to bufsiz (tells the truth) receiv14:mov rposted,0 ; clear interlock flag pop si pop dx pop cx pop bx clc ret RECEIVE ENDP ; NetBios Receive post processing interrupt routine. ; Sets rposted interlock flag RPOST PROC NEAR ; NetBios receive post interrupt routine push ds push ax mov ax,data ; reestablish data segment mov ds,ax mov rposted,2 ; set interlock flag to completed pop ax pop ds iret ; return from interrupt RPOST endp ; Ungermann-Bass NETCI port receive characters routine. Receive one or more ; characters. Calls the receiv5 routine to transfer character to main source ; circular buffer. Return carry clear if success. UBRECV PROC near push bx push cx push es mov ax, data mov es, ax ; es:bx will point to rcvbuf mov bx, offset rcvbuf mov cx, nbuflen ; buffer length ubrecv2:test nettype,bapi ; 3Com BAPI interface? jz ubrecv2a ; z = no, Int 6bh kind mov ah,bapiread xor dh,dh ; session 0 int bapiint jmp short ubrecv2b ubrecv2a:mov ax, nciread ; function 1 (receive) port 0 [ohl] int netci ; get characters [ohl] ubrecv2b:stc jcxz ubrec1 ; cx = z = nothing to do mov rcv.scb_length, cx ; prepare for rpost call [ohl] call receiv5 ; process buffer clc ubrec1: pop es pop cx pop bx ret UBRECV ENDP ; Put the char in AH to the serial port, assumimg the port is active. ; Returns carry clear if success, else carry set. ; 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:and xofsnt,not usron ; cancel user level xoff 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 push cx ; save reg mov ch,15 ; 15 sec timeout interval xor cl,cl ; convert to 4 millsec increments 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 cmp ttyact,0 ; in Connect mode? je outch1c ; e = no push ax ; save char around the call call beep ; let user know we are xoff-ed pop ax ; but are sending anyway 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 cmp repflg,0 ; doing REPLAY from a file? je outch3 ; e = no and al,7fh ; strip parity cmp al,'C'-40h ; Control-C? (to exit playback mode) je outch2a ; e = yes, return failure clc ; return success, send nothing ret outch2a:stc ; return failure to exit playback mode ret outch3: cmp flags.comflg,'0' ; Bios? jb outch3a ; b = no, UART cmp flags.comflg,'4' ; Bios? jbe outch3e ; be = yes jmp outch8 ; else try networks outch3e:jmp outch6 ; do Bios routine outch3a:push cx ; save registers push dx outch3b:cmp dupflg,0 ; full duplex? je outch3d ; e = yes mov dx,modem.mddat add dx,4 ; modem control reg 3fch in al,dx or al,2 ; set RTS jmp $+2 out dx,al add dx,2 ; modem status register 3feh jmp $+2 in al,dx test al,20h ; ignore CTS if DSR is not asserted jz outch3d ; z = DSR not asserted mov cx,8000 ; ~10 seconds worth of waiting on CTS outch3c:test al,10h ; is CTS asserted? jnz outch3d ; nz = yes push cx push dx mov ax,1 ; wait one millisec call pcwait pop dx pop cx loop outch3c ; test again call beep ; half duplex timeout, make non-fatal pop dx pop cx clc ret outch3d:xor cx,cx mov dx,modem.mdstat ; get port status in al,dx test al,20H ; Transmitter (THRE) ready? jnz outch4 ; nz = 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 cmp dupflg,0 ; full duplex? je outch4a ; e = yes cmp al,trans.seol ; End of Line char? jne outch4a ; ne = no xor cx,cx ; loop counter outch4b:mov dx,modem.mdstat ; modem line status reg in al,dx ; read transmitter shift reg empty bit jmp $+2 jmp $+2 jmp $+2 ; wait for char to be sent test al,40h ; is it empty? loopz outch4b ; z = no, not yet mov dx,modem.mddat add dx,4 ; modem control reg 3fch in al,dx and al,not 2 ; unassert RTS jmp $+2 out dx,al outch4a:pop dx ; exit success pop cx clc ret outch5: call beep pop dx ; exit failure pop cx stc ret ; finish up for Bios calls outch6: push cx ; find current port push dx xor dx,dx ; 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 ; bios send pop dx ; exit success pop cx shl ah,1 ; put status high bit into carry ret ; c set = failure, else success outch8: ; Network sending, buffered and single char cmp flags.comflg,'D' ; DECnet? je outch14 ; e = yes 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 flags.comflg,'U' ; check for UB port [ohl] je outch12 ; e = yes [ohl] cmp flags.comflg,'W' ; Novell network? je outch12 ; e = yes cmp flags.comflg,'C' ; 3Com BAPI je outch12 ; e = yes call send ; NetBios network send routine jc outch11 ; c = error outch10:clc ; good exit ret outch11:stc ; bad exit ret outch12:jmp ubsend ; UB/Novell network send outch14:mov dx,decneth ; DECnet, handle or dl,dl ; legal handle? jle outch14c ; le = invalid handle test nettype,declat ; LAT? jz outch14a ; z = no, use CTERM mov al,ah mov ah,latsend ; LAT send byte int latint jmp short outch14b ; common completion outch14a:push bx ; CTERM mov bl,ah ; char to be sent mov ax,dsend ; send byte in bl int decint pop bx outch14b:rcl ah,1 ; status 80h bit, did char get sent? jnc outch15 ; nc = success outch14c:call nethangup ; failure stc outch15:ret OUTCHR ENDP ; NetBios 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 these regs push cx push si push di push es push ds pop es ; set es to data segment mov si,offset xmtbufx ; external buffer mov di,offset xmtbuf ; copy for network packets mov cx,xmtcnt ; buffer length mov xmt.scb_length,cx ; tell buffer length shr cx,1 ; divide by two (words), set carry jnc send2 ; nc = even number of bytes movsb ; do single move send2: jcxz send2a ; z = nothing left 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 ; NetBios 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,data mov ds,ax mov sposted,0 ; clear send interlock pop ax pop ds iret SPOST ENDP ; Ungermann-Bass NETCI/Novell NASI port send packet routine. ; 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, data mov es, ax ubsend2:test nettype,bapi ; 3Com BAPI? jz ubsend2a ; z = no, Int 6bh kind mov ah,bapiwrit ; 3Com block write xor dh,dh ; session 0 int bapiint jmp short ubsend2b ubsend2a:mov ax, nciwrit ; write function, port 0 [ohl] int netci ubsend2b: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 clc ; success, need failure case too ret ubsend endp ; [ohl] --- ; Dispatch prebuilt NetBios 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 data seg mov ax,exnbios ; funct 4 execute netbios, for Int 2ah int netint ; use NetBios interrupt pop es ; saved registers ret ; exit with status in ax SESSION ENDP ; Make a NetBios 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 ; NetBios, 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 mov lposted,1 ; set listen interlock flag mov lsn.scb_cmd,nlisten+nowait ; do LISTEN command, no wait push bx ; save reg mov bx,offset lsn call session pop bx ret setne2: ; Non-server (Client) mode 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 push bx ; save reg 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 bx pop es ; restore regs jmp short setne3 ; finish up ; Regular Netbios Call setne2a:cmp flags.comflg,'O' ; Opennet network? (FGR) jne setne2b ; ne = no mov xmt.scb_rname+15,'v' ; fix name to use VT port under nameserver mov rcv.scb_rname+15,'v' setne2b:mov xmt.scb_cmd,ncall ; CALL, wait for answer push bx ; save reg mov bx,offset xmt ; setup scb pointer call session pop bx ; restore register setne3: ; 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 setne3b ; nz = bad connection jmp short setne4 ; good connection so far ; We try twice, to allow for R1, and R3 ; versions of the nameservers setne3b:cmp flags.comflg,'O' ; Opennet netnork? (FGR) jne setne3a ; ne = no mov xmt.scb_rname+15,' ' ; try generic port under nameserver mov rcv.scb_rname+15,' ' ; Regular Netbios Call mov xmt.scb_cmd,ncall ; CALL, wait for answer push bx ; save reg mov bx,offset xmt ; setup scb pointer call session pop bx ; restore register ; 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 jz setne4 ; z = 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 ; Here is the real difference between Opennet and generic Netbios. ; The Opennet Virtual Terminal Services exchange a small handshake at connect ; time. After that it is just normal Netbios data transfer between the host ; and Kermit. cmp flags.comflg,'O' ; Opennet netnork? (FGR) jne setne4o ; ne = no push si push di mov si,offset ivt1str ; protocol string "iVT1\0" mov di,offset xmtbufx ; buffer call strcpy ; copy asciiz string mov xmtcnt,5 ; length of asciiz string, for send pop di pop si call send ; send signon packet ; Note to Opennet purists: this just sends the handshake string to the host ; system without checking for an appropriate response. Basically, I am just ; very willing to talk to ANY VT server, and do the host response checking ; (if desired) in a Kermit script file (so its optional). setne4o: 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: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 chars saynod2:mov ah,prstr mov dx,offset crlf int dos pop si pop dx 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,data ; reestablish data 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 mov al,flags.comflg ; get type of port cmp al, 'U' ; Ungermann-Bass port? [ohl] je nethang2 ; e = yes [ohl] cmp al,'W' ; Novell? je nethang2 ; e = yes cmp al,'D' ; DECnet? je nethang3 ; e = yes 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 portn.portrdy,0 ; say the comms port is not ready mov pcnet,1 ; say network but no session call serrst ; reset the serial port for reiniting nethang1:clc ret ; UB network [ohl] +++ nethang2:call ubclose ; close connection if any [ohl] mov xmtcnt,0 clc ret ; [ohl] --- nethang3:mov dx,decneth ; DECnet handle test nettype,declat ; DECnet LAT active? jz nethang4 ; z = no mov ax,latclose int latint and nettype,not declat ; remove net type bit jmp short nethang5 nethang4:test nettype,decnet ; DEC CTERM active? jz nethang5 ; z = no mov ax,dclose ; CTERM close int decint and nettype,not decnet ; remove net type bit nethang5:xor dx,dx ; clear handle mov decneth,dx mov portn.portrdy,0 ; say the comms port is not ready mov pcnet,1 ; say network but no session call serrst ; reset the serial port for reiniting clc ret NETHANGUP ENDP ; Ungermann Bass. Do a disconnect from the current connection. ubclose proc near push ax push cx test nettype,netone ; UB network has been activated? jz ubclos3 ; z = no mov ax, ncistat ; get status [ohl] int netci or ch,ch ; check if we have a connection [ohl] jz ubclos2 ; z = no [ohl] mov ax, ncicont ; control function [ohl] mov cx, ncidis ; say disconnect [ohl] int netci ubclos1:call ubrecv ; read response from net cmdintpr[ohl] jnc ubclos1 ; continue till no chars [ohl] mov ax, ncistat ; get status again int netci or ch,ch ; check if we have a connection jnz ubclos3 ; nz = yes, had more than one ubclos2:and nettype,not netone ; remove network type mov pcnet,1 ; net but no connection mov portn.portrdy,0 ; say the comms port is not ready ubclos3:pop cx pop dx ret ubclos4:test nettype,bapi ; 3Com BAPI in use? jz ubclos3 ; z = no mov ah,bapieecm ; control Enter Command Mode char mov al,1 ; enable it int bapiint jmp short ubclos3 ubclose endp ; Ungermann Bass/Novell. Put current connection on Hold. Requires keyboard ; verb \kubhold to activate. Should return to Connect mode to see NASI. [jrd] ubhold proc near push ax push cx test nettype,netone ; UB/Novell network active? jz ubhold1 ; z = no mov ax,ncistat ; get link status int netci or ch,ch ; connection active? jz ubhold1 ; z = no mov ax,ncicont ; control command mov cl,ncihld ; place circuit on HOLD int netci jmp short ubhold2 ubhold1:test nettype,bapi ; 3Com BAPI? jz ubhold2 ; z = no mov ah,bapiecm ; do Enter Command Mode char int bapiint ubhold2:pop cx pop dx clc ret ubhold endp ; Called when Kermit exits. Name passed to mssker by initialization lclini ; 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 mov portn.portrdy,0 ; say comms port is not ready and nettype,not netbios ; remove network kind netclo1:clc 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 flags.comflg,'U' ; Ungermann Bass network? jb chknea ; b = no, (ae includes U and W) 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 call chknew ; Resume current session? jc chknex ; c = yes cmp rdbuf,0 ; New session? je chkneb ; e = yes clc chknex: 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 xor ax,ax ; trapped this vector to dummy iret xor bx,bx ; 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 xor ax,ax xor bx,bx ; 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 #) xor ah,ah ; vendor installation check on int 2ah xor al,al ; 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 dx mov ah,prstr mov dx,offset nonetmsg ; say network is not available int dos pop dx stc ; set carry for failure ret ; and exit now ; net ready to operate chknet1:mov portn.portrdy,1 ; say the comms port is ready 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 data 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 cld repne scasb ; look for the slash 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,data ; 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 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 data 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 ; e = yes, nothing to add cmp word ptr [di-2],'k.' ; check lower case too je chknet4a ; 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 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 or al,al ; success? jz chknet7 ; z = 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 push ax push cx 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 cx pop ax clc ; carry clear for success ret chknet endp ; Network session exists. Tell user and ask for new node or Resume. ; Returns rdbuf 0 if Resume response. chknew proc near mov dx,offset naskpmt ; prompt for New or Resume call prompt mov dx,offset nettab ; table of answers xor bx,bx ; help for the question mov ah,cmkey ; get answer keyword mov comand.cmcr,1 ; allow bare CR's call comnd mov comand.cmcr,0 ; dis-allow bare CR's jc chknew1 ; c = failure, means Resume mov rdbuf,bl ; save keyword action value here mov ah,cmeol ; get a confirm call comnd chknew1:ret ; carry may be set chknew 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 xor ax,ax ; 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 chkxo1: ret chkxon endp ; IHOSTS - Initialize the host by sending XOFF, or equivalent. ; Requires that the port be initialized before hand. ; Do not send flow control if doing half duplex. 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,flowoff ; put wait flow control char in ah or ah,ah ; check for null char jz ihosts1 ; z = null, don't send it cmp dupflg,0 ; full duplex? jne ihosts1 ; ne = no, half call outchr ; send it 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. Do not send flow control if doing half duplex. 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 cmp dupflg,0 ; full duplex? jne ihostr1 ; ne = no, half call outchr ; send it (release Host's output queue) 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: mov al,flags.comflg ; get type of port cmp al,4 ; running on a UART? ja sendbw2 ; a = yes push dx ; UART BREAK 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 ax ret sendbw2:cmp al,'N' ; is this a NetBios network port? jne sendbw5 ; 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 data 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 sendbw5:cmp al,'4' ; Bios? ja sendbw6 ; a = no push ax push dx call chkbios ; set dl to port, check IBM EBIOS jc sendbw5a ; c = not present mov ah,0fbh ; send BREAK, IBM EBIOS code int rs232 ; bios send sendbw5a:pop dx pop ax ret sendbw6:cmp al,'U' ; is it an UB NETCI port? [ohl] je sendbw6 ; e = yes [ohl] cmp al,'W' ; Novell network? jne sendbw7 ; ne = no push cx ; UB port send break [ohl] +++ 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 ret ; [ohl] --- sendbw7:cmp al,'D' ; DECnet? jne sendbw9 ; ne = no test nettype,declat ; LAT? jz sendbw8 ; z = no, CTERM cannot send a BREAK mov ax,latbreak ; LAT BREAK command push dx mov dx,decneth ; DECnet handle int latint pop dx sendbw8:ret sendbw9:test nettype,bapi ; 3Com BAPI interface? jne sendbw8 ; ne = no mov ah,bapibrk ; BAPI, send BREAK xor dh,dh ; session id of 0 (external sessions) int bapiint ret 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. ; 9 July 1989 Add support for 16550/A 14 char receiver fifo. SERINI PROC NEAR call pcwtst ; recalibrate pcwait loop timer cmp portin,0 ; did we initialize port already? je serin5 ; e = yes jl serin0 ; l = no, not yet jmp serin4 ; yes, update flow and leave serin0: mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT now jnc serin5 ; nc = success ret ; failed, exit now serin5: push bx mov bx,portval mov bl,[bx].duplex ; get full/half duplex flag, local cpy mov dupflg,bl pop bx cmp flags.comflg,4 ; UART? jbe serin2 ; be = yes, real thing jmp serin3 ; else try other port kinds serin2: 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 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 ax,modem.mdiir mov miir,ax ; uart interrupt ident register cli ; Disable interrupts cld ; Do increments in string operations 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 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.mdiir ; Interrupt Ident reg (03fah) mov al,087h ; 8 byte trigger (80), reset fifos (2/4), Rx fifo(1) out dx,al jmp $+2 in al,dx ; read back iir and al,0c0h ; select BOTH fifo bits: 16550A vs 16550 (bad fifo) cmp al,0c0h ; are both fifo enabled bits set? je serin2b ; e = yes, rcvr fifo is ok (16550/A) xor al,al ; else turn off fifo mode (16550/etc) out dx,al serin2b:mov dx,modem.mddat ; data and command port, read and flush any in al,dx ; char in UART's receive buffer inc dx ; interrupt enable register 3f9h mov al,1 ; set up interrupt enable register out dx,al ; for Data Available only jmp $+2 add dx,3 ; modem control register 3fch mov al,0bh ; assert DTR, RTS, not OUT1, and OUT2 cmp dupflg,0 ; full duplex? je serin2c ; e = yes mov al,9h ; assert DTR, not RTS, not OUT1, OUT2 serin2c: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' ; NetBios? je serin3o ; e = yes cmp flags.comflg,'O' ; Opennet Network? (FGR) jne serin3a ; ne = no serin3o:cmp pcnet,2 ; already going? je serin4 ; e = yes call setnet ; setup network session and pcnet flag jnc serin4 ; nc = success ret ; fail, carry set, leave portin at 0 serin3a:cmp flags.comflg,'4' ; using Bios? ja serin3b ; a = no call chkbios ; set dl to port, check IBM EBIOS jc serin4 ; c = not present mov ah,0f9h ; do a Regain Control operation int rs232 mov bx,offset biosbuf ; receive buffer for EBIOS push ds pop es ; set es:bx to the buffer address mov cx,biosblen ; set cx to buffer's length mov ax,0ff02h ; set buffered mode for receiving int rs232 mov ax,0fb03h ; set outgoing DTR and RTS leads int rs232 serin3b:cmp flags.comflg,'D' ; DECnet? jne serin4 ; ne = no cmp pcnet,2 ; going already? je serin4 ; e = yes call chkdec ; reinit jnc serin4 ; nc = success ret ; fail, carry set, leave portin at 0 serin4: push bx mov bx,portval ; get port data structure mov [bx].portrdy,1 ; say the comms port is ready 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: xor ax,ax cmp [bx].floflg,0 ; flow control is off? je serin1a ; e = yes mov ax,[bx].flowc ; get flow control chars serin1a:mov flowoff,al ; xoff or null mov flowon,ah ; 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 [jrd] ; 17 May 1987 Redo for COM3/4 support [jrd] ; 9 July 1989 Accomodate 16550/A receiver fifo mode. [jrd] SERRST PROC NEAR cmp portin,0 ; Reset already? jg srst3 ; g = no clc ret ; e = yes, l=not used yet, just leave srst3: cmp flags.comflg,'0' ; Bios or networks? jb srst4 ; b = 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 xor cx,cx ; 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 xor al,al mov dx,modem.mdiir ; modem Interrupt Ident reg (03fah) out dx,al ; turn off FIFO mode jmp $+2 dec dx ; point at int enable reg 3f9h 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 cmp dupflg,0 ; full duplex? je srst2a ; e = yes xor cx,cx push dx ; save dx around test below srst2b: mov dx,modem.mdstat ; modem line status reg in al,dx ; read transmitter shift reg empty bit jmp $+2 jmp $+2 jmp $+2 ; wait for char to be sent test al,40h ; is it empty? loopz srst2b ; z = no, not yet pop dx mov al,1 ; reassert DTR but not RTS, OUT1, OUT2 srst2a: 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 flags.comflg,'4' ; using Bios? ja srst6 ; a = no call chkbios ; set dl to port, check IBM EBIOS jc srst6 ; c = not present mov ah,0f9h ; do a Regain Control operation int rs232 srst1a: mov ax,0fd02h ; see if buffer has chars already int rs232 jcxz srst1b ; z = no, ok to reset buffering mov ah,0fch ; do receive /nowait to clear buff int rs232 jmp short srst1a ; repeat til empty srst1b: mov bx,offset biosbuf ; receive buffer for EBIOS push ds pop es ; set es:bx to the buffer address xor cx,cx ; set cx to zero to terminate buffering mov ax,0ff02h ; reset buffered mode for receiving int rs232 jmp srst9 ; done srst6: cmp pcnet,0 ; a network active? je srst9 ; e = no cmp flags.comflg,'O' ; Opennet network? (FGR) je srst7 ; e = yes cmp flags.comflg,'N' ; NetBios network? jne srst9 ; ne = no srst7: 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 rposted,0 ; clear interlock flag no matter what srst9: mov portin,0 ; reset flag push bx mov bx,portval ; port data structure mov [bx].portrdy,0 ; say port is not ready pop bx clc ret 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 data ; 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,1 ; send char in al? jb serdu3 ; b = no, init, return dummy status=ok ja serdu2 ; a = no, other 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 xor ah,ah ; 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] ; 9 July 1989 Add support for 16550/A 14 char receiver fifo. SERINT PROC FAR push ax ; save registers push dx ; push ds mov ax,seg data mov ds,ax ; address data segment mov dx,miir ; modem interrupt ident 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 srint0b: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 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 mov dx,mst ; uart line status register in al,dx ; get status test al,1 ; data ready? jnz srint0b ; nz = yes, and preserve al 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,mst ; 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,cmline ; 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 jc dtrlow3 ; c = failure cmp flags.comflg,'0' ; Bios? jb dtrlow1 ; b = no, UART cmp flags.comflg,'4' ; Bios? jbe dtrlow2 ; be = yes, can't access modem lines dtrlow1:call serhng ; drop DTR and RTS cmp taklev,0 ; in a Take file or macro? jne dtrlow2 ; ne = yes, no message mov ah,prstr ; give a nice message mov dx,offset hngmsg int dos dtrlow2:clc ; success dtrlow3:ret 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 call serini ; init port, if not done already mov al,flags.comflg ; get kind of port cmp al,4 ; UART? jbe shng1 ; be = yes cmp al,'4' ; Bios? ja shng2 ; a = no, networks clc ; else Bios, no hangup ret shng2: cmp pcnet,0 ; network operational? je shng1 ; e = no call nethangup ; break the session call serrst ; reset port so can be opened again clc 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 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 code ends end