TBBS 2-3 Disk 2/IPX/TIPXI14.ASM

160.6 KB 038ca5be7571ef44…
	NAME    TIPXI14
	PAGE    60,132
	TITLE   INT 14 driver for TBBS/IPAD IPX protocol connections
;
;       Version 1.0 01/20/95  by Philip L. Becker
;                            and Robert C. Hartman
;
;       Installs as a TSR and becomes an interrupt driven
;       replacement for the BIOS INT 14 routines.
;
;       This code was developed by eSoft, Inc. and Philip L. Becker, Ltd.
;       It is released to the public domain, for any use whatever.
;       Feel free to modify it as you wish, but no technical support
;       for modifications is implied or offered.
;
;
;  Note:  Although the TIPX protocol has provisions for windowed
;         transfers to be efficient over networks with large delays
;         in packet transmission, the use of windowing creates very
;         large memory demands in a multi-line environment like TBBS.
;         As a result, TBBS can only handle a window size of 1, and
;         this driver does not really support more either without
;         a bit of work in the receive routines.
;
;
WINDOW_SIZE     EQU     1               ;# IPX blocks in window
DEBUG           EQU     0               ;no debug code
;DEBUG          EQU     500             ;>0 = # entries in transaction buffer
;
	PUBLIC  START,INT_14,INT_14F
;
;   Public all variables for debugger...
;
	PUBLIC  IB_START,IB_END,IB_IPTR,IB_EPTR,OB_START,OB_END,OB_IPTR
	PUBLIC  OB_EPTR,IRQ14VEC,PSPSEG,XMIT_ACT,MODEM_SR
	PUBLIC  MODEM_CR,INPUT_FLOW,SAP_FIND_STATE,CIRC_0_STATE,CIRC_N_STATE
	PUBLIC  IPX_VECTOR,NETWORK_ADDRESS,NODE_ADDRESS,SOCKET_ADDRESS
	PUBLIC  SERVER_NAME,HIS_ADDRESS,HIS_IMMED_ADDRESS,WE_ARE_SERVER
	PUBLIC  WE_ARE_CLIENT,OPEN_452,SAP_DEST,SAP_CNTR,SERVER_TNAME
	PUBLIC  SERVER_HOPS,SERVER_FOUND,FS_HIT,FIND_NAME,FIND_ADDRESS
	PUBLIC  SAVE_HIS_ADDRESS,SAVE_HIS_HOPS,SAVE_HIS_SEQ,RIPXHDR1
	PUBLIC  RIPXHDR2,RIPXHDR3,RIPXHDR4,RIPXHDR5,RIPXDATA1,RIPXDATA2
	PUBLIC  RIPXDATA3,RIPXDATA4,RIPXDATA5,RECB1,RECB2,RECB3,RECB4
	PUBLIC  RECB5,SIPXHDR1,SIPXHDR2,SIPXHDR3,SIPXHDR4,SIPXHDR5
	PUBLIC  SIPXDATA1,SIPXDATA2,SIPXDATA3,SIPXDATA4,SIPXDATA5
	PUBLIC  SECB1,SECB2,SECB3,SECB4,SECB5,SEND_ECB_QUEUE
	PUBLIC  RCV_ECB_QUEUE,SQ_IPX_HDR,SQ_IPX_DATA,SQ_ECB,SAP_IPX_HDR1
	PUBLIC  SAP_IPX_HDR2,SAP_IPX_HDR3,SAP_IPX_DATA1,SAP_IPX_DATA2
	PUBLIC  SAP_ECB1,SAP_ECB2,SIB_LENGTH,SIB_PACKET,SIB_NAME
	PUBLIC  SIB_ADDRESS,CIRC_N_OPEN,CIRC_N_NUMBER,CIRC_N_RCTL
	PUBLIC  CIRC_N_SEQ,CIRC_N_CSEQ,CIRC_N_NEXT,CIRC_0_SEQ,CIRC_0_NEXT
	PUBLIC  RETRY_CNT1,RETRY_CNT2,TIMER1,TIMER2,TIMER3,SAP_RESP_IN
	PUBLIC  CTL_ACK,LAST_CTL_SEQ,RCV_HOLDING,PERM_CONNECT
	PUBLIC  NOW_CONNECTED,PS2_BIOS,E_BIOS,FOSSIL,RCV_RE_ENT
	PUBLIC  SEARCHING
;
	IF      DEBUG GT 0
	PUBLIC  DEBUG_BUFFER,DEBUG_PTR
	ENDIF
;
;-----------------------------------------------
;  IPX ECB Structure
;-----------------------------------------------
ECB             STRUC
LINK_ADDR       DD      0               ;Link address
ESR_ADDR        DD      0               ;ESR Service
IN_USE_FLAG     DB      0               ;In use flag
COMPLETION_CODE DB      0               ;Completion code
SOCKET_NUMBER   DW      0               ;Socket #
IPX_WORK_SPACE  DB      4 DUP(0)        ;IPX Work Space
DRV_WORK_SPACE  DB      12 DUP(0)       ;Driver Work Space
IMMEDIATE_ADDR  DB      6 DUP(0)        ;Immediate Address
FRAG_COUNT      DW      2               ;Fragment Count
FRAG1_ADDR      DD      0
FRAG1_SIZE      DW      30
FRAG2_ADDR      DD      0
FRAG2_SIZE      DW      0
ECB             ENDS
;
;-----------------------------------------------
;  IPX header structure
;-----------------------------------------------
IPXHDR          STRUC
CHECKSUM        DW      0FFFFh  ;Checksum
HDR_LEN         DW      0       ;Length
TRANSPORT       DB      0       ;Transport
PKT_TYPE        DB      0       ;Packet Type
DEST            DB      12 DUP(0) ;Destination Address
SOURCE          DB      12 DUP(0) ;Source Address
IPXHDR          ENDS
;
;-----------------------------------------------
;   TBBS/IPAD IPX data packet structure
;-----------------------------------------------
IPXDATA         STRUC
TPKT_LEN        DW      0       ;# of active bytes
TPKT_SEQ        DW      0       ;packet sequence #
TPKT_TYPE       DB      0       ;packet type
TPKT_CIRCUIT    DB      0       ;packet circuit #
TPKT_CHECKSUM   DW      0       ;packet checksum
TPKT_DATA       DB      512 DUP(0)
IPXDATA         ENDS
;
;-------------------------------------------------------------------
;   Character Buffer Size Parameters
;-------------------------------------------------------------------
IN_SIZE         EQU     4096                    ;# bytes in input buffer
OUT_SIZE        EQU     4096                    ;# bytes in output buffer
;
;-------------------------------------------------------------------
CODE    SEGMENT PARA 'CODE'
;
	ASSUME  CS:CODE,DS:CODE,ES:NOTHING,SS:NOTHING
;
MAIN    PROC    NEAR
	JMP     START                   ;Begin Execution
	DB      'TIPXI14 Version 1.0 - Developed 1995 by Philip'
	DB      ' L. Becker, Ltd.                              '
	DB      '                                              '
	DB      '                                              '
	DB      '                                              '
	DB      '                                              '
	DB      '                               End of Zap Area'
;
;-------------------------------------------------------------------
;   Data Area - Logical "Serial Port Interrupt" data buffers
;
INBUF1          DB      IN_SIZE DUP(0)          ;input buffer - COM1 (IRQ4)
OUTBUF1         DB      OUT_SIZE DUP(0)         ;output buffer - COM1 (IRQ4)
		EVEN                            ;assure on word boundary
;-------------------------------------------------------------------
;  Following tables have one entry for each com port/IRQ
;  They are accessed by indexing by com port number.
;
;  Word tables are all first
;
IB_START        DW      INBUF1                  ;inbuf start address
;
IB_END          DW      INBUF1+IN_SIZE          ;end of buffer + 1
;
IB_IPTR         DW      INBUF1                  ;inbuf insertion pointer
;
IB_EPTR         DW      INBUF1                  ;inbuf extraction pointer
;
OB_START        DW      OUTBUF1                 ;outbuf start address
;
OB_END          DW      OUTBUF1+OUT_SIZE        ;end of buffer + 1
;
OB_IPTR         DW      OUTBUF1                 ;outbuf insertion pointer
;
OB_EPTR         DW      OUTBUF1                 ;outbuf extraction pointer
;
IRQ14VEC        DD      0                       ;original INT 14 vector
PSPSEG          DW      0                       ;segment of our PSP
;
;-------------------------------------------------------------------
;     Byte tables with one entry/port for "serial port" status
;
XMIT_ACT        DB      0                       ;>0 = xmitter active
;
MODEM_SR        DB      0               ;modem status register flags
;                                         Bit 7 = Carrier Detect (CD)
;                                         Bit 6 = Ring Indicator (RI)
;                                         Bit 5 = Data Set Ready (DSR)
;                                         Bit 4 = Clear To Send (CTS)
;                                         Bit 3 = CD chg since last status
;                                         Bit 2 = RI chg since last status
;                                         Bit 1 = DSR chg since last status
;                                         Bit 0 = CTS chg since last status
;
MODEM_CR        DB      0               ;modem control regs
;                                         Bit 7 = Reserved, Always 0
;                                         Bit 6 = Reserved, Always 0
;                                         Bit 5 = Reserved, Always 0
;                                         Bit 4 = LOOP
;                                         Bit 3 = OUT2
;                                         Bit 2 = OUT1
;                                         Bit 1 = RTS state
;                                         Bit 0 = DTR state
;
INPUT_FLOW      DB      0                       ;>0 = RTS lowered
;
;-------------------------------------------------------------------
;  IPX variable cells
;
SAP_FIND_STATE  DW      0               ;state of server locator
CIRC_0_STATE    DW      0               ;state of circuit 0
CIRC_N_STATE    DW      0               ;state of circuit "n"
IPX_VECTOR      DD      0               ;filled in by init
;
;   Our Local IPX address (filled in by init)
;
NETWORK_ADDRESS DB      4 DUP(0)        ;used as 12 byte address here...
NODE_ADDRESS    DB      6 DUP(0)
SOCKET_ADDRESS  DW      0
;
;   Name and IPX address of our desired server
;
SERVER_NAME     DB      49 DUP(0)       ;name of server to find (as client)
HIS_ADDRESS     DB      12 DUP(0)       ;server address after located
HIS_IMMED_ADDRESS DB 6 DUP(0)   ;open circuit's immediate address
;
;
BE_QUIET        DB      0               ;>0 = no "pseudo-device" messages
WE_ARE_SERVER   DB      0               ;>0 = /s:name was given and we
;                                       ;are a server...
WE_ARE_CLIENT   DB      0               ;>0 - /c:name was given
OPEN_452        DB      0               ;>0 = socket 0452h is open
;-----------------------------------------------------------------------
;  SAP lookup temp cells
;
;  Name found during SAP requests...
;
SAP_DEST        DB      10 DUP(0FFh),04h,52h ;broadcast address for SAP query
SAP_CNTR        DW      0               ;# times to try to find server
SERVER_TNAME    DB      49 DUP(0)       ;server temp name from SAP pkt
SERVER_HOPS     DW      0               ;# of hops to that server
SERVER_FOUND    DB      0               ;>0 = server located
FS_HIT          DB      0
FIND_NAME       DB      49 DUP(0)       ;server lookup name
FIND_ADDRESS    DB      12 DUP(0)       ;temp find address
SAVE_HIS_ADDRESS DB     12 DUP(0)       ;temp adr hold area
SAVE_HIS_HOPS   DW      0               ;temp distance
SAVE_HIS_SEQ    DW      0               ;temp seq #
;-----------------------------------------------------------------------
;
;-------------------------------------------------------------------
;   Receive IPX header buffers for connected data paths
;-------------------------------------------------------------------
RIPXHDR1        IPXHDR <>
RIPXHDR2        IPXHDR <>
RIPXHDR3        IPXHDR <>
RIPXHDR4        IPXHDR <>
RIPXHDR5        IPXHDR <>
;-------------------------------------------------------------------
;   Receive TBBS/IPAD packet IPX data buffers for connected data paths
;-------------------------------------------------------------------
RIPXDATA1       IPXDATA <>
RIPXDATA2       IPXDATA <>
RIPXDATA3       IPXDATA <>
RIPXDATA4       IPXDATA <>
RIPXDATA5       IPXDATA <>
;-------------------------------------------------------------------
;   ECBs to Receive TBBS/IPAD packets from server
;-------------------------------------------------------------------
RECB1           ECB <,RCV_IPX,,,,,,,2,RIPXHDR1,30,RIPXDATA1,520>
RECB2           ECB <,RCV_IPX,,,,,,,2,RIPXHDR2,30,RIPXDATA2,520>
RECB3           ECB <,RCV_IPX,,,,,,,2,RIPXHDR3,30,RIPXDATA3,520>
RECB4           ECB <,RCV_IPX,,,,,,,2,RIPXHDR4,30,RIPXDATA4,520>
RECB5           ECB <,RCV_IPX,,,,,,,2,RIPXHDR5,30,RIPXDATA5,520>
;
;-------------------------------------------------------------------
;   Send IPX header buffers for connected data paths
;-------------------------------------------------------------------
SIPXHDR1        IPXHDR <>
SIPXHDR2        IPXHDR <>
SIPXHDR3        IPXHDR <>
SIPXHDR4        IPXHDR <>
SIPXHDR5        IPXHDR <>
;-------------------------------------------------------------------
;   Send IPX header buffers for connected data paths
;-------------------------------------------------------------------
SIPXDATA1       IPXDATA <>
SIPXDATA2       IPXDATA <>
SIPXDATA3       IPXDATA <>
SIPXDATA4       IPXDATA <>
SIPXDATA5       IPXDATA <>
;-------------------------------------------------------------------
;   ECBs to send TBBS/IPAD packets to server
;-------------------------------------------------------------------
SECB1           ECB <,0,,,,,,,2,SIPXHDR1,30,SIPXDATA1,520>
SECB2           ECB <,0,,,,,,,2,SIPXHDR2,30,SIPXDATA2,520>
SECB3           ECB <,0,,,,,,,2,SIPXHDR3,30,SIPXDATA3,520>
SECB4           ECB <,0,,,,,,,2,SIPXHDR4,30,SIPXDATA4,520>
SECB5           ECB <,0,,,,,,,2,SIPXHDR5,30,SIPXDATA5,520>
;
ECB_SIZE        EQU     SECB2-SECB1
;
;  Que list for sending ECBs.
;  In following QUEUE 0 = avail, non-zero = in use
;
SEND_ECB_QUEUE  DB      0,0,0,0,0       ;outgoing ECB assignment
		EVEN
RCV_ECB_QUEUE   DW      0,0,0,0,0       ;incoming ECB deferment for re-entrancy
;
;-------------------------------------------------------------------
;   IPX Header buffer, Data Packet, and ECB to send SAP polling request
;-------------------------------------------------------------------
SQ_IPX_HDR      IPXHDR  <>
SQ_IPX_DATA     DW      0300h,9191h     ;query for all of our type servers
SQ_ECB          ECB <,0,,,,,,,2,SQ_IPX_HDR,30,SQ_IPX_DATA,4>
;
;-------------------------------------------------------------------
;   IPX header buffer, data packet and ECB to listen for SAP requests
;   if the /s:name option is given
;-------------------------------------------------------------------
;
SAP_IPX_HDR1    IPXHDR  <>
SAP_IPX_HDR2    IPXHDR  <>
SAP_IPX_HDR3    IPXHDR  <>
;
SAP_IPX_DATA1   DB      4 DUP(0)
SAP_IPX_DATA2   DB      4 DUP(0)
;
SAP_ECB1        ECB <,SAP_RESPOND,,,5204h,,,,2,SAP_IPX_HDR1,30,SAP_IPX_DATA1,4>
SAP_ECB2        ECB <,SAP_RESPOND,,,5204h,,,,2,SAP_IPX_HDR2,30,SAP_IPX_DATA2,4>
;
;  Service Advertising Protocol Identification Packet
;
SIB_LENGTH      DW      66              ;length (add 64 for each extra name)
SIB_PACKET      DW      4               ;Query or Response Type
		DW      9191h           ;Server Type (hi-lo order)
SIB_NAME        DB      48 DUP(' ')     ;48 character server name ID
SIB_ADDRESS     DB      4 DUP(0)        ;network (filled in by init)
		DB      6 DUP(0)        ;node
		DB      2 DUP(0)        ;socket
		DB      0,1             ;Intermediate Nets (bumped by router/bridge)
;-------------------------------------------------------------------
;
;-----------------------------------------------
		EVEN
CIRC_N_OPEN     DB      0               ;0 = closed, 1=opening, 2=assg, 3=open
CIRC_N_NUMBER   DB      1               ;circuit number assigned by server
CIRC_N_RCTL     DB      20h,0           ;received control status for line "n"
CIRC_N_SEQ      DW      0               ;data packet sequence numbers
CIRC_N_CSEQ     DW      0               ;control pkt seq numbers
CIRC_N_NEXT     DW      0               ;"next" sequence number
;
CIRC_0_SEQ      DW      0               ;circuit 0 outgoing seq numbers
CIRC_0_NEXT     DW      0               ;"next" sequence # on circuit 0
RETRY_CNT1      DW      0               ;retry counter
RETRY_CNT2      DW      0               ;retry counter
RETRY_CNT3      DW      0               ;retry counter
;
TIMER1          DW      0,0
TIMER2          DW      0,0
TIMER3          DW      0,0
SAP_RESP_IN     DW      0               ;flag for SAP response "viewing"
CTL_ACK         DB      0               ;# ctl acks rcvd
LAST_CTL_SEQ    DW      0               ;seq # of last rcvd ctl pkt
RCV_HOLDING     DB      0               ;>0 = RCV holding packets
PERM_CONNECT    DB      0               ;>0 = connect @ init and stay there
NOW_CONNECTED   DB      0               ;>0 = circuit now connected
SEARCHING       DB      0               ;>0 = doing outbound server locate
PS2_BIOS        DB      1               ;>0 = use FCN 4 & 5, 0 = only 0-3
E_BIOS          DB      1               ;>0 = use EBIOS functions F4 and above
FOSSIL          DB      0               ;>0 = use FOSSIL calls
RCV_RE_ENT      DW      0               ;>0 = rcv ESR task running
;-----------------------------------------------
;  "Pseudo-device" message strings
;
PD_RINGING      DB      0Dh,0Ah,'TIPX CALL RECEIVED',0Dh,0Ah,0
PD_BUSY         DB      0Dh,0Ah,'SERVER HAS NO CIRCUITS AVAILABLE...',0Dh,0Ah,0
PD_NOCON        DB      0Dh,0Ah,'CANNOT CONNECT TO SERVER!',0Dh,0Ah,0
PD_CONNECT      DB      0Dh,0Ah,'CONNECT TIPX',0Dh,0Ah,0
PD_CLOSED       DB      0Dh,0Ah,'TIPX CONNECTION CLOSED',0Dh,0Ah,0
PD_FAIL         DB      0Dh,0Ah,'CANNOT LOCATE SERVER!',0Dh,0Ah,0
PD_TIMEOUT      DB      0Dh,0Ah,'SERVER CONNECTION LOST!',0Dh,0Ah,0
;-----------------------------------------------
;  Internal Stack area...
;
	EVEN
	DB      400 DUP(0)      ;400 byte stack
STACK   DB      0
;
	IF      DEBUG GT 0
	EVEN
DEBUG_BUFFER DB 16*DEBUG DUP(0) ;# of log entries
DEBUG_PTR DW    DEBUG_BUFFER
	ENDIF
;
		PAGE
;===================================================================
;   INT 14 service routine Entry Point
;
;   Called with following registers:
;
;   DX = COM PORT (0=COM1, 1=COM2)
;   AH = FCN
;
;   FCNs allowed are:
;
;   AH = 0  INIT Port
;      AL=Parameters (ignored by IPX routines)
;
;      Return is AH=Line Status, AL=Modem Status
;
;   AH = 1  Send Character in AL
;
;      Return is AH=Line Status
;                   Bit 7 = timed out (other bits meaningless if set)
;                   Bit 6 = Xmit Shift Register Empty
;                   Bit 5 = Xmit Holding Register empty
;                   Bit 4 = break detected
;                   Bit 3 = framing error
;                   Bit 2 = parity error
;                   Bit 1 = overrun error
;                   Bit 0 = data ready to read
;
;   AH = 2  Receive Character into AL
;
;      Return is AL = character
;                AH = Line Status as above
;
;   AH = 3  Read Status
;
;      Return is AL=Modem Status
;                   Bit 7 = Carrier Detect (CD)
;                   Bit 6 = Ring Indicator (RI)
;                   Bit 5 = Data Set Ready (DSR)
;                   Bit 4 = Clear To Send (CTS)
;                   Bit 3 = CD changed since last status
;                   Bit 2 = RI changed since last status
;                   Bit 1 = DSR changed since last status
;                   Bit 0 = CTS changed since last status
;                AH=Line Status as above
;
;---------------------------------------------------------------------
;  Above is generic BIOS only.  Following type 4 and 5 are
;  PS/2 extended BIOS calls...
;
;   AH = 4 Extended INIT port
;        AL = 0, no break or 1, send break
;        BH = parity 0=none, 1=odd, 3=even, 3&4 = stick
;        BL = stop bits 0=1 stop bit, 1=2 stop bits
;        CH = word length 0=5 bits, 1=6 bits, 2=7 bits, 3=8 bits
;        CL = bps rate 0=110, 1=150, 2=300, 3=600, 4=1200, 5=2400
;                      6=4800, 7=9600, 8=19200
;
;      Return is AH=Line Status, AL=Modem Status
;
;
;   AH = 5 Extended Port Control
;        AL=0 Read Modem Control Reg
;
;        Return is AH=Line Status, AL=Modem Status
;                  BL=Modem Control Register
;                   Bit 7 = Reserved, always 0
;                   Bit 6 = Reserved, always 0
;                   Bit 5 = Reserved, always 0
;                   Bit 4 = LOOP
;                   Bit 3 = OUT2
;                   Bit 2 = OUT1
;                   Bit 1 = RTS
;                   Bit 0 = DTR
;
;        AL=1 Write Modem Control Reg
;             BL=modem control reg
;
;        Return is AH=Line Status, AL=Modem Status
;---------------------------------------------------------------------
FUNCTBL DW      F00_INIT                ;function table (AH=0, etc.)
	DW      F01_TX                  ;etc
	DW      F02_RX
	DW      F03_STATUS
	DW      F04_FINIT
	DW      F05_UNINIT
	DW      F06_DTR
	DW      F07_TICK
	DW      F08_TXFORCE
	DW      F09_TXDUMP
	DW      F0A_RXDUMP
	DW      F0B_TXNW
	DW      F0C_RXSCAN
	DW      F0D_KEYSCAN
	DW      F0E_KEYGETC
	DW      F0F_FLOW
	DW      F10_FLAG
	DW      F11_SETCURSOR
	DW      F12_GETCURSOR
	DW      F13_SCRNCHR
	DW      F14_WATCHDOG
	DW      F15_OUTDIRECT
	DW      F16_CLKINT
	DW      F17_REBOOT
	DW      F18_READBLOCK
	DW      F19_WRITEBLOCK
	DW      F1A_BREAK
	DW      F1B_INFO
