/*
Copyright (c) 1998 Richard Lawrence

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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <windows.h>
#include <windowsx.h>
#include <dinput.h>
#include "atari.h"
#include "winatari.h"
#include "registry.h"
#include "resource.h"
#include "platform.h"
#include "pokey.h"
#include "cpu.h"

extern void DisablePILL(void);
extern void EnablePILL(void);
extern void SetSafeDisplay( void );
extern HWND	hWnd, MainhWnd;
extern void Clear_Sound( BOOL bPermanent );
extern void	Toggle_FullSpeed( void );
extern void Toggle_Pause( void );
extern void Toggle_SIO_Patch( void );
extern ULONG *atari_screen;
static BOOL GetDIErrorString( HRESULT hResult, LPSTR lpszErrorBuff, DWORD cchError);

extern ULONG	ulAtariState;
extern ULONG	ulMiscStates;
extern TCHAR	gcErrorString[];
extern int mach_xlxe;
extern int pil_on;

HINSTANCE	hInstance = NULL;

static LPDIRECTINPUT			lpDInput = NULL;
static LPDIRECTINPUTDEVICE2		g_rgpdevFound[MAX_INPUT_DEVICES];
static LPDIRECTINPUTDEVICE2		g_lpDev[MAX_ATARI_JOYPORTS] = {NULL, NULL, NULL, NULL};
static BOOL	bBtn2Flip, bBtn3Flip, bBtn4Flip;
static int	nJoyCount = 0;
static int trig[MAX_ATARI_JOYPORTS];
static int stick[MAX_ATARI_JOYPORTS];
static int consol;

char	cInputDevNames[MAX_INPUT_DEVICES][INPUT_DEV_NAMELEN];
int		gNumDIDevs = 0, gSelectedInputDevice[MAX_ATARI_JOYPORTS] = { -1, 127, 127, 127 };
BOOL	bKBJoystick = FALSE;
unsigned long	ulJoystickSelects = 0x7f7f7fff;
int		nJoyRepeat = 0, nJoySkip = 0;
static unsigned int keystick = STICK_CENTRE;
static unsigned int	keytrig = 1;

int		iCurrentKey = AKEY_NONE;
int		iCurrentVK = 0;
int		iNewKey = AKEY_NONE;
int		iNewVK = 0;
extern int SHIFT_KEY, KEYPRESSED;
int		CTRL_KEY;
static const int iStickTable[] = { STICK_LL, STICK_BACK, STICK_LR, STICK_LEFT, STICK_CENTRE, STICK_RIGHT, STICK_UL, STICK_FORWARD, STICK_UR };
		
void Reset_Keys( void )
{
	keystick = STICK_CENTRE;
	keytrig = 1;
	iCurrentKey = iNewKey = AKEY_NONE;
	iCurrentVK = iNewVK = 0;
	SHIFT_KEY = CTRL_KEY = FALSE;
	KEYPRESSED = FALSE;
	consol = 0x07;
}

void Input_Reset( void )
{
	int i;

	bBtn2Flip = FALSE;
	bBtn3Flip = FALSE;
	bBtn4Flip = FALSE;
	Reset_Keys();

	for( i=0; i < MAX_ATARI_JOYPORTS; i++ )
	{
		trig[i] = 1;
		stick[i] = STICK_CENTRE;
	}
}

/*Fatal DirectInput error function. Since we cannot continue, bring us back to a
  known working state before displaying the error, then start an exit. Make sure
  to shut down the Atari layer on the way.*/
static void ShowDInputError( UINT nUID, HRESULT hResult, BOOL quit )
{
	TCHAR	error[LOADSTRING_STRING_SIZE];
	TCHAR	fullstring[LOADSTRING_STRING_SIZE];
	TCHAR	action[LOADSTRING_STRING_SIZE];
	
	/* Get us back to a GDI display, and stop making noises */
	SetSafeDisplay();
	Clear_Sound( TRUE );

	//Get the error string and present it to the user
	GetDIErrorString( hResult, error, LOADSTRING_STRING_SIZE );
	LoadString( NULL, IDS_DINPUT_ERROR_PROMPT, gcErrorString, LOADSTRING_STRING_SIZE );
	LoadString( NULL, nUID, action, LOADSTRING_STRING_SIZE );
	wsprintf( fullstring, gcErrorString, action, error );
	LoadString( NULL, IDS_DINPUT_ERROR_HDR, gcErrorString, LOADSTRING_STRING_SIZE );
	MessageBox( MainhWnd, fullstring, gcErrorString, MB_ICONSTOP );

	//Make sure the atari is turned off
	ulAtariState = ATARI_UNINITIALIZED | ATARI_PAUSED;

	//Start a quit (this will end up in Atari_Exit() )
	if( quit )
		PostMessage( MainhWnd, WM_CLOSE, 0, 0L );
	return;
}

