/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/Altair.c 139   11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  WinMain and User Interface
 
  Copyright (c) 1991-1997 Claus T. Giloi
  Copyright (c) 2000-2006 Richard A. Cini


Change Log:
  2000/06/08  RAC -- Initial modifications
  2000/08/26  RAC -- Began mods for "get address" function. Added TODOs
 	 					for emulating virtual console I/O hardware at the 
 						code locations.
  2000/09/10  RAC -- Fixed minor errors. Compiles with warnings only.
  2000/09/11  RAC -- Deleted fFileType def (defined in punch.h)
  2000/09/18  RAC -- Changed proto for InitApplication to int from BOOL
  2000/10/01  RAC -- Added code to restrict punch to emulator power on
  2000/10/16  RAC -- Code check-in from Theo. Minor mods to def of DLGPROC
  2000/10/31  RAC -- Added global command line variable
  2000/11/10  RAC -- Moved call to LoadStartupFiles to the power-on switch
 						code. Code also clears page status array.
  2000/12/21  RAC -- Integrated interface to LPR code.
  2001/01/04  RAC -- Removed LPR configuration. Code now assumes that if 
 						the printer is "on" that there is no connection to
 						punch device. In WM_DESTROY, made sure that LPR
 						file gets closed. Added comments for fan noise.
  2001/01/10  RAC -- Moved device shutdown code to WM_COMMAND
  2001/01/27  RAC -- Added multimedia defs
  2001/02/20  RAC -- Return parens; string term correct
  2001/02/26  RAC -- Added "switch click" wave file to DoSwitch switch dispatcher
  2001/03/29  RAC -- Began mods for new front panel look. Changed FP bitmap size
 						to 640x320 (2.0:1) to more closely match the actual
 						aspect ratio of the Altair (19"x9", 2.1:1)
  2001/05/11  RAC -- Redefined module globals as "static"; played with title bar text
  2001/05/23  RAC -- Moved some 8080-specific code to i8080.c and fixed register
 						pointers to use macros.
  2001/07/18  RAC -- Started playing with Telnet stuff.
  2001/08/19  RAC -- RELEASE MARKER -- v2.0
  2001/08/26  RAC -- Began work on disk image use v2.01b
  2001/08/27  RAC -- Changed emulator halt in menu code to bPrun=RUN_OFF
  2001/08/29  RAC -- Fixed LED update after file load
  2001/08/30  RAC -- Moved emulator startup stuff (like CPU reset) to InitApp
  2001/09/04  RAC -- Improved speed by changing FP LED updating; working on 
 						PTP loader.
  2001/09/07  RAC -- Added hooks for debugger; removed SIO config code.
  2001/09/12  RAC -- Minor changes for calling dialogs; fixed access violation
 						in OkMessage.
  2001/09/13  RAC -- Moved toggle switch protos here from header file.
  2001/09/17  RAC -- Added hooks for a PTP mode switch.
  2001/10/01  RAC -- Added dialog message trap for PTP device window in main
 						message loop.
  2001/10/02  RAC -- Added code to reposition main window on startup.
  2001/11/07  RAC -- Began modifications for debugger
  2001/11/15  RAC -- Changed bIntEnabled to I80Regs.IFF
  2001/11/28  RAC -- Added WM_SETFOCUS handler to force repaint of FP
  2001/12/05  RAC -- Fixed clock8080_bkpt. Need to change all calls into
   						lpfn's.
  2001/12/13  RAC -- Added option to kill FP lights; final changes for help
   						menu; minor changes to message loop for disk dialog.
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2001/12/19  RAC -- Made initial changes for winstate; moved all serial
   						routines into one header.
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/09/03  RAC -- Started changes for new loader routines.
  2002/09/12  RAC -- Changes made to support console abstraction layer.
  2002/09/23  RAC -- Added menu items for configuration dialogs; modified
   						InitEmu to do more work of setting up emulation state
   						including loading INI options -- makes for a cleaner
   						initialization.
  2002/10/02  RAC -- Console Config disabled since function call method not
						implemented in sio.c yet.
  2002/10/04  RAC -- Enabled console config; added simstate.runstate handling
   						in WndProc and switch handlers as final step towards
   						getting timeslicing working.
  2002/10/16  RAC -- Added SYS_Set8080Speed(??) to power, reset, and step
   						switch handlers. Slow-stepping really is running
   						at 500KHz (quarter-speed prototypical). Eliminated
   						private header file.
  2002/10/17  RAC -- TIMESLICING FORK
  2002/10/21  RAC -- Conversion to timeslicing model completed -- WORKS!
   						Minor screen paint issue when power OFF.
  2002/10/22  RAC -- Fixed screen repaint issue (MoveWindow call in LoadOptions
   						un-synched the validation region).
  2002/10/26  RAC -- Fixed single-step screen update anomaly (full-screen
   						repaint on toggle -- traced to null pointer in
   						InvalidateRegion in windbg.c. Called due to fudging
   						single-step in SYS_DoTimeSlicing. Created separate
   						SST handler in timeslice loop. WORKS)
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2002/11/20  RAC -- Added call to TcpInit to force startup of telnet server
  						at emulator startup...later removed.
  2002/11/22  RAC -- Added support for HtmlHelp.
  2003/02/24  JJF -- Changed Altair LED and Switch layout for new Bitmap
  2003/02/25  RAC -- Changed to InitEmu to support third console type (serial)
  2003/03/04  RAC -- Merged changes from Joe Forgione
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2003/11/18  RAC -- Minor changes to support revised cassette/paper tape
  2004/01/20  RAC -- Diff'ed changes from FJS (Fred J. Scipione)
  2004/03/09  RAC -- Diff'ed more changes from FJS (Fred J. Scipione); mostly
  						LCC32 compatibility changes. See changelog.
  2004/06/16  SML -- Several changes as part of moving SCHED_DoTimeSlicing
						to its own thread.
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135 
  2004/09/09  RAC -- Moved console assignment from InitEmu to syscfg.c.
  2004/09/17  RAC -- Rearranged hardware menu items and added hook for
  						video screen.
  2004/10/09  FJS -- Much...
  2004/11/19  RAC -- Began changes to support physical front panel. Need
  						to add stuff to syscfg.c also.
  2005/04/22  RAC -- Began fpdll integration; awaiting panel
  2006/04/04  RAC -- Removed init code to init.c
  2006/04/15  RAC -- Created links to USB front panel
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/06/12  RAC -- Change to ts_power for Dazzler window.
  2006/11/30  RAC -- RELEASE MARKER -- v3.20.0400
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/

#include <windows.h>	// required for all Windows applications
#include <windowsx.h>	// message cracker macros
#include <shellapi.h>	// ShellExecute	shell32.lib
#include <commdlg.h>	// Windows common dialogs
#include <commctrl.h>	// Windows common controls
#include <mmsystem.h>	// Windows multimedia/timers
#include <stdlib.h>		// C standard lib
#include <string.h>		// C-lib - strings
#include <stdio.h>		// C-lib - I/O included in altair32.h
#include <process.h>	// threads
#include "altair32.h"	// includes "resource.h" and exports from other major modules
//#include "extensions.h" // corrections for bad functions in windowsx
#include "comcthlp.h"	// common control macros
#include "htmlhelp.h"	// HTML help engine
#include "windbg.h"		// integrated debugger header
#include "i8080.h"		// processor emulation
#include "fpdll.h"		// front panel dll header


/**  Typedefs & Defines  **************************************/
// switch positions and up/neut/down moved to altair32.h for use with FP
#define	C_RED   0	// primary color for IMSAI
#define	C_BLUE  1	// secondary switch color for IMSAI

