/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/syscfg.c 83    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  System Configuration Dialog code

  Copyright (c) 2000-2006 Richard A. Cini
  Copyright (c) 2003 Joseph J. Forgione
  Certain portions Copyright 1996 Paul Yao (Windows 95 Programming)
  Certain portions Copyright (c) Jim Battle, 2000-2002


Change Log:
  2002/09/09  RAC -- Wrote it.
  2002/09/14  RAC -- Added support for swappable console pointers.
  2002/09/16  RAC -- Removed file name handling from MemCfg
  2002/09/19  RAC -- Reworked whole configuration to 1 RAM and 1
  						ROM region.
  2002/09/22  RAC -- Added setting of con_typ in LoadOptions; clicking
						OK now closes dialog.
  2002/09/24  RAC -- Fixed PAR array problem (not all of the selected
  						memory range was IDed properly).
  2002/09/30  RAC -- Fixed INI file handling mixed data entry (string, int).
  2002/10/18  RAC -- Added support for configurable CPU speeds.
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2002/11/19  RAC -- Changes to support configurable telnet port
  2002/12/31  RAC -- Began prototyping to support a ROM mapping function
  2003/03/06  RAC -- Merged changes from Joe Forgione
  2003/03/16  JJF -- New Unified Options Dialog
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2003/05/01  RAC -- Modifications for updated CPU speeds.
  2003/11/14  RAC -- Moved WriteConfigInfo to public
  2004/01/06  RAC -- Added configuration tab for "Printer"
  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)
  2004/06/26  RAC -- Removed printer configuration dialog.
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135  
  2004/08/20  RAC -- Added code to trap ESC keys in options dialog
  2004/09/09  RAC -- Moved console assignment to LoadOptions from altair.c
  2004/10/09  FJS -- Added unused console channel options for SIO
  2006/03/16  RAC -- Added LED brightness slider to misc. options as FPv.2 has
						an LED intensity control.
  2006/04/06  RAC -- Added separate tab for front panel
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>	// required for all Windows applications
#include <windowsx.h>	// Windows message crackers
#include <commdlg.h>	// Windows common dialogs
#include <commctrl.h>	// Windows common controls
#include <stdlib.h>		// C standard lib
#include <string.h>		// C-lib - strings
#include <stdio.h>		// C-lib - I/O 
#include <tchar.h>
#include "altair32.h"	// includes "resource.h"
//#include "extensions.h" // corrections for bad functions in windowsx
#include "comcthlp.h"	// common control macros
#include "fpdll.h"


/**  Declarations  ********************************************/
#ifdef IMSAI
	const char *g_ini_file = "\\imsai32.ini";
#else
	const char *g_ini_file = "\\altair32.ini";
#endif

#define FIRST_ROM	1		// start at slot 1

BOOL bFPsound = FALSE;		// click sound when FP connected
char szProfileBuf[80];		// general profile buffer
HWND hwndOPTS = NULL;		// handle to options container window
HWND hwndConCfg = NULL;
HWND hwndMemCfg = NULL;
HWND hwndSpeedCfg = NULL;
HWND hwndFPCfg = NULL;
HWND hwndMiscCfg = NULL;
int	 LptCharSet = 0;		// current LPT character set (0=ASCII, 1=MITS)
word ramstart = 0;			// start of RAM
word ramend = 0xefff;		// end of RAM
word romstart = 0xf000;	// start of ROM
word romend = 0xffff;		// end of ROM
LOADROM load_rom[MAXROMS];
// HWND hwndPrinterCfg = NULL;
// HWND hwndRomCfg = NULL;

// Private
static int Do_shutdown = 0; 	// flag for configuration child window Cancel actions
static BOOL Text_Bright = 0; 
static char Tcolor[8] , Bcolor[8];
static char szFileDir[_MAX_PATH];		// to retain selected directory in OFNstatic
char Color_Selections[8][8]={"Black","Blue","Green","Cyan","Red","Magenta","Yellow","White"};
static LOADROM T_load_rom[MAXROMS];
static int last_editbox = 0;
static int iConTypID[] = {IDC_RADIO_TELNET, IDC_RADIO_CONS, IDC_RADIO_SER};
static int con_typ = IDC_RADIO_CONS;
static int cpu_speed = IDC_RADIO_M204A;
static int cpu_type = IDC_RADIO_I8080;
static int iLEDlevel = 8;		// default to 50% brightness 16-steps
// int  lpt_charset = IDC_RADIO_ASCII7;	// default to 7-bit ASCII


/**  Forward Prototypes  **************************************/
//static BOOL GenerateRomUsageMap( HWND );
static HWND SYSCFG_OpenConsoleWin( HWND );
static HWND SYSCFG_OpenMemoryWin( HWND );
static HWND SYSCFG_OpenSpeedWin( HWND );
static HWND SYSCFG_OpenMiscWin( HWND );
static HWND SYSCFG_OpenFPWin( HWND );
static BOOL CALLBACK lpWndProcOPT( HWND, UINT, WPARAM, LPARAM );	// options container
static BOOL CALLBACK lpWndProcCCfg( HWND, UINT, WPARAM, LPARAM );	// console
static BOOL CALLBACK lpWndProcMCfg( HWND, UINT, WPARAM, LPARAM );	// memory
static BOOL CALLBACK lpWndProcSCfg( HWND, UINT, WPARAM, LPARAM );	// speed/cpu
static BOOL CALLBACK lpWndProcFPCfg( HWND, UINT, WPARAM, LPARAM );	// front panel
static BOOL CALLBACK lpWndProcMiscCfg( HWND, UINT, WPARAM, LPARAM );	// misc

// helper routines
static int Xtoi( char*, short int );
int StrToNum( TCHAR *, int , int );

#if 0
static BOOL CALLBACK lpWndProcLptCfg( HWND, UINT, WPARAM, LPARAM );
BOOL CALLBACK lpWndProcRomList( HWND, UINT, WPARAM, LPARAM );
BOOL CALLBACK lpWndProcRomInfo( HWND, UINT, WPARAM, LPARAM );
#endif


/**************************************************************\
|** PUBLIC ROUTINES  ******************************************|
\**************************************************************/
/*--  SYSCFG_OpenOPTS  ----------------------------------------
	Called by UI code to create and show a modeless dialog as
	a container to hold the various option dialogs.
-------------------------------------------------------------*/
void SYSCFG_OpenOPTS(void)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndOPTS)) {
		hwndOPTS = CreateDialog(winstate.hInst,
								"IDD_OPTIONS",
								winstate.hWnd,
								(DLGPROC)lpWndProcOPT);
		ShowWindow(hwndOPTS, SW_SHOW);
	}
}


/*--  SYSCFG_OpenConsoleWin  ----------------------------------
	Called by container dialog code to create and show a
	the related borderless configuration window on the right
	side of the container. This routine specifically handles
	the console configuration.
-------------------------------------------------------------*/
static HWND SYSCFG_OpenConsoleWin(HWND win_handle)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndConCfg)) {
		hwndConCfg = CreateDialog(winstate.hInst,
								   "ConTab",
								   win_handle,
								   (DLGPROC)lpWndProcCCfg);
		ShowWindow(hwndConCfg, SW_SHOW);
	}
	return(hwndConCfg);
}


/*--  SYSCFG_OpenMemoryWin  -----------------------------------
	Called by container dialog code to create and show a
	the related borderless configuration window on the right
	side of the container. This routine specifically handles
	the memory configuration.
-------------------------------------------------------------*/
static HWND SYSCFG_OpenMemoryWin(HWND window_hanle)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndMemCfg)) {
		hwndMemCfg = CreateDialog(winstate.hInst,
								   "Memconfig",	
								   window_hanle,
								   (DLGPROC)lpWndProcMCfg);
		ShowWindow(hwndMemCfg, SW_SHOW);
	}
	return(hwndMemCfg);
}


/*--  SYSCFG_OpenSpeedWin  ------------------------------------
	Called by container dialog code to create and show a
	the related borderless configuration window on the right
	side of the container. This routine specifically handles
	the emulated CPU speed and type.
-------------------------------------------------------------*/
static HWND SYSCFG_OpenSpeedWin(HWND window_handle)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndSpeedCfg)) {
		hwndSpeedCfg = CreateDialog(winstate.hInst,
								   "SpeedTab",
								   window_handle,
								   (DLGPROC)lpWndProcSCfg);
		ShowWindow(hwndSpeedCfg, SW_SHOW);
	}
	return(hwndSpeedCfg);
}


/*--  SYSCFG_OpenFPWin  ---------------------------------------
	Called by container dialog code to create and show a
	the related borderless configuration window on the right
	side of the container. This routine specifically handles
	the front panel configuration.
-------------------------------------------------------------*/
static HWND SYSCFG_OpenFPWin(HWND window_handle)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndFPCfg)) {
		hwndFPCfg = CreateDialog(winstate.hInst,
								   "FrontPanelTab",
								   window_handle,
								   (DLGPROC)lpWndProcFPCfg);
		ShowWindow(hwndFPCfg, SW_SHOW);
	}
	return(hwndFPCfg);
}


/*--  SYSCFG_OpenMiscWin  ----------------------------------------
	Called by container dialog code to create and show a
	the related borderless configuration window on the right
	side of the container. This routine specifically handles
	the miscellaneous emulator options.
-------------------------------------------------------------*/
static HWND SYSCFG_OpenMiscWin(HWND window_handle)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndMiscCfg)) {
		hwndMiscCfg = CreateDialog(winstate.hInst,
								   "IDD_MISC",
								   window_handle,
								   (DLGPROC)lpWndProcMiscCfg);
		ShowWindow(hwndMiscCfg, SW_SHOW);
	}
	return(hwndMiscCfg);
}


#if 0
/*--  SYSCFG_OpenRomWin  --------------------------------------
	Called by main UI code to open up a	modeless dialog for
	selecting the ROMs
	
-------------------------------------------------------------*/
void SYSCFG_OpenRomWin(void)
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndConCfg)) {
		hwndRomCfg = CreateDialog(winstate.hInst,
								   "RomTab",
								   winstate.hWnd,
								   (DLGPROC) lpWndProcRomList);
		ShowWindow(hwndRomCfg, SW_SHOW);
	}
}
#endif