//Helper function just makes sure the Property struct is set correctly
static HRESULT SetDIDwordProperty(LPDIRECTINPUTDEVICE pdev, REFGUID guidProperty, DWORD dwObject, DWORD dwHow, DWORD dwValue)
{
	DIPROPDWORD dipdw;
	
	dipdw.diph.dwSize       = sizeof(dipdw);
	dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
	dipdw.diph.dwObj        = dwObject;
	dipdw.diph.dwHow        = dwHow;
	dipdw.dwData            = dwValue;
	
	return pdev->lpVtbl->SetProperty(pdev, guidProperty, &dipdw.diph);
}

static BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
{
	LPDIRECTINPUT pdi = pvRef;
	LPDIRECTINPUTDEVICE pdev;
	DIPROPRANGE diprg;
	HRESULT hResult;
	
	// create the DirectInput joystick device
	hResult = pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_CREATING_DEVICE, hResult, TRUE );
		return DIENUM_STOP;/* DIENUM_CONTINUE;*/
	}
	
	// set joystick data format
	hResult = pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_SETTING_JOYSTICK_FORMAT, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return DIENUM_STOP;
	}
	
	// set the cooperative level
	hResult = pdev->lpVtbl->SetCooperativeLevel(pdev, MainhWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_SETTING_COOP, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return DIENUM_STOP;
	}
	
	// set X-axis range to (-1000 ... +1000)
	// This lets us test against 0 to see which way the stick is pointed.
	
	diprg.diph.dwSize       = sizeof(diprg);
	diprg.diph.dwHeaderSize = sizeof(diprg.diph);
	diprg.diph.dwObj        = DIJOFS_X;
	diprg.diph.dwHow        = DIPH_BYOFFSET;
	diprg.lMin              = -1000;
	diprg.lMax              = +1000;
	
	hResult = pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph);
	if( FAILED( hResult) )
	{
		ShowDInputError( IDS_DIERR_SETTING_PROPS, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return FALSE;
	}
	
	//
	// And again for Y-axis range
	//
	diprg.diph.dwObj        = DIJOFS_Y;
	
	hResult = pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_SETTING_PROPS, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return FALSE;
	}
	
	// set X axis dead zone to 50% (to avoid accidental turning)
	// Units are ten thousandths, so 50% = 5000/10000.
	hResult = SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000);
	if ( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_SETTING_DEADZONE, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return FALSE;
	}
	
	
	// set Y axis dead zone to 50% (to avoid accidental thrust)
	// Units are ten thousandths, so 50% = 5000/10000.
	hResult = SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_SETTING_DEADZONE, hResult, TRUE );
		pdev->lpVtbl->Release(pdev);
		return FALSE;
	}
	
	
	//
	// Add it to our list of devices.
	// 
	//AddInputDevice(pdev, pdinst);
	hResult = pdev->lpVtbl->QueryInterface( pdev, &IID_IDirectInputDevice2, (LPVOID *)&g_rgpdevFound[gNumDIDevs]);
	if( FAILED( hResult ) )
	{
		ShowDInputError( IDS_DIERR_ADDING_DEVICE, hResult, TRUE );
		return FALSE;
	}
	strncpy( cInputDevNames[gNumDIDevs++], pdinst->tszInstanceName, 32 );
	pdev->lpVtbl->Release(pdev);

	return DIENUM_CONTINUE;;
}

static BOOL ReacquireInput( int sticknum )
{
    HRESULT hResult;
	
    // if we have a current device
    if(g_lpDev[sticknum] )
    {
		// acquire the device
		hResult = IDirectInputDevice_Acquire(g_lpDev[sticknum]);
		if(SUCCEEDED(hResult))	// acquisition successful
			return TRUE;
		else					// acquisition failed
			return FALSE;
    }
    else	// we don't have a current device
		return FALSE;
}