// Toggle switch defs - values are significant
#define	SWTYPE_1  0	// two-position spring loaded switch [(on)-off-(on)]
#define	SWTYPE_2  2	// two-position toggle switch [on-off]
#define	SWTYPE_3  3	// three-position toggle switch [on-off-on]


/**  Forward Prototypes  **************************************/
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
static  void ts_power(short, short);
static  void ts_inoutacc(short, short);
static  void ts_examine(short, short);
static  void ts_deposit(short, short);
static  void ts_loadacc(short, short);
static  void ts_ADset(short, short);
static  void ts_protect(short, short);
static  void ts_run(short, short);
static  void ts_run2(short, short);
static  void ts_reset(short, short);

// WndProc message handlers
static void _OnChar(HWND, TCHAR, int);
static void _OnClose(HWND);
static void _OnCommand(HWND, int, HWND, UINT);
static BOOL _OnCreate(HWND, LPCREATESTRUCT);
static void _OnDestroy(HWND);
static void _OnInitMenu(HWND, HMENU);
static void _OnKeyDown(HWND, UINT, BOOL, int, UINT);
static void _OnLButtonDown(HWND, BOOL, int, int, UINT);
static void _OnLButtonUp(HWND, int, int, UINT);
static void _OnMenuSelect(HWND, HMENU, int, HMENU, UINT);
static void _OnPaint(HWND);
static void _OnPower(HWND, int);
static void _OnQuit(HWND, int);
static void _OnSetFocus(HWND, HWND);


/**  Globals  *************************************************/
HWND   hwndStatusBar = NULL;	// global handle to status bar
ushort Dswitches;				// switches 7-0


/**  Locals  **************************************************/
static TCHAR   szBuildNumber[ ] = {"Version 3.20.0400"};
static char    szBuffer[64];
static HBITMAP hSav1;
static HBITMAP hSav2;
static HBITMAP hAltairPanel;	// CG altair bitmap handle
static HBITMAP hLED_on;
static HBITMAP hLED_off;
static HBITMAP hSwitchBuf;
static HCURSOR hWinHelpCursor;
static HDC	   hMemoryDC;
static HDC	   hScratchDC;
static HICON   hTogUp, hTogDown, hTogNeut;
static HICON   hTogUp2, hTogDown2, hTogNeut2;	// for alternate color: blue
static HMODULE hModFP = NULL;
static short   Togswitchdown = -1;


/*--  LED Structure  -----------------------------------------
	struct data (loc_x, loc_y, def_val, *variable, bitmask)
-------------------------------------------------------------*/
struct leds {
	short x;					// h_pos
	short y;					// v_pos
	short status; 				// ON=1 | OFF=0
	unsigned short * srcbyte;	// source data byte
	unsigned short srcmask;	// source bit within source byte
} LedList[] = {

#ifdef IMSAI
	{ 460, 165, 0, &I80Regs.IFF, 0xff },		// interrupts enabled...
	{ 490, 165, 0, &simstate.bPrun, RUN_ON },	// Run...
	{ 520, 165, 0, &simstate.bPrun, RUN_OFF },	// kludged for now... WAIT signal???
	{ 550, 165, 0, &I80Regs.IFF, 0x0 }, 		// kludged for now... HLDA sig

	// MEMR, etc.
	{ 50, 105, 0, &I80Regs.CPUstatus, MEMR },
	{ 70, 105, 0, &I80Regs.CPUstatus, INP },
	{ 90, 105, 0, &I80Regs.CPUstatus, M1 },
	{ 110, 105, 0, &I80Regs.CPUstatus, pOUT },
	{ 130, 105, 0, &I80Regs.CPUstatus, HLTA },
	{ 150, 105, 0, &I80Regs.CPUstatus, STACK },
	{ 170, 105, 0, &I80Regs.CPUstatus, WO },
	{ 190, 105, 0, &I80Regs.CPUstatus, sINT },

	// IOPANEL
	{ 50, 45, 0, &IOPANEL, 0x80 },
	{ 70, 45, 0, &IOPANEL, 0x40 },
	{ 90, 45, 0, &IOPANEL, 0x20 },
	{ 110, 45, 0, &IOPANEL, 0x10 },
	{ 130, 45, 0, &IOPANEL, 0x08 },
	{ 150, 45, 0, &IOPANEL, 0x04 },
	{ 170, 45, 0, &IOPANEL, 0x02 },
	{ 190, 45, 0, &IOPANEL, 0x01 },

	// data bus LEDs
	{ 238, 105, 0, &MBR, 0x80 },
	{ 258, 105, 0, &MBR, 0x40 },
	{ 278, 105, 0, &MBR, 0x20 },
	{ 298, 105, 0, &MBR, 0x10 },
	{ 318, 105, 0, &MBR, 0x08 },
	{ 338, 105, 0, &MBR, 0x04 },
	{ 358, 105, 0, &MBR, 0x02 },
	{ 378, 105, 0, &MBR, 0x01 },

	// Address bus 0-7 LEDs
	{ 238, 172, 0, &I80Regs.nPC.W, 0x80 },
	{ 258, 172, 0, &I80Regs.nPC.W, 0x40 },
	{ 278, 172, 0, &I80Regs.nPC.W, 0x20 },
	{ 298, 172, 0, &I80Regs.nPC.W, 0x10 },
	{ 318, 172, 0, &I80Regs.nPC.W, 0x08 },
	{ 338, 172, 0, &I80Regs.nPC.W, 0x04 },
	{ 358, 172, 0, &I80Regs.nPC.W, 0x02 },
	{ 378, 172, 0, &I80Regs.nPC.W, 0x01 },

	// Addres bus 8-15 LEDs
	{ 50, 172, 0, &I80Regs.nPC.W, 0x8000 },
	{ 70, 172, 0, &I80Regs.nPC.W, 0x4000 },
	{ 90, 172, 0, &I80Regs.nPC.W, 0x2000 },
	{ 110, 172, 0, &I80Regs.nPC.W, 0x1000 },
	{ 130, 172, 0, &I80Regs.nPC.W, 0x0800 },
	{ 150, 172, 0, &I80Regs.nPC.W, 0x0400 },
	{ 170, 172, 0, &I80Regs.nPC.W, 0x0200 },
	{ 190, 172, 0, &I80Regs.nPC.W, 0x0100 },
#else						// Altair
	// WAIT is CPU output pin that acknowledges start of waitstate triggered
	//	by READY=LOW. READY is toggled by single-stepper and run/stop.
	// HLDA is CPU output pin that acknowledges HOLD input. Data and address
	//	bus is tri-stated during HOLD. Used for DMA.
	{ 95, 47, 0, &simstate.bPprotect, 0xff }, 	// PROTECT 69 (95,47)
	{ 71, 47, 0, &I80Regs.IFF, 0xff }, 			// interrupts enabled... 69 (71,47)
	{ 71, 94, 0, &simstate.bPrun, RUN_OFF },	// kludged for now... WAIT signal 123 (71,94)
	{ 95, 94, 0, &I80Regs.IFF, 0 },				// kludged for now... HLDA signal 123 (95,94)

	// MEMR, etc.
	{ 119, 47, 0, &I80Regs.CPUstatus, MEMR },	//130,38  (119,47)
	{ 143, 47, 0, &I80Regs.CPUstatus, INP },	//156 (143,47)
	{ 167, 47, 0, &I80Regs.CPUstatus, M1 },		//182 (167,47)
	{ 191, 47, 0, &I80Regs.CPUstatus, pOUT },	//208 (191,47)
	{ 215, 47, 0, &I80Regs.CPUstatus, HLTA },	//234 (215,47)
	{ 239, 47, 0, &I80Regs.CPUstatus, STACK },	//260 (239,47)
	{ 263, 47, 0, &I80Regs.CPUstatus, WO },		//286 (263,47)
	{ 287, 47, 0, &I80Regs.CPUstatus, sINT },	//312 (287,47)

	// data bus LEDs
	{ 369, 47, 0, &MBR, 0x80 },		//358,38 (369,47)
	{ 392, 47, 0, &MBR, 0x40 },		//384 (392,47)
	{ 428, 47, 0, &MBR, 0x20 },		//410 (428,47)
	{ 451, 47, 0, &MBR, 0x10 },		//436 (451,47)
	{ 474, 47, 0, &MBR, 0x08 },		//462 (474,47)
	{ 510, 47, 0, &MBR, 0x04 },		//488 (510,47)
	{ 533, 47, 0, &MBR, 0x02 },		//512 (533,47)
	{ 556, 47, 0, &MBR, 0x01 },		//540 (556,47)

	// Address bus 0-7 LEDs
	{ 369,  94, 0, &I80Regs.nPC.W, 0x80 },		//358,100 (369,94)
	{ 392,  94, 0, &I80Regs.nPC.W, 0x40 },		//384 (392,94)
	{ 428,  94, 0, &I80Regs.nPC.W, 0x20 },		//410 (428,94)
	{ 451,  94, 0, &I80Regs.nPC.W, 0x10 },		//436 (451,94)
	{ 474,  94, 0, &I80Regs.nPC.W, 0x08 },		//462 (474,94)
	{ 510,  94, 0, &I80Regs.nPC.W, 0x04 },		//488 (510,94)
	{ 533,  94, 0, &I80Regs.nPC.W, 0x02 },		//514 (533,94)
	{ 556,  94, 0, &I80Regs.nPC.W, 0x01 },		//540 (556,94)

	// Addres bus 8-15 LEDs
	{ 146,  94, 0, &I80Regs.nPC.W, 0x8000 },		//130,100  (146,94)
	{ 182,  94, 0, &I80Regs.nPC.W, 0x4000 },		//156  (182,94)
	{ 205,  94, 0, &I80Regs.nPC.W, 0x2000 },		//182 (205,94)
	{ 228,  94, 0, &I80Regs.nPC.W, 0x1000 },		//208 (228,94)
	{ 264,  94, 0, &I80Regs.nPC.W, 0x0800 },		//234 (264,94)
	{ 287,  94, 0, &I80Regs.nPC.W, 0x0400 },		//260 (287,94)
	{ 310,  94, 0, &I80Regs.nPC.W, 0x0200 },		//286 (310,94)
	{ 346,  94, 0, &I80Regs.nPC.W, 0x0100 },		//312 (346,94)
#endif
};
#define	N_LEDS (sizeof(LedList)/sizeof(LedList[0]))