/**************************************************************\
|** INI FILE HANDLING CODE  ***********************************|
\**************************************************************/
/*--  SYSCFG_LoadOptions  -------------------------------------
	Scans the ini file for the options and loads them.

	Params:		none

	Uses:		locals only

	Returns:	nothing
	
	Comments:	Must be called before the emulation starts since
				the memory code would rely on the population of
				the start and end variables. Defaults are set in
				calls to GetPrivateProfileString. If INI file is
				missing or key is corrupt, GPPS parameter becomes
				as default.	
-------------------------------------------------------------*/
void SYSCFG_LoadOptions(void)
{

	BOOL readonly = 0;
	char buf[80];
	char ini_file[_MAX_PATH];
	char ini_version[20];
	char *ptr;
	const char *seps = " ,\t\n";		// field delimiters
	char *f_bootrom = "88dskrom.bin";
	char t_buf[_MAX_PATH];
	char t_buf2[_MAX_PATH];
	DWORD dwGPPSret = 0;
	int visible = 0;
	int ct = 0;
	short int Temp=0;
	// int file_size = 0;
	RECT rc;
	register i;
	uint32 strt = 0;
	uint32 w_end = 0;

	/* Default "other" settings
	 *  Bell = TRUE;
	 *  WordWrap = FALSE;
	 *	Ansi = TRUE;
	 *	Disksnd = TRUE;
	 *	Fansnd = FALSE;
	 *	bFPsound = FALSE;
	 *
	 * Default front panel settings
	 *  iLEDlevel = 8;
	 *  S_fpport0
	 *  S_fpport1
	 *
	 * Default console-related settings
	 *	baudrate=9600;
	 *	strcpy(S_port,"COM1");
	 *	simstate.usTelnetPort = 23;
	 *
	 * Default printer settings
	 *	LptCharSet = 0;	// ASCII7
	 */
	 
	// set INI file path to location of executable
	strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);

	// 2006/05/06 - moved load_rom array clearing to CpuPOR

   	// ----- Process INI file -----
	// Get ini file format version number
    GetPrivateProfileString("fileformat", "version", "3.0", ini_version,
			sizeof(ini_version), ini_file);
#if 0
	// Load default disk ROM and populate slot 0
	strcpy(t_buf, winstate.szFilesDir);
	strcat(t_buf, f_bootrom);
	ReadBinaryFile(winstate.hWnd, (PSTR)t_buf, (PSTR)f_bootrom, 0xff00);

	// Disk ROM is stuffed into Mem[] in 88disk.c. So, fill in slot 0
	// to keep things straight.
	strcpy(load_rom[0].szROMName, f_bootrom);
	load_rom[0].iROMStart = 0xff00;
	load_rom[0].iROMSize = 0x0100;		// end address is start+length-1
	load_rom[0].iUsed = 1;
	load_rom[0].Read_only = 1;
#endif
#if 0
	// Load printer options
    GetPrivateProfileString("printer", "charset", "ASCII7", buf, sizeof(buf), ini_file);
	if (!strcmp(buf, "MITS6")){
		LptCharSet = 1;
		lpt_charset = IDC_RADIO_MITS6;
	}
    else if (!strcmp(buf, "ASCII7")){
		LptCharSet = 0;
		lpt_charset = IDC_RADIO_ASCII7;
	}
	else {
		LptCharSet = 0;
		lpt_charset = IDC_RADIO_ASCII7;
	}
#endif

	// Load CPU type 
    GetPrivateProfileString("cpu", "type", "I8080", buf, sizeof(buf), ini_file);
	if (!strcmp(buf, "Z80Cy")) {
		SYS_SetCPUType(simstate.up_type = CPU_Z80_Cycle);
		cpu_type = IDC_RADIO_Z80_CYC;
		Status_SetText(hwndStatusBar, STATBAR_CPU, 0, "Z80Cy");
	}
    else if (!strcmp(buf, "Z80")) {
		SYS_SetCPUType(simstate.up_type = CPU_Z80); 
		cpu_type = IDC_RADIO_Z80;
		Status_SetText(hwndStatusBar, STATBAR_CPU, 0, "Z80");
	}
    else if (!strcmp(buf, "8080Cy")) {
		SYS_SetCPUType(simstate.up_type = CPU_8080_Cycle);
		cpu_type = IDC_RADIO_I8080;
		Status_SetText(hwndStatusBar, STATBAR_CPU, 0, "8080Cy");
	}
	else { // default
		SYS_SetCPUType(simstate.up_type = CPU_8080); 
		cpu_type = IDC_RADIO_I8080;
		Status_SetText(hwndStatusBar, STATBAR_CPU, 0, "8080");
	}

	// Load CPU speed
    GetPrivateProfileString("cpu", "speed", "2.04MHz", buf, sizeof(buf), ini_file);
	if (!strcmp(buf, "999MHz")){
		SYS_Set8080Speed(simstate.orig_up_speed = MHZ_unregulated);
		cpu_speed = IDC_RADIO_UNREG;
	}
    else if (!strcmp(buf, "4.09MHz")){
		SYS_Set8080Speed(simstate.orig_up_speed = MHZ_4_09);
		cpu_speed = IDC_RADIO_M409A;
	}
    else if (!strcmp(buf, "1.02MHz")){
		SYS_Set8080Speed(simstate.orig_up_speed = MHZ_1_02);
		cpu_speed = IDC_RADIO_M102A;
	}
    else { // "2.04MHz" default FJS
		SYS_Set8080Speed(simstate.orig_up_speed = MHZ_2_04);
		cpu_speed = IDC_RADIO_M204A;
	}

    // Restore debugger window size and placement
    GetPrivateProfileString("windows", "debug", "", buf, sizeof(buf), ini_file);
    if (sscanf(buf, "org=%d,%d, size=%d,%d", &rc.left, &rc.top,	&rc.bottom,
      &rc.right) == 5){
		rc.right  += rc.left;
		rc.bottom += rc.top;
		DebuggerWindowSetSize(&rc);
    }

	// ------ Get memory configuration ------
    // RAM
	GetPrivateProfileString("memcfg", "RAM", "0x0000,0xEFFF", buf, sizeof(buf), ini_file);
	if (sscanf(buf, "0x%04X,0x%04X", &strt, &w_end) == 2) {
		ramstart = strt;
		ramend = w_end;
	}

	// ROM
	GetPrivateProfileString("memcfg", "ROM", "0xF000,0xFFFF", buf, sizeof(buf), ini_file);
	if (sscanf(buf, "0x%04X,0x%04X", &strt, &w_end) == 2) {
		romstart = strt;
		romend = w_end;
	}

	// Update PageAccessRights based on INI configuration
	// memset(PageAccessRights,0 sizeof(PageAccessRights));
	for (i=ramstart; i<=ramend; i++)
		PageAccessRights[i] = 0;
	
	for (i=romstart; i<=romend; i++)
		PageAccessRights[i] = 1;
	
	// Load native ROMs
	for (i=FIRST_ROM; i<MAXROMS; i++) {
		char buf2[10];
		t_buf[0] = 0;

		sprintf(buf2, "ROM%d", i);
		GetPrivateProfileString("ROMS", buf2, "x", buf, sizeof(buf), ini_file);
		if (!strcmp(buf, "x")) continue;

		// Parse buf to extract file name
		ptr = strtok(buf, seps);
		sprintf(t_buf, "%s", ptr);

		// Retrieve start address (in hex)		
		ptr = strtok(NULL, seps);
		strt = hextoi(ptr);
		ptr = strtok(NULL, seps);
		readonly = 1; // Default to read only
		if (ptr != NULL) readonly = atoi(ptr);

		// Make fully-qualified path to file and go get it
		strcpy(t_buf2, winstate.szFilesDir);
		strcat(t_buf2, t_buf);
		ReadBinaryFile (winstate.hWnd, t_buf2, t_buf, (int)strt);
		
		// PROPOSED CHANGE -- need to track through loader.c; return 0 on error
		// file_size = ReadBinaryFile(winstate.hWnd, t_buf2, t_buf, (int)strt,);
		// END OF CHANGE
		
		strcpy(load_rom[i].szROMName, t_buf );
		load_rom[i].iROMStart = strt;
		load_rom[i].iROMSize = (int)TapeFileLength(t_buf2);
		// load_rom[i].iROMSize = (int)file_size;
		load_rom[i].iUsed = 1;
		load_rom[i].Read_only = readonly;
	}	// loop
	// ----- End of memory configuration -----

	// Load console type - 0=telnet, 1=Windows console, 2=serial port
	GetPrivateProfileString("console", "contype", "1", buf, sizeof(buf), ini_file);
	if (sscanf(buf, "%d", &ct) == 1) {
		if ((ct < 0) || (ct > 2)) ct = 1;
		con_typ = iConTypID[(simstate.iConsoleType = ct)]; // assign ID for button
	}

	GetPrivateProfileString("console", "port", "23", buf, sizeof(buf), ini_file);	
	sscanf(buf, "%d", &ct);
	simstate.usTelnetPort = ct;

	GetPrivateProfileString("console", "concolor","",Tcolor,sizeof(Tcolor),ini_file);
	if (strlen(Tcolor) == 0) strcpy(Tcolor, "Green");

	GetPrivateProfileString("console", "conbright","0",buf,sizeof(buf),ini_file);
	sscanf(buf,"%d",&ct);
	
	GetPrivateProfileString("console", "conback", "",Bcolor,sizeof(Bcolor),ini_file);
	if (strlen(Bcolor) == 0) strcpy(Bcolor, "Black");
	// Text_Color = 2; // Green default
	// Back_Color = 0; // Black default
	for (Temp=0; Temp <=7; Temp++) {
		if (strcmp(Tcolor, Color_Selections[Temp])==0) Text_Color = Temp;
		if (strcmp(Bcolor, Color_Selections[Temp])==0) Back_Color = Temp;
	}

	if ( (Text_Bright = ct) /* != NO */ ) Text_Color |= 8;
	if (Back_Color == (Text_Color & 7)) { // same hue is bad; change
		Back_Color = Back_Color ? 0 : 7; // Black or White
		strcpy(Bcolor, Color_Selections[Back_Color]);
	}

	GetPrivateProfileString("console", "comport", "COM1", buf, sizeof(buf), ini_file);	
	sscanf(buf, "%s", S_port);

	GetPrivateProfileString("console", "baudrate", "9600", buf, sizeof(buf), ini_file);	
	sscanf(buf, "%i", &ct);
	baudrate = ct;

	simstate.iSioType = 0; // FJS default to file

	// Set proper console function pointers based on console method -
	switch (simstate.iConsoleType) {
	    case 0:									// >>> Telnet
			simstate.conread_func  = TELNET_TcpRead;	// read from console channel
			simstate.conwrite_func = TELNET_TcpWrite;	// write to console channel
			simstate.constato_func = TELNET_TcpOutStatus;	// get output channel status
			simstate.constati_func = TELNET_TcpInStatus;	// get input channel status
			simstate.conshtdn_func = TELNET_TcpShutdown;	// console shutdown
			sprintf(buf, "Telnet %u", simstate.usTelnetPort);
			Status_SetText(hwndStatusBar, STATBAR_CONS, 0, buf);
			// FJS let PUN & RDR use Sio channel which console does not -
			simstate.Sioread_func  = SERIAL_SerRead;	// to read from Sio channel
			simstate.Siowrite_func = SERIAL_SerWrite;		// to write to  Sio channel
			simstate.Siostato_func = SERIAL_SerOutStatus;	// to get Sio channel output status
			simstate.Sioshtdn_func = SERIAL_SerShutdown;	// for Sio shutdown
		break;

		default: // FJS
		case 1:									// >>> Windows console
			simstate.conread_func  = WINCON_Read;		// read from console channel
			simstate.conwrite_func = WINCON_Write;		// write to console channel
			simstate.constato_func = WINCON_OutStatus;	// get output channel status
			simstate.constati_func = WINCON_InStatus;	// get input channel status
			simstate.conshtdn_func = WINCON_Shutdown;	// console shutdown
			Status_SetText(hwndStatusBar, STATBAR_CONS, 0, "Console");
			// FJS let PUN & RDR use Sio channel which console does not (TCP default) -
			simstate.Sioread_func  = TELNET_TcpRead;		// to read from Sio channel
			simstate.Siowrite_func = TELNET_TcpWrite;		// to write to  Sio channel
			simstate.Siostato_func = TELNET_TcpOutStatus;	// to get Sio channel output status
			simstate.Sioshtdn_func = TELNET_TcpShutdown;	// for Sio shutdown
		break;

		case 2:									// >>> Serial terminal
			simstate.conread_func  = SERIAL_SerRead;	// read from console channel
			simstate.conwrite_func = SERIAL_SerWrite;	// write to console channel
			simstate.constato_func = SERIAL_SerOutStatus;	// get output channel status
			simstate.constati_func = SERIAL_SerInStatus;	// get input channel status
			simstate.conshtdn_func = SERIAL_SerShutdown;	// console shutdown
			sprintf(buf, "Serial %s", S_port);
			Status_SetText(hwndStatusBar, STATBAR_CONS, 0, buf);
			// FJS let PUN & RDR use Sio channel which console does not -
			simstate.Sioread_func  = TELNET_TcpRead;		// to read from Sio channel
			simstate.Siowrite_func = TELNET_TcpWrite;		// to write to  Sio channel
			simstate.Siostato_func = TELNET_TcpOutStatus;	// to get Sio channel output status
			simstate.Sioshtdn_func = TELNET_TcpShutdown;	// for Sio shutdown
		// break;
	}

	// FJS let Sio activity actually make TCP/COMx connection -
	simstate.Siostati_func = NULL;					// will get Sio channel input status

	// Load default front panel options
	if (GetPrivateProfileString("FrontPanel", "ledlevel", "8", buf, sizeof(buf), ini_file))
		iLEDlevel = atoi(buf);
	if (GetPrivateProfileString("FrontPanel", "sound", "0", buf, sizeof(buf), ini_file))
		bFPsound = atoi(buf);
	// ***** front panel serial port assignments go here

	// Load "other" default emulator options
	if (GetPrivateProfileString("MISC", "bell", "1", buf, sizeof(buf), ini_file))
		Bell = atoi(buf);
	if (GetPrivateProfileString("MISC", "wordwrap", "0", buf, sizeof(buf), ini_file))
		WordWrap = atoi(buf);
	if (GetPrivateProfileString("MISC", "ansi", "1", buf, sizeof(buf), ini_file))
		Ansi = atoi(buf);
	if (GetPrivateProfileString("MISC", "disksnd", "1", buf, sizeof(buf), ini_file))
		Disksnd = atoi(buf);
	if (GetPrivateProfileString("MISC", "fansnd", "0", buf, sizeof(buf), ini_file))
		Fansnd = atoi(buf);
}


