//
// (C) 2004 Mike Brent aka Tursi aka HarmlessLion.com
// This software is provided AS-IS. No warranty
// express or implied is provided.
//
// This notice defines the entire license for this code.
// All rights not explicity granted here are reserved by the
// author.
//
// You may redistribute this software provided the original
// archive is UNCHANGED and a link back to my web page,
// http://harmlesslion.com, is provided as the author's site.
// It is acceptable to link directly to a subpage at harmlesslion.com
// provided that page offers a URL for that purpose
//
// Source code, if available, is provided for educational purposes
// only. You are welcome to read it, learn from it, mock
// it, and hack it up - for your own use only.
//
// Please contact me before distributing derived works or
// ports so that we may work out terms. I don't mind people
// using my code but it's been outright stolen before. In all
// cases the code must maintain credit to the original author(s).
//
// -COMMERCIAL USE- Contact me first. I didn't make
// any money off it - why should you? ;) If you just learned
// something from this, then go ahead. If you just pinched
// a routine or two, let me know, I'll probably just ask
// for credit. If you want to derive a commercial tool
// or use large portions, we need to talk. ;)
//
// If this, itself, is a derived work from someone else's code,
// then their original copyrights and licenses are left intact
// and in full force.
//
// http://harmlesslion.com - visit the web page for contact info
//
//*****************************************************
//* Classic 99 - TI Emulator for Win32				  *
//* by M.Brent                                        *
//*                                                   *
//* Version QI3.5 -	20 Apr 06			              *
//*                                                   *
//* Thanks to:	Jeff Brown ('X' instruction)		  *
//*				Frank Palazolo, Ralph Nebet (speech)  *
//*			    Roland Meier (Disk)                   *
//*													  *
//* It should be noted that this Emulator is Dependant*
//* on running the original ROMs and expects to find  *
//* ROM routines at certain addresses. Custom ROMs    *
//* are very likely to act strangely or outright fail *
//* (actually, that may not be true except for paste) *
//*****************************************************

// Win32 Code for MSVC 6.0

// Scratchpad RAM is now at 0x8300
// any patches that want to access it directly (not through ROMWORD or RCPUBYTE)
// must take note of this or they will fail

#pragma warning (disable: 4113 4761 4101)

//#define WIN32_LEAN_AND_MEAN
//#define _WIN32_WINNT 0x0400

////////////////////////////////////////////
// Includes
////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
//#include <mmsystem.h>
#include <process.h>
#include <malloc.h>
#include <dsound.h>
#include <time.h>
#include <math.h>

#include "resource.h"
#include "tiemul.h"
#include "tms5220\5220intf.h"
#include "rs232_pio.h"

////////////////////////////////////////////
// Globals
// These don't all NEED to be globals, but I'm only cleaning up the code, 
// not re-writing it all from scratch.
////////////////////////////////////////////

// Win32 Stuff
HINSTANCE hInstance;						// global program instance
HINSTANCE hPrevInstance;					// prev instance (always null so far)

// speech (default 8000 and 4000)
#define SPEECHRATE 8000	
#define SPEECHBUFFER 4000
//FILE *DacLog;

HMODULE hSpeechDll;											// Handle to speech DLL
void (*SpeechInit)(Byte *pROM, int nRomLen, int BufLen,int SampleRate);	// Pointer to SpeechInit function
void (*SpeechStop)(void);									// Pointer to SpeechStop function
Byte (*SpeechRead)(void);									// Pointer to SpeechRead function
void (*SpeechWrite)(Byte b);								// Pointer to SpeechWrite function
void (*SpeechProcess)(Byte *pBuf, int nLen);				// Pointer to SpeechProcess function
DWORD sample_pos;											// used in the speech buffer code

// Memory
Byte CPU[65536];							// Main CPU Memory
Byte CPU2[8192];							// Cartridge space bank-switched (ROM >6000 space)
Byte GROM[65536];							// GROM space
Byte ROMMAP[65536];							// Write-protect map of CPU space
Byte CRU[4096];								// CRU space
Byte SPEECH[65536];							// Speech Synth ROM
Byte DSR[16][16384];						// 16 CRU bases, up to 16k each (ROM >4000 space)
int  nDSRBank[16];							// Is the DSR bank switched?

Byte PCODE[65536];							// P-Code GROM space
Word GRMADD;								// GROM Address counter
Byte grmaccess,grmdata;						// GROM Prefetch emulation
Word PCODEADD;								// GROM Address counter (P-code)
Byte pcodeaccess,pcodedata;					// GROM Prefetch emulation (P-code)

int  nSystem=1;			// Which system do we default to?
int  nCartGroup=0;		// Which cart group?
int	 nCart=-1;			// Which cart is loaded (-1=none)
struct DISKS *pMagicDisk=NULL;	// which magic disk is loaded?
bool fKeyEverPressed=false;	// used to suppress warning when changing cartridges
int  nLoadedUserCarts=0;

unsigned char DummyROM[6]={
	0x83, 0x00,			// >0000	reset vector, workspace
	0x00, 0x04,			// >0002	reset vector, address
	0x10, 0xff			// >0004	JMP @>0004
};

int KEYS[2][8][8]= {  
{
// Keyboard - 99/4
/* unused */	VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE,

/* 1 */			'N', 'H', 'U', '7', 'C', 'D', 'R', '4',
/* Joy 1 */		VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE,
/* 3 */			VK_OEM_PERIOD, 'K', 'O', '9', 'Z', 'A', 'W', '2',

/* Joy 2 */		VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE,
	
/* 5 */			'M', 'J', 'I', '8', 'X', 'S', 'E', '3',
/* 6 */			'B', 'G', 'Y', '6', 'V', 'F', 'T', '5',
/* 7 */			VK_RETURN, 'L', 'P', '0', VK_SHIFT, VK_SPACE, 'Q', '1'
},
{
// Keyboard - 99/4A
/* Joy 2 */		VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE,

/* 1 */			'M', 'J', 'U', '7', '4', 'F', 'R', 'V',					// MJU7 4FRV
/* 2 */			VK_OEM_2, VK_OEM_1, 'P', '0', '1', 'A', 'Q', 'Z',		// /;P0 1AQZ
/* 3 */			VK_OEM_PERIOD, 'L', 'O', '9', '2', 'S', 'W', 'X',		// .LO9 2SWX

/* Joy 1 */		VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE, VK_ESCAPE,
	
/* 5 */			VK_OEM_COMMA, 'K', 'I', '8', '3', 'D', 'E', 'C',		// ,KI8 3DEC
/* 6 */			'N', 'H', 'Y', '6', '5', 'G', 'T', 'B',					// NHY6 5GTB
/* 7 */			VK_OEM_PLUS, VK_SPACE, VK_RETURN, VK_ESCAPE, VK_LMENU, VK_LSHIFT, VK_LCONTROL, VK_ESCAPE 
}																		// = rx fscx
};

char key[256];										// keyboard state buffer

// Win32 Joysticks
//JOYINFOEX myJoy;
int fJoy;
int joy1mode, joy2mode;

// Audio
int need_byte;										// flag for whether waiting on a second byte
int last_byte;										// copy of last byte
int volume_mult;									// Volume multiplier
int nSampleIdx[3];									// Which sample (per voice)
bool fSampleShouldLoop[3];							// And whether to loop it
bool fVoiceSimulatedDAC[3];							// true if frequency is so high we should use DAC mode
int  DAC[3];										// actual DAC level (0 (silent) - 15 (loudest) )
LPDIRECTSOUND lpds;									// DirectSound handle
LPDIRECTSOUNDBUFFER voice[3];						// Voice buffers
LPDIRECTSOUNDBUFFER noise[8];						// Noise buffers
LPDIRECTSOUNDBUFFER speechbuf;						// speech structure

// Assorted
char qw[80];										// temp string
int quitflag;										// quit flag
char lines[30][DEBUGLEN];							// debug lines
Word disasm[20];									// last 20 addresses for disasm
int bank=0;								// Cartridge bank switch
int xb = 0;											// Is second bank (XB) loaded?
int nCurrentDSR=-1;									// Which DSR Bank are we on?
unsigned int index1;								// General counter variable
int drawspeed=0;									// flag used in display updating
int max_cpf=DEFAULT_CPF;							// Maximum cycles per frame
int oldmax=DEFAULT_CPF;									// copy of same
int idling=0;										// Set if an IDLE has occured
int speedup_float = 0;								// speedup floating point operations
int simulate_int = 0;								// simulate console interrupt (faster)
int slowdown_keyboard = 1;							// slowdown keyboard autorepeat in the GROM code
int cpucount=0, cpuframes=0;							// CPU counters for timing
int last_count=0;									// Separates video redraw and video interrupts
int timercount=0;										// Used to estimate runtime

int timer9901=0;									// 9901 interrupt timer
int starttimer9901=0;								// and it's set time
int keyboard=1;										// keyboard map (0=99/4, 1=99/4A)

int retrace_count=0;								// count on the 60hz timer

int PauseInactive;									// what to do when the window is inactive
int SpeechEnabled;									// whether speech is enabled
int CPUThrottle;									// Whether or not the CPU is throttled

time_t STARTTIME, ENDTIME;
long ticks;

ATOM myClass;										// Window Class
HWND myWnd, memWnd, regWnd, asmWnd, dbgWnd;			// Handle to windows
HDC myDC;											// Handle to Device Context
int fontX, fontY;									// Non-proportional font x and y size
int wndXtrim=0, wndYtrim=0;							// Window trim, used for sizing

char AVIFileName[256]="C:\\TI99AVI.AVI";			// AVI Filename

char *PasteString;									// Used for Edit->Paste
char *PasteIndex;
int PasteCount;

char diskpath[10][256];								// path to disk directories or images
char disktype[11];									// i=image, v=files with v9t9 header
													// t=files with TIFILES header,
													// r=raw program files without header
													// (load will attempt to autodetect)
unsigned long myThread;								// timer thread
HANDLE MyMutex;										// Synchronization mutex

// Console Interrupt Routine Simulator
int num_sprites;									// Number of active sprites
int y_speed;										// X pixel speed
int x_speed;										// Y pixel speed
int t1, t1a, t1b, t2, t2a, t2b;						// temp variables
int sprite_y, sprite_x, offset;						// Sprite location
int ty, tx;											// Temp location
int snd_address;									// Sound chip data address
Byte *sndlist;										// The list itself
Byte data;											// temp data

extern const char *pCurrentHelpMsg;
extern int VDPDebug;
extern int nCycleCount;
extern int TVScanLines;

// 22khz buffer, 1 seconds worth
#define AUDIO_BUFFER_SIZE 22050
#define INIFILE ".\\classic99.ini"

///////////////////////////////////
// Built-in Cart library
///////////////////////////////////

// ROMs to always load
struct IMG AlwaysLoad[] = {
	{	IDR_AMI99DSK,	0x1100, 0x0130,	TYPE_DSR	},
	{	IDR_RS232,		0x1300, 0x0900, TYPE_DSR	},
	{	IDR_SPCHROM,	0x0000,	0x8000,	TYPE_SPEECH	},
	{	IDR_PGROM,		0x0000, 0xF800, TYPE_PCODEG },
};

// Extra files to support certain cartridges
// These files, when the list is loaded, can be loaded as if they were
// on the disk without the disk actually needing it
// Currently these can only be loaded program image files!
struct DISKS Disk_EA[] = {
	{	"DSK1.ASSM1",	IDR_ASSM1	},
	{	"DSK1.ASSM2",	IDR_ASSM2	},
	{	"DSK1.EDIT1",	IDR_EDIT1	},
	{	"",				0			},
};

struct DISKS Disk_SSA[] = {
	{	"DSK1.ACER_C",	IDR_ACERC	},
	{	"DSK1.ACER_P",	IDR_ACERP	},
	{	"DSK1.SSD",		IDR_SSD		},
	{	"DSK1.SSE",		IDR_SSE		},
	{	"",				0			},
};

struct DISKS Disk_Tunnels[] = {
	{	"DSK1.PENNY",	IDR_PENNY	},
	{	"DSK1.QUEST",	IDR_QUEST	},

	{	"",				0			},
};

// Actual cartridge definitions (broken into categories)
struct CARTS Users[100];		// these are loaded dynamically

struct CARTS Systems[] = {
	// very slow to start - messes with the 9901? Sometimes fails
	{	"TI-99/4",	{	
						{	IDR_CON4R0,		0x0000, 0x2000,	TYPE_ROM	},

						{	IDR_CON4G0,		0x0000, 0x2000,	TYPE_GROM	},
						{	IDR_CON4G1,		0x2000,	0x2000,	TYPE_GROM	},
						{	IDR_CON4G2,		0x4000,	0x2000,	TYPE_GROM	},
					},
		NULL,
		NULL
	},

