/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/fpdll.c 12    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  Physical front panel DLL interface to Altair32
 
  Copyright (c) 2006 Richard A. Cini (Altair32-specific changes)

  The front panel DLL is a user-mode DLL that manages the communications
  between the USB-based front panel designed by Howard Harte and the
  emulation environment. The DLL provides several callback facilities
  to the emulator can get notification messages when a specific front
  panel event occurs.

  The SetLEDs routine in altair.c calls into this module to update the FP
  LEDs in addition to updating the on-screen representation. Switch toggles
  are handled in the various OnXXX handlers. These handlers set the proper
  params and call the DoSwitch routine in altair.c to actually perform the
  toggle action. DoSwitch will also update the on-screen switch images.
  
	NOTE: Don't forget to include usbfpdll.lib on the linker line.

	TODO: Still need to do serial port I/O stuff.

Change Log:
  2005/04/22  RAC -- Created file
  2005/04/28  RAC -- Clean compile. Waiting for test article.
  2005/06/15  RAC -- Minor clean-up changes. Still waiting for test article.
  2006/03/16  RAC -- Changes to _FPNotify
  2006/05/12  RAC -- Last minute changes for explicit linking
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/08/16  RAC -- Changes to _Open and _Close to use _beginthreadex rather
  						than CreateThread.
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>	// required for all Windows applications
#include <process.h>	// thread stuff
#include <commctrl.h>	// Windows common controls
#include <stdlib.h>		// C standard lib
#include "altair32.h"	// includes "resource.h" and exports from other major modules
#include "comcthlp.h"	// common control macros
#include "fpdll.h"		// front panel dll header


/**  Typedefs & Defines  **************************************/
#define DAV	 	0x01	// true logic (1=DAV at receiver)
#define DAV_BIT	   0
#define TMBT	0x02	// true logic (1=xmit buffer empty)
#define TMBT_BIT   1
#define CPC_UPDATE_FASTEST    0		// 42MHz in DEBUG build
#define CPC_UPDATE_VERY_FAST  1		// 51MHz in DEBUG build
#define CPC_UPDATE_RAPID      5		// 55MHz in DEBUG build
#define CPC_UPDATE_MEDIUM    10		// 56MHz in DEBUG build
#define CPC_UPDATE_WOPR     100		// 57MHz in DEBUG build
#define CPC_UPDATE_DELAY    3

// UsbFpanelDll function prototypes
typedef HANDLE (__stdcall *USBFPANELINITCONTEXT)(void);
static USBFPANELINITCONTEXT _UsbFpanelInitContext;

typedef void (__stdcall *USBFPANELDESTROYCONTEXT)(HANDLE);
static USBFPANELDESTROYCONTEXT _UsbFPanelDestroyContext;

typedef BOOL (__stdcall *USBFPANELFINDANDOPENDEVICE)(HANDLE, int, int, int);
static USBFPANELFINDANDOPENDEVICE _UsbFPanelFindAndOpenDevice;

typedef BOOL (__stdcall *USBFPANELCLOSEDEVICE)(HANDLE);
static USBFPANELCLOSEDEVICE _UsbFPanelCloseDevice;

typedef BOOL (__stdcall *USBFPANELGETHARDWAREINFO)(HANDLE, USB_FPANEL_INFO *);
static USBFPANELGETHARDWAREINFO _UsbFPanelGetHardwareInfo;

typedef int (__stdcall *USBFPANELRESETZDI)(HANDLE);
static USBFPANELRESETZDI _UsbFPanelResetZdi;

typedef int (__stdcall *USBFPANELWRITEZDIREGISTER)(HANDLE, BYTE, unsigned long);
static USBFPANELWRITEZDIREGISTER _UsbFPanelWriteZdiRegister;

typedef unsigned long (__stdcall *USBFPANELREADZDIREGISTER)(HANDLE, BYTE);
static USBFPANELREADZDIREGISTER _UsbFPanelReadZdiRegister;

typedef int (__stdcall *USBFPANELUPDATELEDS)(HANDLE, BYTE *);
static USBFPANELUPDATELEDS _UsbFPanelUpdateLEDs;

typedef int (__stdcall *USBFPANELGETSWITCHVALUES)(HANDLE);
static USBFPANELGETSWITCHVALUES _UsbFPanelGetSwitchValues;

typedef int (__stdcall *USBFPANELGETSR)(HANDLE, WORD *);
static USBFPANELGETSR _UsbFPanelGetSR;

typedef int (__stdcall *USBFPANELSENDSERIAL)(HANDLE, BYTE, BYTE, BYTE *);
static USBFPANELSENDSERIAL _UsbFPanelSendSerial;

typedef BOOL (__stdcall *USBFPANELGETCHAR)(HANDLE, BYTE, BYTE *, BYTE *);
static USBFPANELGETCHAR _UsbFPanelGetChar;

typedef int (__stdcall *USBFPANELINITIALIZE)(HANDLE, PUSBFP_DATA);
static USBFPANELINITIALIZE _UsbFPanelInitialize;


/**  Forward Prototypes  **************************************/
int UsbFPanelUpdate(void);
static DWORD WINAPI UsbFPanelUpdateThread(LPVOID);	// thread routine to update panel
static void UpdateSwitchRegister(void);
static BOOL InitFpdllFuncs();
static BOOL DestroyFpdll();

// Data & Address switches
static DWORD OnAddressSwitches(HANDLE, LPVOID);
static DWORD OnAddressSwitchesReleased(HANDLE, LPVOID);

// Power switch
static DWORD OnPowerOn(HANDLE, LPVOID);
static DWORD OnPowerOff(HANDLE, LPVOID);