;
FUNCNT  EQU     ($ - FUNCTBL) / 2         ;Number of words in table
;
EBIOTBL DW      FF4_ICHK                ;function table (AH=F4, etc.)
	DW      PORTER                  ;etc
	DW      PORTER
	DW      PORTER
	DW      PORTER
	DW      FF9_REGAIN
	DW      FFA_BREAK
	DW      FFB_EPC
	DW      FFC_RXSCAN
	DW      FFD_INFO
	DW      PORTER
	DW      FFF_READBLOCK
;
LOCATE_STRING   DB      'TIPXI14 VERSION 1.0',0 ;must stay here!!!
;
; Entry point when being used as a FOSSIL
;
INT_14F:JMP     SHORT INT_14            ;go to the real code
	ORG     INT_14F+6               ;put it here
	DW      1954H                   ;magic number
	DB      FUNCNT                  ;number of functions supported
;
; Normal entry point for non-FOSSIL
;
INT_14: STI                             ;re-enable interrupts
	PUSH    CX
	PUSH    BX                      ;save registers we will use
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    DS
	PUSH    ES
	MOV     SI,CS
	MOV     DS,SI                   ;make DS: = CS: -> CODE
	MOV     ES,SI                   ;ditto ES
	CMP     [FOSSIL],0              ;are we FOSSIL?
	JNZ     PORTOK                  ;yes, so just go with the flow!
	OR      DX,DX                   ;assure valid port address (0 only)
	JNZ     PORTER                  ;bad call
;
;  DX requested COM1 correctly
;  AH = action code
;
PORTOK: CMP     AH,FUNCNT               ;are we in range?
	JA      EB_CHK                  ;might be no good
	PUSH    AX
	XCHG    AH,AL
	XOR     AH,AH
	MOV     SI,AX
	ADD     SI,SI                   ;make it a word index
	POP     AX
	JMP     [FUNCTBL][SI]
;
EB_CHK: CMP     [E_BIOS],0              ;are we doing extended BIOS?
	JNZ     EB_CHK1                 ;it is, so we might be ok
EB_BAD: JMP     PORTER                  ;it was bad
;
EB_CHK1:SUB     AH,0F4h                 ;is it an extended call?
	JC      EB_BAD                  ;no, so it was no good
	PUSH    AX
	XCHG    AH,AL
	XOR     AH,AH
	MOV     SI,AX
	ADD     SI,SI                   ;make it a word index
	POP     AX
	JMP     [EBIOTBL][SI]
;
F04_PS2_EINIT:
	OR      AL,AL                   ;Type=4, is break=1?
	JZ      F00_INIT                ;no, just like TYPE=0 init
;                                       ;yes, just like TYPE=3 call
;-------------------------------------------------------------------
;  Compose and return status for the AH=3 - Read Status call
;
F03_STATUS:
	CALL    LOAD_LINE_STATUS        ;set AH = "Line Status"
	CALL    LOAD_MODEM_STATUS       ;set AL = "Modem Status"
	JMP     PORTXT                  ;return
;
;   Exit from INT 14 service call.  AH and AL = return values
;
PORTER: MOV     AH,80h                  ;indicate error
	CMP     [FOSSIL],0              ;are we a FOSSIL?
	JZ      PORTXT                  ;no
	PUSH    AX
	JMP     FOSSER                  ;yes, so different error code
;
PORTXT: POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     BX
	POP     CX
	IRET                            ;return to caller
;
;-------------------------------------------------------------------
;   Send character in AL to "com port"
;
F01_TX: CMP     [NOW_CONNECTED],0       ;TYPE 1 & 2 force connection port
	JNZ     F01_TX1
	CALL    DP_CONN
	JC      F01_TXE                 ;send char to "pseudo-device"
;
F01_TX1:MOV     BX,[OB_IPTR]            ;get output buffer pointer
	MOV     CX,BX                   ;copy to CX
	INC     CX                      ;bump pointer to next slot
	CMP     [OB_END],CX             ;is it past end?
	JNZ     DOSND2                  ;no
	MOV     CX,[OB_START]           ;yes, "turn corner" to front
DOSND2: CMP     [OB_EPTR],CX            ;is buffer full?
	JZ      F01_TX                  ;yes - wait for spot to open up
	MOV     [BX],AL                 ;no, store our character in buffer
	MOV     [OB_IPTR],CX            ;update insertion pointer
	CALL    START_XMIT              ;start transmitter if 1st
	CALL    LOAD_LINE_STATUS        ;set AH = "Line Status"
	CMP     [FOSSIL],0              ;are we a FOSSIL?
	JZ      DOSND3                  ;no
	CALL    LOAD_MODEM_STATUS       ;set AL = "Modem Status"
	AND     AL,80h                  ;get DCD
	OR      AL,08h                  ;put this there for FOSSIL
DOSND3: JMP     PORTXT                  ;return it
;
;-------------------------------------------------------------------
;  We get here if we could not make a connection.
;  AL = character we were trying to send...
;
;  For now, we just say we took it, as we're trying each
;  time to connect.
;
F01_TXE:CALL    LOAD_LINE_STATUS        ;set AH = "Line Status"
	CMP     [FOSSIL],0              ;are we a FOSSIL?
	JZ      FTXE3                   ;no
	CALL    LOAD_MODEM_STATUS       ;yes, set AL = "Modem Status"
	AND     AL,80h                  ;get DCD
	OR      AL,08h                  ;put this there for FOSSIL
FTXE3:  JMP     PORTXT                  ;return it
;
;-------------------------------------------------------------------
;   Initialize the selected "COM port"
;
;   SI = byte index, DX = port base address
;   AL = user's request parameters
;
F00_INIT:
	CLI                             ;halt any interrupts
	CMP     [INPUT_FLOW],0          ;have we lowered RTS?
	JZ      DOINI1                  ;no - all is well
	MOV     [INPUT_FLOW],0          ;yes, indicate we raised RTS again
	OR      [MODEM_CR],02h          ;raise "CTS" to other end
	CALL    SEND_CONTROL            ;negotiate with other end
;
DOINI1: MOV     AX,[OB_START]           ;get output buffer start pointer
	MOV     [OB_IPTR],AX            ;reset buffer pointers
	MOV     [OB_EPTR],AX            ;to empty buffer
;
	STI                             ;allow interrupts again
	CALL    LOAD_LINE_STATUS        ;set AH = "Line Status"
	CALL    LOAD_MODEM_STATUS       ;AH = "modem" status
	JMP     PORTXT                  ;return status - port initialized
;
;-------------------------------------------------------------------
;  Do extended port status/control call
;
;  BL = New Modem Control Register Bits
;
F05_PS2_EPC:
	OR      AL,AL                   ;is it read or write MCR?
	JZ      EPCRD                   ;read
	MOV     BH,[MODEM_CR]
	AND     [MODEM_CR],0FEh         ;remove DTR from us
	AND     BL,01h                  ;isolate DTR from his data
	OR      [MODEM_CR],BL           ;store his new setting
	XOR     BH,BL
	AND     BH,01h                  ;did DTR change?
	JZ      EPCRD                   ;no, just report status
;
;  He changed something, BL has new data
;
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     DOEPC1                  ;yes
	JMP     SHORT EPCRD             ;no, return status after connect
;
;  He changed something and we're connected...
;
DOEPC1: TEST    BL,01h                  ;did he lower DTR?
	JNZ     EPCRDR                  ;no, just report what we did
	CMP     [PERM_CONNECT],0        ;yes, are we perm or dynamic?
	JNZ     EPCRD                   ;perm, don't release
	CALL    RELEASE_CIRCUIT         ;dynamic, shut down circuit
	JMP     SHORT EPCRD             ;and return status
;
EPCRDR: CALL    SEND_CONTROL            ;report changes
EPCRD:  MOV     AH,5                    ;Restore AH to calling condition
	CALL    LOAD_MODEM_STATUS       ;set AL = "Modem Status"
	MOV     BL,[MODEM_CR]           ;get MCR to return
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     CX                      ;old BX back
	MOV     BH,BL                   ;restore original BH, return BL
	POP     CX
	IRET                            ;return to caller
;
DP_CONN:PUSH    AX
	PUSH    SI
	PUSH    DI
	CLI                             ;*** disable ints ***
	CMP     [NOW_CONNECTED],0       ;check close timing...
	JNZ     DP_INC                  ;incoming just got to us!
	MOV     [SEARCHING],1           ;flag busy outbound now...
	STI                             ;*** enable ints ***
	MOV     SI,OFFSET SERVER_NAME   ;name of server to locate
	MOV     DI,OFFSET HIS_ADDRESS   ;put address here if found
	CALL    FIND_SERVER             ;find our server
	POP     DI
	POP     SI
	JC      PCONER                  ;can't find it
	CALL    REQUEST_CIRCUIT         ;request a circuit
	JC      PCONER1
	MOV     [NOW_CONNECTED],1       ;no, indicate it is now
	MOV     [SEARCHING],0           ;and we're not searching
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AH,40h                  ;isolate RI
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;position chg bits
	OR      AL,AH                   ;restore RI as it was
	XOR     AL,0BBh                 ;set CD, DSR & CTS, also set chg bits
	AND     AL,0FBh                 ;no chg poss in RI
	MOV     [MODEM_SR],AL           ;store new status reg image
	MOV     [MODEM_CR],0Bh          ;set OUT2, DTR and RTS high
DPC2:   CALL    SEND_CONTROL            ;report control status
	POP     AX
	CLC
	RET
;
DP_INC: POP     DI
	POP     SI
	POP     AX
	CLC
	RET                             ;report connection made
;
PCONER: PUSH    BX
	MOV     BX,OFFSET PD_FAIL       ;can't locate server
	JMP     SHORT PCONEC
;
PCONER1:PUSH    BX
	MOV     BX,OFFSET PD_NOCON      ;server timed out
PCONEC: CALL    STUFF_PDS
	MOV     [SEARCHING],0           ;we're not searching
	POP     BX
	POP     AX
	STC
	RET
;
;------------------------------------------------------------
;  Stuff Pseudo-Device string into rcv buffer...
;
;  On entry BX: -> null terminated string
;
STUFF_PDS:
	CMP     [BE_QUIET],0            ;are we to be quite?
	JNZ     SPDSX                   ;yes, quick out
	PUSH    SI
	PUSH    BX
	PUSH    AX
	PUSHF                           ;save STI/CLI status
	CLI                             ;assure ints turned off
	MOV     SI,BX                   ;CS:SI -> string
	MOV     BX,[IB_IPTR]            ;get insertion pointer
PDS1:   MOV     AX,BX                   ;copy it to AX
	INC     AX                      ;bump store pointer
	CMP     [IB_END],AX             ;at end of buffer?
	JNZ     PDS1A                   ;no, all is well
	MOV     AX,[IB_START]           ;yes, turn corner
PDS1A:  CMP     [IB_EPTR],AX            ;is buffer full?
	JZ      PDS2                    ;yes, truncate response
	PUSH    AX                      ;no, save new pointer
	MOV     AL,CS:[SI]              ;fetch char
	INC     SI                      ;bump fetch index
	OR      AL,AL
	JZ      PDS2P                   ;<EOM> hit = all done
	MOV     CS:[BX],AL              ;store the new char
	POP     BX                      ;set new pointer
	MOV     [IB_IPTR],BX            ;no, update pointer
	JMP     PDS1                    ;move packet to input buffer
;
PDS2P:  POP     AX                      ;even stack
PDS2:   POPF
	POP     AX
	POP     BX
	POP     SI
SPDSX:  RET
;
;-------------------------------------------------------------------
;   Receive a character from com port to AL
;
F02_RX: XOR     CX,CX                   ;say not waiting now...
DORD1:  MOV     BX,[IB_EPTR]            ;get input buffer pointer
	CMP     [IB_IPTR],BX            ;is there a character available?
	JNZ     GOTCHR                  ;yes, return it to caller
	JCXZ    WTINIT                  ;1st time to wait, get set up
;
DORDW:  PUSH    BP
	PUSH    BX
	PUSH    ES
	MOV     BX,08h
	CALL    [IPX_VECTOR]
	CMP     AX,CX                   ;is time here yet?
	JA      DORD1A                  ;yes - timed out
	MOV     BX,0Ah                  ;do IPX relinquish control
	CALL    [IPX_VECTOR]
	POP     ES
	POP     BX
	POP     BP
	JMP     DORD1                   ;no - delay a while waiting
;
DORD1A: POP     ES
	POP     BX
	POP     BP                      ;restore regs
	MOV     AH,80h                  ;timed out - return that status
	JMP     PORTXT                  ;and get out
;
;  Set CX up for 5 sec wait for character...
;
WTINIT: CMP     [NOW_CONNECTED],0       ;TYPE 1 & 2 force connect to port
	JNZ     WTINC                   ;we-re connected
	MOV     AH,80h
	JMP     PORTXT                  ;signal timed out waiting...
;
WTINC:  PUSH    BP
	PUSH    BX
	PUSH    AX
	PUSH    ES
	MOV     BX,08h                  ;get interval marker
	CALL    [IPX_VECTOR]
	POP     ES
	MOV     CX,AX                   ;save it in CX
	POP     AX
	POP     BX
	POP     BP
	ADD     CX,18*5                 ;5 sec delay if waiting
	JMP     DORDW                   ;continue w/wait for char
;
;   Character is waiting, read it into AL
;
GOTCHR: MOV     AL,[BX]                 ;yes, fetch the character
	INC     BX                      ;bump pointer to next slot
	CMP     [IB_END],BX             ;is it past end?
	JNZ     DORD2                   ;no
	MOV     BX,[IB_START]           ;yes, "turn corner" to front
DORD2:  MOV     [IB_EPTR],BX            ;update extraction ptr for next time
	CMP     [INPUT_FLOW],0          ;have we lowered RTS?
	JZ      DORD4                   ;no - all done
	MOV     BX,[IB_IPTR]            ;yes...
	SUB     BX,[IB_EPTR]            ;calculate # bytes now in buffer
	JNS     DORD3
	ADD     BX,IN_SIZE              ;rounding corner, correct for it
DORD3:  CMP     BX,200                  ;are we less than 200 bytes used?
	JAE     DORD4                   ;no - still halt input
	MOV     [INPUT_FLOW],0          ;yes, indicate we raised RTS again
	OR      [MODEM_CR],02h          ;raise "CTS" to other end
	CALL    SEND_CONTROL            ;negotiate with other end
DORD4:  CALL    LOAD_LINE_STATUS        ;set AH = "Line Status"
	JMP     PORTXT                  ;return character and status
;
FF4_ICHK:
	CMP     AL,0FFh                 ;is this legal?
	JZ      FF4_1                   ;yes
	JMP     PORTER                  ;no good
;
FF4_1:  XOR     AX,AX                   ;clear AX
	JMP     PORTXT                  ;yes, we are here
;
FF9_REGAIN:
	JMP     PORTXT                  ;nop
;
FFA_BREAK:
	JMP     PORTXT                  ;nop
;
FFB_EPC:
	PUSH    AX
	MOV     BL,AL                   ;bits to set to BL
	MOV     BH,[MODEM_CR]
	AND     [MODEM_CR],0FEh         ;remove DTR from us
	AND     BL,01h                  ;isolate DTR from his data
	OR      [MODEM_CR],BL           ;store his new setting
	XOR     BH,BL
	AND     BH,01h                  ;did DTR change?
	JZ      FFB_RD                  ;no, done
;
;  He changed something, BL has new data
;
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     DOFFB_1                 ;yes
	JMP     SHORT FFB_RD            ;no, return status after connect
;
;  He changed something and we're connected...
;
DOFFB_1:TEST    BL,01h                  ;did he lower DTR?
	JNZ     FFB_RDR                 ;no, just report what we did
	CMP     [PERM_CONNECT],0        ;yes, are we perm or dynamic?
	JNZ     FFB_RD                  ;perm, don't release
	CALL    RELEASE_CIRCUIT         ;dynamic, shut down circuit
	JMP     SHORT FFB_RD            ;and return status
;
FFB_RDR:CALL    SEND_CONTROL            ;report changes
FFB_RD: POP     AX
	JMP     PORTXT                  ;get out
;
FFC_RXSCAN:
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     FFC_1                   ;yes
	XOR     AL,AL
	STC
	JMP     PORTER                  ;no, it is an error
;
FFC_1:  CALL    LOAD_LINE_STATUS        ;AH=line status
	MOV     BX,[IB_EPTR]            ;get input buffer pointer
	CMP     [IB_IPTR],BX            ;is there a character available?
	JNZ     FFC_2                   ;yes, return it to caller
	XOR     AL,AL
	STC
	JMP     PORTXT                  ;no, get out
;
FFC_2:  MOV     AL,[BX]                 ;get character
	CLC
	JMP     PORTXT
;
FFD_INFO:
	CMP     AL,02h                  ;is it legal?
	JZ      FFD_1                   ;yes
	JMP     PORTER                  ;no good
;
FFD_1:  MOV     CX,[IB_IPTR]            ;yes...
	SUB     CX,[IB_EPTR]            ;calculate # bytes now in buffer
	JNS     PORTXTCX
	ADD     CX,IN_SIZE              ;rounding corner, correct for it
PORTXTCX:
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     BX
	ADD     SP,2                    ;don't abuse CX
	IRET                            ;return to caller
;
FFF_READBLOCK:
	CMP     AL,02h                  ;is this legal?
	JZ      FFF_1                   ;yes
	JMP     PORTER                  ;no good
;
FFF_1:  CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     FFF_1A                  ;yes
	XOR     CX,CX                   ;didn't read anything
	JMP     PORTXTCX                ;get out with CX=0
;
FFF_1A: POP     ES
	PUSH    ES
	MOV     DI,BX                   ;ES:DI->his buffer
	CLD                             ;for saving
	XOR     DX,DX                   ;clear counter
	MOV     SI,[IB_EPTR]            ;get extraction pointer
FFF_2:  CMP     [IB_IPTR],SI            ;are we done?
	JZ      FFF_X                   ;yes, so get out
	MOVSB                           ;move byte over
	CMP     SI,[IB_END]             ;are we at the corner?
	JNZ     FFF_3                   ;no
	MOV     SI,[IB_START]           ;yes, wrap
FFF_3:  INC     DX                      ;we did a character
	LOOP    FFF_2                   ;do all we can
FFF_X:  MOV     [IB_EPTR],SI
	CMP     [INPUT_FLOW],0          ;have we lowered RTS?
	JZ      FFF_X2                  ;no - all done
	MOV     BX,[IB_IPTR]            ;yes...
	SUB     BX,[IB_EPTR]            ;calculate # bytes now in buffer
	JNS     FFF_X1
	ADD     BX,IN_SIZE              ;rounding corner, correct for it
FFF_X1: CMP     BX,200                  ;are we less than 200 bytes used?
	JAE     FFF_X2                  ;no - still halt input
	MOV     [INPUT_FLOW],0          ;yes, indicate we raised RTS again
	OR      [MODEM_CR],02h          ;raise "CTS" to other end
	CALL    SEND_CONTROL            ;negotiate with other end
FFF_X2: MOV     CX,DX                   ;get count to AX
	JMP     PORTXTCX
;
	PAGE
;-------------------------------------------------------------------
;    Subroutine to load LINE_STATUS into AH
;
;   On entry: SI = byte index for port (0=COM1, 1=COM2)
;
;   On exit:  AH = status as follows:
;                  Bit 7 = timed out (other bits meaningless if set)
;                  Bit 6 = Xmit Shift Register Empty
;                  Bit 5 = Xmit Holding Register empty
;                  Bit 4 = break detected
;                  Bit 3 = framing error
;                  Bit 2 = parity error
;                  Bit 1 = overrun error
;                  Bit 0 = data ready to read
;
LOAD_LINE_STATUS:
	PUSH    BX                      ;save BX
	PUSH    AX                      ;and AX
	PUSH    SI
	MOV     SI,OFFSET SEND_ECB_QUEUE ;init for "shift reg" check
	XOR     AH,AH                   ;clear all bits initially
;
;  Check for Receive Data Available
;
	MOV     BX,[IB_IPTR]            ;get Input Insertion Pointer
	CMP     [IB_EPTR],BX            ;is data available?
	JZ      LLS1                    ;no - pointers the same
	OR      AH,01h                  ;yes, set RDA status bit
;
;  Now check for Xmit "holding register" empty
;
LLS1:   MOV     BX,[OB_IPTR]            ;get output insertion pointer
	INC     BX
	CMP     [OB_END],BX             ;is it at end?
	JNZ     LLS2                    ;no
	MOV     BX,[OB_START]           ;yes, "turn corner"
LLS2:   CMP     [OB_EPTR],BX            ;is output buffer full?
	JZ      LLS3                    ;yes, "THR" is full
	OR      AH,20h                  ;no, "THR" is empty
;
;  Now check for Xmit "shift register" empty
;
LLS3:   CMP     BYTE PTR[SI],02h        ;any un-ackd dta pkts?
	JZ      LLS4                    ;yes, leave bit low
	INC     SI
	CMP     SI,OFFSET SEND_ECB_QUEUE+4
	JBE     LLS3                    ;check all entries
	OR      AH,40h                  ;"Shift register" is empty