/*--  SYSCFG_SaveOptions  -------------------------------------
	Saves program options to the private ini file.

	Params:		none

	Uses:		locals only

	Returns:	nothing
	
	Comments:	The ini file is located wherever the current
				program is located. Memory and console config
				saved within the dialog box handler. Same would
				go for any other config dialogs we create in
				the future.	
-------------------------------------------------------------*/
void SYSCFG_SaveOptions(void)
{

    char ini_file[_MAX_PATH];
    char buf[80];		// key info
    char buf2[10];		// key name
    char *pbuf;
    int newlen;
    // char *str;
    DWORD fstat;
	int visible;
    RECT rc;
	register i;


    strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);
    newlen = strlen(ini_file) + 45;  // add 45 chars for msgbox text, etc...
    pbuf = (char *)malloc(newlen);

    fstat = GetFileAttributes(ini_file);
    if (fstat == 0xFFFFFFFF) {
		int rslt;

		sprintf(pbuf, "Creating file '%s'", ini_file);
		rslt = MessageBox(winstate.hWnd, pbuf, "Confirm",
					MB_OKCANCEL | MB_ICONEXCLAMATION | MB_TOPMOST);
		if (rslt != IDOK) return;

    } else if (fstat & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY)) {
		sprintf(pbuf, "Can't open file '%s': it is not writable", ini_file);
		HOST_ThrowErrMsg(pbuf);
		return;
    }

    // just in case it changes monumentally in the future
    WritePrivateProfileString("fileformat", "version", "3.0", ini_file);

	// Save main window placement; skip if minimized otherwise invalid window coordinates
    // will be saved in the .ini file.
	if (!IsIconic(winstate.hWnd)){
 		GetWindowRect(winstate.hWnd, &rc);
	    sprintf(buf, "org=%d,%d, size=%d,%d",
				rc.left, rc.top, /* rc.bottom, rc.right, */	// x,y FJS
				rc.right-rc.left, rc.bottom-rc.top);		// w,h FJS
	    WritePrivateProfileString("windows", "main", buf, ini_file);
	}

    // save debugger window placement but not visibility
    if (DebuggerWindowGetSize(&rc, &visible)) {
		sprintf(buf, "org=%d,%d, size=%d,%d", rc.left, rc.top,	// x,y
			rc.right-rc.left, rc.bottom-rc.top);		// w,h
		WritePrivateProfileString("windows", "debug", buf, ini_file);
    }

	// Probably don't need to do this.
	// Write ROM info (will also capture bootrom)
	for (i=FIRST_ROM; i<MAXROMS; i++) {
		if (load_rom[i].iUsed == 1) {	// contains entry
			sprintf(buf2, "ROM%d", i);
			sprintf(buf, "%s,%04X,%i", load_rom[i].szROMName, load_rom[i].iROMStart,load_rom[i].Read_only);
			WritePrivateProfileString("ROMS", buf2, buf, ini_file);
		} 
	}
}


/*--  SYSCFG_WriteConfigInfo  ---------------------------------
	Writes a configuration option line directly to the INI file.

	Params:		section_name, key_name, data

	Uses:		locals only

	Returns:	nothing
	
	Comments:	Self-contained routine used by dialog box
				routines to add config info directly to the ini
				file.

-------------------------------------------------------------*/
void SYSCFG_WriteConfigInfo( char *section, char *key, char *keydata )
{

    char ini_file[_MAX_PATH];
    char *pbuf;
    int newlen;
    DWORD fstat;

    strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);
    newlen = strlen(ini_file) + 45;  // add 45 chars for msgbox text, etc...
    pbuf = (char *)malloc(newlen);

	// See if file exists and if not, create it
    fstat = GetFileAttributes(ini_file);
    if (fstat == 0xFFFFFFFF) {
		int rslt;

		sprintf(pbuf, "Creating file '%s'", ini_file);
		rslt = MessageBox(winstate.hWnd, pbuf, "Confirm",
			    MB_OKCANCEL | MB_ICONEXCLAMATION | MB_TOPMOST);
		if (rslt != IDOK) return;

		// just in case it changes monumentally in the future
		WritePrivateProfileString("fileformat", "version", "3.0", ini_file);
		
    } else if (fstat & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY)) {
		sprintf(pbuf, "Can't open file '%s': it is not writable", ini_file);
		HOST_ThrowErrMsg(pbuf);
		return;
    }

	// write the string
	ASSERT(section != NULL);
	ASSERT(key != NULL);
	ASSERT(keydata != NULL);
	WritePrivateProfileString(section, key, keydata, ini_file);

}


/*--  SYSCFG_ReadConfigInfo  ----------------------------------
	Reads a configuration option line directly from the INI file.

	Params:		section_name, key_name, data

	Uses:		locals only

	Returns:	pointer to returned string
	
	Comments:	Self-contained routine used by dialog box
				routines to get config info directly from the
				ini file.
-------------------------------------------------------------*/
char *SYSCFG_ReadConfigInfo (char *section, char *key)
{
    char ini_file[_MAX_PATH];

    strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);

	// read the string
	ASSERT(section != NULL);
	ASSERT(key != NULL);
	szProfileBuf[0] = 0 /* EOS */;
	GetPrivateProfileString(section, key, NULL, szProfileBuf, sizeof(szProfileBuf), ini_file);
	return szProfileBuf;
}