// Action switches. Each position requires an "on" and "release" handler
static DWORD OnRun(HANDLE, LPVOID);
static DWORD OnRunReleased(HANDLE, LPVOID) ;
static DWORD OnStop(HANDLE, LPVOID);
static DWORD OnStopReleased(HANDLE, LPVOID) ;

static DWORD OnStepUp(HANDLE, LPVOID);
static DWORD OnStepUpReleased(HANDLE, LPVOID) ;
static DWORD OnStepDown(HANDLE, LPVOID);
static DWORD OnStepDownReleased(HANDLE, LPVOID) ;

static DWORD OnExamine(HANDLE, LPVOID);
static DWORD OnExamineReleased(HANDLE, LPVOID) ;
static DWORD OnExamineNext(HANDLE, LPVOID);
static DWORD OnExamineNextReleased(HANDLE, LPVOID) ;

static DWORD OnDeposit(HANDLE, LPVOID);
static DWORD OnDepositReleased(HANDLE, LPVOID) ;
static DWORD OnDepositNext(HANDLE, LPVOID);
static DWORD OnDepositNextReleased(HANDLE, LPVOID) ;

static DWORD OnReset(HANDLE, LPVOID);
static DWORD OnResetReleased(HANDLE, LPVOID) ;
static DWORD OnExtClr(HANDLE, LPVOID);
static DWORD OnExtClrReleased(HANDLE, LPVOID) ;

static DWORD OnProtect(HANDLE, LPVOID) ;
static DWORD OnProtectReleased(HANDLE, LPVOID) ;
static DWORD OnUnprotect(HANDLE, LPVOID) ;
static DWORD OnUnprotectReleased(HANDLE, LPVOID) ;

static DWORD OnAux1Up(HANDLE, LPVOID) ;
static DWORD OnAux1UpReleased(HANDLE, LPVOID) ;
static DWORD OnAux1Down(HANDLE, LPVOID) ;	
static DWORD OnAux1DownReleased(HANDLE, LPVOID) ;

static DWORD OnAux2Up(HANDLE, LPVOID) ;
static DWORD OnAux2UpReleased(HANDLE, LPVOID) ;
static DWORD OnAux2Down(HANDLE, LPVOID) ;	
static DWORD OnAux2DownReleased(HANDLE, LPVOID) ;

static DWORD OnUndefined(HANDLE, LPVOID) ;


/**  Globals  *************************************************/
BOOL bLampTest = FALSE;
USBFP_INDICATORS  UsbfpIndicators;				// LED indicators
CRITICAL_SECTION UsbFPanelCriticalSection;	// synch object
WORD SR = 0;								// switch register


/**  Locals  **************************************************/
static BOOL bFPinit = TRUE;		// start off in init_state
static char  szBuffer[64];			// general message buffer
//static word  wLastADmap = 0;		// last switch register map
static HANDLE hUsbFp = NULL;			// handle to front panel device
static HINSTANCE hInstUsbFpdll = NULL;	// handle to dll for FreeLibrary
static HANDLE UsbFPanelUpdateThreadHandle = NULL;	// thread handle
static DWORD  UsbFPanelUpdateThreadIdentifier = 0;
static DWORD  UsbFPanelThreadAlive = 0;
static DWORD  UsbFPanelStatus = FP_DISCONNECTED;
static PUSBFP_DATA pUsbfpData;				// fp data structure


/***********************************************************************\
*  Front Panel Application Programming Interface
*  
\***********************************************************************/
/*--  FP_OpenFrontPanel  --------------------------------------
	Enumerate and activate front panel device.

	Params:		None

	Uses:		local data

	Returns:	HANDLE to enumerated device. Calling _Open
				when device is already open returns the
				existing handle value. Extended error
				information can be retreieved by calling
				FP_GetFrontPanelStatus.
	
	Comments:	Called at emulator startup by init code.
-------------------------------------------------------------*/
HANDLE FP_OpenFrontPanel(void){

	//BOOL bResult;

	if(hUsbFp != NULL){	// NULLCHG
		return hUsbFp;		// intialized once already so return internal copy
	}
	
	if (!InitFpdllFuncs()){
		// There was an error initializing front panel dll but we don't
		// know where, so we have to free the DLL and then make sure that
		// any important handles are null.
		UsbFPanelStatus = FP_ERR_DLLNOTFOUND;	// can't locate or init dll
		if(hInstUsbFpdll)		// dll found but can't GPA on a function,
			DestroyFpdll();	// so unload dll
		hUsbFp = (HANDLE)NULL;		// make sure hardware handle is NULL
		return NULL;		
	}

	// Initialize driver context
	hUsbFp = _UsbFpanelInitContext();

	// Attempt to locate and create a handle to the device. 
    if (FALSE == (_UsbFPanelFindAndOpenDevice(hUsbFp, 1, USBFP_USB_VENDOR_ID, USBFP_USB_DEVICE_ID))){
		UsbFPanelStatus = FP_DISCONNECTED;
		return NULL;
	}

	InitializeCriticalSection(&UsbFPanelCriticalSection);
	UsbFPanelThreadAlive = TRUE;	// ensure that thread routine is "enabled"
	// Use _beginthreadex rather than CreateThread if the thread uses CRT functions.
	UsbFPanelUpdateThreadHandle = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES)0,
										0,
										&UsbFPanelUpdateThread,	// WINAPI==__stdcall
										(LPVOID)NULL,
										0,
										&UsbFPanelUpdateThreadIdentifier);