LLS4:   POP     SI
	POP     BX                      ;original AL to BL
	MOV     AL,BL                   ;restore caller's AL
	POP     BX                      ;and caller's BX
	RET
;
;-------------------------------------------------------------------
;    Subroutine to load MODEM_STATUS into AL
;
;   On entry: SI = byte index for line
;
;   On Exit:  AL = modem status:
;                  Bit 7 = Carrier Detect (CD)
;                  Bit 6 = Ring Indicator (RI)
;                  Bit 5 = Data Set Ready (DSR)
;                  Bit 4 = Clear To Send (CTS)
;                  Bit 3 = CD changed since last status
;                  Bit 2 = RI changed since last status
;                  Bit 1 = DSR changed since last status
;                  Bit 0 = CTS changed since last status
;
LOAD_MODEM_STATUS:
	MOV     AL,[MODEM_SR]           ;get "UART" Modem Status
	AND     [MODEM_SR],0F0h         ;reset "delta" bits
	RET
;
;-------------------------------------------------------------------
;    START_XMIT
;
;    This routine will start output if it is not now active,
;    If CTS allows it, and if there is data waiting to be sent.
;
;    On entry, SI = word index of port (0 or 2)
;
;
START_XMIT:
	PUSH    AX                      ;save AX & BX
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    BP
;
;   See if output already running, or if CTS is holding it off
;
	CLI                             ; *** Inhibit ints ***
	CMP     [XMIT_ACT],0            ;is output running?
	JNZ     SXM1                    ;yes, all done
	TEST    [MODEM_SR],10h          ;does CTS allow output now?
	JZ      SXM1                    ;no, all done
	MOV     AX,[OB_IPTR]            ;CX=insertion ptr
	CMP     AX,[OB_EPTR]            ;get extraction pointer
	JZ      SXM1                    ;nothing to send
;
;  We have data to send, so start it going.  To do this, we will
;  first post an AES IPX event to delay 1 55ms tick, and then
;  start sending data as we find it.
;
	MOV     [XMIT_ACT],1            ;say output routine active
	STI                             ; *** Allow ints ***
	CALL    GET_SEND_ECB            ;DS:SI -> send ECB/buffer
	JNC     SXM1A
	INT     3                       ;should never happen!!
SXM1A:  MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SEND_ESR ;to send next block
	PUSH    ES                      ;!!! THis call kills ES: !!!
	MOV     BX,05h                  ;Schedule IPX event
	MOV     AX,1                    ;1 "tick" from now
	CALL    [IPX_VECTOR]            ;and proceed
	POP     ES                      ;restore ES:
SXM1:   STI                             ;*** allow ints if off ***
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX                      ;restore registers
	POP     AX
	RET                             ;exit
;
;----------------------------------------------------------------
;   ESR routine to send data.  Entered from timer or end-action
;   of previous send pkt ECB.
;----------------------------------------------------------------
;
;  On entry: ES:SI -> ECB which is free to use to send data
;
;   Ok to start output, is there anything to send?
;
SEND_ESR:
	PUSH    ES
	POP     DS                      ;set DS: -> ES: -> us
SESR_TOP:
	TEST    [MODEM_SR],10h          ;does CTS allow output now?
	JZ      SESRFC                  ;no, stop here!
	CLI                             ;assure ints off (they should be)
	MOV     CX,[OB_IPTR]            ;CX=insertion ptr
	MOV     BX,[OB_EPTR]            ;get extraction pointer
	SUB     CX,BX                   ;anything waiting to go?
	JNZ     SESR1                   ;yes, send next block
;
;  We've emptied the send queue - release ECB and get out
;
SESRFC: CALL    POINT_TO_SND_QUE        ;DS:BX = queue index
	MOV     [SEND_ECB_QUEUE][BX],0  ;release the ECB
	MOV     [XMIT_ACT],0            ;say no longer active
	RETF
;
;  Data to go, CX has size (maybe reversed)
;
SESR1:  JNS     SESR2                   ;no wrapping
	ADD     CX,OUT_SIZE             ;CX = # bytes to send
SESR2:  CMP     CX,512
	JBE     SESR3
	MOV     CX,512                  ;max pkt size
;
;   CX=# to send, BX=fetch pointer.
;   Load into IPX data packet and send it out
;
SESR3:  MOV     AX,CX                   ;size of packet
	PUSH    AX                      ;save size
	PUSH    SI                      ;save ECB pointer
	ADD     AX,8                    ;full packet length
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     SI,OFFSET HIS_ADDRESS   ;where to send it
	CALL    INIT_IPX_HEADER         ;init IPX header
	POP     SI                      ;DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;build data packet
	INC     [CIRC_N_SEQ]            ;next seq number
	JNZ     SESR3A                  ;non-zero, ok
	INC     [CIRC_N_SEQ]            ;rolled over - 1 is next seq number
SESR3A: MOV     AX,[CIRC_N_SEQ]
	XCHG    AH,AL                   ;put in (hi-lo) order
	MOV     [DI.TPKT_SEQ],AX        ;packet sequence number
	MOV     [DI.TPKT_CHECKSUM],0    ;zero checksum for now
	MOV     AL,[CIRC_N_NUMBER]      ;get circuit number
	MOV     [DI.TPKT_CIRCUIT],AL    ;set circuit number
	POP     AX                      ;restore length to AX
	MOV     CX,AX                   ;CX = length
	XCHG    AH,AL                   ;put in forward order
	MOV     [DI.TPKT_LEN],AX        ;store data length
	MOV     [DI.TPKT_TYPE],5        ;data packet
	LEA     DI,[DI.TPKT_DATA]       ;DS:DI -> store data
;
;  DS:SI -> ECB
;  DS:DI -> packet store buffer
;  DS:BX -> data to fetch
;     CX = # bytes to send
;
SESR4:  MOV     AL,[BX]                 ;fetch char to send
	INC     BX                      ;bump extraction pointer
	CMP     [OB_END],BX             ;at end?
	JNZ     SESR5                   ;no
	MOV     BX,[OB_START]           ;yes, "turn corner"
SESR5:  MOV     [DI],AL
	INC     DI
	LOOP    SESR4                   ;move all data to packet
	MOV     [OB_EPTR],BX            ;update pointer
	CALL    POINT_TO_SND_QUE        ;DS:BX = offset of queue entry
	MOV     [SEND_ECB_QUEUE][BX],2  ;say it needs an ack
	MOV     [RETRY_CNT3],3          ;try three times to get through
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SEND_ESR2 ;when send completes
	CALL    SEND_IPX                ;send the data packet
	RETF
;
;====================================================================
;
;  IPX end-action comes here when send releases ECB
;
;  ES:SI -> ECB
;--------------------------------------------------------------------
SEND_ESR2:
	PUSH    ES
	POP     DS                      ;set up DS:
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SXMTO ;set up timeout...
	MOV     BX,05h                  ;Schedule IPX event
	MOV     AX,90                   ;5 seconds from now
	CALL    [IPX_VECTOR]            ;and hope for better luck
	RETF
;
; Called from receive ACK routines...
;
CONTINUE_XMIT:
	CALL    GET_SEND_ECB            ;DS:SI -> send ECB/buffer
	JNC     ESR2C
	INT     3                       ;should never happen!!
ESR2C:  JMP     SESR_TOP                ;see if more to send
;
;  Timed Out - resend oldest waiting one
;
SXMTO:  PUSH    ES
	POP     DS                      ;set DS=ES=CODE
	DEC     [RETRY_CNT3]            ;any retries left?
	JZ      SXMBC                   ;no, server must be gone...
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SEND_ESR2 ;when send completes
	CALL    SEND_IPX_NOW            ;resend packet
	RETF                            ;ESR will go to SEND_ESR2
;
;  We can't get a packet through, so end our connection.
;
SXMBC:  CALL    RELEASE_SEND_ECB        ;release the ECB
	MOV     [CIRC_N_OPEN],0         ;mark circuit closed
	MOV     [NOW_CONNECTED],0       ;end of connection
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AX,40B0h                ;RI only in AH, reset in AL
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;whatever used to be high has chgd
	OR      AL,AH                   ;restore RI
	MOV     [MODEM_SR],AL           ;set CD, DSR, CTS lowered now
	MOV     [MODEM_CR],0            ;set DTR and RTS low
	MOV     [XMIT_ACT],0            ;say no longer active
	MOV     BX,OFFSET PD_TIMEOUT    ;lost server
	CALL    STUFF_PDS               ;say so if not quiet...
	RETF
;
;-------------------------------------------------------------------
;  SEND_CONTROL - Send IPX control packet to server
;
;  On exit: NC = all is well
;            C = server timed out, all cells reset
;-------------------------------------------------------------------
SCERRJ: JMP     SCERR
;
SEND_CONTROL:
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    BP
	PUSH    AX
	CMP     [NOW_CONNECTED],0
	JZ      SCERRJ                  ;not now connected!
	MOV     [RETRY_CNT1],3          ;try 3 times
SCO5:   MOV     AX,2                    ;2 data bytes in packet
	CALL    GET_SCTL_PKT            ;DS:DI -> ECB, DS:DI-> packet
	JC      SCERR                   ;can't get one
	MOV     AH,0                    ;ACK=no (we're starting things)
	MOV     AL,[MODEM_CR]
	ROR     AL,1
	ROR     AL,1                    ;position RTS=7, CD=6
	AND     AL,0C0h                 ;isolate them
	OR      AL,20h                  ;set circuit up bit
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
	MOV     [DI.TPKT_TYPE],4        ;type=control
	MOV     [CTL_ACK],0             ;reset ack watcher...
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SC6T ;to check for ACK
	CALL    SEND_IPX                ;send packet
	POP     AX                      ;rest happens on ESR level
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	CLC
	RET
;
SCERR:  POP     AX
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	STC
	RET
;
;  ESR on SEND_CONTROL to wait for an ACK
;
SC6T:   PUSH    ES
	POP     DS                      ;get regs together
	PUSH    ES
	STI                             ;allow ints from here...
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]            ;to let things clear out
	POP     ES
	MOV     AX,500                  ;5 second timeout
	MOV     DI,OFFSET TIMER2
	CALL    START_TIMER             ;start up timer
;
;  Now wait for an ACK
;
SCO6:   CMP     [CTL_ACK],0             ;have we gotten a response?
	JNZ     SCO6P234                ;yes, handle it
	MOV     DI,OFFSET TIMER2        ;no, timed out?
	CALL    CHECK_TIMER             ;let's see
	JC      SCO7                    ;yes, timed out
	STI
	PUSH    ES
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]
	POP     ES
	CLI
	JMP     SCO6                    ;wait for one or the other
;
;  State: CO7 - COWtRty
;
;  Timed out, no response from server
;
SCO7:   DEC     [RETRY_CNT1]
	JNZ     SCO5R                   ;more tries remain
	CALL    RELEASE_SEND_ECB        ;release the ECB
	MOV     [CIRC_N_OPEN],0         ;mark circuit closed
	MOV     [NOW_CONNECTED],0       ;end of connection
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AX,40B0h                ;RI only in AH, reset in AL
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;whatever used to be high has chgd
	OR      AL,AH                   ;restore RI
	MOV     [MODEM_SR],AL           ;set CD, DSR, CTS lowered now
	MOV     [MODEM_CR],0            ;set DTR and RTS low
	MOV     [XMIT_ACT],0            ;say no longer active
	MOV     BX,OFFSET PD_TIMEOUT    ;lost server
	CALL    STUFF_PDS               ;say so if not quiet...
	RETF                            ;server timed out
