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