_IPX: THE GREAT COMMUNICATOR_ by Rahner James [LISTING ONE] ; *************************************************************************** ; * Title: ESRS.ASM -- by Rahner James ; * Copyright (c) January 1991, Ryu Consulting, 916/722-1939 ; * File contains default Event Service Routine for listening & talking packets ; **************************************************************************** _ESRS_ASM_ equ 1 ifdef LARGEMODEL .model large,c else .model small,c endif ADDRESS_S struct network dw ?,? ; Network number node dw 3 dup(?); Node address on that network socket dw ? ; Socket number on that node ADDRESS_S ends IPX_PACKET_S struct next dd ? ; Used by IPX/SPX when the ECB is active function dd ? ; Called after packet sent/recd, called ESR in_use db ? ; Set to !0 by IPX/SPX when packet is in use completion_code db ? ; Set by XPX after packet task is complete socket dw ? ; Socket to use for this ECB IPX_work dd ? ; Workspace used internally by IPX driver_work dd ?,?,? ; Workspace used internally by IPX driver dest_address db 6 dup(?); Destination address for packet fragment_count dw ? ; Fragments descriptors that follow hdr dd ? ; -> IPX/SPX packet descriptor to use size_hdr dw ? ; Size of the IPX(30) or SPX(42) descriptor buffer_ptr dd ? ; -> data buffer to use for transmission/reception buffer_size dw ? ; Number of bytes in that buffer next_allocated dd ? ; -> next allocated packet structure next_sibling dd ? ; -> next packet for stream and condition parent dd ? ; -> parent stream definition packet default_buffer dd ? ; -> default buffer to use for IPX or SPX default_size dw ? ; Size of the default buffer done_flag dd ? ; Set by the ESR with the completion code checksum dw ? ; Dummy checksum of 30-byte packet header packet_length dw ? ; Length of complete IPX packet control db ? ; Transport control byte for internet bridges packet_type db ? ; Packet type: IPX(4)/SPX(5) dest_network dd ? ; Destination network address dest_node db 6 dup(?); Destination node address dest_socket dw ? ; Destination socket src_network dd ? ; Source network address src_node db 6 dup(?); Source node address src_socket dw ? ; Source socket IPX_PACKET_S ends XPX_STREAM_S struct next dd ? ; -> next stream structure opened first_allocated dd ? ; -> first allocated packet for handle last_allocated dd ? ; -> last allocated packet for handle first_unread dd ? ; -> first unread packet last_unread dd ? ; -> last unread packet in the list first_free dd ? ; -> first packet available for talking first_error dd ? ; -> first packet encountering an error last_error dd ? ; -> last packet encountering an error dest_network dd ? ; Destination network address dest_node db 6 dup(?); Destination node address dest_socket dw ? ; Destination socket local_target db 6 dup(?); Node address of local target for dest connection_ID dw ? ; Connection ID used for SPX total_talkers dw ? ; Number of talkers for this stream total_listeners dw ? ; Number of listeners for this stream unread_count dw ? ; Number of packets unread by app free_count dw ? ; Number of packets ready for talking maximum_unread dw ? ; Maximum number of unread packets error_count dw ? ; Number of unprocessed error packets total_transmissions dd ? ; Number of transmissions performed total_receptions dd ? ; Number of receptions performed total_errors dd ? ; Number of errors encountered XPX_STREAM_S ends .data extern _Ignore_Nomatch:byte, _Our_Address:word, IPX_Vector:dword, _First_Stream:dword extern _First_Nomatch:dword, _Last_Nomatch:dword, _Total_Nomatchs:word .code Last_Broad_Ptr dw 0,0 ; -> last checked broadcast stream ; **************************************************************************** ; * void far TALK_ESR( void ) -- Event Service Routine (ESR) for IPX ; * functions and their talking packets ; * Given: AL = 0 if AES called this ESR, 0xff if this is a normal event ; * ES:SI -> ECB that just finished talking ; * Returns: Packet either glued onto the free list or the error list ; * Note: Interrupts are enabled at this point and should stay that way ; **************************************************************************** talk_esr proc far ; * See if we need to set the done flag lds bx, es:[si].IPX_PACKET_S.done_flag ; DS:BX -> process done flag mov cl, es:[si].IPX_PACKET_S.completion_code mov ax, ds or ax, bx jz @F ; If DS:BX -> NULL, just skip it mov [bx], cl ; Set the flag with our completion code mov word ptr es:[si].IPX_PACKET_S.done_flag, 0 ; Make it NULL mov word ptr es:[si].IPX_PACKET_S.done_flag+2, 0 ; * Check whether the packet goes in the error list or the free list @@: lds bx, es:[si].IPX_PACKET_S.parent ; DS:BX -> parent structure or cl, cl ; See if we got a transmission error jnz talk20_esr ; Jump if we got one ; * Here's where we process the good transmissions add word ptr [bx].XPX_STREAM_S.total_transmissions, 1 adc word ptr [bx].XPX_STREAM_S.total_transmissions+2, 0 inc [bx].XPX_STREAM_S.free_count; mov cx, word ptr [bx].XPX_STREAM_S.first_free ; DX:CX -> first free mov dx, word ptr [bx].XPX_STREAM_S.first_free+2 mov word ptr [bx].XPX_STREAM_S.first_free, si mov word ptr [bx].XPX_STREAM_S.first_free+2, es mov word ptr es:[si].IPX_PACKET_S.next_sibling, cx mov word ptr es:[si].IPX_PACKET_S.next_sibling+2, dx talk10_esr: ret ; * Here's where we take care of our challenged packets talk20_esr: add word ptr [bx].XPX_STREAM_S.total_errors, 1 adc word ptr [bx].XPX_STREAM_S.total_errors+2, 0 inc [bx].XPX_STREAM_S.error_count mov cx, word ptr [bx].XPX_STREAM_S.last_error ; DX:CX ->last error mov dx, word ptr [bx].XPX_STREAM_S.last_error+2 mov word ptr [bx].XPX_STREAM_S.last_error, si ; Set new last error mov word ptr [bx].XPX_STREAM_S.last_error+2, es mov word ptr es:[si].IPX_PACKET_S.next_sibling, 0 mov word ptr es:[si].IPX_PACKET_S.next_sibling+2, 0 mov ax, cx ; See if we need to do the first as well or ax, dx jnz @F mov word ptr [bx].XPX_STREAM_S.first_error, si ;Set new first error mov word ptr [bx].XPX_STREAM_S.first_error+2, es ret @@: mov ds, dx ; DS:BX -> the first born mov bx, cx mov word ptr [bx].IPX_PACKET_S.next_sibling, si ; Point old end mov word ptr [bx].IPX_PACKET_S.next_sibling+2, es ret talk_esr endp ; **************************************************************************** ; * void far LISTEN_ESR( void ) -- Event Service Routine (ESR) for IPX ; * functions and their listening packets ; * Given: AL = 0 if AES called this ESR, 0xff if this is a normal event ; * ES:SI -> ECB that just got something ; * Returns: Packet put at the end of the unread packet list of the stream it ; * was intended for. ; * Note: Interrupts are enabled at this point and should stay that way. This ; * packet may not be put with its parent if there are multiple parents ; * associated with one socket ; **************************************************************************** listen_esr proc far mov ax, @Data ; DS = our data segment mov ds, ax ; * First see if we sent it as a broadcast and it got back to us mov ax, _Our_Address.ADDRESS_S.node+4 cmp word ptr es:[si].IPX_PACKET_S.src_node+4, ax jne listen10_esr mov ax, _Our_Address.ADDRESS_S.node+2 cmp word ptr es:[si].IPX_PACKET_S.src_node+2, ax jne listen10_esr mov ax, _Our_Address.ADDRESS_S.node cmp word ptr es:[si].IPX_PACKET_S.src_node, ax jne listen10_esr mov ax, _Our_Address.ADDRESS_S.network+2 cmp word ptr es:[si].IPX_PACKET_S.src_network+2, ax jne listen10_esr mov ax, _Our_Address.ADDRESS_S.network cmp word ptr es:[si].IPX_PACKET_S.src_network, ax jne listen10_esr listen_again_buckwheat: mov bx, 4 ; BX = IPX Listen For Packet command call dword ptr IPX_Vector ; Call the IPX function ret ; * See if we need to set the done flag listen10_esr: mov ax, es:[si].IPX_PACKET_S.packet_length ; Change format xchg ah, al sub ax, es:[si].IPX_PACKET_S.size_hdr mov es:[si].IPX_PACKET_S.packet_length, ax lds bx, es:[si].IPX_PACKET_S.done_flag ; DS:BX -> process done flag mov cl, es:[si].IPX_PACKET_S.completion_code mov ax, ds or ax, bx jz listen20_esr mov [bx], cl mov word ptr es:[si].IPX_PACKET_S.done_flag, 0 mov word ptr es:[si].IPX_PACKET_S.done_flag+2, 0 ; * Check whether the packet goes in the error list or the free list listen20_esr: lds bx, es:[si].IPX_PACKET_S.parent ; DS:BX -> parent structure or cl, cl ; See if we got a reception error jz listen40_esr ; Jump if we have an unimpaired reception ; * Here's where we take care of our datistically challenged packets add word ptr [bx].XPX_STREAM_S.total_errors, 1 adc word ptr [bx].XPX_STREAM_S.total_errors+2, 0 inc [bx].XPX_STREAM_S.error_count mov cx, word ptr [bx].XPX_STREAM_S.last_error ; DX:CX->last error mov dx, word ptr [bx].XPX_STREAM_S.last_error+2 mov word ptr [bx].XPX_STREAM_S.last_error, si ; Set new last error mov word ptr [bx].XPX_STREAM_S.last_error+2, es mov word ptr es:[si].IPX_PACKET_S.next_sibling, 0 mov word ptr es:[si].IPX_PACKET_S.next_sibling+2, 0 mov ax, dx ; See if we are the only packet here or ax, cx jnz @F ; Skip out of this ESR if we are not alone mov word ptr [bx].XPX_STREAM_S.first_error, si ;Set new first error mov word ptr [bx].XPX_STREAM_S.first_error+2, es ret @@: mov ds, dx ; DS:BX -> the first born mov bx, cx mov word ptr [bx].IPX_PACKET_S.next_sibling, si mov word ptr [bx].IPX_PACKET_S.next_sibling+2, es ret ; * Here's where we process the good transmissions listen40_esr: push ds ; DX:CX -> the first stream structure mov ax, @Data mov ds, ax mov cx, word ptr _First_Stream mov dx, word ptr _First_Stream+2 pop ds mov Last_Broad_Ptr, 0 mov Last_Broad_Ptr+2, 0 listen50_esr: cmp [bx].XPX_STREAM_S.total_listeners, 0 ; See if READ ONLY stream jz not_parent ; Skip this one if it is READ ONLY mov ax, word ptr [bx].XPX_STREAM_S.dest_node and ax, word ptr [bx].XPX_STREAM_S.dest_node+2 and ax, word ptr [bx].XPX_STREAM_S.dest_node+4 inc ax jnz @F ; Skip if it is not a broadcast type mov Last_Broad_Ptr, bx ; Save this for later mov Last_Broad_Ptr+2, ds jmp short not_parent ; Still not necessarily the right one @@: mov ax, word ptr [bx].XPX_STREAM_S.local_target+4 ; Match parent cmp word ptr es:[si].IPX_PACKET_S.src_node+4, ax jne not_parent mov ax, word ptr [bx].XPX_STREAM_S.local_target+2 cmp word ptr es:[si].IPX_PACKET_S.src_node+2, ax jne not_parent mov ax, word ptr [bx].XPX_STREAM_S.local_target cmp word ptr es:[si].IPX_PACKET_S.src_node, ax jne not_parent mov ax, word ptr es:[si].IPX_PACKET_S.src_network+2 cmp word ptr es:[si].IPX_PACKET_S.dest_network+2, ax jne not_parent mov ax, word ptr es:[si].IPX_PACKET_S.src_network cmp word ptr es:[si].IPX_PACKET_S.dest_network, ax je found_listener ; * At this point, the current structure has been determined not to be suitable not_parent: mov ax, cx ; See if we are at the end of our rope or ax, dx jz no_listener ; No stream match found mov ds, dx ; DS:BX -> next stream definition mov bx, cx mov cx, word ptr [bx].XPX_STREAM_S.next ; DX:CX -> next stream mov dx, word ptr [bx].XPX_STREAM_S.next+2 jmp listen50_esr ; Loop until we poop ; * Stream ID matches up with destination address, so add packet to stream list found_listener: add word ptr [bx].XPX_STREAM_S.total_receptions, 1 adc word ptr [bx].XPX_STREAM_S.total_receptions+2, 0 inc [bx].XPX_STREAM_S.unread_count mov ax, [bx].XPX_STREAM_S.unread_count ; Update our statistics cmp [bx].XPX_STREAM_S.maximum_unread, ax jnc found10_listener ; Skip if no need to update mov [bx].XPX_STREAM_S.maximum_unread, ax found10_listener: mov cx, word ptr [bx].XPX_STREAM_S.last_unread mov dx, word ptr [bx].XPX_STREAM_S.last_unread+2 mov word ptr [bx].XPX_STREAM_S.last_unread, si mov word ptr [bx].XPX_STREAM_S.last_unread+2, es mov word ptr es:[si].IPX_PACKET_S.next_sibling, 0 mov word ptr es:[si].IPX_PACKET_S.next_sibling+2, 0 mov ax, cx ; See if we need to do the first as well or ax, dx jnz @F ; All done if there are others mov word ptr [bx].XPX_STREAM_S.first_unread, si mov word ptr [bx].XPX_STREAM_S.first_unread+2, es ret @@: mov ds, dx ; DS:BX -> the first born mov bx, cx mov word ptr [bx].IPX_PACKET_S.next_sibling, si mov word ptr [bx].IPX_PACKET_S.next_sibling+2, es ret ; * At this point, packet is an orphan and must be sent off to farm or be glue no_listener: lds bx, dword ptr Last_Broad_Ptr ; DS:BX->last broadcast stream mov ax, ds or ax, bx jnz found_listener ; If one was found, use as last resort cmp _Ignore_Nomatch, al ; Ignore orphans or adopt jmp listen_again_buckwheat ; Put back on the mountaintop no10_listener: mov ax, @Data ; DS = our most lovable data segment mov ds, ax inc _Total_Nomatchs mov cx, word ptr _Last_Nomatch ; DX:CX -> last error packet mov dx, word ptr _Last_Nomatch+2 mov word ptr _Last_Nomatch, si ; Make it point to us mov word ptr _Last_Nomatch+2, es mov word ptr es:[si].IPX_PACKET_S.next_sibling, 0 mov word ptr es:[si].IPX_PACKET_S.next_sibling+2, 0 mov ax, cx ; See if we need to do the first as well or ax, dx jnz @F ; All done if there are others mov word ptr _First_Nomatch, si ; Set us as the new first error mov word ptr _First_Nomatch+2, es ret @@: mov ds, dx ; DS:BX -> the first born mov bx, cx mov word ptr [bx].IPX_PACKET_S.next_sibling, si mov word ptr [bx].IPX_PACKET_S.next_sibling+2, es ret listen_esr endp end [LISTING TWO] ; **************************************************************************** ; * Title: XPX_INIT.ASM -- Rahner James ; * Copyright (c) December 1991, Ryu Consulting, 916/722-1939 ; * File contains all the functions to support initializing IPX engine ; **************************************************************************** _XPX_INIT_ASM_ equ 1 include network.inc .data public IPX_Vector, _Socket_Life, _SPX_Version, _SPX_Max_Connections, _Our_Address public _SPX_Available_Connections, _SPX_Retry_Count IPX_Vector dw offset dummy_IPX_function,@Code ; -> IPX support function _Socket_Life db 0 ; 0=socket closed at app termination ; 0ffh= socket closed when requested _SPX_Version dw 0 ; SPX version #: MSByte=major, LSByte=minor _SPX_Max_Connections dw 0 ; Max number of SPX connections _SPX_Available_Connections dw 0 ; # of SPX connections available to app _SPX_Retry_Count db 0 ; Retry count for SPX establish connection _SPX_Bowser_Flag db 1 ; Watchdog flag, 0=disable, 1=enable _Our_Address label dword ; Global access for this structure network dd 0 ; Network address node db 6 dup(0) ; Node address socket dw 0 ; Socket number .code ; **************************************************************************** ; * int XPX_INIT( us SOCKET_NUMBER ) -- Initializes all IPX/SPX internals ; * Given: SOCKET_NUMBER = socket number to open for listening, 0 opens ; * the next available ; * Returns: 0 if IPX was initialized successfully ; * -1 = socket already open (!) ; * -2 = socket table full ; * -3 = IPX or SPX is not installed ; * Note: Initializes IPX vector, opens a listening socket for IPX driver. ; * Internal vectors, counter, & pointers are brought to initial conditions ; **************************************************************************** xpx_init proc uses di si, socket_number:word mov ax, 7a00h ; Get the IPX vector int 2fh ; Query the DOS multiplexer inc al ; AL = 0ffh if IPX is there jnz derr_xpx_init ; Quit in disgrace if it's not there mov IPX_Vector, di ; IPX function vector returned in ES:DI mov IPX_Vector+2, es ; * See if we need to close the old stuff down mov dx, socket or dx, dx ; See if we opened a socket jz xpx10_init ; Skip if we didn't cmp socket_number, 0 jz xpx20_init ; Skip if so cmp socket_number, dx ; See if it's the same as before je xpx20_init ; Skip if it is IPX 1 ; IPX Close Socket command ; * Now, open the socket xpx10_init: mov dx, socket_number mov al, _Socket_Life IPX_CHECK 0 ; IPX Open Socket command jnz done_xpx_init ; Quit if an error xpx20_init: mov socket, dx ; * Get our internetwork address mov ax, ds ; ES = DS mov es, ax mov si, offset network mov di, si IPX 9 ; IPX Get Internetwork Address command ; * Last, we have to initialize the SPX interface xor ax, ax IPX_CHECK 10h ; SPX Initialize command jz derr_xpx_init ; Quit if it's not there mov _SPX_Version, bx ; Save the information returned mov _SPX_Max_Connections, cx mov _SPX_Available_Connections, dx xor ax, ax ; Good return jmp short done_xpx_init derr_xpx_init: mov al, -3 ; It's gone McCreedy! done_xpx_init: cbw ret xpx_init endp ; **************************************************************************** ; * int DUMMY_IPX_FUNCTION( void ) -- Dummy function that returns error ; * code -10 so that system will not hang if not initialized ; * Given: nothing ; * Returns: -10 always ; **************************************************************************** dummy_IPX_function proc far mov ax, -10 ret dummy_IPX_function endp end Example 1: mov ax, 7a00h ; Function 7Ah, AL = 0 int 2fh ; MS-DOS Multiplex interrupt ; Returns with AL == 0FFh if xPX ; exists and ES:DI == xPX vector inc al ; Set ZERO if AL == -1 jnz outta_here ; Quit if xPX isn't around mov IPX_Vector, di ; Save the xPX entry vector mov IPX_Vector+2, es