;
;  SCO5R - Resend the packet w/current info
;
SCO5R:  LDS     DI,[SI.FRAG2_ADDR]      ;DS:DI -> packet data
	MOV     AH,0                    ;ACK=no (we're starting things)
	MOV     AL,[MODEM_CR]
	ROR     AL,1
	ROR     AL,1                    ;position RTS=7, CD=6
	AND     AL,0C0h                 ;isolate them
	OR      AL,20h                  ;set circuit up bit
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
	MOV     [DI.TPKT_TYPE],4        ;type=control
	CLI                             ;turn off ints here...
	CALL    SEND_IPX_NOW            ;resend the packet
	RETF                            ;we'll come back at SCT6
;
;  State: CO6.2, CO6.3, or CO6.4
;
SCO6P234: TEST  [CIRC_N_RCTL],20h       ;is circuit marked up?
	JNZ     SCO6P2                  ;yes, we got it ok
;
;  Mark Circuit closed and exit...
;
	MOV     [CIRC_N_OPEN],0         ;mark circuit closed
	MOV     [NOW_CONNECTED],0       ;end of connection
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AX,40B0h                ;RI only in AH, reset in AL
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;whatever used to be high has chgd
	OR      AL,AH                   ;restore RI
	MOV     [MODEM_SR],AL           ;set CD, DSR, CTS lowered now
	MOV     [MODEM_CR],0            ;set DTR and RTS low
SCO6P2: CALL    RELEASE_SEND_ECB        ;let go of the ECB
	RETF
;
;
	PAGE
;--------------------------------------------------------------------
;  Get and Return Send ECBs to queue
;
;  On Exit: DS:SI -> ECB to release or C set to indicate none avail
;--------------------------------------------------------------------
GET_SEND_ECB:
	PUSH    AX
	PUSH    BX
	PUSH    CX
	MOV     AX,CS
	MOV     BX,DS
	CMP     AX,BX
	JZ      DBG1
	INT     3
DBG1:   MOV     BX,ES
	CMP     AX,BX
	JZ      DBG2
	INT     3
DBG2:
	MOV     CX,5                    ;5 send ECBS total
	MOV     SI,OFFSET SECB1         ;start w/1st one
	MOV     BX,OFFSET SEND_ECB_QUEUE;DS:BX -> queue
G_S_E1: PUSHF
	CLI                             ;  *** Assure Ints Inhibited ***
	CMP     BYTE PTR[BX],0          ;is it avail?
	JZ      G_S_E2                  ;yes
	POPF                            ; *** restore ints as they were ***
	ADD     SI,ECB_SIZE             ;no, try next one
	INC     BX
	LOOP    G_S_E1
	POP     CX
	POP     BX
	POP     AX
	STC
	RET
;
G_S_E2: CMP     [SI.IN_USE_FLAG],0      ;is it in use?
	JZ      DBG77                   ;no
	INT     3
DBG77:  MOV     BYTE PTR [BX],1         ;assign to us
	POPF                            ; *** restore ints as they were ***
	MOV     [SI.FRAG2_SIZE],520     ;open up to max data size
	MOV     WORD PTR[SI.ESR_ADDR+2],0 ;clear out any ESR
	MOV     WORD PTR[SI.ESR_ADDR],0 ;from last user
	POP     CX
	POP     BX
	POP     AX
	CLC                             ;indicate one assigned
	RET
;
;--------------------------------------------------------------------
;  On Entry:  DS:SI -> ECB to release
;
;   On Exit: NC = released, C = not send ECB
;--------------------------------------------------------------------
RELEASE_SEND_ECB:
	PUSH    AX
	PUSH    BX
	PUSH    DX
	CALL    POINT_TO_SND_QUE        ;DS:BX = offset of queue entry
	JC      R_S_ERR                 ;invalid ECB address!
	MOV     [SEND_ECB_QUEUE][BX],0  ;release it
	CLC
	POP     DX
	POP     BX
	POP     AX
	RET
;
R_S_ERR:STC
	POP     DX
	POP     BX
	POP     AX
	RET
;
;--------------------------------------------------------------------
;  Given send ECB offset, return ECB QUEUE index
;
;  On Entry DS:SI -> Send ECB
;
;  On Exit:  BX = 0-n byte index of that entry if NC set
;               C set if DS:SI invalid send queue ECB
;
POINT_TO_SND_QUE:
	PUSH    AX
	PUSH    DX
	MOV     AX,SI
	SUB     AX,OFFSET SECB1         ;make relative to list
	JC      PTSQE
	MOV     BX,ECB_SIZE
	XOR     DX,DX
	DIV     BX                      ;make ECB number 0-4
	OR      DX,DX                   ;was it exact?
	JNZ     PTSQE                   ;no
	MOV     BX,AX                   ;ECB index to BX
	CMP     BX,4
	JA      PTSQE                   ;not ECB 0-4
	POP     DX
	POP     AX
	CLC                             ;BX = entry #
	RET
;
PTSQE:  POP     DX
	POP     AX
	STC                             ;invalid send queue entry
	RET
;
;--------------------------------------------------------------------
;  Given send ECB queue index, return send ECB offset
;
;  On Entry  BX = 0-n byte index of send ECB queue entry
;
;  On Exit:  DS:SI -> Send ECB if NC set
;               C set if invalid queue index presented.
;
POINT_TO_SND_ECB:
	PUSH    AX
	PUSH    DX
	CMP     BX,4                    ;0-4 is max for now
	JA      PTSEE                   ;error
	MOV     AX,ECB_SIZE             ;size of an ECB
	MUL     BX                      ;times index
	ADD     AX,OFFSET SECB1         ;AX=ECB offset
	MOV     SI,AX                   ;return it in SI
	POP     DX
	POP     AX
	CLC                             ;BX = entry #
	RET
;
PTSEE:  POP     DX
	POP     AX
	STC                             ;invalid send queue entry
	RET
;
;===================================================================
;  Routine called from 2nd call to TIPXI14 with /u option
;  to shut down in preparation for unloading from memory
;--------------------------------------------------------------------
TSR_UNLOAD:
	PUSH    DS
	MOV     AX,CS
	MOV     DS,AX
	MOV     ES,AX                   ;DS:=ES:=us
	CALL    RELEASE_CIRCUIT         ;shut down circuit if open
	CALL    CLOSE_SOCKETS           ;close any open socket
	LDS     DX,[IRQ14VEC]           ;DS:DX = old IRQ 14 vector
	MOV     AX,2514h                ;restore INT 14 vector
	INT     21h                     ;debug, don't take over yet
	MOV     ES,CS:[PSPSEG]          ;return w/ES: for memory release
	POP     DS                      ;restore caller's DS
	RETF                            ;go back and release mem
;
TSR_RELEASE:
	PUSH    DS
	PUSH    ES
	MOV     AX,CS
	MOV     DS,AX
	MOV     ES,AX                   ;DS:=ES:=us
	CALL    RELEASE_CIRCUIT         ;shut down circuit if open
	POP     ES
	POP     DS
	RETF
;
;--------------------------------------------------------------------
;  Close socket if it is open.
;--------------------------------------------------------------------
CLOSE_SOCKETS:
	PUSH    ES
	MOV     BX,01h                  ;close socket
	XOR     DX,DX
	XCHG    DX,WORD PTR [SOCKET_ADDRESS] ;fetch and clear
	OR      DX,DX
	JZ      CSEX1                   ;not open
	CALL    [IPX_VECTOR]
CSEX1:  CMP     [OPEN_452],0            ;is socket 452 open?
	JZ      CSEX2                   ;no, done
	MOV     BX,01h
	MOV     DX,5204h                ;yes, close it
	CALL    [IPX_VECTOR]
CSEX2:  POP     ES
	RET
;
;--------------------------------------------------------------------
;  INIT IPX HEADER to Send Packet
;
;  On Entry:  DS:DI -> Header structure
;             DS:SI -> destination net/node/socket address
;                AX = Data block length
;       [NETWORK_ADDRESS] contains our net/node/socket address
;
;  On Exit: IPX Header is ready to send...
;--------------------------------------------------------------------
INIT_IPX_HEADER:
	PUSH    AX
	PUSH    BX
	PUSH    CX
	PUSH    SI
	PUSH    DI
	MOV     [DI.CHECKSUM],0FFFFh    ;init checksum
	ADD     AX,30                   ;add IPX header to data length
	XCHG    AH,AL                   ;put in forward order in hdr...
	MOV     [DI.HDR_LEN],AX         ;set total length in header
	MOV     [DI.TRANSPORT],0
	MOV     [DI.PKT_TYPE],4         ;packet service type
	LEA     DI,[DI.DEST]            ;point to destination loc
	PUSH    ES
	PUSH    DS
	POP     ES                      ;ES: -> DS:
	MOV     CX,12                   ;move 12 bytes
	CLD                             ;set direction  
	REP     MOVSB                   ;move in destination adr
	MOV     SI,OFFSET NETWORK_ADDRESS
	MOV     CX,12
	REP     MOVSB                   ;move in source address
	POP     ES
;
	POP     DI
	POP     SI
	POP     CX
	POP     BX
	POP     AX
	RET
;
;--------------------------------------------------------------------
;  SEND IPX packet.
;
;  On Entry: DS:SI -> ECB to send.  ECB must have socket, Frag Count
;                     and Descriptor fields, and ESR address set.
;
;  Note: Call SEND_IPX if ECB immediate address is not set
;        Call SEND_IPX_NOW if ECB immediate address already set
;
;   On Exit: NC = packet sent, no error.
;             C = error, AX = error code as follows:
;                               0 = Application Canceled the send request
;                               1 = Packet is Malformed
;                               2 = Packet is undeliverable
;                               3 = Unable to send due to network or hdw fail
;                               4 = Unknown Error Code
;                               5 = Get Target Address Failure
;--------------------------------------------------------------------
SEND_IPX_NOW:                           ;entry if ECB immed adr already set
	PUSH    BX
	PUSH    AX
	PUSH    CX
	PUSH    DX
	PUSH    BP
	PUSH    DI
	PUSH    SI
	PUSH    ES
	MOV     AX,DS
	MOV     ES,AX                   ;ES:SI -> ECB
	JMP     S_I_I1                  ;send the packet
;
SEND_IPX:                               ;entry when ECB immed adr not set
	PUSH    BX
	PUSH    AX
	PUSH    CX
	PUSH    DX
	PUSH    BP
	PUSH    DI
	PUSH    SI
	PUSH    ES
	MOV     AX,DS
	MOV     ES,AX                   ;ES:SI -> ECB
;
;  Set ECB immediate address field from IPX header's destination address...
;
	PUSH    DS
	PUSH    SI                      ;save ECB pointer
	LEA     DI,[SI.IMMEDIATE_ADDR]  ;ES:DI -> ECB immediate address
	MOV     SI,OFFSET HIS_IMMED_ADDRESS ;get his immediate address
	MOV     CX,6
	CLD
	REP     MOVSB                   ;put it in ECB
	POP     SI                      ;ES:SI -> ECB to send
	POP     DS                      ;restore DS:=ES:-> ECB
	JMP     S_I_I1                  ;send the packet
;
SEND_IPX_FORCE:                         ;entry when ECB immed adr not known
	PUSH    BX
	PUSH    AX
	PUSH    CX
	PUSH    DX
	PUSH    BP
	PUSH    DI
	PUSH    SI
	PUSH    ES
	MOV     AX,DS
	MOV     ES,AX                   ;ES:SI -> ECB
;
;  Set ECB immediate address field from IPX header's destination address...
;
	PUSH    DS
	PUSH    SI                      ;save ECB pointer
	LEA     DI,[SI.IMMEDIATE_ADDR]  ;ES:DI -> ECB immediate address
	LDS     SI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header buffer
	LEA     SI,[SI.DEST]            ;DS:SI -> destination address
	PUSH    ES
	MOV     BX,02h                  ;do IPX get local target
	CALL    [IPX_VECTOR]            ;convert to immediate NIC address in ECB
	POP     ES
	OR      AL,AL
	JNZ     BAD_XLATE               ;error from Get Target Address
;
	POP     SI                      ;ES:SI -> ECB to send
	POP     DS                      ;restore DS:=ES:-> ECB
;
;  Enter here if immediate address already set...
;
S_I_I1: PUSH    DS
	PUSH    SI                      ;save ECB pointer
	LDS     SI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     AX,[SI.HDR_LEN]         ;get packet length
	XCHG    AH,AL                   ;put in 8088 order
	POP     SI
	POP     DS
	SUB     AX,30                   ;remove header length
	JC      BAD_SIZE                ;bad IPX header size
	CMP     AX,520
	JA      BAD_SIZE                ;bad IPXheader size
	MOV     [SI.FRAG2_SIZE],AX      ;set ECB data packet size for xmit
;
	IF      DEBUG GT 0
	PUSH    CX
	PUSH    SI
	PUSH    DI
	MOV     SI,WORD PTR[SI.FRAG2_ADDR] ;point to packet data
	MOV     DI,[DEBUG_PTR]
	MOV     CX,15
	MOV     BYTE PTR[DI],0FFh       ;show send block
	INC     DI
	CLD
	REP     MOVSB                   ;move 1st 15 bytes to log buffer
	CMP     DI,OFFSET DEBUG_PTR
	JB      SIDB1
	MOV     DI,OFFSET DEBUG_BUFFER
SIDB1:  MOV     [DEBUG_PTR],DI          ;update pointer
	POP     DI
	POP     SI
	POP     CX
	ENDIF
;
	MOV     BX,0003h                ;IPX Send
	CALL    [IPX_VECTOR]            ;send it
;
	POP     ES                      ;restore original es
	POP     SI
	POP     DI                      ;all sent
	POP     BP
	POP     DX
	POP     CX
	POP     AX
	POP     BX
	CLC
	RET
;
;  Entry to wait for send complete on ECB in DS:SI
;
WAIT_FOR_SEND:
	PUSH    BX
	PUSH    AX
	PUSH    CX
	PUSH    DX
	PUSH    BP
	PUSH    DI
W_F_S1: CMP     [SI.IN_USE_FLAG],0
	JNZ     W_F_S1                  ;wait for completion
	CMP     [SI.COMPLETION_CODE],0  ;see if any errors
	JNZ     BAD_SEND
	POP     DI
	POP     BP
	POP     DX
	POP     CX
	POP     AX
	POP     BX
	CLC
	RET
;
BAD_SIZE:
	POP     ES
	POP     SI                      ;ES:SI -> restored
	MOV     AL,1                    ;flag malformed packet
	JMP     SHORT B_S_1
;
BAD_XLATE:
	POP     SI                      ;ES:SI -> ECB to send
	POP     DS                      ;restore DS:=ES:-> ECB
	POP     ES                      ;restore original es
	POP     SI                      ;even stack
	MOV     AL,5                    ;error code=5
	JMP     SHORT B_S_1
;
BAD_SEND:
	MOV     AL,[SI.COMPLETION_CODE] ;get code
	SUB     AL,0FCh
	JNC     B_S_1                   ;no bogus code
	MOV     AL,4                    ;bogus non-zero code
B_S_1:  POP     DI
	POP     BP
	POP     DX
	POP     CX
	POP     BX
	MOV     AH,BH                   ;restore AH
	POP     BX
	CLC
	RET
;
;--------------------------------------------------------------------
;
;  Call to calculate the immediate address for HIS_ADDRESS.
;
CALC_IMMEDIATE_ADDRESS:
	PUSH    SI
	PUSH    DI
	PUSH    BX
	PUSH    AX
	MOV     SI,OFFSET HIS_ADDRESS   ;set up for HIS address
	MOV     DI,OFFSET HIS_IMMED_ADDRESS ;to get immediate adr
	PUSH    ES
	MOV     BX,02h                  ;do IPX get local target
	CALL    [IPX_VECTOR]            ;convert to immediate NIC address in ECB
	POP     ES
	OR      AL,AL
	JZ      CIA1                    ;error from Get Target Address
	STC                             ;report error
CIA1:   POP     AX
	POP     BX
	POP     DI
	POP     SI
	RET
;
;--------------------------------------------------------------------
;  IPX Post Listen For Packet
;
;  On Entry:
;     DS:SI -> ECB on which to post a listen.  This ECB must have
;              ESR address, Socket Number, Frag count, and Frag Descriptor
;              fields correctly filled in prior to call.
;
;  On Exit:
;     NC = post made
;      C = Error, AX = error code (0FFh = socket does not exist)
;--------------------------------------------------------------------
IPX_LISTEN:
	PUSH    BX
	PUSH    AX
	PUSH    ES
	PUSH    DS
	POP     ES                      ;ES:SI -> ECB
	MOV     BX,04h                  ;issue Listen
	CALL    [IPX_VECTOR]
	POP     ES                      ;restore ES:
	OR      AL,AL
	JNZ     IPXL_ER                 ;error on listen
	POP     AX
	POP     BX
	CLC                             ;indicate no error+
	RET
;
IPXL_ER:
	POP     BX                      ;burn original AX
	POP     BX                      ;restore BX
	STC                             ;indicate error
	RET
;
;---------------------------------------------------------------------
; Get a packet ready to send.
;
; On Entry:  AX = # bytes in data field of TBBS/IPAD packet
;
; On Exit:   DS:SI -> ECB
;            DS:DI -> IPX data packet (TBBS/IPAD packet)
;
;       TPKT_LEN is set
;       TPKT_SEQ is set
;       TPKT_CIRCUIT is set to CIRC_N_NUMBER
;       TPKT_CHECKSUM is set to 0
;
;  All other regs preserved...   
;---------------------------------------------------------------------
;  Get control packet for Circuit N
;
GET_SCTL_PKT:
	PUSH    AX
	CALL    GET_SEND_ECB            ;DS:DI -> ECB
	JC      GSPX                    ;can't get one
	PUSH    SI                      ;and ECB pointer
	ADD     AX,8                    ;full packet length
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     SI,OFFSET HIS_ADDRESS   ;where to send it
	CALL    INIT_IPX_HEADER         ;init IPX header
	POP     SI                      ;DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;build data packet
	INC     [CIRC_N_CSEQ]           ;next seq number
	MOV     AX,[CIRC_N_CSEQ]
	JMP     SHORT GSPCOM
;
;  Get data packet for Circuit N
;
GET_SEND_PKT:
	PUSH    AX                      ;save seq # for now
	CALL    GET_SEND_ECB            ;DS:DI -> ECB
	JC      GSPX                    ;can't get one
	PUSH    SI                      ;and ECB pointer
	ADD     AX,8                    ;full packet length
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     SI,OFFSET HIS_ADDRESS   ;where to send it
	CALL    INIT_IPX_HEADER         ;init IPX header
	POP     SI                      ;DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;build data packet
	INC     [CIRC_N_SEQ]            ;next seq number
	JNZ     GSPSTC                  ;non-zero, all is well
	INC     [CIRC_N_SEQ]            ;turned corner, 1 is next seq number
GSPSTC: MOV     AX,[CIRC_N_SEQ]
GSPCOM: XCHG    AH,AL                   ;put in (hi-lo) order
	MOV     [DI.TPKT_SEQ],AX        ;packet sequence number
	MOV     [DI.TPKT_CHECKSUM],0    ;zero checksum for now
	MOV     AL,[CIRC_N_NUMBER]      ;get circuit number
	MOV     [DI.TPKT_CIRCUIT],AL    ;set circuit number
	POP     AX
	XCHG    AH,AL
	MOV     [DI.TPKT_LEN],AX        ;store data length
	XCHG    AH,AL                   ;put back in 8088 order
	CLC
	RET
;
GSPX:   POP     AX
	STC
	RET
;
;  Get send packet for circuit 0
;
GET_CIRC0_PKT:
	PUSH    AX                      ;save seq # for now
	CALL    GET_SEND_ECB            ;DS:DI -> ECB
	JC      GSPX                    ;can't get one
	PUSH    SI                      ;and ECB pointer
	ADD     AX,8                    ;full packet length
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     SI,OFFSET HIS_ADDRESS   ;where to send it
	CALL    INIT_IPX_HEADER         ;init IPX header
	POP     SI                      ;DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;build data packet
	INC     [CIRC_0_SEQ]            ;next seq number
	MOV     AX,[CIRC_0_SEQ]
	XCHG    AH,AL                   ;put in (hi-lo) order
	MOV     [DI.TPKT_SEQ],AX        ;packet sequence number
	MOV     [DI.TPKT_CHECKSUM],0    ;zero checksum for now
	MOV     [DI.TPKT_CIRCUIT],0     ;set circuit number=0
	POP     AX
	XCHG    AH,AL
	MOV     [DI.TPKT_LEN],AX        ;store data length
	XCHG    AH,AL                   ;put back in 8088 order
	CLC
	RET
;
;-----------------------------------------------
;  Set TIMER for CX*10ms from now
;  On Entry: AX = # 0.01 sec "ticks" to wait
;         DS:DI -> Timer Cells to set (DD)
;
;  On Exit:  Timer value is set
;
START_TIMER:
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    AX
	MOV     DX,10
	MUL     DX              ;ticks*10
	ADD     AX,5            ;round up
	MOV     BX,55
	DIV     BX              ;AX = # of 55ms ticks needed
	PUSH    AX              ;save for now
	PUSH    ES
	MOV     BX,08h
	CALL    [IPX_VECTOR]    ;get current time in ticks
	POP     ES
	POP     CX
	ADD     AX,CX           ;get ending time
	MOV     [DI],AX         ;set timer
	POP     AX
	POP     DX
	POP     CX
	POP     BX
	RET
;
;  Check Timer
;
;  On Entry: DS:DI -> timer to check
;
;  On Exit: NC = not expired, C = expired
;
CHECK_TIMER:
	PUSH    AX
	PUSH    BX
	PUSH    CX
	PUSH    ES
	MOV     BX,08h
	CALL    [IPX_VECTOR]
	POP     ES
	CMP     AX,[DI]
	JB      C_T1            ;go until time reached
	STC                     ;indicate timeout
	POP     CX
	POP     BX
	POP     AX
	RET
;
C_T1:
	CLC                     ;indicate input avail
	POP     CX
	POP     BX
	POP     AX
	RET
;
	PAGE
;-------------------------------------------------------------------
;  When an IPX send call (and a few other calls) are done from a
;  receive packet ESR routine, IPX allows us to re-enter the receive
;  ESR handler.  This is bad, as it makes stack depth unpredictable,
;  and also means we cannot assure we finish handling one packet
;  before we begin handling another.  To avoid this, any ESR entry
;  attempt on a receive packet is deferred until the end of the
;  current packet handling at ESR_EXIT.
;
;  On Entry: DS:SI -> ECB to defer
;
;  Deferred ECBs are marked in the RCV_ECB_QUEUE list as needing
;  processing, and RCV_RE_ENT is counted up by the number of
;  deferred packets.  Thus RCV_RE_ENT > 1 at ESR_EXIT means that
;  there are deferred packets.
;
RCV_IPX_DEFER:
	MOV     [RCV_RE_ENT],AX         ;restore # deferred counter
	MOV     BX,AX
	DEC     BX                      ;make 0-n index
	SHL     BX,1                    ;make word index
	MOV     [RCV_ECB_QUEUE][BX],SI  ;store deferred rcv ECB
	INC     [RCV_RE_ENT]            ;show another one deferred
	RETF
;
;-------------------------------------------------------------------
;   IPX packet receive state machine handler
;
;   On Entry: ES:SI -> ECB w/rcvd data packet
;
RCV_IPX: PUSH   ES
	POP     DS                      ;get DS set up

;
;  Look at the received packet to see if it looks like a SAP response.
;
	MOV     AX,1
	XCHG    [RCV_RE_ENT],AX         ;test re-entrancy
	OR      AX,AX
	JNZ     RCV_IPX_DEFER           ;re-entered, defer the work
;
;  Process next rcv ECB, DS:SI -> ECB
;
RCV_CYC:
	LDS     DI,ES:[SI.FRAG1_ADDR]   ;DS:BX -> incoming IPX header
	MOV     CX,[DI.HDR_LEN]         ;get packet length
	XCHG    CH,CL                   ;put in 8088 order
	LDS     BX,[SI.FRAG2_ADDR]      ;DS:BX -> incoming packet data
	IF      DEBUG GT 0
	PUSH    CX
	PUSH    SI
	PUSH    DI
	MOV     SI,BX
	MOV     DI,[DEBUG_PTR]
	MOV     CX,15
	MOV     BYTE PTR[DI],0FEh       ;show rcvd block
	INC     DI
	CLD
	REP     MOVSB                   ;move 1st 15 bytes to log buffer
	CMP     DI,OFFSET DEBUG_PTR
	JB      RIDB1
	MOV     DI,OFFSET DEBUG_BUFFER
RIDB1:  MOV     [DEBUG_PTR],DI          ;update pointer
	POP     DI
	POP     SI
	POP     CX
	ENDIF
	SUB     CX,30                   ;CX = data size in bytes
	JC      RCVPKE                  ;bad size word
	PUSH    SI
	PUSH    CX
	LEA     DI,[DI.SOURCE]          ;assure we aren't seeing our own stuff
	MOV     SI,OFFSET NETWORK_ADDRESS ;in SAP broadcast if
	MOV     CX,12                   ;we are set up as both client & server!
	CLD
	REPZ    CMPSB
	POP     CX
	POP     SI
	JZ      RCVPKE                  ;if so, discard packet
;
;  CX = data size (0-n) of this packet
;
;  See if it could be a TBBS/IPAD protocol packet, or other known types
;
	CMP     CX,9
	JB      RCVPKE                  ;not legal packet
	CMP     [BX.TPKT_TYPE],5        ;see if it could be ours
	JA      RCVNT                   ;not TBBS/IPAD protocol packet
	MOV     AX,CX
	SUB     AX,8                    ;our fixed length
	XCHG    AH,AL                   ;put in forward order
	CMP     [BX.TPKT_LEN],AX        ;check data length
	JZ      RCVTI                   ;TBBS/IPAD protocol packet...
;
;  Not TBBS/IPAD protocol packet, see if SAP response pkt...
;
RCVNT:  CMP     CX,66                   ;was it a SAP response?
	JAE     RCVSAP                  ;could be
RCVPKE: CALL    IPX_LISTEN              ;discard the packet...
;
;  Look for any deferred incoming ECB's
;
ESR_EXIT:
	PUSH    ES
	MOV     BX,0Ah
	CALL    [IPX_VECTOR]            ;release time to IPX
	POP     ES
	CMP     [RCV_RE_ENT],1          ;any deferred?
	JZ      ESE1X                   ;no, all done
	STI                             ;Allow Interrupts
	MOV     BX,OFFSET RCV_ECB_QUEUE
	MOV     SI,[BX]                 ;SI = next ECB to handle
	CLI                             ;hold off ints
	MOV     CX,4
ESE1:   MOV     AX,[BX][2]              ;shuffle list down one
	MOV     [BX],AX
	ADD     BX,2                    ;point to next entry
	LOOP    ESE1                    ;shuffle all waiting down
	DEC     [RCV_RE_ENT]            ;count it out
	LDS     DI,ES:[SI.FRAG1_ADDR]   ;DS:BX -> incoming IPX header
	JMP     RCV_CYC                 ;look like another
;
ESE1X:  MOV     [RCV_RE_ENT],0          ;say RCV ESR not running
	RETF                            ;finally all done
;
;  SAP announcement packet is all this can be that we want now...
;
RCVSAP: CMP     CX,450                  ;max SAP packet size
	JA      RCVPKE                  ;not SAP packet
	CMP     WORD PTR[BX],0200h      ;it is type 2 response?
	JZ      RCVSAP1
	CMP     WORD PTR[BX],0400h      ;is it type 4 response?
	JNZ     RCVPKE                  ;no, ignore try again
RCVSAP1: CMP    WORD PTR[BX+2],9191h    ;is it our type of server?
	JNZ     RCVPKE                  ;no, ignore try again
	PUSH    CX
	SUB     CX,2                    ;size-2 must be evenly divisible by 64
	TEST    CX,3Fh                  ;see if it is
	POP     CX                      ;restore true count
	JNZ     RCVPKE                  ;not an allowable size for SAP pkt
	CALL    LOOKUP_SERVER_IPX       ;see if it is one we want
	JC      RCVPKE                  ;no - discard it
	MOV     [SAP_RESP_IN],SI        ;save ECB with response
	JMP     ESR_EXIT                ;all done here
;
;------------------------------------------------------------
;  We received a TBBS/IPAD protocol packet, process it here...
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RCVTI:  MOV     AL,[BX.TPKT_CIRCUIT]    ;circuit 0 or "n"?
	OR      AL,AL
	JZ      RCVT0                   ;circuit 0
	JMP     RCVTN                   ;circuit "n"
;
;------------------------------------------------------------
;  Circuit 0 received message
;------------------------------------------------------------
RCVT0:  MOV     AL,[BX.TPKT_TYPE]       ;get packet type
	OR      AL,AL                   ;type=0?
	JZ      RCVSO0                  ;yes, handle if we're a server
	DEC     AL                      ;type=1?
	JZ      RCVPCO2                 ;yes, post if CO2 is waiting
	DEC     AL                      ;type=2?
	JZ      RCVDPK                  ;yes, discard ACK
	DEC     AL                      ;type=3?
	JNZ     RCVNPE                  ;no - NAK - protocol error
RCVDPK: CALL    IPX_LISTEN              ;release rcv packet
	JMP     ESR_EXIT
;
;------------------------------------------------------------------------
;  We received a circuit response packet on channel 0
;------------------------------------------------------------------------
RCVPCO2:
	MOV     DX,[BX.TPKT_SEQ]        ;get seq number
	XCHG    DH,DL                   ;in 8088 order
	MOV     AL,[BX.TPKT_DATA]       ;get circuit number
	CMP     [CIRC_N_NUMBER],0FFh
	JNZ     RCPC02X                 ;not waiting for one, discard
	MOV     [CIRC_N_NUMBER],AL      ;set channel assigned
	MOV     [CIRC_0_NEXT],DX        ;and seq # to ACK on channel 0
RCPC02X: CALL   IPX_LISTEN              ;release the ECB
	JMP     ESR_EXIT
;
RCVNPD: POP     AX                      ;even stack
	JMP     ESR_EXIT                ;all done
;
;------------------------------------------------------------
;  TYPE 4 (CCTL) and TYPE 5(DATA) are also NAK - protocol error
;------------------------------------------------------------
RCVNPE: MOV     AX,[BX.TPKT_SEQ]        ;get sequence # of packet
	PUSH    AX                      ;save seq # being NAK'd
	CALL    IPX_LISTEN              ;release this rcv ECB
	MOV     AX,4                    ;4 data bytes
	CALL    GET_CIRC0_PKT           ;DS:SI->ECB, DS:DI ->pkt to send with
	JC      RCVNPD                  ;can't get one, ignore it
	CALL    POINT_TO_SND_QUE        ;BX = queue index
	MOV     [DI.TPKT_SEQ],AX        ;store packet sequence number
	MOV     [DI.TPKT_TYPE],3        ;NAK packet type
	POP     AX                      ;get SEQ # being NAK'd
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in NAK packet
	MOV     WORD PTR[DI.TPKT_DATA+2],0200h ;protocol error=reason code
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
	JMP     ESR_EXIT                ;dummy for now
;
;------------------------------------------------------------
;   We received a Type=0 request to open a channel.
;   If we're a server and idle, then give him circuit #1
;------------------------------------------------------------
RCVSO0: PUSH    SI
	LDS     SI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX Header
	LEA     SI,[SI.SOURCE]          ;DS:SI -> source address
	MOV     DI,OFFSET SAVE_HIS_ADDRESS ;stash it here
	MOV     CX,12
	CLD
	REP     MOVSB                   ;move out return address
	POP     SI
	MOV     AX,[BX.TPKT_SEQ]        ;get his seq #
	XCHG    AH,AL
	MOV     [SAVE_HIS_SEQ],AX       ;save it out
	CALL    IPX_LISTEN              ;let go of incoming packet
	CMP     [SEARCHING],0           ;are we in middle of outbound look?
	JNZ     RCVCNA                  ;yes, reject incoming calls...
	CMP     [WE_ARE_SERVER],0       ;are we a server?
	JNZ     SO0                     ;yes
RCVCNA: MOV     BX,0                    ;circuit to give (reject him)
RCVCAV: PUSH    BX
	MOV     AX,3                    ;3 bytes of data in response pkt
	CALL    GET_CIRC0_PKT           ;DS:SI -> ECB, DS>DI -> data pkt
	POP     BX
	JC      RCVSOX                  ;can't get packet
	MOV     [DI.TPKT_DATA],BL       ;set circuit
	MOV     AX,[SAVE_HIS_SEQ]
	XCHG    AH,AL
	MOV     WORD PTR[DI.TPKT_DATA+1],AX ;seq # in pkt
	MOV     [DI.TPKT_TYPE],1        ;Circ response packet type
	PUSH    SI
	PUSH    DI
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:DI -> IPX Header
	LEA     DI,[DI.DEST]            ;DS:DI -> destination address
	MOV     SI,OFFSET SAVE_HIS_ADDRESS ;send to proper IPX address
	MOV     CX,12
	CLD
	REP     MOVSB
	POP     DI
	POP     SI
;
	MOV     AX,OFFSET SND_IPX       ;assume we don't need watchdog here...
	OR      BH,BH                   ;do we?
	JZ      RCNA                    ;no, done
	MOV     AX,OFFSET SO4           ;yes, state SO4 is next
;
RCNA:   MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],AX ;to
	CALL    SEND_IPX_FORCE          ;send IPX packet and release ECB
