CnC_Red_Alert/IPX/OK/IPX16A.ASM

1451 lines
26 KiB
NASM
Raw Normal View History

;
; Command & Conquer Red Alert(tm)
; Copyright 2025 Electronic Arts Inc.
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;
.8086
.model large
option segment:USE16
option readonly
option oldstructs
assume ds:@data
assume es:nothing
RECEIVE_BUFFER_LENGTH =1024
MAX_RECEIVE_BUFFERS =32
externdef GLOBALDOSALLOC:far
externdef GLOBALDOSFREE:far
.data
IPXCallOffset dw 0
IPXCallSegment dw 0
RealSegment dw 0
PseudoES dw 0
RegisterDump db 32h dup (0)
MyNetworkNumber db 4 dup (?)
MyNodeAddress db 6 dup (?)
MySocket dw ?
ReceiveBufferSegment dw ?
ReceiveBufferSelector dw ?
ReceiveECBOffset dw ?
CurrentReceiveBuffer dw ?
LastPassedReceiveBuffer dw ?
RealModeCallbackSegment dw ?
RealModeCallbackOffset dw ?
CallbackRegisterDump db 32h dup (0)
Listening db ?
NoReenter db 0
IPXHold db 0
OFFS =0
SEGM =2
;---------------------------------------------------------------------------
;These defines are for the IPX functions. The function number is specified
;by placing it in BX when IPX is called. There are two ways to invoke IPX:
;use interrupt 0x7a, or use a function whose address is obtained by calling
;interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI.
;This is the preferred method, since other apps are known to use int 0x7a.
;---------------------------------------------------------------------------
IPX_OPEN_SOCKET = 0000h
IPX_CLOSE_SOCKET = 0001h
IPX_GET_LOCAL_TARGET = 0002h
IPX_SEND_PACKET = 0003h
IPX_LISTEN_FOR_PACKET = 0004h
IPX_SCHEDULE_EVENT = 0005h
IPX_CANCEL_EVENT = 0006h
IPX_GET_INTERVAL_MARKER = 0008h
IPX_GET_INTERNETWORK_ADDRESS = 0009h
IPX_RELINQUISH_CONTROL = 000Ah
IPX_DISCONNECT_FROM_TARGET = 000Bh
;/*---------------------------------------------------------------------------
;These defines are for various IPX error codes:
;---------------------------------------------------------------------------*/
IPXERR_CONNECTION_SEVERED = 00ech
IPXERR_CONNECTION_FAILED = 00edh
IPXERR_NO_CONNECTION = 00eeh
IPXERR_CONNECTION_TABLE_FULL = 00efh
IPXERR_NO_CANCEL_ECB = 00f9h
IPXERR_NO_PATH = 00fah
IPXERR_ECB_INACTIVE = 00fch
IPXERR_INVALID_PACKET_LENGTH = 00fdh
IPXERR_SOCKET_TABLE_FULL = 00feh
IPXERR_SOCKET_ERROR = 00ffh
;/*---------------------------------------------------------------------------
;These defines are for various interrupt vectors and DPMI functions:
;---------------------------------------------------------------------------*/
IPX_INT = 007ah
DPMI_INT = 0031h
DPMI_ALLOC_DOS_MEM = 0100h
DPMI_FREE_DOS_MEM = 0101h
DPMI_CALL_REAL_INT = 0300h
DPMI_CALL_REAL_PROC = 0301h
DPMI_ALLOCATE_CALLBACK = 0303h
DPMI_RELEASE_CALLBACK = 0304h
DPMI_LOCK_MEM = 0600h
DPMI_UNLOCK_MEM = 0601h
request_buffer struct
len word ?
buffer_type byte ?
connect_number byte ?
request_buffer ends
request_local_target_buffer struct
lt_network_number db ?,?,?,?
lt_physical_node db ?,?,?,?,?,?
lt_socket dw ?
request_local_target_buffer ends
local_target_reply_buffer struct
lt_local_target db ?,?,?,?,?,?
local_target_reply_buffer ends
reply_buffer struct
lem word ?
network_number byte ?,?,?,?
physical_node byte ?,?,?,?,?,?
server_socket word ?
reply_buffer ends
userid_buffer struct
struct_len word ?
object_id byte ?,?,?,?
object_type byte ?,?
object_name byte 48 dup(?)
login_time byte 7 dup (?)
reserved word ?
userid_buffer ends
.code
.386
include <pcmacro.16>
externdef pascal _IPX_Initiialise:far16
externdef pascal _IPX_Uninitialise:far16
_IPX_Initialise proc far pascal
push ebx
push ecx
push edx
push esi
push edi
push ebp
push ds
push es
push fs
push gs
mov ax,7a00h
int 2fh
and eax,0ffh
cmp al,-1
setz al
test al,al
jz @f
mov bx,@data
mov ds,bx
mov [IPXCallSegment],es
mov [IPXCallOffset],di
@@: pop gs
pop fs
pop es
pop ds
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
ret
_IPX_Initialise endp
_IPX_Open_Socket95 proc far pascal uses bx cx dx si di ds es fs gs,
socket:word
mov bx,@data
mov ds,bx
mov bx,IPX_OPEN_SOCKET ;open socket
mov_w dx,[socket] ;socket number
mov_w [MySocket],dx ;save it for later
mov ax,0ffh ;long lived
call Call_IPX
ret
_IPX_Open_Socket95 endp
_IPX_Close_Socket95 proc far pascal uses ax bx cx dx si di ds es fs gs,
socket:word
mov bx,1
mov_w dx,[socket]
call Call_IPX
ret
_IPX_Close_Socket95 endp
_IPX_Uninitialise proc far pascal
;int 3
ret
_IPX_Uninitialise endp
_IPX_Get_Connection_Number95 proc far pascal uses bx cx dx si di ds es fs gs
mov ax,0dc00h
call Call_DOS
and eax,255
ret
_IPX_Get_Connection_Number95 endp
_IPX_Get_Internet_Address95 proc far pascal uses bx cx dx si di ds es fs gs,
connection_number :dword,
network_number_off :word,
network_number_seg :word,
physical_node_off :word,
physical_node_seg :word
local selector:word
local segmento:word
ret
ifdef cuts ;(unfinished)
;
; Allocate DOS memory for buffers passed to interrupt
;
int 3
xor ax,ax
mov bx,(sizeof reply_buffer + sizeof request_buffer + 15)
save bp
push ax
push bx
call GLOBALDOSALLOC
restore bp
test ax,ax
jz @@dpmi_error
mov [segmento],dx
mov [selector],ax
mov fs,dx
xor di,di
mov_w fs:[di.len],2
mov_b fs:[di.buffer_type],13h
mov_b al,[connection_number]
mov_b fs:[di.connect_number],al
mov di,sizeof request_buffer
mov fs:[di.len],12
mov ax,0e300h
mov ds,[segmento]
mov es,[segmento]
xor si,si
pusha
call Call_DOS
popa
les di,dword ptr [network_number_off]
mov ds,[selector]
mov si,sizeof request_buffer + network_number
movsd
les di,dword ptr [physical_node_off]
mov si,sizeof request_buffer + physical_node
movsd
movsw
;
; Save it here for posterity (or perhaps later use)
;
mov di,offset MyNetworkNumber
mov ax,@data
mov es,ax
mov ds,[selector]
mov si,sizeof request_buffer + network_number
movsd
mov di,offset MyNodeAddress
mov si,sizeof request_buffer + physical_node
movsd
movsw
;
; Free the DOS memory
;
mov bx,@data
mov ds,bx
mov es,bx
mov fs,bx
mov gs,bx
mov bx,[selector]
push bx
call GLOBALDOSFREE
xor ax,ax
ret
@@dpmi_error: mov ax,-1
ret
endif
_IPX_Get_Internet_Address95 endp
; Never called!!
_IPX_Get_User_ID95 proc far pascal uses bx cx dx si di ds es fs gs,
connection_number:dword,
user_id_off:word,
user_id_seg:word
local segmento:word
local selector:word
cmp [connection_number],0
jz @@return_error
;
; Allocate DOS memory for buffers passed to interrupt
;
xor ax,ax
mov bx,(sizeof reply_buffer + sizeof request_buffer + 15)
save bp
push ax
push bx
call GLOBALDOSALLOC
restore bp
test ax,ax
jz @@return_error
mov [segmento],dx
mov [selector],ax
mov fs,dx
xor di,di
mov ax,@data
mov ds,ax
mov_w fs:[di.len],2
mov_b fs:[di.buffer_type],16h
mov_b al,[connection_number]
mov_b fs:[di.connect_number],al
mov di,sizeof request_buffer
mov fs:[di.struct_len],sizeof userid_buffer-2
mov ax,0e300h
mov ds,[segmento]
mov es,[segmento]
xor si,si
pusha
call Call_DOS
popa
les di,dword ptr [user_id_off]
mov ds,[selector]
mov si,sizeof request_buffer + object_name
mov cx,48/4
rep movsd
;
; Free the DOS memory
;
mov bx,@data
mov ds,bx
mov es,bx
mov fs,bx
mov gs,bx
mov bx,[selector]
push bx
call GLOBALDOSFREE
xor ax,ax
ret
@@return_error: mov ax,-1
ret
_IPX_Get_User_ID95 endp
;---------------------------------------------------------------------------
;This is the IPX Packet structure. It's followed by the data itself, which
;can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this
;field; annotation of 'APP' means the application must set the field.
;NOTE: All header fields are ordered high-byte,low-byte.
;---------------------------------------------------------------------------*/
IPXHeaderType struct
CheckSum dw ? ; IPX: Not used; always 0xffff
IPXLength dw ? ; IPX: Total size, incl header & data
TransportControl db ? ; IPX: # bridges message crossed
PacketType db ? ; APP: Set to 4 for IPX (5 for SPX)
DestNetworkNumber db ?,?,?,? ; APP: destination Network Number
DestNetworkNode db ?,?,?,?,?,? ; APP: destination Node Address
DestNetworkSocket dw ? ; APP: destination Socket Number
SourceNetworkNumber db ?,?,?,? ; IPX: source Network Number
SourceNetworkNode db ?,?,?,?,?,? ; IPX: source Node Address
SourceNetworkSocket dw ? ; IPX: source Socket Number
IPXHeaderType ends
;/*---------------------------------------------------------------------------
;This is the IPX Event Control Block. It serves as a communications area
;between IPX and the application for a single IPX operation. You should set
;up a separate ECB for each IPX operation you perform.
;---------------------------------------------------------------------------*/
ECB struct
Link_Address dd ?
Event_Service_Routine dd ? ; APP: event handler (NULL=none)
InUse db ? ; IPX: 0 = event complete
CompletionCode db ? ; IPX: event's return code
SocketNumber dw ? ; APP: socket to send data through
ConnectionID dw ? ; returned by Listen (???)
RestOfWorkspace dw ?
DriverWorkspace db 12 dup (?)
ImmediateAddress db 6 dup (?) ; returned by Get_Local_Target
PacketCount dw ?
PacketAddress0 dd ?
PacketLength0 dw ?
PacketAddress1 dd ?
PacketLength1 dw ?
ECB ends
SEND_ECB_OFFSET =0
SEND_HEADER_OFFSET =(sizeof ECB +3) AND 0fffch
SEND_BUFFER_OFFSET =(SEND_HEADER_OFFSET + sizeof IPXHeaderType +3) AND 0fffch
_IPX_Send_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs,
send_address :far ptr byte,
send_buffer :far ptr byte,
send_buffer_len :word
local selector :word
local segmento :word
local dos_send_ecb :far ptr ECB
local dos_send_header :far ptr IPXHeaderType
local dos_send_buffer :far ptr byte
local completion_code :word
;
; Allocate required DOS memory
;
xor ax,ax
mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31)
save bp
push ax
push bx
call GLOBALDOSALLOC
restore bp
test ax,ax
jz @@error
mov [segmento],dx
mov [selector],ax
;
; Set up the pointers to the dos memory
;
mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET
mov_w [dos_send_ecb+SEGM],ax
mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET
mov_w [dos_send_header+SEGM],ax
mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET
mov_w [dos_send_buffer+SEGM],ax
;
; Clear out the send ECB
;
xor al,al
mov cx,sizeof ECB
les di,[dos_send_ecb]
rep stosb
;
; Clear out the send header
;
mov cx,sizeof IPXHeaderType
les di,[dos_send_header]
rep stosb
;
; Copy the data to be sent into the send buffer
;
mov cx,546
cmp_w cx,[send_buffer_len]
jle @@got_buffer_len
mov_w cx,[send_buffer_len]
@@got_buffer_len:
; mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe)
les di,[dos_send_buffer]
lds si,[send_buffer]
rep movsb
;
; Fill in the ECB
;
mov ax,@data
mov ds,ax
mov fs,ax ;keep ptr to data seg in fs
les di,[dos_send_ecb]
mov ax,[MySocket]
mov es:[di.SocketNumber],ax
mov es:[di.PacketCount],2
mov ax,[selector]
shl eax,16
mov_w ax,[dos_send_header+OFFS]
mov_d es:[di.PacketAddress0],eax
mov_w es:[di.PacketLength0],sizeof IPXHeaderType
mov_w ax,[dos_send_buffer+OFFS]
mov_d es:[di.PacketAddress1],eax
mov_w ax,[send_buffer_len]
mov_w es:[di.PacketLength1],ax
;
; Fill in the address field
;
lds si,[send_address]
mov eax,[si]
mov_d es:[di.ImmediateAddress],eax
mov ax,[si+4]
mov_w es:[di.ImmediateAddress+4],ax
;
; Fill in the outgoing header
;
les di,[dos_send_header]
mov es:[di.PacketType],4
push fs:[MySocket]
pop es:[di.DestNetworkSocket]
;
; Fill in the nwtowrk number and node address
;
mov_d eax,fs:[MyNetworkNumber]
mov_d es:[di.DestNetworkNumber],eax
mov_d eax,fs:[MyNodeAddress]
mov_d es:[di.DestNetworkNode],eax
mov_w ax,fs:[MyNodeAddress+4]
mov_w es:[di.DestNetworkNode+4],ax
;
; Send that sucker!
;
mov es,[selector]
mov si,SEND_ECB_OFFSET
mov bx,IPX_SEND_PACKET
pusha
push fs
call Call_IPX
pop fs
popa
mov fs:[PseudoES],0
;
; Wait for the send to finish
;
@@wait_send_loop:
lds si,[dos_send_ecb]
cmp [si.InUse],0
jz @@done
mov bx,IPX_RELINQUISH_CONTROL
save bp
call Call_IPX
restore bp
jmp @@wait_send_loop
;
; Get the completion code
;
@@done: lds si,[dos_send_ecb]
mov al,[si.CompletionCode]
xor ah,ah
mov [completion_code],ax
;
; Free the DOS memory
;
mov bx,@data
mov ds,bx
mov es,bx
mov fs,bx
mov gs,bx
mov bx,[selector]
save bp
push bx
call GLOBALDOSFREE
restore bp
cmp [completion_code],0
jnz @@error
@@success: mov ax,1
ret
@@error: xor ax,ax
ret
_IPX_Send_Packet95 endp
_IPX_Broadcast_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs,
send_buffer :far ptr byte,
send_buffer_len :word
local selector :word
local segmento :word
local dos_send_ecb :far ptr ECB
local dos_send_header :far ptr IPXHeaderType
local dos_send_buffer :far ptr byte
local completion_code :word
;
; Allocate required DOS memory
;
xor ax,ax
mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31)
save bp
push ax
push bx
call GLOBALDOSALLOC
restore bp
test ax,ax
jz @@error
mov [segmento],dx
mov [selector],ax
;
; Set up the pointers to the dos memory
;
mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET
mov_w [dos_send_ecb+SEGM],ax
mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET
mov_w [dos_send_header+SEGM],ax
mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET
mov_w [dos_send_buffer+SEGM],ax
;
; Clear out the send ECB
;
xor al,al
mov cx,sizeof ECB
les di,[dos_send_ecb]
rep stosb
;
; Clear out the send header
;
mov cx,sizeof IPXHeaderType
les di,[dos_send_header]
rep stosb
;
; Copy the data to be sent into the send buffer
;
mov cx,546
cmp_w cx,[send_buffer_len]
jle @@got_buffer_len
mov_w cx,[send_buffer_len]
@@got_buffer_len:
; mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe)
les di,[dos_send_buffer]
lds si,[send_buffer]
rep movsb
;
; Fill in the ECB
;
mov ax,@data
mov ds,ax
mov fs,ax ;keep ptr to data seg in fs
les di,[dos_send_ecb]
mov ax,[MySocket]
mov es:[di.SocketNumber],ax
mov es:[di.PacketCount],2
mov ax,[selector]
shl eax,16
mov_w ax,[dos_send_header+OFFS]
mov_d es:[di.PacketAddress0],eax
mov_w es:[di.PacketLength0],sizeof IPXHeaderType
mov_w ax,[dos_send_buffer+OFFS]
mov_d es:[di.PacketAddress1],eax
mov_w ax,[send_buffer_len]
mov_w es:[di.PacketLength1],ax
;
; Fill in the address field
;
mov_d es:[di.ImmediateAddress],0ffffffffh
mov_w es:[di.ImmediateAddress+4],0ffffh
;
; Fill in the outgoing header
;
les di,[dos_send_header]
mov es:[di.PacketType],4
push fs:[MySocket]
pop es:[di.DestNetworkSocket]
;
; Fill in the nwtowrk number and node address
;
mov_d es:[di.DestNetworkNumber],0ffffffffh
mov_d es:[di.DestNetworkNode],0ffffffffh
mov_w es:[di.DestNetworkNode+4],0ffffh
;
; Send that sucker!
;
mov es,[selector]
mov si,SEND_ECB_OFFSET
mov bx,IPX_SEND_PACKET
pusha
push fs
call Call_IPX
pop fs
popa
mov fs:[PseudoES],0
;
; Wait for the send to finish
;
@@wait_send_loop:
lds si,[dos_send_ecb]
cmp [si.InUse],0
jz @@done
mov bx,IPX_RELINQUISH_CONTROL
save bp
call Call_IPX
restore bp
jmp @@wait_send_loop
;
; Get the completion code
;
@@done: lds si,[dos_send_ecb]
mov al,[si.CompletionCode]
xor ah,ah
mov [completion_code],ax
;
; Free the DOS memory
;
mov bx,@data
mov ds,bx
mov es,bx
mov fs,bx
mov gs,bx
mov bx,[selector]
save bp
push bx
call GLOBALDOSFREE
restore bp
cmp [completion_code],0
jnz @@error
@@success: mov ax,1
ret
@@error: xor ax,ax
ret
_IPX_Broadcast_Packet95 endp
_IPX_Get_Local_Target95 proc far pascal uses ebx ecx edx esi edi ds es fs gs,
dest_network :far ptr byte,
dest_node :far ptr byte,
socket :word,
bridge_address :far ptr byte
local segmento :word
local selector :word
local result_code :word
;
; Allocate DOS memory for buffers passed to interrupt
;
xor ax,ax
mov bx,(sizeof local_target_reply_buffer + \
sizeof request_local_target_buffer + 15)
save bp
push ax
push bx
call GLOBALDOSALLOC
restore bp
test ax,ax
jz @@return_error
mov [segmento],dx
mov [selector],ax
mov fs,ax
xor di,di
;
; Init the request structure
;
lds si,[dest_network]
mov_d eax,[si]
mov_d fs:[di.lt_network_number],eax
lds si,[dest_node]
mov eax,[si]
mov_d fs:[di.lt_physical_node],eax
mov_w ax,[si+4]
mov_w fs:[di.lt_physical_node+4],ax
mov ax,[socket]
mov fs:[di.lt_socket],ax
mov bx,IPX_GET_LOCAL_TARGET
mov ax,[selector]
mov ds,ax
xor si,si
mov es,ax
mov di,sizeof request_local_target_buffer
push bp
call Call_IPX
pop bp
mov [result_code],ax
mov ds,[selector]
mov si,sizeof request_local_target_buffer + lt_local_target
les di,[bridge_address]
movsd
movsw
;
; Free the DOS memory
;
mov bx,@data
mov ds,bx
mov es,bx
mov fs,bx
mov gs,bx
mov bx,[selector]
save bp
push bx
call GLOBALDOSFREE
restore bp
;
; Return the IPX result code
;
mov ax,[result_code]
ret
@@return_error: mov ax,-1
ret
_IPX_Get_Local_Target95 endp
_IPX_Get_Outstanding_Buffer95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs,
copy_receive_buffer:far ptr byte
mov ax,@data
mov ds,ax
xor ax,ax
mov si,[LastPassedReceiveBuffer]
cmp si,[CurrentReceiveBuffer]
jz @@done
push ds
mov cx,RECEIVE_BUFFER_LENGTH/4
mov ds,[ReceiveBufferSelector]
les di,[copy_receive_buffer]
rep movsd
pop ds
cmp si,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS
jc @@no_wrap
xor si,si
@@no_wrap: mov [LastPassedReceiveBuffer],si
mov ax,1
@@done: ret
_IPX_Get_Outstanding_Buffer95 endp
Receive_Callback proc far
pushf
cli
cld
pushad
push ds
push es
push fs
push gs
mov ax,@data
mov ds,ax
cmp [NoReenter],0
jnz @@out
mov [NoReenter],1
mov ax,[CurrentReceiveBuffer]
add ax,RECEIVE_BUFFER_LENGTH
cmp ax,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS
jc @@no_wrap
xor ax,ax
@@no_wrap: mov [CurrentReceiveBuffer],ax
call Listen_For_Packet
mov ax,@data
mov ds,ax
mov [NoReenter],0
@@out: pop gs
pop fs
pop es
pop ds
popad
popf
ret
Receive_Callback endp
Init_Receive_ECB proc near uses eax ebx ecx edx esi edi ebp ds es
mov ax,@data
mov ds,ax
mov es,[ReceiveBufferSelector]
;
; Clear out the ECB
;
mov di,[ReceiveECBOffset]
mov cx,sizeof ECB
xor al,al
rep stosb
;
; Set up the ECB
;
;
;General ECB data
mov di,[ReceiveECBOffset]
mov ax,[MySocket]
mov es:[di.SocketNumber],ax
mov es:[di.PacketCount],2
;
; Packet address for IPX header
mov ax,[CurrentReceiveBuffer]
mov bx,[ReceiveBufferSelector]
mov_w es:[di.PacketAddress0+OFFS],ax
mov_w es:[di.PacketAddress0+SEGM],bx
mov es:[di.PacketLength0],sizeof IPXHeaderType
;
; Packet address for receive buffer
mov ax,[CurrentReceiveBuffer]
add ax,sizeof IPXHeaderType
mov_w es:[di.PacketAddress1+OFFS],ax
mov_w es:[di.PacketAddress1+SEGM],bx
mov es:[di.PacketLength1],546
;
; Set up the callback address
mov ax,[RealModeCallbackOffset]
mov_w es:[di.Event_Service_Routine+OFFS],offset Receive_Callback ;ax
mov ax,[RealModeCallbackSegment]
mov_w es:[di.Event_Service_Routine+SEGM],cs ;ax
ret
Init_Receive_ECB endp
_IPX_Start_Listening95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs
mov ax,@data
mov ds,ax
cmp [Listening],0
jnz @@restart ;already listening
;
; Allocate and lock DOS memory for listen
; ECB and receive buffers
;
mov bx,(RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + sizeof ECB + 15)
xor ax,ax
save bp,ds
push ax
push bx
call GLOBALDOSALLOC
restore bp,ds
test ax,ax
jz @@error
mov [ReceiveBufferSegment],dx
mov [ReceiveBufferSelector],ax
;
; Set up pointers to the DOS memory
;
mov [ReceiveECBOffset],RECEIVE_BUFFER_LENGTH * MAX_RECEIVE_BUFFERS
mov [CurrentReceiveBuffer],0 ;1st receive buffer
mov [LastPassedReceiveBuffer],0
;
; Set up a real mode callback function
;
;mov ax,DPMI_ALLOCATE_CALLBACK
;mov si,offset Receive_Callback
;mov di,offset CallbackRegisterDump
;push cs
;pop ds
;mov bx,@data
;mov es,bx
;save bp
;int 31h
;restore bp
;bcs @@error
mov ax,@data
mov ds,ax
mov [RealModeCallbackSegment],cx
mov [RealModeCallbackOffset],dx
@@restart: save ds
call Listen_For_Packet
restore ds
mov [Listening],1
@@out: mov ax,1
ret
@@error: xor ax,ax
ret
_IPX_Start_Listening95 endp
_IPX_Shut_Down95 proc far pascal uses eax ebx ecx edx esi edi ebp ds es fs gs
mov ax,@data
mov ds,ax
cmp [Listening],0
jz @@out
;
; Stop listening for a packet
;
mov es,[ReceiveBufferSelector]
mov si,[ReceiveECBOffset]
mov bx,IPX_CANCEL_EVENT
save bp,ds
call Call_IPX
restore bp,ds
;
; Remove the real mode callback function
;
;mov cx,[RealModeCallbackSegment]
;mov dx,[RealModeCallbackOffset]
;mov ax,DPMI_RELEASE_CALLBACK
;save bp,ds
;int 31h
;restore bp,ds
;
; Free the DOS memory
;
mov ax,@data
mov ds,ax
mov es,ax
mov ax,[ReceiveBufferSelector]
save bp,ds
push ax
call GLOBALDOSFREE
restore bp,ds
mov [Listening],0
@@out: ret
_IPX_Shut_Down95 endp
Listen_For_Packet proc near uses ebx ecx edx esi edi ebp ds es fs gs
call Init_Receive_ECB
mov ax,@data
mov ds,ax
mov es,[ReceiveBufferSelector]
mov si,[ReceiveECBOffset]
mov bx,IPX_LISTEN_FOR_PACKET
save ds
call Call_IPX
restore ds
mov [PseudoES],0
ret
Listen_For_Packet endp
Call_IPX proc far
push gs
push ax
mov ax,@data
mov gs,ax
pop ax
push bp
mov bp,offset IPXCallOffset
call dword ptr gs:[bp]
pop bp
pop gs
ret
push gs
push ax
mov ax,@data
mov gs,ax
pop ax
;
; Prevent reenterancy
;
@@hold: ;cmp gs:[IPXHold],0
;jnz @@hold
mov gs:[IPXHold],1
;
; Dump the registers first so we can use them
;
mov_d gs:[RegisterDump+0ch],0
mov_d gs:[RegisterDump],edi
mov_d gs:[RegisterDump+4],esi
mov_d gs:[RegisterDump+8],ebp
mov_d gs:[RegisterDump+10h],ebx
mov_d gs:[RegisterDump+14h],edx
mov_d gs:[RegisterDump+18h],ecx
mov_d gs:[RegisterDump+1ch],eax
push gs
xor ax,ax
push ax
mov ax,4096 ;4k real mode stack
push ax
call GLOBALDOSALLOC
pop gs
mov bx,4094 ;stack ptr
test ax,ax
jnz @f
xor bx,bx
;
; Set up stack addr to zero - DPMI will supply a real mode stack
;
@@: mov_w gs:[RegisterDump+2eh],bx ;sp
mov_w gs:[RegisterDump+30h],dx ;ss
save ax ;save selector so we can free the memory later
;
; Dump the flags
;
pushf
pop gs:[word ptr RegisterDump+20h]
;
; Set up all segment register areas to point to our real mode segment
;
mov_w gs:[RegisterDump+22h],es
mov_w gs:[RegisterDump+24h],ds
mov_w gs:[RegisterDump+26h],fs
mov_w gs:[RegisterDump+28h],gs
cmp gs:[PseudoES],0
jz @f
mov ax,gs:[PseudoES]
mov_w gs:[RegisterDump+22h],ax
@@:
;
; Set up the address to call
;
mov ax,gs:[IPXCallSegment]
mov_w gs:[RegisterDump+2ch],ax
mov ax,gs:[IPXCallOffset]
mov_w gs:[RegisterDump+2ah],ax
mov ax,DPMI_CALL_REAL_INT ; Call real mode procedure with far return frame
xor bh,bh ; flags - should be zero
mov bl,07ah ; IPX interrupt
mov ecx,0 ;512 ; number of words to copy from the protected mode stack
mov di,offset RegisterDump
push gs
pop es
save gs
;push ss
;push sp
int 31h ;DPMI interrupt
;pop ax
;pop bx
;pushf
;pop cx
;cli
;mov sp,ax
;mov ss,bx
;add sp,2
;push cx
;popf
restore gs
mov ax,@data
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
call GLOBALDOSFREE
mov_d eax,gs:[RegisterDump+1ch]
mov gs:[IPXHold],0
pop gs
ret
Call_IPX endp
Call_DOS proc far
push gs
push ax
mov ax,@data
mov gs,ax
pop ax
;
; Dump the registers first so we can use them
;
mov_d gs:[RegisterDump],edi
mov_d gs:[RegisterDump+4],esi
mov_d gs:[RegisterDump+8],ebp
mov_d gs:[RegisterDump+10h],ebx
mov_d gs:[RegisterDump+14h],edx
mov_d gs:[RegisterDump+18h],ecx
mov_d gs:[RegisterDump+1ch],eax
;
; Dump the flags
;
pushf
pop gs:[word ptr RegisterDump+20h]
;
; Set up all segment register areas to point to our real mode segment
;
mov_w gs:[RegisterDump+22h],es
mov_w gs:[RegisterDump+24h],ds
mov_w gs:[RegisterDump+26h],fs
mov_w gs:[RegisterDump+28h],gs
;
; Set up stack addr to zero - DPMI will supply a real mode stack
;
xor ax,ax
mov_w gs:[RegisterDump+2eh],ax ;sp
mov_w gs:[RegisterDump+30h],ax ;ss
mov ax,DPMI_CALL_REAL_INT; Simulate real mode interrupt
xor bh,bh ; flags - should be zero
mov bl,21h ; interrupt number
mov ecx,0 ;512 ; number of words to copy from the protected mode stack
mov di,offset RegisterDump
push gs
pop es
save gs
int 31h ;DPMI interrupt
restore gs
mov_d edi,gs:[RegisterDump]
mov_d esi,gs:[RegisterDump+4]
mov_d ebp,gs:[RegisterDump+8]
mov_d ebx,gs:[RegisterDump+10h]
mov_d edx,gs:[RegisterDump+14h]
mov_d ecx,gs:[RegisterDump+18h]
mov_d eax,gs:[RegisterDump+1ch]
mov_w es,gs:[RegisterDump+22h]
mov_w ds,gs:[RegisterDump+24h]
pop gs
ret
Call_DOS endp
end