#if 0
/*--  GenerateRomUsageMap  ------------------------------------
	Generates a composite ROM usage map,

	Params:		none

	Uses:		load_rom global

	Returns:	nothing
	
	Comments:	Used by RomCfg dialog box to present a map of the
				used ROM region to the user.
					
-------------------------------------------------------------*/
static BOOL GenerateRomUsageMap( HWND hwnd ) 
{

	byte ucDumpVal = 0;
	char t_buf[_MAX_PATH];
	char rom_map[MEMSIZE];	// should dynamically allocate as (maxrom-minrom)
	FILE *pFile = NULL;
	int	i = 0;
	int x = 0;
	SYSTEMTIME systime;


	strcpy(t_buf, winstate.szFilesDir);
	strcat(t_buf, "rommap.dmp");

    if (NULL == (pFile = fopen (t_buf, "w"))) return FALSE;

	memset(rom_map, 0xff, MEMSIZE);	// setr array to known value
	
	GetSystemTime(&systime);

	fprintf(pFile, "START OF ROM MAP\n");
	// probably should do some sort of timezone adjustment...
	fprintf(pFile, "Timestamp: %02d/%02d/%02d - %02d:%02d\n\n", systime.wMonth,
		systime.wDay, systime.wYear, systime.wHour, systime.wMinute);
	// it's nice to tell everyone what the numbers mean...
	fprintf(pFile, "Key: 0xff -- no ROM; numbers 0x00-0x7f -- ROM handle number.\n\n");

	// loop through load_rom.  Spot in map is (rom_addr-minrom)
	for (i=FIRST_ROM; i<MAXROMS; i++){
		if (load_rom[i].iUsed == 0)	continue;	// skip if unused

		// slot is used, so copy range into map
		for (x=0; x<load_rom[i].iROMSize; x++)
			rom_map[load_rom[i].iROMStart + x] = i;	// use ROM_ID as marker
	}

	// Output the image map; line format: aaaa: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
	for (i=(romstart>>4); i<=(romend>>4); i++){
		// output the address -
		fprintf(pFile, "%04x: ", (i << 4));

		for (x=0; x<=0x0f; x++) {
			ucDumpVal = (byte)rom_map[(word)((i << 4) + x)];	// should be ROM_ID
			fprintf(pFile, "%02x ", ucDumpVal);
		}
		fprintf(pFile, "\n");
	}

	fprintf(pFile, "\n\nEND OF ROM MAP\n");
	fclose(pFile);

	wsprintf(szBuffer, "Generated ROM usage map to file 'rommap.dmp'.");
	Status_SetText(hwndStatusBar, STATBAR_READY, 0, szBuffer);
	return TRUE;
}
#endif


/**************************************************************\
|** CALLBACKS  ************************************************|
\**************************************************************/
/**************************************************************\
*
*   FUNCTION:	lpWndProcOPT (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for options container dialog
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS: Container dialog proc for options sub-dialogs
*
\**************************************************************/
BOOL CALLBACK lpWndProcOPT(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	HWND hLB = GetDlgItem(hDlg, IDC_CATS);
	static HWND HWND_Temp = NULL;
	int Select=0;

	
	switch (iMsg){
		case WM_INITDIALOG:
			ListBox_AddString(hLB, "Console");	// 0
			ListBox_AddString(hLB, "Memory");	// 1
			ListBox_AddString(hLB, "CPU");		// 2
			// ListBox_AddString(hLB, "Printer");
			ListBox_AddString(hLB, "Front Panel");	// 3
		    ListBox_AddString(hLB, "Other");	// 4
			ListBox_SetCurSel(hLB, 0);
			HWND_Temp=SYSCFG_OpenConsoleWin(hDlg); // remember default
			Do_shutdown = 0 /* FALSE */;
			return TRUE;

		case WM_KEYDOWN: // FJS TBD watch for confilcts on shutdown !!!
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;

		case WM_COMMAND:
			switch (LOWORD (wParam)){
				case IDC_CATS:
					if (HIWORD(wParam) == LBN_SELCHANGE) {
						Select = SendMessage(hLB, LB_GETCURSEL, 0, 0);
						if (HWND_Temp) { // Close current Dialog to open next.
							Do_shutdown = 1 /* TRUE */; // FJS
							SendMessage(HWND_Temp,WM_COMMAND,IDCANCEL,0);
						}
						Do_shutdown = 0 /* FALSE */; // FJS
						switch (Select) {
							case 0:
								HWND_Temp=SYSCFG_OpenConsoleWin(hDlg); 
								break;
							case 1:
								HWND_Temp=SYSCFG_OpenMemoryWin(hDlg);
								break;
							case 2:
								HWND_Temp=SYSCFG_OpenSpeedWin(hDlg);
								break;
							case 3:
								HWND_Temp=SYSCFG_OpenFPWin(hDlg);
								break;
							// case 4:
							//	HWND_Temp=SYSCFG_OpenPrinterWin(hDlg);
							//	break;
							// case 4:
							default:
								HWND_Temp=SYSCFG_OpenMiscWin(hDlg);
							// break;

							// default: break;
						} // End switch Select
					} // fi HIWORD (wParam)
					break; 	

				case IDOK:
					Do_shutdown = 1 /* TRUE */; // FJS
					SendMessage(HWND_Temp,WM_COMMAND,IDOK,0);
					// Crush...kill...
					DestroyWindow(hDlg);
					break;

				case IDCANCEL:
					Do_shutdown = 1 /* TRUE */; // FJS
					SendMessage(HWND_Temp,WM_COMMAND,IDCANCEL,0);
					DestroyWindow(hDlg);
					break;

				case IDCAPPLY:
					SendMessage(HWND_Temp,WM_COMMAND,IDCAPPLY,0);
					break;

				// default: break;
			} // End switch LOWORD (wParam)
		// break;

		// default: /* no-op */ break;
	} // End switch (iMsg)
	return FALSE;
}