RCVSOX: JMP     ESR_EXIT
;
;  State: SO0
;
SO0:    MOV     [RETRY_CNT2],3          ;try 3 times to give him circuit
;
;  State SO1:
;
	CMP     [CIRC_N_OPEN],0         ;is circuit avail?
	JNZ     RCVCK1                  ;no, see if duplicate request
SO0A:   MOV     [CIRC_N_OPEN],1         ;yes, show "opening"
	MOV     [CIRC_N_SEQ],0          ;set circuit sequence number-1
	MOV     [CIRC_N_CSEQ],0         ;for control too...
	MOV     [CIRC_N_NEXT],1         ;reset expected rcv seq
	MOV     [LAST_CTL_SEQ],0        ;reset "last" control pkt
	PUSH    SI
	MOV     SI,OFFSET SAVE_HIS_ADDRESS
	MOV     DI,OFFSET HIS_ADDRESS   ;assign it to him
	MOV     CX,12
	CLD
	REP     MOVSB
	CALL    CALC_IMMEDIATE_ADDRESS  ;set up his immediate address
	POP     SI
	OR      [MODEM_CR],03h          ;raise "CTS" and "CD" for other side
	MOV     BX,OFFSET PD_RINGING    ;say we assigned the circuit
	CALL    STUFF_PDS
	MOV     BH,0FFh
	MOV     BL,[CIRC_N_NUMBER]      ;give him our only circuit #
	JMP     RCVCAV                  ;for first time, so start watchdog
;
RCVCK1: CMP     [CIRC_N_OPEN],3         ;is it already fully open?
	JNZ     RCVCK3
	JMP     RCVCNA                  ;yes, this guy can't have it
;
RCVCK3: PUSH    SI
	MOV     SI,OFFSET SAVE_HIS_ADDRESS+4 ;skip network...
	MOV     DI,OFFSET HIS_ADDRESS+4 ;is it repeat request?
	MOV     CX,8
	CLD
	REPZ    CMPSB
	JNZ     RCVCK5                  ;not same guy, turn him down
	MOV     BH,0
	MOV     BL,[CIRC_N_NUMBER]      ;still has it, no new watchdog
	JMP     RCVCAV                  ;send him another "you got it"
;
RCVCK5: POP     SI
	JMP     RCVCNA
;
;------------------------------------------------------------------------
;  Channel assignment process has begun, time it out
;
;  ES:SI -> ECB  Entered on ESR from pkt sent by RCNA above
;------------------------------------------------------------------------
SO4:    PUSH    ES
	POP     DS                      ;get DS: -> us
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SO5 ;for timer expiration
	PUSH    ES                      ;!!! THis call kills ES: !!!
	MOV     BX,05h                  ;Schedule IPX event
	MOV     AX,50                   ;3 seconds from now
	CALL    [IPX_VECTOR]            ;and proceed
	POP     ES                      ;restore ES:
	RETF
;
;  State: SO5  Entered 3 seconds after Assignment sent
;
SO5:    PUSH    ES
	POP     DS                      ;get DS: -> us
	CMP     [CIRC_N_OPEN],3         ;did open finish?
	JZ      SO5X                    ;yes, all done
	DEC     [RETRY_CNT2]            ;no, any retries left?
	JZ      SO7                     ;no, kill the open, it failed
;
;  Try sending response again...
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SO4
	CALL    SEND_IPX_NOW            ;re-send IPX packet and go to S04
	RETF
;
;  Open Failed, clean it up...
;
SO7:    CALL    RELEASE_SEND_ECB        ;release our ECB
	MOV     [CIRC_N_OPEN],0         ;end assignment
	RETF
;
;  Open completed successfully
;
SO5X:   CALL    RELEASE_SEND_ECB        ;release our ECB
	RETF
;
	PAGE
;------------------------------------------------------------
;  Packet received on circuit "n", process it...
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RCVTN:  MOV     AL,[BX.TPKT_TYPE]       ;get packet type
	OR      AL,AL
	JNZ     RCVTN1
;
;------------------------------------------------------------
;  Type 0 rcvd on Circuit n - NAK - protocol error
;------------------------------------------------------------
RCVTN0: MOV     AX,[BX.TPKT_SEQ]        ;get sequence # of packet
	PUSH    AX                      ;save seq # being NAK'd
	CALL    IPX_LISTEN              ;release this rcv ECB
	MOV     AX,4                    ;4 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	JC      RCVTN0X                 ;can't get one, ignore it
	MOV     [DI.TPKT_TYPE],3        ;NAK packet type
	POP     AX                      ;get SEQ # being NAK'd
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in NAK packet
	MOV     WORD PTR[DI.TPKT_DATA+2],0200h ;protocol error=reason code
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
RCVTN0X: JMP    ESR_EXIT
;
RCVTN1: DEC     AL                      ;TYPE=1?
	JZ      RCVTN0                  ;yes, protocol error
	DEC     AL                      ;TYPE=2?
	JNZ     RCVTN3                  ;no
	JMP     RA0                     ;yes, ACK rcvd
;
RCVTN3: DEC     AL                      ;TYPE=3?
	JNZ     RCVTN4                  ;no
	JMP     RN0                     ;yes, NAK rcvd
;
RCVTN4: DEC     AL                      ;TYPE=4?
	JNZ     RCVTN5                  ;no
	JMP     RC0                     ;yes, CCTL rcvd
;
RCVTN5: DEC     AL                      ;TYPE=5?
	JZ      RD0                     ;yes, data packet
	CALL    IPX_LISTEN              ;no, release this rcv ECB
	JMP     ESR_EXIT                ;and discard the packet
;
;------------------------------------------------------------
;  Data packet received
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RD0:    MOV     AX,[BX.TPKT_SEQ]        ;set seq # to NAK
	XCHG    AH,AL                   ;put in 8088 order
	CMP     [CIRC_N_OPEN],0
	JNZ     RD1                     ;circuit is open
	PUSH    AX                      ;save seq # being NAK'd
	CALL    IPX_LISTEN              ;release this rcv ECB
	MOV     AX,4                    ;4 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	JC      RD0NX                   ;can't get one, ignore it
	MOV     [DI.TPKT_TYPE],3        ;NAK packet type
	POP     AX                      ;get SEQ # being NAK'd
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in NAK packet
	MOV     WORD PTR[DI.TPKT_DATA+2],0100h ;circuit closed=reason code
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
RD0NX:  JMP     ESR_EXIT
;
;  Circuit is open, look at incoming data packet
;
RD1:    CMP     [CIRC_N_NEXT],AX        ;is this next expected packet?
	JNZ     RD1P2                   ;no
	CMP     [RCV_HOLDING],0         ;are we in RD2 state?
	JZ      RD1B                    ;no
	JMP     RD2                     ;yes
;
;  AX = packet # in 8088 order...
;
RD1B:   CALL    PROCESS_DATA            ;process its data
	JC      RD1AO                   ;input buffer overrun
	CALL    IPX_LISTEN              ;release the ECB
	INC     [CIRC_N_NEXT]           ;bump next expected
	JNZ     RD1A
	INC     [CIRC_N_NEXT]           ;data never uses seq # 0