/*--  Toggle Switch Structure  -------------------------------
	struct data (loc_x, loc_y, def_pos, action, color, *handler)
-------------------------------------------------------------*/
struct togs {
	short x;				// h_pos
	short y;				// v_pos
	short pos;				// switch position
	short type;			// switch type
	short color;			// switch color
	void (*action)(short, short);		// switch action handler
} TogSwitches[] = {

#ifdef	IMSAI
	// IMSAI switch locations
	// address a-15 to a-8
	#define A15BASE	0	// toggle switch ID of A15
	{  50, 238, SW_UP,	 SWTYPE_2, C_BLUE, ts_ADset },
	{  70, 238, SW_UP,	 SWTYPE_2, C_BLUE, ts_ADset },
	{  90, 238, SW_UP,	 SWTYPE_2, C_BLUE, ts_ADset },
	{ 110, 238, SW_UP,	 SWTYPE_2, C_BLUE, ts_ADset },
	{ 130, 238, SW_UP,	 SWTYPE_2, C_RED, ts_ADset },
	{ 150, 238, SW_UP,	 SWTYPE_2, C_RED, ts_ADset },
	{ 170, 238, SW_UP,	 SWTYPE_2, C_RED, ts_ADset },
	{ 190, 238, SW_UP,	 SWTYPE_2, C_RED, ts_ADset },
	// address/data ad-7 to ad-0
	#define	D7BASE 8
	{ 240, 238, SW_DOWN, SWTYPE_2, C_BLUE, ts_ADset },
	{ 260, 238, SW_DOWN, SWTYPE_2, C_BLUE, ts_ADset },
	{ 280, 238, SW_DOWN, SWTYPE_2, C_BLUE, ts_ADset },
	{ 300, 238, SW_DOWN, SWTYPE_2, C_BLUE, ts_ADset },
	{ 320, 238, SW_DOWN, SWTYPE_2, C_RED, ts_ADset },
	{ 340, 238, SW_DOWN, SWTYPE_2, C_RED, ts_ADset },
	{ 360, 238, SW_DOWN, SWTYPE_2, C_RED, ts_ADset },
	{ 380, 238, SW_DOWN, SWTYPE_2, C_RED, ts_ADset },

	// utility control switches at bottom - need individual defs
	{ 458, 238, SW_NEUT, SWTYPE_1, C_BLUE, ts_examine },	// examine/ex next
	{ 478, 238, SW_NEUT, SWTYPE_1, C_RED, ts_deposit },		// deposit/dep next
	{ 498, 238, SW_NEUT, SWTYPE_1, C_BLUE, ts_reset },		// reset/clear ext
	{ 518, 238, SW_NEUT, SWTYPE_1, C_RED, ts_run },			// stop/run
	{ 538, 238, SW_NEUT, SWTYPE_1, C_BLUE, ts_run2 },		// sstep/slow

	// power switch
	{ 560, 238, SW_DOWN, SWTYPE_2, C_RED, ts_power },
#else														// Altair
	// power switch
	{ 33, 187, SW_UP, SWTYPE_2, 0, ts_power },	// (33,187)	default UP

	// address a-15 to a-8
	#define A15BASE	1	// toggle switch ID of A15
	{ 145, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 181, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 204, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 227, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 263, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 286, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 309, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 345, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	// address/data ad-7 to ad-0
	#define	D7BASE 9
	{ 368, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 391, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 427, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 450, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 473, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 509, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 532, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },
	{ 555, 139, SW_DOWN, SWTYPE_2, 0, ts_ADset },

	// utility control switches at bottom - need individual defs 17
	{ 151, 187, SW_NEUT, SWTYPE_1, 0, ts_run },			// stop/run
	{ 198, 187, SW_NEUT, SWTYPE_1, 0, ts_run2 },		// sstep/slow
	{ 244, 187, SW_NEUT, SWTYPE_1, 0, ts_examine },		// examine/ex next
	{ 290, 187, SW_NEUT, SWTYPE_1, 0, ts_deposit },		// deposit/dep next
	{ 336, 187, SW_NEUT, SWTYPE_1, 0, ts_reset },		// reset/clear ext
	{ 382, 187, SW_NEUT, SWTYPE_1, 0, ts_protect },		// protect/unprotect
	{ 428, 187, SW_NEUT, SWTYPE_1, 0, ts_loadacc },		// display/load acc
	{ 474, 187, SW_NEUT, SWTYPE_1, 0, ts_inoutacc },	// input/output acc
#endif
};

#define	N_TOGSW (sizeof(TogSwitches)/sizeof(TogSwitches[0]))


/***********************************************************************\
*  Toggle Switch Functions
*  Perform an action when the switch is toggled ... switch specific
\***********************************************************************/

//static void do_nothing( short i, short pos ) {; }


/*----------------------------------------------------------------------
 *	Accumulator in/out toggle handler -- Unconnected switches in initial
 *  and "A" revisions of the Altair 8080 but used in the "B" model.
 *  Labeled as AUX2
 *  UNDOCUMENTED functionality in the A32.
 */
static void ts_inoutacc( short i, short pos )
{
	I80Regs.CPUstatus = CPU_CLR;

	// Exit handler if power is off or simulator is not halted
	if ( !simstate.bPpower 
		|| (simstate.runstate != HALTED) 
		|| (simstate.runstate == RESET)
		)
		return;
	
	if ( pos == SW_NEUT ) return;

	if ( pos == SW_UP )			// get
		MBR = I80Regs.nAF.B.h = GetIO( (ushort)(Aswitches >> 8) );
	else if ( pos == SW_DOWN )	// set
		SetIO( (ushort)(Aswitches >> 8), I80Regs.nAF.B.h );
		
	SetLEDs( SETLED_UPD );
}

/*----------------------------------------------------------------------
 *	Accumulator load toggle handler -- Unconnected switches in initial
 *  and "A" revisions of the Altair 8080 but used in the "B" model.
 *  Labeled as AUX1
 *  UNDOCUMENTED functionality in the A32.
 */
static void ts_loadacc( short i, short pos )
{
	// Exit handler if power is off or simulator is not halted
	if ( !simstate.bPpower || (simstate.runstate != HALTED) )
		return;

	if ( pos == SW_NEUT ) return;

	if ( pos == SW_UP )  			// display
		MBR = I80Regs.nAF.B.h;
	else if ( pos == SW_DOWN ) {  	// load
		MBR = Dswitches;
		I80Regs.nAF.B.h = (uchar) MBR;
	}
	
	SetLEDs( SETLED_UPD );
}

/*----------------------------------------------------------------------
 *	Memory examine toggle handler
 */	
static void ts_examine( short i, short pos )
{
	// Exit handler if power is off or simulator is not halted
	if ( !simstate.bPpower
		|| (simstate.runstate != HALTED)
		|| (simstate.runstate == RESET) )
		return;

	if ( pos == SW_NEUT ) return;

	if ( pos == SW_UP )	{ 			// examine
		I80Regs.nPC.W = Aswitches;
		MBR = GetMem( I80Regs.nPC.W );
	}
	else if ( pos == SW_DOWN ) { 	// examine next
		I80Regs.nPC.W++;
		MBR = GetMem( I80Regs.nPC.W );
	}
	
	SetLEDs( SETLED_UPD );
}

/*----------------------------------------------------------------------
 *	Memory deposit toggle handler
 */
static void ts_deposit( short i, short pos )
{
	// Exit handler if power is off or simulator is not halted
	if ( !simstate.bPpower || (simstate.runstate != HALTED) )
		return;

	if ( pos == SW_NEUT ) return;

	if ( pos == SW_UP )	{ 			// deposit
		MBR = Dswitches;
		SetMem( I80Regs.nPC.W, (uchar)MBR );
	}
	else if ( pos == SW_DOWN ) { 	// deposit next
		MBR = Dswitches;
		SetMem( ++I80Regs.nPC.W, (uchar)MBR );
	}
	
	SetLEDs( SETLED_UPD );
}

/*----------------------------------------------------------------------
 *	Address/data switch toggle handler
 */
static void ts_ADset( short i, short pos )
{
	ushort tmp;

	/*  Switch states should be valid at all times so that when power
	 *	is turned on, switch registers reflect actual switch state
	 *	without having to re-toggle the switches to register the state.
	 */
	tmp = 0x8000 >> ( i - A15BASE );	// build switch mask

	if ( pos == SW_UP ) {
		Aswitches |= tmp;
		Dswitches |= tmp;
	}
	else {
		Aswitches &= ~tmp;
		Dswitches &= ~tmp;
	}

	if ( !simstate.bPpower ) return;	// if power is off, return
		
	SetLEDs( SETLED_UPD );
}

/*----------------------------------------------------------------------
 *	Reset switch toggle handler
 */
static void ts_reset( short i, short pos )
{

	if ( !simstate.bPpower ) return;

	if ( pos == SW_NEUT ) {			// spring to neutral
		Reset8080();
		SYS_Set8080Speed(simstate.orig_up_speed);	// restore original speed

		// What emulated hardware, if any, should be RESET here??

		SetLEDs( SETLED_UPD );
	}

	if ( pos == SW_UP ) {			// Reset
		simstate.runstate = HALTED;	// was =RESET
		simstate.bPrun = RUN_OFF;
		simstate.bPprotect = 0;
		SetLEDs( SETLED_SET );
	}
	else if ( pos == SW_DOWN ) {	// External Clear
		return;
	}
}

/*----------------------------------------------------------------------
 *	Power switch toggle handler. Positions are opposite on each machine
 */
static void ts_power( short i, short pos )
{

	if ( pos == PWR_OFF ){
		SetLEDs( SETLED_CLR );			// clear FP
		simstate.bPpower = 0;			// main power
		simstate.runstate = HALTED;	// halt scheduler
		simstate.bPrun = RUN_OFF;		// 'WAIT' FP LED register
		simstate.bPprotect = 0;		// memory write protect
		SYSCFG_SaveOptions();
		simstate.conshtdn_func();		// kill console, ...
		DISK_Shutdown();				// required in order
		CASS_Shutdown();				//	to delete GDI
		LPR_Shutdown();				//	resources properly
		DAZZLER_VideoWin(VWM_Inactivate, 0); // hide the Dazzler window
		if (simstate.Siostati_func /* != NULL; Sio active */) {
			// kill TCP/COMx connection -
			simstate.Sioshtdn_func();
			simstate.Siostati_func = NULL;
		}
	}
	else if ( pos == PWR_ON ){
		/*  Perform master init.  Calls CpuPOR, LoadOptions, sets
		 *  initial execution pointers (simstate and console),
		 *  inits scheduler and breakpoints.
		 */
		InitEmu();		// bPpower=0; bPprotect=0; bPrun=OFF; runstate=HALTED
						// DISK/CASS inits
		SYS_Set8080Speed(simstate.orig_up_speed);	// in case shut off during slow-step
		simstate.bPpower = 1;		// set "power on" flag
		SetLEDs( SETLED_CLR );	
		SetLEDs( SETLED_UPD );
		if (Fansnd){
			PlaySound("Fan1", winstate.hInst, SND_RESOURCE | SND_SYNC | SND_NOWAIT);
			PlaySound("Fan2", winstate.hInst, SND_RESOURCE | SND_ASYNC | SND_NOWAIT);
		}
	}
}


/*----------------------------------------------------------------------
 *	Memory write-protect switch toggle handler
 *		DOWN=unprotect; UP=protect
 */
static void ts_protect( short i, short pos )
{
	if ( !simstate.bPpower ) return;	// do nothing if OFF
		
	if ( pos == SW_NEUT ) return;

	if		( pos == SW_DOWN )	simstate.bPprotect = 0;
	else if ( pos == SW_UP )	simstate.bPprotect = 1;

	SetLEDs( SETLED_UPD );
}


/*----------------------------------------------------------------------
 *	RUN state switch toggle handler
 *		UP=STOP; DOWN=RUN (Altair); IMSAI is opposite
 */
static void ts_run( short i, short pos )
{
	if ( !simstate.bPpower ) return;
	
	if ( pos == SW_RUN /* FJS */ )
	{
		// Restore original speed set by syscfg as user may be going from
		// slow-stepping to RUN or STOP
		SYS_Set8080Speed(simstate.orig_up_speed);
		simstate.bPrun = RUN_ON;
		simstate.runstate = RUNNING;
	}
	else if ( pos == SW_STOP /* FJS */ )
	{
		simstate.bPrun = RUN_OFF;
		simstate.runstate = HALTED;
		SYS_Set8080Speed(simstate.orig_up_speed);
	}

	SetLEDs( SETLED_UPD );
}


/*----------------------------------------------------------------------
 *	Step mode switch toggle handler
 *		UP=single-step; DOWN=slow-step
 */
static void ts_run2( short i, short pos )
{
	
	// Exit handler if power is off or simulator is not halted
	if ( !simstate.bPpower || (simstate.runstate != HALTED) )
		return;

	if ( SW_STEP )
	{
		SYS_Set8080Speed(simstate.orig_up_speed);	// restore CPU speed
		simstate.runstate  = STEP;		// prevents debug window from appearing
		Sys_DbgNotify(STEP_KEY, 1);	// results in a call to simstepover from sched.
	}
	else if ( pos == SW_DOWN ) {		// Imsai always neutral here
		SYS_Set8080Speed(MHZ_0_50);	// set speed for slow motion
		simstate.bPrun = RUN_ON;
		simstate.runstate = RUNNING;
	}
}


/*--  SetLEDs  -----------------------------------------------
	Update the status of the LEDs on the front panel by walking
	through LedList[] and blitting the proper icon to the
	front panel graphic depending on the LED state (ON or OFF).

	If the physical front panel is enabled, this routine will
	update the LEDs there, too.

	Param (mode) defines action:
		SETLED_UPD:	update LEDs based on source_byte
		SETLED_CLR:	all LEDs off
		SETLED_SET:	all LEDs on

-------------------------------------------------------------*/
void SetLEDs( short mode )
{
	register i;

	SelectObject( hMemoryDC, hLED_on );
	SelectObject( hScratchDC, hLED_off );

	switch ( mode ) {
		case SETLED_UPD:
			for ( i=0; i< N_LEDS; i++ ) {
				if ( ( *(LedList[i].srcbyte) & LedList[i].srcmask ) &&
						!LedList[i].status ) {
					BitBlt(winstate.hdcWin, LedList[i].x -6, LedList[i].y -6,
						12, 12, hMemoryDC, 0, 0, SRCCOPY );
					LedList[i].status = 1;
				}
				else if ( !( *(LedList[i].srcbyte) & LedList[i].srcmask )
						 && LedList[i].status ) {
					BitBlt(winstate.hdcWin, LedList[i].x -6, LedList[i].y -6,
						12, 12, hScratchDC, 0, 0, SRCCOPY );
					LedList[i].status = 0;
				}
			}
			bLampTest = FALSE;
//			Front panel LED updating handled by thread in fpdll
			break;

		case SETLED_CLR:
			for ( i=0; i< N_LEDS; i++ ) {
					BitBlt(winstate.hdcWin, LedList[i].x -6, LedList[i].y -6,
						12, 12, hScratchDC, 0, 0, SRCCOPY );
					LedList[i].status = 0;
			}

			bLampTest = FALSE;
			FP_AllLampsOff(simstate.hFrontPanel);	// force all off
			break;

		case SETLED_SET:
			for ( i=0; i< N_LEDS; i++ ) {
					BitBlt(winstate.hdcWin, LedList[i].x -6, LedList[i].y -6,
						12, 12, hMemoryDC, 0, 0, SRCCOPY );
					LedList[i].status = 1;
			}

			//FP_LampTest(simstate.hFrontPanel);		// force all on
			bLampTest = TRUE;
			break;

		// default:	// SETLED_REFRESH
			// break;
	}
}


/*--  DoSwitch  ----------------------------------------------
	Called by HitTest and WM_LBUTTONUP to handle switch action
	and bitblt the proper switch icon (using DrawIcon) based
	on the action being performed.

	Params:
		(i):	switch number mapped from (x,y)
		(pos):	switch position (UP, DOWN, NEUTRAL)

-------------------------------------------------------------*/
void DoSwitch( short i, short pos )
{

	//char buf[20];
	DWORD fpstat;

	SelectObject( hMemoryDC, hAltairPanel );
	SelectObject( hScratchDC, hSwitchBuf );

	BitBlt( hScratchDC, 0, 0, 32, 32, hMemoryDC,
		TogSwitches[i].x - 15, TogSwitches[i].y - 15, SRCCOPY );
	
	switch( pos ) {
		case SW_UP:
			DrawIcon( hScratchDC, 0, 0,
				(!TogSwitches[i].color)?hTogUp:hTogUp2 );
			break;
		case SW_DOWN:
			DrawIcon( hScratchDC, 0, 0,
				(!TogSwitches[i].color)?hTogDown:hTogDown2  );
			break;
		case SW_NEUT:
			DrawIcon( hScratchDC, 0, 0,
				(!TogSwitches[i].color)?hTogNeut:hTogNeut2 );
			// break;
	}

	// Play a switch "click" sound if enabled and FP connected
	fpstat = FP_GetFrontPanelStatus(simstate.hFrontPanel);
	if ((fpstat != FP_CONN_ACTIVE) | ((fpstat == FP_CONN_ACTIVE) && bFPsound)){
		PlaySound("Click", winstate.hInst, SND_RESOURCE | SND_ASYNC | SND_NOWAIT);
	}

	#define	TRIM 5

	BitBlt( winstate.hdcWin, TogSwitches[i].x - 15 + TRIM, TogSwitches[i].y - 15,
		32 - 2 * TRIM, 32, hScratchDC, TRIM, 0, SRCCOPY );

	//wsprintf(buf,"Switch %d", i);
	//Status_SetText(hwndStatusBar, STATBAR_READY, 0, buf);
	TogSwitches[i].action( i, pos );		// call handler
}


/*--  UpdSwitches  -------------------------------------------
	Called by HitTest to re-draw the switch icons (using DrawIcon)
	based on the then current positions in the TogSwitches[]
	array.

	Params:	{none}

-------------------------------------------------------------*/
void UpdSwitches ( void )
{
	register i;


	// draw toggle switches
	for ( i=0; i< N_TOGSW; i++ ) {
		switch ( TogSwitches[i].pos ) {
			case SW_UP:
				DrawIcon( winstate.hdcWin, TogSwitches[i].x - 15, TogSwitches[i].y - 15,
					(!TogSwitches[i].color)?hTogUp:hTogUp2 );
				break;

			case SW_DOWN:
				DrawIcon( winstate.hdcWin, TogSwitches[i].x - 15, TogSwitches[i].y - 15,
					(!TogSwitches[i].color)?hTogDown:hTogDown2 );
				break;

			case SW_NEUT:
				DrawIcon( winstate.hdcWin, TogSwitches[i].x - 15, TogSwitches[i].y - 15,
					(!TogSwitches[i].color)?hTogNeut:hTogNeut2 );
				// break;
		}
	}
}


/*--  HitTest  -----------------------------------------------
	Called by WM_LBUTTONDOWN to map the mouse coordinates
	to a switch number later used by DoSwitch to actually
	perform the switch action.

	Params:
		(lParam):	mouse coordinates (y,x)

-------------------------------------------------------------*/
short HitTest( LONG lParam )
{

	register short i;
	register x = LOWORD( lParam );
	register y = HIWORD( lParam );
	

	for ( i=0; i< N_TOGSW; i++ ) {
		if ( ABS( x-TogSwitches[i].x ) < 10 && ABS(y-TogSwitches[i].y) < 24 ) {
			switch ( TogSwitches[i].type ) {
				
				case SWTYPE_1:
					if ( y < TogSwitches[i].y )
						TogSwitches[i].pos = SW_UP;
					else
						TogSwitches[i].pos = SW_DOWN;
					Togswitchdown = i;
					break;
				
				case SWTYPE_3:
					TogSwitches[i].pos = ++TogSwitches[i].pos % 3;
					break;
				
				case SWTYPE_2:
					TogSwitches[i].pos = ++TogSwitches[i].pos % 2;
					// break;
			}

			DoSwitch ( i, TogSwitches[i].pos );
			return ( i );
		} // end if

	}
	return (-1);
}


/*--  Sys_DbgNotify  -----------------------------------------
	When the user interacts with the integrated debugger, the
	major stage changes come through this code.

	Params:
		(event):	event ID
		(param):	param related to event ID (if required)

-------------------------------------------------------------*/
void Sys_DbgNotify(sysnotify_t event, int param)
{
	
    switch (event) {
		case DEBUG_KEY:
		    // halt no matter what and pop open debugger
		    simstate.bPrun = RUN_OFF;
		    simstate.runstate = HALTED;
			simstate.inactivate_timer = 0;
		    UI_DbgWin(DW_Activate, 0);
		    report_curstate();
		    break;
		    
		case RUN_KEY:
		    // run if we're halted, halt if we're running
			if (simstate.runstate == RUNNING){
				simstate.runstate = HALTED;
				simstate.bPrun = RUN_OFF;
				simstate.inactivate_timer = 0;
				UI_DbgWin(DW_Activate, 0);
				report_curstate();
		    } else {
				simstate.bPrun = RUN_ON;
				simstate.runstate = RUNNING;
				startrun();	// establish normal/debugging fcn ptrs
				simstate.firststep = 1;
				simstate.inactivate_timer = 3;
		    }
		    break;
		    
		case FORCERUN_KEY:
		    // run whether we are currently running or not
			// FIXME -- called by close_debugwin
			simstate.bPrun = RUN_ON;
			simstate.runstate = RUNNING;
			startrun();	// establish normal/debugging fcn ptrs
			simstate.firststep = 1;
			simstate.inactivate_timer = 3;
		    break;
		    
		case STEP_KEY:
		    // step over {param} instructions
			if (simstate.runstate == RUNNING)
				UI_DbgWin(DW_Activate, 0);
		    simstate.runstate  = STEP;
		    simstate.bPrun = RUN_ON;	    
		    simstate.stepcount = param;
			simstate.firststep = 1;
			startrun();	// establish normal/debugging fcn ptrs
			simstate.inactivate_timer = 3;
			break;
			
		case STEPI_KEY:
			// step into {param} instructions
			if (simstate.runstate == RUNNING)
		    	UI_DbgWin(DW_Activate, 0);
		    simstate.runstate  = STEPI;
	   	    simstate.bPrun = RUN_ON;
		    simstate.stepcount = param;
			simstate.firststep = 1;
			simstate.inactivate_timer = 3;
		    startrun();	// establish normal/debugging fcn ptrs
		    break;
		    
		case CLOSE_WIN:
			simstate.runstate = RUNNING;
		    simstate.bPrun = RUN_ON;
		    UI_DbgWin(DW_Close, 0);
		    break;
		    
		default:
		    ASSERT(0); // force error!
    }
}


void bkptchange_coresim(void)
{
    startrun();
}


/*--  startrun  -----------------------------------------------
	This routine sets up breakpoint-related stuff just before
	resuming execution.
	
-------------------------------------------------------------*/
void startrun(void)
{
    // generate a bitmap of any active breakpoints
    breakpoint_genmap();

    // we select the fast function if there is no breakpoint
    // of that type, otherwise we pick the slow one that tests
    // for breakpoints on each access.

    if ((simstate.runstate == STEP) // in case we set tmp breakpoint
			|| any_breakpoints(BRK_TEMP)
			|| any_breakpoints(BRK_OP)
			|| any_breakpoints(BRK_RD)
			|| any_breakpoints(BRK_WR)
			|| any_breakpoints(BRK_RD16)
			|| any_breakpoints(BRK_WR16)
			|| any_breakpoints(BRK_IN)
			|| any_breakpoints(BRK_OUT)
			)
		simstate.exec8080_func = clock8080_brkpt;
    else
		simstate.exec8080_func = clock8080;

    if (any_breakpoints(BRK_OP) || any_breakpoints(BRK_TEMP))
		simstate.op80_func = GetMem;	// OpZ80_brkpt; not used
    else
		simstate.op80_func = GetMem;

    if (any_breakpoints(BRK_RD) || any_breakpoints(BRK_RD16))
		simstate.rd80_func = GetMem_brkpt;
    else
		simstate.rd80_func = GetMem;

    if (any_breakpoints(BRK_WR) || any_breakpoints(BRK_WR16))
		simstate.wr80_func = SetMem_brkpt;
    else
		simstate.wr80_func = SetMem;

    if (any_breakpoints(BRK_IN))
		simstate.in80_func = GetIO_brkpt;
    else
		simstate.in80_func = GetIO;

    if (any_breakpoints(BRK_OUT))
		simstate.out80_func = SetIO_brkpt;
    else
		simstate.out80_func = SetIO;

    // reset debugging state
    I80Regs.brkpt = 0;
}


/*--  SysKeyPress  --------------------------------------------
	Winmain calls this when keyboard input is received/
	If we receive two notifications before the first one
	has been processed, the older one is lost.  The kb
	strobe nominally lasts 6 uS, so that if the key is read
	during that window, it will register more than once.
	Called by winmain_kb.c during VK_ processing.
	SOL_COMPAT
		
-------------------------------------------------------------*/
void SysKeyPress(byte data)
{
    /*
  if (simstate.kb_script) {
	// a script is running.  we ignore all input except ESC,
	// which will cancel the script.
	if (data == 0x1B) {
	    script_close(simstate.kb_sh);
	    simstate.kb_script =  0;
	    simstate.kb_sh     = -1;
	    if (simstate.kb_supptimer >= 0) {
		TimerKill(simstate.kb_supptimer);
		simstate.kb_suppress  =  0;
		simstate.kb_supptimer = -1;
	    }
	}
	return;
  }

    // normal processing
    sysstate.kb_stat  = 1;
    if (simstate.hkb_stat < 0)
	simstate.hkb_stat = TimerCreate(TIMER_US(6), handle_kb_stat, 0, 0);
    sysstate.kb_key = data;
	*/;
}


/***************************************************************************\
*
*	FUNCTION: LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM)
*
*	PURPOSE:  Processes messages for main window
*
*	COMMENTS:
*
*
\***************************************************************************/
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		// WM_POWERBROADCAST message is a suspend mode notification
		HANDLE_MSG (hwnd, WM_CHAR,			_OnChar);
		HANDLE_MSG (hwnd, WM_CLOSE, 		_OnClose);
		HANDLE_MSG (hwnd, WM_COMMAND, 		_OnCommand);
		HANDLE_MSG (hwnd, WM_CREATE, 		_OnCreate);
		HANDLE_MSG (hwnd, WM_DESTROY, 		_OnDestroy);
		HANDLE_MSG (hwnd, WM_INITMENU, 		_OnInitMenu);
		HANDLE_MSG (hwnd, WM_KEYDOWN, 		_OnKeyDown);
		HANDLE_MSG (hwnd, WM_LBUTTONDOWN,	_OnLButtonDown);
		HANDLE_MSG (hwnd, WM_LBUTTONUP, 	_OnLButtonUp);
		HANDLE_MSG (hwnd, WM_MENUSELECT, 	_OnMenuSelect);
		HANDLE_MSG (hwnd, WM_PAINT, 		_OnPaint);
		HANDLE_MSG (hwnd, WM_QUIT, 			_OnQuit);
		HANDLE_MSG (hwnd, WM_SETFOCUS, 		_OnSetFocus);

	default: return DefWindowProc (hwnd, msg, wParam, lParam);
	}
	return FALSE;
}


