/*EM	BOGUS.C - Bogus Device Driver DLL
 *
 *	SUMMARY
 *	    Basic LibMain and WEP functions
 *
 *	COMMENTS
 *
 *	WARNINGS
 *
 */


#include <windows.h>
#include "bogusa.h"
#include "pic.h"
#include "dpmi.h"

#define EXPORT _export _loadds
#include "bogus.h"

#define FAKE_PORT	0x141  /* Bogosity Level =  9.4 */
#define FAKE_IRQ	11     /* Bogosity Level =  9.8 */

#define FAKE_CTL_START	0x01   /* bogus port start command (set to zero) */
#define FAKE_CTL_EOI	0x02   /* bogus port EOI (set to zero) */

#define FAKE_STAT_BUSY	0x01   /* bogus port busy indication (zero=>busy) */
#define FAKE_STAT_IRQ	0x02   /* bogus port IRQ (zero=>IRQ) */
#define FAKE_STAT_ERROR 0x04   /* I/O error (zero=>error) (cleared on read) */


/* Set variables for our interrupt number */

#if (FAKE_IRQ < 8)
#define INT_DEV (INT_MASTER_0+(FAKE_IRQ & 7))
#define PIC00 INTA00
#define PIC01 INTA01
#else
#define INT_DEV (INT_SLAVE_0+(FAKE_IRQ & 7))
#define PIC00 INTB00
#define PIC01 INTB01
#endif
#define INT_MASK (1 << (FAKE_IRQ & 7))


BOOL FAR PASCAL LibMain(HANDLE hInstance   /* Library instance handle  */
		       ,WORD wDataSeg	    /* Default data segment	*/
		       ,WORD cbHeap	    /* Default Heap size	*/
		       ,LPSTR lpszCmdLine) ;/* Command line		*/
int FAR PASCAL WEP(int fSystemExit) ;
#pragma alloc_text(INIT_TEXT,LibMain)	/* Keep this with LIBENTRY.ASM	*/
#pragma alloc_text(FIXED_TEXT,WEP)

HANDLE hLibInstance ;
FARPROC lpfnPrevISR ;		/* Saved Previous ISR */
DWORD lpfnPrevRMISR ;		/* Saved Previous real-mode ISR */
HANDLE hReflector ;

DWORD DPMI_AllocateRMCallback(FARPROC lpfnCallback, _RMCS FAR *lpRMCS)
{
    DWORD dwRet ;
    _asm {
	push	ds
	lds	si,lpfnCallback
	les	di,lpRMCS
	mov	ax,DPMI_ALLOCRMC
	int	IVEC_DPMI
	pop	ds
	jc	lbl1
	mov	word ptr dwRet,dx	 ; return callback address
	mov	word ptr dwRet+2,cx
	jmp	short lbl2
lbl1:
	mov	word ptr dwRet,ax	 ; error code in ax
	mov	word ptr dwRet+2,0	 ; return seg=0 if error
lbl2:
	}

    return dwRet ;
}


WORD DPMI_FreeRMCallback(DWORD lpfnCallback)
{
    WORD wRet ;

    _asm {
	mov	dx,word ptr lpfnCallback
	mov	cx,word ptr lpfnCallback+2
	mov	ax,DPMI_FREERMC
	int	IVEC_DPMI
	jc	lbl1
	xor	ax,ax
lbl1:
	mov	wRet,ax
	}
    return wRet ;
}


DWORD DPMI_GetRMVector(int iVector)
{
    DWORD dwRet ;
    _asm {
	mov	ax,DPMI_GETRMVEC
	mov	bl,byte ptr iVector
	int	31h
	mov	word ptr dwRet,dx
	mov	word ptr dwRet+2,cx
	}
    return dwRet ;
}


void DPMI_SetRMVector(int iVector, DWORD lpfnRMISR)
{
    _asm {
	mov	ax,DPMI_SETRMVEC
	mov	bl,byte ptr iVector
	mov	dx,word ptr lpfnRMISR
	mov	cx,word ptr lpfnRMISR+2
	int	31h
	}
}