/*	This way might still be OK since the update thread does not use any CRT routines.
	UsbFPanelUpdateThreadHandle = CreateThread((LPSECURITY_ATTRIBUTES)0,
												0,
												UsbFPanelUpdateThread,
												(LPVOID)NULL,
												0,
												&(UsbFPanelUpdateThreadIdentifier)
											);
*/

	if (UsbFPanelUpdateThreadHandle == INVALID_HANDLE_VALUE){
		UsbFPanelStatus = FP_ERR_NO_THREAD;
		_UsbFPanelCloseDevice(hUsbFp);			// close device
		_UsbFPanelDestroyContext(hUsbFp);		// destroy driver context
		DestroyFpdll();						// unload DLL
		hUsbFp = (HANDLE)NULL;
		return NULL;
	}

	if((pUsbfpData = (PUSBFP_DATA)calloc(1, sizeof(USBFP_DATA))) == NULL){
		UsbFPanelStatus = FP_ERR_OUTOFMEMORY;
		_UsbFPanelCloseDevice(hUsbFp);			// close device
		_UsbFPanelDestroyContext(hUsbFp);		// destroy driver context
		DestroyFpdll();						// unload DLL
		hUsbFp = (HANDLE)NULL;
		return NULL;
	}

	pUsbfpData->lpCallbackParameter = (PVOID)0x12345678;

	pUsbfpData->OnAddressSwitches = OnAddressSwitches;

	pUsbfpData->OnPowerOff = OnPowerOff;
	pUsbfpData->OnPowerOffReleased = OnPowerOn;

	pUsbfpData->OnStop = OnStop;
	pUsbfpData->OnStopReleased = OnStopReleased;
	pUsbfpData->OnRun = OnRun;
	pUsbfpData->OnRunReleased = OnRunReleased;

	pUsbfpData->OnStepUp = OnStepUp;
	pUsbfpData->OnStepUpReleased = OnStepUpReleased;
	pUsbfpData->OnStepDown = OnStepDown;
	pUsbfpData->OnStepDownReleased = OnStepDownReleased;
	
	pUsbfpData->OnExamine = OnExamine;
 	pUsbfpData->OnExamineReleased = OnExamineReleased;
	pUsbfpData->OnExamineNext = OnExamineNext;
	pUsbfpData->OnExamineNextReleased = OnExamineNextReleased;

	pUsbfpData->OnDeposit = OnDeposit;
	pUsbfpData->OnDepositReleased = OnDepositReleased;
	pUsbfpData->OnDepositNext = OnDepositNext;
	pUsbfpData->OnDepositNextReleased = OnDepositNextReleased;

	pUsbfpData->OnReset = OnReset;
	pUsbfpData->OnResetReleased = OnResetReleased;
	pUsbfpData->OnExtClr = OnExtClr;
	pUsbfpData->OnExtClrReleased = OnExtClrReleased;

	pUsbfpData->OnProtect = OnProtect;
	pUsbfpData->OnProtectReleased = OnProtectReleased;  
	pUsbfpData->OnUnprotect = OnUnprotect;
	pUsbfpData->OnUnprotectReleased = OnUnprotectReleased;

	pUsbfpData->OnAux1Up = OnAux1Up;
	pUsbfpData->OnAux1UpReleased = OnAux1UpReleased;   
	pUsbfpData->OnAux1Down = OnAux1Down;
	pUsbfpData->OnAux1DownReleased = OnAux1DownReleased; 

	pUsbfpData->OnAux2Up = OnAux2Up;
	pUsbfpData->OnAux2UpReleased = OnAux2UpReleased;   
	pUsbfpData->OnAux2Down = OnAux2Down;
	pUsbfpData->OnAux2DownReleased = OnAux2DownReleased; 
	
	pUsbfpData->OnMode = NULL;
	pUsbfpData->OnModeReleased = NULL;
	pUsbfpData->OnLoadAddress = NULL;
	pUsbfpData->OnLoadAddressReleased = NULL;
	pUsbfpData->OnUndefined = OnUndefined;
	pUsbfpData->OnUndefinedReleased = OnUndefined;
	_UsbFPanelInitialize(hUsbFp, pUsbfpData);

	EnterCriticalSection(&UsbFPanelCriticalSection);
	UpdateSwitchRegister();
	LeaveCriticalSection(&UsbFPanelCriticalSection);
	bFPinit = FALSE;					// done with init
	UsbFPanelStatus = FP_CONN_ACTIVE;
	return hUsbFp;		// saved in simstate.hFrontPanel
}


/*--  FP_CloseFrontPanel  -------------------------------------
	Closes front panel device.

	Params:		HANDLE to front panel device. Handle passed to
				the routine is validated against internal copy
				of the handle value returned by the open call.
				If an invalid handle value is passed in, it is
				ignored.

	Uses:		local data, critical section

	Returns:	BOOL success/failure. FALSE returned if handle
				passed in is invalid or if there is an error
				communicating with the device. TRUE otherwise.
				Extended error information can be retreieved by
				calling FP_GetFrontPanelStatus.
	
	Comments:	Called at emulator shutdown by exit code in
				init.c.
-------------------------------------------------------------*/
BOOL FP_CloseFrontPanel(HANDLE hFP){

	// Validate passed in handle.
	if((hFP == INVALID_HANDLE_VALUE) || (hFP == NULL))
    	return TRUE;

	// If handle in doesn't match local copy, return false.
	if (hFP != hUsbFp){
		UsbFPanelStatus = FP_ERR_INVALIDHANDLE;
		return FALSE;
	}
	
	FP_AllLampsOff(hFP);						// clear LEDs
	UsbFPanelThreadAlive = FALSE;				// cause thread to exit
	UsbFPanelStatus = FP_DISCONNECTED;			// show panel as disconnected
	CloseHandle(UsbFPanelUpdateThreadHandle);	// close thread handle
	DeleteCriticalSection(&UsbFPanelCriticalSection);
	_UsbFPanelCloseDevice(hUsbFp);		// close device
	_UsbFPanelDestroyContext(hUsbFp);	// destroy driver context
	DestroyFpdll();						// unload DLL
	hUsbFp = (HANDLE)NULL;
	return TRUE;
}