void PickJoystick( int sticknum, int joystick )
{
	int i;

	for( i=0; i < MAX_ATARI_JOYPORTS; i++ )
	{
		if( g_lpDev[i] != NULL && g_lpDev[i] == g_rgpdevFound[joystick] )
		{
			IDirectInputDevice_Unacquire(g_lpDev[i]);
			g_lpDev[i] = NULL;
		}
	}

	gSelectedInputDevice[sticknum] = joystick;

	if( g_lpDev[sticknum] )
		IDirectInputDevice_Unacquire(g_lpDev[sticknum]);
	g_lpDev[sticknum] = NULL;

	bKBJoystick = FALSE;
	for( i=0; i < MAX_ATARI_JOYPORTS; i++ )
	{
		if( gSelectedInputDevice[i] < 0 )
			bKBJoystick = TRUE;
	}

	/* -1 means to use the keyboard instead */
	if( joystick < 0 )
		return;

	if( (joystick < MAX_INPUT_DEVICES) && g_rgpdevFound[joystick] )
	{
		g_lpDev[sticknum] = g_rgpdevFound[joystick];
		if( !ReacquireInput( sticknum ) )
		{
			ShowDInputError( IDS_DIERR_ACQUIRE_INIT, DIERR_NOTACQUIRED, TRUE );
			return;
		}
	}
}

int Input_Initialise( void )
{
	HRESULT	hResult;
	int stick;
	char value;

	iCurrentKey = iNewKey = AKEY_NONE;
	iCurrentVK = iNewVK = 0;
	KEYPRESSED = FALSE;

	Input_Reset();

	if( !lpDInput )
	{
		ZeroMemory( g_rgpdevFound, sizeof( LPDIRECTINPUTDEVICE ) * MAX_INPUT_DEVICES );

		/* create the DirectInput 5.0 interface object */
		hResult = DirectInputCreate(hInstance, DIRECTINPUT_VERSION, &lpDInput, NULL);
		if( FAILED( hResult ) )
		{
			if( hResult == DIERR_OLDDIRECTINPUTVERSION )
			{
				lpDInput = NULL;
			}
			else
			{
				ShowDInputError( IDS_DIERR_CREATING_OBJ, hResult, FALSE );
				return 0;
			}
		}
		else
		{
			/* Enumerate the joystick devices. */
			
			hResult = lpDInput->lpVtbl->EnumDevices(lpDInput, DIDEVTYPE_JOYSTICK, InitJoystickInput, lpDInput, DIEDFL_ATTACHEDONLY);
			if( FAILED( hResult ) )
			{
				ShowDInputError( IDS_DIERR_ENUMERATING, hResult, FALSE );
				return 0;
			}
			lpDInput->lpVtbl->Release(lpDInput);
		}
	}

	value = (char)(ulJoystickSelects & 0x000000ff);
	stick = value;
	PickJoystick( 0, stick );

	stick = ulJoystickSelects & 0x0000ff00;
	stick >>= 8;
	value = (char)stick;
	stick = value;
	if( stick == gSelectedInputDevice[0] )
		stick = NO_JOYSTICK;
	PickJoystick( 1, stick );

	stick = ulJoystickSelects & 0x00ff0000;
	stick >>= 16;
	value = (char)stick;
	stick = value;
	if( stick == gSelectedInputDevice[0] || stick == gSelectedInputDevice[1] )
		stick = NO_JOYSTICK;
	PickJoystick( 2, stick );

	stick = ulJoystickSelects & 0xff000000;
	stick >>= 24;
	value = (char)stick;
	stick = value;
	if( stick == gSelectedInputDevice[0] || stick == gSelectedInputDevice[1] || stick == gSelectedInputDevice[2] )
		stick = NO_JOYSTICK;
	PickJoystick( 3, stick );

	nJoyCount = nJoyRepeat;
	return 1;
}

void CleanupInput(void)
{
	int idev;
	
	// make sure the device is unacquired
	// it doesn't harm to unacquire a device that isn't acquired
	for( idev = 0; idev < MAX_ATARI_JOYPORTS; idev++ )
	{
		if (g_lpDev[idev])
		{
			IDirectInputDevice_Unacquire(g_lpDev[idev]);
			g_lpDev[idev] = NULL;
		}
	}
	
	// release all the devices we created
	for (idev = 0; idev < gNumDIDevs; idev++)
	{
		if (g_rgpdevFound[idev]) {
			IDirectInputDevice_Release(g_rgpdevFound[idev]);
			g_rgpdevFound[idev] = NULL;
		}
	}
}