RD1A:   CALL    SEND_ACK                ;yes, ACK it (AX=# to ACK)
	JMP     ESR_EXIT
;
RD1AO:  CALL    IPX_LISTEN              ;discard data packet
	JMP     ESR_EXIT
;
;  AX = packet # in 8088 order
;
RD1P2:  MOV     CX,[CIRC_N_NEXT]
	ADD     CX,WINDOW_SIZE-1        ;CX = last window pkt forward
	JC      RD1P2W                  ;wrapped, do the hard way
	CMP     AX,[CIRC_N_NEXT]
	JA      RD1P2C                  ;may be either RD1.2 or RD1.3
RD1P4:  CALL    IPX_LISTEN              ;discard packet
	CALL    SEND_ACK                ;RD1.4, send ACK (AX=pkt # to ACK)
	JMP     ESR_EXIT
;
; window ahead is wrapped, check this out
;
RD1P2W: INC     CX                      ;account for lack of 0 seq #
	CMP     AX,[CIRC_N_NEXT]        ;see if at top of number range
	JA      RD1P2D                  ;yes, in window - hold it
	CMP     AX,CX                   ;see if in window
	JB      RD1P2D                  ;yes, hold pkt
	MOV     CX,[CIRC_N_NEXT]
	SUB     CX,WINDOW_SIZE          ;can't wrap both ways!
	CMP     AX,CX
	JB      RD1P3                   ;RSN < next-win-1
	JMP     SHORT RD1P4             ;older within window, just ack
;
RD1P2C: CMP     AX,CX                   ;RSN > NEXT, see if in window
	JA      RD1P3                   ;no, RD1.3
RD1P2D: CALL    HOLD_PACKET             ;hold out of order pkt
	JMP     ESR_EXIT
;
;  State: RD1.3
;
RD1P3:  CALL    IPX_LISTEN              ;discard packet
	MOV     AX,4                    ;4 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	JC      RD1P3X                  ;can't get one, ignore it
	MOV     [DI.TPKT_TYPE],3        ;NAK packet type
	MOV     AX,[CIRC_N_NEXT]        ;NAK "next" SEQ number
	XCHG    AH,AL
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in NAK packet
	MOV     WORD PTR[DI.TPKT_DATA+2],0300h ;out of window=reason code
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
RD1P3X: JMP     ESR_EXIT
;
;  State: RD2 - holding packets
;
RD2:    JMP     ESR_EXIT                ;### not implemented for now!!
;
;------------------------------------------------------------
;  Send ACK on data packet, AX=seq# to ACK in 8088 order
;
SEND_ACK:
	PUSH    SI
	PUSH    BX                      ;save regs
	PUSH    AX                      ;stack seq #
	XCHG    AH,AL                   ;put in IPX forward order
	PUSH    AX                      ;and stack it...
	MOV     AX,4                    ;4 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	POP     AX                      ;seq # to ACK
	JNC     SADB1
	INT     3                       ;can't get ECB, ignore it
SADB1:  MOV     [DI.TPKT_TYPE],2        ;ACK packet type
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in NAK packet
	MOV     AH,0                    ;ACK=no (irrelevant in ACK pkt)
	MOV     AL,[MODEM_CR]
	ROR     AL,1
	ROR     AL,1                    ;position RTS=7, CD=6
	AND     AL,0C0h                 ;isolate them
	OR      AL,20h                  ;set circuit up bit
	MOV     WORD PTR[DI.TPKT_DATA+2],AX ;put data in packet
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
S_AX:   POP     AX                      ;restore calling regs
	POP     BX
	POP     SI
	RET
;
;------------------------------------------------------------
;  HOLD rcv data pkt.  DS:SI -> ECB to hold
;
HOLD_PACKET:
	CALL    IPX_LISTEN              ;discard it for now
	RET
;
;------------------------------------------------------------
;  PROCESS_DATA
;
;  DS:SI -> ECB, DS:BX -> data buffer
;
;  Move the data out of this packet
;
PROCESS_DATA:
	PUSH    AX
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	MOV     CX,[BX.TPKT_LEN]        ;get # chars
	XCHG    CH,CL                   ;in CX
	JCXZ    P_DX                    ;empty packet!!
;
	MOV     AX,[IB_IPTR]            ;get # chars used in buf
	SUB     AX,[IB_EPTR]            ;in AX
	JNS     P_D2                    ;see if size wrapped
	ADD     AX,IN_SIZE              ;wrapped, correct used space count
P_D2:   MOV     DX,AX                   ;DX = # bytes USED in buffer
	MOV     AX,IN_SIZE              ;total length of buffer
	SUB     AX,DX                   ;AX = # bytes empty in buffer
	SUB     AX,CX                   ;see what its like after this pkt
	JC      RXDOVF                  ;signal overflow
	CMP     AX,513                  ;time to flow control?
	JA      P_D3                    ;no, all is well
	CMP     [INPUT_FLOW],0          ;have we flowed?
	JNZ     P_D3                    ;yes, all is well
	MOV     [INPUT_FLOW],1          ;no, do flow control
	AND     [MODEM_CR],0FDh         ;lower "CTS" to other end
;
;  We now know packet will fit into input buffer - move it in
;
P_D3:   LEA     SI,[BX.TPKT_DATA]       ;DS:SI -> chars to fetch
	MOV     BX,[IB_IPTR]            ;get insertion pointer
P_D1:   MOV     AL,[SI]                 ;fetch char
	INC     SI
	MOV     [BX],AL                 ;store the new char
	INC     BX                      ;bump it
	CMP     [IB_END],BX             ;at end of buffer?
	JNZ     RECD1                   ;no
	MOV     BX,[IB_START]           ;yes, turn corner
RECD1:  LOOP    P_D1                    ;move packet to input buffer
	MOV     [IB_IPTR],BX            ;update pointer
P_DX:   CLC
P_DX1:  POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	POP     AX
	RET
;
RXDOVF: MOV     [INPUT_FLOW],1          ;assure flow control
	AND     [MODEM_CR],0FDh         ;lower "CTS" to other end
	STC                             ;flag error
	JMP     P_DX1
;
;------------------------------------------------------------
;  ACK packet received
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RA0:    MOV     AX,WORD PTR[BX.TPKT_DATA+2] ;get control bits
	MOV     WORD PTR[CIRC_N_RCTL],AX ;set them locally
	CALL    POST_CONTROL            ;update MODEM_SR
	MOV     AX,WORD PTR[BX.TPKT_DATA] ;get seq # being ACK'd
	XCHG    AH,AL                   ;in 8088 order
	CALL    IPX_LISTEN              ;release rcv ECB
	CMP     [CIRC_N_OPEN],0         ;is circuit closed?
	JNZ     RA1                     ;no
	JMP     ESR_EXIT                ;yes, discard packet
;
RA1:    CALL    CHECK_IN_WINDOW         ;is this # in "sent window"?
	JC      RA1P2                   ;no, discard packet
	CALL    ACK_SENT                ;yes, release any ACK'd sent packets
	JMP     ESR_EXIT                ;all done w/ack
;
RA1P2:  JMP     ESR_EXIT                ;discard out-of-window packet
;
;------------------------------------------------------------
;  Check that the sequence # in AX is within the "sent window" of
;  pending packets...
;
;  On Exit, C = out of window, NC = within window
;
CHECK_IN_WINDOW:
	PUSH    BX
	PUSH    DX
	MOV     BX,[CIRC_N_SEQ]         ;get highest pending data pkt seq #
	MOV     DX,BX                   ;DX = top of window
	SUB     BX,WINDOW_SIZE          ;back up "bottom" of window
	JC      CIW_WW                  ;wrapping corner - bummer
	JZ      CIW_WW                  ;hit 0, means wrapped by one
	CMP     AX,BX                   ;linear window, easy deal...
	JB      CIW_NO                  ;# not in window
	CMP     AX,DX
	JA      CIW_NO
CIW_YES:POP     DX
	POP     BX
	CLC
	RET
;
;  Window wrapped, so high end is "above" low end.
;
CIW_WW:                                 ;have to do double check
	DEC     BX                      ;adjust for fact seq # 0 for CTL
	CMP     AX,BX                   ;see if in wrapped part of window
	JAE     CIW_YES                 ;yes, all is well
	OR      AX,AX                   ;zero not in any window
	JZ      CIW_NO
	CMP     AX,DX                   ;see if in rest of window
	JBE     CIW_YES                 ;yes
CIW_NO: POP     DX                      ;not in window
	POP     BX
	STC
	RET
;
;------------------------------------------------------------
;  ACK all packets as sent, if their sequence number is
;  "older" than or equal to number in AX.
;
ACK_SENT:
	PUSH    AX
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	MOV     DX,AX                   ;save top # in DX
	XOR     BX,BX                   ;queue index
A_S0:   CMP     [SEND_ECB_QUEUE][BX],2  ;is this waiting data pkt?
	JNZ     A_S1                    ;no, try next one
	CALL    POINT_TO_SND_ECB        ;DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;DS:DI -> data packet
	MOV     AX,[DI.TPKT_SEQ]        ;get its seq #
	XCHG    AH,AL                   ;put in order
;
;  now we must see if AX is "older than or equal to" DX
;
	CMP     AX,DX                   ;if they are the same, all is well
	JZ      A_S2                    ;release it
	MOV     CX,DX
	SUB     CX,AX                   ;note: carry makes this work...
	CMP     CX,WINDOW_SIZE          ;even with wrap cases
;       JA      A_S1                    ;not "older than", don't ack it
	JBE     A_S2                    ;### debug, older than
	JMP     SHORT A_S1              ;### debug
A_S2:   MOV     [SEND_ECB_QUEUE][BX],0  ;release this entry
	PUSH    BX
	PUSH    ES
	MOV     BX,6
	CALL    [IPX_VECTOR]            ;cancel timeout timer...
	POP     ES
	PUSH    CS                      ;do FAR call...
	CALL    CONTINUE_XMIT           ;send any next output blocks...
	POP     BX
A_S1:   INC     BX
	CMP     BX,4                    ;check all avail entries
	JBE     A_S0
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	POP     AX
	RET                             ;done
;
;------------------------------------------------------------
;  NAK packet received
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RN0:    MOV     AX,WORD PTR[BX.TPKT_DATA] ;AX = seq # being NAKd
	XCHG    AH,AL
	MOV     DX,WORD PTR[BX.TPKT_DATA+2];DX = NAk reason code
	XCHG    DH,DL
	CALL    IPX_LISTEN              ;release rcv ECB
	CMP     [CIRC_N_OPEN],0         ;is circuit closed?
	JNZ     RN1                     ;no, continue
	JMP     ESR_EXIT
;
;  State: RN1 - RcvNAK1
;
RN1:    CMP     DX,3                    ;was reason=data out of window?
	JZ      RN2                     ;yes
	CMP     DX,0                    ;was reason data error?
	JZ      RN4                     ;yes
	CMP     DX,8000h                ;alternate data error?
	JZ      RN4                     ;yes
;
;  State: RN3
;
	DEC     AX                      ;get RSN-1
	CALL    ACK_SENT                ;ack any pending data packets
	JMP     ESR_EXIT                ;done
;
RN2:    SUB     AX,2                    ;ret RSN-2
	CALL    ACK_SENT                ;ack any pending packets
	JMP     ESR_EXIT
;
RN4:    XOR     BX,BX
RN4A:   CMP     [SEND_ECB_QUEUE][BX],2  ;is it pending data pkt?
	JNZ     RN4B                    ;no
	CALL    POINT_TO_SND_ECB        ;yes, DS:SI -> ECB
	LDS     DI,[SI.FRAG2_ADDR]      ;point to data block
	MOV     DX,[DI.TPKT_SEQ]
	XCHG    DH,DL                   ;get seq # of packet
	CMP     AX,DX                   ;is this NAK'd pkt?
	JNZ     RN4B                    ;no
;
;  DS:SI -> ECB to re-send
;
	PUSH    ES
	MOV     BX,0003h                ;IPX Send
	CALL    [IPX_VECTOR]            ;send it
	POP     ES
	JMP     ESR_EXIT                ;done here...
;
RN4B:   INC     BX
	CMP     BX,4                    ;max # send ECBs for now
	JBE     RN4A                    ;check them all
	JMP     ESR_EXIT                ;done
;
;------------------------------------------------------------
;  CTL packet received
;
;  ES:SI -> ECB
;  DS:BX -> data packet
;------------------------------------------------------------
RC0:    MOV     AX,WORD PTR[BX.TPKT_DATA] ;get control bits
	MOV     WORD PTR[CIRC_N_RCTL],AX ;set them locally
	MOV     DX,[BX.TPKT_SEQ]        ;get seq #, just in case
	CALL    IPX_LISTEN              ;release this rcv ECB
	CMP     [CIRC_N_OPEN],0         ;is circuit closed?
	JNZ     RC1                     ;no
;
;  Circuit is closed, did this open it?
;
	TEST    [CIRC_N_RCTL],20h
	JZ      RC0P3                   ;no, circuit still closed
;
;  State: RC0.2 - NAK circuit not open
;
RC0P2:  PUSH    DX                      ;save seq # being NAK'd
	MOV     AX,4                    ;4 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	JC      RC0P2X                  ;can't get one, ignore it
	MOV     [DI.TPKT_TYPE],3        ;type=NAK
	POP     AX                      ;restore seq # to NAK
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put in CTL packet
	MOV     WORD PTR[DI.TPKT_DATA+2],0100h ;protocol error=circuit closed
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
RC0P2X: JMP     ESR_EXIT                ;dummy for now
;
;  Send circuit closed packet
;
RC0P3:  XOR     DL,DL                   ;data to send
	JMP     RC2                     ;send circuit control packet
;
;  State: RC1 - RcvCtl1
;
RC1:    MOV     AX,DX
	XCHG    AH,AL
	CMP     AX,[LAST_CTL_SEQ]       ;is this new information?
	JBE     RC1OLD                  ;no, ignore but acknowledge
	MOV     [LAST_CTL_SEQ],AX       ;save last rcvd ctl seq
	TEST    [CIRC_N_RCTL],20h       ;is circuit connected set?
	JZ      RC1P2                   ;no
;
;  State: RC1.1
;
	CMP     [NOW_CONNECTED],0       ;is this incoming connection?
	JNZ     RC1NS
	PUSH    BX
	MOV     BX,OFFSET PD_CONNECT
	CALL    STUFF_PDS               ;say we just connected
	POP     BX
RC1NS:  MOV     [NOW_CONNECTED],1       ;yes, indicate it is now
	MOV     [CIRC_N_OPEN],3         ;mark circuit open
	CALL    POST_CONTROL            ;update MODEM_SR
	JNC     RC1A                    ;CTS didn't just go high
	CALL    START_XMIT              ;CTS came up, start output if any held
RC1A:   MOV     DL,20h                  ;send control pkt w/connected
	JMP     RC2
;
;  Old packet, ack as needed.
;
RC1OLD: MOV     DL,20h                  ;assume connected
	CMP     [CIRC_N_OPEN],3
	JZ      RC2J                    ;say we are
	XOR     DL,DL
RC2J:   JMP     RC2                     ;we're not
;
;  POST incoming control bits into MODEM_SR including chg bits
;
POST_CONTROL:
	MOV     AX,WORD PTR[CIRC_N_RCTL];get remote end's status bits
	MOV     AH,AL
	AND     AL,080h                 ;strip down to "CTS" bit
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;put in bit 4
	AND     AH,40h                  ;strip down to "CD" bit
	SHL     AH,1                    ;put in bit 7
	OR      AL,AH                   ;now we have current states
	SHR     AH,1
	SHR     AH,1                    ;position to bit 5
	OR      AL,AH                   ;now we have all bits together
	MOV     AH,[MODEM_SR]
	AND     AH,0B0h                 ;isolate them
	XOR     AH,AL                   ;what changed?
	SHR     AH,1                    ;position changed bits
	SHR     AH,1
	SHR     AH,1
	SHR     AH,1                    ;to low nibble
	OR      AL,AH                   ;Whew!! it's assembled!
	MOV     AH,[MODEM_SR]
	AND     AH,44h                  ;isolate RI and its chg bit
	OR      AL,AH                   ;preserve them
	XCHG    [MODEM_SR],AL           ;stash new, fetch old
	TEST    AL,10h                  ;was CTS high before?
	MOV     AL,[MODEM_SR]           ;get new bits
	JNZ     P_C1                    ;not a low-to-high transition
	TEST    AL,10h                  ;did it just come up?
	JNZ     P_C2                    ;yes, signal transition
P_C1:   CLC                             ;not CTS went high
	RET
;
P_C2:   STC                             ;indicate CTS just went high
	RET
;
;  State: RC1.2 - circuit closed rcvd
;
RC1P2:  CMP     [NOW_CONNECTED],0
	JZ      RC1P2A                  ;already was closed
	PUSH    BX
	MOV     BX,OFFSET PD_CLOSED
	CALL    STUFF_PDS               ;say closed if not quiet...
	POP     BX
RC1P2A: MOV     [CIRC_N_OPEN],0         ;mark circuit closed
	MOV     [NOW_CONNECTED],0       ;no, indicate it is now
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AX,40B0h                ;RI only in AH, reset in AL
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;whatever used to be high has chgd
	OR      AL,AH                   ;restore RI
	MOV     [MODEM_SR],AL           ;set CD, DSR, CTS lowered now
	MOV     [MODEM_CR],0            ;set DTR and RTS low
	XOR     DL,DL
;
; State: RC2 - RcvCtl2 - send "ACK" CTL pkt if we rcvd original
;
RC2:    TEST    [CIRC_N_RCTL+1],01h     ;was rcvd pkt a control "ack"?
	JNZ     RC2ACK                  ;yes, all done
;
; Send circuit control packet.  DL has connected bit on/off
;
	PUSH    DX
	MOV     AX,2                    ;2 byte data field
	CALL    GET_SCTL_PKT            ;DS:SI ->ECB, DS:DI ->pkt data
	POP     DX                      ;DL = connected/not connected
	JC      RC2X                    ;can't get one, ignore it
	MOV     [DI.TPKT_TYPE],4        ;CTL packet type
	MOV     AH,01h                  ;ACK bit = 1
	MOV     AL,[MODEM_CR]
	ROR     AL,1
	ROR     AL,1                    ;position RTS=7, CD=6
	AND     AL,0C0h                 ;isolate them
	OR      AL,DL                   ;set circuit up bit
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
RC2X:   JMP     ESR_EXIT
;
RC2ACK: INC     [CTL_ACK]               ;count another control ack in
	JMP     ESR_EXIT                ;done
;
	PAGE
;------------------------------------------------------------
;  ESR used to release a send ECB when send completes
;
;  On entry ES:SI -> ECB
;------------------------------------------------------------
SND_IPX:
	PUSH    ES
	POP     DS
	CALL    RELEASE_SEND_ECB        ;release the ECB
	RETF                            ;all done
;
	PAGE
;--------------------------------------------------------------------
;  ESR entered at IPX level for SAP incoming packets.
;
;  On Entry:  ES:SI -> ECB we just received
;                AL=0FFh if IPX called us, 0 if AES called us.
;              Note: DS is unknown on entry!
;--------------------------------------------------------------------
SR_REQUEJ: JMP  SR_REQUE                ;handle distance on conditional JMP
;
SAP_RESPOND:
	LDS     BX,ES:[SI.FRAG1_ADDR]   ;DS:BX -> input IPX header
	LDS     DI,ES:[SI.FRAG2_ADDR]   ;DS:DI -> input data block
;
;  Note: DS: is now set to UDATA as that's where all the ECBS are
;
	CMP     ES:[SI.COMPLETION_CODE],0  ;good receive?
	JNZ     SR_REQUEJ               ;no, ignore packet
	CMP     [NOW_CONNECTED],0       ;could we give him connection?
	JNZ     SR_REQUE                ;no, ignore the packet
	MOV     AX,[BX.HDR_LEN]         ;get packet length
	XCHG    AH,AL                   ;put in 8088 order
	SUB     AX,30                   ;remove header length
	JC      SR_REQUE                ;error, ignore pkt
	CMP     AX,4                    ;is it a query packet?
	JNZ     SR_REQUE                ;no, ignore other's SAP ads...
	CMP     WORD PTR[DI+2],9191h    ;is it looking for my type?
	JNZ     SR_REQUE                ;no, ignore it
	MOV     AX,WORD PTR[DI]         ;get Query Type
	XCHG    AH,AL                   ;put in 8088 order
	CMP     AL,3                    ;only 1 or 3 allowed
	JA      SR_REQUE                ;not good type
	JZ      SRESP2                  ;TYPE=3, do response
	CMP     AL,1
	JNZ     SR_REQUE                ;TYPE not 1, ignore
;
;  Generate a SAP response to this request...
;
;  AX = Query Type
;  DS:BX = IPX header of request
;  ES:SI = ECB we got request on
;
SRESP2: INC     AX                      ;set response type
	XCHG    AH,AL                   ;put in Motorola Order
	PUSH    AX                      ;save on stack
	MOV     DI,SI                   ;save input ECB in DI
	CALL    GET_SEND_ECB            ;get a sending ECB in ES:SI
	JC      SR_REQUE1
	POP     AX                      ;restore type code
;
;  DS:SI -> sending ECB, DS:DI -> input ECB
;
	PUSH    DI                      ;save input ECB
	PUSH    SI                      ;save output ECB
	XCHG    SI,DI                   ;DS:SI -> input ECB, ES:DI -> output ECB
	LEA     SI,[SI.IMMEDIATE_ADDR]
	LEA     DI,[DI.IMMEDIATE_ADDR]
	MOV     CX,6
	CLD
	REP     MOVSB                   ;set immediate adr in output ECB
	POP     SI                      ;DS:SI -> output ECB
	POP     BX                      ;DS:BX -> input ECB
	PUSH    BX
	PUSH    SI                      ;save again
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:DI -> output IPX header
	LDS     SI,[BX.FRAG1_ADDR]      ;DS:SI -> input IPX header
	LEA     SI,[SI.SOURCE]          ;DS:SI -> sender's IPX address
	PUSH    AX                      ;save type code
	MOV     AX,[SIB_LENGTH]         ;# SAP packet bytes to send
	CALL    INIT_IPX_HEADER         ;init outgoing IPX header
	POP     AX                      ;fetch type code
	POP     SI                      ;DS:SI -> output ECB
	PUSH    SI                      ;re-stack it
	LDS     DI,[SI.FRAG2_ADDR]      ;DS:SI -> output data buffer
	MOV     SI,OFFSET SIB_PACKET    ;point to packet template
	CLD
	MOV     CX,[SIB_LENGTH]
	PUSH    DI                      ;save packet start offset
	REP     MOVSB                   ;load packet into output data
	POP     DI                      ;point back at packet in buffer
	MOV     [DI],AX                 ;put type code in packet
	POP     SI                      ;DS:SI -> output ECB
;
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX_NOW            ;send response packet
	POP     SI                      ;DS:SI -> input ECB to release
	CALL    IPX_LISTEN              ;re-queue the input ECB
	RETF
;
SR_REQUE1:
	POP     AX                      ;even stack
	MOV     SI,DI                   ;set DS:SI -> input ECB
SR_REQUE:
	CALL    IPX_LISTEN              ;return this ECB to listening...
	RETF
;
	PAGE
;----------------------------------------------------------------------
;  FIND our server using Service Advertising Protocol
;
;  On Entry: DS:SI -> null terminates string w/name of server to locate
;            DS:DI -> buffer to hold server's address if found
;
;  On Exit:  C set if not found, AX = error code
;                                       0 = Server Not Found
;                                       1 = IPX send error
;                                       2 = internal error
;                                       3 = Input name > 47 characters
;
;           NC set if found, and designated buffer has server's IPX address
;
;----------------------------------------------------------------------
FIND_SERVER:                    ;State: FS1 (SndQuery)
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    BP
	PUSH    AX
	MOV     [SERVER_FOUND],0        ;no server found as of now
;
;  State: FS0 (SQInit)
;
	MOV     [SAP_CNTR],3            ;try 3 times to find server
;
	MOV     DI,OFFSET FIND_NAME     ;temp area for lookup
	MOV     CX,47                   ;max length
FS1A:   MOV     AL,[SI]                 ;fetch next char
	INC     SI
	MOV     [DI],AL                 ;store it in temp string
	INC     DI
	OR      AL,AL
	JZ      FS1                     ;EOM just moved, done...
	LOOP    FS1A                    ;check length and continue
	MOV     AX,3                    ;name too long!
	JMP     SHORT FSERX
;
;  State: FS1
;
FS1:    MOV     DI,OFFSET SQ_IPX_HDR    ;point to IPX header
	MOV     SI,OFFSET SAP_DEST      ;SAP sending address
	MOV     AX,4                    ;4 byte packet data
	CALL    INIT_IPX_HEADER         ;init IPX header
	MOV     SI,OFFSET SAP_DEST+4    ;node portion of bcast adr
	MOV     CX,6
	CLD
	MOV     DI,OFFSET [SQ_ECB.IMMEDIATE_ADDR] ;move it here
	REP     MOVSB                   ;set immediate address
;
;  Query built in special SAP query ECB, now send it with timeout
;
	MOV     AX,500                  ;set 500ms timer (5 sec)
	MOV     DI,OFFSET TIMER1        ;use TIMER 1
	CALL    START_TIMER             ;set the timeout timer
	MOV     [SAP_RESP_IN],0         ;say no SAP response pkt in now
;
	MOV     [SAP_FIND_STATE],1      ;indicate looking for SAP data
	MOV     SI,OFFSET SQ_ECB        ;point to ECB to send
	CALL    SEND_IPX_NOW            ;send it, immediate adr set already
	CALL    WAIT_FOR_SEND           ;wait for it to go
	JNC     FS2                     ;all is well
	MOV     AX,1
FSERX:  POP     BP                      ;burn caller's AX
	MOV     [SAP_FIND_STATE],0      ;no longer looking for SAP data
	POP     BP                      ;restore regs
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	STC
	RET
;
;  State: FS2
;
FS2:    CMP     [SAP_RESP_IN],0         ;have we gotten a response?
	JNZ     FS2P1                   ;yes, handle it
	MOV     DI,OFFSET TIMER1        ;no, timed out?
	CALL    CHECK_TIMER             ;let's see
	JC      FS3
	STI
	PUSH    ES
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]
	POP     ES
	CLI
	JMP     FS2                     ;wait for one or the other
;
;  State: FS3 - Timed out waiting for SAP response from server
;
FS3:    DEC     [SAP_CNTR]              ;decrement retry counter
	JNZ     FS1                     ;try 5 times to find server
;
;  State: FS3.2 - "Server Not Found"
;
	MOV     [SAP_FIND_STATE],0      ;no longer looking for SAP data
	XOR     AX,AX
	JMP     FSERX                   ;error return
;
;  State: FS2.1 - Got a response listing desired server
;
;  [SAP_RESP_IN] has offset of ECB with data...
;
FS2P1:  MOV     [SAP_FIND_STATE],0      ;no longer looking for SAP data
	MOV     SI,[SAP_RESP_IN]        ;get ECB
	LDS     BX,[SI.FRAG1_ADDR]      ;DS:BX -> incoming IPX header
	MOV     CX,[BX.HDR_LEN]         ;get packet length
	XCHG    CH,CL                   ;put in 8088 order
	LDS     BX,[SI.FRAG2_ADDR]      ;DS:BX -> incoming packet data
	SUB     CX,30                   ;CX = data size in bytes
;
;  DS:SI -> ECB, DS:BX -> SAP data packet, CX=# bytes in data packet
;
	CALL    LOOKUP_SERVER           ;find us in the packet
	PUSHF
	CALL    IPX_LISTEN              ;packet done, re-queue it
	POPF
	JNC     FS2P1A                  ;found our server
	MOV     AX,2                    ;internal error - lower level
;                                       ;said name was here, we can't find it!
	JMP     FSERX                   ;error exit
;
FS2P1A: POP     AX                      ;restore regs
	POP     BP
	POP     DI
	PUSH    DI                      ;DS:DI -> where he wants adr
	PUSH    ES
	PUSH    DS
	POP     ES
	MOV     SI,OFFSET SAVE_HIS_ADDRESS
	MOV     CX,12
	CLD
	REP     MOVSB                   ;store out server's address
	POP     ES
	POP     DI                      ;restore user's DI
;----------------------------------------------------------------------
;  Exit from SAP locate state, we know where out server is now...
;----------------------------------------------------------------------
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	CLC                             ;show server was found
	RET
;------------------------------------------------------------
;  Search a SAP packet for our desired server's name.
;
;  On Entry: DS:SI -> ECB
;            DS:BX -> SAP data packet
;            CX=# bytes in data packet
;
;  On Exit:  C set, our server's name not in packet or
;                   else we already have a hit from before...
;
;           NC set, SAVE_HIS_ADDRESS has server address, 
;                   SERVER_FOUND set to 1
;
LOOKUP_SERVER_IPX:
	CMP     [SERVER_FOUND],0        ;already have one?
	JZ      LOOKUP_SERVER           ;no, process
	STC                             ;yes, say not in this pkt
	RET
;
LOOKUP_SERVER:
	PUSH    BX
	PUSH    CX
	MOV     [FS_HIT],0              ;say no hit this packet
FIND_NSV:
	PUSH    AX
	PUSH    DI
	PUSH    SI                      ;save ECB address
	PUSH    CX                      ;save packet size
	PUSH    BX                      ;and packet buffer address
	MOV     SI,BX                   ;DS:SI -> response data packet
	ADD     SI,4                    ;point to name
	MOV     CX,48
	MOV     DI,OFFSET SERVER_TNAME  ;move out server's name
	CLD
	REP     MOVSB
	MOV     DI,OFFSET FIND_ADDRESS  ;move out server's address
	MOV     CX,12                   ;including socket
	CLD
	REP     MOVSB                   ;put it out
	LODSW                           ;get hops
	XCHG    AH,AL
	MOV     [SERVER_HOPS],AX        ;store it
;
;  Strip trailing blanks from SAP advertised server name...
;
	MOV     DI,OFFSET SERVER_TNAME+48
F_S5:   MOV     BYTE PTR[DI],0          ;null terminate the name
	DEC     DI
	CMP     BYTE PTR[DI],' '        ;remove trailing blanks
	JZ      F_S5
;
;  Now see if this is the server we were looking for...
;
	MOV     SI,OFFSET SERVER_TNAME
	MOV     DI,OFFSET FIND_NAME
F_SMAT: MOV     AL,[DI]                 ;see if names match
	INC     DI
	CMP     [SI],AL
	JNZ     F_SNF                   ;not what we want
	INC     SI
	OR      AL,AL                   ;compare to EOM
	JNZ     F_SMAT
;
;  This is the server we want!
;
	MOV     [FS_HIT],1              ;say 1 or more hits
	CMP     [SERVER_FOUND],0
	JZ      F_SFND                  ;this is first time for this name
	MOV     AX,[SERVER_HOPS]
	CMP     [SAVE_HIS_HOPS],AX      ;is this one closer?
	JBE     F_SNF                   ;no, stick with what we have
;
;  This is the one we're using, save it's IPX address.
;
F_SFND: MOV     SI,OFFSET FIND_ADDRESS
	MOV     DI,OFFSET SAVE_HIS_ADDRESS ;save it out
	MOV     CX,12
	CLD
	REP     MOVSB
	MOV     AX,[SERVER_HOPS]
	MOV     [SAVE_HIS_HOPS],AX      ;save distance to it
	MOV     [SERVER_FOUND],1        ;say we found it
F_SNF:
;
;  See if multiple entries in this SAP packet...
;
	POP     BX                      ;restore buffer address
	POP     CX                      ;byte count left
	POP     SI                      ;restore ECB
	POP     DI
	POP     AX
	ADD     BX,64                   ;point to next block (if present)
	SUB     CX,64                   ;see if another one
	JC      F_S5A                   ;shouldn't happen!
	CMP     CX,66                   ;is one left?
	JB      F_S5A                   ;no
	JMP     FIND_NSV                ;yes, process it too...
;
;  We've picked best if there are multiples, now release rcv ECB
;
F_S5A:  POP     CX
	POP     BX
	CMP     [FS_HIT],0
	JNZ     F_S5B                   ;we got a hit
	STC
	RET
;
F_S5B:  CLC
	RET
;
	PAGE
;----------------------------------------------------------------------
;  Request a circuit from server who's address is in HIS_ADDRESS.
;
;  On Exit: NC means circuit is assigned, [CIRC_N_NUMBER] = number
;            C means server refused circuit
;               AX=0, server busy
;               AX=1, server timed out
;----------------------------------------------------------------------
REQUEST_CIRCUIT:
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    BP
	PUSH    AX
	CALL    CALC_IMMEDIATE_ADDRESS  ;set up immediate address
;
;  State: CO0 - COInit
;
CO0:    MOV     [RETRY_CNT1],3          ;try 3 times max for circuit
	INC     [CIRC_0_SEQ]            ;bump seq # for send pkt
;
;  State: CO1 - COQuery
;
CO1:    CALL    GET_SEND_ECB            ;get an ECB
	JNC     CO1A                    ;got one
	JMP     DCSERR                  ;can't get one
;
;  DS:SI -> ECB to use to send...
;
CO1A:   PUSH    SI
	XOR     AX,AX                   ;make data = our name
	MOV     DI,OFFSET SERVER_NAME
CO1B:   CMP     BYTE PTR[DI],0          ;see how long that is
	JZ      CO1C
	INC     AX
	INC     DI
	JMP     CO1B
;
CO1C:   PUSH    AX                      ;save length
	ADD     AX,8                    ;add in fixed byte count
	LDS     DI,[SI.FRAG1_ADDR]      ;DS:SI -> IPX header
	MOV     SI,OFFSET HIS_ADDRESS   ;where to send it
	CALL    INIT_IPX_HEADER         ;init IPX header
	POP     AX                      ;restore data byte count
	MOV     CX,AX                   ;save for name move below...
	POP     SI
	PUSH    SI                      ;restore DS:SI -> ECB
	MOV     DI,WORD PTR[SI.FRAG2_ADDR] ;DS:DI -> data buffer
	XCHG    AH,AL                   ;put in (hi-lo) order
	MOV     [DI.TPKT_LEN],AX        ;store data length
	MOV     AX,[CIRC_0_SEQ]         ;restart seq # at 1
	XCHG    AH,AL                   ;put in (hi-lo) order
	MOV     [DI.TPKT_SEQ],AX        ;store packet sequence number
	MOV     [DI.TPKT_TYPE],0        ;Packet type=0
	MOV     [DI.TPKT_CIRCUIT],AL    ;Packet circuit=0
;
;  Now load up data field with our client name
;
	LEA     DI,[DI.TPKT_DATA]       ;point to data buffer
	MOV     SI,OFFSET SERVER_NAME   ;put our name in the buffer
CO1D:   MOV     AL,[SI]
	MOV     [DI],AL
	INC     DI
	INC     SI
	LOOP    CO1D
;
	MOV     [CIRC_N_NUMBER],0FFh    ;set state=CO2
	MOV     AX,500                  ;set 5 second timeout
	MOV     DI,OFFSET TIMER1
	CALL    START_TIMER             ;start timeout timer
	POP     SI                      ;DS:SI -> ECB again
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send IPX packet and release ECB
;
;  State: CO2 - WtCirc
;
;  Await assignment of circuit by the server
;
CO2:    CMP     [CIRC_N_NUMBER],0FFh    ;have we gotten a response?
	JNZ     CO2P2                   ;yes, handle it
	MOV     DI,OFFSET TIMER1        ;no, timed out?
	CALL    CHECK_TIMER             ;let's see
	JC      CO3                     ;yes, timed out
	STI
	PUSH    ES
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]
	POP     ES
	CLI
	JMP     CO2                     ;wait for one or the other
;
;  No response on circuit request, retry if any left
;
CO3:    DEC     [RETRY_CNT1]            ;use another retry
	JZ      DCSETO                  ;"server timed out"
	JMP     CO1                     ;there is one
;
DCSBSY: MOV     BX,OFFSET PD_BUSY       ;flag server is busy
	CALL    STUFF_PDS
DCSERR: XOR     AX,AX
DCSECM: POP     BP                      ;burn user's AX
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	STC
	RET
;
DCSETO: MOV     AX,1                    ;server timed out
	JMP     DCSECM