/***************************************************************************\
*
*	FUNCTION: LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM)
*
*	PURPOSE:  Processes messages for "About" dialog box
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
*	No initialization is needed for this particular dialog box, but TRUE
*	must be returned to Windows.
*
*	Wait for user to click on "Ok" button, then close the dialog box.
*
\***************************************************************************/
LRESULT CALLBACK About (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message) {
		case WM_INITDIALOG:
			SetDlgItemText(hDlg, IDC_BUILDNUMBER, szBuildNumber);
		return TRUE;

		case WM_COMMAND:
		    if (wParam == IDOK || wParam == IDCANCEL) {
				EndDialog(hDlg, TRUE);
				return TRUE;
	    	}
		// break;
		// default: /* nil */ break;
    }
    return FALSE;			      // Didn't process a message
}


/**************************************************************\
**  WndProc Routines  *****************************************|
\**************************************************************/
//
//  Process WM_CHAR message for window/dialog
//
void _OnChar(HWND hwnd, TCHAR ch, int cRepeat)
{
	/* The WM_CHAR message is posted to the window with the
	   keyboard focus when a WM_KEYDOWN message is translated by
	   the TranslateMessage function. The WM_CHAR message contains
	   the character code of the key that was pressed.

	   Useful routines:
	   MapVirtualKey: translates (maps) a VK code to a scan code
	    or character value,	or translates a scan code into a VK code.

	   ToAscii: translates a VK code and keyboard state to corresponding
	    character or characters. The function translates the code using
	    the input language and physical keyboard layout identified by
	    the keyboard layout handle.
	*/

}