/*--  FP_GetFrontPanelStatus  ---------------------------------
	Retrieve the status of the front panel device.

	Params:		HANDLE to front panel.

	Uses:		local data

	Returns:	Status DWORD
	
	Comments:	
-------------------------------------------------------------*/
DWORD FP_GetFrontPanelStatus(HANDLE hFP){

	return UsbFPanelStatus;
}


/*--  FP_LampTest  --------------------------------------------
	Perform a lamp test.

	Params:		HANDLE to front panel device.

	Uses:		local data, critical section

	Returns:	BOOL success/failure. FALSE returned if handle
				passed in is invalid or if there is an error
				communicating with the device. TRUE otherwise.
				Extended error information can be retreieved by
				calling	FP_GetFrontPanelStatus.
	
	Comments:	Called by SetLEDs routine in altair.c when
				toggling RESET key.
-------------------------------------------------------------*/
BOOL FP_LampTest(HANDLE hFP){


	if((hFP == INVALID_HANDLE_VALUE) || (hFP == NULL))
    	return FALSE;

    // if handle in doesn't match local copy, return false
	if (hFP != hUsbFp){
		UsbFPanelStatus = FP_ERR_INVALIDHANDLE;
		return FALSE;
	}

#if 0
	EnterCriticalSection(&UsbFPanelCriticalSection);
	UsbfpIndicators.Address = 0xffff;
	UsbfpIndicators.Data = 0xff;
	UsbfpIndicators.PgmOut = 0xff;	// available only on IMSAI panel
	UsbfpIndicators.IE = 1;
	UsbfpIndicators.Prot = 1;
	UsbfpIndicators.Wait = 1;
	UsbfpIndicators.HoldA = 1;
	UsbfpIndicators.ssINT = 1;
	UsbfpIndicators.sWO = 1;
	UsbfpIndicators.sStack = 1;
	UsbfpIndicators.sHLTA = 1;
	UsbfpIndicators.sOutp = 1;
	UsbfpIndicators.sM1 = 1;
	UsbfpIndicators.sInp = 1;
	UsbfpIndicators.sMemr = 1;

	if (FALSE == (_UsbFPanelUpdateLEDs(hUsbFp, (BYTE *)&UsbfpIndicators))){
		UsbFPanelStatus = FP_ERR_CONNECT;
		return FALSE;
	}

	LeaveCriticalSection(&UsbFPanelCriticalSection);
#endif
	bLampTest = TRUE;
	return TRUE;
}


/*--  FP_AllLampsOff  -----------------------------------------
	Turn all front panel LEDs off.

	Params:		HANDLE to front panel device.

	Uses:		local data, critical section

	Returns:	BOOL success/failure. FALSE returned if handle
				passed in is invalid or if there is an error
				communicating with the device. TRUE otherwise.
				Extended error information can be retreieved by
				calling FP_GetFrontPanelStatus.
	
	Comments:	Called at emulator shutdown or by SetLEDs in
				altair.c
-------------------------------------------------------------*/
BOOL FP_AllLampsOff(HANDLE hFP){

	if((hFP == INVALID_HANDLE_VALUE) || (hFP == NULL))
    	return FALSE;

	// if handle in doesn't match local copy, return false
	if (hFP != hUsbFp){
		UsbFPanelStatus = FP_ERR_INVALIDHANDLE;
		return FALSE;
	}

	EnterCriticalSection(&UsbFPanelCriticalSection);
	UsbfpIndicators.Address = 0;
	UsbfpIndicators.Data = 0;
	UsbfpIndicators.PgmOut = 0;	// available only on IMSAI panel
	UsbfpIndicators.IE = 0;
	UsbfpIndicators.Prot = 0;
	UsbfpIndicators.Wait = 0;
	UsbfpIndicators.HoldA = 0;
	UsbfpIndicators.ssINT = 0;
	UsbfpIndicators.sWO = 0;
	UsbfpIndicators.sStack = 0;
	UsbfpIndicators.sHLTA = 0;
	UsbfpIndicators.sOutp = 0;
	UsbfpIndicators.sM1 = 0;
	UsbfpIndicators.sInp = 0;
	UsbfpIndicators.sMemr = 0;

	if (FALSE == (_UsbFPanelUpdateLEDs(hUsbFp, (BYTE *)&UsbfpIndicators))){
		UsbFPanelStatus = FP_ERR_CONNECT;
		return FALSE;
	}

	LeaveCriticalSection(&UsbFPanelCriticalSection);
	return TRUE;
}