FARPROC GetPMVector(int iVector)
{
    FARPROC dwRet ;

    _asm {
	mov	al,byte ptr iVector
	mov	ah,35h
	int	21h
	mov	word ptr dwRet,bx
	mov	word ptr dwRet+2,es ; Save it
	}
    return dwRet ;
}


void SetPMVector(int iVector, FARPROC lpfnISR)
{
    _asm {
	push	ds
	lds	dx,lpfnISR
	mov	al,byte ptr iVector
	mov	ah,25h
	int	21h		    ; Set our ISR
	pop	ds
	}
}


HANDLE AllocIntReflector(int iVector, FARPROC lpfnCallback)
{
    DWORD dwDosMem ;
    LPSTR lpLowRMISR ;
    DWORD lpfnRMCallback ;
    _RMCS FAR *lpSaveRegs ;

    /* Allocate some DOS memory for the Real-mode Interrupt service routine */

    dwDosMem = GlobalDosAlloc(16 + sizeof (int) + sizeof (_RMCS)) ;

    if (dwDosMem == 0)
	return 0 ;
    lpLowRMISR = (LPSTR) MAKELONG(0,LOWORD(dwDosMem)) ;
    lpSaveRegs = (_RMCS FAR *) (&lpLowRMISR[16]) ;

    /* Allocate a real-mode callback */

    lpfnRMCallback = DPMI_AllocateRMCallback((FARPROC)lpfnCallback,lpSaveRegs)
;
    if (HIWORD((DWORD)lpfnRMCallback) == 0)
	{
	GlobalDosFree(LOWORD(dwDosMem)) ;
	return 0;
	}

    /* Generate the low-memory code (it's only 6 bytes) */

    lpLowRMISR[0] = 0x9A ;	    /* CALL FAR PTR */
    *((DWORD FAR *)&(lpLowRMISR[1])) = lpfnRMCallback ;
    lpLowRMISR[5] = 0xCF ;	    /* IRET */
    *((int FAR *)&(lpLowRMISR[6])) = iVector ;

    /* hook the real mode interrupt vector */
    DPMI_SetRMVector(iVector,MAKELONG(0,HIWORD(dwDosMem))) ;

    return (HANDLE) LOWORD(dwDosMem) ;	/* return the reflector handle */
}


void FreeIntReflector(HANDLE hReflector)
{
    LPSTR lpLowRMISR ;
    DWORD lpfnRMCallback ;

    /* Get the protected mode address of the low ISR */

    lpLowRMISR = (LPSTR)MAKELONG(0,(WORD)hReflector) ;


    /* make sure that it's a reflector */
    if ((lpLowRMISR[0] != 0x9A) || (lpLowRMISR[5] != 0xCF))
	return ;	/* exit if not a reflector */


    /* Extract the callback address and free the callback */

    lpfnRMCallback = *((DWORD FAR *)&(lpLowRMISR[1])) ;
    DPMI_FreeRMCallback(lpfnRMCallback) ;


    /* free the real-mode interrupt service routine */

    GlobalDosFree((WORD) hReflector) ;
}


/*XP<	LibMain - Main library entry point.
 *
 *	ENTRY
 *
 *	EXIT
 *
 *	RETURNS
 *	    TRUE if initialization succeeds, FALSE otherwise.
 *
 *	WARNINGS
 *
 *	CALLS
 *
 *	NOTES
 *	    The true library entry point is in the assembly language
 *	    module LIBENTRY.ASM, but it simply passes control to here.
 *
 */
BOOL FAR PASCAL LibMain(HANDLE hInstance   /* Library instance handle  */
		       ,WORD wDataSeg	    /* Default data segment	*/
		       ,WORD cbHeap	    /* Default Heap size	*/
		       ,LPSTR lpszCmdLine)  /* Command line		*/