//
//  Process WM_CLOSE message for window/dialog
//
static void _OnClose(HWND hwnd)
{
	simstate.bPpower = 0;				// main power
	simstate.runstate = HALTED;			// halt scheduler
	simstate.bPrun = RUN_OFF;			// 'WAIT' FP LED register
	SetLEDs(SETLED_CLR);				// clear the LEDs before deleting DCs (should do an FP_)
	SelectObject(hMemoryDC, hSav2);		// destroy various GDI objects
	DeleteDC(hMemoryDC);				// create--delete
	SelectObject(hScratchDC, hSav1);
	DeleteDC(hScratchDC);
	DeleteObject(hSwitchBuf);
	DeleteObject(hLED_on);
	DeleteObject(hLED_off);
	DeleteObject(hAltairPanel);
	ReleaseDC(hwnd, winstate.hdcWin);	// get--release
	winstate.hdcWin = (HDC)NULL;				// probably not needed
	// release graphic UI related stuff versus system stuff which is
	// released in init.c
	PTP_Shutdown();				// PTP device
	LPR_Destroy(1);				// printer
	CASS_Destroy(1);			// cassette device-destroys DCs
	HDISK_IoCtl(1);				// hard disk subsystem
	DISK_Shutdown();			// floppy disk subsystem
	simstate.conshtdn_func();	// console
	DestroyDebugger();			// release debugger resources
	SYSCFG_SaveOptions();		// save emulator options (turns on LEDs for some reason)
	FP_AllLampsOff(simstate.hFrontPanel);		// clear front panel LEDs last
	//HOST_ThrowErrMsg("What are you doing Dave...\n");
	DestroyWindow(hwnd);		// destroy main window
}