int ReadJoystickInput( int sticknum, BOOL bTestButton )
{
	int						stickval = 0;
	HRESULT                 hResult;
	DIJOYSTATE              js;

	if( (sticknum >= MAX_INPUT_DEVICES) || !g_lpDev[sticknum] )
		return STICK_CENTRE;

	// poll the joystick to read the current state
	hResult = IDirectInputDevice2_Poll( g_lpDev[sticknum] );

	// get data from the joystick
	hResult = IDirectInputDevice_GetDeviceState( g_lpDev[sticknum], sizeof(DIJOYSTATE), &js );
	if( FAILED( hResult ) )
	{
		// did the read fail because we lost input for some reason?
		// if so, then attempt to reacquire.  If the second acquire
		// fails, then the error from GetDeviceData will be
		// DIERR_NOTACQUIRED, so we won't get stuck an infinite loop.
		if( hResult == DIERR_INPUTLOST )
		{
			ReacquireInput( sticknum );
			// return the fact that we did not read any data
			return 0;
		}
		ShowDInputError( IDS_DIERR_ACQUIRE, DIERR_NOTACQUIRED, TRUE );
	}

	if (machine == Atari5200) 
	{
		int i;
		for( i = 0; i < 4; i++ )
		{
			if( js.rgbButtons[i] & 0x80 )
				trig[i] = 0;
			else
				trig[i] = 1;
		}
	}
	else
	{
		if( !bBtn2Flip )
		{
			if( js.rgbButtons[1] & 0x80 )
			{
				Toggle_FullSpeed();
				bBtn2Flip = TRUE;
			}
		}
		else
		{
			if( !(js.rgbButtons[1] & 0x80) )
				bBtn2Flip = FALSE;
		}

		if( !bBtn3Flip )
		{
			if( js.rgbButtons[2] & 0x80 )
			{
				Toggle_Pause();
				bBtn3Flip = TRUE;
			}
		}
		else
		{
			if( !(js.rgbButtons[2] & 0x80) )
				bBtn3Flip = FALSE;
		}

		if( !bBtn4Flip )
		{
			if( js.rgbButtons[3] & 0x80 )
			{
				Toggle_SIO_Patch();
				bBtn4Flip = TRUE;
			}
		}
		else
		{
			if( !(js.rgbButtons[3] & 0x80) )
				bBtn4Flip = FALSE;
		}

		if( bTestButton )
		{
			/* See if the repeat/skip options are being used for holding button down */
			if( nJoyRepeat )
			{
				if( js.rgbButtons[0] & 0x80)
				{
					/* Count down the number of repeat cycles and return button up for those */
					if( --nJoyCount > 0 )
						trig[sticknum] = 1;

					/* If we've counted the number of skip cycles, start over and send button down */
					if( nJoyCount < -nJoySkip )
					{
						nJoyCount = nJoyRepeat;
						trig[sticknum] = 0;
					}
				}
				else
				{
					/* Otherwise continue skipping, sending button up */
					nJoyCount = nJoyRepeat;
					trig[sticknum] = 1;
				}
			}
			else
			{
				/* No special handling, just return whether it is held down */
				if( js.rgbButtons[0] & 0x80)
					trig[sticknum] = 0;
				else
					trig[sticknum] = 1;
			}
		}
	}

	if( js.lX < 0 )
	{
		if( js.lY < 0 )
			stickval |= STICK_UL;
		if( js.lY > 0 )
			stickval |= STICK_LL;
		if( js.lY == 0 )
			stickval |= STICK_LEFT;
	}

	if( js.lX > 0 )
	{
		if( js.lY < 0 )
			stickval |= STICK_UR;
		if( js.lY > 0 )
			stickval |= STICK_LR;
		if( js.lY == 0 )
			stickval |= STICK_RIGHT;
	}

	if( js.lX == 0 )
	{
		if( js.lY < 0 )
			stickval |= STICK_FORWARD;

		if( js.lY > 0 )
			stickval |= STICK_BACK;

		if( js.lY == 0 )
			stickval |= STICK_CENTRE;
	}
	stick[sticknum] = stickval;
	return 1;
}

