1473 lines
47 KiB
NASM
1473 lines
47 KiB
NASM
;
|
|
; 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/>.
|
|
;
|
|
|
|
|
|
;***************************************************************************
|
|
;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C **
|
|
;***************************************************************************
|
|
;* *
|
|
;* Project Name : Command & Conquer *
|
|
;* *
|
|
;* File Name : IPX16a.asm *
|
|
;* *
|
|
;* Programmer : Steve Tall *
|
|
;* *
|
|
;* Start Date : January 26th, 1996 *
|
|
;* *
|
|
;* Last Update : January 26th, 1996 [ST] *
|
|
;* *
|
|
;*-------------------------------------------------------------------------*
|
|
;* Overview: *
|
|
;* 16-bit protected mode windows code. This code sits in a 16 bit .DLL *
|
|
;* and provides access to IPX functions via the standard DOS interface. *
|
|
;* *
|
|
;* Strangely enough, although you would expect to have to use segment *
|
|
;* addresses when passing data to and from the DOS IPX services, it only *
|
|
;* seems to work with selector addressing. This could be something to do *
|
|
;* with being in a windows .DLL. *
|
|
;* *
|
|
;* It seems that in Win95, the DOS services are stubbed to just pass up *
|
|
;* to NWLINK which is the win95 32bit netware services layer. If we ever *
|
|
;* have direct access to that then all this code can go away. *
|
|
;* *
|
|
;* *
|
|
;*-----------------------------------------------------------------------------------------------*
|
|
;* Functions: *
|
|
;* _IPX_Initialise -- confirms the presence of IPX and gets the entry point for subsequent calls *
|
|
;* _IPX_Open_Socket95 -- opens the given socket number *
|
|
;* IPX_Close_Socket95 -- closes the given socket number *
|
|
;* _IPX_Get_Connection_Number95 -- get the users local 'connection number' *
|
|
;* _IPX_Send_Packet95 -- Send a packet via IPX *
|
|
;* _IPX_Broadcast_Packet95 -- Broadcast a packet via IPX *
|
|
;* _IPX_Get_Local_Target95 -- Gets the node address of a destination or bridge *
|
|
;* _IPX_Get_Outstanding_Buffer95 -- return contents of the next unread buffer in the buffer list *
|
|
;* Receive_Callback -- IPX callback function. Called when a new packet arrives *
|
|
;* Init_Receive_ECB -- initialises an ECB for receiving a packet *
|
|
;* _IPX_Start_Listening95 -- initialise DOS memory for receive buffers and start listening for *
|
|
;* IPX packets *
|
|
;* _IPX_Shut_Down95 -- finish an IPX session. Releases any allocated resources except the socket *
|
|
;* Listen_For_Packet -- Start IPX listening for an incoming packet *
|
|
;* Call_IPX -- Make a call to the IPX entry point *
|
|
;* Call_DOS -- use DPMI to call a DOS interrupt service *
|
|
;* *
|
|
;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
|
|
|
|
|
|
.8086 ;cant specify .386 until we are into the code segment
|
|
|
|
|
|
.model large
|
|
option segment:USE16 ;16-bit segment
|
|
option readonly
|
|
option oldstructs
|
|
|
|
|
|
assume ds:@data ;ds is defauly segment register for our data segment
|
|
assume es:nothing
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Constants
|
|
;
|
|
RECEIVE_BUFFER_LENGTH =1024 ;length of each DOS memory receive buffer
|
|
MAX_RECEIVE_BUFFERS =32 ;number of receive buffers allocated
|
|
|
|
OFFS =0 ;offset of OFFSET part of a far ptr
|
|
SEGM =2 ;offset of SEGMENT part of a far ptr
|
|
|
|
SEND_ECB_OFFSET =0 ;offset of SEND ecb
|
|
SEND_HEADER_OFFSET =(sizeof ECB +3) AND 0fffch ;offset of our send header
|
|
SEND_BUFFER_OFFSET =(SEND_HEADER_OFFSET + sizeof IPXHeaderType +3) AND 0fffch ;offset of our send buffer
|
|
|
|
;---------------------------------------------------------------------------
|
|
;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
|
|
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Windows API functions
|
|
;
|
|
externdef GLOBALDOSALLOC:far
|
|
externdef GLOBALDOSFREE:far
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Global initialised data
|
|
;
|
|
.data
|
|
|
|
IPXCallOffset dw 0
|
|
IPXCallSegment dw 0
|
|
|
|
RegisterDump db 32h dup (0)
|
|
NoReenter db 0
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Global uninitialised data
|
|
;
|
|
.data?
|
|
|
|
MySocket dw ?
|
|
ReceiveBufferSegment dw ?
|
|
ReceiveBufferSelector dw ?
|
|
ReceiveECBOffset dw ?
|
|
CurrentReceiveBuffer dw ?
|
|
LastPassedReceiveBuffer dw ?
|
|
|
|
ReceiveDescriptor db 8 dup (?)
|
|
OriginalReceiveDescriptor db 8 dup (?)
|
|
|
|
Listening db ?
|
|
ListeningFailed db ?
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Structures
|
|
;
|
|
|
|
;---------------------------------------------------------------------------
|
|
;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
|
|
|
|
|
|
|
|
;
|
|
; Structures used for Get_Local_Target calls
|
|
;
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;#############################################################################
|
|
;
|
|
; Start of code segment
|
|
;
|
|
;
|
|
.code
|
|
|
|
.386 ;now we can specify 386 instructions. Its still a 16 bit code segment though.
|
|
|
|
include <pcmacro.16> ;include 16 bit instruction macros
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* _IPX_Initialise -- confirms the presence of IPX and gets the entry point for subsequent calls *
|
|
;* *
|
|
;* *
|
|
;* INPUT: eax - nothing *
|
|
;* *
|
|
;* OUTPUT: true if IPX was initialised *
|
|
;* *
|
|
;* 32bit PROTO: extern BOOL __stdcall IPX_Initialise(void); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
_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
|
|
|
|
; Call the multiplex interrupt to confirm the presence of IPX
|
|
; The real mode entry point is returned in es:di if present
|
|
;
|
|
mov ax,7a00h
|
|
int 2fh
|
|
and eax,0ffh
|
|
cmp al,-1
|
|
setz al
|
|
|
|
test al,al
|
|
jz @f
|
|
|
|
; Save the real mode entry point
|
|
;
|
|
mov bx,@data
|
|
mov ds,bx
|
|
mov [IPXCallSegment],es
|
|
mov [IPXCallOffset],di
|
|
mov [Listening],0
|
|
mov [ListeningFailed],0
|
|
|
|
@@: 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 -- opens the given socket number *
|
|
;* *
|
|
;* *
|
|
;* INPUT: socket - socket number to open *
|
|
;* *
|
|
;* OUTPUT: 0 if socket opened successfully *
|
|
;* *
|
|
;* 32bit PROTO: extern int __stdcall IPX_Open_Socket95(int socket); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_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 -- closes the given socket number *
|
|
;* *
|
|
;* *
|
|
;* INPUT: socket - socket number to close *
|
|
;* *
|
|
;* OUTPUT: nothing *
|
|
;* *
|
|
;* 32bit PROTO: extern void __stdcall IPX_Close_Socket95(int socket); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_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_Get_Connection_Number95 -- get the users local 'connection number' *
|
|
;* *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: connection number. 0 if user isnt logged into novell *
|
|
;* *
|
|
;* 32bit PROTO: extern int __stdcall IPX_Get_Connection_Number95(void); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_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_Send_Packet95 -- Send a packet via IPX *
|
|
;* *
|
|
;* *
|
|
;* INPUT: send_address - ptr to address of destination for packet *
|
|
;* send_buffer - ptr to data to be sent *
|
|
;* send_buffer_len - length of data in send_buffer *
|
|
;* network_num - network number *
|
|
;* node_address - node address *
|
|
;* *
|
|
;* OUTPUT: true if successfully sent *
|
|
;* *
|
|
;* 32bit PROTO: extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, *
|
|
;* int, unsigned char*, unsigned char*); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_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,
|
|
network_num :far ptr byte,
|
|
node_address :far ptr byte
|
|
|
|
local selector :word ;DOS memory selector
|
|
local segmento :word ;DOS memory segment
|
|
local dos_send_ecb :far ptr ECB ;ptr to send ECB
|
|
local dos_send_header :far ptr IPXHeaderType ;ptr to IPX header for sending
|
|
local dos_send_buffer :far ptr byte ;ptr to copy of buffer to be sent
|
|
local completion_code :word ;IPX send packet completion code
|
|
|
|
; Allocate required DOS memory
|
|
; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory
|
|
; GlobalDosAlloc returns the segment in dx and a selector in ax
|
|
xor ax,ax
|
|
mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31)
|
|
push bp
|
|
push ax
|
|
push bx
|
|
call GLOBALDOSALLOC
|
|
pop bp
|
|
test ax,ax
|
|
jz @@error
|
|
mov [segmento],dx ;save the segment pointer
|
|
mov [selector],ax ;save the selector pointer
|
|
|
|
;
|
|
; 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 IPX header
|
|
;
|
|
mov cx,sizeof IPXHeaderType
|
|
les di,[dos_send_header]
|
|
rep stosb
|
|
|
|
;
|
|
; Copy the data to be sent into the DOS memory send buffer
|
|
;
|
|
mov cx,546 ;maximum size of IPX data packet
|
|
cmp_w cx,[send_buffer_len]
|
|
jle @@got_buffer_len
|
|
mov_w cx,[send_buffer_len]
|
|
@@got_buffer_len:
|
|
les di,[dos_send_buffer]
|
|
lds si,[send_buffer]
|
|
rep movsb
|
|
|
|
;
|
|
; Fill in the send 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 network number and node address
|
|
;
|
|
lds si,[network_num]
|
|
mov_d eax,[si]
|
|
mov_d es:[di.DestNetworkNumber],eax
|
|
lds si,[node_address]
|
|
mov_d eax,[si]
|
|
mov_d es:[di.DestNetworkNode],eax
|
|
mov_w ax,[si+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
|
|
call Call_IPX
|
|
popa
|
|
|
|
;
|
|
; Wait for the send to finish by monitoring the 'in use' flag of the ECB
|
|
;
|
|
@@wait_send_loop:
|
|
lds si,[dos_send_ecb]
|
|
cmp [si.InUse],0
|
|
jz @@done
|
|
|
|
;
|
|
; Let IPX 'breath' if send isnt complete
|
|
;
|
|
mov bx,IPX_RELINQUISH_CONTROL
|
|
push bp
|
|
call Call_IPX
|
|
pop bp
|
|
jmp @@wait_send_loop
|
|
|
|
|
|
;
|
|
; Get the completion code and save it for posterity
|
|
;
|
|
@@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]
|
|
push bp
|
|
push bx
|
|
call GLOBALDOSFREE
|
|
pop bp
|
|
|
|
;
|
|
; Set up the return value
|
|
;
|
|
cmp [completion_code],0
|
|
jnz @@error
|
|
|
|
@@success: mov ax,1
|
|
ret
|
|
|
|
@@error: xor ax,ax
|
|
ret
|
|
|
|
|
|
_IPX_Send_Packet95 endp
|
|
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* _IPX_Broadcast_Packet95 -- Broadcast a packet via IPX *
|
|
;* *
|
|
;* *
|
|
;* INPUT: send_buffer - ptr to data to be sent *
|
|
;* send_buffer_len - length of data in send_buffer *
|
|
;* *
|
|
;* OUTPUT: true if successfully sent *
|
|
;* *
|
|
;* 32bit PROTO: extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_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 ;DOS memory selector
|
|
local segmento :word ;DOS memory segment
|
|
local dos_send_ecb :far ptr ECB ;ptr to send ECB
|
|
local dos_send_header :far ptr IPXHeaderType ;ptr to IPX header for sending
|
|
local dos_send_buffer :far ptr byte ;ptr to copy of buffer to be sent
|
|
local completion_code :word ;IPX send packet completion code
|
|
|
|
; Allocate required DOS memory
|
|
; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory
|
|
; GlobalDosAlloc returns the segment in dx and a selector in ax
|
|
xor ax,ax
|
|
mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31)
|
|
push bp
|
|
push ax
|
|
push bx
|
|
call GLOBALDOSALLOC
|
|
pop bp
|
|
test ax,ax
|
|
jz @@error
|
|
mov [segmento],dx ;save the segment pointer
|
|
mov [selector],ax ;save the selector pointer
|
|
|
|
;
|
|
; 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 ;maximum size of IPX data packet
|
|
cmp_w cx,[send_buffer_len]
|
|
jle @@got_buffer_len
|
|
mov_w cx,[send_buffer_len]
|
|
@@got_buffer_len:
|
|
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 network 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
|
|
|
|
;
|
|
; Wait for the send to finish by monitoring the 'in use' flag of the ECB
|
|
;
|
|
@@wait_send_loop:
|
|
lds si,[dos_send_ecb]
|
|
cmp [si.InUse],0
|
|
jz @@done
|
|
|
|
;
|
|
; Let IPX 'breath' if send isnt complete
|
|
;
|
|
mov bx,IPX_RELINQUISH_CONTROL
|
|
push bp
|
|
call Call_IPX
|
|
pop bp
|
|
jmp @@wait_send_loop
|
|
|
|
|
|
;
|
|
; Get the completion code and save it for posterity
|
|
;
|
|
@@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]
|
|
push bp
|
|
push bx
|
|
call GLOBALDOSFREE
|
|
pop bp
|
|
|
|
;
|
|
; Set up the return value
|
|
;
|
|
cmp [completion_code],0
|
|
jnz @@error
|
|
|
|
@@success: mov ax,1
|
|
ret
|
|
|
|
@@error: xor ax,ax
|
|
ret
|
|
|
|
|
|
_IPX_Broadcast_Packet95 endp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* _IPX_Get_Local_Target95 -- Gets the node address of a destination or bridge *
|
|
;* *
|
|
;* *
|
|
;* INPUT: dest_network - ptr to network number of target *
|
|
;* dest_node - ptr to node number of target *
|
|
;* socket - socket number *
|
|
;* bridge_address - ptr to buffer to store immediate address in *
|
|
;* *
|
|
;* OUTPUT: IPX result code. -1 if error *
|
|
;* *
|
|
;* 32bit PROTO: extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, *
|
|
;* unsigned short, unsigned char*); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
_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 ; DOS memory segment
|
|
local selector :word ; DOS memory selector
|
|
local result_code :word ; return code
|
|
|
|
; Allocate required DOS memory for data passed to IPX
|
|
; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory
|
|
; GlobalDosAlloc returns the segment in dx and a selector in ax
|
|
xor ax,ax
|
|
mov bx,(sizeof local_target_reply_buffer + \
|
|
sizeof request_local_target_buffer + 15)
|
|
push bp
|
|
push ax
|
|
push bx
|
|
call GLOBALDOSALLOC
|
|
pop bp
|
|
test ax,ax
|
|
jz @@return_error
|
|
|
|
mov [segmento],dx ; save the segment
|
|
mov [selector],ax ; save the selector
|
|
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
|
|
|
|
;
|
|
; Set up the registers to call IPX
|
|
;
|
|
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
|
|
|
|
;
|
|
; Call IPX
|
|
;
|
|
push bp
|
|
call Call_IPX
|
|
pop bp
|
|
|
|
;
|
|
; Save the result code so we can return it
|
|
;
|
|
mov [result_code],ax
|
|
|
|
;
|
|
; Copy the bridge address into the return buffer
|
|
;
|
|
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]
|
|
push bp
|
|
push bx
|
|
call GLOBALDOSFREE
|
|
pop 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 -- return contents of the next unread buffer in the buffer list *
|
|
;* *
|
|
;* *
|
|
;* INPUT: copy_receive_buffer -- ptr to buffer that data will be copied to *
|
|
;* *
|
|
;* OUTPUT: number of buffers returned (0 or 1) *
|
|
;* *
|
|
;* 32bit PROTO: extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
_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
|
|
|
|
;
|
|
; If the receive buffer pointer is the same as the pointer to the last buffer
|
|
; returned then there is no outstanding data to return so just exit
|
|
;
|
|
xor ax,ax
|
|
mov si,[LastPassedReceiveBuffer]
|
|
cmp si,[CurrentReceiveBuffer]
|
|
jz @@done
|
|
|
|
;
|
|
; Copy the IPX receive data buffer into the buffer provided by the caller
|
|
;
|
|
push ds
|
|
mov cx,RECEIVE_BUFFER_LENGTH/4
|
|
mov ds,[ReceiveBufferSelector]
|
|
les di,[copy_receive_buffer]
|
|
rep movsd
|
|
pop ds
|
|
|
|
;
|
|
; Update the pointer to the last buffer we copied
|
|
;
|
|
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 -- IPX callback function. Called when a new packet arrives *
|
|
;* *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: nothing *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
Receive_Callback proc far
|
|
|
|
;
|
|
; Save everything
|
|
;
|
|
pushf
|
|
cli
|
|
cld
|
|
pushad
|
|
push ds
|
|
push es
|
|
push fs
|
|
push gs
|
|
|
|
;
|
|
; Dont reenter
|
|
;
|
|
mov ax,@data
|
|
mov ds,ax
|
|
cmp [NoReenter],0
|
|
jnz @@out
|
|
mov [NoReenter],1
|
|
|
|
;
|
|
; Update the receive buffer pointer to point to the next buffer
|
|
;
|
|
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
|
|
|
|
;
|
|
; Listen for another packet
|
|
;
|
|
call Listen_For_Packet
|
|
mov ax,@data
|
|
mov ds,ax
|
|
mov [NoReenter],0
|
|
|
|
;
|
|
; Restore everything and exit
|
|
;
|
|
@@out: pop gs
|
|
pop fs
|
|
pop es
|
|
pop ds
|
|
popad
|
|
popf
|
|
ret
|
|
|
|
Receive_Callback endp
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* Init_Receive_ECB -- initialises an ECB for receiving a packet *
|
|
;* *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: nothing *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
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_w es:[di.Event_Service_Routine+OFFS],offset Receive_Callback
|
|
mov_w es:[di.Event_Service_Routine+SEGM],cs
|
|
|
|
ret
|
|
|
|
Init_Receive_ECB endp
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* _IPX_Start_Listening95 -- initialise DOS memory for receive buffers and start listening for *
|
|
;* IPX packets *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: true if we started listening *
|
|
;* *
|
|
;* 32bit PROTO: extern BOOL __stdcall IPX_Start_Listening95(void); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
_IPX_Start_Listening95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs
|
|
|
|
;
|
|
; Check that we arent already listening
|
|
;
|
|
mov ax,@data
|
|
mov ds,ax
|
|
cmp [ListeningFailed],0
|
|
jnz @@restart
|
|
cmp [Listening],0
|
|
jnz @@out ;already listening
|
|
|
|
;
|
|
; Allocate and lock DOS memory for listen ECB and receive buffers
|
|
; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory
|
|
; GlobalDosAlloc returns the segment in dx and a selector in ax
|
|
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 ;save the DOS memory segment in a global
|
|
mov [ReceiveBufferSelector],ax ;save the DOS memory selector in a global
|
|
|
|
saveall
|
|
; int 3
|
|
mov ax,@data
|
|
mov es,ax
|
|
mov ax,0bh
|
|
mov bx,es:[ReceiveBufferSelector]
|
|
mov di,offset OriginalReceiveDescriptor
|
|
int 31h
|
|
bcc @f
|
|
; int 3
|
|
@@: restall
|
|
|
|
|
|
;
|
|
; Set up pointers to the DOS memory
|
|
;
|
|
mov [ReceiveECBOffset],RECEIVE_BUFFER_LENGTH * MAX_RECEIVE_BUFFERS
|
|
mov [CurrentReceiveBuffer],0 ;1st receive buffer
|
|
mov [LastPassedReceiveBuffer],0
|
|
|
|
;
|
|
; Start listening
|
|
;
|
|
@@restart: mov [ListeningFailed],0
|
|
push ds
|
|
call Listen_For_Packet
|
|
pop ds
|
|
mov [Listening],1
|
|
|
|
@@out: mov ax,1
|
|
ret
|
|
|
|
@@error: xor ax,ax
|
|
ret
|
|
|
|
_IPX_Start_Listening95 endp
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* _IPX_Shut_Down95 -- finish an IPX session. Releases any allocated resources except the socket *
|
|
;* *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: nothing *
|
|
;* *
|
|
;* 32bit PROTO: extern void __stdcall IPX_Shut_Down95(void); *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
_IPX_Shut_Down95 proc far pascal uses eax ebx ecx edx esi edi ebp ds es fs gs
|
|
|
|
;
|
|
; If we aren't listening then we cant stop
|
|
;
|
|
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
|
|
|
|
;
|
|
; 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
|
|
|
|
|
|
|
|
|
|
|
|
Verify_Selectors proc near uses eax ebx ecx edx esi edi ebp ds es fs gs
|
|
|
|
; int 3
|
|
mov ax,1686h
|
|
int 2fh
|
|
test ax,ax
|
|
jnz @@fail
|
|
|
|
mov ax,@data
|
|
mov es,ax
|
|
mov ax,0bh
|
|
mov bx,es:[ReceiveBufferSelector]
|
|
mov di,offset ReceiveDescriptor
|
|
int 31h
|
|
bcs @@fail
|
|
|
|
mov ax,@data
|
|
mov ds,ax
|
|
mov_d eax,[ReceiveDescriptor]
|
|
cmp_d eax,[OriginalReceiveDescriptor]
|
|
jnz @@fail
|
|
mov_d eax,[ReceiveDescriptor+4]
|
|
cmp_d eax,[OriginalReceiveDescriptor+4]
|
|
jnz @@fail
|
|
clc
|
|
ret
|
|
|
|
@@fail: stc
|
|
ret
|
|
|
|
Verify_Selectors endp
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* Listen_For_Packet -- Start IPX listening for an incoming packet *
|
|
;* *
|
|
;* *
|
|
;* INPUT: nothing *
|
|
;* *
|
|
;* OUTPUT: nothing *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
Listen_For_Packet proc near uses eax ebx ecx edx esi edi ebp ds es fs gs
|
|
|
|
call Verify_Selectors
|
|
bcs @@fail
|
|
|
|
call Init_Receive_ECB
|
|
|
|
mov ax,@data
|
|
mov ds,ax
|
|
mov es,[ReceiveBufferSelector]
|
|
mov si,[ReceiveECBOffset]
|
|
mov bx,IPX_LISTEN_FOR_PACKET
|
|
push ds
|
|
call Call_IPX
|
|
pop ds
|
|
ret
|
|
|
|
@@fail: mov ax,@data
|
|
mov ds,ax
|
|
mov [ListeningFailed],1
|
|
ret
|
|
|
|
|
|
Listen_For_Packet endp
|
|
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* Call_IPX -- Make a call to the IPX entry point *
|
|
;* *
|
|
;* *
|
|
;* INPUT: registers set up for the IPX call *
|
|
;* *
|
|
;* OUTPUT: registers returned by IPX *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
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
|
|
|
|
Call_IPX endp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;*************************************************************************************************
|
|
;* Call_DOS -- use DPMI to call a DOS interrupt service *
|
|
;* *
|
|
;* *
|
|
;* INPUT: registers set up for the DOS call *
|
|
;* *
|
|
;* OUTPUT: registers returned by DOS *
|
|
;* *
|
|
;* HISTORY: *
|
|
;* 01/29/96 ST: Created. *
|
|
;*===============================================================================================*
|
|
|
|
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]
|
|
|
|
;
|
|
; Save the segment registers
|
|
;
|
|
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 ; number of words to copy from the protected mode stack
|
|
mov di,offset RegisterDump
|
|
push gs
|
|
pop es
|
|
push gs
|
|
int 31h ;DPMI interrupt
|
|
pop gs
|
|
|
|
;
|
|
; Get the return registers
|
|
;
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|