//
//  Process WM_COMMAND message for window/dialog
//
static void _OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{							//loword(wParam), lparam, hiword(wParam)

	HWND	hHTMLHelpWin;		// handle to HTML help window


	Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Ready");
	switch(id){
	//------ File menu ------
		case IDM_READIMAGE:
			simstate.bPrun = RUN_OFF;
			simstate.runstate = HALTED;
			DialogBox(winstate.hInst, "LoadDlg", hwnd, (DLGPROC)lpAskLoadAddr);
			MBR = Rd80(I80Regs.nPC.W);		// update data bus buffer
			SetLEDs( SETLED_UPD );
			break;

		case IDM_SAVEIMAGE:
			simstate.bPrun = RUN_OFF;
			simstate.runstate = HALTED;
			DialogBox(winstate.hInst, "SaveDlg", hwnd, (DLGPROC)lpAskSaveAddr);
			MBR = Rd80(I80Regs.nPC.W);		// update data bus buffer
			SetLEDs( SETLED_UPD );
			break;

		case IDM_EXIT1:
			PostMessage(hwnd, WM_CLOSE, 0, 0L);	// EXIT
			break;

	// ------ Devices menu ------
		case IDM_FILE_DSKMGT:	OpenDiskWin() ; break;
		case IDM_HDISK:			HDISK_OpenContainerFile(); break;
		case IDM_CASSCTRL:		CASS_OpenWin(); break;
		case IDM_PTPCTRL:		PTP_OpenWin() ; break;
		case IDM_LPRCTRL:		LPR_OpenWin() ; break;
		case IDM_VIDEO:			DAZZLER_VideoWin(VWM_Activate, 0); break;

	// ------ Tools menu ------
		case IDM_OPTIONS:	SYSCFG_OpenOPTS(); break;
		case IDM_DEBUGGER:	Sys_DbgNotify(DEBUG_KEY, 0); break;
		case IDM_COREDUMP:	CoreDump();	break;

	// ------ Help menu ------
		case IDM_HELPTOPICS:
			hHTMLHelpWin = HtmlHelp(GetDesktopWindow(), szHelpDir, HH_DISPLAY_TOC, 0);
			break;

		case IDM_ABOUT1:
			DialogBox(winstate.hInst, "AboutBox", hwnd, (DLGPROC)About);
			break;
	}	// end of switch
}