/* In other Atari800 versions, keyboard handling is here. In Atari800Win I handle
   the actual key processing in the mainframe of the app, and by the time it gets
   here we already have the appropriate Atari key code (except for keycodes that 
   require multiple logic operations, like the stick								*/
int Atari_Keyboard (void)
{
	if( iCurrentKey != AKEY_NONE )
	{
		if( GetAsyncKeyState( iCurrentVK ) >= 0 )
		{
			KEYPRESSED = FALSE;
			iCurrentKey = AKEY_NONE;
		}
	}

	if( iNewKey == AKEY_NONE )
		return iCurrentKey;

/*	if( iNewVK & AKEY_PENDING )
	{
		iNewVK &= ~AKEY_PENDING;
		return iCurrentKey;
	}*/

	if (machine == Atari5200) 
	{
		switch ( iNewKey ) 
		{
		case AKEY_COLDSTART:
			iNewKey = AKEY_COLDSTART;
			break;
			
		case AKEY_WARMSTART:
			iNewKey = 0x29;		/* 5200 has no warmstart */	/* reset */
			break;
			
		case AKEY_STARTDWN:
			iNewKey = 0x39;		/* start */
			break;
			
		case AKEY_OPTIONDWN:		
			iNewKey = 0x31;		/* pause */
			break;
			
		case AKEY_0:
			iNewKey = 0x2b;
			break;
			
		case AKEY_1:
			iNewKey = 0x0f;
			break;
			
		case AKEY_2:
			iNewKey = 0x3f;
			break;
			
		case AKEY_3:
			iNewKey = 0x3d;
			break;
			
		case AKEY_4:
			iNewKey = 0x3b;
			break;
			
		case AKEY_5:
			iNewKey = 0x37;
			break;
			
		case AKEY_6:
			iNewKey = 0x35;
			break;
			
		case AKEY_7:
			iNewKey = 0x33;
			break;
			
		case AKEY_8:
			iNewKey = 0x2f;
			break;
			
		case AKEY_9:
			iNewKey = 0x2d;
			break;
			
		case AKEY_MINUS:
			iNewKey = 0x23;		/* # key on 5200 controller */
			break;
			
		case AKEY_ASTERISK:		/* * key on 5200 controller */
			iNewKey = 0x27;
			break;
			
		default:
			iNewKey = AKEY_NONE;
			break;
		}
	}

	if( iNewKey & SPECIAL_HANDLER_MASK )
	{
		int	keytemp = iNewKey;
		iNewKey = AKEY_NONE;

		if( iNewVK >= VK_NUMPAD0 && iNewVK <= VK_NUMPAD9 && !bKBJoystick )
			iNewKey = keytemp & ~SPECIAL_HANDLER_MASK;

		switch (keytemp) 
		{
			case	(AKEY_HELP | SPECIAL_HANDLER_MASK):
				keytemp &= ~SPECIAL_HANDLER_MASK;
				if( mach_xlxe )
					iNewKey = keytemp;
				break;
				
			case	(AKEY_F1 | SPECIAL_HANDLER_MASK):
			case	(AKEY_F2 | SPECIAL_HANDLER_MASK):
			case	(AKEY_F3 | SPECIAL_HANDLER_MASK):
			case	(AKEY_F4 | SPECIAL_HANDLER_MASK):
				keytemp &= ~SPECIAL_HANDLER_MASK;
				/*if( SHIFT_KEY )
				iNewKey |= AKEY_SHFT;*/
				if( machine == AtariXL )
					iNewKey = keytemp;
				break;
				
			case AKEY_COLDSTART:
				Coldstart();
				break;
				
			case AKEY_WARMSTART:
				Warmstart();
				break;
				
			case AKEY_EXIT:
				PostMessage( MainhWnd, WM_CLOSE, 0, 0L );
				break;
				
			case AKEY_BREAK:
				IRQST &= ~0x80;
				if (IRQEN & 0x80) {
					GenerateIRQ();
				}
				break;
				
			case AKEY_UI:
				/*		  ui((UBYTE *)atari_screen);*/
				break;
				
			case AKEY_PIL:
				if (pil_on)
					DisablePILL();
				else
					EnablePILL();
				break;
				
			default:
				break;
		}
		if( iNewKey == AKEY_NONE )
			iNewVK = 0;
	}

	if( iNewKey != AKEY_NONE && iCurrentKey == AKEY_NONE )
	{
		if( iNewKey != AKEY_NONE )
		{
			KEYPRESSED = TRUE;
			KBCODE = iCurrentKey = iNewKey;
			iCurrentVK = iNewVK;
			iNewKey = AKEY_NONE;
			iNewVK = 0;
			IRQST &= ~0x40;
			if (IRQEN & 0x40) 
			{
				GenerateIRQ();
			}
		}
	}
	return iCurrentKey;
}