/*--  FP_UpdateLedStatus  -------------------------------------
	Updates the front panel LEDs with the current emulator
	values.

	Params:		HANDLE to front panel device.

	Uses:		local data, critical section, CPU registers
				and flags

	Returns:	BOOL success/failure. FALSE returned if handle
				passed in is invalid or if there is an error
				communicating with the device. TRUE otherwise.
				Extended error information can be retreieved by
				calling FP_GetFrontPanelStatus.
	
	Comments:	Can be called any time. Front panel thread
				performs this function asynchronously from
				the SetLEDs function but polls the same data.
-------------------------------------------------------------*/
BOOL FP_UpdateLedStatus(HANDLE hFP){

	if((hFP == INVALID_HANDLE_VALUE) || (hFP == NULL))
    	return FALSE;

	// if handle in doesn't match local copy, return false
	if (hFP != hUsbFp){
		UsbFPanelStatus = FP_ERR_INVALIDHANDLE;
		return FALSE;
	}
	
	EnterCriticalSection(&UsbFPanelCriticalSection);
	UsbfpIndicators.Address = I80Regs.nPC.W;
	UsbfpIndicators.Data = (MBR & 0xff);
#ifdef IMSAI
	UsbfpIndicators.PgmOut = (IOPANEL & 0xff);		// programmed output IMSAI
#else
	UsbfpIndicators.PgmOut = 0;
#endif
	UsbfpIndicators.IE = I80Regs.IFF & 0xff;								// 5.6
	UsbfpIndicators.Prot = simstate.bPprotect & 1;						// 5.0
	UsbfpIndicators.Wait = (simstate.bPrun & RUN_OFF);					// 7.1
	UsbfpIndicators.HoldA = (I80Regs.IFF & 0);							// 5.2
	UsbfpIndicators.ssINT = (I80Regs.CPUstatus & sINT) >> sINT_BIT;		// 7.0
	UsbfpIndicators.sWO = (I80Regs.CPUstatus & WO) >> WO_BIT;			// 7.2
	UsbfpIndicators.sStack = (I80Regs.CPUstatus & STACK) >> STACK_BIT;	// 7.3
	UsbfpIndicators.sHLTA = (I80Regs.CPUstatus & HLTA) >> HLTA_BIT;		// 5.3
	UsbfpIndicators.sOutp = (I80Regs.CPUstatus & pOUT) >> pOUT_BIT;		// 7.4
	UsbfpIndicators.sM1 = (I80Regs.CPUstatus & M1) >> M1_BIT;			// 7.5
	UsbfpIndicators.sInp = (I80Regs.CPUstatus & INP) >> INP_BIT;			// 7.6
	UsbfpIndicators.sMemr = (I80Regs.CPUstatus & MEMR) >> MEMR_BIT;		// 7.7

	if (FALSE == (_UsbFPanelUpdateLEDs(hUsbFp, (BYTE *)&UsbfpIndicators))){
		UsbFPanelStatus = FP_ERR_CONNECT;
		return FALSE;
	}

	LeaveCriticalSection(&UsbFPanelCriticalSection);
	return TRUE;
}


/*--  FP_SetLedBrightness  ------------------------------------
	Change the brightness of the front panel LEDs.

	Params:		HANDLE to front panel device.

	Uses:		local data, critical section, CPU registers

	Returns:	BOOL success/failure. FALSE returned if handle
				passed in is invalid or if there is an error
				communicating with the device. TRUE otherwise.
				Extended error information can be retreieved by
				calling FP_GetFrontPanelStatus.
	
	Comments:	Called by dialog routine in syscfg.c. The 
				Max695x supports changing the LED brightness in
				16 steps but the driver DLL does not support
				that command yet.
-------------------------------------------------------------*/
BOOL FP_SetLedBrightness(HANDLE hFP, int brightness){

	if((hFP == INVALID_HANDLE_VALUE) || (hFP == NULL))
    	return FALSE;

	// if handle in doesn't match local copy, return false
	if (hFP != hUsbFp){
		UsbFPanelStatus = FP_ERR_INVALIDHANDLE;
		return FALSE;
	}
	
	UsbFPanelStatus = FP_ERR_BADCOMMAND;
	return FALSE;
}


/*--  UsbFPanelUpdateThread  ----------------------------------
	Thread used to update the front panel LEDs and switch
	register.

	Params:		lpThreadParameter - "magic cookie" that
				identifies this device and is a member of the
				CPC_DATA structure.

	Uses:		Nothing

	Returns: 	DWORD - 0 upon successful completion of this
						  thread (no errors)
					  - 1 if the thread terminates due to
					  	  an error
	Comments:	
-------------------------------------------------------------*/
DWORD WINAPI UsbFPanelUpdateThread(LPVOID lpThreadParameter)
{
    int NoUpdateCount = 0;

	while (UsbFPanelThreadAlive == TRUE){
		UsbFPanelUpdate();	// update LEDs and SR
		Sleep(CPC_UPDATE_DELAY);
    }	// while
    
	_endthreadex(0);	// thread stopped
	return 0;
}