/*>*/
{
    lpszCmdLine = lpszCmdLine ;     /* Avoid -W4 warnings		*/
	 wDataSeg = wDataSeg ;
	 cbHeap = cbHeap ;
	 hLibInstance = hInstance ;	    /* We may need this later to access
				       resources from our executable	*/


    return TRUE ;
}



/*XP<	WEP - Windows Exit Procedure
 *
 *	ENTRY
 *	    fSystemExit indicates if we are terminating the Windows
 *	    session.  Otherwise, we are only unloading this DLL.
 *
 *	RETURNS
 *	    Always returns "1".
 *
 *	WARNINGS
 *	    Due to bugs in Windows 3.0 and earlier (and perhaps later),
 *	    this function must be placed in a fixed segment.  This same
 *	    bug makes the value of DS questionable, so we cannot use it
 *	    (or any static data).
 *
 *	    To be sure, we don't do anything here, anyway.
 *
 *	CALLS
 *	    None
 *
 *	NOTES
 *	    This is the standard DLL exit procedure.
 *
 */
int FAR PASCAL WEP(int fSystemExit)
/*>*/
{
    fSystemExit = fSystemExit ;     /* Avoid -W4 warning		*/
    return 1 ;			    /* Always indicate success		*/
}


int EXPORT FAR PASCAL BogusCheck(void)
{
    BYTE bPortVal ;

    _asm {
	mov	dx,FAKE_PORT
	in	al,dx		    ; Is the bogus device present ?
	mov	bPortVal,al
	}

    return ((bPortVal & 0x80) == 0) ; /* Return TRUE if device is present */
}


void EXPORT FAR PASCAL BogusStart(HWND hWnd, WPARAM wParam)
{
    wParamEvent = wParam ;
    hWndEvent = hWnd ;

    if (!lpfnPrevISR)
	{
	/* Save the previous ISR and set ours */
	_asm cli
	lpfnPrevISR = GetPMVector(INT_DEV) ;
	SetPMVector(INT_DEV,(FARPROC)IntSvcRtn) ;
	_asm sti

	/* Save the previous real mode ISR and reflect to ours */
	lpfnPrevRMISR = DPMI_GetRMVector(INT_DEV) ;
	hReflector = AllocIntReflector(INT_DEV,(FARPROC)BogusCallback) ;

	/* Unmask the interrupt and begin device I/O */
	_asm {
	    cli
	    in	    al,PIC01		; unmask the interrupt
	    and     al,NOT INT_MASK
	    out     PIC01,al
	    sti
	    mov     al,NOT FAKE_CTL_START
	    mov     dx,FAKE_PORT
	    out     dx,al		; start device I/O
	    }
	}
}


int EXPORT FAR PASCAL BogusGetEvent(void)
{
    WORD wCountRet ;

    _asm {
	mov	ax,SEG wCount
	mov	es,ax
	xor	ax,ax
	xchg	ax,es:wCount		; get count, set to zero
	mov	wCountRet,ax
	}

    return wCountRet ;
}


void EXPORT FAR PASCAL BogusStop(void)
{
    hWndEvent = 0x0000 ;	/* Tell the ISR to stop */

    if (!lpfnPrevISR)
	return ;		/* return if not started */

    _asm {
	mov	dx,FAKE_PORT
l1:
	in	al,dx
	rcr	al,1
	jnc	l1		  ; loop while busy

	cli
	in	al,PIC01
	or	al,INT_MASK
	out	PIC01,al	    ; mask the interrupt level
	sti
	}

    DPMI_SetRMVector(INT_DEV, lpfnPrevRMISR) ;	 /* Restore real-mode vector */
    FreeIntReflector(hReflector) ;		 /* Free the reflector */
    SetPMVector(INT_DEV, lpfnPrevISR) ; 	 /* Restore the prot-mode vector */

    lpfnPrevISR = NULL ;
}