/**************************************************************\
*
*   FUNCTION:	lpWndProcCCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for console config sub-dialog
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
*		If the TCP connection port is changed, then we need
*		to shutdown the existing connection and restart with
*		the new port.
*
\**************************************************************/
BOOL CALLBACK lpWndProcCCfg(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	static int T_con_typ;
	static BOOL T_Text_Bright;
	short int Temp=0;
	char buf[80];
	char buf2[80];
	char szHexBufr[10];
	static char colors[] = "Black,Red,Green,Yellow,Blue,Magenta,Cyan,White";
	BOOL apflag=FALSE;
    // HWND hDlgList = NULL;


	T_con_typ = con_typ;
	
	switch (iMsg) {
		case WM_INITDIALOG:
			// Populate com port combobox dropdown -
			Populate_ComboBox(GetDlgItem(hDlg, IDC_COMPORT),"COM1,COM2,COM3,COM4");
			// Populate baud combobox dropdown -
			Populate_ComboBox(GetDlgItem(hDlg, IDC_BAUDRATE),
					"50,75,110,134,150,300,600,1200,1800,2000,2400,3600,"
					"4800,7200,9600,14400,19200,38400,57600,115200");
			// Populate Console color combobox dropdown(s) -
			Populate_ComboBox(GetDlgItem(hDlg, IDC_COLOR), colors);
			Populate_ComboBox(GetDlgItem(hDlg, IDC_BKGRND),colors); 

			// Arrange all per current settings -
			CheckRadioButton(hDlg, IDC_RADIO_TELNET, IDC_RADIO_SER, T_con_typ);

			sprintf(buf, "%d", simstate.usTelnetPort);
			SetDlgItemText(hDlg, IDC_EDIT_TPORT, buf);

			sprintf(buf, "%s", S_port); 
			SetDlgItemText(hDlg, IDC_COMPORT, buf);
			sprintf(buf, "%i", baudrate); 
			SetDlgItemText(hDlg, IDC_BAUDRATE, buf);				

			sprintf(buf,"%s",Tcolor);
			SetDlgItemText(hDlg, IDC_COLOR,buf);
			CheckDlgButton(hDlg,IDC_BRIGHT, (T_Text_Bright = Text_Bright)); 
			sprintf(buf,"%s",Bcolor);
			SetDlgItemText(hDlg, IDC_BKGRND,buf);

			// enable/disable sub-options per current T_con_type value -
			Do_shutdown = 0 /* NO */;
			Edit_Enable(GetDlgItem(hDlg, IDC_EDIT_TPORT),	(T_con_typ == IDC_RADIO_TELNET));

			ComboBox_Enable(GetDlgItem(hDlg, IDC_COLOR),	(T_con_typ == IDC_RADIO_CONS));
			Button_Enable(GetDlgItem(hDlg, IDC_BRIGHT),		(T_con_typ == IDC_RADIO_CONS));
			ComboBox_Enable(GetDlgItem(hDlg, IDC_BKGRND),	(T_con_typ == IDC_RADIO_CONS));

			ComboBox_Enable(GetDlgItem(hDlg, IDC_COMPORT),	(T_con_typ == IDC_RADIO_SER));
			ComboBox_Enable(GetDlgItem(hDlg, IDC_BAUDRATE),	(T_con_typ == IDC_RADIO_SER));
			return TRUE;			// Let Windows set default focus

		case WM_KEYDOWN: // FJS TBD watch for conflicts !!!
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;
			
		case WM_COMMAND:
			T_Text_Bright=IsDlgButtonChecked(hDlg,IDC_BRIGHT);
			switch (LOWORD(wParam)){
				case IDC_RADIO_TELNET:
				case IDC_RADIO_CONS:
				case IDC_RADIO_SER:
					// disable old -
					if (con_typ == IDC_RADIO_TELNET) {
						Edit_Enable(GetDlgItem(hDlg, IDC_EDIT_TPORT),0);
					}
					if (con_typ == IDC_RADIO_CONS) {
						ComboBox_Enable(GetDlgItem(hDlg, IDC_COLOR),0);
						Button_Enable(GetDlgItem(hDlg, IDC_BRIGHT),0);
						ComboBox_Enable(GetDlgItem(hDlg, IDC_BKGRND),0);
					}
					if (con_typ == IDC_RADIO_SER) {
						ComboBox_Enable(GetDlgItem(hDlg, IDC_COMPORT),0);
					}
					// enable new -
					con_typ = T_con_typ = (LOWORD (wParam));	// copy button ID
					CheckRadioButton(hDlg, IDC_RADIO_TELNET, IDC_RADIO_SER,  T_con_typ);

					if (T_con_typ == IDC_RADIO_TELNET) {
						Edit_Enable(GetDlgItem(hDlg,IDC_EDIT_TPORT),1);
					}
					if (T_con_typ == IDC_RADIO_CONS) {
						ComboBox_Enable(GetDlgItem(hDlg, IDC_COLOR),1);
						Button_Enable(GetDlgItem(hDlg, IDC_BRIGHT),1);
						ComboBox_Enable(GetDlgItem(hDlg, IDC_BKGRND),1);
					}
					if (T_con_typ == IDC_RADIO_SER) {
						ComboBox_Enable(GetDlgItem(hDlg, IDC_COMPORT),1);
						ComboBox_Enable(GetDlgItem(hDlg, IDC_BAUDRATE),1);
					}
					return TRUE;

				case IDCAPPLY:
					apflag = TRUE; // don't fall through to IDCANCEL
					// fall through to IDOK

				case IDOK:
					con_typ = T_con_typ;
					GetDlgItemText(hDlg, IDC_EDIT_TPORT, szHexBufr, 6);
					simstate.usTelnetPort = atoi(szHexBufr);
					GetDlgItemText(hDlg, IDC_COMPORT, S_port, 6);
					GetDlgItemText(hDlg, IDC_BAUDRATE, szHexBufr, 6);
					baudrate = atoi(szHexBufr);					
					GetDlgItemText(hDlg,IDC_COLOR, Tcolor,8);
					Text_Bright = T_Text_Bright;
					GetDlgItemText(hDlg,IDC_BKGRND,Bcolor,8);
					for (Temp=0; Temp <=7; Temp++) {
						if (strcmp(Tcolor, Color_Selections[Temp])==0)
							Text_Color = Temp;
						if (strcmp(Bcolor, Color_Selections[Temp])==0)
							Back_Color = Temp;
					}
					if (Text_Bright) Text_Color |= 8; 
					if (Back_Color == (Text_Color & 7)) { // same hue is bad; change
						Back_Color = Back_Color ? 0 : 7; // Black or White
						strcpy(Bcolor, Color_Selections[Back_Color]);
						sprintf(buf,"%s", Bcolor); 
						SetDlgItemText(hDlg, IDC_BKGRND, buf);
					}
					
					// map ID to integer -
					switch (con_typ) {
						case IDC_RADIO_TELNET:
							simstate.iConsoleType = 0;
							sprintf(buf2, "Telnet %u", simstate.usTelnetPort);							
							break;

						default:
						case IDC_RADIO_CONS:
							simstate.iConsoleType = 1;
							sprintf(buf2, "Console");
							break;

						case IDC_RADIO_SER:
							simstate.iConsoleType = 2;
							sprintf(buf2, "Serial %s", S_port);
						// break;
					}

					// Commit configuration to the private INI file -
					sprintf(buf, "%d", simstate.iConsoleType);
					SYSCFG_WriteConfigInfo("console", "contype", buf);

					sprintf(buf, "%u", simstate.usTelnetPort); 
					SYSCFG_WriteConfigInfo("console", "port", buf);

					sprintf(buf,"%s",Color_Selections[Text_Color & 7]);
					SYSCFG_WriteConfigInfo("console", "concolor", buf);
					sprintf(buf,"%i",Text_Bright);
					SYSCFG_WriteConfigInfo("console", "conbright", buf);
					sprintf(buf,"%s",Color_Selections[Back_Color & 7]);
					SYSCFG_WriteConfigInfo("console", "conback", buf);
					sprintf(buf, "%s", S_port);
					SYSCFG_WriteConfigInfo("console", "comport", buf);					
					sprintf(buf, "%i", baudrate);
					SYSCFG_WriteConfigInfo("console", "baudrate", buf);

					// Update status bar
					Status_SetText(hwndStatusBar, STATBAR_CONS, 0, buf2);

					if (apflag) { // Dont fall through on APPLY.
						break;
					}

				// Fall through to IDCANCEL -

				case IDCANCEL:
					if (!Do_shutdown) { // FJS
						SendMessage(hwndOPTS,WM_COMMAND,IDCANCEL,0); // !!!
						return FALSE; // FJS!!!
					}

					DestroyWindow(hDlg);
					hwndConCfg = (HWND)NULL;
					return TRUE;

				// default: break;
			}	// end of switch (wParam)
 		return TRUE;

		// default: break;
	}	// message case
	return FALSE;
}