/*--  UsbFPanelUpdate  ----------------------------------------
	Called by CpcUsbUpdateThread to perform the actual work of
	updating the front panel LEDs and the switch register.
	This routins should not be called by any other routine.

	Params:		Nothing

	Uses:		Nothing

	Returns: 	INT (success/failure)

	Comments:	
-------------------------------------------------------------*/
int UsbFPanelUpdate(void)
{

	BOOL bResult = FALSE;
	static nUpdateCount = 0;


	EnterCriticalSection(&UsbFPanelCriticalSection);
	if (bLampTest){
		UsbfpIndicators.Address = 0xffff;
		UsbfpIndicators.Data = 0xff;
		UsbfpIndicators.PgmOut = 0xff;	// available only on IMSAI panel
		UsbfpIndicators.IE = 1;
		UsbfpIndicators.Prot = 1;
		UsbfpIndicators.Wait = 1;
		UsbfpIndicators.HoldA = 1;
		UsbfpIndicators.ssINT = 1;
		UsbfpIndicators.sWO = 1;
		UsbfpIndicators.sStack = 1;
		UsbfpIndicators.sHLTA = 1;
		UsbfpIndicators.sOutp = 1;
		UsbfpIndicators.sM1 = 1;
		UsbfpIndicators.sInp = 1;
		UsbfpIndicators.sMemr = 1;
	}
	else if (!simstate.bPpower) {
		UsbfpIndicators.Address = 0;
		UsbfpIndicators.Data = 0;
		UsbfpIndicators.PgmOut = 0;	// available only on IMSAI panel
		UsbfpIndicators.IE = 0;
		UsbfpIndicators.Prot = 0;
		UsbfpIndicators.Wait = 0;
		UsbfpIndicators.HoldA = 0;
		UsbfpIndicators.ssINT = 0;
		UsbfpIndicators.sWO = 0;
		UsbfpIndicators.sStack = 0;
		UsbfpIndicators.sHLTA = 0;
		UsbfpIndicators.sOutp = 0;
		UsbfpIndicators.sM1 = 0;
		UsbfpIndicators.sInp = 0;
		UsbfpIndicators.sMemr = 0;
	}
	else {
		UsbfpIndicators.Address = I80Regs.nPC.W;
		UsbfpIndicators.Data = (MBR & 0xff);
#ifdef IMSAI
		UsbfpIndicators.PgmOut = (IOPANEL & 0xff);		// programmed output IMSAI
#else
		UsbfpIndicators.PgmOut = 0;
#endif
		UsbfpIndicators.IE = I80Regs.IFF & 0xff;								// 5.6
		UsbfpIndicators.Prot = simstate.bPprotect & 1;						// 5.0
		UsbfpIndicators.Wait = (simstate.bPrun & RUN_OFF);					// 7.1
		UsbfpIndicators.HoldA = (I80Regs.IFF & 0);							// 5.2
		UsbfpIndicators.ssINT = (I80Regs.CPUstatus & sINT) >> sINT_BIT;		// 7.0
		UsbfpIndicators.sWO = (I80Regs.CPUstatus & WO) >> WO_BIT;			// 7.2
		UsbfpIndicators.sStack = (I80Regs.CPUstatus & STACK) >> STACK_BIT;	// 7.3
		UsbfpIndicators.sHLTA = (I80Regs.CPUstatus & HLTA) >> HLTA_BIT;		// 5.3
		UsbfpIndicators.sOutp = (I80Regs.CPUstatus & pOUT) >> pOUT_BIT;		// 7.4
		UsbfpIndicators.sM1 = (I80Regs.CPUstatus & M1) >> M1_BIT;			// 7.5
		UsbfpIndicators.sInp = (I80Regs.CPUstatus & INP) >> INP_BIT;			// 7.6
		UsbfpIndicators.sMemr = (I80Regs.CPUstatus & MEMR) >> MEMR_BIT;		// 7.7
	}

    bResult = _UsbFPanelUpdateLEDs(hUsbFp, (BYTE *)&UsbfpIndicators);

	if(bResult == FALSE){
		UsbFPanelStatus = FP_ERR_CONNECT;
	}

	if(nUpdateCount % 10 == 0) {	// update switches every 10 passes
		UpdateSwitchRegister();
	}
	
	nUpdateCount++;
	LeaveCriticalSection(&UsbFPanelCriticalSection);
    return TRUE;
}


/*--  UpdateSwitchRegister  -----------------------------------
	Called by front panel update thread to update the internal
	copy of the SR. When the map is updated, the bitmaps on
	the on-screen window are updated to match the switches
	on the front panel.
-------------------------------------------------------------*/
static void UpdateSwitchRegister(void)
{

	int i = 0;				// loop
	short temp = 0;		// current bit location being tested in loop
	short swpos = 0;		// translated switch position sent to DoSwitch
	word  XORmap = 0;		// map of changed switches
	static word wLastSR;	// previous SR value


	// Get current switch map from panel
	_UsbFPanelGetSR(hUsbFp, &SR);

	// Toggle only those switches that are different between SR
	// and wLastADmap.
	XORmap = (wLastSR ^ SR);	// load old map and XOR to find which changed

	// Loop through XOR map and toggle only changed switches
	for (i = 0; i <= 15; i++){
		temp = 0x8000 >> i;		// calculate bit location
		if (XORmap & temp){			// has it changed?
			((SR & temp) ? (swpos = SW_UP) : (swpos = SW_DOWN));
			DoSwitch((i + 1), swpos);
		}
	}

	wLastSR = SR;
	return;
}


/*--  Switch Action Callbacks  -------------------------------
	The following routines are the callbacks registered with
	the FP DLL in the call to UsbCpaInitialize. Routine name
	describes related switch action.

	Params:
	(hUsbFp):	handle to panel returned by call to 
			UsbFPanelFndAndOpenDevice
	(lpParameter):		identifying number from lpCallbackParameter
						member of _CPC_DATA

-------------------------------------------------------------*/
DWORD OnExamine(HANDLE hCP, LPVOID lpParameter)
{

	UpdateSwitchRegister();
	DoSwitch((short)19, SW_UP);
 	return TRUE;
}

DWORD OnExamineReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)19, SW_NEUT);
 	return TRUE;
}

DWORD OnExamineNext(HANDLE hCP, LPVOID lpParameter)
{

	UpdateSwitchRegister();
	DoSwitch((short)19, SW_DOWN);
 	return TRUE;
}

DWORD OnExamineNextReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)19, SW_NEUT);
 	return TRUE;
}

DWORD OnDeposit(HANDLE hCP, LPVOID lpParameter)
{

	UpdateSwitchRegister();
	DoSwitch((short)20, SW_UP);
 	return TRUE;
}

DWORD OnDepositReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)20, SW_NEUT);
 	return TRUE;
}