	{	"TI-99/4A",	{	{	IDR_994AGROM,	0x0000, 0x6000,	TYPE_GROM	},
						{	IDR_994AROM,	0x0000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000, TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"TI-99/4A V2.2",{{	IDR_CON22R0,	0x0000, 0x2000,	TYPE_ROM	},
						{	IDR_CON22G0,	0x0000,	0x2000,	TYPE_GROM	},
						{	IDR_CON22G1,	0x2000,	0x2000,	TYPE_GROM	},
						{	IDR_CON22G2,	0x4000,	0x2000,	TYPE_GROM	},
					},
		NULL,
		NULL
	},
};

struct CARTS Apps[] = {
	{	"Demonstration",{{	IDR_DEMOG,		0x6000, 0x8000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Diagnostics",{	{	IDR_DIAGNOSG,	0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		"The maintenance tests are intended for use with external hardware, and are not supported. They will hang the emulator. Use File->Reset to bring it back."
	},
						
	{	"Editor/Assembler",{{IDR_TIEAG,		0x6000,	0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		Disk_EA,
		"The Editor and Assembler files are built-in. It is not currently possible to use the assembler due to incomplete disk emulation."
	},

	{	"Extended BASIC",{{	IDR_TIEXTG,		0x6000,	0x8000,	TYPE_GROM	},
						{	IDR_TIEXTC,		0x6000,	0x2000,	TYPE_ROM	},
						{	IDR_TIEXTD,		0x6000,	0x2000,	TYPE_XB		},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Home Finance",{{	IDR_HOMEG,		0x6000, 0x4000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Mini Memory",{	{	IDR_MINIMEMG,	0x6000, 0x2000,	TYPE_GROM	},
						{	IDR_MINIMEMC,	0x6000,	0x1000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		"The Line-By-Line assembler is not yet available."
	},

	{	"P-Code Card",{	{	IDR_PCODEC,		0x1F00,	0x2000,	TYPE_DSR	},
						{	IDR_PCODED,		0x1F00, 0x2000,	TYPE_DSR2	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		"This device does not work at all yet. Select another cartridge to restore the system."
	},

	{	"Terminal Emulator 2",{{IDR_TE2G,	0x6000, 0xA000,	TYPE_GROM	},
						{	IDR_TE2C,		0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		"Serial is not supported yet."
	},

	{	"TI Logo ][",{	{	IDR_LOGOG,		0x6000, 0x6000,	TYPE_GROM	},
						{	IDR_LOGOC,		0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
};

struct CARTS Games[] = {
	{	"Alpiner",	{	{	IDR_ALPINERG,	0x6000, 0x8000,	TYPE_GROM	},
						{	IDR_ALPINERC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
	
	{	"A-Maze-Ing",{	{	IDR_AMAZEG,		0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"BlackJack&&Poker",{{IDR_BLACKJACK,	0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Car Wars",	{	{	IDR_CARWARS,	0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Chisholm Trail",{{	IDR_CHISHOLMG,	0x6000, 0x2000,	TYPE_GROM	},
						{	IDR_CHISHOLMC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Football",{	{	IDR_FOOTBALLG,	0x6000, 0x4000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Hustle",	{	{	IDR_HUSTLEG,	0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Hunt the Wumpus",{{IDR_WUMPUSG,	0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
 
	{	"Mind Challengers",{{IDR_MINDG,		0x6000, 0x2000,	TYPE_GROM	},
						{	0,				0x6000,	0x2000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Munch Man",{	{	IDR_MUNCHMNG,	0x6000, 0x2000,	TYPE_GROM	},
						{	IDR_MUNCHMNC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
	
	{	"Parsec",	{	{	IDR_PARSECG,	0x6000, 0x6000,	TYPE_GROM	},
						{	IDR_PARSECC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},


	{	"Super Space Acer",{{IDR_SSALOAD,	0x6000, 0x0030,	TYPE_ROM	},
						{	IDR_DEMQ,		0x2000,	0x0706,	TYPE_RAM	},
						{	IDR_SSARAM,		0xA000,	0x5A28,	TYPE_RAM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		Disk_SSA,
		NULL
	},

	{	"TI Invaders",{	{	IDR_TIINVADG,	0x6000, 0x8000,	TYPE_GROM	},
						{	IDR_TIINVADC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
	
	{	"Tombstone City",{{	IDR_TOMBCITG,	0x6000, 0x2000,	TYPE_GROM	},
						{	IDR_TOMBCITC,	0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},

	{	"Tunnels of Doom",{{ IDR_TUNDOOMG,	0x6000,	0xA000,	TYPE_GROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		Disk_Tunnels,
		"Select DSK1, and PENNY for introductory quest, or QUEST for a full quest."
	},

	{	"Video Chess",{	{	IDR_CHESSG,		0x6000, 0x8000,	TYPE_GROM	},
						{	IDR_CHESSC,		0x6000,	0x2000,	TYPE_ROM	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
						{	0,				0x0000,	0x0000,	TYPE_NONE	},
					},
		NULL,
		NULL
	},
};

// Configuration access
void ReadConfig() {
	int idx,idx2,idx3;

	for (idx=0; idx<3; idx++) {
		fVoiceSimulatedDAC[idx]=false;
	}

#if 0
	// type of tone: 0-9 (0=default square)
	nSampleIdx[0]=			GetPrivateProfileInt("audio",	"sampleidx0",	nSampleIdx[0],				INIFILE);
	nSampleIdx[1]=			GetPrivateProfileInt("audio",	"sampleidx1",	nSampleIdx[1],				INIFILE);
	nSampleIdx[2]=			GetPrivateProfileInt("audio",	"sampleidx2",	nSampleIdx[2],				INIFILE);
	fSampleShouldLoop[0]=(	GetPrivateProfileInt("audio",	"sampleloop0",	fSampleShouldLoop[0]?1:0,	INIFILE)!=0);
	fSampleShouldLoop[1]=(	GetPrivateProfileInt("audio",	"sampleloop1",	fSampleShouldLoop[1]?1:0,	INIFILE)!=0);
	fSampleShouldLoop[2]=(	GetPrivateProfileInt("audio",	"sampleloop2",	fSampleShouldLoop[2]?1:0,	INIFILE)!=0);
	// Volume multiplier
	volume_mult=			GetPrivateProfileInt("audio",	"volume_mult",	volume_mult,				INIFILE);

	// Disk paths
	for (idx=0; idx<10; idx++) {
		char buf[128];
		sprintf(buf, "dskpath%d", idx);
		// Path to the disk files - usually relative but not required
		GetPrivateProfileString("disk", buf, diskpath[idx],	diskpath[idx], 256, INIFILE);
	}
	// type of files in folder - soon to be deprecated: T-TIFILES, V-V9T9 files, I-DSK image, R-Raw files
	// (Only TIFILES and V9T9 files are written, raw support to be removed eventually)
	// (One char per disk, 10 chars max)
	GetPrivateProfileString("disk",	"types", disktype, disktype, 11, INIFILE);

	// Filename used to write recorded video
	GetPrivateProfileString("emulation", "AVIFilename", AVIFileName, AVIFileName, 256, INIFILE);
	// CPU Throttling? 0-no, 1-yes
	CPUThrottle=	GetPrivateProfileInt("emulation",	"cputhrottle",			CPUThrottle,	INIFILE);
	// Proper CPU throttle (cycles per frame) - ipf is deprecated
	max_cpf=		GetPrivateProfileInt("emulation",	"maxcpf",				max_cpf,		INIFILE);
	// Pause emulator when window inactive: 0-no, 1-yes
	PauseInactive=	GetPrivateProfileInt("emulation",	"pauseinactive",		PauseInactive,	INIFILE);
	// Disable speech cause it sucks at the moment
	SpeechEnabled=  GetPrivateProfileInt("emulation",   "speechenabled",         SpeechEnabled,  INIFILE);
	// Simulation of the floating point functions in the ROM: 0-no, 1-yes
	speedup_float=	GetPrivateProfileInt("emulation",	"speedup_float",		speedup_float,	INIFILE);
	// Simulate the console interrupt (faster): 0-no, 1-yes
	simulate_int=	GetPrivateProfileInt("emulation",	"speedup_interrupt",	simulate_int,	INIFILE);
	// Get system type: 0-99/4, 1-99/4A, 2-99/4Av2.2
	nSystem=		GetPrivateProfileInt("emulation",	"system",				nSystem,		INIFILE);
	// Read flag for slowing keyboard repeat: 0-no, 1-yes
	slowdown_keyboard=GetPrivateProfileInt("emulation",	"slowdown_keyboard",	slowdown_keyboard, INIFILE);

	// Joystick active: 0 - off, 1 on
	fJoy=		GetPrivateProfileInt("joysticks", "active",		fJoy,		INIFILE);
	// 0-keyboard, 1-PC joystick 1, 2-PC joystick 2
	joy1mode=	GetPrivateProfileInt("joysticks", "joy1mode",	joy1mode,	INIFILE);
	joy2mode=	GetPrivateProfileInt("joysticks", "joy2mode",	joy2mode,	INIFILE);

	// Cartridge group loaded (0-apps, 1-games, 2-user)
	nCartGroup=	GetPrivateProfileInt("roms",	"cartgroup",	nCartGroup,	INIFILE);
	// Cartridge index (depends on group)
	nCart=		GetPrivateProfileInt("roms",	"cartidx",		nCart,		INIFILE);
#endif
	// User cartridges
	nLoadedUserCarts=0;
	idx=0;
	idx2=0;
#if 0
	for (idx=0; idx<100; idx++) {
		char buf[256], buf2[256];

		sprintf(buf, "UserCart%d", idx);
		GetPrivateProfileString(buf, "name", "", Users[idx2].szName, 64, INIFILE);
		if (strlen(Users[idx2].szName) > 0) {
			Users[idx2].pDisk=NULL;
			GetPrivateProfileString(buf, "message", "", buf2, 256, INIFILE);
			if (strlen(buf2) > 0) {
				Users[idx2].szMessage=strdup(buf2);		// this memory will leak!
			} else {
				Users[idx2].szMessage=NULL;
			}
			for (idx3=0; idx3<4; idx3++) {
				char buf3[1024];

				sprintf(buf2, "ROM%d", idx3);
				// line is formatted, except filename which finishes the line
				// T|AAAA|LLLL|filename
				Users[idx2].Img[idx3].dwImg=NULL;
				GetPrivateProfileString(buf, buf2, "", buf3, 1024, INIFILE);
				if (strlen(buf3) > 0) {
					if (4 != sscanf(buf3, "%c|%x|%x|%s", 
						&Users[idx2].Img[idx3].nType,
						&Users[idx2].Img[idx3].nLoadAddr,
						&Users[idx2].Img[idx3].nLength,
						&Users[idx2].Img[idx3].szFileName)) {
							sprintf(buf3, "INI File error reading %s in %s", buf2, buf);
							MessageBox(NULL, buf3, "Classic99 Error", MB_OK);
							goto skiprestofuser;
					}
					// this doesn't read correctly?
					Users[idx2].Img[idx3].nType=buf3[0];
				}
			}

			idx2++;
		}
	}
	nLoadedUserCarts=idx2;
skiprestofuser:
	// video filter mode
	FilterMode=		GetPrivateProfileInt("video",	"FilterMode",		FilterMode,		INIFILE);
	// essentially frameskip
	drawspeed=		GetPrivateProfileInt("video",	"frameskip",		drawspeed,		INIFILE);
	// graphics mode used for full screen direct X (see SetupDirectDraw() in tivdp.cpp)
	FullScreenMode=	GetPrivateProfileInt("video",	"fullscreenmode",	FullScreenMode, INIFILE);
	// set interrupt rate - 50/60
	hzRate=			GetPrivateProfileInt("video",	"hzRate",			hzRate,			INIFILE);
	// whether to force correct aspect ratio
	MaintainAspect=	GetPrivateProfileInt("video",	"MaintainAspect",	MaintainAspect, INIFILE);
	// 0-none, 1-DIB, 2-DX, 3-DX Full
	StretchMode=	GetPrivateProfileInt("video",	"StretchMode",		StretchMode,	INIFILE);

	// TV stuff
	TVScanLines=	GetPrivateProfileInt("tvfilter","scanlines",		TVScanLines,	INIFILE);
	double thue, tsat, tcont, tbright, tsharp, tmp;
	tmp=			GetPrivateProfileInt("tvfilter","hue",				100,			INIFILE);
	thue=(tmp-100)/100.0;
	tmp=			GetPrivateProfileInt("tvfilter","saturation",		100,			INIFILE);
	tsat=(tmp-100)/100.0;
	tmp=			GetPrivateProfileInt("tvfilter","contrast",			100,			INIFILE);
	tcont=(tmp-100)/100.0;
	tmp=			GetPrivateProfileInt("tvfilter","brightness",		100,			INIFILE);
	tbright=(tmp-100)/100.0;
	tmp=			GetPrivateProfileInt("tvfilter","sharpness",		100,			INIFILE);
	tsharp=(tmp-100)/100.0;
	SetTVValues(thue, tsat, tcont, tbright, tsharp);
#endif
}

// Wrapper function - not available in Win32?
void WritePrivateProfileInt(LPCTSTR lpApp, LPCTSTR lpKey, int nVal, LPCTSTR lpFile) {
	char buf[256];

	sprintf(buf, "%d", nVal);
	//WritePrivateProfileString(lpApp, lpKey, buf, lpFile);
}

void SaveConfig() {
#if 0
	int idx;

	WritePrivateProfileInt(		"audio",		"sampleidx0",			nSampleIdx[0],				INIFILE);
	WritePrivateProfileInt(		"audio",		"sampleidx1",			nSampleIdx[1],				INIFILE);
	WritePrivateProfileInt(		"audio",		"sampleidx2",			nSampleIdx[2],				INIFILE);
	WritePrivateProfileInt(		"audio",		"sampleloop0",			fSampleShouldLoop[0]?1:0,	INIFILE);
	WritePrivateProfileInt(		"audio",		"sampleloop1",			fSampleShouldLoop[1]?1:0,	INIFILE);
	WritePrivateProfileInt(		"audio",		"sampleloop2",			fSampleShouldLoop[2]?1:0,	INIFILE);
	WritePrivateProfileInt(		"audio",		"volume_mult",			volume_mult,				INIFILE);

	for (idx=0; idx<10; idx++) {
		char buf[128];
		sprintf(buf, "dskpath%d", idx);
		WritePrivateProfileString("disk",		buf,					diskpath[idx],				INIFILE);
	}
	WritePrivateProfileString(	"disk",			"types",				disktype,					INIFILE);

	WritePrivateProfileString(	"emulation",	"AVIFilename",			AVIFileName,				INIFILE);
	WritePrivateProfileInt(		"emulation",	"cputhrottle",			CPUThrottle,				INIFILE);
	if (0 != max_cpf) {
		WritePrivateProfileInt(		"emulation",	"maxcpf",				max_cpf,					INIFILE);
	}
	WritePrivateProfileInt(		"emulation",	"pauseinactive",		PauseInactive,				INIFILE);
	WritePrivateProfileInt(     "emulation",    "speechenabled",        SpeechEnabled,              INIFILE);
	WritePrivateProfileInt(		"emulation",	"speedup_float",		speedup_float,				INIFILE);
	WritePrivateProfileInt(		"emulation",	"speedup_interrupt",	simulate_int,				INIFILE);
	WritePrivateProfileInt(		"emulation",	"system",				nSystem,					INIFILE);
	WritePrivateProfileInt(		"emulation",	"slowdown_keyboard",	slowdown_keyboard,			INIFILE);

	WritePrivateProfileInt(		"joysticks",	"active",				fJoy,						INIFILE);
	WritePrivateProfileInt(		"joysticks",	"joy1mode",				joy1mode,					INIFILE);
	WritePrivateProfileInt(		"joysticks",	"joy2mode",				joy2mode,					INIFILE);

	WritePrivateProfileInt(		"roms",			"cartgroup",			nCartGroup,					INIFILE);
	WritePrivateProfileInt(		"roms",			"cartidx",				nCart,						INIFILE);
	
	WritePrivateProfileInt(		"video",		"FilterMode",			FilterMode,					INIFILE);
	WritePrivateProfileInt(		"video",		"frameskip",			drawspeed,					INIFILE);
	WritePrivateProfileInt(		"video",		"fullscreenmode",		FullScreenMode,				INIFILE);
	WritePrivateProfileInt(		"video",		"hzRate",				hzRate,						INIFILE);
	WritePrivateProfileInt(		"video",		"MaintainAspect",		MaintainAspect,				INIFILE);
	WritePrivateProfileInt(		"video",		"StretchMode",			StretchMode,				INIFILE);

	// TV stuff
	double thue, tsat, tcont, tbright, tsharp;
	int tmp;
	GetTVValues(&thue, &tsat, &tcont, &tbright, &tsharp);

	tmp=(int)((thue+1.0)*100.0);
	WritePrivateProfileInt(		"tvfilter",		"hue",					tmp,						INIFILE);
	tmp=(int)((tsat+1.0)*100.0);
	WritePrivateProfileInt(		"tvfilter",		"saturation",			tmp,						INIFILE);
	tmp=(int)((tcont+1.0)*100.0);
	WritePrivateProfileInt(		"tvfilter",		"contrast",				tmp,						INIFILE);
	tmp=(int)((tbright+1.0)*100.0);
	WritePrivateProfileInt(		"tvfilter",		"brightness",			tmp,						INIFILE);
	tmp=(int)((tsharp+1.0)*100.0);
	WritePrivateProfileInt(		"tvfilter",		"sharpness",			tmp,						INIFILE);
	WritePrivateProfileInt(		"tvfilter",		"scanlines",			TVScanLines,				INIFILE);
#endif
}

///////////////////////////////////
// Main
// Startup and shutdown system
///////////////////////////////////
int classic99_WinMain( char *filename, int imageType)
{
	int idx;
	int err;
	char temp[255];
	//WNDCLASS aclass;
	//TEXTMETRIC myMetric;
	//RECT myrect, myrect2;

	//hInstance = hInst;
	//hPrevInstance=hInPrevInstance;

	// Null the pointers
	myClass=0;
	myWnd=NULL;		// Classic99 Window
	memWnd=NULL;	// Memory Window
	regWnd=NULL;	// Register Window
	asmWnd=NULL;	// Assembly Window
	dbgWnd=NULL;	// Debug Window
	lpds=NULL;
	voice[0]=NULL;
	voice[1]=NULL;
	voice[2]=NULL;
	PasteString=NULL;
	PasteIndex=NULL;
	PasteCount=0;
	ZeroMemory(Users, sizeof(Users));
	nLoadedUserCarts=0;

#if 0
	// Get the default np font dimensions with a dummy dc
	myDC=CreateCompatibleDC(NULL);
	SelectObject(myDC, GetStockObject(ANSI_FIXED_FONT));
	if (GetTextMetrics(myDC, &myMetric)) {
		fontX=myMetric.tmMaxCharWidth;
		fontY=myMetric.tmHeight;
	} else {
		fontX=20;
		fontY=20;
	}
	DeleteDC(myDC);
#endif

	for (idx=0; idx<8; idx++) {
		noise[idx]=NULL;
	}

	framedata=(unsigned short*)malloc((256+16)*(192+16)*2);	// This is where we draw everything - 8 pixel border
	//framedata2=(unsigned short*)malloc((512+32)*(384+32)*2);// used for the 2xSaI filters - 16 pixel border (x2)

	MyMutex=CreateMutex(NULL, false, NULL);

#if 0
	// create and register a class and open a window
	if (NULL == hPrevInstance)
	{
		aclass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
		aclass.lpfnWndProc = myproc;
		aclass.cbClsExtra = 0;
		aclass.cbWndExtra = 0;
		aclass.hInstance = hInstance;
		aclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
		aclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
		aclass.hbrBackground = NULL;
		aclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
		aclass.lpszClassName = "TIWndClass";
		myClass = RegisterClass(&aclass);
		if (0 == myClass)
		{	
			err=GetLastError();
			sprintf(temp, "Can't create class: 0x%x", err);
			fail(temp);
		}

		// Info windows
		aclass.lpszClassName="Classic99Info";
		aclass.hbrBackground=NULL;
		aclass.lpszMenuName=NULL;
		myClass=RegisterClass(&aclass);
		if (0 == myClass) {
			debug_write("Couldn't register debug window class: 0x%x", GetLastError());
		}
	}

	myWnd = CreateWindow("TIWndClass", "Classic99", WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE, CW_USEDEFAULT, 0, 536, 446, NULL, NULL, hInstance, NULL);
	if (NULL == myWnd)
	{	
		err=GetLastError();
		sprintf(temp, "Can't open window: %x", err);
		fail(temp);
	}

	// Fill in the menus
	HMENU hMenu;
	// Systems
	hMenu=GetMenu(myWnd);
	if (hMenu) {
		hMenu=GetSubMenu(hMenu, 2);
		if (hMenu) {
			for (idx=0; idx<sizeof(Systems)/sizeof(struct CARTS); idx++) {
				AppendMenu(hMenu, MF_STRING, ID_SYSTEM_0+idx, Systems[idx].szName);
			}
			DeleteMenu(hMenu, 0, MF_BYPOSITION);	// remove temp separator
		}
	}
	// Apps
	hMenu=GetMenu(myWnd);
	if (hMenu) {
		hMenu=GetSubMenu(hMenu, 3);
		if (hMenu) {
			hMenu=GetSubMenu(hMenu, 0);
			if (hMenu) {
				for (idx=0; idx<sizeof(Apps)/sizeof(struct CARTS); idx++) {
					AppendMenu(hMenu, MF_STRING, ID_APP_0+idx, Apps[idx].szName);
				}
				DeleteMenu(hMenu, 0, MF_BYPOSITION);	// remove temp separator
			}
		}
	}
	// Games
	hMenu=GetMenu(myWnd);
	if (hMenu) {
		hMenu=GetSubMenu(hMenu, 3);
		if (hMenu) {
			hMenu=GetSubMenu(hMenu, 1);
			if (hMenu) {
				for (idx=0; idx<sizeof(Games)/sizeof(struct CARTS); idx++) {
					AppendMenu(hMenu, MF_STRING, ID_GAME_0+idx, Games[idx].szName);
				}
				DeleteMenu(hMenu, 0, MF_BYPOSITION);	// remove temp separator
			}
		}
	}
	ShowWindow(myWnd, SW_SHOWNORMAL);

	// Get window trimming size
	GetWindowRect(myWnd, &myrect);
	GetClientRect(myWnd, &myrect2);
	wndXtrim=((myrect.right - myrect.left)-(myrect2.right - myrect2.left))-1;
	wndYtrim=((myrect.bottom - myrect.top)-(myrect2.bottom - myrect2.top))-fontY;
#endif

	grmaccess=0;		// No GROM Access yet
	pcodeaccess=0;		// No pcode grom access yet
	vdpaccess=0;		// No VDP address writes yet 
	vdpprefetch=0;		// Not really accurate, but eh
	tmpVDPADD=0;		// part of the vdpaccess
	interrupt_needed=0;	// No interrupt missed yet
	quitflag=0;			// no quit yet
	nCurrentDSR=-1;		// no DSR selected
	memset(nDSRBank, 0, sizeof(nDSRBank));

	// clear debugging strings
	memset(lines, 0, sizeof(lines));
	memset(disasm, 0, sizeof(disasm));

	//DacLog=fopen("C:\\daclog.raw", "wb");

	// Print some initial debug
	//debug_write("---");
	//debug_write("Classic99 version %s (C)2002-2006 M.Brent", VERSION);
	//debug_write("ROM files included under license from Texas Instruments");

	// Set default values for config (alphabetized here)
	strcpy(AVIFileName, "D:\\Classic99.AVI");	// default movie filename
	nCartGroup=0;				// Cartridge group (0-apps, 1-games, 2-user)
	nCart=-1;					// loaded cartridge (-1 is none)
	CPUThrottle=1;				// throttle the CPU
	drawspeed=0;				// no frameskip
	FilterMode=0;				// super 2xSAI
	FullScreenMode=6;			// full screen at 640x480x16
	fJoy=1;						// enable joysticks
	joy1mode=0;					// keyboard
	joy2mode=1;					// joystick 1
	hzRate=60;					// 60 hz
	MaintainAspect=1;			// Keep aspect ratio
	max_cpf=DEFAULT_CPF;		// max cycles per frame
	PauseInactive=0;			// don't pause when window inactive
	SpeechEnabled=1;			// on for now, though it's imperfect
	Recording=0;				// not recording AVI
	nSampleIdx[0]=0;			// square wave
	nSampleIdx[1]=0;			// square wave
	nSampleIdx[2]=0;			// square wave
	fSampleShouldLoop[0]=true;	// default for square wave
	fSampleShouldLoop[1]=true;	// default for square wave
	fSampleShouldLoop[2]=true;	// default for square wave
	simulate_int=0;				// don't use interrupt simulator
	speedup_float=0;			// don't use float simulator
	slowdown_keyboard=1;		// slow down keyboard repeat when read via BASIC
	StretchMode=2;				// dx
	TVScanLines=1;				// on by default
	nSystem=1;					// TI-99/4A
	volume_mult=-200;			// Default volume attenuation

	// Default to only DSK1 setup as a folder
	ZeroMemory(diskpath, sizeof(diskpath));
	strcpy(disktype, "..........");		// must be 10 dots
	strcpy(&diskpath[1][0], "D:\\DISKFILES\\");
	disktype[1] = 'T';

	// Read configuration - uses above settings as default!
	ReadConfig();

	switch( imageType )
	{
		case 0 : //cart
		{
			char *fptr ;
			int imagenum = 0 ;
			FILE *infile ;
			int filesize = 0 ;

			fptr = strrchr( filename, '.' ) ;

			if ( fptr )
				fptr-- ;
			else
				fptr = filename +strlen(filename)-1 ;

			*fptr = 'c' ;

			nCartGroup=2;				// Cartridge group (0-apps, 1-games, 2-user)
			nCart=0;					// loaded cartridge (-1 is none)

			infile = fopen( filename, "rb" ) ;

			if ( infile )
			{
				fclose( infile ) ;
				Users[0].Img[imagenum].nType = 'C' ;
				Users[0].Img[imagenum].nLoadAddr = 0x6000 ;
				Users[0].Img[imagenum].nLength = 0x2000 ;
				strcpy( Users[0].Img[imagenum].szFileName, filename ) ;
				imagenum++ ;
			}

			*fptr = 'd' ;

			infile = fopen( filename, "rb" ) ;

			if ( infile )
			{
				fclose( infile ) ;
				Users[0].Img[imagenum].nType = 'X' ;
				Users[0].Img[imagenum].nLoadAddr = 0x6000 ;
				Users[0].Img[imagenum].nLength = 0x2000 ;
				strcpy( Users[0].Img[imagenum].szFileName, filename ) ;
				imagenum++ ;
			}

			*fptr = 'g' ;

			infile = fopen( filename, "rb" ) ;

			if ( infile )
			{
				fseek( infile, 0, SEEK_END ) ;
				filesize = ftell(infile ) ;
				fclose( infile ) ;
				Users[0].Img[imagenum].nType = 'G' ;
				Users[0].Img[imagenum].nLoadAddr = 0x6000 ;
				Users[0].Img[imagenum].nLength = filesize ;
				strcpy( Users[0].Img[imagenum].szFileName, filename ) ;
				imagenum++ ;
			}

			break ;
		}
		case 1 : //program file
		default :
		{
			strcpy(&diskpath[1][0], filename ) ;
			if ( strrchr( &diskpath[1][0], '\\' ) )
				*(strrchr( &diskpath[1][0], '\\' )+1) = 0 ;
			nCartGroup=0;				// Cartridge group (0-apps, 1-games, 2-user)
			nCart=3;					// ext basic
			break ;
		}
	}

#if 0
	// Update user menu - this will be a function later
	hMenu=GetMenu(myWnd);
	if (hMenu) {
		hMenu=GetSubMenu(hMenu, 3);
		if (hMenu) {
			hMenu=GetSubMenu(hMenu, 2);
			if (hMenu) {
				// User ROMs are a bit different, since we have to read them
				// from the configuration
				for (idx=0; idx<nLoadedUserCarts; idx++) {
					AppendMenu(hMenu, MF_STRING, ID_USER_0+idx, Users[idx].szName);
				}
			}
		}
	}
#endif

	// set temp stuff
	oldmax=max_cpf;
	// Load a dummy CPU ROM for the emu to spin on till we load something real
	memcpy(CPU, DummyROM, 6);

#if 0
	// Set menu-based settings (lParam 1 means it's coming from here, not the user)
	// Only some messages care about that param, though
	PostMessage(myWnd, WM_COMMAND, ID_SYSTEM_0+nSystem, 1);
	PostMessage(myWnd, WM_COMMAND, ID_OPTIONS_CPUTHROTTLING, 1);
	PostMessage(myWnd, WM_COMMAND, ID_VIDEO_MAINTAINASPECT, 1);
	PostMessage(myWnd, WM_COMMAND, ID_VIDEO_FILTERMODE_NONE+FilterMode, 1);

	if ((StretchMode>=0)&&(StretchMode<=2)) {
		PostMessage(myWnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_NONE+StretchMode, 1);
	} else {
		if (StretchMode==3) {
			PostMessage(myWnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_DXFULL_320X240X8+FullScreenMode-1, 1);
		}
	}
	PostMessage(myWnd, WM_COMMAND, ID_VIDEO_50HZ, 1);
	PostMessage(myWnd, WM_COMMAND, ID_OPTIONS_PAUSEINACTIVE, 1);
	PostMessage(myWnd, WM_COMMAND, ID_OPTIONS_SPEECHENABLED, 1);

	if (nCart != -1) {
		switch (nCartGroup) {
		case 0:
			PostMessage(myWnd, WM_COMMAND, ID_APP_0+nCart, 1);
			break;

		case 1:
			PostMessage(myWnd, WM_COMMAND, ID_GAME_0+nCart, 1);
			break;

		case 2:
			PostMessage(myWnd, WM_COMMAND, ID_USER_0+nCart, 1);
			break;
		}
	}
#endif
	// build the CPU table
	debug_write("Building CPU");
	buildcpu();

	// start sound
	debug_write("Starting Sound");
	startsound();
	
	// Init disk
	debug_write("Starting Disk");
	do_files(3);		// TI DSR sets up VDP for 3 files

	// prepare the emulation...
	cpucount=0;
	cpuframes=0;
	timercount=0;

	// start the video processor
	debug_write("Starting Video");
	startvdp();

	// set up 60hz timer
	myThread=NULL;
	myThread=_beginthread((void (__cdecl *)(void*))TimerThread, 0, NULL);
	if (myThread != -1)
		debug_write("Thread began...");
	else
		debug_write("Thread failed.");

	Sleep(100);			// time for thread to start

	// begin emulation - returns when it's time to exit
	debug_write("Starting Emulation");
	readroms() ;
	emulti();

	// save out our config
	SaveConfig();

	// Fail is the full exit
	debug_write("Shutting down");
	fail("Normal Termination");

	// good bye
	return 0;
}



//////////////////////////////////////////////////////
// start up the sound system
//////////////////////////////////////////////////////
bool LoadResourceWAV(LPDIRECTSOUNDBUFFER *ppdSnd, int nResource, bool fCreateBuffer=true) {
	unsigned char *pData;
	//HRSRC hRsrc;
	//HGLOBAL hGlob;
	DSBUFFERDESC dsbd;
	WAVEFORMATEX pcmwf;
	unsigned int idx, idx2;
	UCHAR *ptr1, *ptr2;
	unsigned long len1, len2, len;
	char buf[80];
	char *filename ;
	FILE *infile ;
	unsigned long filesize ;

	ZeroMemory(&pcmwf, sizeof(pcmwf));
	//hRsrc=NULL;

	switch ( nResource )
	{
		case IDR_NOISE1 :
		{
			filename = "D:\\tones\\noise1.wav" ;
			break ;
		}
		case IDR_NOISE2:
		{
			filename = "D:\\tones\\noise2.wav" ;
			break ;
		}
		case IDR_NOISE3:
		{
			filename = "D:\\tones\\noise3.wav" ;
			break ;
		}
		case IDR_NOISE4:
		{
			filename = "D:\\tones\\noise4.wav" ;
			break ;
		}
		case IDR_NOISE5:
		{
			filename = "D:\\tones\\noise5.wav" ;
			break ;
		}
		case IDR_NOISE6:
		{
			filename = "D:\\tones\\noise6.wav" ;
			break ;
		}
		case IDR_NOISE7:
		{
			filename = "D:\\tones\\noise7.wav" ;
			break ;
		}
		case IDR_NOISE8:
		{
			filename = "D:\\tones\\noise8.wav" ;
			break ;
		}
		case IDR_FLUTE:
		{
			filename = "D:\\tones\\flutea440.wav" ;
			break ;
		}
		case IDR_OBOE:
		{
			filename = "D:\\tones\\oboea440.wav" ;
			break ;
		}
		case IDR_ORGAN:
		{
			filename = "D:\\tones\\organa440.wav" ;
			break ;
		}
		case IDR_PIANO:
		{
			filename = "D:\\tones\\pianoa440.wav" ;
			break ;
		}
		case IDR_SAX:
		{
			filename = "D:\\tones\\saxa440.wav" ;
			break ;
		}
		case IDR_STRING:
		{
			filename = "D:\\tones\\stringa440.wav" ;
			break ;
		}
		case IDR_TROMBONE:
		{
			filename = "D:\\tones\\trombonea440.wav" ;
			break ;
		}
		case IDR_VIOLIN:
		{
			filename = "D:\\tones\\violina440.wav" ;
			break ;
		}
		default :
		{
			filename = NULL ;
			break ;
		}

	}

	if ( filename == NULL )
		return false ;

	infile = fopen( filename, "rb" ) ;

	if ( infile == NULL )
		return false ;

	fseek( infile, 0, SEEK_END ) ;
	filesize = ftell( infile ) ;
	fseek( infile, 0, SEEK_SET );

	pData = (unsigned char*)malloc( filesize ) ;

	fread( pData, filesize, 1, infile ) ;
	fclose( infile ) ;

//	hRsrc=FindResource(NULL, MAKEINTRESOURCE(nResource), "WAVE"); 
	//if (hRsrc) {
		//hGlob=LoadResource(NULL, hRsrc);
		//if (NULL != hGlob) {
			//pData=(unsigned char*)LockResource(hGlob);
			if (NULL != pData) {
				// in a demonstration of bad programming, I'm just going to read the darn thing ;)
				// if it's not a WAV, you may be in trouble. Even if it IS a WAV, you may be. ;)
				// This is hard coded to the WAVs I inserted into the resources.

				// assume data chunk starts at 0x24
				idx2=0x2c;								// first byte of data

				// it's simple. I expect it to be a 22khz, 8 bit mono WAV with 1 data part
				// good luck with anything else
				len=pData[0x28]+(256*pData[0x29])+(65536*pData[0x2a]); // and we'll ignore the highest byte

				// we could read this data now instead of assuming?
				pcmwf.wFormatTag = WAVE_FORMAT_PCM;		// wave file
				pcmwf.nChannels=1;						// 1 channel (mono)
				pcmwf.nSamplesPerSec=22050;				// 22khz
				pcmwf.nBlockAlign=1;					// 1 byte per sample * 1 channel
				pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
				pcmwf.wBitsPerSample=8;					// 8 bit samples
				pcmwf.cbSize=0;							// always zero;

				ZeroMemory(&dsbd, sizeof(dsbd));
				dsbd.dwSize=sizeof(dsbd);
				dsbd.dwFlags=DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY ; //| DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS;
				dsbd.dwBufferBytes=len;
				dsbd.lpwfxFormat=&pcmwf;

				if (fCreateBuffer) {
					if (lpds->CreateSoundBuffer(&dsbd, ppdSnd, NULL) != DS_OK)
					{
						lpds->Release();
						return false;
					}
				}
		
				if ((*ppdSnd)->Lock(0, len, (void**)&ptr1, &len1, (void**)&ptr2, &len2, DSBLOCK_ENTIREBUFFER) == DS_OK)
				{
					// blank the buffer
					memset(ptr1, 0, len1);

					// since we haven't started the sound, hopefully the second pointer is nil
					if (len2 != 0) {
						//MessageBox(NULL, "Failed to lock sound buffer", "Classic99 Error", MB_OK);
					}

					memcpy(ptr1, &pData[idx2], min(len, len1));
				}
				(*ppdSnd)->Unlock(ptr1, len1, ptr2, len2);
				free( pData ) ;
				return true ;
			}
		//}
		//return true;
	//} else {
		return false;
	//}
}

void GenerateToneBuffer(int idx) {
	unsigned int idx2;
	UCHAR c;
	UCHAR *ptr1, *ptr2;
	unsigned long len1, len2, len;
	DSBUFFERDESC dsbd;
	WAVEFORMATEX pcmwf;

	if (NULL != voice[idx]) {
		voice[idx]->Stop();
		voice[idx]->Release();
	}

	// In case we need it below for square or sine waves, we'll fill in the structs
	ZeroMemory(&pcmwf, sizeof(pcmwf));
	pcmwf.wFormatTag = WAVE_FORMAT_PCM;		// wave file
	pcmwf.nChannels=1;						// 1 channel (mono)
	pcmwf.nSamplesPerSec= 22050;			// 22khz
	pcmwf.nBlockAlign=1;					// 1 byte per sample * 1 channel
	pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	pcmwf.wBitsPerSample=8;					// 8 bit samples
	pcmwf.cbSize=0;							// always zero;

	ZeroMemory(&dsbd, sizeof(dsbd));
	dsbd.dwSize=sizeof(dsbd);
	dsbd.dwFlags=DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY ; //| DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS;
	dsbd.dwBufferBytes=AUDIO_BUFFER_SIZE;	// the sample is AUDIO_BUFFER_SIZE bytes long
	dsbd.lpwfxFormat=&pcmwf;

	switch (nSampleIdx[idx]) {
	default:
	case 0:		// square wave
		if (lpds->CreateSoundBuffer(&dsbd, &voice[idx], NULL) != DS_OK)
		{
			lpds->Release();
			return;
		}
		
		// I have calculated that the square wav changed state every 25 samples
		if (voice[idx]->Lock(0, AUDIO_BUFFER_SIZE, (void**)&ptr1, &len1, (void**)&ptr2, &len2, DSBLOCK_ENTIREBUFFER) == DS_OK)
		{
			// since we haven't started the sound, hopefully the second pointer is null
			if (len2 != 0) {
				//MessageBox(NULL, "Failed to lock tone buffer", "Classic99 Error", MB_OK);
			}

			// square wave with a period of 50 samples (441hz - but I assume 440 throughout)
			// unsigned
			c=200;
			for (idx2=0; idx2<len1; idx2++)
			{
				if ((idx2%25) == 0)
				{
					if (c==0) {
						c=200;
					} else {
						c=0;
					}
				}

				*(ptr1 + idx2)=c;
			}
			fSampleShouldLoop[idx]=true;

			voice[idx]->Unlock(ptr1, len1, ptr2, len2);
		}
		break;
	
	case 1:		// sine wave
		if (lpds->CreateSoundBuffer(&dsbd, &voice[idx], NULL) != DS_OK)
		{
			lpds->Release();
			return;
		}
		
		// I have calculated that the square wav changed state every 25 samples
		if (voice[idx]->Lock(0, AUDIO_BUFFER_SIZE, (void**)&ptr1, &len1, (void**)&ptr2, &len2, DSBLOCK_ENTIREBUFFER) == DS_OK)
		{
			// since we haven't started the sound, hopefully the second pointer is null
			if (len2 != 0) {
				//MessageBox(NULL, "Failed to lock tone buffer", "Classic99 Error", MB_OK);
			}

			// Generate a sine wave with a period of 50 samples (441hz - but I assume 440 (A above Middle C) throughout)
			// since the musical scale is not exact, this is close enough. Middle C ranges from 256-280hz
			// depending on who you ask!
			for (idx2=0; idx2<len1; idx2++) {
				*(ptr1 + idx2) = (unsigned char)((int)((sin((6.283/50.0)*(idx2%50))*100)+100)&0xff);
			}
			fSampleShouldLoop[idx]=true;

			voice[idx]->Unlock(ptr1, len1, ptr2, len2);
		}
		break;

	case 2:		// flute
		LoadResourceWAV(&voice[idx], IDR_FLUTE);
		fSampleShouldLoop[idx]=false;
		break;

	case 3:		// oboe
		LoadResourceWAV(&voice[idx], IDR_OBOE);
		fSampleShouldLoop[idx]=false;
		break;

	case 4:		// organ
		LoadResourceWAV(&voice[idx], IDR_ORGAN);
		fSampleShouldLoop[idx]=false;
		break;

	case 5:		// piano
		LoadResourceWAV(&voice[idx], IDR_PIANO);
		fSampleShouldLoop[idx]=false;
		break;

	case 6:		// sax
		LoadResourceWAV(&voice[idx], IDR_SAX);
		fSampleShouldLoop[idx]=false;
		break;

	case 7:		// string
		LoadResourceWAV(&voice[idx], IDR_STRING);
		fSampleShouldLoop[idx]=false;
		break;

	case 8:		// trombone
		LoadResourceWAV(&voice[idx], IDR_TROMBONE);
		fSampleShouldLoop[idx]=false;
		break;

	case 9:		// violin
		LoadResourceWAV(&voice[idx], IDR_VIOLIN);
		fSampleShouldLoop[idx]=false;
		break;
	}
}


void sSpeechInit(unsigned char *pROM, int nRomLen, int nBufferLen, int nEmulationRate) ;
void sSpeechStop() ;
unsigned char sSpeechRead() ;
void sSpeechWrite(unsigned char B) ;
void sSpeechProcess(unsigned char *pBuf, int nMax) ;

void startsound()
{ /* start up the sound files */

	DSBUFFERDESC dsbd;
	WAVEFORMATEX pcmwf;
	unsigned int idx, idx2;
	UCHAR *ptr1, *ptr2;
	unsigned long len1, len2, len;
	char buf[80];
	
	need_byte=0;
	last_byte=0;
	if (DirectSoundCreate(NULL, &lpds, NULL) != DS_OK)
	{
		lpds=NULL;		// no sound
		return;
	}
#if 0
	
	if (lpds->SetCooperativeLevel(myWnd, DSSCL_NORMAL) != DS_OK)	// normal created a 22khz, 8 bit stereo DirectSound system
	{
		lpds->Release();
		return;
	}
#endif
	for (idx=0; idx<3; idx++)
	{
		GenerateToneBuffer(idx);

		if (voice[idx]->SetVolume(DSBVOLUME_MIN) != DS_OK) {
			debug_write("Set volume failed");
		}

		if (voice[idx]->Play(0, 0, fSampleShouldLoop[idx]?DSBPLAY_LOOPING:0) != DS_OK) {
			debug_write("Voice DID NOT START");
		}
	}

	// each noise channel now has it's own voice, to eliminate the need to change buffers
	// and mess with repeat lengths and the like

	for (idx=0; idx<8; idx++)
	{
		bool fRet;

		switch (idx) {
		case 0: fRet=LoadResourceWAV(&noise[0], IDR_NOISE1); break;
		case 1: fRet=LoadResourceWAV(&noise[1], IDR_NOISE2); break;
		case 2: fRet=LoadResourceWAV(&noise[2], IDR_NOISE3); break;
		case 3: fRet=LoadResourceWAV(&noise[3], IDR_NOISE4); break;
		case 4: fRet=LoadResourceWAV(&noise[4], IDR_NOISE5); break;
		case 5: fRet=LoadResourceWAV(&noise[5], IDR_NOISE6); break;
		case 6: fRet=LoadResourceWAV(&noise[6], IDR_NOISE7); break;
		case 7: fRet=LoadResourceWAV(&noise[7], IDR_NOISE8); break;
		}
		
		if (fRet) {
			if (noise[idx]->SetVolume(DSBVOLUME_MIN) != DS_OK) {
				debug_write("Set volume failed");
			}

			if (noise[idx]->Play(0, 0, DSBPLAY_LOOPING) != DS_OK) {
				debug_write("Noise DID NOT START");
			}
		}
	}

#if 0
	// load the Speech DLL
	hSpeechDll=LoadLibrary("SpeechDll.dll");
	if (NULL == hSpeechDll) {
		debug_write("Failed to load speech library.");
	} else {
		SpeechInit=(void (*)(Byte*,int,int,int))GetProcAddress(hSpeechDll, "SpeechInit");
		SpeechStop=(void (*)(void))GetProcAddress(hSpeechDll, "SpeechStop");
		SpeechRead=(Byte (*)(void))GetProcAddress(hSpeechDll, "SpeechRead");
		SpeechWrite=(void (*)(Byte))GetProcAddress(hSpeechDll, "SpeechWrite");
		SpeechProcess=(void (*)(Byte*,int))GetProcAddress(hSpeechDll, "SpeechProcess");
	}
#endif

	SpeechInit = sSpeechInit ;
	SpeechStop = sSpeechStop ;
	SpeechRead = sSpeechRead ;
	SpeechWrite = sSpeechWrite ;
	SpeechProcess = sSpeechProcess ;

	// Now load up the speech system
	/* start audio stream - SPEECHBUFFER buffer, 8 bit, 8khz, max vol, center */
	/* note! this is unsigned mono, not signed. Routines modified accordingly */
	sample_pos=0;

	ZeroMemory(&pcmwf, sizeof(pcmwf));
	pcmwf.wFormatTag = WAVE_FORMAT_PCM;		// wave file
	pcmwf.nChannels=1;						// 1 channel (mono)
	pcmwf.nSamplesPerSec=SPEECHRATE;		// Should be 8khz
	pcmwf.nBlockAlign=1;					// 1 byte per sample * 1 channel
	pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	pcmwf.wBitsPerSample=8;					// 8 bit samples
	pcmwf.cbSize=0;							// always zero;

	ZeroMemory(&dsbd, sizeof(dsbd));
	dsbd.dwSize=sizeof(dsbd);
	dsbd.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY ; //| DSBCAPS_GETCURRENTPOSITION2;
	dsbd.dwBufferBytes=SPEECHBUFFER;
	dsbd.lpwfxFormat=&pcmwf;

	if (lpds->CreateSoundBuffer(&dsbd, &speechbuf, NULL) != DS_OK) {
		debug_write("Failed to create speech sound buffer!");
	} else {
		if (speechbuf->Lock(0, SPEECHBUFFER, (void**)&ptr1, &len1, (void**)&ptr2, &len2, DSBLOCK_ENTIREBUFFER) == DS_OK) {
			// since we haven't started the sound, hopefully the second pointer is nil
			if (len2 != 0) {
				debug_write("Failed to lock speech buffer");
			}

			// unsigned - zero the sample (we have an offset sadly)
			memset(ptr1, 0x80, len1);
			speechbuf->Unlock(ptr1, len1, ptr2, len2);
		}

		if (speechbuf->Play(0, 0, DSBPLAY_LOOPING) != DS_OK) {
			debug_write("Speech DID NOT START");
		}

		if (SpeechInit) SpeechInit(SPEECH, 0x8000, SPEECHRATE, SPEECHBUFFER);
	}
}

//////////////////////////////////////////////////////////
// Start up the video system 
//////////////////////////////////////////////////////////
void fixpal() ;

void startvdp()
{ 
	// call VDP Startup
	hVideoThread=NULL;
	fixpal() ;
	//hVideoThread=_beginthread((void (__cdecl *)(void*))VDPmain, 0, NULL);
	if (hVideoThread != -1)
		debug_write("Video Thread began...");
	else
		debug_write("Video Thread failed.");

	Sleep(100);

	// first retrace
	retrace_count=0;
}

//////////////////////////////////////////////////////////
// Non-fatal recoverable (?) error
//////////////////////////////////////////////////////////
void warn(char *x)
{ 
	// Warn will for now just dump a message into the log
	// eventually it should pop up a window and ask about
	// continuing

	debug_write("%s at address >%04x", x, PC);
}

//////////////////////////////////////////////////////////
// Fatal error - clean up and exit
// Note that normal exit is a fatal error ;)
//////////////////////////////////////////////////////////
void fail(char *x)
{ 
	// fatal error
	char buffer[1024];
	char buf2[256];

	// just in case it's not set yet
	quitflag=1;

	// add to the log - not useful now, but maybe in the future when it writes to disk
	debug_write(x);

	//timeEndPeriod(1);

	sprintf(buffer,"\n%s\n",x);
	sprintf(buf2,"PC-%.4X  WP-%.4X  ST-%.4X  OP-%.4X\nGROM-%.4X VDP-%.4X\n",PC,WP,ST,in,GRMADD,VDPADD);
	strcat(buffer,buf2);
	sprintf(buf2,"Run Duration  : %d seconds\n",timercount/60);
	strcat(buffer,buf2);
	sprintf(buf2,"Operation time: %d instructions processed.\n",cpucount);
	strcat(buffer,buf2);
	sprintf(buf2,"Display frames: %d video frames displayed.\n",cpuframes);
	strcat(buffer,buf2);

	if (timercount<60) timercount=60;	// avoid divide by zero
	
	sprintf(buf2,"Average speed : %d instructions per second.\n",cpucount/(timercount/60));
	strcat(buffer,buf2);
	sprintf(buf2,"Frameskip     : %d\n",drawspeed);
	strcat(buffer,buf2);

	// the messagebox fails during a normal exit in WIN32.. why is that?
	//MessageBox(myWnd, buffer, "Classic99 Exit", MB_OK);

	Sleep(600);			// give the threads a little time to shut down

	//if (Recording) {
		//CloseAVI();
	//}

	if (SpeechStop) SpeechStop();

    if (speechbuf) {
		speechbuf->Stop();
		speechbuf->Release();
		speechbuf=NULL;
	}

	for (int idx=0; idx<3; idx++) {
		if (voice[idx])
		{
			voice[idx]->Stop();
			voice[idx]->Release();
			voice[idx] = NULL ;
		}
	}
	for (idx=0; idx<8; idx++) {
		if (noise[idx])
		{
			noise[idx]->Stop();
			noise[idx]->Release();
			noise[idx] = NULL ;
		}
	}

	if (lpds) {
		lpds->Release();
		lpds = NULL ;
	}

	if (myWnd) {
		//DestroyWindow(myWnd);
	}
	
	if (myClass) {
		//UnregisterClass("TIWndClass", hInstance);
	}

	if (framedata)
	{
		free(framedata);
		framedata = NULL ;
	}

	//if (framedata2) free(framedata2);

	//if (hSpeechDll) {
		//FreeLibrary(hSpeechDll);
		//hSpeechDll=NULL;
	//}

	//exit(0);
}

/////////////////////////////////////////////////////////
// Return a Word from CPU memory
/////////////////////////////////////////////////////////
Word romword(Word x)
{ 
	x&=0xfffe;		// drop LSB
	return((rcpubyte(x)<<8)+rcpubyte(x+1));
}
/////////////////////////////////////////////////////////
// Return a Word from CPU memory using byte access
/////////////////////////////////////////////////////////
Byte romwordbyte(Word x)
{ 
	return rcpubyte(x);
}

/////////////////////////////////////////////////////////
// Write a Word to CPU memory
/////////////////////////////////////////////////////////
void wrword(Word x, Word y)
{ 
	x&=0xfffe;		// drop LSB

	// Emulate the read-before-write
	romword(x);

	// now write the new data
	wcpubyte(x,(Byte)(y>>8));
	wcpubyte(x+1,(Byte)(y&0xff));
}

/////////////////////////////////////////////////////////
// Write a Byte to CPU memory using Word access
/////////////////////////////////////////////////////////
void wrwordbyte(Word x, Byte y)
{ 
	romword(x);	// read-before-write (should this be a word or a byte?)

	// now write the new data
	wcpubyte(x,y);
}

/////////////////////////////////////////////////////////
// Main loop for Emulation
/////////////////////////////////////////////////////////
void emulti()
{
	//MSG msg;

	WP=romword(0);		// >0000 is the reset vector - read WP
	PC=romword(2);		// and then read the PC
	X_flag=0;			// not currently executing an X instruction
	quitflag=0;			// Don't quit
	nCycleCount=26;		// not that it's a big deal, but that's how long reset takes ;)

	//readroms() ;

	while (!quitflag)
	{ 
		char buf[128];
		//static FILE *fp=NULL;
		int cnt, idx, wid;

//	Full CPU trace to disk (warning: eats disk fast!)
//		cnt=Dasm9900(buf, PC);
//		if (NULL == fp) {
//			fp=fopen("C:\\9900.log", "w");
//		}
//		fprintf(fp, "%04x:", PC);
//		wid=18;
//		for (idx=0; idx<cnt; idx++) {
//			fprintf(fp, " %02x", GetSafeByte(PC+idx));
//			wid-=3;
//		}
//		fprintf(fp, "%*s", wid+1, " ");
//		fprintf(fp, ": %s\n", buf);
//

		// Dumps the XB Utilities to a disk file
//		if (0) {
//			fp=fopen("C:\\XB_UTILS", "wb");
//			fwrite(&CPU[0x2000], 1, 8192, fp);
//			fclose(fp);
//		}

		if ((PauseInactive)) {//&&(myWnd != GetForegroundWindow())) {
			// we're supposed to pause when inactive, and we are not active
			// So, don't execute an instruction, and sleep a bit to relieve CPU
			Sleep(100);
		} else {
			// execute one opcode
			do1();
		}

#if 0
		// check for messages
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT) {
				quitflag=1;
			}
			
			TranslateMessage(&msg);
			DispatchMessage(&msg);		// this will push it to the Window procedure
		}
#endif
	}
}

//////////////////////////////////////////////////////////
// Read and process the load files
//////////////////////////////////////////////////////////
void LoadOneImg(struct IMG *pImg, char *szFork) {
	FILE *fp;
	char *pData;
	HRSRC hRsrc;
	HGLOBAL hGlob;
	unsigned char DiskFile[32768*2+6];	// 32k ROM plus 6 byte kracker header max
	char *pszFrom="resource";
	char *filename ;
	FILE *infile ;
	unsigned long filesize=0 ;

	if ((NULL == pImg) || (pImg->nType == TYPE_NONE)) return;

	pData=NULL;

	int nLen=pImg->nLength;

	if (NULL != pImg->dwImg) {
		switch ( pImg->dwImg )
		{
			case IDR_994AROM : filename = "d:\\MODS\\994AROM.BIN"; break ; 
			case IDR_994AGROM : filename = "d:\\MODS\\994AGROM.BIN"; break ; 
			case IDR_AMI99DSK : filename = "d:\\MODS\\AMI99DSK.BIN"; break ; 
			case IDR_SPCHROM  :filename =  "d:\\MODS\\SPCHROM.BIN"; break ; 
			case IDR_PGROM   : filename = "d:\\MODS\\p1f00g_0000_f800.GRM"; break ; 
			case IDR_RS232   : filename = "d:\\MODS\\RS232.Bin"; break ; 
			case IDR_TIEXTG  : filename = "d:\\MODS\\TI-ExtBG.Bin" ; break ;
			case IDR_TIEXTC  : filename = "d:\\MODS\\TI-ExtBC.Bin" ; break ;
			case IDR_TIEXTD  : filename = "d:\\MODS\\TI-ExtBD.Bin" ; break ;
			default : filename = NULL ; break ;

		}
		if ( filename == NULL )
			return ;

		infile = fopen( filename, "rb");
		if ( infile == NULL )
			return ;

		fseek( infile, 0, SEEK_END ) ;
		filesize = ftell( infile ) ;
		fseek( infile, 0, SEEK_SET ) ;

		if (nLen > filesize) nLen=filesize;
		pData = (char*)malloc( filesize ) ;
		fread( pData, 1, filesize, infile ) ;
		fclose(infile) ;
#if 0
		hRsrc=FindResource(NULL, MAKEINTRESOURCE(pImg->dwImg), szFork);
		if (hRsrc) {
			int nRealLen=SizeofResource(NULL, hRsrc);
			if (nLen > nRealLen) nLen=nRealLen;

			hGlob=LoadResource(NULL, hRsrc);
			if (NULL != hGlob) {
				pData=(char*)LockResource(hGlob);
			}
		}
#endif
	} else {
		// It's a disk file. Worse, it may be a disk file with a header. 
		// But we may be able to determine that.
		if (strlen(pImg->szFileName) == 0) {
			return;
		}
		fp=fopen(pImg->szFileName, "rb");
		if (NULL == fp) {
			return;
		}
		pszFrom="disk";
		int nRealLen=fread(DiskFile, 1, 32768*2+6, fp);
		fclose(fp);

		if (nRealLen > 6) {
			// Check for 6 byte header - our simple check is if
			// byte 0 is 0x00 or 0xff, and bytes 4/5 contain the
			// load address, then strip the first six bytes
			if ((DiskFile[0]==0x00) || (DiskFile[0]==0xff)) {	// a flag byte?
				if (DiskFile[4]*256+DiskFile[5] == pImg->nLoadAddr) {
					debug_write("Removing header from %s", pImg->szFileName);
					nRealLen-=6;
					memmove(DiskFile, &DiskFile[6], nRealLen);
				}
			}
		}
		if (nLen != nRealLen) {
			debug_write("Warning: size mismatch on %s - expected >%04x bytes but found >%04x", pImg->szFileName, pImg->nLength, nRealLen);
		}
		if (nLen > nRealLen) nLen=nRealLen;
		pData=(char*)DiskFile;
	}

	if (NULL != pData) {
		// finally ;)
		debug_write("Loading file from %s: Type %c, Address 0x%04X, Length 0x%04X", pszFrom, pImg->nType, pImg->nLoadAddr, nLen);

		switch (pImg->nType) {
			case TYPE_GROM:
				memcpy(&GROM[pImg->nLoadAddr], pData, nLen);
				break;

			case TYPE_ROM:
				memcpy(&CPU[pImg->nLoadAddr], pData, nLen);
				memset(&ROMMAP[pImg->nLoadAddr], 1, nLen);
				break;

			case TYPE_SPEECH:
				memcpy(&SPEECH[pImg->nLoadAddr], pData, nLen);
				break;

			case TYPE_XB:
				memcpy(&CPU2[pImg->nLoadAddr-0x6000], pData, nLen);
				xb=1;		// xb bank loaded
				break;

			case TYPE_RAM:
				memcpy(&CPU[pImg->nLoadAddr], pData, nLen);
				break;

			case TYPE_VDP:
				memcpy(&VDP[pImg->nLoadAddr], pData, nLen);
				break;

			case TYPE_DSR:	// always loads at >4000, the load address is the CRU base
				memcpy(&DSR[(pImg->nLoadAddr>>8)&0x0f][0], pData, nLen);
				break;

			case TYPE_DSR2:	// always loads at >4000, the load address is the CRU base
				memcpy(&DSR[(pImg->nLoadAddr>>8)&0x0f][0x2000], pData, nLen);
				break;

			case TYPE_PCODEG:
				memcpy(&PCODE[pImg->nLoadAddr], pData, nLen);
				break;

			default:
				break;
		}

		if ( filesize > 0 )
			free( pData ) ;
	}
	
	// WIN32 does not require (or even permit!) us to unlock and release these objects
}

void readroms()
{ 
	int idx;

	// set memory banks to zero. Real TIs don't do this here
	memset(CPU, 0, 65536);
	memset(ROMMAP, 0, 65536);
	memset(CPU2, 0, 8192);
	memset(GROM, 0, 65536);
	memset(VDP, 0, 16384);
	memset(CRU, 1, 4096);
	memset(DSR, 0, 16*16384);
	memset(PCODE, 0, 65536);
	memset(key, 0, 256);
	// Disable the PCode card (at >1F00)
	DSR[0xE][0]=0;
	xb=0;	// no XB bank loaded
	nCurrentDSR=-1;							// no DSR paged in
	memset(nDSRBank, 0, sizeof(nDSRBank));	// not on second page of DSR

	// load the always load files
	for (idx=0; idx<sizeof(AlwaysLoad)/sizeof(IMG); idx++) {
		LoadOneImg(&AlwaysLoad[idx], "ROMS");
	}

	// load the appropriate system ROMs - each system is a cart structure that contains 4 ROMs
	for (idx=0; idx<4; idx++) {
		LoadOneImg(&Systems[nSystem].Img[idx], "ROMS");
	}

	// if there is a cart plugged in, load that too
	if (nCart != -1) {
		struct CARTS *pBank=NULL;

 		switch (nCartGroup) {
		case 0:	pBank=Apps; break;
		case 1: pBank=Games; break;
		case 2: pBank=Users; break;
		}

		if (pBank) {
			pCurrentHelpMsg=pBank[nCart].szMessage;
			for (idx=0; idx<4; idx++) {
				LoadOneImg(&pBank[nCart].Img[idx], "ROMS");
			}
		}

		pMagicDisk=pBank[nCart].pDisk;
	}

	//reset stuff
				ST=(ST&0xfff0);							// disable interrupts
				memset(CRU, 1, 4096);					// reset 9901
				timer9901=0;
				wrword(0x83c4,0);						// Console bug work around, make sure no user int is active
				WP=romword(0x0000);						// Reset = BLWP @>0000
				wrword(WP+26,0xffff);
				wrword(WP+28,PC);
				wrword(WP+30,ST);
				PC=romword(0x0002);
				grmaccess=0;			// No GROM Access yet
				pcodeaccess=0;
				nCurrentDSR=-1;
				memset(nDSRBank, 0, sizeof(nDSRBank));
				vdpaccess=0;			// No VDP address writes yet 
				interrupt_needed=0;		// No interrupt missed yet
				fKeyEverPressed=false;	// No key pressed yet (to disable the warning on cart change)
				if (max_cpf == 0) {
					max_cpf=oldmax;		// unpause!
				}
				nCycleCount=26;		// not that it's a big deal, but that's how long reset takes ;)

}

//////////////////////////////////////////////////////////
// Convert hex to decimal
//////////////////////////////////////////////////////////
Word hex2dec(char *x)
{
	Word z;

	z=0;
	sscanf(x, "%4hx", &z);

	return z;
}

//////////////////////////////////////////////////////////
// Speedup routine - convert from TI floating point
//////////////////////////////////////////////////////////
double tifloat2double(Byte *p) 
{
	short int firstword;
	int i, exp, sign=1;
	double res;
  
	firstword = *p++ << 8;
	firstword |= *p++;
	if (firstword==0)
	{
		return 0;
	}
	else 
	{
		if (firstword<0) 
		{								/* negative */
			sign=-1;
			firstword = -firstword;
		}
	}
	exp = ((firstword >> 8) - 0x40 - 6) << 1;
	res = firstword & 0xff;
	for (i=0; i<6; i++)
	{
		res = 100*res + *p++;
	}
	
	return (sign * res * pow(10,exp));
}
	
//////////////////////////////////////////////////////////
// Speedup routine - convert to TI floating point and set status+error flags
//////////////////////////////////////////////////////////
void double2tifloat(double d, Byte *p)
{
	short int firstword;
	int i, exp, sign=1;
  
	wcpubyte(0x8354, 0);		/* reset error flag */
	if (d==0)
	{
		*p++ = 0;
		*p = 0;
		wcpubyte(0x8354, 0x20);	/* zero flag in GPL status */
		return;
	} 
	else
	{
		if (d<0) 
		{						/* negative */
			sign=-1;
			d = -d;
		}
	}
	exp = (int) log10(d) >> 1;
	d /= pow(10,exp<<1);
	d += 0.0000000000005;		/* round last digit */
	if (d>=100) {				/* and handle overflow here... */
	  d /= 100;
	  exp++;
	}
	if (exp<=-128) {			/* underflow */
	  *p++ = 0;
	  *p = 0;
	  wcpubyte(0x8354, 0x20);	/* zero flag in GPL status */
	  return;
	} else if (exp>=128) {		/* overflow */
	  exp = 127;
	  d = 99;
	  wcpubyte(0x8354, 0x01);	/* set error flag (FIXME: 02 for division) */
	}
	firstword = (exp + 0x40) << 8 | (Byte) d;
	if (sign<0) {
	  firstword = -firstword;
	  wcpubyte(0x837c, 0x00);	/* GPL status: reset Arithmetic Greater Than */
	} else {
	  wcpubyte(0x837c, 0x40);	/* GPL status: set Arithmetic Greater Than */
	}
	*p++ = firstword >> 8;
	*p++ = firstword & 0xff;
	for (i=0; i<6; i++) 
	  {
		d = (d - (int) d) * 100;
		*p++ = (Byte) d;
	  }
}

DWORD cptime = 0 ;
DWORD frtime = 0 ;
extern "C" unsigned int xbox_get_throttle() ;
extern "C" unsigned int xbox_get_throttle_speed();
extern "C" int xbox_check_events() ;

//////////////////////////////////////////////////////////
// Interpret a single instruction
//////////////////////////////////////////////////////////
void do1()
{
	cpucount++;										// increment instruction counter

	// check for video interrupt or redraw
	//if (CRU[2])
	//nCycleCount=0;									// for throttling

	if ( xbox_get_throttle() )
	{
		//nCycleCount=0;									// for throttling
		if ( cptime +5 < GetTickCount() )
		//if ( cpucount%100 == 0 )
		{
			Counting() ;
			SpeechUpdate() ;
			cptime = GetTickCount() ;
		}
	}

	if (interrupt_needed)
	{
		nCycleCount=0;									// for throttling

		if (xbox_check_events() )
		{
			quitflag = 1 ;
		}
		if ((retrace_count>drawspeed)&&(redraw_needed))	// only draw if VDP RAM has been changed, and it's time
		{ 
			//if ( frtime + 10 > GetTickCount() )
			{
				VDPdisplay();
				doBlit();
				//frtime = GetTickCount() ;
			}
			//if (DisplayEvent)
			{
				//SetEvent(DisplayEvent);
			}

			redraw_needed=0;
		}
	
		timercount+=retrace_count;						// update timer statistics

		if (retrace_count>drawspeed)
		{
			cpuframes++;								// for accounting
			retrace_count=0;							// reset retrace counter
			last_count=0;								// to verify changes above

#if 0
			if (Recording)								// for AVI recording
			{
				// write a frame to the AVI
				// I want only every 4th frame.
				RecordFrame++;
				if (RecordFrame >= 4/(drawspeed+1))
				{
					RecordFrame=0;
					WriteFrame();
				}
			}
#endif
		}

		if ((VDPREG[1]&0x20)&&(!(VDPS&0x80)))		// are VDP interrupts on, and previous cleared?
		{	
			VDPS|=0x80;								// set the interrupt flag
		}
					
		interrupt_needed=0;							// No matter what, this tick is passed!
	}

	// Check if the CPU wants an interrupt (99/4A has only level 1 interrupts)
	if ((VDPS&0x80) && ((ST&0x000f) >= 1) && (!skip_interrupt)) {
		idling=0;
		if ((CPUThrottle==0)||(nCycleCount < max_cpf-1)) {	// speed throttling
			if (simulate_int) {
				ConsoleInterrupt();				// Call console interrupt routine
			} else {
				wrword(0x83da,WP);				// WP in R13 
				wrword(0x83dc,PC);				// PC in R14 
				wrword(0x83de,ST);				// ST in R15 
				//ST=(ST&0xfff0);				// disable interrupts
				
				/* now load the correct workspace, and perform a branch and link to the address */
				/* (level 1 interrupt assumed as that's all we have) */
				WP=romword(0x0004);
				PC=romword(0x0006);

				nCycleCount+=22;
			}
		}
	}
	
#if 0
	// Control keys - must always be active
	if (key[VK_HOME]) 
	{
		// 39x19 window
		if (NULL == regWnd) {
			regWnd=CreateWindow("Classic99Info", "Classic99 - System Registers", WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION, 0, 0, 39*fontX+wndXtrim, 19*fontY+wndYtrim, myWnd, NULL, NULL, NULL);
			if (NULL == regWnd) {
				MessageBox(NULL, "Can't create register window", "D'oh", MB_OK);
			}
		}

		// DEBUGLEN-1x30 window
		if (NULL == dbgWnd) {
			dbgWnd=CreateWindow("Classic99Info", "Classic99 - Last 30 Debug Messages", WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION, 0, 0, (DEBUGLEN-1)*fontX+wndXtrim, 30*fontY+wndYtrim, myWnd, NULL, NULL, NULL);
			if (NULL == dbgWnd) {
				MessageBox(NULL, "Can't create debug window", "D'oh", MB_OK);
			}
		}

		// 40x30 window
		if (NULL == asmWnd) {
			asmWnd=CreateWindow("Classic99Info", "Classic99 - Disassembly", WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION, 0, 0, 40*fontX+wndXtrim, 30*fontY+wndYtrim, myWnd, NULL, NULL, NULL);
			if (NULL == dbgWnd) {
				MessageBox(NULL, "Can't create disassembly window", "D'oh", MB_OK);
			}
		}

		key[VK_HOME]=0;
	}

	if (key[VK_END]) {
		if (regWnd) {
			SendMessage(regWnd, WM_CLOSE, 0, 0);
		}
		if (dbgWnd) {
			SendMessage(dbgWnd, WM_CLOSE, 0, 0);
		}
		if (asmWnd) {
			SendMessage(asmWnd, WM_CLOSE, 0, 0);
		}
	}

	if (key[VK_PRIOR]) {
		VDPDebug=1;
		key[VK_PRIOR]=0;
		redraw_needed=1;
	}
	if (key[VK_NEXT]) {
		VDPDebug=0;
		key[VK_NEXT]=0;
		redraw_needed=1;
	}

	// breakpoint handling
//	if (PC == 0xa22C) {
//	  max_cpf=0;
//	  CPUThrottle=1;
//	}

	if (key[VK_F1])
	{	
		if (0 == max_cpf)
		{
			max_cpf=oldmax;
			SetWindowText(myWnd, "Classic99");
		}
		else
		{
			max_cpf=0;
			SetWindowText(myWnd, "Classic99 - Paused. F1 to Continue, F2 to Step");
		}
		key[VK_F1]=0;
	}
	if (key[VK_F2])
	{
		nCycleCount=-1;			// this will allow 1 instruction to run in pause
		key[VK_F2]=0;
	}									
#endif

	////////////////// System Patches
	if ((!idling)&&((CPUThrottle==0)||(nCycleCount<max_cpf)))		// speed throttling
	{
		// Keyboard hacks - requires original 99/4A ROM!
		if ((keyboard == 1) && (PC==0x478)) {	// This is where KSCAN begins to exit
			if (NULL != PasteString) {

#if 0
				if (PasteString==PasteIndex) SetWindowText(myWnd, "Classic99 - Pasting (ESC Cancels)");	

				if (key[VK_ESCAPE]) {
					*PasteIndex='\0';
				}
#endif
				if ((rcpubyte(0x8374)==0)||(rcpubyte(0x8374)==5)) {		// Check for pastestring - note keyboard is still active
					if (*PasteIndex) {
						if (*PasteIndex==10) {
							// CRLF to CR, LF to CR
							if ((PasteIndex==PasteString)||(*(PasteIndex-1)!=13)) {
								*PasteIndex=13;
							}
						}

						if (PasteCount<1) {
							if ((*PasteIndex>31)||(*PasteIndex==13)) {
								wcpubyte(WP, *PasteIndex);					/* set R0 (byte) with keycode */
								wcpubyte(WP+12, rcpubyte(0x837c) | 0x20);	/* R6 must contain the status byte */
							}
							if (PasteCount<1) wcpubyte(0x837c, rcpubyte(0x837c)|0x20);
							PasteCount++;
						} else {
							PasteCount++;
							if (PasteCount==2) {
								PasteIndex++;
							}
							if (PasteCount==3) {
								PasteCount=0;
							}
						}

					} else {
						PasteIndex=NULL;
						free(PasteString);
						PasteString=NULL;
						//SetWindowText(myWnd, "Classic99");
					}
				}
			}
		}

		// intercept patches for Classic99 DSK1 emulation
		// These patches handle the custom DSR:
		// >4800 - DSK file access
		// >4810 - DSK powerup
		// >4820->482C - SBRLNK - Special subprograms
		if (nCurrentDSR == 1) {
			switch (PC) {
			case 0x4800:
				do_dsrlnk();
				PC=romword(WP+22)+2;						// return address in R11.. TI is a bit silly
															// and if we don't increment by 2, it's considered
															// an error condition
				return;

			case 0x4810:
				wrword(0x8358, 0x3ef5);						// volume ID block
				wrword(0x8366, 0x3eea);						// VDP stack
				do_files(3);
				PC=romword(WP+22);
				return;

			case 0x4820:
			case 0x4821:
			case 0x4822:
			case 0x4823:
			case 0x4824:
			case 0x4825:
			case 0x4826:
			case 0x4827:
			case 0x4828:
			case 0x4829:
			case 0x482a:
			case 0x482b:
			case 0x482c:
				do_sbrlnk();
				PC=romword(WP+22)+2;						// return address in R11.. as above
				return;
			}
		}

		if (speedup_float) 
		{									/* speedup floating point operations */
			if (PC==0x0d80) {					/* ADD */
				double2tifloat(tifloat2double(&CPU[0x835c]) + tifloat2double(&CPU[0x834a]), &CPU[0x834a]);
				PC=romword(WP+22);			// return address in R11.
				return;
			}
			if (PC==0x0d7c) {					/* SUBT */
				double2tifloat(tifloat2double(&CPU[0x835c]) - tifloat2double(&CPU[0x834a]), &CPU[0x834a]);
				PC=romword(WP+22);			// return address in R11.
				return;
			}
			if (PC==0xe88) {					/* MULT */
				double2tifloat(tifloat2double(&CPU[0x835c]) * tifloat2double(&CPU[0x834a]), &CPU[0x834a]);
				PC=romword(WP+22);			// return address in R11.
				return;
			}
			if (PC==0x0ff4) {					/* DIV */
				double den = tifloat2double(&CPU[0x834a]);
				if (den) {
				  double2tifloat(tifloat2double(&CPU[0x835c]) / den, &CPU[0x834a]);
				} else {
				  double2tifloat((CPU[0x835c] >= 0x80) ? -99e127 : 99e-127, &CPU[0x834a]);
				  CPU[0x8354] = 0x02; /* set error */
				}
				  PC=romword(WP+22);			// return address in R11.
				return;
			}
			if (PC==0x12b8) {					/* CFI */
				wrword(0x834a, (Word) tifloat2double(&CPU[0x834a]));
				PC=romword(WP+22);			// return address in R11.
				return;
			}
		} /* speedup_float */

		if (X_flag==0)
		{ 
			// Update the disassembly trace
			memmove(&disasm[0], &disasm[1], 38);//19*sizeof(Word));	// really should be a ring buffer
			disasm[19]=PC;

			in=romword(PC);							// ie: not an 'X' command
		    ADDPC(2);								// thanks to Jeff Brown for explaining that!
		}

		(*opcode[in])();							// 'in' contains the opcode to execute

		// Slow down autorepeat using a timer - requires 99/4A GROM
		// We check if the opcode was "MOVB 2,*3+", (PC >025e, but we don't assume that) 
		// the 99/4A keyboard is on, and the GROM Address
		// has been incremented to the next instruction (IE: we just incremented the
		// repeat counter). If so, we only allow the increment at a much slower rate
		// based on the interrupt timer (for real time slowdown).
		if ((!CPUThrottle) && (slowdown_keyboard) && (in == 0xdcc2) && (keyboard == 1) && (GRMADD == 0x2a62)) {
			if ((ticks%10) != 0) {
				CPU[0x830D]--;
			}
		}
	}

	skip_interrupt=0;
}

//////////////////////////////////////////////////////
// Read a single byte from CPU memory
//////////////////////////////////////////////////////
Byte rcpubyte(Word x)
{
	// TI CPU memory map
	// >0000 - >1fff  Console ROM
	// >2000 - >3fff  Low bank RAM
	// >4000 - >5fff  DSR ROMs
	// >6000 - >7fff  Cartridge expansion
	// >8000 - >9fff  Memory-mapped devices & CPU scratchpad
	// >a000 - >ffff  High bank RAM
	// 
	// All is fine and dandy, except the memory-mapped devices, and the
	// fact that writing to the cartridge port with XB in places causes
	// a bank switch. In this emulator, that will only happen if bank 2
	// has been loaded. A custom DSR will be written to be loaded which
	// will appear to the "TI" to support all valid devices, and it will
	// be loaded into the DSR space, which then will not be paged.

  nCycleCount+=2;	// 1/2 of the word access - this will be wrong for the places that directly read bytes!

  switch (x & 0xe000) {
  case 0x8000:
	switch (x & 0xfc00) {
	case 0x8000:				// scratchpad RAM - 256 bytes repeating.
		nCycleCount-=2;			// never mind for scratchpad :)
		return(CPU[x|0x0300]);	// I map it all to >83xx
	case 0x8400:				// Don't read the sound chip (can hang a real TI)
		return 0;
	case 0x8800:				// VDP read data
		return(rvdpbyte(x));
	case 0x8c00:				// VDP write data
		return 0;
	case 0x9000:				// Speech read data
		return(rspeechbyte(x));
	case 0x9400:				// Speech write data
		return 0;
	case 0x9800:				// read GROM data
		return(rgrmbyte(x));
	case 0x9c00:				// write GROM data
		return 0;
	default:					// We shouldn't get here, but just in case...
		return 0;
	}
  case 0x0000:					// console ROM
  		nCycleCount-=2;			// never mind for console ROM :)
		// fall through
  case 0x2000:					// normal CPU RAM
  case 0xa000:					// normal CPU RAM
  case 0xc000:					// normal CPU RAM
  case 0xe000:					// normal CPU RAM
		return(CPU[x]);
  case 0x4000:					// DSR ROM (with bank switching and CRU)
	  if (-1 == nCurrentDSR) return 0;
	  // special case for P-Code Card GROM addresses
	  if (nCurrentDSR == 0xf) {
		  switch (x) {
		  case 0x5bfc:		// read grom data
		  case 0x5bfd:
		  case 0x5bfe:		// read grom address
		  case 0x5bff:
  			  return(rpcodebyte(x));
		  }
		  // any other case, fall through
	  }
	  if (nDSRBank[nCurrentDSR]) {
		  return DSR[nCurrentDSR][x-0x2000];	// -4k for base, +2k for second page
	  } else {
		  return DSR[nCurrentDSR][x-0x4000];
	  }
	  break;
		
  case 0x6000:					// cartridge ROM
		// XB is supposed to only page the upper 4k, but some other carts seem to like it all
		// paged? Dunno.. weird.
		if (xb && bank)
			return(CPU2[x-0x6000]);	// cartridge bank 2
		else 
			return(CPU[x]);			// cartridge bank 1
  default:						// We shouldn't get here, but just in case...
		return 0;
	}
}

//////////////////////////////////////////////////////////
// Write a byte to CPU memory
//////////////////////////////////////////////////////////
void wcpubyte(Word x, Byte c)
{
	nCycleCount+=2;	// 1/2 of the word access time

	if (ROMMAP[x])	// trap ROM writes and check for XB bank switch
	{
		if ((x>=0x6000)&&(x<0x8000)&&(xb))		
		{ 
			bank=(x&0x0002)>>1;							// XB bank switch
		}
		return;											// can't write to ROM!
	}

  switch (x & 0xe000) {
  case 0x8000:
	switch (x & 0xfc00) {
	case 0x8000:				// scratchpad RAM - 256 bytes repeating.
		nCycleCount-=2;			// never mind for scratchpad
		CPU[x|0x0300] = c;		// I map it all to >83xx
		break;
	case 0x8400:				// Sound write data
		wsndbyte(c);
		break;
	case 0x8800:				// VDP read data
		break;
	case 0x8c00:				// VDP write data
		wvdpbyte(x,c);
		break;
	case 0x9000:				// Speech read data
		break;
	case 0x9400:				// Speech write data
		wspeechbyte(x,c);
		break;
	case 0x9800:				// read GROM data
		break;
	case 0x9c00:				// write GROM data
		wgrmbyte(x,c);
		break;
	default:					// We shouldn't get here, but just in case...
		break;
	}
	break;
  case 0x0000:					// console ROM
	  nCycleCount-=2;			// never mind for ROM
	  // fall through
  case 0x2000:					// normal CPU RAM
  case 0x6000:					// Cartridge RAM (ROM is trapped above)
  case 0xa000:					// normal CPU RAM
  case 0xc000:					// normal CPU RAM
  case 0xe000:					// normal CPU RAM
		CPU[x]=c;
		break;
  case 0x4000:					// DSR ROM - read only for now
	  // special case for P-Code Card GROM addresses
	  if (nCurrentDSR == 0xf) {
		  switch (x) {
		  case 0x5ffc:		// write grom data
		  case 0x5ffd:
		  case 0x5ffe:		// write grom address
		  case 0x5fff:
  			  wpcodebyte(x,c);
			  return;
		  }
		  // any other case, fall through, but we currently treat all DSR memory as ROM
	  }
  	  break;
  }
}

//////////////////////////////////////////////////////
// Read a byte from the speech synthesizer
//////////////////////////////////////////////////////
Byte rspeechbyte(Word x)
{
	Byte ret=0;

	if (SpeechRead) {
		ret=SpeechRead();
	}
	return ret;
}

//////////////////////////////////////////////////////
// Write a byte to the speech synthesizer
//////////////////////////////////////////////////////
void wspeechbyte(Word x, Byte c)
{
	if ((SpeechWrite)&&(SpeechEnabled)) {
		SpeechWrite(c);
	}
}

//////////////////////////////////////////////////////
// Speech Update function - runs every tick
//////////////////////////////////////////////////////
void SpeechUpdate() {
	DWORD iRead, iWrite;
	int num;
	Byte *ptr1, *ptr2;
	DWORD len1, len2;

	if ((speechbuf==NULL) || (SpeechProcess == NULL)) {
		return;
	}

	speechbuf->GetCurrentPosition(&iRead, &iWrite);
	if (sample_pos < iRead) {
		num=iRead-sample_pos;
	} else {
		num=iRead+(SPEECHBUFFER - sample_pos);
	}
	num--;

	if (num>0)		
	//if (num>=0)		
	{
		if (speechbuf->Lock(sample_pos, num, (void**)&ptr1, &len1, (void**)&ptr2, &len2, 0) == DS_OK) 
		{
			SpeechProcess(ptr1, len1);				// load buffer
//			if (len2 > 0) {							// handle wraparound
//				SpeechProcess(ptr2, len2);			// load buffer
//			}
			speechbuf->Unlock(ptr1, len1, ptr2, len2);	

			sample_pos+=num;
			while (sample_pos >= SPEECHBUFFER)
				sample_pos-=SPEECHBUFFER;
		}
	}
}

//////////////////////////////////////////////////////
// 'DAC' Update function - drops a brief sample of
// volume level into the speech buffer at a fixed
// interval. Called as needed - does NOT use sample_pos
// since we aren't doing fixed chunks of audio
// running Speech and DAC at the same time won't
// work properly, but it seems unlikely to happen ;)
// Also, Barry Boone reports that his SoundFX plays
// back at up to 11kHz, but the speech buffer is only
// 8kHz, that may cause samples to sound worse than
// they should!!
//////////////////////////////////////////////////////
void DACUpdate(unsigned char val) {
	DWORD iRead, iWrite;
	int num;
	Byte *ptr1, *ptr2;
	DWORD len1, len2;
	int idx, pos;

	// we use real time to find the point to write the data to
	static LARGE_INTEGER freq={ 0, 0 };
	static LARGE_INTEGER last= { 0, 0 };
	LARGE_INTEGER tmp;
	static DWORD nWritePos=0;
	static float cum_distance=0.0f;

	if (speechbuf==NULL) {
		return;
	}

//	fputc(val, DacLog);

	if (0 == freq.QuadPart) {
		QueryPerformanceFrequency(&freq);
		QueryPerformanceCounter(&last);
		speechbuf->GetCurrentPosition(NULL, &nWritePos);
	} else {
		tmp.QuadPart=last.QuadPart;
		QueryPerformanceCounter(&last);
		// calculate where nWritePos is now
		tmp.QuadPart=last.QuadPart-tmp.QuadPart;
		if (tmp.QuadPart < 0) {
			return;
		}
		float distance=((float)tmp.QuadPart / (float)freq.QuadPart)*SPEECHRATE;
		cum_distance+=distance;
		nWritePos+=(int)cum_distance;
		//debug_write("Distance: %f", cum_distance);
		cum_distance-=(int)cum_distance;
		while (nWritePos >= SPEECHBUFFER) nWritePos-=SPEECHBUFFER;
	}

	num=10;		// tune this for number of samples to set
	if (speechbuf->Lock(nWritePos, num, (void**)&ptr1, &len1, (void**)&ptr2, &len2, 0) == DS_OK) {
		memset(ptr1, val, len1);
		if (len2 > 0) {
			memset(ptr2, val, len2);
		}

		speechbuf->Unlock(ptr1, len1, ptr2, len2);	
	}
}

//////////////////////////////////////////////////////
// Increment VDP Address
//////////////////////////////////////////////////////
void increment_vdpadd() 
{
	VDPADD=(++VDPADD) & 0x3fff;
}

//////////////////////////////////////////////////////////////
// Read from VDP chip
//////////////////////////////////////////////////////////////
Byte rvdpbyte(Word x)
{ 
	unsigned short z;

	if ((x>=0x8c00) || (x&1))
	{
		return(0);											// write address
	}

	if (x&0x0002)
	{	/* read status */
		z=VDPS;
		VDPS&=0x1f;			// reset interrupt, five sprite and collision bits
		vdpaccess=0;		// reset byte flag
		return((Byte)z);
	}
	else
	{ /* read data */
		z=vdpprefetch;
		vdpprefetch=VDP[VDPADD];
		increment_vdpadd();
		vdpaccess=0;
		return ((Byte)z);
	}
}

///////////////////////////////////////////////////////////////
// Write to VDP chip
///////////////////////////////////////////////////////////////
void wvdpbyte(Word x, Byte c)
{
	if (x<0x8c00 || (x&1)) 
	{
		return;							/* not going to write at that block */
	}

	if (x&0x0002)
	{	/* write address */
		tmpVDPADD=(tmpVDPADD>>8)|(c<<8);
		vdpaccess++;
		if (vdpaccess==2)
		{ 
			if (tmpVDPADD&0x8000)
			{ 
				wVDPreg((Byte)((tmpVDPADD&0x0700)>>8),(Byte)(tmpVDPADD&0x00ff));
				redraw_needed=1;
			} else {
				VDPADD=tmpVDPADD;
				if ((VDPADD&0x4000)==0) {	// set for reading?
					vdpprefetch=VDP[VDPADD];
					increment_vdpadd();
				} else {
					VDPADD&=0x3fff;			// writing, just mask the bit off
				}
			}
			vdpaccess=0;
		}
	}
	else
	{	/* write data */
//		if (VDPADD == 0x3b8) __asm { int 3 };
		VDP[VDPADD]=c;
		vdpprefetch=c;
		increment_vdpadd();
		redraw_needed=1;
		vdpaccess=0;
	}
}

////////////////////////////////////////////////////////////////
// Write to VDP Register
////////////////////////////////////////////////////////////////
void wVDPreg(Byte r, Byte v)
{ 
	int t;

	VDPREG[r]=v;

	if (r==7)
	{	/* color of screen, set color 0 (trans) to match */
		t=v&0xf;
		if (t) {
			TIPALETTE[0]=TIPALETTE[t];
		} else {
			TIPALETTE[0]=0;
		}
		redraw_needed=1;
	}

}

////////////////////////////////////////////////////////////////
// Write a byte to the sound chip
////////////////////////////////////////////////////////////////
void wsndbyte(Byte c)
{
	unsigned int x, idx;								// temp variable
	static int noise_volume=-10000;						// volume of noise
	static int noise_type=0;							// type of noise
	static int freq3=22000;								// frequency calculator
	static int oldVol[3]={0,0,0};						// tone generator old volumes
	bool fDACChanged=false;

	if (NULL == lpds) return;

	// 'c' contains the byte currently being written to the sound chip
	// all functions are 1 or 2 bytes long, as follows					
	//
	// BYTE		BIT		PURPOSE											
	//	1		0		always '1'										
	//			1-3		Operation:	000 - tone 1 frequency				
	//								001 - tone 1 volume					
	//								010 - tone 2 frequency				
	//								011 - tone 2 volume					
	//								100 - tone 3 frequency				
	//								101 - tone 3 volume					
	//								110 - noise control					
	//								111 - noise volume					
	//			4-7		Least sig. frequency bits for tone, or volume	
	//					setting (0-F), or type of noise.				
	//					(volume F is off)								
	//					Noise set:	4 - always 0						
	//								5 - 0=periodic noise, 1=white noise 
	//								6-7 - shift rate from table, or 11	
	//									to get rate from voice 3.		
	//	2		0-1		Always '0'. This byte only used for frequency	
	//			2-7		Most sig. frequency bits						
	//
	// Commands are instantaneous

	switch (c&0xf0)										// check command
	{	
	case 0x80:											// Voice 1 frequency
		last_byte=c;
		need_byte=1;									// needs a second byte
		break;

	case 0x90:
		x=(c&0x0f);										// Voice 1 volume
		if (x==0xf) {
			voice[0]->SetVolume(DSBVOLUME_MIN);
			DAC[0]=0;
			if (fVoiceSimulatedDAC[0]) fDACChanged=true;
		} else {
			DAC[0]=15-x;
			if (fVoiceSimulatedDAC[0]) {
				fDACChanged=true;
			} else {
				voice[0]->SetVolume(x*(volume_mult));
			}

			if ((!fSampleShouldLoop[0])&&(oldVol[0]==0x0f)) {
				DWORD stat;
				if ((DS_OK == voice[0]->GetStatus(&stat))&&(stat&DSBSTATUS_PLAYING)) {
					voice[0]->SetCurrentPosition(0);	// ...and retrigger sample
				} else {
					voice[0]->Play(0, 0, 0);
				}
			}
		}
		oldVol[0]=x;
		break;

	case 0xa0:
		last_byte=c;									// Voice 2 frequency
		need_byte=2;									// needs a second byte
		break;

	case 0xb0:
		x=(c&0x0f);										// Voice 2 volume
		if (x==0xf) {
			voice[1]->SetVolume(DSBVOLUME_MIN);
			DAC[1]=0;
			if (fVoiceSimulatedDAC[1]) fDACChanged=true;
		} else {
			voice[1]->SetVolume(x*(volume_mult));
			DAC[1]=15-x;
			if (fVoiceSimulatedDAC[1]) fDACChanged=true;
			if ((!fSampleShouldLoop[1])&&(oldVol[1]==0x0f)) {
				DWORD stat;
				if ((DS_OK == voice[1]->GetStatus(&stat))&&(stat&DSBSTATUS_PLAYING)) {
					voice[1]->SetCurrentPosition(0);	// ...and retrigger sample
				} else {
					voice[1]->Play(0, 0, 0);
				}
			}
		}
		oldVol[1]=x;
		break;

	case 0xc0:
		last_byte=c;									// Voice 3 frequency
		need_byte=3;									// needs a second byte
		break;

	case 0xd0:
		x=(c&0x0f);										// Voice 3 volume
		if (x==0xf) {
			voice[2]->SetVolume(DSBVOLUME_MIN);
			DAC[2]=0;
			if (fVoiceSimulatedDAC[2]) fDACChanged=true;
		} else {
			voice[2]->SetVolume(x*(volume_mult));
			DAC[2]=15-x;
			if (fVoiceSimulatedDAC[2]) fDACChanged=true;
			if ((!fSampleShouldLoop[2])&&(oldVol[2]==0x0f)) {
				DWORD stat;
				if ((DS_OK == voice[2]->GetStatus(&stat))&&(stat&DSBSTATUS_PLAYING)) {
					voice[2]->SetCurrentPosition(0);	// ...and retrigger sample
				} else {
					voice[2]->Play(0, 0, 0);
				}
			}
		}
		oldVol[2]=x;
		break;

	case 0xe0:
		x=(c&0x07);										// Noise - get type
		for (idx=0; idx<8; idx++)
		{
			if (idx==x)
				noise[idx]->SetVolume(noise_volume);
			else
				noise[idx]->SetVolume(DSBVOLUME_MIN);	// turn off noises, except the one we want
		}
		noise_type=x;
		break;

	case 0xf0:											// Noise volume
		x=(c&0x0f);
		if (x==0xf) {
			noise[noise_type]->SetVolume(DSBVOLUME_MIN);
			noise_volume=DSBVOLUME_MIN;
		} else {
			noise[noise_type]->SetVolume(x*(volume_mult));
			noise_volume=x*(volume_mult);
		}
		break;

	default:											// unknown byte
		if (need_byte)									// if we were waiting for it...
		{
			need_byte--;								// now channel 0-2
			x=((last_byte&0x0f) | ((c&0x3f)<<4)) + 1;	// Get frequency code
			x=(111860/x)*22050;							// Make sample rate in HZ (22khz samples)
			//debug_write("Settting sample rate for voice %d to %dHZ", need_byte-1, x/22050);
			if (x>220500000) {	// 10khz - more than that won't play well anyway!
				fVoiceSimulatedDAC[need_byte]=true;
				debug_write("Enabling DAC mode on channel %d", need_byte);
				fDACChanged=true;
			} else {
				fVoiceSimulatedDAC[need_byte]=false;
				debug_write("Disabling DAC mode on channel %d", need_byte);
				fDACChanged=true;
			}
			x/=440;										// adjust to balance (sample recorded as 440hz)
			if ( x > DSBFREQUENCY_MAX )
				x = DSBFREQUENCY_MAX ;
			voice[need_byte]->SetFrequency(x);		// set new frequency

			if (!fSampleShouldLoop[need_byte]) {
				DWORD stat;
				if ((DS_OK == voice[need_byte]->GetStatus(&stat))&&(stat&DSBSTATUS_PLAYING)) {
					voice[need_byte]->SetCurrentPosition(0);	// ...and retrigger sample
				} else {
					voice[need_byte]->Play(0, 0, 0);
				}
			}
			if (need_byte==2)							// If third voice, save and adjust for noise channel
			{
				freq3=x;								
				noise[3]->SetFrequency(freq3);
				noise[7]->SetFrequency(freq3);
			}
			need_byte=0;								// don't need another byte
		}
		break;
	}

	if (fDACChanged) {
		// we need to update the DAC channel. For that, we cheat and use the speech synth
		// channel (although it being at only 8khz may be a problem, we'll see how it works)
		int val=0x80;
		if (fVoiceSimulatedDAC[0]) val+=DAC[0]*25/10;
		if (fVoiceSimulatedDAC[1]) val+=DAC[1]*25/10;
		if (fVoiceSimulatedDAC[2]) val+=DAC[2]*25/10;
		DACUpdate(val);
	}
}

//////////////////////////////////////////////////////////////////
// Read a byte from GROM
//////////////////////////////////////////////////////////////////
Byte rgrmbyte(Word x)
{
	unsigned int z;										// temp variable

//	debug_write("Read Main  GROM >%04x", GRMADD);

	if (x>=0x9c00)
	{
		return(0);										// write address
	}

	if (x&0x0002)
	{
		grmaccess=2;									// read GROM address
		z=(GRMADD&0xff00)>>8;
		GRMADD=(((GRMADD&0xff)<<8)|(GRMADD&0xff));		// read is destructive
		return((Byte)z);
	}
	else
	{
		grmaccess=2;									// read data
		z=grmdata;
		grmdata=GROM[GRMADD++];
		return((Byte)z);
	}
}

//////////////////////////////////////////////////////////////////
// Write a byte to GROM
//////////////////////////////////////////////////////////////////
void wgrmbyte(Word x, Byte c)
{
	if (x<0x9c00) 
	{
		return;											// read address
	}

	if (x&0x0002)
	{
		GRMADD=(GRMADD<<8)|(c&0x00ff);					// write GROM address
		grmaccess--;
		if (grmaccess==0)
		{ 
			grmaccess=2;								// prefetch emulation
			grmdata=GROM[GRMADD++];
		}
	}
	else
	{
		grmaccess=2;
		warn("Writing to GROM!!");						// write to GROM not supported
		//GROM[GRMADD++]=c;								// don't write data (it's G*ROM*)
		GRMADD++;
	}
}

//////////////////////////////////////////////////////////////////
// Read a byte from P-Code GROM
//////////////////////////////////////////////////////////////////
Byte rpcodebyte(Word x)
{
	unsigned int z;										// temp variable

//	debug_write("Read PCODE GROM >%04x", PCODEADD);

	if (x>0x5bfe)
	{
		return(0);										// write address
	}

	if (x&0x0002)
	{
		pcodeaccess=2;									// read GROM address
		z=(PCODEADD&0xff00)>>8;
		PCODEADD=(((PCODEADD&0xff)<<8)|(PCODEADD&0xff));// read is destructive
		return((Byte)z);
	}
	else
	{
		pcodeaccess=2;									// read data
		z=pcodedata;
		pcodedata=PCODE[PCODEADD++];
		return((Byte)z);
	}
}

//////////////////////////////////////////////////////////////////
// Write a byte to P-Code GROM
//////////////////////////////////////////////////////////////////
void wpcodebyte(Word x, Byte c)
{
	if (x<0x5ffc) 
	{
		return;											// read address
	}

	if (x&0x0002)
	{
		PCODEADD=(PCODEADD<<8)|(c&0x00ff);				// write GROM address
		pcodeaccess--;
		if (pcodeaccess==0)
		{ 
			pcodeaccess=2;								// prefetch emulation
			pcodedata=PCODE[PCODEADD++];
		}
	}
	else
	{
		pcodeaccess=2;
		warn("Writing to P-CODE GROM!!");				// write to GROM not supported
		//PCODE[PCODEADD++]=c;							// don't write data (it's G*ROM*)
		PCODEADD++;
	}
}

//////////////////////////////////////////////////////////////////
// Write a bit to CRU
//////////////////////////////////////////////////////////////////
void wcru(Word ad, int bt)
{
	if (ad>=0x800) {
		// DSR space
		ad<<=1;		// put back into familiar space. A bit wasteful, but devices aren't high performance
		if (bt) {
			// bit 0 enables the DSR rom, so we'll check that first
			if ((ad&0xff) == 0) {
				nCurrentDSR=(ad>>8)&0xf;
//				debug_write("Enabling DSR at >%04x", ad);
				return;
			}
			// else, it's device dependent
			switch (ad&0xff00) {
			case 0x1f00:	// pCode card
				if ((ad&0xff) == 0x80) {
					// bank switch
//					debug_write("Switching P-Code to bank 2");
					nDSRBank[0xf]=1;
				}
				break;
			}
		} else {
			// bit 0 enables the DSR rom, so we'll check that first
			if ((ad&0xff) == 0) {
				if (((ad>>8)&0xf) == nCurrentDSR) {
					nCurrentDSR=-1;
//					debug_write("Disabling DSR at >%04x", ad);
					return;
				}
			}
			// else, it's device dependent
			switch (ad&0xff00) {
			case 0x1f00:	// pCode card
				if ((ad&0xff) == 0x80) {
					// bank switch
//					debug_write("Switching P-Code to bank 1");
					nDSRBank[0xf]=0;
				}
				break;
			}
		}
		return;
	} else {
		ad=(ad&0x0fff);										// get actual CRU line

		if (bt) {											// write the data
			CRU[ad]=1;
		} else {
			CRU[ad]=0;
			if (ad == 0) {
				// Turning off timer mode - start timer
				timer9901=CRU[1];
				timer9901|=CRU[2]<<1;
				timer9901|=CRU[3]<<2;
				timer9901|=CRU[4]<<3;
				timer9901|=CRU[5]<<4;
				timer9901|=CRU[6]<<5;
				timer9901|=CRU[7]<<6;
				timer9901|=CRU[8]<<7;
				timer9901|=CRU[9]<<8;
				timer9901|=CRU[10]<<9;
				timer9901|=CRU[11]<<10;
				timer9901|=CRU[12]<<11;
				timer9901|=CRU[13]<<12;
				timer9901|=CRU[14]<<13;
				timer9901|=CRU[15]<<14;
				starttimer9901=timer9901;
				debug_write("Setting 9901 timer to %d ticks", timer9901);
			}
		}
	}
}
extern "C" unsigned int xbox_read_port( int which );

#define JOYSTICKID1 0
#define JOYSTICKID2 1
//////////////////////////////////////////////////////////////////
// Read a bit from CRU
//////////////////////////////////////////////////////////////////
int rcru(Word ad)
{
	int ret,col;										// temp variables
	int joyX, joyY, joyFire;
	int joy1col, joy2col;
	unsigned int joyval ;

	if ((CRU[0]==0)&&(ad<=16)&&(ad>0)) {				// read elapsed time from timer
		//if (ad==2) debug_write("Reading 9901 timer at %d ticks", timer9901);
		Word mask=0x01<<(ad-1);
		if (timer9901 & mask) {
			return 1;
		} else {
			return 0;
		}
	}

	// Read external hardware
	joyX=0;
	joyY=0;
	joyFire=0;

	// only certain CRU bits are implemented, check them
	if (ad >= 0x1000) {
		// Plugin card - none supported yet
		return 1;	// false
	}

	// The CRU bits >0000 through >001f are repeated through the whole 4k range!
	ad=(ad&0x001f);										// get actual CRU line
	ret=1;												// default return code (false)

	// keyboard/joystick. PC Keyboard is currently *NOT* remapped		
	// so that it's keys work. (TODO)
	
	// keyboard reads as an array. Bits 24, 26 and 28 set the line to	
	// scan (columns). Bits 6-14 are used for return. 0 means on.		
	// The address was divided by 2 before being given to the routine	

	// Some hacks here for 99/4 scanning
	if ((ad>=0x03)&&(ad<=0x0a))
	{	
		if ((ad==0x07)&&(CRU[0x15]==0))					// is it ALPHA LOCK?
		{	
			ret=1;
#if 0
			if (GetKeyState(VK_CAPITAL) & 0x01)			// check CAPS LOCK (on?)
			{	
				ret=0;									// set Alpha Lock on
			}
#endif
		}
		else											// other key
		{
			col=(CRU[0x14]==0 ? 1 : 0) | (CRU[0x13]==0 ? 2 : 0) | (CRU[0x12]==0 ? 4 : 0);	// get column

			if (keyboard) {
				// 99/4A
				joy1col=4;
				joy2col=0;
			} else {
				// 99/4
				joy1col=2;
				joy2col=4;
			}


			if ((col == joy1col) || (col == joy2col))				// reading joystick
			{	
				// TODO: This still reads the joystick many times for a single scan, but it's better ;)

				if ( col==joy2col )
				{
					joyval = xbox_read_port( 1 ) ;
				}
				else
				{
					joyval = xbox_read_port( 0 ) ;
				}

				if ( joyval & 0x01 )
				{
					joyY = 4 ;
				}
				if ( joyval & 0x02 )
				{
					joyY = -4 ;
				}
				if ( joyval & 0x04 )
				{
					joyX = -4 ;
				}
				if ( joyval & 0x08 )
				{
					joyX = 4 ;
				}
				if ( joyval & 0x10 )
				{
					joyFire = 1 ;
				}
#if 0
				if (fJoy) {
					int device;

					device=-1;

					if (col==joy2col) {
						switch (joy2mode) {
							case 1: device=JOYSTICKID1; break;
							case 2: device=JOYSTICKID2; break;
						}
					} else {
						switch (joy1mode) {
							case 1: device=JOYSTICKID1; break;
							case 2: device=JOYSTICKID2; break;
						}
					}

					if (device!=-1) {
						memset(&myJoy, 0, sizeof(myJoy));
						myJoy.dwSize=sizeof(myJoy);
						myJoy.dwFlags=JOY_RETURNBUTTONS | JOY_RETURNX | JOY_RETURNY | JOY_USEDEADZONE;
						if (JOYERR_NOERROR == joyGetPosEx(device, &myJoy)) {
							if (0!=myJoy.dwButtons) {
								joyFire=1;
							}
							if (myJoy.dwXpos<0x4000) {
								joyX=-4;
							}
							if (myJoy.dwXpos>0xC000) {
								joyX=4;
							}
							if (myJoy.dwYpos<0x4000) {
								joyY=4;
							}
							if (myJoy.dwYpos>0xC000) {
								joyY=-4;
							}
						}
					} else {	// read the keyboard
						if (key[VK_TAB]) {
							joyFire=1;
						}
						if (key[VK_LEFT]) {
							joyX=-4;
						}
						if (key[VK_RIGHT]) {
							joyX=4;
						}
						if (key[VK_UP]) {
							joyY=4;
						}
						if (key[VK_DOWN]) {
							joyY=-4;
						}
					}
				}
#endif

				if (ad == 3)
				{	
					if ((key[KEYS[keyboard][col][0]])||(joyFire))	// button reads normally
					{
						ret=0;
					}
				}
				else
				{
					if (key[KEYS[keyboard][col][ad-3]])		// stick return (*not* inverted. Duh)
					{	
						ret=0;
					}
					if (ret) {
						switch (ad-3) {						// Check real joystick
						case 1: if (joyX ==-4) ret=0; break;
						case 2: if (joyX == 4) ret=0; break;
						case 3: if (joyY ==-4) ret=0; break;
						case 4: if (joyY == 4) ret=0; break;
						}
					}
				}
			}
			else
			{
				if (key[KEYS[keyboard][col][ad-3]])				// normal key
				{	
					ret=0;
				}
			}

		}
	}

	// are we checking VDP interrupt?
	if (ad == 0x02) {		// that's the only int we have
		ret=0;		
	}

	// no other devices at this time

	return(ret);
}

/////////////////////////////////////////////////////////////////////////
// Write a line to the debug buffer displayed on the debug screen
/////////////////////////////////////////////////////////////////////////
void debug_write(char *s, ...)
{
	char buf[1024];

	vsprintf(buf, s, (char*)((&s)+1));

	if (!quitflag) {
		OutputDebugString(buf);
		OutputDebugString("\n");
	}

	buf[DEBUGLEN-1]='\0';

	memcpy(&lines[0][0], &lines[1][0], 29*DEBUGLEN);				// scroll data
	strncpy(&lines[29][0], buf, DEBUGLEN);							// copy in new line
	memset(&lines[29][strlen(buf)], 0x20, DEBUGLEN-strlen(buf));	// clear rest of line
	lines[29][DEBUGLEN-1]='\0';										// zero terminate
}

///////////////////////////////////////////////////////////////////
// Console interrupt simulator
///////////////////////////////////////////////////////////////////
void ConsoleInterrupt()
{
	Byte x1;											// temp var
	int t1,t2;											// temp vars

	interrupt_needed=0;
	x1=rcpubyte(0x83c2);
	if ((x1&0x80)==0)									// if console routine on
	{ 
		if ((x1&0x40)==0)								// update sprites
	    {												// tables are fixed in memory
			for (num_sprites=0; num_sprites<rcpubyte(0x837a); num_sprites++) // note: not masking the sprites!
			{	
				offset=num_sprites<<2;					// I didn't fully understand the sprite
				y_speed=(Byte)VDP[0x780+offset];		// movement routine, and my own simplified
				x_speed=(Byte)VDP[0x781+offset];		// version didn't work as expected. So this
				t1=(Byte)VDP[0x782+offset];				// code is more or less a straight translation
				t2=(Byte)VDP[0x783+offset];				// of the 9900 code ;)
				t1a=(y_speed&0xf0)>>4;
				t1b=(y_speed&0xf);
				t2a=(x_speed&0xf0)>>4;
				t2b=(x_speed&0xf);

				if (t1a>0x7)
				{	
					t1a-=0xf;
					if (t1b==0)
						t1a-=1;
				}
				if (t2a>0x7)
				{	
					t2a-=0xf;
					if (t2b==0)
						t2a-=1;
				}
	
				t1b=(y_speed&0xf);
				if ((y_speed>0x7f)&&(t1b))
					t1b=(t1b-0x10);
				if ((x_speed>0x7f)&&(t2b))
					t2b=(t2b-0x10);

				sprite_y=(Byte)VDP[0x300+offset];
				sprite_x=(Byte)VDP[0x301+offset];
				
				ty=sprite_y;
				tx=sprite_x;
			
				t1+=t1b;
				t2+=t2b;

				sprite_y+=t1a;
				sprite_x+=t2a;
	
				if (t1>0xf)
				{	
					t1-=0xf;
					sprite_y++;
				}
				if (t1<0)
				{	
					t1+=0xf;
					sprite_y--;
				}

				if (t2>0xf)
				{	
					t2-=0xf;
					sprite_x++;
				}
				if (t2<0)
				{	
					t2+=0xf;
					sprite_x--;
				}

				sprite_x&=0xff;
				sprite_y&=0xff;

				if ((sprite_y>=0xc0)&&(sprite_y<=0xe0))
				{	
					if (y_speed>0x7f)
						sprite_y-=0x20;
					else
						sprite_y+=0x20;
				}
	
				VDP[0x300+offset]=(Byte)sprite_y;
				VDP[0x301+offset]=(Byte)sprite_x;
				VDP[0x782+offset]=(Byte)t1;
				VDP[0x783+offset]=(Byte)t2;
			
				if ((ty!=sprite_y)||(tx!=sprite_x))
					redraw_needed=1;

			}
		}
    
		if ((x1&0x20)==0)								// update sound list
	    { 
	      if (rcpubyte(0x83ce))
		  {		wcpubyte(0x83ce, rcpubyte(0x83ce)-1);
				if (rcpubyte(0x83ce)==0)
				{	
					snd_address=romword(0x83cc);
					if (rcpubyte(0x83fd) & 0x01) {
						sndlist=(&VDP[snd_address]);	// VDP sound list
					} else
					{
						sndlist=(&GROM[snd_address]);	// GROM sound list
					}
					data=*sndlist++;
					snd_address+=data;
					if ((data!=0)&&(data!=0xff))
					{	
						while (data--)
						{	
							wsndbyte(*sndlist++);
						}
						data=*sndlist++;
						snd_address+=2;
						wrword(0x83cc, snd_address);
						wcpubyte(0x83ce, data);
					}	
					else
					{	
						if (data==0xff)
						{	
							if (rcpubyte(0x83fd) & 0x1)
								wcpubyte(0x83fd, rcpubyte(0x83fd)&0xfe);
							else
								wcpubyte(0x83fd, rcpubyte(0x83fd)|0x01);
						}
						wcpubyte(0x83cc, *sndlist++);
						wcpubyte(0x83cd, *sndlist++);
						wcpubyte(0x83ce, 0x01);
					}
				}
			}
		}
	
	    if ((x1&0x10)==0)								// Check QUIT key
		{ 
			if (((key[VK_MENU])||(key[VK_CONTROL]))&&(key[187]))
			{ 
				while (key['=']);						// wait for key release
				ST=(ST&0xfff0);							// disable interrupts
				WP=romword(0x0000);						// Reset = BLWP @>0000
				wrword(WP+26,0xffff);
				wrword(WP+28,PC);
				wrword(WP+30,ST);
				PC=romword(0x0002);
				grmaccess=0;		// No GROM Access yet
				pcodeaccess=0;
				vdpaccess=0;		// No VDP address writes yet 
				interrupt_needed=0;	// No interrupt missed yet
				nCurrentDSR=-1;		// no DSR selected
				memset(nDSRBank, 0, sizeof(nDSRBank));	// base DSR bank
			}
	    }
	}

	// the following parts can't be turned off, except by LIMI or disabling VDP ints 
  
  	wrword(0x83d6,romword(0x83d6)+2);	// this blanks the screen when it 
										// wraps around to zero 
	
	if (romword(0x83d6)==0)				// we're wrapped					
	{
		t1=rcpubyte(0x83d4);			// read the byte...					
		t1=(t1|0x20)&0xbf;				// int turns ON VDP interrupts, and	
										// turns OFF display bit			
		VDPREG[1]=(Byte)t1;				// write it							
	}
  
	wcpubyte(0x8379, rcpubyte(0x8379)+rcpubyte(0x83fc));// VDP Int Timer - not sure how it	
										// gets this, but here it is. Add	
										// byte from R14 of the GPL WP at
										// >83e0. Can't trace what it is.	
										// E/A manual calls it System Status

	wcpubyte(0x837b, VDPS);				// VDP Status register - copied to CPU RAM	
	VDPS&=0x1f;							// A read clears the register
    
	if (romword(0x83C4)) 				// call the user interrupt routine 
	{	// pretend that we really were called with BLWP by saving the values	
		// into the appropriate workspace (0x83c0)
		
		wrword(0x83da,WP);				// WP in R13 
		wrword(0x83dc,PC);				// PC in R14 
		wrword(0x83de,ST);				// ST in R15 

		ST=(ST&0xfff0);					// disable interrupts

		/* now load the correct workspace, and perform a branch and link to the address */
		WP=0x83e0;				/* interrupt workspace is 0x80e0						*/
		wrword(0x83f6,0x0ab8);	/* write into new R11 hard-coded return address in ROM	*/
		PC=romword(0x83c4);		/* off we go.. TI should return normally now			*/
	}
}

//////////////////////////////////////////////////////////////
// 'Retrace' counter for timing - runs at 50 or 60 hz
//////////////////////////////////////////////////////////////
void __cdecl TimerThread()
{
	HANDLE hWaiter;
	LARGE_INTEGER nWait;
	DWORD lastTick ;
	DWORD elapsed ;
	int nOldHzRate=0;	// used to detect a change

	time(&STARTTIME);

	// The 9901 timer decrements every 64 periods, with the TI having a 3MHz clock speed
	// Thus, it decrements 46875 times per second. If CRU bit 3 is on, we trigger an
	// interrupt when it expires

	hWaiter=CreateWaitableTimer(NULL, TRUE, NULL);
	//if (NULL == hWaiter) {
		//debug_write("Failed to create waitable timer, error 0x%08x\n", GetLastError());
		//MessageBox(myWnd, "Unable to start timer system.", "Classic99 Error", MB_ICONSTOP|MB_OK);
		//ExitProcess(-1);
	//}

	while (quitflag==0) {
		if ((PauseInactive)){ //&&(myWnd != GetForegroundWindow())) {
			// Reduce CPU usage when inactive (temp hack)
			Sleep(100);
		} else {
			if (nOldHzRate != hzRate) {
				// units are 100nS each (so 10,000,000 = 1 second). Negative means relative time
				nWait.QuadPart=-(10000000/hzRate);
				nOldHzRate=hzRate;
				// Set the timer right away
				if (!SetWaitableTimer(hWaiter, &nWait, 0, NULL, NULL, FALSE)) {
					//debug_write("Failed to set waitable timer, error 0x%08x\n", GetLastError());
					//MessageBox(myWnd, "Unable to set timer system.", "Classic99 Error", MB_ICONSTOP|MB_OK);
					//ExitProcess(-1);
				}
			}

			//Sleep(1) ;
#if 1
			if (WAIT_TIMEOUT == WaitForSingleObject(hWaiter, 100)) {
				// well, something went wrong if this happens, but whatever :) Check the quitflag.
				continue;
			}
	
			//elapsed = GetTickCount() - lastTick ;
			//lastTick = GetTickCount() ;

			// Reset the timer for the next tick while we process this one
			if (!SetWaitableTimer(hWaiter, &nWait, 0, NULL, NULL, FALSE)) {
				debug_write("Failed to set waitable timer, error 0x%08x\n", GetLastError());
				//MessageBox(myWnd, "Unable to set timer system.", "Classic99 Error", MB_ICONSTOP|MB_OK);
				//ExitProcess(-1);
			}
#endif

			// Somewhat better 9901 timing - still very wrong though (should be more accurate than per frame -
			// need to use a second timer when I get time)
			if (timer9901>0) {
				timer9901-=CLOCK_MHZ/hzRate;

				if (timer9901 < 1) {
					timer9901=starttimer9901;
					debug_write("9901 timer expired");
					if ((CRU[3])&&(ST&0xF)) {			// Timer interrupts, and interrupt mask
						wrword(0x83da,WP);				// WP in R13 
						wrword(0x83dc,PC);				// PC in R14 
						wrword(0x83de,ST);				// ST in R15 
						
						/* now load the correct workspace, and perform a branch and link to the address */
						WP=romword(0x0004);
						PC=romword(0x0006);

						nCycleCount+=22;
					}
				}
			}

			Counting();
			SpeechUpdate();
		}	
	}

	time(&ENDTIME);
	debug_write("Seconds: %ld, ticks: %ld", (long)ENDTIME-STARTTIME, ticks);

	debug_write("Ending Timer Thread");
	CloseHandle(hWaiter);
}

int doingvdp = 0 ;

////////////////////////////////////////////////////////////////
// Timer calls this function each tick
////////////////////////////////////////////////////////////////
void Counting()
{
	ticks++;
	retrace_count++;
	if (CRU[2]) {			// VDP interrupts enabled?
		interrupt_needed=1;
	}

	//if (memWnd || regWnd || asmWnd || dbgWnd) {
		//draw_debug();
	//}
}

////////////////////////////////////////////////////////////////
// stpcpy - strcpy then return the address of the \0
////////////////////////////////////////////////////////////////
char *stpcpy(char *dest, char *src)
{
	while (*src) {
		*dest++ = *src++;
	}
	*dest++ = *src++;
  return (dest-1);
}

////////////////////////////////////////////////////////////////
// stpncpy - strncpy then return the address of the \0
////////////////////////////////////////////////////////////////
char *stpncpy(char *dest, char *src, int n)
{
	while (n--) {
		*dest++=*src;
		if (!(*src)) {
			break;
		}
		src++;
	}
	return (dest-1);
}



extern Word PC;											// Program Counter
extern Word WP;											// Workspace Pointer
extern Word X_flag;										// Set during an 'X' instruction
extern Word ST;											// Status register
extern Word in,D,S,Td,Ts,B;								// Opcode interpretation
extern int nCycleCount;									// Used in CPU throttle
extern struct {
	int LengthSectors;
	int FileType;
	int RecordsPerSector;
	int BytesInLastSector;
	int RecordLength;
	int NumberRecords;		// or sectors in a variable file
} FileInfo;
extern bool atEOF;	// Set true if last read hit EOF. Hacky, but should work in most cases
extern int nNextVarRec;	// I dunno if this is right - autocount var records (1 file at a time this way)
extern DWORD sample_pos;											// used in the speech buffer code
extern Byte CPU[65536];							// Main CPU Memory
extern Byte CPU2[8192];							// Cartridge space bank-switched (ROM >6000 space)
extern Byte GROM[65536];							// GROM space
extern Byte ROMMAP[65536];							// Write-protect map of CPU space
extern Byte CRU[4096];								// CRU space
extern Byte SPEECH[65536];							// Speech Synth ROM
extern Byte DSR[16][16384];						// 16 CRU bases, up to 16k each (ROM >4000 space)
extern int  nDSRBank[16];							// Is the DSR bank switched?
extern Byte PCODE[65536];							// P-Code GROM space
extern Word GRMADD;								// GROM Address counter
extern Byte grmaccess,grmdata;						// GROM Prefetch emulation
extern Word PCODEADD;								// GROM Address counter (P-code)
extern Byte pcodeaccess,pcodedata;					// GROM Prefetch emulation (P-code)
extern int  nSystem;			// Which system do we default to?
extern int  nCartGroup;		// Which cart group?
extern int	 nCart;			// Which cart is loaded (-1=none)
extern bool fKeyEverPressed;	// used to suppress warning when changing cartridges
extern int  nLoadedUserCarts;
extern char key[256];										// keyboard state buffer
extern int need_byte;										// flag for whether waiting on a second byte
extern int last_byte;										// copy of last byte
extern int volume_mult;									// Volume multiplier
extern int nSampleIdx[3];									// Which sample (per voice)
extern bool fSampleShouldLoop[3];							// And whether to loop it
extern bool fVoiceSimulatedDAC[3];							// true if frequency is so high we should use DAC mode
extern int  DAC[3];										// actual DAC level (0 (silent) - 15 (loudest) )
extern int bank;								// Cartridge bank switch
extern int xb;											// Is second bank (XB) loaded?
extern int nCurrentDSR;									// Which DSR Bank are we on?
extern int max_cpf;							// Maximum cycles per frame
extern int oldmax;									// copy of same
extern int idling;										// Set if an IDLE has occured
extern int speedup_float;								// speedup floating point operations
extern int simulate_int;								// simulate console interrupt (faster)
extern int slowdown_keyboard;							// slowdown keyboard autorepeat in the GROM code
extern int cpucount, cpuframes;							// CPU counters for timing
extern int last_count;									// Separates video redraw and video interrupts
extern int timercount;										// Used to estimate runtime
extern int timer9901;									// 9901 interrupt timer
extern int starttimer9901;								// and it's set time
extern int keyboard;										// keyboard map (09/4, 199/4A)
extern int retrace_count;								// count on the 60hz timer
extern long ticks;
extern int num_sprites;									// Number of active sprites
extern int y_speed;										// X pixel speed
extern int x_speed;										// Y pixel speed
extern int t1, t1a, t1b, t2, t2a, t2b;						// temp variables
extern int sprite_y, sprite_x, offset;						// Sprite location
extern int ty, tx;											// Temp location
extern int snd_address;									// Sound chip data address
extern int TIPALETTE[16];
extern int SIT;									// Screen Image Table
extern int CT;										// Color Table
extern int PDT;									// Pattern Descriptor Table
extern int SAL;									// Sprite Allocation Table
extern int SDT;									// Sprite Descriptor Table
extern int CTsize;									// Color Table size in Bitmap Mode
extern int PDTsize;								// Pattern Descriptor Table size in Bitmap Mode
extern Byte VDP[16384];							// Video RAM
extern Byte SprColBuf[256][192];					// Sprite Collision Buffer
extern int SprColFlag;								// Sprite collision flag
extern int redraw_needed;							// redraw flag
extern int interrupt_needed;						// interrupt flag
extern int skip_interrupt;							// flag for some instructions
extern Byte VDPREG[9];								// VDP read-only registers
extern Byte VDPS;									// VDP Status register
extern Word VDPADD;								// VDP Address counter
extern Word tmpVDPADD;								// Temp VDP address
extern int vdpaccess;								// VDP Write count
extern Byte vdpprefetch;							// VDP Prefetch
extern Byte hzRate;								// flag for 50 or 60hz
extern Byte Recording;								// Flag for AVI recording
extern Byte RecordFrame;							// Current frame recorded (currently we only write 1/4 of the frames)
extern int MaintainAspect;							// Flag for Aspect ratio
extern int StretchMode;							// Setting for video stretching
extern DWORD ssample_pos;
extern DWORD buffer_len;
extern int emulation_rate;
extern int INUSE;
#define FIFO_SIZE 16
extern unsigned char fifo[FIFO_SIZE];
extern int fifo_head;
extern int fifo_tail;
extern int fifo_count;
extern int bits_taken;
extern int tms5220_speaking;
extern int first_frame;
extern int irq_pin;
extern int talk_status;
extern int buffer_low;
extern int buffer_empty;
extern int speak_external;
extern unsigned short old_energy;
extern unsigned short old_pitch;
extern int old_k[10];
extern unsigned short new_energy;
extern unsigned short new_pitch;
extern int new_k[10];
extern unsigned short current_energy;
extern unsigned short current_pitch;
extern int current_k[10];
extern unsigned short target_energy;
extern unsigned short target_pitch;
extern int target_k[10];
extern int interp_count;       /* number of interp periods (0-7) */
extern int sample_count;       /* sample number within interp (0-24) */
extern int pitch_count;
extern int u[11];
extern int x[10];
extern int randbit;
extern unsigned long speechROMaddr;
extern Byte data_register;
extern int external_read_from_data;


void tisavestate( char *filename )
{
	FILE *sm ;

	sm = fopen ( filename, "wb" ) ;

	if ( sm == NULL )
		return ;

	fwrite( &FileInfo, sizeof(FileInfo), 1, sm ) ;

	fwrite( &atEOF, sizeof(bool), 1, sm ) ;
	fwrite( &fKeyEverPressed, sizeof(bool), 1, sm ) ;

	fwrite( &PC, sizeof(unsigned short), 1, sm ) ;
	fwrite( &WP, sizeof(unsigned short), 1, sm ) ;
	fwrite( &X_flag, sizeof(unsigned short), 1, sm ) ;
	fwrite( &ST, sizeof(unsigned short), 1, sm ) ;
	fwrite( &in, sizeof(unsigned short), 1, sm ) ;
	fwrite( &D, sizeof(unsigned short), 1, sm ) ;
	fwrite( &S, sizeof(unsigned short), 1, sm ) ;
	fwrite( &Td, sizeof(unsigned short), 1, sm ) ;
	fwrite( &Ts, sizeof(unsigned short), 1, sm ) ;
	fwrite( &B, sizeof(unsigned short), 1, sm ) ;
	fwrite( &GRMADD, sizeof(unsigned short), 1, sm ) ;
	fwrite( &PCODEADD, sizeof(unsigned short), 1, sm ) ;
	fwrite( &VDPADD, sizeof(unsigned short), 1, sm ) ;
	fwrite( &tmpVDPADD, sizeof(unsigned short), 1, sm ) ;
	fwrite( &old_energy, sizeof(unsigned short), 1, sm ) ;
	fwrite( &old_pitch, sizeof(unsigned short), 1, sm ) ;
	fwrite( &new_energy, sizeof(unsigned short), 1, sm ) ;
	fwrite( &new_pitch, sizeof(unsigned short), 1, sm ) ;
	fwrite( &current_energy, sizeof(unsigned short), 1, sm ) ;
	fwrite( &current_pitch, sizeof(unsigned short), 1, sm ) ;
	fwrite( &target_energy, sizeof(unsigned short), 1, sm ) ;
	fwrite( &target_pitch, sizeof(unsigned short), 1, sm ) ;

	fwrite( &nCycleCount, sizeof(int), 1, sm ) ;
	fwrite( &nNextVarRec, sizeof(int), 1, sm ) ;
	fwrite( &nSystem, sizeof(int), 1, sm ) ;
	fwrite( &nCartGroup, sizeof(int), 1, sm ) ;
	fwrite( &nCart, sizeof(int), 1, sm ) ;
	fwrite( &sample_pos, sizeof(int), 1, sm ) ;
	fwrite( &nLoadedUserCarts, sizeof(int), 1, sm ) ;
	fwrite( &need_byte, sizeof(int), 1, sm ) ;
	fwrite( &last_byte, sizeof(int), 1, sm ) ;
	fwrite( &volume_mult, sizeof(int), 1, sm ) ;
	fwrite( &bank, sizeof(int), 1, sm ) ;
	fwrite( &xb, sizeof(int), 1, sm ) ;
	fwrite( &nCurrentDSR, sizeof(int), 1, sm ) ;
	fwrite( &max_cpf, sizeof(int), 1, sm ) ;
	fwrite( &oldmax, sizeof(int), 1, sm ) ;
	fwrite( &idling, sizeof(int), 1, sm ) ;
	fwrite( &speedup_float, sizeof(int), 1, sm ) ;
	fwrite( &simulate_int, sizeof(int), 1, sm ) ;
	fwrite( &slowdown_keyboard, sizeof(int), 1, sm ) ;
	fwrite( &cpucount, sizeof(int), 1, sm ) ;
	fwrite( &cpuframes, sizeof(int), 1, sm ) ;
	fwrite( &last_count, sizeof(int), 1, sm ) ;
	fwrite( &timercount, sizeof(int), 1, sm ) ;
	fwrite( &timer9901, sizeof(int), 1, sm ) ;
	fwrite( &starttimer9901, sizeof(int), 1, sm ) ;
	fwrite( &keyboard, sizeof(int), 1, sm ) ;
	fwrite( &retrace_count, sizeof(int), 1, sm ) ;
	fwrite( &ticks, sizeof(int), 1, sm ) ;
	fwrite( &num_sprites, sizeof(int), 1, sm ) ;
	fwrite( &y_speed, sizeof(int), 1, sm ) ;
	fwrite( &x_speed, sizeof(int), 1, sm ) ;
	fwrite( &t1, sizeof(int), 1, sm ) ;
	fwrite( &t1a, sizeof(int), 1, sm ) ;
	fwrite( &t1b, sizeof(int), 1, sm ) ;
	fwrite( &t2, sizeof(int), 1, sm ) ;
	fwrite( &t2a, sizeof(int), 1, sm ) ;
	fwrite( &t2b, sizeof(int), 1, sm ) ;
	fwrite( &sprite_y, sizeof(int), 1, sm ) ;
	fwrite( &sprite_x, sizeof(int), 1, sm ) ;
	fwrite( &offset, sizeof(int), 1, sm ) ;
	fwrite( &ty, sizeof(int), 1, sm ) ;
	fwrite( &tx, sizeof(int), 1, sm ) ;
	fwrite( &snd_address, sizeof(int), 1, sm ) ;
	fwrite( &SIT, sizeof(int), 1, sm ) ;
	fwrite( &CT, sizeof(int), 1, sm ) ;
	fwrite( &PDT, sizeof(int), 1, sm ) ;
	fwrite( &SAL, sizeof(int), 1, sm ) ;
	fwrite( &SDT, sizeof(int), 1, sm ) ;
	fwrite( &CTsize, sizeof(int), 1, sm ) ;
	fwrite( &PDTsize, sizeof(int), 1, sm ) ;
	fwrite( &SprColFlag, sizeof(int), 1, sm ) ;
	fwrite( &redraw_needed, sizeof(int), 1, sm ) ;
	fwrite( &interrupt_needed, sizeof(int), 1, sm ) ;
	fwrite( &vdpaccess, sizeof(int), 1, sm ) ;
	fwrite( &skip_interrupt, sizeof(int), 1, sm ) ;
	fwrite( &MaintainAspect, sizeof(int), 1, sm ) ;
	fwrite( &StretchMode, sizeof(int), 1, sm ) ;
	fwrite( &ssample_pos, sizeof(int), 1, sm ) ;
	fwrite( &buffer_len, sizeof(int), 1, sm ) ;
	fwrite( &emulation_rate, sizeof(int), 1, sm ) ;
	fwrite( &INUSE, sizeof(int), 1, sm ) ;
	fwrite( &fifo_head, sizeof(int), 1, sm ) ;
	fwrite( &fifo_tail, sizeof(int), 1, sm ) ;
	fwrite( &fifo_count, sizeof(int), 1, sm ) ;
	fwrite( &bits_taken, sizeof(int), 1, sm ) ;
	fwrite( &tms5220_speaking, sizeof(int), 1, sm ) ;
	fwrite( &first_frame, sizeof(int), 1, sm ) ;
	fwrite( &irq_pin, sizeof(int), 1, sm ) ;
	fwrite( &talk_status, sizeof(int), 1, sm ) ;
	fwrite( &buffer_low, sizeof(int), 1, sm ) ;
	fwrite( &buffer_empty, sizeof(int), 1, sm ) ;
	fwrite( &speak_external, sizeof(int), 1, sm ) ;
	fwrite( &interp_count, sizeof(int), 1, sm ) ;
	fwrite( &sample_count, sizeof(int), 1, sm ) ;
	fwrite( &pitch_count, sizeof(int), 1, sm ) ;
	fwrite( &randbit, sizeof(int), 1, sm ) ;
	fwrite( &speechROMaddr, sizeof(int), 1, sm ) ;
	fwrite( &external_read_from_data, sizeof(int), 1, sm ) ;

	fwrite( VDPREG, sizeof(char), 9, sm ) ;
	fwrite( VDP, sizeof(char), 16384, sm ) ;
	fwrite( SprColBuf, sizeof(char), 256*192, sm ) ;
	fwrite( CPU, sizeof(char), 65536, sm ) ;
	fwrite( CRU, sizeof(char), 4096, sm ) ;
	fwrite( key, sizeof(char), 256, sm ) ;
	fwrite( fifo, sizeof(char), FIFO_SIZE, sm ) ;

	fwrite( TIPALETTE, sizeof(int), 16, sm ) ;
	fwrite( nDSRBank, sizeof(int), 16, sm ) ;
	fwrite( nSampleIdx, sizeof(int), 3, sm ) ;
	fwrite( DAC, sizeof(int), 3, sm ) ;
	fwrite( old_k, sizeof(int), 10, sm ) ;
	fwrite( new_k, sizeof(int), 10, sm ) ;
	fwrite( current_k, sizeof(int), 10, sm ) ;
	fwrite( target_k, sizeof(int), 10, sm ) ;
	fwrite( u, sizeof(int), 11, sm ) ;
	fwrite( x, sizeof(int), 10, sm ) ;

	fwrite( fSampleShouldLoop, sizeof(bool), 3, sm ) ;
	fwrite( fVoiceSimulatedDAC, sizeof(bool), 3, sm ) ;

	fwrite( &grmaccess, sizeof(char), 1, sm ) ;
	fwrite( &grmdata, sizeof(char), 1, sm ) ;
	fwrite( &pcodeaccess, sizeof(char), 1, sm ) ;
	fwrite( &pcodedata, sizeof(char), 1, sm ) ;
	fwrite( &VDPS, sizeof(char), 1, sm ) ;
	fwrite( &vdpprefetch, sizeof(char), 1, sm ) ;
	fwrite( &hzRate, sizeof(char), 1, sm ) ;
	fwrite( &Recording, sizeof(char), 1, sm ) ;
	fwrite( &RecordFrame, sizeof(char), 1, sm ) ;
	fwrite( &data_register, sizeof(char), 1, sm ) ;

	fclose( sm ) ;

}

void tiloadstate( char *filename )
{
	FILE *sm ;

	sm = fopen ( filename, "rb" ) ;

	if ( sm == NULL )
		return ;

	fread( &FileInfo, sizeof(FileInfo), 1, sm ) ;

	fread( &atEOF, sizeof(bool), 1, sm ) ;
	fread( &fKeyEverPressed, sizeof(bool), 1, sm ) ;

	fread( &PC, sizeof(unsigned short), 1, sm ) ;
	fread( &WP, sizeof(unsigned short), 1, sm ) ;
	fread( &X_flag, sizeof(unsigned short), 1, sm ) ;
	fread( &ST, sizeof(unsigned short), 1, sm ) ;
	fread( &in, sizeof(unsigned short), 1, sm ) ;
	fread( &D, sizeof(unsigned short), 1, sm ) ;
	fread( &S, sizeof(unsigned short), 1, sm ) ;
	fread( &Td, sizeof(unsigned short), 1, sm ) ;
	fread( &Ts, sizeof(unsigned short), 1, sm ) ;
	fread( &B, sizeof(unsigned short), 1, sm ) ;
	fread( &GRMADD, sizeof(unsigned short), 1, sm ) ;
	fread( &PCODEADD, sizeof(unsigned short), 1, sm ) ;
	fread( &VDPADD, sizeof(unsigned short), 1, sm ) ;
	fread( &tmpVDPADD, sizeof(unsigned short), 1, sm ) ;
	fread( &old_energy, sizeof(unsigned short), 1, sm ) ;
	fread( &old_pitch, sizeof(unsigned short), 1, sm ) ;
	fread( &new_energy, sizeof(unsigned short), 1, sm ) ;
	fread( &new_pitch, sizeof(unsigned short), 1, sm ) ;
	fread( &current_energy, sizeof(unsigned short), 1, sm ) ;
	fread( &current_pitch, sizeof(unsigned short), 1, sm ) ;
	fread( &target_energy, sizeof(unsigned short), 1, sm ) ;
	fread( &target_pitch, sizeof(unsigned short), 1, sm ) ;

	fread( &nCycleCount, sizeof(int), 1, sm ) ;
	fread( &nNextVarRec, sizeof(int), 1, sm ) ;
	fread( &nSystem, sizeof(int), 1, sm ) ;
	fread( &nCartGroup, sizeof(int), 1, sm ) ;
	fread( &nCart, sizeof(int), 1, sm ) ;
	fread( &sample_pos, sizeof(int), 1, sm ) ;
	fread( &nLoadedUserCarts, sizeof(int), 1, sm ) ;
	fread( &need_byte, sizeof(int), 1, sm ) ;
	fread( &last_byte, sizeof(int), 1, sm ) ;
	fread( &volume_mult, sizeof(int), 1, sm ) ;
	fread( &bank, sizeof(int), 1, sm ) ;
	fread( &xb, sizeof(int), 1, sm ) ;
	fread( &nCurrentDSR, sizeof(int), 1, sm ) ;
	fread( &max_cpf, sizeof(int), 1, sm ) ;
	fread( &oldmax, sizeof(int), 1, sm ) ;
	fread( &idling, sizeof(int), 1, sm ) ;
	fread( &speedup_float, sizeof(int), 1, sm ) ;
	fread( &simulate_int, sizeof(int), 1, sm ) ;
	fread( &slowdown_keyboard, sizeof(int), 1, sm ) ;
	fread( &cpucount, sizeof(int), 1, sm ) ;
	fread( &cpuframes, sizeof(int), 1, sm ) ;
	fread( &last_count, sizeof(int), 1, sm ) ;
	fread( &timercount, sizeof(int), 1, sm ) ;
	fread( &timer9901, sizeof(int), 1, sm ) ;
	fread( &starttimer9901, sizeof(int), 1, sm ) ;
	fread( &keyboard, sizeof(int), 1, sm ) ;
	fread( &retrace_count, sizeof(int), 1, sm ) ;
	fread( &ticks, sizeof(int), 1, sm ) ;
	fread( &num_sprites, sizeof(int), 1, sm ) ;
	fread( &y_speed, sizeof(int), 1, sm ) ;
	fread( &x_speed, sizeof(int), 1, sm ) ;
	fread( &t1, sizeof(int), 1, sm ) ;
	fread( &t1a, sizeof(int), 1, sm ) ;
	fread( &t1b, sizeof(int), 1, sm ) ;
	fread( &t2, sizeof(int), 1, sm ) ;
	fread( &t2a, sizeof(int), 1, sm ) ;
	fread( &t2b, sizeof(int), 1, sm ) ;
	fread( &sprite_y, sizeof(int), 1, sm ) ;
	fread( &sprite_x, sizeof(int), 1, sm ) ;
	fread( &offset, sizeof(int), 1, sm ) ;
	fread( &ty, sizeof(int), 1, sm ) ;
	fread( &tx, sizeof(int), 1, sm ) ;
	fread( &snd_address, sizeof(int), 1, sm ) ;
	fread( &SIT, sizeof(int), 1, sm ) ;
	fread( &CT, sizeof(int), 1, sm ) ;
	fread( &PDT, sizeof(int), 1, sm ) ;
	fread( &SAL, sizeof(int), 1, sm ) ;
	fread( &SDT, sizeof(int), 1, sm ) ;
	fread( &CTsize, sizeof(int), 1, sm ) ;
	fread( &PDTsize, sizeof(int), 1, sm ) ;
	fread( &SprColFlag, sizeof(int), 1, sm ) ;
	fread( &redraw_needed, sizeof(int), 1, sm ) ;
	fread( &interrupt_needed, sizeof(int), 1, sm ) ;
	fread( &vdpaccess, sizeof(int), 1, sm ) ;
	fread( &skip_interrupt, sizeof(int), 1, sm ) ;
	fread( &MaintainAspect, sizeof(int), 1, sm ) ;
	fread( &StretchMode, sizeof(int), 1, sm ) ;
	fread( &ssample_pos, sizeof(int), 1, sm ) ;
	fread( &buffer_len, sizeof(int), 1, sm ) ;
	fread( &emulation_rate, sizeof(int), 1, sm ) ;
	fread( &INUSE, sizeof(int), 1, sm ) ;
	fread( &fifo_head, sizeof(int), 1, sm ) ;
	fread( &fifo_tail, sizeof(int), 1, sm ) ;
	fread( &fifo_count, sizeof(int), 1, sm ) ;
	fread( &bits_taken, sizeof(int), 1, sm ) ;
	fread( &tms5220_speaking, sizeof(int), 1, sm ) ;
	fread( &first_frame, sizeof(int), 1, sm ) ;
	fread( &irq_pin, sizeof(int), 1, sm ) ;
	fread( &talk_status, sizeof(int), 1, sm ) ;
	fread( &buffer_low, sizeof(int), 1, sm ) ;
	fread( &buffer_empty, sizeof(int), 1, sm ) ;
	fread( &speak_external, sizeof(int), 1, sm ) ;
	fread( &interp_count, sizeof(int), 1, sm ) ;
	fread( &sample_count, sizeof(int), 1, sm ) ;
	fread( &pitch_count, sizeof(int), 1, sm ) ;
	fread( &randbit, sizeof(int), 1, sm ) ;
	fread( &speechROMaddr, sizeof(int), 1, sm ) ;
	fread( &external_read_from_data, sizeof(int), 1, sm ) ;

	fread( VDPREG, sizeof(char), 9, sm ) ;
	fread( VDP, sizeof(char), 16384, sm ) ;
	fread( SprColBuf, sizeof(char), 256*192, sm ) ;
	fread( CPU, sizeof(char), 65536, sm ) ;
	fread( CRU, sizeof(char), 4096, sm ) ;
	fread( key, sizeof(char), 256, sm ) ;
	fread( fifo, sizeof(char), FIFO_SIZE, sm ) ;

	fread( TIPALETTE, sizeof(int), 16, sm ) ;
	fread( nDSRBank, sizeof(int), 16, sm ) ;
	fread( nSampleIdx, sizeof(int), 3, sm ) ;
	fread( DAC, sizeof(int), 3, sm ) ;
	fread( old_k, sizeof(int), 10, sm ) ;
	fread( new_k, sizeof(int), 10, sm ) ;
	fread( current_k, sizeof(int), 10, sm ) ;
	fread( target_k, sizeof(int), 10, sm ) ;
	fread( u, sizeof(int), 11, sm ) ;
	fread( x, sizeof(int), 10, sm ) ;

	fread( fSampleShouldLoop, sizeof(bool), 3, sm ) ;
	fread( fVoiceSimulatedDAC, sizeof(bool), 3, sm ) ;

	fread( &grmaccess, sizeof(char), 1, sm ) ;
	fread( &grmdata, sizeof(char), 1, sm ) ;
	fread( &pcodeaccess, sizeof(char), 1, sm ) ;
	fread( &pcodedata, sizeof(char), 1, sm ) ;
	fread( &VDPS, sizeof(char), 1, sm ) ;
	fread( &vdpprefetch, sizeof(char), 1, sm ) ;
	fread( &hzRate, sizeof(char), 1, sm ) ;
	fread( &Recording, sizeof(char), 1, sm ) ;
	fread( &RecordFrame, sizeof(char), 1, sm ) ;
	fread( &data_register, sizeof(char), 1, sm ) ;

	fclose( sm ) ;
}

extern "C" size_t xbox_mfread( const void *buffer, size_t len1, size_t len2, void *st ) ;
extern "C" size_t xbox_mfwrite( const void *buffer, size_t len1, size_t len2, void *st ) ;

extern "C" void xbox_savestatemem( void *sm ) 
{

	xbox_mfwrite( &FileInfo, sizeof(FileInfo), 1, sm ) ;

	xbox_mfwrite( &atEOF, sizeof(bool), 1, sm ) ;
	xbox_mfwrite( &fKeyEverPressed, sizeof(bool), 1, sm ) ;

	xbox_mfwrite( &PC, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &WP, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &X_flag, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &ST, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &in, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &D, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &S, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &Td, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &Ts, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &B, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &GRMADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &PCODEADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &VDPADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &tmpVDPADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &old_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &old_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &new_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &new_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &current_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &current_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &target_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfwrite( &target_pitch, sizeof(unsigned short), 1, sm ) ;

	xbox_mfwrite( &nCycleCount, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nNextVarRec, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nSystem, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nCartGroup, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nCart, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &sample_pos, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nLoadedUserCarts, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &need_byte, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &last_byte, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &volume_mult, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &bank, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &xb, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &nCurrentDSR, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &max_cpf, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &oldmax, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &idling, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &speedup_float, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &simulate_int, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &slowdown_keyboard, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &cpucount, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &cpuframes, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &last_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &timercount, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &timer9901, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &starttimer9901, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &keyboard, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &retrace_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &ticks, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &num_sprites, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &y_speed, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &x_speed, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t1, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t1a, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t1b, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t2, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t2a, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &t2b, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &sprite_y, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &sprite_x, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &offset, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &ty, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &tx, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &snd_address, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &SIT, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &CT, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &PDT, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &SAL, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &SDT, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &CTsize, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &PDTsize, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &SprColFlag, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &redraw_needed, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &interrupt_needed, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &vdpaccess, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &skip_interrupt, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &MaintainAspect, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &StretchMode, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &ssample_pos, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &buffer_len, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &emulation_rate, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &INUSE, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &fifo_head, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &fifo_tail, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &fifo_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &bits_taken, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &tms5220_speaking, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &first_frame, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &irq_pin, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &talk_status, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &buffer_low, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &buffer_empty, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &speak_external, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &interp_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &sample_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &pitch_count, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &randbit, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &speechROMaddr, sizeof(int), 1, sm ) ;
	xbox_mfwrite( &external_read_from_data, sizeof(int), 1, sm ) ;

	xbox_mfwrite( VDPREG, sizeof(char), 9, sm ) ;
	xbox_mfwrite( VDP, sizeof(char), 16384, sm ) ;
	xbox_mfwrite( SprColBuf, sizeof(char), 256*192, sm ) ;
	xbox_mfwrite( CPU, sizeof(char), 65536, sm ) ;
	xbox_mfwrite( CRU, sizeof(char), 4096, sm ) ;
	xbox_mfwrite( key, sizeof(char), 256, sm ) ;
	xbox_mfwrite( fifo, sizeof(char), FIFO_SIZE, sm ) ;

	xbox_mfwrite( TIPALETTE, sizeof(int), 16, sm ) ;
	xbox_mfwrite( nDSRBank, sizeof(int), 16, sm ) ;
	xbox_mfwrite( nSampleIdx, sizeof(int), 3, sm ) ;
	xbox_mfwrite( DAC, sizeof(int), 3, sm ) ;
	xbox_mfwrite( old_k, sizeof(int), 10, sm ) ;
	xbox_mfwrite( new_k, sizeof(int), 10, sm ) ;
	xbox_mfwrite( current_k, sizeof(int), 10, sm ) ;
	xbox_mfwrite( target_k, sizeof(int), 10, sm ) ;
	xbox_mfwrite( u, sizeof(int), 11, sm ) ;
	xbox_mfwrite( x, sizeof(int), 10, sm ) ;

	xbox_mfwrite( fSampleShouldLoop, sizeof(bool), 3, sm ) ;
	xbox_mfwrite( fVoiceSimulatedDAC, sizeof(bool), 3, sm ) ;

	xbox_mfwrite( &grmaccess, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &grmdata, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &pcodeaccess, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &pcodedata, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &VDPS, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &vdpprefetch, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &hzRate, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &Recording, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &RecordFrame, sizeof(char), 1, sm ) ;
	xbox_mfwrite( &data_register, sizeof(char), 1, sm ) ;

}
extern "C" void xbox_loadstatemem( void *sm ) 
{
	xbox_mfread( &FileInfo, sizeof(FileInfo), 1, sm ) ;

	xbox_mfread( &atEOF, sizeof(bool), 1, sm ) ;
	xbox_mfread( &fKeyEverPressed, sizeof(bool), 1, sm ) ;

	xbox_mfread( &PC, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &WP, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &X_flag, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &ST, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &in, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &D, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &S, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &Td, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &Ts, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &B, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &GRMADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &PCODEADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &VDPADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &tmpVDPADD, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &old_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &old_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &new_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &new_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &current_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &current_pitch, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &target_energy, sizeof(unsigned short), 1, sm ) ;
	xbox_mfread( &target_pitch, sizeof(unsigned short), 1, sm ) ;

	xbox_mfread( &nCycleCount, sizeof(int), 1, sm ) ;
	xbox_mfread( &nNextVarRec, sizeof(int), 1, sm ) ;
	xbox_mfread( &nSystem, sizeof(int), 1, sm ) ;
	xbox_mfread( &nCartGroup, sizeof(int), 1, sm ) ;
	xbox_mfread( &nCart, sizeof(int), 1, sm ) ;
	xbox_mfread( &sample_pos, sizeof(int), 1, sm ) ;
	xbox_mfread( &nLoadedUserCarts, sizeof(int), 1, sm ) ;
	xbox_mfread( &need_byte, sizeof(int), 1, sm ) ;
	xbox_mfread( &last_byte, sizeof(int), 1, sm ) ;
	xbox_mfread( &volume_mult, sizeof(int), 1, sm ) ;
	xbox_mfread( &bank, sizeof(int), 1, sm ) ;
	xbox_mfread( &xb, sizeof(int), 1, sm ) ;
	xbox_mfread( &nCurrentDSR, sizeof(int), 1, sm ) ;
	xbox_mfread( &max_cpf, sizeof(int), 1, sm ) ;
	xbox_mfread( &oldmax, sizeof(int), 1, sm ) ;
	xbox_mfread( &idling, sizeof(int), 1, sm ) ;
	xbox_mfread( &speedup_float, sizeof(int), 1, sm ) ;
	xbox_mfread( &simulate_int, sizeof(int), 1, sm ) ;
	xbox_mfread( &slowdown_keyboard, sizeof(int), 1, sm ) ;
	xbox_mfread( &cpucount, sizeof(int), 1, sm ) ;
	xbox_mfread( &cpuframes, sizeof(int), 1, sm ) ;
	xbox_mfread( &last_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &timercount, sizeof(int), 1, sm ) ;
	xbox_mfread( &timer9901, sizeof(int), 1, sm ) ;
	xbox_mfread( &starttimer9901, sizeof(int), 1, sm ) ;
	xbox_mfread( &keyboard, sizeof(int), 1, sm ) ;
	xbox_mfread( &retrace_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &ticks, sizeof(int), 1, sm ) ;
	xbox_mfread( &num_sprites, sizeof(int), 1, sm ) ;
	xbox_mfread( &y_speed, sizeof(int), 1, sm ) ;
	xbox_mfread( &x_speed, sizeof(int), 1, sm ) ;
	xbox_mfread( &t1, sizeof(int), 1, sm ) ;
	xbox_mfread( &t1a, sizeof(int), 1, sm ) ;
	xbox_mfread( &t1b, sizeof(int), 1, sm ) ;
	xbox_mfread( &t2, sizeof(int), 1, sm ) ;
	xbox_mfread( &t2a, sizeof(int), 1, sm ) ;
	xbox_mfread( &t2b, sizeof(int), 1, sm ) ;
	xbox_mfread( &sprite_y, sizeof(int), 1, sm ) ;
	xbox_mfread( &sprite_x, sizeof(int), 1, sm ) ;
	xbox_mfread( &offset, sizeof(int), 1, sm ) ;
	xbox_mfread( &ty, sizeof(int), 1, sm ) ;
	xbox_mfread( &tx, sizeof(int), 1, sm ) ;
	xbox_mfread( &snd_address, sizeof(int), 1, sm ) ;
	xbox_mfread( &SIT, sizeof(int), 1, sm ) ;
	xbox_mfread( &CT, sizeof(int), 1, sm ) ;
	xbox_mfread( &PDT, sizeof(int), 1, sm ) ;
	xbox_mfread( &SAL, sizeof(int), 1, sm ) ;
	xbox_mfread( &SDT, sizeof(int), 1, sm ) ;
	xbox_mfread( &CTsize, sizeof(int), 1, sm ) ;
	xbox_mfread( &PDTsize, sizeof(int), 1, sm ) ;
	xbox_mfread( &SprColFlag, sizeof(int), 1, sm ) ;
	xbox_mfread( &redraw_needed, sizeof(int), 1, sm ) ;
	xbox_mfread( &interrupt_needed, sizeof(int), 1, sm ) ;
	xbox_mfread( &vdpaccess, sizeof(int), 1, sm ) ;
	xbox_mfread( &skip_interrupt, sizeof(int), 1, sm ) ;
	xbox_mfread( &MaintainAspect, sizeof(int), 1, sm ) ;
	xbox_mfread( &StretchMode, sizeof(int), 1, sm ) ;
	xbox_mfread( &ssample_pos, sizeof(int), 1, sm ) ;
	xbox_mfread( &buffer_len, sizeof(int), 1, sm ) ;
	xbox_mfread( &emulation_rate, sizeof(int), 1, sm ) ;
	xbox_mfread( &INUSE, sizeof(int), 1, sm ) ;
	xbox_mfread( &fifo_head, sizeof(int), 1, sm ) ;
	xbox_mfread( &fifo_tail, sizeof(int), 1, sm ) ;
	xbox_mfread( &fifo_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &bits_taken, sizeof(int), 1, sm ) ;
	xbox_mfread( &tms5220_speaking, sizeof(int), 1, sm ) ;
	xbox_mfread( &first_frame, sizeof(int), 1, sm ) ;
	xbox_mfread( &irq_pin, sizeof(int), 1, sm ) ;
	xbox_mfread( &talk_status, sizeof(int), 1, sm ) ;
	xbox_mfread( &buffer_low, sizeof(int), 1, sm ) ;
	xbox_mfread( &buffer_empty, sizeof(int), 1, sm ) ;
	xbox_mfread( &speak_external, sizeof(int), 1, sm ) ;
	xbox_mfread( &interp_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &sample_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &pitch_count, sizeof(int), 1, sm ) ;
	xbox_mfread( &randbit, sizeof(int), 1, sm ) ;
	xbox_mfread( &speechROMaddr, sizeof(int), 1, sm ) ;
	xbox_mfread( &external_read_from_data, sizeof(int), 1, sm ) ;

	xbox_mfread( VDPREG, sizeof(char), 9, sm ) ;
	xbox_mfread( VDP, sizeof(char), 16384, sm ) ;
	xbox_mfread( SprColBuf, sizeof(char), 256*192, sm ) ;
	xbox_mfread( CPU, sizeof(char), 65536, sm ) ;
	xbox_mfread( CRU, sizeof(char), 4096, sm ) ;
	xbox_mfread( key, sizeof(char), 256, sm ) ;
	xbox_mfread( fifo, sizeof(char), FIFO_SIZE, sm ) ;

	xbox_mfread( TIPALETTE, sizeof(int), 16, sm ) ;
	xbox_mfread( nDSRBank, sizeof(int), 16, sm ) ;
	xbox_mfread( nSampleIdx, sizeof(int), 3, sm ) ;
	xbox_mfread( DAC, sizeof(int), 3, sm ) ;
	xbox_mfread( old_k, sizeof(int), 10, sm ) ;
	xbox_mfread( new_k, sizeof(int), 10, sm ) ;
	xbox_mfread( current_k, sizeof(int), 10, sm ) ;
	xbox_mfread( target_k, sizeof(int), 10, sm ) ;
	xbox_mfread( u, sizeof(int), 11, sm ) ;
	xbox_mfread( x, sizeof(int), 10, sm ) ;

	xbox_mfread( fSampleShouldLoop, sizeof(bool), 3, sm ) ;
	xbox_mfread( fVoiceSimulatedDAC, sizeof(bool), 3, sm ) ;

	xbox_mfread( &grmaccess, sizeof(char), 1, sm ) ;
	xbox_mfread( &grmdata, sizeof(char), 1, sm ) ;
	xbox_mfread( &pcodeaccess, sizeof(char), 1, sm ) ;
	xbox_mfread( &pcodedata, sizeof(char), 1, sm ) ;
	xbox_mfread( &VDPS, sizeof(char), 1, sm ) ;
	xbox_mfread( &vdpprefetch, sizeof(char), 1, sm ) ;
	xbox_mfread( &hzRate, sizeof(char), 1, sm ) ;
	xbox_mfread( &Recording, sizeof(char), 1, sm ) ;
	xbox_mfread( &RecordFrame, sizeof(char), 1, sm ) ;
	xbox_mfread( &data_register, sizeof(char), 1, sm ) ;
}