;
;  CO2.2 - Circuit Assignment packet received
;
;  Now build ACK packet to send
;
CO2P2:  MOV     AX,2                    ;2 data bytes in packet
	CALL    GET_CIRC0_PKT           ;DS:DI -> ECB, DS:DI-> packet
	JC      DCSERR                  ;can't get one
	MOV     AX,[CIRC_0_NEXT]        ;get packet number
	XCHG    AH,AL                   ;get ack packet
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
	MOV     [DI.TPKT_TYPE],2        ;type=ack
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send ACK
;
;  State: CO4 - CORcvd
;
;  See if we got a channel...
;
CO4:    CMP     [CIRC_N_NUMBER],0       ;did we get a channel number?
	JZ      DCSBSY                  ;no, "server busy"
	MOV     [RETRY_CNT1],3          ;reset retry counter
;
;  State: CO5: COAsgn
;
;  Send Circuit Control command to channel we were assigned
;
CO5:    MOV     [CIRC_N_OPEN],1         ;indicate opening...
	MOV     [CIRC_N_SEQ],0          ;set circuit sequence number-1
	MOV     [CIRC_N_CSEQ],0         ;for control too...
	MOV     [CIRC_N_NEXT],1         ;reset expected rcv seq
	MOV     [LAST_CTL_SEQ],0        ;reset "last" control pkt
	MOV     AX,2                    ;2 data bytes in packet
	CALL    GET_SCTL_PKT            ;DS:DI -> ECB, DS:DI-> packet
	JC      DCSERR                  ;can't get one
	MOV     AH,0                    ;ACK=no (we're starting things)
	MOV     AL,[MODEM_CR]
	ROR     AL,1
	ROR     AL,1                    ;position RTS=7, CD=6
	AND     AL,0C0h                 ;isolate them
	OR      AL,20h                  ;set circuit up bit
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
	MOV     [DI.TPKT_TYPE],4        ;type=control
	MOV     [CTL_ACK],0             ;reset ack watcher...
	MOV     AX,500                  ;5 second timeout
	MOV     DI,OFFSET TIMER2
	CALL    START_TIMER             ;start up timer
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send packet
;
;  Now wait for an ACK
;
CO6:    CMP     [CTL_ACK],0             ;have we gotten a response?
	JNZ     CO6P234                 ;yes, handle it
	MOV     DI,OFFSET TIMER2        ;no, timed out?
	CALL    CHECK_TIMER             ;let's see
	JC      CO7                     ;yes, timed out
	STI
	PUSH    ES
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]
	POP     ES
	CLI
	JMP     CO6                     ;wait for one or the other
;
;  State: CO7 - COWtRty
;
;  Timed out, no response from server
;
CO7:    DEC     [RETRY_CNT1]
	JNZ     CO5                     ;more tries remain
	JMP     DCSETO                  ;"server timed out"
;
;  State: CO6.2, CO6.3, or CO6.4
;
CO6P234: TEST   [CIRC_N_RCTL],20h       ;is circuit marked up?
	JNZ     CO6P2                   ;yes, we got it ok
;
;  Mark Circuit closed and exit...
;
	MOV     [CIRC_N_OPEN],0
	JMP     DCSERR                  ;server reneged on the deal!
;
;  We have a channel, assign COM1 to it...
;
CO6P2:  POP     AX
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	CLC
	RET
;
;----------------------------------------------------------------------
;  Release any circuit which is now open to a server
;
;  On Exit: C if no circuit to release, NC if all went well.
;
;  Send Circuit Control command to channel we were assigned
;
RELEASE_CIRCUIT:
	PUSH    BX
	PUSH    CX
	PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    BP
	PUSH    AX
	CMP     [CIRC_N_OPEN],0
	JZ      RCSERR                  ;not open now
	MOV     [RETRY_CNT1],3          ;max 3 tries to disconnect...