/**************************************************************\
*
*   FUNCTION:	lpWndProcMCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for memory config sub-dialog
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:base[0]=HEX base[1]=decimal base[2]=octal
*
\**************************************************************/
BOOL CALLBACK lpWndProcMCfg(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL apflag = FALSE;
	static char szFilter[] = "Binary ROM Files (.BIN)\0*.bin\0\0";
	char buf[80], buf2[80] ,szHexBufr[5];
	char ini_file[_MAX_PATH],base[3][5]={"%04X","%5d","%6o"};
	HWND hCtrl = NULL;
	int index = 0, slidepos = 0, rslt = 0;
	int rom_image[6]={IDC_ROMIMAGE1,IDC_ROMIMAGE2,IDC_ROMIMAGE3,IDC_ROMIMAGE4,IDC_ROMIMAGE5,IDC_ROMIMAGE6};
	int rom_addr[6]={IDC_ROMADDR1,IDC_ROMADDR2,IDC_ROMADDR3,IDC_ROMADDR4,IDC_ROMADDR5,IDC_ROMADDR6};
	int Maxram = 0, T_Maxram = 0 /* ,retval = 0 */; // FJS
	BOOL read_only[6]={IDC_READ1,IDC_READ2,IDC_READ3,IDC_READ4,IDC_READ5,IDC_READ6};
	OPENFILENAME l_ofn;
	register i;
	static WORD T_ramend = 0;	
 	static short unsigned int current_base = 0, cur_base = IDC_RADIO_HEX;
   
	
	strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);

	switch (iMsg) {
		case WM_INITDIALOG:
			// Fill in edit controls
			hCtrl = GetDlgItem(hDlg, IDC_SLIDER_RAMSIZE);
			SendMessage(hCtrl, TBM_SETRANGE, TRUE, MAKELONG( 1, 256 ));
			SendMessage(hCtrl, TBM_SETPOS,TRUE,(ramend/256+1));
			if (current_base == 0) cur_base = IDC_RADIO_HEX;
			if (current_base == 1) cur_base = IDC_RADIO_DEC;
			if (current_base == 2) cur_base = IDC_RADIO_OCT;			
			CheckRadioButton(hDlg, IDC_RADIO_HEX, IDC_RADIO_OCT, cur_base);
			
			// copy current data to Temp variables so CANCEL button works as expected.
			T_ramend=ramend;
			for (index=0;index<=5;index++) {
				T_load_rom[index].iUsed=load_rom[index].iUsed;
				T_load_rom[index].iROMSize=load_rom[index].iROMSize;
				T_load_rom[index].iROMStart=load_rom[index].iROMStart;
				T_load_rom[index].Read_only=load_rom[index].Read_only;	
				strcpy(T_load_rom[index].szROMName,load_rom[index].szROMName);
			} 

			// Fill in Roms fields
			for (index=0;index<=5;index++) {
				if (T_load_rom[index].iUsed == 1) {
					SetDlgItemText(hDlg,rom_image[index],T_load_rom[index].szROMName);
					sprintf(szHexBufr,base[current_base],T_load_rom[index].iROMStart);
					SetDlgItemText(hDlg,rom_addr[index],szHexBufr);		
					CheckDlgButton(hDlg,read_only[index],T_load_rom[index].Read_only);
				} //End if
			}//End for 
			sprintf(buf, base[current_base], T_ramend);
			SetDlgItemText(hDlg,IDC_RAMSIZE,buf);
			return TRUE;					// Let Windows set default focus

		case WM_KEYDOWN: // FJS TBD watch for conflicts !!!
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;

		case WM_HSCROLL:
			hCtrl = GetDlgItem( hDlg, IDC_SLIDER_RAMSIZE );
			slidepos = SendMessage(hCtrl, TBM_GETPOS,0,0);
			T_ramend=(slidepos*256-1);
			sprintf(buf, base[current_base], T_ramend);
			SetDlgItemText(hDlg,IDC_RAMSIZE,buf);
			return TRUE;

		case WM_COMMAND:
			if (LOWORD(wParam) != IDC_KILLROM) // Remember what edit box had focus
				last_editbox=LOWORD(wParam);	// last for remove ROM routine.

			switch (LOWORD(wParam)){
				case IDC_RADIO_HEX: // If were changing Number base
				case IDC_RADIO_DEC:	// save all edit box values
				case IDC_RADIO_OCT:	// to their Temp Variables
					GetDlgItemText(hDlg,IDC_RAMSIZE,buf,7);
					T_ramend=Xtoi(buf,current_base);
					for (index=0;index<6;index++) {
						GetDlgItemText(hDlg,rom_image[index],buf,80);
						strcpy(T_load_rom[index].szROMName,buf);
						GetDlgItemText(hDlg,rom_addr[index],szHexBufr,7);
						T_load_rom[index].Read_only=IsDlgButtonChecked(hDlg,read_only[index]);
						T_load_rom[index].iROMStart=(Xtoi(szHexBufr,current_base) & 0xffff);
					} // End for loop
					// Change base
					if (LOWORD(wParam)== IDC_RADIO_HEX) current_base=0;
					if (LOWORD(wParam)== IDC_RADIO_DEC) current_base=1;
					if (LOWORD(wParam)== IDC_RADIO_OCT) current_base=2;
					// Redraw dialog with new base
					for (index=0;index<=5;index++){
						if (T_load_rom[index].iUsed == 1) {
							SetDlgItemText(hDlg,rom_image[index],T_load_rom[index].szROMName);
							sprintf(szHexBufr,base[current_base],T_load_rom[index].iROMStart);
							SetDlgItemText(hDlg,rom_addr[index],szHexBufr);		
							CheckDlgButton(hDlg,read_only[index],T_load_rom[index].Read_only);	
						}
						sprintf(buf, base[current_base], T_ramend);
						SetDlgItemText(hDlg,IDC_RAMSIZE,buf);
					}
					break;

				case IDC_KILLROM:
					// First update load_rom array with whats on screen
					for (index=0;index<6;index++){
						GetDlgItemText(hDlg,rom_image[index],buf,80);
						strcpy(T_load_rom[index].szROMName,buf);
						GetDlgItemText(hDlg,rom_addr[index],szHexBufr,7);		
						T_load_rom[index].iROMStart=(Xtoi(szHexBufr,current_base) & 0xffff);
						T_load_rom[index].Read_only=IsDlgButtonChecked(hDlg,read_only[index]);
					}
					
					// find out what editbox had focus just before button was clicked
					for (index=0;index<=6;index++)
						if (last_editbox == rom_image[index]) break;
							
					if (index>=6){ //It wasn't a ROM edit box forget it.
						MessageBox(winstate.hWnd,"You haven't selected a ROM! \nFirst Click on a Image name \nThen click Remove.", "Remove What?",MB_OK | MB_ICONQUESTION | MB_TOPMOST);
						return FALSE;
					}

					if (strlen(T_load_rom[index].szROMName)==0){
						MessageBox(winstate.hWnd,"You can't remove an empty slot!", "Nothing to do",MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
						return FALSE;
					}

					sprintf(buf,"Removing ROM %s\nFrom slot %i?",T_load_rom[index].szROMName,index);
					rslt = MessageBox(winstate.hWnd, buf, "Confirm",MB_OKCANCEL | MB_ICONEXCLAMATION | MB_TOPMOST);
					if (rslt != IDOK) return FALSE;

					// Slide all ROMS up one slot starting at the one were Removing.
					for (;index<=4;index++){
						T_load_rom[index].iROMStart=T_load_rom[index+1].iROMStart;
						strcpy(T_load_rom[index].szROMName,T_load_rom[index+1].szROMName);
						T_load_rom[index].iUsed=T_load_rom[index+1].iUsed;
						T_load_rom[index].Read_only=T_load_rom[index+1].Read_only;
					}
					T_load_rom[5].iROMStart=0; //Clear out the last one.
					strcpy(T_load_rom[5].szROMName,"");
					T_load_rom[5].iUsed=0;
					T_load_rom[5].Read_only=0;
					// Refresh dialog box from Temp load_rom array
					for (index=0;index<=5;index++){
						SetDlgItemText(hDlg,rom_image[index],T_load_rom[index].szROMName);
						CheckDlgButton(hDlg,read_only[index],T_load_rom[index].Read_only);
						if (T_load_rom[index].iUsed ==1)
							sprintf(szHexBufr,base[current_base],T_load_rom[index].iROMStart);
						else
							sprintf(szHexBufr,"%s","");
						SetDlgItemText(hDlg,rom_addr[index],szHexBufr);		
					}
					return TRUE;
	
				case IDC_ADDROM:
					strcpy(szFileDir, winstate.szFilesDir);
					l_ofn.lStructSize       = sizeof (OPENFILENAME);
					l_ofn.hwndOwner         = hDlg;
					l_ofn.hInstance         = (HINSTANCE)NULL;
					l_ofn.lpstrFilter       = szFilter;
					l_ofn.lpstrCustomFilter = NULL;
					l_ofn.nMaxCustFilter    = 0;
					l_ofn.nFilterIndex      = 0;
					l_ofn.lpstrFile         = winstate.szFileName;
					l_ofn.nMaxFile          = _MAX_PATH;
					l_ofn.lpstrFileTitle    = winstate.szTitleName;
					l_ofn.nMaxFileTitle     = _MAX_FNAME + _MAX_EXT;
				 	l_ofn.lpstrInitialDir   = szFileDir;		// module global
					l_ofn.lpstrTitle        = NULL;
					l_ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
					l_ofn.nFileOffset       = 0;
					l_ofn.nFileExtension    = 0;
					l_ofn.lpstrDefExt       = "bin";
					l_ofn.lCustData         = 0L;
					l_ofn.lpfnHook          = NULL;
					l_ofn.lpTemplateName    = NULL;
					l_ofn.lpstrFile         = winstate.szFileName; // full path
					l_ofn.lpstrFileTitle    = winstate.szTitleName;// short name

					// GOFN returns zero on cancel. nFilterIndex is the 1-based
					// index number of the selected file filter. If nFilterIndex
					// is 0 and lpstrCustomFilter is NULL, the system sets the
					// filter index to the first filter.
					if (!GetOpenFileName(&l_ofn)) return TRUE;

					//Look for an open slot.
					for (index=0;index<=6;index++)
						if (T_load_rom[index].iUsed ==0) break;

					if (index>=6){
						MessageBox(winstate.hWnd, "SPACE! The final frontier!\n To bad your out of it.", "No free Slots",MB_OK |MB_ICONHAND| MB_TOPMOST);
						return FALSE;
					}

					T_load_rom[index].iUsed=1;
					T_load_rom[index].Read_only=1;
					strcpy(T_load_rom[index].szROMName,winstate.szTitleName);
					SetDlgItemText(hDlg,rom_image[index],T_load_rom[index].szROMName);
					sprintf(szHexBufr,base[current_base],T_load_rom[index].iROMStart);
					SetDlgItemText(hDlg,rom_addr[index],szHexBufr);		
					CheckDlgButton(hDlg,read_only[index],T_load_rom[index].Read_only);
					return TRUE;

				case IDCAPPLY:
					apflag = TRUE;
					// fall through to IDOK, but not to IDCANCEL

				case IDOK:
					// Commit configuration to the private INI file.
					// We copy the Temp variables back and then 
					// write the whole mess to the ini file.
					Maxram = 65535;
					for (index=0;index<=5;index++){
						GetDlgItemText(hDlg,rom_addr[index],szHexBufr,7);
						T_Maxram=Xtoi(szHexBufr,current_base);
						if ((T_Maxram <= Maxram) & IsDlgButtonChecked(hDlg,read_only[index]))
							Maxram=T_Maxram-1;
					}

					GetDlgItemText(hDlg, IDC_RAMSIZE, szHexBufr, 7);
					T_ramend=(Xtoi(szHexBufr,current_base) & 0xFFFF);
					if (T_ramend>Maxram){
						T_ramend=Maxram;
						if (!apflag)
							MessageBox(winstate.hWnd, "RAM/ROM Overlaping\nRamsize will be adjusted accordingly.", "Warning",MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
					}
					ramend = T_ramend;
					ramstart = 0;
					romend = 65535;
					romstart = (ramend+1);
								
					for (index=0;index<=5;index++){
						GetDlgItemText(hDlg,rom_image[index],buf,80);
						strcpy(load_rom[index].szROMName,buf);
						GetDlgItemText(hDlg,rom_addr[index],szHexBufr,7);		
						load_rom[index].iROMStart=(Xtoi(szHexBufr,current_base) & 0xffff);
						load_rom[index].Read_only=IsDlgButtonChecked(hDlg,read_only[index]);
						if (strlen(load_rom[index].szROMName) !=0)
							load_rom[index].iUsed=1;
						else
							load_rom[index].iUsed=0;
					}			

					// No conversion here, we always save in HEX.
					sprintf(buf, "0x%04X,0x%04X", ramstart, ramend);
					SYSCFG_WriteConfigInfo("memcfg", "RAM", buf);
					sprintf(buf, "0x%04X,0x%04X", romstart, romend);
					SYSCFG_WriteConfigInfo("memcfg", "ROM", buf);

					// Commit updated ranges to PageAccessRights[]
					for (i=ramstart; i<=ramend; i++)
						PageAccessRights[i] = 0;
	
					for (i=romstart; i<=romend; i++)
						PageAccessRights[i] = 1;

					WritePrivateProfileSection("ROMS","",ini_file);
					for (index=0; index<=5; index++) {
						if (load_rom[index].iUsed == 1) {	// contains entry
							sprintf(buf2, "ROM%d", index);
							sprintf(buf, "%s,%04X,%i", load_rom[index].szROMName, load_rom[index].iROMStart,load_rom[index].Read_only);
							WritePrivateProfileString("ROMS", buf2, buf, ini_file);
						}
					}
			
					if (apflag){ //were just applying not OKing
						for (index=0;index<=5;index++){
							if (load_rom[index].iUsed == 1){
								SetDlgItemText(hDlg,rom_image[index],load_rom[index].szROMName);
								sprintf(szHexBufr,base[current_base],load_rom[index].iROMStart);
								SetDlgItemText(hDlg,rom_addr[index],szHexBufr);		
								CheckDlgButton(hDlg,read_only[index],load_rom[index].Read_only);
							}
							sprintf(buf, base[current_base], ramend);
							SetDlgItemText(hDlg,IDC_RAMSIZE,buf);
							hCtrl = GetDlgItem(hDlg, IDC_SLIDER_RAMSIZE);
							SendMessage(hCtrl, TBM_SETPOS,TRUE,(ramend/256+1));					
						}
						apflag = FALSE; // don't fall through to IDCANCEL
						break;
					}
					// Fall through to IDCANCEL

				case IDCANCEL:
					if (!Do_shutdown) { // FJS
						SendMessage(hwndOPTS,WM_COMMAND,IDCANCEL,0); // !!!
						return FALSE; // FJS!!!
					}

					DestroyWindow(hDlg);
					hwndMemCfg = NULL;
					return TRUE;

				// default: break;
			}	// end of switch (wParam)
 		return TRUE;

		// default: break;
	}	// message case
	return FALSE;
}


/**************************************************************\
*
*   FUNCTION:	lpWndProcSCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for CPU speed/type config sub-dialog
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
\**************************************************************/
BOOL CALLBACK lpWndProcSCfg(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	char buf[80];
	char *str;
	char *str2;
	BOOL apflag /* =FALSE */;
	static int T_cpu_speed = 0;
	static int T_cpu_type = 0;


    switch (iMsg) {
		case WM_INITDIALOG:
			T_cpu_speed = cpu_speed;
			T_cpu_type = cpu_type;
			CheckRadioButton(hDlg, IDC_RADIO_M102A, IDC_RADIO_UNREG, T_cpu_speed);
			CheckRadioButton(hDlg, IDC_RADIO_I8080, IDC_RADIO_Z80, T_cpu_type);
			return TRUE;			// Let Windows set default focus

		case WM_KEYDOWN: // FJS TBD watch for conflicts !!!
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;

		case WM_COMMAND:
			apflag = FALSE;
			
			switch (LOWORD(wParam)){
				case IDC_RADIO_M102A:
				case IDC_RADIO_M204A:
				case IDC_RADIO_M409A:
				case IDC_RADIO_UNREG:
					T_cpu_speed = (LOWORD(wParam));	// copy button ID
					CheckRadioButton(hDlg, IDC_RADIO_M204A, IDC_RADIO_UNREG, T_cpu_speed);
					return TRUE;

				case IDC_RADIO_Z80_CYC:
				case IDC_RADIO_I8080_CYC:
				case IDC_RADIO_Z80:
				case IDC_RADIO_I8080:
					T_cpu_type = (LOWORD(wParam));	// copy button ID
					CheckRadioButton(hDlg, IDC_RADIO_I8080, IDC_RADIO_Z80_CYC, T_cpu_type);
					return TRUE;

				case IDCAPPLY:
					apflag = TRUE;
					// fall through to IDOK

				case IDOK:
					cpu_speed = T_cpu_speed;
					cpu_type = T_cpu_type;

					switch (cpu_speed) {
						case IDC_RADIO_M102A:
							SYS_Set8080Speed(MHZ_1_02);
							simstate.orig_up_speed = MHZ_1_02;
							str = "1.02MHz";
							break;


						default:
						case IDC_RADIO_M204A:
							SYS_Set8080Speed(MHZ_2_04);
							simstate.orig_up_speed = MHZ_2_04;
							str = "2.04MHz";
							break;

						case IDC_RADIO_M409A:
							SYS_Set8080Speed(MHZ_4_09);
							simstate.orig_up_speed = MHZ_4_09;
							str = "4.09MHz";
							break;

						case IDC_RADIO_UNREG:
							SYS_Set8080Speed(MHZ_unregulated);
							simstate.orig_up_speed = MHZ_unregulated;
							str = "999MHz";
						// break;	
					}

					switch (cpu_type) {
						case IDC_RADIO_Z80_CYC:
							SYS_SetCPUType(simstate.up_type = CPU_Z80_Cycle);
							str2 = "Z80Cy";
							break;

						case IDC_RADIO_I8080_CYC:
							SYS_SetCPUType(simstate.up_type = CPU_8080_Cycle);
							str2 = "8080Cy";
							break;

						case IDC_RADIO_Z80:
							SYS_SetCPUType(simstate.up_type = CPU_Z80);
							str2 = "Z80";
							break;

						default: 
						case IDC_RADIO_I8080:
							SYS_SetCPUType(simstate.up_type = CPU_8080);
							str2 = "8080";
						// break;
					}

					// Commit configuration to the INI file and alert user.
					SYSCFG_WriteConfigInfo("cpu", "type", str2);
					SYSCFG_WriteConfigInfo("cpu", "speed", str);
					wsprintf(buf, "CPU changed to %s @ %s", str2, str);
					Status_SetText(hwndStatusBar, STATBAR_READY, 0, buf);
					Status_SetText(hwndStatusBar, STATBAR_CPU, 0, str2);

					if (apflag) { // Dont fall through on APPLY.
						break;
					}

				// Fall through to IDCANCEL

				case IDCANCEL:
					if (!Do_shutdown) { // FJS
						SendMessage(hwndOPTS,WM_COMMAND,IDCANCEL,0); // !!!
						return FALSE; // FJS!!!
					}

					DestroyWindow(hDlg);
					hwndSpeedCfg = NULL;
					return TRUE;

				// default: break;
			}	// end of switch (wParam)
 		return TRUE;

		// default: break;
	}	// message case
	return FALSE;
}


/**************************************************************\
*
*   FUNCTION:	lpWndProcFPCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for front panel config sub-dialog
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS: NEED TO ADD CODE FOR SERIAL PORT OPTIONS
*			  NEED TO BUILD DIALOG BOX TEMPLATE
*
\**************************************************************/
BOOL CALLBACK lpWndProcFPCfg (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL apflag = FALSE;
	char buf[20], ini_file[_MAX_PATH];
	HWND hCtrl = NULL;
	int index = 0, slidepos = 0;


    strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);

	switch (iMsg) {
		case WM_INITDIALOG:
			// Set initial slider position
			hCtrl = GetDlgItem(hDlg, IDC_SLIDER_LED_BRIGHTNESS);
			SendMessage(hCtrl, TBM_SETRANGE, TRUE, MAKELONG( 1, 16 ));
			SendMessage(hCtrl, TBM_SETPOS, TRUE, iLEDlevel);
			CheckDlgButton(hDlg, IDC_CHK_FRONTPANEL, bFPsound);
			return TRUE;			// Let Windows set default focus

		case WM_KEYDOWN:
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;

		case WM_HSCROLL:
			hCtrl = GetDlgItem(hDlg, IDC_SLIDER_LED_BRIGHTNESS);
			iLEDlevel = SendMessage(hCtrl, TBM_GETPOS, 0, 0);
			FP_SetLedBrightness(simstate.hFrontPanel, iLEDlevel);
			return TRUE;

		case WM_COMMAND:
			switch (LOWORD(wParam)){
				case IDCAPPLY:
					apflag = TRUE; // don't fall through to cancel

					// fall through to IDOK

				case IDOK:
					sprintf(buf,"%i",iLEDlevel);					
					WritePrivateProfileString("FrontPanel", "ledlevel", buf, ini_file);
					bFPsound = IsDlgButtonChecked(hDlg, IDC_CHK_FRONTPANEL);
					sprintf(buf, "%i", bFPsound);
					WritePrivateProfileString("FrontPanel", "sound", buf, ini_file);

					if (apflag) { // Dont fall through on APPLY
						break; 
					}

					// fall through to IDCANCEL -

				case IDCANCEL:
					if (!Do_shutdown) {
						SendMessage(hwndOPTS, WM_COMMAND, IDCANCEL,0); // !!!
						return FALSE;
					}

					DestroyWindow(hDlg);
					hwndFPCfg = NULL;
				// break;

				// default: break;
			} //End switch LOWORD (wParam)
		// break;

		// default: break;
	} //End switch iMsg
	return FALSE;
}