//
//  Process WM_CREATE message for window/dialog
//
static BOOL _OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
	hAltairPanel = LoadBitmap(winstate.hInst, PANEL_NAME);
	if ( !hAltairPanel ) {
		PostQuitMessage(0);
		return TRUE;
	}

	hWinHelpCursor = LoadCursor(winstate.hInst, IDC_HELP);
	winstate.hdcWin = GetDC(hwnd);	// hDC to front panel
			
#ifdef IMSAI
	hLED_on = LoadBitmap(winstate.hInst, "iled_on");
	hLED_off = LoadBitmap(winstate.hInst, "iled_off");
	hTogUp = LoadIcon(winstate.hInst, "itogup");		//red
	hTogDown = LoadIcon(winstate.hInst, "itogdown");
	hTogNeut = LoadIcon(winstate.hInst, "itogneut");
	hTogUp2 = LoadIcon(winstate.hInst, "btogup");		//blue
	hTogDown2 = LoadIcon(winstate.hInst, "btogdown");
	hTogNeut2 = LoadIcon(winstate.hInst, "btogneut");
#else
	hLED_on = LoadBitmap(winstate.hInst, "led_on");
	hLED_off = LoadBitmap(winstate.hInst, "led_off");
	hTogUp = LoadIcon(winstate.hInst, "togup");
	hTogDown = LoadIcon(winstate.hInst, "togdown");
	hTogNeut = LoadIcon(winstate.hInst, "togneut");
	hTogUp2 = LoadIcon(winstate.hInst, "togup");
	hTogDown2 = LoadIcon(winstate.hInst, "togdown");
	hTogNeut2 = LoadIcon(winstate.hInst, "togneut");
