;
; 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 *****************************