DWORD OnDepositNext(HANDLE hCP, LPVOID lpParameter)
{

	UpdateSwitchRegister();
	DoSwitch((short)20, SW_DOWN);
 	return TRUE;
}

DWORD OnDepositNextReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)20, SW_NEUT);
 	return TRUE;
}

DWORD OnReset(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)21, SW_UP);
 	return TRUE;
}

DWORD OnResetReleased(HANDLE hCP, LPVOID lpParameter)
{
	
	DoSwitch((short)21, SW_NEUT);
	return TRUE;
}

DWORD OnExtClr(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)21, SW_DOWN);
 	return TRUE;
}

DWORD OnExtClrReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)21, SW_NEUT);
 	return TRUE;
}

DWORD OnStepUp(HANDLE hCP, LPVOID lpParameter)
{

	// Single-step
	DoSwitch((short)18, SW_UP);
 	return TRUE;
}

DWORD OnStepUpReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)18, SW_NEUT);
 	return TRUE;
}

DWORD OnStepDown(HANDLE hCP, LPVOID lpParameter)
{

	// Slow-step
	DoSwitch((short)18, SW_DOWN);
 	return TRUE;
}

DWORD OnStepDownReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)18, SW_NEUT);
 	return TRUE;
}

DWORD OnRun(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)17, SW_DOWN);
 	return TRUE;
}

DWORD OnRunReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)17, SW_NEUT);
 	return TRUE;
}

DWORD OnStop(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)17, SW_UP);
 	return TRUE;
}

DWORD OnStopReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)17, SW_NEUT);
 	return TRUE;
}

DWORD OnPowerOn(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)0, PWR_ON);
 	return TRUE;
}

DWORD OnPowerOff(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)0, PWR_OFF);
 	return TRUE;
}

DWORD OnAddressSwitches(HANDLE hCP, LPVOID lpParameter)
{

	// This callback doesn't work.

	int i = 0;				// loop
	short temp = 0;		// current bit location being tested in loop
	short swpos = 0;		// translated switch position sent to DoSwitch
	word  XORmap = 0;		// map of changed switches
	static word wLastSR;	// previous SR value


	// Get current switch map from panel
	_UsbFPanelGetSR(hUsbFp, &SR);

	// Toggle only those switches that are different between SR
	// and wLastADmap.
	XORmap = (wLastSR ^ SR);	// load old map and XOR to find which changed

	// Loop through XOR map and toggle only changed switches
	for (i = 0; i <= 15; i++){
		temp = 0x8000 >> i;		// calculate bit location
		if (XORmap & temp){			// has it changed?
			((SR & temp) ? (swpos = SW_UP) : (swpos = SW_DOWN));
			DoSwitch((i + 1), swpos);
		}
	}

	wLastSR = SR;
	return TRUE;
}

DWORD OnAux1Up(HANDLE hCP, LPVOID lpParameter)
{
	// Display Accumulator
	DoSwitch((short)23, SW_UP);
	return TRUE;
}

DWORD OnAux1UpReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)23, SW_NEUT);
 	return TRUE;
}

DWORD OnAux1Down(HANDLE hCP, LPVOID lpParameter)
{
	// Load Accumulator
	DoSwitch((short)23, SW_DOWN);
	return TRUE;
}

DWORD OnAux1DownReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)23, SW_NEUT);
 	return TRUE;
}

DWORD OnAux2Up(HANDLE hCP, LPVOID lpParameter)
{
	// IN/get
	DoSwitch((short)24, SW_UP);
	return TRUE;
}

DWORD OnAux2UpReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)24, SW_NEUT);
 	return TRUE;
}

DWORD OnAux2Down(HANDLE hCP, LPVOID lpParameter)
{
	// OUT/set
	DoSwitch((short)24, SW_DOWN);
	return TRUE;
}

DWORD OnAux2DownReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)24, SW_NEUT);
 	return TRUE;
}

DWORD OnProtect(HANDLE hCP, LPVOID lpParameter)
{
 
	DoSwitch((short)22, SW_UP);
	return TRUE;
}

DWORD OnProtectReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)22, SW_NEUT);
 	return TRUE;
}

DWORD OnUnprotect(HANDLE hCP, LPVOID lpParameter)
{
 
	DoSwitch((short)22, SW_DOWN);
	return TRUE;
}

DWORD OnUnprotectReleased(HANDLE hCP, LPVOID lpParameter)
{

	DoSwitch((short)22, SW_NEUT);
 	return TRUE;
}

DWORD OnUndefined(HANDLE hCP, LPVOID lpParameter)
{

 	return TRUE;
}


/*--  FP_S14h  ------------------------------------------------
	Status/configuration register for front panel serial
	port 0. Status information is passed on simply as go/no-go.
	io=0=read  io=1=write
-------------------------------------------------------------*/
int fp_s14h(int io, int data)
{
	//int port_status = TMBT;	// xmit ready; no char available
	int port_status = 0xff;


	if((hUsbFp == INVALID_HANDLE_VALUE) || (hUsbFp == NULL)){
		return 0xff;
	}
	
	if (io == 0){
		// read
		// always return TMBT
	}

	else {
		// write -- nothing to do
	
	}
	
	return port_status;
}