void GetJoystickInput( void )
{
	int i;

	for( i = 0; i < MAX_ATARI_JOYPORTS; i++ )
	{
		if( g_lpDev[i] )
			ReadJoystickInput( i, TRUE );
		else
		{
			if( gSelectedInputDevice[i] == -1 )
			{
				stick[i] = keystick;
				trig[i] = keytrig;
			}
			else
			{
				stick[i] = 0xf;
				trig[i] = 1;
			}
		}
	}
}

int Atari_PORT (int num)
{
	if (!num)
		return (stick[1] << 4) | stick[0];
		//return( (stick[1] & 0xF) << 4 | (stick[0] & 0xF));
	else
		return (stick[3] << 4) | stick[2];
}

int Atari_TRIG (int num)
{
	return trig[num];
}

int Atari_POT (int num)
{
	if (machine == Atari5200) 
	{
		if (num == 0 )
		{
			unsigned char pot0 = 120;
			if (!(stick[0] & 0x04))
				pot0 = 15;
			if (!(stick[0] & 0x08))
				pot0 = 220;
			return pot0;
		}
		
		if( num == 1)
		{
			unsigned char pot1 = 120;
			if (!(stick[0] & 0x01))
				pot1 = 15;
			if (!(stick[0] & 0x02))
				pot1 = 220;
			return pot1;
		}
	}
 	return 228;
}

int Atari_CONSOL (void)
{
  return consol;
}

static void Atari_Set_CONSOL( int newconsol )
{
	switch( newconsol )
	{
		case	AKEY_OPTIONDWN:	/* OPTION */
			consol &= 0x03;
			break;

		case	AKEY_SELECTDWN:	/* SELECT */
			consol &= 0x05;
			break;

		case	AKEY_STARTDWN:	/* START */
			consol &= 0x06;
			break;

		case	AKEY_OPTIONUP:
			consol |= 0x04;
			break;

		case	AKEY_SELECTUP:
			consol |= 0x02;
			break;

		case	AKEY_STARTUP:
			consol |= 0x01;
			break;
	}
}

BOOL ToggleKeyDown( WPARAM wp )
{
	if( wp >= VK_NUMPAD0 && wp <= VK_NUMPAD9 )
	{
		if( !bKBJoystick )
			return FALSE;

		if( wp == VK_NUMPAD0 )
		{
			keytrig = 0;
			return TRUE;
		}

		keystick &= iStickTable[ wp - VK_NUMPAD1 ];
		return TRUE;
	}

	switch( wp )
	{
		case	VK_F8:
			Toggle_FullSpeed();
			return TRUE;
			break;
			
		case	VK_F9:
			Toggle_Pause();
			return TRUE;
			break;
			
		case	VK_F11:
			Toggle_SIO_Patch();
			return TRUE;
			break;
				
		case	VK_SHIFT:
			SHIFT_KEY = TRUE;
			return TRUE;
			break;

		case	VK_CONTROL:
			CTRL_KEY = TRUE;
			return TRUE;
			break;

		case	VK_F2:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_OPTIONDWN );
			break;

		case	VK_F3:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_SELECTDWN );
			break;

		case	VK_F4:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_STARTDWN );
			break;			
	}
	return FALSE;
}

