text: ;
  ; Copyright 2020 Electronic Arts Inc.
  ;
  ; TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
  
  ; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
  ; in the hope that it will be useful, but with permitted additional restrictions 
  ; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
  ; distributed with this program. You should have received a copy of the 
  ; GNU General Public License along with permitted additional restrictions 
  ; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>.
  
  ;***************************************************************************
  ;**   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        **
  ;***************************************************************************
  ;*                                                                         *
  ;*                 Project Name : VQLIB                                    *
  ;*                                                                         *
  ;*                    File Name : HANDLER.ASM                              *
  ;*                                                                         *
  ;*                   Programmer : Bill Randolph                            *
  ;*                                                                         *
  ;*                   Start Date : April 7, 1995                            *
  ;*                                                                         *
  ;*                  Last Update : April 7, 1995   [BRR]                    *
  ;*                                                                         *
  ;*-------------------------------------------------------------------------*
  ;* Functions:                                                              *
  ;*   IPXHandler -- callback routine for IPX                                *
  ;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
  ;********************* Model & Processor Directives ************************
  IDEAL
  MODEL LARGE
  P386N
  LOCALS ??
  
  
  ;******************************** Includes *********************************
  
  
  ;******************************** Defines ***********************************
  
  
  ;****************************** Declarations ********************************
  global C	IPXHandler:FAR
  
  
  ;********************************* Code ************************************
  	CODESEG
  
  
  ;---------------------------------------------------------------------------
  ; The markers let the application verify that it's mapping this memory
  ; correctly.
  ;---------------------------------------------------------------------------
  Marker1			DW 1111h	; placeholder to find data start
  
  ;---------------------------------------------------------------------------
  ; This is the IPX Event Control Block:
  ;---------------------------------------------------------------------------
  ECB_LinkAddress		DD ?
  ECB_EventServiceRoutine	DD ?		; Event Handler ptr
  ECB_InUse		DB ?		; 0 = event is complete
  ECB_CompletionCode	DB ?		; 0 = OK, IPX error otherwise
  ECB_SocketNumber	DW ?		; socket to listen/send on
  ECB_ConnectionID	DW ?
  ECB_RestOfWorkspace	DW ?
  ECB_DriverWorkSpace	DB 12 DUP (?)
  ECB_ImmediateAddress	DB 6 DUP (?)	; bridge address
  ECB_PacketCount		DW ?		; # data areas (2)
  ECB_HeaderAddress	DD ?		; ptr to IPX header buffer
  ECB_HeaderLength	DW ?		; length of IPX header buffer
  ECB_PacketAddress	DD ? 		; ptr to packet buffer
  ECB_PacketLength	DW ?		; length of packet buffer
  
  ;---------------------------------------------------------------------------
  ; The rest of the variables are for telling IPX which buffer to store the
  ; next incoming packet in.  They must be initialized by the application.
  ;---------------------------------------------------------------------------
  NumBufs		DW 0			; # buffers provided by app
  BufferFlags	DD 0			; array of in-use flags (1 = in use)
  PacketSize	DW 0			; total size of 1 buf (incl IPX hdr)
  FirstPacketBuf	DD 0			; ptr to 1st packet buffer
  CurIndex	DW 0			; current packet/flag index
  CurPacketBuf	DD 0			; ptr to current packet buf
  FuncOffset	DW StartLabel		; offset of our routine
  
  ;---------------------------------------------------------------------------
  ; These values are for preventing re-entrancy; they're currently not used.
  ;---------------------------------------------------------------------------
  Semaphore	DB 0			; prevents re-entrancy
  ReEntrantCount	DW 0			; times we've been called re-entrantly
  
  ;---------------------------------------------------------------------------
  ; Local stack space
  ;---------------------------------------------------------------------------
  StackPtr	DW 0			; saved copy of stack ptr
  StackSeg	DW 0			; saved copy of stack seg
  StackPtr_int	DW 0			; our internal stack ptr
  StackSeg_int	DW 0			; our internal stack seg
  StackCheck	DW 1234h		; check for stack overflow
  		DW 256 DUP (0)		; stack storage space
  StackSpace	DW 0			; label for our stack space
  
  ;---------------------------------------------------------------------------
  ; These bytes mark the end of the real-mode data area
  ;---------------------------------------------------------------------------
  Marker2		DW 2222h		; placeholder to find data end
  
  
  ;***************************************************************************
  ;* IPXHandler -- IPX callback routine					   *
  ;*                                                                         *
  ;* This routine is assembled as a stand-alone executable, then loaded      *
  ;* into low DOS memory by a protected-mode application.                    *
  ;*                                                                         *
  ;* INPUT:                                                                  *
  ;*	none.								   *
  ;*									   *
  ;* OUTPUT: 			                                           *
  ;*	none.								   *
  ;*                                                                         *
  ;* WARNINGS:                                                               *
  ;*	none.								   *
  ;* 									   *
  ;* HISTORY:                                                                *
  ;*   04/07/1995 BRR : Created.                                             *
  ;*=========================================================================*
  	label StartLabel
  	PROC	IPXHandler C FAR USES
  
  	;...................................................................
  	; Turn off interrupts; make sure memory copies go forward
  	;...................................................................
  	pushf
  	cli
  	cld
  
  	;...................................................................
  	; Set up segment registers to point DS to CS
  	;...................................................................
  	push	ds
  	push	ax
  	mov	ax,cs
  	mov	ds,ax
  
  	;...................................................................
  	; Set up our local stack; save SS & SP first.
  	;...................................................................
  	mov	[StackSeg],ss
  	mov	[StackPtr],sp
  	mov	[StackPtr_int], OFFSET StackSpace
  	mov	[StackSeg_int], SEG StackSpace
  	lss	sp, [DWORD PTR StackPtr_int]
  
  
  	;...................................................................
  	; Save all registers
  	;...................................................................
  	pushad
  	push	es
  
  	;...................................................................
  	; If we've been called re-entrantly, just exit
  	;...................................................................
  	cmp	[Semaphore],0
  	jz	??Start_Handler
  	add	[ReEntrantCount],1
  	jmp	??Exit_Handler
  
  ??Start_Handler:
  	;...................................................................
  	; Set our semaphore
  	;...................................................................
  	mov	[Semaphore],1
  
  	;-------------------------------------------------------------------
  	; Set 'CurIndex' to the index of the next-available receive buffer,
  	; and 'CurPacketBuf to the next-available packet buffer
  	;-------------------------------------------------------------------
  	;...................................................................
  	; Get 'CurIndex' & increment it.  Wrap to 0 if we reach 'NumBufs'
  	; Since I'm treating 'CurPacketBuf' as a long integer (and not as
  	; a segment:offset), the entire data area can't be larger than 64K.
  	;...................................................................
  	mov	dx,[CurIndex]		; DX = CurIndex
  	mov	eax,[CurPacketBuf]	; EAX = current packet buffer addr
  	inc	dx			; DX = next index
  	add	ax,[PacketSize]		; EAX = next buffer ptr
  	cmp	dx,[NumBufs]		; see if DX is past # buffers
  	jb	??Get_Flag
  	mov	dx,0			; wrap to 1st index
  	mov	eax,[FirstPacketBuf]	; wrap to 1st packet buffer
  
  ??Get_Flag:
  	;...................................................................
  	; Get the next buffer-in-use flag; if it's 0, load [CurIndex] with
  	; the value of SI (the next index).  If it's 1, skip the updating of
  	; the index, flag & buffer ptr.
  	; DX = new CurIndex
  	; EAX = new CurPacketBuf
  	;...................................................................
  	les	di,[BufferFlags]	; ES:DI = BufferFlags address
  	mov	bx,di			; BX = DI + new CurIndex
  	add	bx,dx
  
  	cmp	[BYTE PTR es:bx],0	; compare next flag to 0 (avail)
  	jne	??Set_ECB		; if not avail, skip setting new values
  
  	;...................................................................
  	; The next buffer is available; so, set this buffer's In-Use flag
  	; to 1, and move on to the next buffer.  Do not set this buffer's
  	; flag to 1 until we move on to the next buffer, to prevent the
  	; application from reading the currently-in-use packet buffer.
  	; DX = new CurIndex
  	; EAX = new CurPacketBuf
  	; ES:DI = BufferFlags address
  	;...................................................................
  	mov	bx,di			; BX = DI + old CurIndex
  	add	bx,[CurIndex]
  	mov	[BYTE PTR es:bx],1	; set old BufferFlags value to in-use
  
  	mov	[CurIndex],dx		; save new index
  	mov	[CurPacketBuf],eax	; save new packet address
  
  	;-------------------------------------------------------------------
  	; Set up the Event Control Block to tell IPX to start listening.
  	; The following entries are filled in by the app, and should be left
  	; alone:
  	; - EventServiceRoutine
  	; - SocketNumber
  	; The rest should be re-initialized.  Note that EBX is now pointing
  	; to an unavailable buffer if the next buffer was already in use;
  	; so it must be reloaded with the correct buffer address from
  	; [CurPacketBuf].
  	;-------------------------------------------------------------------
  ??Set_ECB:
  	mov	[ECB_LinkAddress],0		; default
  	mov	[ECB_InUse],0			; default
  	mov	[ECB_CompletionCode],0		; default
  	mov	[ECB_ConnectionID],0		; default
  	mov	[ECB_RestOfWorkspace],0		; default
  	mov	[ECB_DriverWorkSpace],0		; default
  	mov	[ECB_ImmediateAddress],0	; default
  	mov	[ECB_PacketCount],2		; use 2 data areas
  	mov	ebx,[CurPacketBuf]		; get current buffer address
  	mov	[ECB_HeaderAddress],ebx		; set header address
  	mov	[ECB_HeaderLength],30		; size of IPX header
  	add	ebx,30				; point to past the header
  	mov	[ECB_PacketAddress],ebx		; set packet data address
  	mov	ax,[PacketSize]			; get size of one buffer
  	sub	ax,30				; remove size of IPX header
  	mov	[ECB_PacketLength],ax		; set size of packet data
  
  	;-------------------------------------------------------------------
  	; Clear the IPX header for this packet
  	;-------------------------------------------------------------------
  	les	di,[ECB_HeaderAddress]		; ES:DI = IPX Listen Header
  	mov	cx,30				; (30 bytes = size of header)
  	mov	al,0
  	rep	stosb				; clear to 0's
  
  	;-------------------------------------------------------------------
  	; Command IPX to start listening again.
  	;-------------------------------------------------------------------
  	mov	bx,4				; IPX code for Listen
  	mov	ax,ds				; ES = segment of ListenECB
  	mov	es,ax
  	mov	ax,OFFSET ECB_LinkAddress
  	mov	si,ax				; ES:SI = address of ECB
  	int	07ah				; call IPX interrupt
  
  	;...................................................................
  	; Clear our semaphore
  	;...................................................................
  	mov	[Semaphore],0
  
  ??Exit_Handler:
  	;...................................................................
  	; Pop values from our local stack
  	;...................................................................
  	pop	es
  	popad
  
  	;...................................................................
  	; Check our stack-check value; if the stack has overflowed, generate
  	; a debugger break.
  	;...................................................................
  	cmp	[StackCheck],1234h
  	je	??Restore_Stack
  	int	3
  
  	;...................................................................
  	; Restore the stack to its previous value
  	;...................................................................
  ??Restore_Stack:
  	lss	sp, [DWORD PTR StackPtr]
  
  	;...................................................................
  	; Pop the rest of the registers
  	;...................................................................
  	pop	ax
  	pop	ds
  
  	popf
  
  	ret
  
  ENDP		IPXHandler
  
  END
  
  ;************************** End of handler.asm *****************************
  