/*--  FP_S15h  ------------------------------------------------
	Data register for front panel serial port 0.
	Uses int UsbFPanelSendSerial(HANDLE, port, count, buffer) and
	int UsbGetChar(HANDLE, port, data)

	WHAT DOES UsbGetChar RETURN IN DATA IF NOTHING IS THERE?
-------------------------------------------------------------*/
int fp_s15h(int io, int data)
{

	byte tdata = 0;
	byte count = 1;

	
	if((hUsbFp == INVALID_HANDLE_VALUE) || (hUsbFp == NULL)){
		return 0xff;
	}
    
	if (io == 0){
		// read				handle, port, &count, &data
		_UsbFPanelGetChar(hUsbFp, 0, &count, &tdata)	;
		if(count == 0){
			return 0xff;
		}
		else {
			return (int)tdata;
		}
	}

	else {
		// write -- only send one char at a time
		tdata = data & 0xff;
		_UsbFPanelSendSerial(hUsbFp, 0, 1, &tdata);
		return 0;
	}

	return 0xff;
}


/*--  FP_S16h  ------------------------------------------------
	Status/configuration register for front panel serial
	port 1. Status information is passed on simply as go/no-go.
-------------------------------------------------------------*/
int fp_s16h(int io, int data)
{

	int port_status = TMBT;
	

	if((hUsbFp == INVALID_HANDLE_VALUE) || (hUsbFp == NULL)){
		return 0xff;
	}

	if (io == 0){
		// read
	}

	else {
		// write - nothing to do
	
	}


	return port_status;
}


/*--  FP_S17h  ------------------------------------------------
	Data register for front panel serial port 1.
	
-------------------------------------------------------------*/
int fp_s17h(int io, int data)
{

	if((hUsbFp == INVALID_HANDLE_VALUE) || (hUsbFp == NULL)){
		return 0xff;
	}

	if (io == 0){
		// read
	}

	else {
		// write
	
	}


	return 0xff;
}


/*--  InitFpdllFuncs  -----------------------------------------
	Initialize explicitly linked fpdll. If the DLL can't be
	found or if GetProcAddress can't link to any exported
	function, the routine returns FALSE to the caller which
	should then mark the front panel as "not available".
	
-------------------------------------------------------------*/
BOOL InitFpdllFuncs(void)
{

	// Locate and load dll.
	hInstUsbFpdll = LoadLibrary(TEXT("USBFPANEL.DLL"));
	
	if ( !hInstUsbFpdll )	// if the dll can't be found, return false
		return FALSE;

	_UsbFPanelCloseDevice = (USBFPANELCLOSEDEVICE)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelCloseDevice@4"));
	if ( !_UsbFPanelCloseDevice )
		return FALSE;

	_UsbFPanelDestroyContext = (USBFPANELDESTROYCONTEXT)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelDestroyContext@4"));
	if ( !_UsbFPanelDestroyContext )
		return FALSE;

	_UsbFPanelFindAndOpenDevice = (USBFPANELFINDANDOPENDEVICE)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelFindAndOpenDevice@16"));
	if ( !_UsbFPanelFindAndOpenDevice )
		return FALSE;

	_UsbFPanelGetChar = (USBFPANELGETCHAR)GetProcAddress( hInstUsbFpdll, TEXT("_UsbFPanelGetChar@16"));
	if ( !_UsbFPanelGetChar )
		return FALSE;

	_UsbFPanelGetHardwareInfo = (USBFPANELGETHARDWAREINFO)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelGetHardwareInfo@8"));
	if ( !_UsbFPanelGetHardwareInfo )
		return FALSE;

	_UsbFPanelGetSR = (USBFPANELGETSR)GetProcAddress( hInstUsbFpdll, TEXT("_UsbFPanelGetSR@8"));
	if ( !_UsbFPanelGetSR )
		return FALSE;

	_UsbFPanelGetSwitchValues = (USBFPANELGETSWITCHVALUES)GetProcAddress( hInstUsbFpdll,
                                                       TEXT("_UsbFPanelGetSwitchValues@4"));
	if ( !_UsbFPanelGetSwitchValues )
		return FALSE;

	_UsbFpanelInitContext = (USBFPANELINITCONTEXT)GetProcAddress( hInstUsbFpdll, 
														TEXT("_UsbFPanelInitContext@0"));
	if ( !_UsbFpanelInitContext )
		return FALSE;

	_UsbFPanelInitialize = (USBFPANELINITIALIZE)GetProcAddress( hInstUsbFpdll,
														TEXT("_UsbFPanelInitialize@8"));
	if ( !_UsbFPanelInitialize )
		return FALSE;

	_UsbFPanelReadZdiRegister = (USBFPANELREADZDIREGISTER)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelReadZdiRegister@8"));
	if ( !_UsbFPanelReadZdiRegister )
		return FALSE;

	_UsbFPanelResetZdi = (USBFPANELRESETZDI)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelResetZdi@4"));
	if ( !_UsbFPanelResetZdi )
		return FALSE;

	_UsbFPanelSendSerial = (USBFPANELSENDSERIAL)GetProcAddress( hInstUsbFpdll,
														TEXT("_UsbFPanelSendSerial@16"));
	if ( !_UsbFPanelSendSerial )
		return FALSE;

	_UsbFPanelUpdateLEDs = (USBFPANELUPDATELEDS)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelUpdateLEDs@8"));
	if ( !_UsbFPanelUpdateLEDs )
		return FALSE;

	_UsbFPanelWriteZdiRegister = (USBFPANELWRITEZDIREGISTER)GetProcAddress( hInstUsbFpdll,
                                                        TEXT("_UsbFPanelWriteZdiRegister@12"));
	if ( !_UsbFPanelWriteZdiRegister )
		return FALSE;

	return TRUE;	
}


/*--  DestroyFpdll  -------------------------------------------
	Unload the front panel DLL.
-------------------------------------------------------------*/
BOOL DestroyFpdll(void)
{

	FreeLibrary(hInstUsbFpdll);
	hInstUsbFpdll = (HINSTANCE)NULL;
	return TRUE;
}

/* end of file: fpdll.c */