/**************************************************************\
*
*   FUNCTION:	lpWndProcMiscCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for "miscellaneous" config dialog box.
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*
\**************************************************************/
BOOL CALLBACK lpWndProcMiscCfg (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL apflag=FALSE;
	char buf[20], ini_file[_MAX_PATH];


    strcpy(ini_file, winstate.szBaseDir);
    strcat(ini_file, g_ini_file);

	switch (iMsg) {
		case WM_INITDIALOG:
			CheckDlgButton(hDlg, IDC_BELL,Bell);
			CheckDlgButton(hDlg, IDC_WORDWRAP,WordWrap);
			CheckDlgButton(hDlg, IDC_ANSI,Ansi);
			CheckDlgButton(hDlg, IDC_DISKSND,Disksnd);
			CheckDlgButton(hDlg, IDC_FANSND,Fansnd);
			return TRUE;			// Let Windows set default focus

		case WM_KEYDOWN:
			// Process key messages. wParam is key code-lParam is message data
			switch (wParam){
				case VK_ESCAPE:
					return TRUE;	// force exit if ESC pressed
			}
			break;

		case WM_COMMAND:
			switch (LOWORD(wParam)){
				case IDCAPPLY:
					apflag = TRUE; // don't fall through to cancel
				// fall through to IDOK

				case IDOK:
					Bell=IsDlgButtonChecked(hDlg, IDC_BELL);
					WordWrap=IsDlgButtonChecked(hDlg, IDC_WORDWRAP);
					Ansi=IsDlgButtonChecked(hDlg, IDC_ANSI);
					Disksnd=IsDlgButtonChecked(hDlg, IDC_DISKSND);
					Fansnd=IsDlgButtonChecked(hDlg, IDC_FANSND);
			
					sprintf(buf, "%i", Bell);
					WritePrivateProfileString("MISC", "bell", buf, ini_file);
					sprintf(buf, "%i", WordWrap);
					WritePrivateProfileString("MISC", "wordwrap", buf, ini_file);
					sprintf(buf, "%i", Ansi);
					WritePrivateProfileString("MISC", "ansi", buf, ini_file);
					sprintf(buf, "%i", iLEDlevel);					
					WritePrivateProfileString("MISC", "ledlevel", buf, ini_file);
					sprintf(buf, "%i", Disksnd);
					WritePrivateProfileString("MISC", "disksnd", buf, ini_file);
					sprintf(buf, "%i", Fansnd);
					WritePrivateProfileString("MISC", "fansnd", buf, ini_file);

					if (apflag) { // Dont fall through on APPLY.
						break; 
					}

				// fall through to IDCANCEL -

				case IDCANCEL:
					if (!Do_shutdown) {
						SendMessage(hwndOPTS,WM_COMMAND,IDCANCEL,0);
						return FALSE;
					}

					DestroyWindow(hDlg);
					hwndMiscCfg = NULL;
				// break;

				// default: break;
			} //End switch LOWORD (wParam)
		// break;

		// default: break;
	} //End switch iMsg
	return FALSE;
}