RCO5:   MOV     AX,2                    ;2 data bytes in packet
	CALL    GET_SCTL_PKT            ;DS:DI -> ECB, DS:DI-> packet
	JC      RCSERR                  ;can't get one
	XOR     AX,AX                   ;Close Channel, ACK=no (we're starting things)
	MOV     WORD PTR[DI.TPKT_DATA],AX ;put data in packet
	MOV     [DI.TPKT_TYPE],4        ;type=control
	MOV     [CTL_ACK],0             ;reset ack watcher...
	MOV     AX,500                  ;5 second timeout
	MOV     DI,OFFSET TIMER2
	CALL    START_TIMER             ;start up timer
	MOV     WORD PTR[SI.ESR_ADDR+2],CS ;set in an ESR
	MOV     WORD PTR[SI.ESR_ADDR],OFFSET SND_IPX ;to release packet
	CALL    SEND_IPX                ;send packet
;
;  Now wait for an ACK
;
RCO6:   CMP     [CTL_ACK],0             ;have we gotten a response?
	JNZ     RCO8                    ;yes, handle it
	MOV     DI,OFFSET TIMER2        ;no, timed out?
	CALL    CHECK_TIMER             ;let's see
	JC      RCO7                    ;yes, timed out
	STI
	PUSH    ES
	MOV     BX,0Ah                  ;relinquish control to IPX
	CALL    [IPX_VECTOR]
	POP     ES
	CLI
	JMP     RCO6                    ;wait for one or the other
;
;  State: RCO7 - COWtRty
;
;  Timed out, no response from server
;
RCO7:   DEC     [RETRY_CNT1]
	JNZ     RCO5                    ;more tries remain
RCSERR: STC                             ;"server timed out"
	JMP     SHORT RCO8A             ;report closed/error
;
;  State: RCO6.2, RCO6.3, or RCO6.4
;
RCO8:   CLC                             ;closed - good
RCO8A:  PUSHF                           ;save good/bad status
	MOV     [CIRC_N_OPEN],0         ;mark circuit closed
	MOV     [NOW_CONNECTED],0       ;no, indicate it is now
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AX,40B0h                ;RI only in AH, reset in AL
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;whatever used to be high has chgd
	OR      AL,AH                   ;restore RI
	MOV     [MODEM_SR],AL           ;set CD, DSR, CTS lowered now
	MOV     [MODEM_CR],0            ;set DTR and RTS low
	POPF                            ;set/clear carry for status
	POP     AX
	POP     BP
	POP     DI
	POP     SI
	POP     DX
	POP     CX
	POP     BX
	CLC
	RET
;
	PAGE
;===================================================================
;  Code past here gets cut off when we go TSR if we are not a FOSSIL
;===================================================================
;
F04_FINIT:
	PUSH    AX
	MOV     AX,1954h
	MOV     BH,5
	MOV     BL,FUNCNT-1
	JMP     FOSSAXBX
;
F05_UNINIT:
	PUSH    AX
	JMP     FOSSXT                  ;NOP
;
F06_DTR:PUSH    AX
	CMP     AL,1
	JBE     F06_F05_COM             ;it was ok
	JMP     FOSSER                  ;bad news if not 0 or 1
;
F06_F05_COM:
	MOV     BH,[MODEM_CR]
	AND     [MODEM_CR],0FEh         ;remove DTR from us
	OR      [MODEM_CR],AL           ;store his new setting
	XOR     BH,AL
	AND     BH,01h                  ;did DTR change?
	JZ      F06_RD                  ;no, just get out
;
;  He changed something, BL has new data
;
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F06_1                   ;yes
	JMP     FOSSXT                  ;no, so get out
;
;  He changed something and we're connected...
;
F06_1:  TEST    AL,01h                  ;did he lower DTR?
	JNZ     F06_RDR                 ;no, just report what we did
	CMP     [PERM_CONNECT],0        ;yes, are we perm or dynamic?
	JNZ     F06_RD                  ;perm, don't release
	CALL    RELEASE_CIRCUIT         ;dynamic, shut down circuit
	JMP     SHORT F06_RD            ;and return status
;
F06_RDR:CALL    SEND_CONTROL            ;report changes
F06_RD: JMP     FOSSXT                  ;no need to return anything
;
F07_TICK:
	PUSH    AX
	MOV     AL,08h                  ;use INT 8,
	MOV     AH,18                   ;ticks per second
	MOV     DX,55                   ;milliseconds per tick
	JMP     FOSSAXDX                ;return
;
F08_TXFORCE:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F08_1                   ;yes
	JMP     FOSSER                  ;no, it is an error
;
F08_1:  MOV     AX,[OB_IPTR]            ;get insertion pointer
	CMP     AX,[OB_EPTR]            ;compare to end
	JNZ     F08_TXFORCE             ;wait until we are done
	JMP     FOSSXT                  ;don't return anything
;
F09_TXDUMP:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F09_1                   ;yes
	JMP     FOSSER                  ;no, it is an error
;
F09_1:  CLI
	MOV     AX,[OB_EPTR]            ;get extraction pointer
	MOV     [OB_IPTR],AX            ;flush buffer
	STI
	JMP     FOSSXT                  ;get out
;
F0A_RXDUMP:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F0A_1                   ;yes
	JMP     FOSSER                  ;no, it is an error
;
F0A_1:  CLI                             ;halt any interrupts
	MOV     AX,[IB_START]           ;get input buffer start pointer
	MOV     [IB_IPTR],AX            ;reset buffer pointers
	MOV     [IB_EPTR],AX            ;to empty buffer
	CMP     [INPUT_FLOW],0          ;have we lowered RTS?
	JZ      F0A_2                   ;no - all is well
	MOV     [INPUT_FLOW],0          ;yes, indicate we raised RTS again
	OR      [MODEM_CR],02h          ;raise "CTS" to other end
	CALL    SEND_CONTROL            ;negotiate with other end
F0A_2:  STI
	JMP     FOSSXT                  ;get out
;
F0B_TXNW:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;We need to connect if not connected...
	JNZ     F0B_TX1
	CALL    DP_CONN
	JNC     F0B_TX1                 ;it was ok
	JMP     FOSSER0                 ;it was bad
;
F0B_TX1:MOV     BX,[OB_IPTR]            ;get output buffer pointer
	MOV     CX,BX                   ;copy to CX
	INC     CX                      ;bump pointer to next slot
	CMP     [OB_END],CX             ;is it past end?
	JNZ     F0B_D2                  ;no
	MOV     CX,[OB_START]           ;yes, "turn corner" to front
F0B_D2: CMP     [OB_EPTR],CX            ;is buffer full?
	JNZ     F0B_D3                  ;no, so use it
	JMP     FOSSER0                 ;yes, so get out with error
;
F0B_D3: MOV     [BX],AL                 ;no, store our character in buffer
	MOV     [OB_IPTR],CX            ;update insertion pointer
	CALL    START_XMIT              ;start transmitter if 1st
	MOV     AX,1                    ;we sent it
	JMP     FOSSAX
;
F0C_RXSCAN:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F0C_1                   ;yes
	JMP     FOSSER                  ;no, it is an error
;
F0C_1:  MOV     BX,[IB_EPTR]            ;get input buffer pointer
	CMP     [IB_IPTR],BX            ;is there a character available?
	JNZ     F0C_2                   ;yes, return it to caller
	JMP     FOSSER                  ;no, get out
;
F0C_2:  MOV     AL,[BX]                 ;get character
	XOR     AH,AH                   ;clear high byte
	JMP     FOSSAX
;
F0D_KEYSCAN:
	PUSH    AX
	MOV     AH,1                    ;use the BIOS function 1 of int 16
	INT     16H                     ;do the interrupt
	JZ      F0D_1                   ;if there was nothing, go there
	JMP     FOSSAX                  ;we got something, so return it
;
F0D_1:  JMP     FOSSER                  ;bad news
;
F0E_KEYGETC:
	PUSH    AX
	MOV     AH,0                    ; use function 0 of int 16
	INT     16H                     ; do the interrupt
	JMP     FOSSAX
;
F0F_FLOW:
	PUSH    AX
	JMP     FOSSXT                  ;nop
;
F10_FLAG:
	PUSH    AX
	JMP     FOSSER0                 ;make believe we don't know how
					;actually, we don't give a damn!
;
F11_SETCURSOR:
	PUSH    AX
	MOV     AH,2                    ;it is int 10 function 2
	MOV     BH,0
	INT     10H
	JMP     FOSSXT                  ;get out
;
F12_GETCURSOR:
	PUSH    AX
	MOV     AH,3                    ; since it looks like int 10 func 3...
	MOV     BH,0
	INT     10H
	JMP     FOSSDX
;
F13_SCRNCHR:
	PUSH    AX
	MOV     DL,AL                   ;just use DOS
	MOV     AH,2
	INT     21H
	JMP     FOSSXT
;
F14_WATCHDOG:
	PUSH    AX
	JMP     FOSSXT
;
F15_OUTDIRECT:
	PUSH    AX
	MOV     AH,14                   ;just use write tty function
	MOV     BL,7
	INT     10H
	JMP     FOSSXT
;
F16_CLKINT:
	PUSH    AX
	JMP     FOSSER
;
F17_REBOOT:
	PUSH    AX
	JMP     FOSSER
;
F18_READBLOCK:
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F18_1                   ;yes
	PUSH    AX
	XOR     AX,AX                   ;didn't read anything
	JMP     FOSSAX                  ;get out
;
F18_1:  POP     ES
	PUSH    ES
	PUSH    AX
	CLD                             ;for saving
	XOR     DX,DX                   ;clear counter
	MOV     SI,[IB_EPTR]            ;get extraction pointer
F18_2:  CMP     [IB_IPTR],SI            ;are we done?
	JZ      F18_X                   ;yes, so get out
	MOVSB                           ;move byte over
	CMP     SI,[IB_END]             ;are we at the corner?
	JNZ     F18_3                   ;no
	MOV     SI,[IB_START]           ;yes, wrap
F18_3:  INC     DX                      ;we did a character
	LOOP    F18_2                   ;do all we can
F18_X:  MOV     [IB_EPTR],SI
	CMP     [INPUT_FLOW],0          ;have we lowered RTS?
	JZ      F18_X2                  ;no - all done
	MOV     BX,[IB_IPTR]            ;yes...
	SUB     BX,[IB_EPTR]            ;calculate # bytes now in buffer
	JNS     F18_X1
	ADD     BX,IN_SIZE              ;rounding corner, correct for it
F18_X1: CMP     BX,200                  ;are we less than 200 bytes used?
	JAE     F18_X2                  ;no - still halt input
	MOV     [INPUT_FLOW],0          ;yes, indicate we raised RTS again
	OR      [MODEM_CR],02h          ;raise "CTS" to other end
	CALL    SEND_CONTROL            ;negotiate with other end
F18_X2: MOV     AX,DX                   ;get count to AX
	JMP     FOSSAX
;
F19_WRITEBLOCK:
	CMP     [NOW_CONNECTED],0       ;TYPE 1 & 2 force connect
	JNZ     F19_1
	CALL    DP_CONN
	JNC     F19_1                   ;it was ok
	PUSH    AX
	JMP     FOSSER0                 ;it was bad
;
F19_1:  POP     ES
	PUSH    ES
	PUSH    AX
	PUSH    CX
	MOV     DX,[OB_IPTR]            ;get output buffer pointer
F19_2:  MOV     BX,DX                   ;copy to DX
	INC     DX                      ;bump pointer to next slot
	CMP     [OB_END],DX             ;is it past end?
	JNZ     F19_3                   ;no
	MOV     DX,[OB_START]           ;yes, "turn corner" to front
F19_3:  CMP     [OB_EPTR],DX            ;is buffer full?
	JZ      F19_4                   ;yes - done
	MOV     AL,ES:[DI]              ;get character to move
	INC     DI                      ;bump to next one
	MOV     [BX],AL                 ;no, store our character in buffer
	LOOP    F19_2                   ;do as many as we can
	MOV     [OB_IPTR],DX            ;update insertion pointer
	JMP     SHORT F19_5             ;all done
;
F19_4:  MOV     [OB_IPTR],BX            ;all done
F19_5:  PUSH    CS
	POP     ES
	CALL    START_XMIT              ;start transmitter if 1st
	POP     AX
	SUB     AX,CX                   ;get count of what we actually moved
	JMP     FOSSAX
;       
F1A_BREAK:
	PUSH    AX
	CMP     [NOW_CONNECTED],0       ;are we connected?
	JNZ     F1A_1                   ;yes
	JMP     FOSSER                  ;no, it is an error
;
F1A_1:  JMP     FOSSXT
;
INFO    EQU     $               ; define begin of structure
STRSIZ  DW      INFO_SIZE       ; size of the structure in bytes
MAJVER  DB      5               ; FOSSIL spec driver conforms to
MINVER  DB      0               ; rev level of this specific driver
IDENT   DD      LOCATE_STRING   ; "FAR" pointer to ASCII ID string
IBUFR   DW      IN_SIZE         ; size of the input buffer (bytes)
IFREE   DW      0               ; number of bytes left in buffer
OBUFR   DW      OUT_SIZE        ; size of the output buffer (bytes)
OFREE   DW      0               ; number of bytes left in the buffer
SWIDTH  DB      80              ; width of screen on this adapter
SHEIGHT DB      25              ; height of screen    "     "
BAUD    DB      1               ; ACTUAL baud rate, computer to modem
INFO_SIZE EQU $-INFO
;
F1B_INFO:
	CMP     CX,INFO_SIZE    ;is it big enough?
	JB      F1B_1           ;no, just copy that many
	MOV     CX,INFO_SIZE    ;copy this many
F1B_1:  POP     ES
	POP     DS                      ;get his DS and others back
	POP     DI
	POP     SI
	PUSH    SI
	PUSH    DI
	PUSH    DS
	PUSH    ES
	PUSH    AX
	MOV     AX,DS
	MOV     ES,AX                   ;ES->his data buffer
	MOV     AX,CS
	MOV     DS,AX                   ;DS->code
	MOV     DI,SI                   ;ES:DI->destination buffer
	MOV     SI,OFFSET INFO          ;DS:SI->source buffer
	MOV     AX,CX                   ;this many will transfer
	CLD
	REP     MOVSB                   ;copy it over
	JMP     FOSSAX
;
FOSSER0:POP     AX
	XOR     AX,AX                   ;indicate error
	JMP     SHORT FOSSXT1
;
FOSSER: POP     AX
	MOV     AX,0FFFFH               ;indicate error
	JMP     SHORT FOSSXT1
;
FOSSXT: POP     AX
FOSSXT1:POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     BX
	POP     CX
	IRET                            ;return to caller
;
FOSSAX: POP     CX                      ;burn original AX
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     BX
	POP     CX
	IRET                            ;return to caller
;
FOSSBX: POP     AX
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     CX                      ;burn original BX
	POP     CX
	IRET                            ;return to caller
;
FOSSDX: POP     AX
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     CX                      ;burn original DX
	POP     BX
	POP     CX
	IRET                            ;return to caller
;
FOSSAXBX:
	POP     CX                      ;burn original AX
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     DX
	POP     CX                      ;burn original BX
	POP     CX
	IRET                            ;return to caller
;
FOSSAXDX:
	POP     CX                      ;burn original AX
	POP     ES
	POP     DS                      ;restore user's registers
	POP     DI
	POP     SI
	POP     CX                      ;burn original DX
	POP     BX
	POP     CX
	IRET                            ;return to caller
;
;
	PAGE
;===================================================================
;  All code past here gets cut off when we go TSR!!!!
;===================================================================
;
;   Initial Program Load Entry Point
;
;   Connect to INT 14 and then exit as a TSR to remain resident
;
START:  MOV     AX,CS
	MOV     DS,AX
	MOV     [PSPSEG],ES             ;save PSP segment
;
	ASSUME  CS:CODE,DS:CODE,ES:NOTHING
;
	MOV     DX,OFFSET HDR_MSG
	MOV     AH,9
	INT     21h                     ;print header message
;
;  Get current IRQ 14 vector...
;
	MOV     AX,3514h
	INT     21h                     ;ES:BX is current IRQ14 vector
	MOV     WORD PTR[IRQ14VEC],BX
	MOV     WORD PTR[IRQ14VEC+2],ES
	CMP     ES:[BX][6],1954h        ;is this the FOSSIL entry point?
	JZ      FOSU                    ;yes
	SUB     BX,INT_14-INT_14F       ;no, adjust to FOSSIL point
;
FOSU:   MOV     AX,INT_14F-LOCATE_STRING;length of string assuming non-FOSSIL
	SUB     BX,AX                   ;ES:BX to locate string in mem
	MOV     SI,OFFSET LOCATE_STRING
ALPCHK: MOV     AL,ES:[BX]
	INC     BX
	CMP     [SI],AL
	JNZ     NALP                    ;not in now
	INC     SI
	OR      AL,AL
	JNZ     ALPCHK                  ;check entire string
	JMP     ALREADY_LOADED          ;TIPXI14 already in memory...
;
;  TIPXI14 is not currently loaded
;
NALP:   MOV     AX,CS
	MOV     ES,AX
;
	ASSUME  CS:CODE,DS:CODE,ES:CODE
;
	CALL    SCAN_SWITCHES           ;scan for switches
;
	MOV     DX,OFFSET INT_14        ;DS:DX = Handler Address
	MOV     AX,2514h                ;replace INT 14 vector
	CMP     [FOSSIL],0              ;is this to be a FOSSIL driver?
	JZ      NOFOS                   ;no
	MOV     DX,OFFSET INT_14F       ;yes, DS:DX = FOSSIL Handler Address
;  ### comment following when not doing TSR for debug
NOFOS:  INT     21h                     ;debug, don't take over yet
;
;------------------------------------------------------------------
;  Begin IPX init, detect IPX presence
;
	MOV     AX,7A00h                ;IPX mux value
	INT     2Fh                     ;call multiplexer int
	CMP     AL,0FFh                 ;should return 0FFh if present
	JZ      IPXI_1                  ;go it
	MOV     DX,OFFSET NOIPXM
FATAL_ERROR:
	MOV     AX,CS
	MOV     DS,AX                   ;assure DS: correct
	MOV     AH,9
	INT     21h                     ;print error message
	CALL    CLOSE_SOCKETS           ;close any open IPX sockets
	LDS     DX,[IRQ14VEC]           ;DS:DX = original vector
	MOV     AX,2514h                ;replace INT 14 vector as it was
	INT     21h
	MOV     AX,CS
	MOV     DS,AX                   ;assure DS: correct
	MOV     AX,4C01h
	INT     21h                     ;bail out w/errorlevel
;
IPXI_1: MOV     WORD PTR [IPX_VECTOR],DI ;store IPX vector
	MOV     WORD PTR [IPX_VECTOR][2],ES
;
;  IPX present, now get our network and node addresses
;
	MOV     BX,09h
	MOV     SI,OFFSET NETWORK_ADDRESS
	PUSH    DS
	POP     ES
	CALL    [IPX_VECTOR]            ;ES:SI -> network:node address
;
	PUSH    DS
	POP     ES                      ;Restore ES: = DS:
;
;------------------------------------------------------------
;  Now open our socket for all IPX sends and non-SAP receives
;------------------------------------------------------------
	MOV     BX,00h                  ;IPX Open Socket Call
	MOV     AL,1                    ;TSR needs long lived socket
	XOR     DX,DX                   ;ask for temp socket
	CALL    [IPX_VECTOR]            ;Perform IPX open call
	OR      AL,AL                   ;Was it successful?
	JZ      IPXI_2                  ;yes
	MOV     DX,OFFSET NOSOCK        ;can't get a socket to use!
	JMP     FATAL_ERROR
;
;  Socket opened, store it's address and init ECBs...
;
IPXI_2: MOV     [SOCKET_ADDRESS],DX     ;save socket number
	MOV     [RECB1.SOCKET_NUMBER],DX;TBBS/IPAD listen on this socket
	MOV     [RECB2.SOCKET_NUMBER],DX
	MOV     [RECB3.SOCKET_NUMBER],DX
	MOV     [RECB4.SOCKET_NUMBER],DX
	MOV     [RECB5.SOCKET_NUMBER],DX
;
	MOV     [SECB1.SOCKET_NUMBER],DX;TBBS/IPAD send on this socket
	MOV     [SECB2.SOCKET_NUMBER],DX
	MOV     [SECB3.SOCKET_NUMBER],DX
	MOV     [SECB4.SOCKET_NUMBER],DX
	MOV     [SECB5.SOCKET_NUMBER],DX
;
	MOV     [SQ_ECB.SOCKET_NUMBER],DX ;make SAP queries on this socket too
;
;  Now post 5 listen ECBs on our socket
;
	MOV     SI,OFFSET RECB1
	CALL    IPX_LISTEN              ;post 5 listen ECBs
	JC      IPXI2ERR
	MOV     SI,OFFSET RECB2         ;on our socket
	CALL    IPX_LISTEN
	JC      IPXI2ERR
	MOV     SI,OFFSET RECB3
	CALL    IPX_LISTEN
	JC      IPXI2ERR
	MOV     SI,OFFSET RECB4
	CALL    IPX_LISTEN
	JC      IPXI2ERR
	MOV     SI,OFFSET RECB5
	CALL    IPX_LISTEN
	JNC     INIT3                   ;listens took
IPXI2ERR:
	MOV     DX,OFFSET NOLISTEN      ;could not post the listens
	JMP     FATAL_ERROR
;
INIT3:  CMP     [WE_ARE_SERVER],0
	JZ      INIT3NS                 ;we are not a server
	MOV     SI,OFFSET NETWORK_ADDRESS
	MOV     DI,OFFSET SIB_ADDRESS   ;load up our SIB address field
	MOV     CX,12
	CLD
	REP     MOVSB                   ;for our response pkt
;
;  We are a server, try to open socket 0452h to see
;  if we are to listen for SAP queries ourselves...
;
	MOV     BX,00h                  ;IPX Open Socket Call
	XOR     AL,AL                   ;Indicate short lived socket
	MOV     DX,5204h                ;try opening SAP socket 0452h...
	CALL    [IPX_VECTOR]            ;Perform IPX open call
	OR      AL,AL                   ;Was it successful?
	JNZ     SERV1                   ;no, don't post SAP listener
;
	MOV     [OPEN_452],1            ;indicate it opened
	MOV     SI,OFFSET SAP_ECB1
	CALL    IPX_LISTEN              ;post SAP listener ECBs
	JC      SERV1E
	MOV     SI,OFFSET SAP_ECB2      ;on socket 0452h
	CALL    IPX_LISTEN
	JNC     SERV1                   ;done w/SAP listener posting
SERV1E: MOV     DX,OFFSET NOLISTEN      ;could not post the listens
	JMP     FATAL_ERROR
;
;  Start task to send our announcement to 0452h every 60 seconds...
;
SERV1:
;
;------------------------------------------------------------------
;   IPX is present and initialized
;------------------------------------------------------------------
INIT3NS:
	CMP     [PERM_CONNECT],0        ;permanent connection?
	JZ      INIT6                   ;no
	MOV     DX,OFFSET PROGRESS1
	MOV     AH,9
	INT     21h                     ;say looking for server...
;
;  Try to find our server...
;
	MOV     SI,OFFSET SERVER_NAME   ;name of server to locate
	MOV     DI,OFFSET HIS_ADDRESS   ;put address here if found
	CALL    FIND_SERVER             ;find our server
	JNC     INIT4                   ;we fount it
	MOV     DX,OFFSET SAP_FAIL      ;error code=0
	OR      AX,AX
	JZ      INIT3A
	MOV     DX,OFFSET SAP_SEND      ;error code=1
	DEC     AX
	JZ      INIT3A
	MOV     DX,OFFSET SAP_NF        ;err code=2 !!! should not happen!!!
INIT3A: JMP     FATAL_ERROR
;
;  Next send the server a connect request packet to open a channel
;
INIT4:  MOV     DX,OFFSET PROGRESS2
	MOV     AH,9
	INT     21h                     ;say looking for circuit...
	CALL    REQUEST_CIRCUIT         ;request a circuit
	JNC     INIT5                   ;got one
	MOV     DX,OFFSET NO_CHAN       ;error code=0
	OR      AX,AX
	JZ      INIT4A
	MOV     DX,OFFSET SERV_TO       ;error code=1
INIT4A: JMP     FATAL_ERROR
;
INIT5:  MOV     [NOW_CONNECTED],1       ;say circuit now connected
	MOV     AL,[MODEM_SR]
	MOV     AH,AL
	AND     AH,40h                  ;isolate RI
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1
	SHR     AL,1                    ;position chg bits
	OR      AL,AH                   ;restore RI as it was
	XOR     AL,0BBh                 ;set CD, DSR & CTS, also set chg bits
	AND     AL,0FBh                 ;no chg poss in RI
	MOV     [MODEM_SR],AL           ;store new status reg image
	MOV     [MODEM_CR],03h          ;set DTR and RTS high
	CALL    SEND_CONTROL            ;report control status
	MOV     DX,OFFSET EXIT_MSG
	MOV     AH,9
	INT     21h                     ;print we're here....
INIT6:  MOV     DX,OFFSET EXIT_MSG1
	MOV     AH,9
	INT     21h                     ;print we're here....
;---------------------------------------------------------------------------
;   Make our function table correct for the type we are
;---------------------------------------------------------------------------
INIT7:  CLD
	CMP     [FOSSIL],0              ;are we a FOSSIL
	JNZ     INIT7A                  ;yes, so nothing to do!
	MOV     DI,OFFSET FUNCTBL+8     ;point to function 4
	MOV     CX,FUNCNT-4
	CMP     [PS2_BIOS],0            ;are we doing PS2?
	JZ      INIT7C                  ;no, so we are right
	MOV     AX,OFFSET F04_PS2_EINIT ;function 4 for PS2
	STOSW
	MOV     AX,OFFSET F05_PS2_EPC   ;function 5 for PS2
	STOSW
	SUB     CX,2                    ;2 less to do
INIT7C: MOV     AX,OFFSET PORTER        ;make the rest errors
	REP     STOSW                   ;store it and be done
;---------------------------------------------------------------------------
;   IPX init is done, now chop off INIT code and become a good little TSR...
;---------------------------------------------------------------------------
INIT7A: MOV     AX,CS
	SUB     AX,[PSPSEG]             ;# paras used by PSP
	MOV     DX,OFFSET START         ;point to end of resident code
	CMP     [FOSSIL],0              ;should we chop off the FOSSIL too?
	JNZ     INIT7B                  ;no, normal
	MOV     DX,OFFSET F04_FINIT     ;chop from here instead
INIT7B: ADD     DX,15                   ;round up to paragraph size
	SHR     DX,1
	SHR     DX,1
	SHR     DX,1
	SHR     DX,1                    ;divide by 16 to get # paras in our code
	ADD     DX,AX                   ;add in PSP paras
	MOV     AX,3100h                ;TSR function, code=0
;       CALL    CLOSE_SOCKETS           ;### temp, no TSR
;       MOV     AX,4C00h                ;### temp, no TSR
	INT     21h                     ;exit - we are installed 
;
	PAGE
;---------------------------------------------------------------------
;
IN_NOW  DB      0               ;>0 = TIPXI14 already in memory
IN_SEG  DW      0               ;segment of active copy
IN_OFFSET DW    0               ;offset to INT_14 in active copy
CALL_VEC DD     0
;
HDR_MSG DB      0Dh,0Ah,'TIPXI14 v1.0 - INT 14/FOSSIL Driver for TIPX protocol...',0Dh,0Ah,'$'
USAGE   DB      'Usage: TIPXI14 [/u] | [/r] | [/d] | [/g|/2|/f] | [/c:name[/p]] [/s:name]',0Dh,0Ah
	DB      0Dh,0Ah,'   /u = Unload TIPXI14 TSR from memory'
	DB      0Dh,0Ah,'   /r = Release any current TIPX connection'
	DB      0Dh,0Ah,'   /d = Disable "call progress" messages',0Dh,0Ah
	DB      0Dh,0Ah,'   /g = Support Generic BIOS functions (0-3) Only'
	DB      0Dh,0Ah,'   /2 = Support PS/2 BIOS functions (0-5) Only'
	DB      0Dh,0Ah,'   /f = Support FOSSIL driver functions (0-1Bh) Only',0Dh,0Ah
	DB      0Dh,0Ah,'   /c:name = Connect (as client) to server "name"'
	DB      0Dh,0Ah,'   /p = Connection as client is permanent.  It will be made immediately,',0Dh,0Ah
	DB      '        and will remain until TIPXI14 /r is issued',0Dh,0Ah
	DB      0Dh,0Ah,'   /s:name = Announce as server "name"',0Dh,0Ah
	DB      0Dh,0Ah,'Note:  If both /c:name and /s:name are used, then TIPXI14'
	DB      0Dh,0Ah,'       will ignore /p and operate dynamically',0Dh,0Ah
	DB      0Dh,0Ah,'Note:  Only one of /g, /2, or /f may be specified.  By default'
	DB      0Dh,0Ah,'       TIPXI14 provides EBIOS plus PS/2 and Generic BIOS functions.'
	DB      0Dh,0Ah,'$'
;
PROGRESS1 DB    0Dh,0Ah,'   Locating server...',0Dh,0Ah,'$'
PROGRESS2 DB    '   Requesting Circuit...',0Dh,0Ah,'$'
EXIT_MSG DB     '   Connected to server...',0Dh,0Ah,'$'
EXIT_MSG1 DB    0Dh,0Ah,'TIPXI14 Driver now resident...',0Dh,0Ah,'$'
NOFOSGEN DB     0Dh,0Ah,'*** Can''t be both a FOSSIL and Generic BIOS driver!',0Dh,0Ah,'$'
NOPERM  DB      0Dh,0Ah,'*** Both /C: and /S: given, /P ignored!',0Dh,0Ah,'$'
NOIPXM  DB      0Dh,0Ah,'*** No IPX driver installed!',0Dh,0Ah,'$'
NOSOCK  DB      0Dh,0Ah,'*** Cannot Open IPX socket!',0Dh,0Ah,'$'
NOLISTEN DB     0Dh,0Ah,'*** IPX error initializing Listen ECBs!',0Dh,0Ah,'$'
SAP_SEND DB     0Dh,0Ah,'*** Error sending queries for server!',0Dh,0Ah,'$'
SAP_FAIL DB     0Dh,0Ah,'*** Cannot locate the server!',0Dh,0Ah,'$'
SAP_NF   DB     0Dh,0Ah,'*** Our server did not respond to poll!',0Dh,0Ah,'$'
ALMSG    DB     0Dh,0Ah,'*** TIPXI14.EXE is already resident!',0Dh,0Ah,'$'
NO_CHAN  DB     0Dh,0Ah,'*** Server busy, no channels available!',0Dh,0Ah,'$'
SERV_TO  DB     0Dh,0Ah,'*** Server timed out!',0Dh,0Ah,'$'
UNLOAD  DB      0Dh,0Ah,'TIPXI14 removed from memory...',0Dh,0Ah,'$'
NUNLOAD DB      0Dh,0Ah,'*** TIPXI14 not resident, cannot unload driver!',0Dh,0Ah,'$'
NRELEASE DB     0Dh,0Ah,'*** TIPXI14 not resident, no connection to release!',0Dh,0Ah,'$'
RELEASE  DB     0Dh,0Ah,'TIPXI14 server connection closed and released...',0Dh,0Ah,'$'
RELNOC   DB     0Dh,0Ah,'TIPXI14 not currently connected to a server...',0Dh,0Ah,'$'
;
	PAGE
;---------------------------------------------------------------------------
;   TIPXI14.EXE is already loaded into memory.  We can only do
;   those things allowed in this case...
;
;   On Entry:  ES:BX -> INT_14 in TSR version of us already loaded...
;
	ASSUME  CS:CODE,DS:CODE,ES:NOTHING
;
ALREADY_LOADED:
	MOV     [IN_NOW],1              ;indicate in memory now
	MOV     [IN_SEG],ES             ;save segment
	MOV     [IN_OFFSET],BX          ;and offset
	MOV     WORD PTR[CALL_VEC][2],ES;sector for calls
	CALL    SCAN_SWITCHES           ;scan command switches
;
	MOV     AX,CS
	MOV     ES,AX                   ;set ES:->
;
	ASSUME  ES:CODE
;
	MOV     DX,OFFSET ALMSG
	MOV     AH,9
	INT     21h
;
	MOV     AX,4C00h
	INT     21h                     ;get out
;
;---------------------------------------------------------------------------
;   SCAN command line for option switches.
;
;   Current options supported are:
;
;   /U = unload TIPXI14 from memory
;   /R = Release any current connection
;   /G = Generic BIOS functions (0-3) only
;   /2 = PS/2 BIOS functions (0-5) only
;   /F = Emulate a FOSSIL driver
;   /Q = no "pseudo device" warning messages
;
;   /C:name = connect (as client) to server "name"
;   /S:name - announce as server "name"
;   /P = Connection as client is permanent.  It will be made immediately
;        and will remain until TIPXI14 /r or /u is issued
;
;       Note:  If both /c:name and /s:name are used, then TIPXI14
;               will override /p and operate dynamically.
;
;---------------------------------------------------------------------------
;
	ASSUME  CS:CODE,DS:CODE,ES:NOTHING
;
SCAN_SWITCHES:
	MOV     ES,[PSPSEG]
	MOV     SI,80h
	MOV     CL,ES:[SI]              ;get tail length
	INC     SI
	XOR     CH,CH
SS_NXT: JCXZ    SSXIT                   ;all done
	MOV     AL,ES:[SI]
	INC     SI
	CMP     AL,'/'
	JZ      SS_P1
	LOOP    SS_NXT
SSXIT:  MOV     AL,[WE_ARE_CLIENT]
	OR      AL,[WE_ARE_SERVER]      ;must be at least one!
	JZ      SS_ERR                  ;give usage line
	MOV     AL,[PS2_BIOS]
	XOR     AL,1                    ;generic = 1
	AND     AL,[FOSSIL]             ;check for both /g and /f
	JNZ     SS_GFE                  ;bad news
	CMP     [PERM_CONNECT],0        ;was /P given?
	JZ      SSXIT1                  ;no, all done
	CMP     [WE_ARE_SERVER],0
	JZ      SSXIT1                  ;ok to be perm
	CMP     [WE_ARE_CLIENT],0
	JZ      SSXIT1                  ;ok to be perm
;
;  /P given and both /C: and /S: given, reset /P and give warning
;
;
	MOV     DX,OFFSET NOPERM
	MOV     AH,9
	INT     21h
SSXIT1: RET
;
SS_GFE: MOV     DX,OFFSET NOFOSGEN      ;can't do both
	JMP     SHORT SS_ERR1
;
SS_ERR: MOV     DX,OFFSET USAGE
SS_ERR1:MOV     AH,9
	INT     21h
	MOV     AX,4C01h
	INT     21h
;
;  Got a switch
;
SS_P1:  CALL    FETCH
	JC      SS_ERR                  ;ran out
	CMP     AL,'C'                  ;is it /C:name?
	JNZ     SS_P2                   ;no
	CALL    FETCH
	JC      SS_ERR                  ;ran out
	CMP     AL,':'
	JNZ     SS_ERR
	MOV     DI,OFFSET SERVER_NAME
SS_P1B: CALL    FETCH
	JC      SS_P1A                  ;end of name
	CMP     AL,' '
	JBE     SS_P1A                  ;end of name
	CMP     AL,'/'                  ;another switch
	JZ      SS_P1A
	MOV     [DI],AL
	INC     DI
	JMP     SS_P1B
;
SS_P1A: MOV     BYTE PTR[DI],0          ;null terminate name
	MOV     [WE_ARE_CLIENT],1       ;indicate we're a client
	JMP     SS_NXT
;
SS_P2:  CMP     AL,'P'                  ;is it /P?
	JNZ     SS_P3
	MOV     [PERM_CONNECT],1        ;yes, indicate permanent mode
	JMP     SS_NXT
;
SS_P3:  CMP     AL,'G'                  ;is it /g?
	JNZ     SS_P3A                  ;no
	MOV     [PS2_BIOS],0            ;yes, set only generic BIOS
	MOV     [E_BIOS],0              ;yes, set only generic BIOS
	JMP     SS_NXT
;
SS_P3A: CMP     AL,'F'                  ;is it /f?
	JNZ     SS_P3B                  ;no
	MOV     [FOSSIL],1              ;yes, set for FOSSIL
	MOV     [E_BIOS],0              ;disable the extended BIOS calls
	JMP     SS_NXT
;
SS_P3B: CMP     AL,'D'                  ;is it /d?
	JNZ     SS_P3C                  ;no
	MOV     [BE_QUIET],1            ;yes, signal quiet mode
	JMP     SS_NXT
;
SS_P3C: CMP     AL,'2'                  ;is it /2?
	JNZ     SS_P3D
	MOV     [E_BIOS],0              ;yes, no EBIOS, just PS/2 BIOS
	JMP     SS_NXT
;
SS_P3D: CMP     AL,'R'                  ;release any connection
	JNZ     SS_P4
	JMP     RELEASE_CON             ;do a release
;
SS_P4:  CMP     AL,'S'                  ;is it /s:
	JNZ     SS_P5
	CALL    FETCH
	JC      SS_ERRJ                 ;ran out
	CMP     AL,':'
	JNZ     SS_ERRJ
	MOV     DI,OFFSET SIB_NAME      ;store name we announce here....
SS_P4B: CALL    FETCH
	JC      SS_P4A                  ;end of name
	CMP     AL,' '
	JBE     SS_P4A                  ;end of name
	CMP     AL,'/'                  ;another switch
	JZ      SS_P4A
	MOV     [DI],AL
	INC     DI
	JMP     SS_P4B
;
SS_P4A: MOV     [WE_ARE_SERVER],1       ;indicate we're a server
	JMP     SS_NXT
;
SS_ERRJ: JMP    SS_ERR
;
SS_P5:  CMP     AL,'U'
	JNZ     SS_ERRJ                 ;bad switch
;
;   /U means unload old copy
;
	CMP     [IN_NOW],0
	JNZ     UNLOAD1                 ;old copy present, unload it
	MOV     DX,OFFSET NUNLOAD
	MOV     AH,9
	INT     21h
	MOV     AX,4C01h
	INT     21h
;
UNLOAD1: MOV    DX,OFFSET UNLOAD
	MOV     AH,9
	INT     21h
	MOV     AX,OFFSET TSR_UNLOAD    ;routine in resident code to call
	MOV     WORD PTR[CALL_VEC],AX   ;set up far call vector
	CALL    [CALL_VEC]              ;call TSR_UNLOAD in resident code
	PUSH    ES                      ;ES: returns set to PSP of TSR
	MOV     BX,02Ch                 ;ES:2C = Segment of environment
	MOV     ES,ES:[BX]              ;ES: -> Environment
	MOV     AH,49h                  ;release environment memory
	INT     21h
	POP     ES                      ;now ES: -> TSR PSP
	MOV     AH,49h                  ;free code memory
	INT     21h                     ;release resident's memory
	MOV     AX,4C00h                ;and get out
	INT     21h
;
;  Get next switch character as upper case
;
FETCH:  DEC     CX
	JZ      FETCH2
	MOV     AL,ES:[SI]
	INC     SI
	CMP     AL,'a'
	JB      FETCH1
	CMP     AL,'z'
	JA      FETCH1
	AND     AL,5Fh
FETCH1: CLC
	RET
;
FETCH2: STC
	RET
;
;  Release connection in resident TIPXI14
;
RELEASE_CON:
	CMP     [IN_NOW],0
	JNZ     RELCON1                 ;old copy present, release it
	MOV     DX,OFFSET NRELEASE      ;no TSR resident...
	MOV     AH,9
	INT     21h
	MOV     AX,4C01h
	INT     21h
;
;
RELCON1:
	MOV     AX,OFFSET TSR_RELEASE   ;routine in resident code to call
	MOV     WORD PTR[CALL_VEC],AX   ;set up far call vector
	CALL    [CALL_VEC]              ;call TSR_RELEASE in resident code
	MOV     DX,OFFSET RELEASE       ;say released
	JNC     RELCON2
	MOV     DX,OFFSET RELNOC        ;say nothing to release
RELCON2:
	MOV     AH,9
	INT     21h
	MOV     AX,4C00h
	INT     21h
;
MAIN    ENDP
CODE    ENDS
;
;  Startup stack only...
;
VSTACK  SEGMENT PARA STACK 'ISTACK'
	DB      250             ;startup stack
VSTACK  ENDS
;
	END     MAIN