BOOL ToggleKeyUp( WPARAM wp )
{
	if( wp >= VK_NUMPAD0 && wp <= VK_NUMPAD9 )
	{
		if( !bKBJoystick )
			return FALSE;

		if( wp == VK_NUMPAD0 )
		{
			keytrig = 1;
			return TRUE;
		}

		if( ulMiscStates & ATARI_STICK_RELEASE )
			keystick |= (~iStickTable[ wp - VK_NUMPAD1 ]) & 0xf; /* Turn bits for this key off */
		return TRUE;
	}

	switch( wp )
	{
		case	VK_SHIFT:
			SHIFT_KEY = FALSE;
			return TRUE;
			break;

		case	VK_CONTROL:
			CTRL_KEY = FALSE;
			return TRUE;
			break;

		case	VK_F2:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_OPTIONUP );
			break;

		case	VK_F3:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_SELECTUP );
			break;

		case	VK_F4:
			if( !SHIFT_KEY )	/* Console keys only work in non-shift mode */
				Atari_Set_CONSOL( AKEY_STARTUP );
			break;
	}
	return FALSE;
}

static BOOL GetDIErrorString( HRESULT hResult, LPSTR lpszErrorBuff, DWORD cchError)
{
    DWORD  cLen;
    LPTSTR lpszError;
    TCHAR  szMsg[256];

    // Check parameters
    if (!lpszErrorBuff || !cchError)
    {
        // Error, invalid parameters
        return FALSE;
    }

    switch ( hResult )
    {
//#ifdef VERBOSE_DIRECTX_ERROR

		case DI_BUFFEROVERFLOW:
			//The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value. 
			lpszError = TEXT("DI_BUFFEROVERFLOW");
			break;

		case DI_DOWNLOADSKIPPED:
			//The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode. 
			lpszError = TEXT("DI_DOWNLOADSKIPPED");
			break;

		case DI_EFFECTRESTARTED:
			//The effect was stopped, the parameters were updated, and the effect was restarted. 
			lpszError = TEXT("DI_EFFECTRESTARTED");
			break;

//		case DI_NOEFFECT:
			//The operation had no effect. This value is equal to the S_FALSE standard COM return value. 
//			lpszError = TEXT("DI_NOEFFECT");
//			break;

//		case DI_NOTATTACHED:
			//The device exists but is not currently attached. This value is equal to the S_FALSE standard COM return value. 
//			lpszError = TEXT("DI_NOTATTACHED");
//			break;
	
		case DI_OK:
			//The operation completed successfully. This value is equal to the S_OK standard COM return value. 
			lpszError = TEXT("DI_OK");
			break;

		case DI_POLLEDDEVICE:
			//The device is a polled device. As a result, device buffering will not collect any data and event notifications will not be signaled until the IDirectInputDevice2::Poll method is called. 
			lpszError = TEXT("DI_POLLEDDEVICE");
			break;

//		case DI_PROPNOEFFECT:
			//The change in device properties had no effect. This value is equal to the S_FALSE standard COM return value. 
//			lpszError = TEXT("DI_PROPNOEFFECT");
//			break;

		case DI_TRUNCATED:
			//The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value. 
			lpszError = TEXT("DI_TRUNCATED");
			break;

		case DI_TRUNCATEDANDRESTARTED:
			//Equal to DI_EFFECTRESTARTED | DI_TRUNCATED.
			lpszError = TEXT("DI_TRUNCATEDANDRESTARTED");
			break;

		case DIERR_ACQUIRED:
			//The operation cannot be performed while the device is acquired. 
			lpszError = TEXT("DIERR_ACQUIRED");
			break;

		case DIERR_ALREADYINITIALIZED:
			//This object is already initialized 
			lpszError = TEXT("DIERR_ALREADYINITIALIZED");
			break;

		case DIERR_BADDRIVERVER:
			//The object could not be created due to an incompatible driver version or mismatched or incomplete driver components. 
			lpszError = TEXT("DIERR_BADDRIVERVER");
			break;

		case DIERR_BETADIRECTINPUTVERSION:
			//The application was written for an unsupported prerelease version of DirectInput. 
			lpszError = TEXT("DIERR_BETADIRECTINPUTVERSION");
			break;

		case DIERR_DEVICEFULL:
			//The device is full. 
			lpszError = TEXT("DIERR_DEVICEFULL");
			break;

		case DIERR_DEVICENOTREG:
			//The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value. 
			lpszError = TEXT("DIERR_DEVICENOTREG");
			break;

		case DIERR_EFFECTPLAYING:
			//The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing. 
			lpszError = TEXT("DIERR_EFFECTPLAYING");
			break;

		case DIERR_HASEFFECTS:
			//The device cannot be reinitialized because there are still effects attached to it.
			lpszError = TEXT("DIERR_HASEFFECTS");
			break;

		case DIERR_GENERIC:
			//An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value. 
			lpszError = TEXT("DIERR_GENERIC");
			break;

		case DIERR_HANDLEEXISTS:
			//The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value. 
			lpszError = TEXT("DIERR_HANDLEEXISTS");
			break;

		case DIERR_INCOMPLETEEFFECT:
			//The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.
			lpszError = TEXT("DIERR_INCOMPLETEEFFECT");
			break;

		case DIERR_INPUTLOST:
			//Access to the input device has been lost. It must be reacquired. 
			lpszError = TEXT("DIERR_INPUTLOST");
			break;

		case DIERR_INVALIDPARAM:
			//An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value. 
			lpszError = TEXT("DIERR_INVALIDPARAM");
			break;

		case DIERR_MOREDATA:
			//Not all the requested information fitted into the buffer.
			lpszError = TEXT("DIERR_MOREDATA");
			break;

		case DIERR_NOAGGREGATION:
			//This object does not support aggregation. 
			lpszError = TEXT("DIERR_NOAGGREGATION");
			break;

		case DIERR_NOINTERFACE:
			//The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value. 
			lpszError = TEXT("DIERR_NOINTERFACE");
			break;

		case DIERR_NOTACQUIRED:
			//The operation cannot be performed unless the device is acquired. 
			lpszError = TEXT("DIERR_NOTACQUIRED");
			break;

		case DIERR_NOTBUFFERED:
			//The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering.
			lpszError = TEXT("DIERR_NOTBUFFERED");
			break;

		case DIERR_NOTDOWNLOADED:
			//The effect is not downloaded.
			lpszError = TEXT("DIERR_NOTDOWNLOADED");
			break;

		case DIERR_NOTEXCLUSIVEACQUIRED:
			//The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode. 
			lpszError = TEXT("DIERR_NOTEXCLUSIVEACQUIRED");
			break;

		case DIERR_NOTFOUND:
			//The requested object does not exist. 
			lpszError = TEXT("DIERR_NOTFOUND");
			break;

		case DIERR_NOTINITIALIZED:
			//This object has not been initialized. 
			lpszError = TEXT("DIERR_NOTINITIALIZED");
			break;

//		case DIERR_OBJECTNOTFOUND:
			//The requested object does not exist. 
//			lpszError = TEXT("DIERR_OBJECTNOTFOUND");
//			break;

		case DIERR_OLDDIRECTINPUTVERSION:
			//The application requires a newer version of DirectInput. 
			lpszError = TEXT("DIERR_OLDDIRECTINPUTVERSION");
			break;

//		case DIERR_OTHERAPPHASPRIO:
			//Another application has a higher priority level, preventing this call from succeeding. This value is equal to the E_ACCESSDENIED standard COM return value. This error can be returned when an application has only foreground access to a device but is attempting to acquire the device while in the background.
//			lpszError = TEXT("DIERR_OTHERAPPHASPRIO");
//			break;

		case DIERR_OUTOFMEMORY:
			//The DirectInput subsystem couldn't allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value. 
			lpszError = TEXT("DIERR_OUTOFMEMORY");
			break;

//		case DIERR_READONLY:
//			//The specified property cannot be changed. This value is equal to the E_ACCESSDENIED standard COM return value. 
//			lpszError = TEXT("DIERR_READONLY");
//			break;

		case DIERR_UNSUPPORTED:
			//The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value. 
			lpszError = TEXT("DIERR_UNSUPPORTED");
			break;

		case E_PENDING:
			//Data is not yet available.
			lpszError = TEXT("E_PENDING");
			break;

//#endif	/* VERBOSE_DIRECTX_ERROR */

		default:
			// Unknown DI Error
			wsprintf (szMsg, "Error #%ld", (DWORD)(hResult & 0x0000FFFFL) );
			//wsprintf (szMsg, "Unknown Error #%ld", (DWORD)(hResult & 0x0000FFFFL));
			lpszError = szMsg;
			break;
	}
    // Copy DS Error string to buff
    cLen = strlen (lpszError);
    if (cLen >= cchError)
    {
        cLen = cchError - 1;
    }

    if (cLen)
    {
        strncpy (lpszErrorBuff, lpszError, cLen);
        lpszErrorBuff[cLen] = 0;
    }

    return TRUE;
}