#if 0
/**************************************************************\
*
*   FUNCTION:	lpWndProcLptCfg (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for printer options dialog box.
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
*	int LptCharSet is global
*
\**************************************************************/
BOOL CALLBACK lpWndProcLptCfg(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	BOOL apflag=FALSE;
	char buf[80];
	char *str;
	char *str2;
	static int T_lpt_charset = 0;


    switch (iMsg) {
		case WM_INITDIALOG:
			T_lpt_charset = lpt_charset;
			CheckRadioButton(hDlg, IDC_RADIO_MITS6, IDC_RADIO_ASCII7, T_lpt_charset);
		return TRUE;			// Let Windows set default focus

		case WM_COMMAND:
			switch (LOWORD(wParam)){
				case IDC_RADIO_MITS6:
				case IDC_RADIO_ASCII7:
					T_lpt_charset = (LOWORD(wParam));	// copy button ID
					CheckRadioButton(hDlg, IDC_RADIO_MITS6, IDC_RADIO_ASCII7, T_lpt_charset);
				return TRUE;

				case IDCAPPLY:
					apflag = TRUE;
				// fall through to IDOK

				case IDOK:
					lpt_charset = T_lpt_charset;

					switch (lpt_charset){
						case IDC_RADIO_MITS6:
							LptCharSet = 1;
							str = "MITS 6-bit";
							str2 = "MITS6";
							SYSCFG_WriteConfigInfo("printer", "charset", str2);
						break;

						case IDC_RADIO_ASCII7:
							LptCharSet = 0;
							str = "7-bit ASCII";
							str2 = "ASCII7";
							SYSCFG_WriteConfigInfo("printer", "charset", str2);
						break;

						default:
							LptCharSet = 0;
							str = "7-bit ASCII";
							str2 = "ASCII7";
							SYSCFG_WriteConfigInfo("printer", "charset", str2);
						// break;

						// default: break;
					}

					// Commit configuration to the INI file and alert user.
					wsprintf(buf, "Printer CharSet Mode changed to %s", str);
					Status_SetText (hwndStatusBar, STATBAR_READY, 0, buf);

					if (apflag) { // Dont fall through on APPLY.
						// apflag = FALSE; // FJS
						break;
					}

				// Fall through to IDCANCEL

				case IDCANCEL:
					DestroyWindow(hDlg);
					hwndPrinterCfg = NULL;
				return TRUE;

				// default: break;
			}	// end of switch (wParam)
 		return TRUE;

		// default: break;
	}	// message case
	return FALSE;
}
#endif


/**************************************************************\
*
*  FUNCTION:    Populate_ComboBox - Adds user choices to a combo box
*
*  INPUTS:      Handle to control, comma separated list of choices
*
*  RETURNS:     Nothing
*
*  COMMENTS:	There are probably better ways to do this.
*				Used in 88disk.c, too.
\**************************************************************/
void Populate_ComboBox(HWND hDlgList, char* List)
{
	char Llist[512]; 
	unsigned int index=0,last_index=0,lenth;
	

	lenth = strlen(List); 
	strcpy(Llist,List);
	for (index=0;index <= lenth;index++){
		if ((Llist[index] == ',') | (Llist[index] == 0)){
			Llist[index]=0;
			ComboBox_AddString(hDlgList, &Llist[last_index]); 
			last_index = (index+1);
		}
	}
	return;
}


static int Xtoi(char* String, short int convert)
{

	if (convert==0) return(hextoi(String));

	if (convert==1) return(atoi(String));

	return(StrToNum(String,_tcslen(String),8)); // octal default
}


int StrToNum(TCHAR *udata, int udatalen, int base)
{
	char *_strupr(char *d);
	long index;
	const TCHAR numdigits[] = TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
	long digitValue = 0;
	long RetVal = 0;
	TCHAR digits[sizeof(numdigits)+1];
	TCHAR *dataVal;
	TCHAR data[512];

	_tcscpy(data, udata); // copy the data to our variable
	_tcsupr(data); // convert it to upper case

	// copy the number of digits supported by base in digits -
	ZeroMemory(digits, sizeof(digits));
	_tcsncpy(digits, numdigits, base);

	for(index = 0; index < udatalen; index++)
	{
		// is the number there?
		dataVal = _tcschr(digits, data[index] );
		if (dataVal /* != NULL */ )
		{
			// if it is, subtract where to start point
			digitValue = (dataVal - digits);
			// increase Retval with digitvalue
			RetVal = RetVal * base + digitValue;
		}
	}
	// return the result
	return RetVal;
}


#if 0
/**************************************************************\
*
*   FUNCTION:	lpWndProcRomList (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for ROM list dialog box.
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
*		Application modal. 
*
\**************************************************************/
BOOL CALLBACK lpWndProcRomList(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	char buf[_MAX_FNAME];
	static char szFilter[] = "Binary ROM Files (.BIN)\0*.bin\0\0";
	HWND hDlgList = NULL;
	int i;
	int lItemID = 0l;		// 0-based index of selected item
	OPENFILENAME l_ofn;


    switch (iMsg) {
		case WM_INITDIALOG:
			// Fill list control with permanent ROMs
			hDlgList = GetDlgItem(hDlg, IDC_LIST_ROMFILES);
			for (i=0; i<MAXROMS; i++){
				if (load_rom[i].iUsed == 1){
					sprintf(buf, "ROM%d-%s", i, load_rom[i].szROMName);
					ListBox_AddString(hDlgList, buf);
					ListBox_SetItemData(hDlgList, i, i);
				} // End if
			} // End for
			return TRUE;			// Let Windows set default focus

		case WM_COMMAND:
			hDlgList = GetDlgItem(hDlg, IDC_LIST_ROMFILES);
			switch (LOWORD (wParam)){
				case ID_BUTTON_ADD:
					strcpy(szFileDir, winstate.szFilesDir);
					
					l_ofn.lStructSize       = sizeof (OPENFILENAME);
					l_ofn.hwndOwner         = hDlg;
					l_ofn.hInstance         = NULL;
					l_ofn.lpstrFilter       = szFilter;
					l_ofn.lpstrCustomFilter = NULL;
					l_ofn.nMaxCustFilter    = 0;
					l_ofn.nFilterIndex      = 0;
					l_ofn.lpstrFile         = winstate.szFileName;
					l_ofn.nMaxFile          = _MAX_PATH;
					l_ofn.lpstrFileTitle    = winstate.szTitleName;
					l_ofn.nMaxFileTitle     = _MAX_FNAME + _MAX_EXT;
				 	l_ofn.lpstrInitialDir   = szFileDir;		// module global
					l_ofn.lpstrTitle        = NULL;
					l_ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
					l_ofn.nFileOffset       = 0;
					l_ofn.nFileExtension    = 0;
					l_ofn.lpstrDefExt       = "bin";
					l_ofn.lCustData         = 0L;
					l_ofn.lpfnHook          = NULL;
					l_ofn.lpTemplateName    = NULL;
					l_ofn.lpstrFile         = winstate.szFileName; // full path
					l_ofn.lpstrFileTitle    = winstate.szTitleName; // short name

					/* GOFN returns zero on cancel. nFilterIndex is a 1-based index
					 * of the selected file filter. If nFilterIndex is 0 and 
					 * lpstrCustomFilter is NULL, the system sets the filter index to 
					 * the first filter.
					 *
					if (!GetOpenFileName (&l_ofn)) return TRUE;

					// Get starting location
					DialogBox(winstate.hInst, "AddrDlg", hDlg, (DLGPROC)lpWndProcRomInfo);

					// Write to INI file and copy data to load_rom. Return value is valid slot
					// number or -1 for failure.
					//i = AddRomToINI(winstate.szTitleName, wStartAddr);
					if (-1 == (i = AddRomToINI(winstate.szTitleName, winstate.szFileName, wStartAddr))){
						HOST_ThrowErrMsg("No more ROM slots available! Delete a ROM before attempting to add another.");
						return TRUE;
					}

					// Add item to list box
					sprintf(buf, "ROM%d-%s", i, load_rom[i].szROMName);
					ListBox_AddString(hDlgList, buf);
					//ListBox_AddString(hDlgList, load_rom[i].szROMName);
					ListBox_SetItemData(hDlgList, i, i);

					// reset globals
					winstate.szFileName[0] = 0;	// full path
					winstate.szTitleName[0] = 0;	// filename only
					wStartAddr = 0;
					return TRUE;

				case ID_BUTTON_REMOVE:
					// Trap item ID. If there is no selection, throw an error.
					if (LB_ERR == (lItemID = ListBox_GetCurSel(hDlgList))){
						OkMessage(hDlg, "No ROM was selected to remove!", winstate.szAppName);
						return TRUE;
					}

					// lItemID is data from list box (array index number) is used as handle
					// to delete ROM from INI file.
					//i = ListBox_GetItemData(hDlgList, lItemID);					
					//RemoveRomFromINI(load_rom[i].szROMName);
					RemoveRomFromINI(lItemID);					
					ListBox_DeleteString(hDlgList, lItemID);
					return TRUE;

 				case ID_BUTTON_MAP:
					/* Map ROM configuration. Dialog is ROMMAP and contains single edit
					 * control IDC_EDIT_MAPCTRL
					 * Generate usage map by looping through load_rom[] and show
					 * usage in the context of minrom/maxrom in 16-byte chunks
					 *
					if (!GenerateRomUsageMap(hDlg)){
						OkMessage (hDlg, "Unable to create ROM usage dump file!", winstate.szAppName);
					}
					return TRUE;

				case IDCANCEL:
					DestroyWindow(hDlg);
					hwndRomCfg = NULL;
					hDlgList = NULL;
					return TRUE;
			}	// end of switch (wParam)
 			return TRUE;
	}	// message case
	return FALSE;
}
#endif

#if 0
/**************************************************************\
*
*   FUNCTION:	lpWndProcRomInfo (HWND, UINT, WPARAM, LPARAM)
*
*   PURPOSE:	WndProc for dialog that prompts user for starting
*				address and filename. Called by RomManager dialog.
*
*   MESSAGES:
*
*		WM_INITDIALOG - initialize dialog box
*		WM_COMMAND    - Input received
*
*   COMMENTS:
*
*		Application modal. 
*
\**************************************************************/
BOOL CALLBACK lpWndProcRomInfo (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	char buf[10];

    switch (iMsg) {
		case WM_INITDIALOG:
			// Might consider centering dialog in window.
			return TRUE;			// Let Windows set default focus

		case WM_COMMAND:
			switch (LOWORD(wParam)){
				case IDOK:
					GetDlgItemText(hDlg, IDC_EDIT_ADDR, buf, 5);
					wStartAddr = (hextoi(buf) & 0xffff);
					EndDialog(hDlg, wParam);	// MODAL
					return TRUE;
			}	// end of switch (wParam)
 			return TRUE;
	}	// message case
	return FALSE;
}
#endif

/* end of file: syscfg.c */