#endif
	hMemoryDC = CreateCompatibleDC(winstate.hdcWin);
	hScratchDC = CreateCompatibleDC(winstate.hdcWin);
	hSwitchBuf = CreateCompatibleBitmap(winstate.hdcWin, 32, 32);
	hSav2 = SelectObject(hMemoryDC, hAltairPanel);
	hSav1 = SelectObject(hScratchDC, hLED_on);
			
	hwndStatusBar = InitStatusBar(hwnd);	// Create status bar
	return TRUE;
}


//
//  Process WM_DESTROY message for window/dialog
//
static void _OnDestroy(HWND hwnd)
{
	PostQuitMessage(0);		// post final quit message
}


//
//  Process WM_INITMENU message for window/dialog
//
static void _OnInitMenu(HWND hwnd, HMENU hMenu)
{
	if ( simstate.bPpower ) {
		// Items to enable if power is on. System configuration options
		// only allowed to be changed when power is OFF.
		if (simstate.runstate == HALTED){	// enable only if HALTED
			EnableMenuItem(hMenu, IDM_SAVEIMAGE,   MF_ENABLED);
			EnableMenuItem(hMenu, IDM_READIMAGE,   MF_ENABLED);
		} else {
			EnableMenuItem(hMenu, IDM_SAVEIMAGE,   MF_GRAYED);
			EnableMenuItem(hMenu, IDM_READIMAGE,   MF_GRAYED);
		}
		EnableMenuItem(hMenu, IDM_FILE_DSKMGT,  MF_ENABLED);
		EnableMenuItem(hMenu, IDM_HDISK,		MF_ENABLED);
		EnableMenuItem(hMenu, IDM_CASSCTRL,		MF_ENABLED);
		EnableMenuItem(hMenu, IDM_PTPCTRL,	 	MF_ENABLED);
		EnableMenuItem(hMenu, IDM_LPRCTRL,	 	MF_ENABLED);
		EnableMenuItem(hMenu, IDM_VIDEO,		MF_ENABLED);
		EnableMenuItem(hMenu, IDM_OPTIONS,	 	MF_GRAYED);
		EnableMenuItem(hMenu, IDM_DEBUGGER,		MF_ENABLED);
		EnableMenuItem(hMenu, IDM_COREDUMP, 	MF_ENABLED);
		EnableMenuItem(hMenu, IDM_DISPREGS,		MF_ENABLED);
		//	CheckMenuItem(hMenu, IDM_FPLIGHTS, 
		//	(iFPlights == TRUE) ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
			}
	else {			// must be off...
		EnableMenuItem(hMenu, IDM_SAVEIMAGE,   MF_GRAYED);
		EnableMenuItem(hMenu, IDM_READIMAGE,   MF_GRAYED);
		EnableMenuItem(hMenu, IDM_FILE_DSKMGT, MF_GRAYED);
		EnableMenuItem(hMenu, IDM_HDISK,	   MF_GRAYED);
		EnableMenuItem(hMenu, IDM_CASSCTRL,	   MF_GRAYED);
		EnableMenuItem(hMenu, IDM_PTPCTRL,     MF_GRAYED);
		EnableMenuItem(hMenu, IDM_LPRCTRL,     MF_GRAYED);
		EnableMenuItem(hMenu, IDM_VIDEO,	   MF_GRAYED);
		EnableMenuItem(hMenu, IDM_OPTIONS,	   MF_ENABLED);
		EnableMenuItem(hMenu, IDM_DEBUGGER,    MF_GRAYED);
		EnableMenuItem(hMenu, IDM_COREDUMP,    MF_GRAYED);
		EnableMenuItem(hMenu, IDM_DISPREGS,    MF_GRAYED);
	}
}


//
//  Process WM_KEYDOWN message for window/dialog
//
static void _OnKeyDown(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
{

	// fDown always appears to be TRUE
	
	HWND hHTMLHelpWin;
	LPARAM lParam;
	
	
	switch (vk){
		case VK_F1:				// HELP
			hHTMLHelpWin = HtmlHelp(GetDesktopWindow(), szHelpDir, HH_DISPLAY_TOC, 0);
			break;

		case VK_F2:				// LPR toggle
			if (simstate.bPpower) LPR_OpenWin();
			break;

		case VK_F3:				// PTP toggle
			if (simstate.bPpower) CASS_OpenWin();
			break;

		case VK_F4:				// Disk management
			if (simstate.bPpower) OpenDiskWin();
			break;

		case VK_F5:				// Debugger
			lParam = MAKELPARAM((cRepeat), (flags));
			if (simstate.bPpower) {
				if (~lParam & 0x4000) {		// this is a new keystroke
					Sys_DbgNotify (DEBUG_KEY, 0);
				}
			}
			break;

		case VK_F6:				// PTP management
			if (simstate.bPpower) PTP_OpenWin();
			break;

		case VK_F7:				// Dazzler
			if (simstate.bPpower) DAZZLER_VideoWin(VWM_Activate, 0);
			break;

		case VK_F8:				// Hard disk
			if (simstate.bPpower) HDISK_OpenContainerFile();
			break;
	}
}


//
//  Process WM_LBUTTONDOWN message for window/dialog: 
//
static void _OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
	Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Ready");
	HitTest(MAKELPARAM((x), (y)));
}


//
//  Process WM_LBUTTONUP message for window/dialog: 
//
static void _OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
	if ( Togswitchdown >= 0 ) {
		DoSwitch( Togswitchdown, TogSwitches[Togswitchdown].pos = SW_NEUT );
		Togswitchdown = -1;
	}
}


//
//  Process WM_MENUSELECT message for window/dialog: 
//
static void _OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
{

	Statusbar_MenuSelect(hwnd, MAKEWPARAM((item), (flags)), (LPARAM)(HMENU)((hmenu) ? (hmenu) : (hmenuPopup)));

}


//
//  Process WM_PAINT message for window/dialog: 
//
static void _OnPaint(HWND hwnd)
{
	HDC hDC_paint;			// scratch HDC
	PAINTSTRUCT	ps;

	// Draw the background bitmap. hDC_paint is a scratch hdc
	SelectObject(hMemoryDC, hAltairPanel);
	hDC_paint = BeginPaint(hwnd, &ps);
	BitBlt(hDC_paint, 0, 0, ALTAIRBMP_W, ALTAIRBMP_H, hMemoryDC, 0, 0, SRCCOPY);
	EndPaint(hwnd, &ps);

	// Draw the LEDs
	SetLEDs( SETLED_CLR );	// if power is off, draw "OFF" LEDs
	if ( simstate.bPpower ) SetLEDs( SETLED_UPD );

	//hwndStatusBar = RebuildStatusBar (hWnd, 0);
	UpdSwitches();		// uses global hDC

}


//
//  Process WM_QUIT message for window/dialog: 
//
static void _OnQuit(HWND hwnd, int exitCode)
{
	// TODO: Add message processing code here...
}


//
//  Process WM_SETFOCUS message for window/dialog: 
//
static void _OnSetFocus(HWND hwnd, HWND hwndOldFocus)
{
	UpdateWindow(hwnd);	// force a WM_PAINT message
}

/* end of file: altair.c */
