// ###########################################################################
// #
// # OSCABoard - Implementation of Top-Level Class for OSCA Board
// # Copyright (C) 2009-2011 Tony Friery [DialTone]
// #
// # ALL RIGHTS RESERVED
// #
// ###########################################################################

#include <vcl.h>
#pragma hdrstop

#include "OSCABoard.h"
#include "stdio.h"
#include "utils.h"
#include "mmsystem.h"
#include "dsound.h"
#include "GR32_Blend.hpp"
#include "FormMain.h"
#include "FormDiag.h"
#include "FormDebug.h"
#include "FormOptions.h"

///////////////////////////////////////////////////////////////////////
//
//		Board Constants
//
///////////////////////////////////////////////////////////////////////

const word 	OSCA_Version				= 0x0672;
const int   OSCA_CyclesPerSecond		= 37000000;

// Used for Ccnverting key codes from the UI to keyboard scancodes
const word	KeyboardMap[] = {
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 00 - 07
	0x0066, 0x000d, 0x0000, 0x0000, 0x0000, 0x005a, 0x0000, 0x0000,	// 08 - 0f
	0x0012, 0x0014, 0x0011, 0x0000, 0x0058, 0x0000, 0x0000, 0x0000,	// 10 - 17
	0x0000, 0x0000, 0x0000, 0x0076, 0x0000, 0x0000, 0x0000, 0x0000,	// 18 - 1f
	0x0029, 0xe07d, 0xe07a, 0xe069, 0xe06c, 0xe06b, 0xe075, 0xe074,	// 20 - 27
	0xe072, 0x0000, 0x0000, 0x0000, 0x0000, 0xe070, 0xe071, 0x0000,	// 28 - 2f
	0x0045, 0x0016, 0x001e, 0x0026, 0x0025, 0x002e, 0x0036, 0x003d,	// 30 - 37
	0x003e, 0x0046, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 38 - 3f
	0x0000, 0x001c, 0x0032, 0x0021, 0x0023, 0x0024, 0x002b, 0x0034,	// 40 - 47
	0x0033, 0x0043, 0x003b, 0x0042, 0x004b, 0x003a, 0x0031, 0x0044,	// 48 - 4f
	0x004d, 0x0015, 0x002d, 0x001b, 0x002c, 0x003c, 0x002a, 0x001d,	// 50 - 57
	0x0022, 0x0035, 0x001a, 0x0000, 0x0000, 0xe027, 0x0000, 0x0000,	// 58 - 5f
	0x0070, 0x0069, 0x0072, 0x007a, 0x006b, 0x0073, 0x0074, 0x006c,	// 60 - 67
	0x0075, 0x007d, 0x007c, 0x0079, 0x0000, 0x007b, 0x0071, 0xe04a,	// 68 - 6f
	0x0005, 0x0006, 0x0004, 0x000c, 0x0003, 0x000b, 0x0083, 0x000a,	// 70 - 77
	0x0001, 0x0009, 0x0078, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000,	// 78 - 7f
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 80 - 87
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 88 - 8f
	0x0077, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 90 - 97
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// 98 - 9f
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// a0 - a7
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// a8 - af
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// b0 - b7
	0x0000, 0x0000, 0x004c, 0x0055, 0x0041, 0x004e, 0x0049, 0x004a,	// b8 - bf
	0x0052, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// c0 - c7
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// c8 - cf
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// d0 - d7
	0x0000, 0x0000, 0x0000, 0x0054, 0x0061, 0x005b, 0x005d, 0x000e,	// d8 - df
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// e0 - e7
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// e8 - ef
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// f0 - f7
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	// f8 - ff
};

//---------------------------------------------------------------------------

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_In
//
//  PURPOSE:	Function called by the z80 core whenever an I/O Port
//					input request is made
//
//  INPUTS:		word					Port Number (16 bit - for 8 bit
//											ports, use only LSB)
//
//  OUTPUT:		byte					Value read from I/O Port
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCABoard::Z80_In(word Port)
{
	byte retv = 0x00;

	switch (Port & 0x3f)
	{
		case 0x00:	// sys_mem_select
			retv = this->fSysMemSelect;
			break;
		case 0x01:	//	sys_irq_ps2_flags
			// MOUDTA | MOUCLK | KEYDTA | KEYCLK | VIDIRQ | TIMIRQ | MOUIRQ | KEYIRQ
			retv |= this->MouseDevice->GetData()			? 0x80 : 0x00;
			retv |= this->MouseDevice->GetClock()			? 0x40 : 0x00;
			retv |= this->KeyboardDevice->GetData()		? 0x20 : 0x00;
			retv |= this->KeyboardDevice->GetClock()		? 0x10 : 0x00;
			retv |= this->VideoModule->GetRasterIRQ() 	? 0x08 : 0x00;
			retv |= this->TimerModule->GetTimerIRQ() 		? 0x04 : 0x00;
			retv |= this->MouseDevice->GetIRQState() 		? 0x02 : 0x00;
			retv |= this->KeyboardDevice->GetIRQState() 	? 0x01 : 0x00;
			break;
		case 0x02:	// sys_keyboard_data
			retv = this->KeyboardDevice->GetDataByte();
			break;
		case 0x03:	// sys_mouse_data
			retv = this->MouseDevice->GetDataByte();
			break;
		case 0x04:	// sys_serial_port
			// TODO: Implement serial read
			break;
		case 0x05:	// sys_joy_com
			// SER_OUT_BUSY | SER_IN_HASDATA | FIRE2 | FIRE1 | RIGHT | LEFT | DOWN | UP
			// TODO: Implement RS232 and Joystick read
			break;
		case 0x06:	// sys_sdcard_ctrl2
			// NMI | RESET | X | X | /POWER | /CS | USER2 | USER1
			retv = this->SDMMCDevice->GetCtrl2Port() << 2;
			break;
		case 0x07:	// sys_vreg_read
			// INTERLACE | SCANLSB | 60HZ | BLIT_LINE | RASTERIRQ | YWIN | XWIN | LASTLINE
			return this->VideoModule->ReadVREG();
		case 0x08:	// sys_audio_flags
			// CH3_LOOP | CH2_LP | CH1_LP | CH0_LP | CH3_PLAYING | CH2_PL | CH1_PL | CH0_PL
			return this->AudioSystem->GetAudioFlags();
		case 0x09:	// sys_hw_flags
			// HW_VER | SDBUSY | VGA_TV | PICRDY | PICRB7 | TIMIRQ | MOUIRQ | KBIRQ

			// Hardware Version Bit
			if (OSCA_Version & (1 << ((Port & 0xf00) >> 8)))
			{
				retv |= 0x80;
			}

			retv |= this->SDMMCDevice->GetBusy() 			? 0x40 : 0x00;	// SD Busy
			retv |= 0x20;																// VGA/TV Mode (VGA if bit set)
			retv |= this->PICModule->IsDataBurstReady() 	? 0x10 : 0x00;

			// 0x08 = PIC RB7
			// TODO: Work out what RB7 does and implement perhaps

			// TODO: Documentation doesn't show this duplicate mapping - remove?
			retv |= this->TimerModule->GetTimerIRQ() 		? 0x04 : 0x00;	// Timer IRQ
			retv |= this->MouseDevice->GetIRQState() 		? 0x02 : 0x00;	// Mouse IRQ
			retv |= this->KeyboardDevice->GetIRQState() 	? 0x01 : 0x00; // Keyb IRQ

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"Read from SysHW Port (0x%4.4x): 0x%2.2x @ 0x%4.4x",
					OPENARRAY(TVarRec, (Port, retv, this->R.PC.W.l))
				));
			}
			break;
		case 0x0a:	// sys_spi_port (data from SD Card)
			retv = this->SDMMCDevice->GetByte();
			break;
		case 0x0b:	// sys_alt_write_page
			retv = this->fSysAltWrPg;
			break;
		case 0x0d:	// sys_eeprom_byte
			retv = this->PICModule->GetDataBurstByte();
			break;
		case 0x0e:	// sys_io_pins
			// NMI | RESET | X | X | X | USER3 | USER2 | USER1
			// These ports not implemented (except directly for NMI/Reset)
			break;
		case 0x0c:	// NO FUNCTION WHEN READ
		case 0x0f:	// NO FUNCTION WHEN READ
		case 0x21:	// NO FUNCTION WHEN READ
			break;
		case 0x20:	// sys_low_page
			retv = this->fSysLowPage;
			break;
		default:
			if (((Port & 0x3f) >= 0x10) && ((Port & 0x3f) <= 0x1f))
			{
				// Audio System
				retv = this->AudioSystem->ReadAudioPort(Port & 0x3f);
			}
			else if (((Port & 0x3f) >= 0x22) && ((Port & 0x3f) <= 0x27))
			{
				// Audio System
				retv = this->AudioSystem->ReadAudioPort(Port & 0x3f);
			}
			else
			{
				if ((DiagForm) && (DiagForm->IsActive(DIAG_CPU)))
				{
					DiagForm->AddMessage(DIAG_CPU, UnicodeString::Format(
						L"Read from Port 0x%2.2x @ 0x%4.4x",
						OPENARRAY(TVarRec, (Port, this->R.PC.W.l))
					));
				}
			}
			break;
	}

	return retv;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_Out
//
//  PURPOSE:	Function called by the z80 core whenever an I/O Port
//					output request is made
//
//  INPUTS:		word					Port Number (16 bit - for 8 bit
//											ports, use only LSB)
//					byte					Value to be written to I/O Port
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_Out(word Port, byte Value)
{
	switch (Port & 0x3f)
	{
		case 0x00:	// sys_mem_select
			this->fSysMemSelect = Value;

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"sys_mem_select Write 0x%2.2x @ 0x%4.4x",
					OPENARRAY(TVarRec, (Value, this->R.PC.W.l))
				));
			}
			break;
		case 0x01:	// sys_irq_enable
			// MASTER | X | X | X | AUD | TIM | MOU | KEY
			this->fSysIRQEn = Value;

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"sys_irq_enable Write 0x%2.2x @ 0x%4.4x",
					OPENARRAY(TVarRec, (Value, this->R.PC.W.l))
				));
			}
			break;
		case 0x02:	// sys_clear_irq_flags
			// Clears: AUD3_LP | AUD2_LP | AUD1_LP | AUD0_LP | X | TIM | MOU | KEY
			for (int i = 0, bit = 1; i < 8; i++, bit <<= 1)
			{
				if (Value & bit)
				{
					switch(i)
					{
						case 0:
							this->KeyboardDevice->ClearIRQ();
							break;
						case 1:
							this->MouseDevice->ClearIRQ();
							break;
						case 2:
							this->TimerModule->ClearIRQ();
							break;
						case 3:
							// No Operation
							break;
						case 4:
						case 5:
						case 6:
						case 7:
							this->AudioSystem->ClearLoopFlag(i - 4);
							break;
					}
				}
			}
			break;
		case 0x03:	// sys_ps2_joy_control
			// MOU_DTA | MOU_CLK | KEY_DTA | KEY_CLK | X | X | X | JOY_PORT0/1
			this->MouseDevice->WriteToDevice(Value & 0x40, Value & 0x80);
			this->KeyboardDevice->WriteToDevice(Value & 0x10, Value & 0x20);
			this->fJoystickPort = (Value & 0x01) ? true : false;
			break;
		case 0x04:	// sys_serial_port
			// TODO: Implement serial port
			break;
		case 0x05:	// sys_sdcard_ctrl1
			// SPI_SPEED | FPGA_SD_ENABLE_OUT | ..unused..
			this->SDMMCDevice->WriteCtrl1Port(Value);
			break;
		case 0x06:	// sys_sdcard_ctrl2
			// X | X | X | X | /POWER | /CS | X | X
			this->SDMMCDevice->WriteCtrl2Port(Value & 0x0f);
			break;
		case 0x07:	// sys_timer
			this->TimerModule->SetTimerSeed(Value);
			break;
		case 0x08:	// sys_audio_enabled
			// X | X | X | X | CH3_PLAY | CH2_PLAY | CH1_PLAY | CH0_PLAY
			this->AudioSystem->SetPlaybackFlags(Value & 0x0f);
			break;
		case 0x09:	// sys_hw_settings
			// X | X | X | X | VGA50 | X | RES_SW_INHIBIT | NMI_SW_INHIBIT
			// TODO: Implement Video Mode Switch
			break;
		case 0x0a:	// sys_spi_port (data to SD Card)
			this->SDMMCDevice->WriteByte(Value);
			break;
		case 0x0b:	// sys_alt_write_page
			this->fSysAltWrPg = Value;

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"sys_alt_write_page Write 0x%2.2x @ 0x%4.4x",
					OPENARRAY(TVarRec, (Value, this->R.PC.W.l))
				));
			}
			break;
		case 0x0c:	// sys_rs232_speed
			// Bit 0 = 57600 baud (0), 115200 baud (1)
			// TODO: Implement RS232
			break;
		case 0x0d:	// sys_pic_comms
			// X | X | X | X | X | X | RA5 (CLOCK) | RB1 (DATA)
			this->PICModule->Write(Value & 0x02, Value & 0x01);
			break;
		case 0x0e:	// sys_io_pins
			// X | X | X | X | X | USER3 | USER2 | USER1
			// These ports not implemented
			break;
		case 0x0f:	// sys_io_dir
			// 0 = output, 1 = input
			// X | X | X | X | X | USER3 | USER2 | USER1
			// These ports not implemented
			break;
		case 0x20:	// sys_low_page
			fSysLowPage = Value & 0x0f;

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"sys_low_page Write 0x%2.2x @ 0x%4.4x",
					OPENARRAY(TVarRec, (Value, this->R.PC.W.l))
				));
			}
			break;
		case 0x21:	// sys_vram_location
			this->fVRAMLocation = (Value & 0x07) << 13;
			if (this->fVRAMLocation == 0)
			{
				this->fVRAMLocation = 0x2000;
			}

			if ((DiagForm) && (DiagForm->IsActive(DIAG_OSCAMISC)))
			{
				DiagForm->AddMessage(DIAG_OSCAMISC, UnicodeString::Format(
					L"sys_vram_location Write 0x%2.2x (0x%4.4x) @ 0x%4.4x",
					OPENARRAY(TVarRec, (Value, this->fVRAMLocation, this->R.PC.W.l))
				));
			}
			break;
		default:
			if (((Port & 0x3f) >= 0x10) && ((Port & 0x3f) <= 0x1f))
			{
				// Audio System
				this->AudioSystem->WriteAudioPort(Port & 0x3f, (Port & 0xff00) | Value);
			}
			else if (((Port & 0x3f) >= 0x22) && ((Port & 0x3f) <= 0x27))
			{
				// Audio System
				this->AudioSystem->WriteAudioPort(Port & 0x3f, (Port & 0xff00) | Value);
			}
			else
			{
				if ((DiagForm) && (DiagForm->IsActive(DIAG_CPU)))
				{
					DiagForm->AddMessage(DIAG_CPU, UnicodeString::Format(
						L"Wrote 0x%2.2x to Port 0x%2.2x @ 0x%4.4x",
						OPENARRAY(TVarRec, (Value, Port, this->R.PC.W.l))
					));
				}
			}
			break;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_RDSTACK
//
//  PURPOSE:	Function called by the z80 core whenever a read
//					from an address is required in the course of executing
//					a Z80 Read (LD) Instruction
//
//  INPUTS:		word					Address to be Read
//
//  OUTPUT:		byte					Value read from memory
//
///////////////////////////////////////////////////////////////////////
unsigned __fastcall TOSCABoard::Z80_RDSTACK(word A)
{
	switch (A & 0xf000)
	{
		case 0x0000:	// System Area
			if (A < 0x200)
			{
				if (this->fSysAltWrPg & 0x50)
				{
					// ROM is paged out
					// Mapped lower page of RAM
					return this->MainRAM[A | (this->fSysLowPage << 15)];
				}

				// ROM is paged in
				return this->BootROM[A];
			}
			else if (this->fSysAltWrPg & 0x80)
			{
				// System Registers paged out
				// Mapped lower page of RAM
				return this->MainRAM[A | (this->fSysLowPage << 15)];
			}
			else
			{
				if (A == 0x700)
				{
					// vreg_read
					return this->VideoModule->ReadVREG();
				}
				else if (A == 0x704)
				{
					return this->MathsUnit->GetMathsResultLSB();
				}
				else if (A == 0x705)
				{
					return this->MathsUnit->GetMathsResultMSB();
				}
				else
				{
					// Mapped lower page of RAM
					return this->MainRAM[A | (this->fSysLowPage << 15)];
				}
			}
		case 0x1000:	// Sprite RAM
			// Sprite RAM is always mapped Write-Only, so reads will hit
			// banked lower memory
			return this->MainRAM[A | (this->fSysLowPage << 15)];
		default:
			if (A < 0x8000)
			{
				// This covers the pages 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000
				if ((this->fSysMemSelect & 0x40) && ((this->fVRAMLocation & 0xe000) == (A & 0xe000)))
				{
					// VIDEO RAM is mapped at this location
					return this->VideoModule->ReadMappedVRAM(A);
				}
				else
				{
					// Mapped lower page of RAM
					return this->MainRAM[A | (this->fSysLowPage << 15)];
				}
			}
			else
			{
				// Upper 32K
				if ((this->fSysMemSelect & 0x40) && ((this->fVRAMLocation & 0xe000) == (A & 0xe000)))
				{
					// VIDEO RAM is mapped at this location
					return this->VideoModule->ReadMappedVRAM(A);
				}
				else
				{
					if (((this->fSysMemSelect & 0x0f) < 0x02) && (!(this->fSysAltWrPg & 0x20)))
					{
						// Upper 32K page mapping is standard
						return this->MainRAM[A];
					}
					else
					{
						// Mapped upper 32K page of RAM
						return this->MainRAM[(A & 0x7fff) | ((this->fSysMemSelect & 0x0f) << 15)];
					}
				}
			}
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::LineCopWrite
//
//  PURPOSE:	Function called by the video system whenever the LineCop
//					writes to one of the system registers
//
//  INPUTS:		word					Address to be Written
//					byte					Value to be Written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::LineCopWrite(word A, byte V)
{
	if (A < 0x200)
	{
		// Write to Palette Registers
		this->VideoModule->WritePalette(A, V);
		return;
	}
	else if (A < 0x700)
	{
		// System Registers mapped - Regs paged in, no Direct VRAM
		switch (A & 0x0600)
		{
			case 0x200:
				switch (A)
				{
					case 0x208:
						this->MathsUnit->WriteMathsValueLSB(V);
						break;
					case 0x209:
						this->MathsUnit->WriteMathsValueMSB(V);
						break;
					case 0x20a:
						this->MathsUnit->WriteMathsIndex(V);
						break;
					default:
						// Video Regs
						this->VideoModule->WriteRegister(A, V);
						break;
				}
				return;
			case 0x400:
				// Sprite Regs
				this->VideoModule->WriteSpriteRegister(A, V);
				return;
			case 0x600:
				// Maths Table
				this->MathsUnit->WriteMathsTable(A & 0x1ff, V);
				return;
		}
	}
	else
	{
		// Maths Table
		this->MathsUnit->WriteMathsTable(A & 0x1ff, V);
		return;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_WRSTACK
//
//  PURPOSE:	Function called by the z80 core whenever a write
//					to an address is required in the course of setting
//					an OPCODE/OPERAND or STACK value
//
//  INPUTS:		word					Address to be Written
//					byte					Value to be Written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_WRSTACK(word A,byte V)
{
	switch (A & 0xf000)
	{
		case 0x0000:	// System Area
			if ((this->fSysMemSelect & 0x60) == 0x20)
			{
				// Direct Video Write Mode, no system preservation
				this->VideoModule->WriteDirectVRAM(A, V);
				return;
			}
			else
			{
				if (A < 0x200)
				{
					if ((this->fSysAltWrPg & 0x50) == 0x40)
					{
						// Palette paged out
						this->MainRAM[A | (this->fSysLowPage << 15)] = V;
					}
					else
					{
						// Can't write to ROM!
						// Write to Palette Registers
						this->VideoModule->WritePalette(A, V);
					}
				}
				else if (A < 0x800)
				{
					if (this->fSysAltWrPg & 0x80)
					{
						// Remaining registers paged out
						// Main RAM always if Direct VRAM not mapped
						this->MainRAM[A | (this->fSysLowPage << 15)] = V;
					}
					else
					{
						if (A < 0x600)
						{
							// System Registers mapped - Regs paged in, no Direct VRAM
							if (A & 0x400)
							{
								// Sprite Regs
								this->VideoModule->WriteSpriteRegister(A, V);
								return;
							}
							else
							{
								switch (A)
								{
									case 0x208:
										this->MathsUnit->WriteMathsValueLSB(V);
										break;
									case 0x209:
										this->MathsUnit->WriteMathsValueMSB(V);
										break;
									case 0x20a:
										this->MathsUnit->WriteMathsIndex(V);
										break;
									default:
										// Video Regs
										this->VideoModule->WriteRegister(A, V);
										break;
								}
								return;
							}
						}
						else
						{
							// Maths Table
							this->MathsUnit->WriteMathsTable(A & 0x1ff, V);
						}
					}
				}
				else
				{
					// Main RAM always if Direct VRAM not mapped
					this->MainRAM[A | (this->fSysLowPage << 15)] = V;
				}
			}
			break;
		case 0x1000:	// Sprite RAM
			if ((this->fSysMemSelect & 0xa0) == 0x80)
			{
				// Sprite RAM Mapped
				this->VideoModule->WriteSpriteRAM(A, V);
			}
			else if (this->fSysMemSelect & 0x20)
			{
				this->VideoModule->WriteDirectVRAM(A, V);
			}
			else
			{
				this->MainRAM[A | (this->fSysLowPage << 15)] = V;
			}
			break;
		default:
			if (A < 0x8000)
			{
				// This handles pages 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000
				if (this->fSysMemSelect & 0x20)
				{
					// Direct write to VRAM
					this->VideoModule->WriteDirectVRAM(A, V);
				}
				else if ((this->fSysMemSelect & 0x40) && ((this->fVRAMLocation & 0xe000) == (A & 0xe000)))
				{
					// Video RAM mapped at this location
					this->VideoModule->WriteMappedVRAM(A, V);
				}
				else
				{
					// Write to mapped lower 32k page of RAM
					this->MainRAM[A  | (this->fSysLowPage  << 15)] = V;
				}
			}
			else
			{
				// Upper 32k
				if (this->fSysMemSelect & 0x20)
				{
					// Direct Video Write
					this->VideoModule->WriteDirectVRAM(A, V);
				}
				else
				{
					if (this->fSysMemSelect & 0x10)
					{
						// Alt Write Mode
						if (((this->fSysAltWrPg & 0x0f) < 0x02) && (!(this->fSysAltWrPg & 0x20)))
						{
							// Upper 32k ram mapping unchanged
							this->MainRAM[A] = V;
						}
						else
						{
							// Write to mapped upper 32k of RAM
							this->MainRAM[(A & 0x7fff) | ((this->fSysAltWrPg & 0x0f) << 15)] = V;
						}
					}
					else
					{
						// Standard Write Mode
						if ((fSysMemSelect & 0x0f) < 0x02)
						{
							// Upper 32k ram mapping unchanged
							this->MainRAM[A] = V;
						}
						else
						{
							// Write to mapped upper 32k of RAM
							this->MainRAM[(A & 0x7fff) | ((this->fSysMemSelect & 0x0f) << 15)] = V;
						}
					}
				}
			}
			break;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_RDMEM
//
//  PURPOSE:	Function called by the z80 core whenever a read
//					from memory is required in the course of executing
//					a Z80 Read (LD) Instruction
//
//  INPUTS:		word					Address to be Read
//
//  OUTPUT:		byte					Value read from memory
//
///////////////////////////////////////////////////////////////////////
unsigned __fastcall TOSCABoard::Z80_RDMEM(word A)
{
	// Delegate through to the main read handler
	return this->Z80_RDSTACK(A);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_WRMEM
//
//  PURPOSE:	Function called by the z80 core whenever a write
//					to memory is required in the course of executing
//					a Z80 Write (LD) Instruction
//
//  INPUTS:		word					Address to be Written
//					byte					Value to be Written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_WRMEM(word A,byte V)
{
	// Delegate through to the main write handler
	this->Z80_WRSTACK(A, V);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_Interrupt
//
//  PURPOSE:	Function called by the Z80 core to check whether
//					a hardware interrupt has occurred
//
//  INPUTS:		void
//
//  OUTPUT:		int					Interrupt status value
//											(one of the following values:
//          							Z80_IGNORE_INT : No Interrupt Required
//											Z80_NMI_INT		: NMI Interrupt Required
//											Z80_INT_REQ		: IRQ Interrupt Required)
//
///////////////////////////////////////////////////////////////////////
int __fastcall TOSCABoard::Z80_Interrupt(void)
{
	if (this->NMIRequired)
	{
		this->NMIRequired = false;
		return Z80_NMI_INT;
	}
	else if ((this->IRQRequired) && (this->R.IFF1))
	{
		this->IRQRequired = false;
		return Z80_INT_REQ;
	}
	else
	{
		return Z80_IGNORE_INT;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_InstHook
//
//  PURPOSE:	Function called by the Z80 core after each opcode
//					has been executed
//
//  INPUTS:		int					Number of cycles used by the opcode
//											which has just been executed
//
//  OUTPUT:		bool					true if an interrupt has changed
//											state, or false otherwise.
//
//  NOTES:		The exact effect of this with regard to interrupts
//					will be tested using a call to Z80_Interrupt to determine
//					what type of interrupt has occurred, if any.
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCABoard::Z80_InstHook(int PC)
{
	byte IRQStates = 0x00;
	bool _timerIRQ, _keybIRQ, _mouseIRQ, _videoIRQ;

	// Handle Clocked Devices
	_videoIRQ = this->VideoModule->Tick(PC);
	_timerIRQ = this->TimerModule->Tick(PC);
	_keybIRQ = this->KeyboardDevice->Tick(PC);
	_mouseIRQ = this->MouseDevice->Tick(PC);
	this->SDMMCDevice->Tick(PC);

	// TODO: Clean this up!
	ThrotMaint += PC;
	this->CycleCount += PC;
	this->AudioCycleCount += PC;

	unsigned int AudioHandleDelay = 10000;
	if (this->AudioCycleCount >= AudioHandleDelay)
	{
		this->AudioSystem->UpdateSound(AudioHandleDelay);
		this->AudioCycleCount -= AudioHandleDelay;
		if (this->AudioSystem->GetAudioFlags() & 0xf0)
		{
			IRQStates = 0x08;
		}
	}

	cycles += PC;

	// Check if a Debugger Breakpoint has been reached
	if (this->R.PC.W.l == BreakPt)
	{
		this->Z80_ICount = -10;
		this->Z80_Running = 0;
		this->VideoModule->Tick(256 * 1024);
		MainForm->Screen->Refresh();
	}

	// Apply additional IRQ states
	IRQStates |= _timerIRQ 	? 0x04 : 0x00;
	IRQStates |= _mouseIRQ 	? 0x02 : 0x00;
	IRQStates |= _keybIRQ 	? 0x01 : 0x00;

	if ((this->fSysIRQEn & 0x80) && ((_videoIRQ) || ((this->fSysIRQEn & 0x0f) & IRQStates)))
	{
		this->IRQRequired = true;
	}
	else
	{
		this->IRQRequired = false;
	}

	  /*
	if (this->fSmoothStretch != OptionsForm->ChkAntiAlias->Checked)
	{
		this->fSmoothStretch = OptionsForm->ChkAntiAlias->Checked;
		MainForm->Screen->Bitmap->ResamplerClassName =
			(this->fSmoothStretch ? "TDraftResampler" : "TNearestResampler");
		MainForm->Screen->Bitmap->Resized();
		MainForm->Screen->Repaint();
	}
		 */
	return (this->NMIRequired | this->IRQRequired);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_Patch
//
//  PURPOSE:	Function called by the z80 core whenever the opcode
//					ED FE is executed - This is an invalid Z80 Instruction,
//					so can be used here by emulated programs as a "backdoor"
//					hook into the emulator
//
//  INPUTS:		Z80_Regs *			Pointer to the current Z80 Registers
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_Patch(Z80_Regs *Regs)
{
	char buf[100];
	// Called when ED FE occurs
	// DE = TRK, BC = SEC, HL = BUF, CF = Clear (Read)

	if (DiagForm)
	{
		sprintf(buf, "PC=0x%4.4x: CPM %s DSK=0x%2.2x TRK=0x%4.4x SEC=0x%4.4x BUF=0x%4.4x",
			Regs->PC.W.l, (Regs->AF.B.l & C_FLAG) ? "WR" : "RD",
			Regs->AF.B.h, Regs->DE.W.l, Regs->BC.W.l, Regs->HL.W.l);
		DiagForm->AddMessage(DIAG_UNUSED, (UnicodeString)buf);
	}

	unsigned int offset = (Regs->DE.W.l * 64 * 512) + (Regs->BC.W.l * 512);
	if (DiagForm)
	{
		sprintf(buf, "Offset=0x%8.8x", offset);
		DiagForm->AddMessage(DIAG_UNUSED, (UnicodeString)buf);
	}

#if 0
	if (!(Regs->AF.B.l & C_FLAG))
	{
		TMemoryStream *ms = new TMemoryStream();
		ms->LoadFromFile(L"c:\\cpmtools\\v6cpm.img");
		ms->Seek(offset, soFromBeginning);
		ms->Read(&MainRAM[Regs->HL.W.l], 512);
		delete ms;
	}
	else
	{
	}
#endif
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_Reti
//
//  PURPOSE:	Function called by the z80 core whenever a RETI
//					(RETurn from Irq) instruction is executed
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_Reti(void)
{
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::Z80_Retn
//
//  PURPOSE:	Function called by the z80 core whenever a RETN
//					(RETurn from Non-maskable interrupt) instruction
//					is executed
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::Z80_Retn(void)
{
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::LoadCurrentSettings
//
//  PURPOSE:	Load emulation settings previously stored to the
//					registry, supplying sane defaults where necessary
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::LoadCurrentSettings(void)
{
	UnicodeString regval;

#if 0
	// Hardware Options
	RamExPresent = GetRegistryValue("RamExpansion", "TRUE") == "TRUE" ? true : false;
	G1Present = GetRegistryValue("Graphics1", "TRUE") == "TRUE" ? true : false;
	G2Present = GetRegistryValue("Graphics2", "TRUE") == "TRUE" ? true : false;

	// General Options
	ScanDouble = GetRegistryValue("ScanDouble", "TRUE") == "TRUE" ? true : false;
#endif
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::SaveCurrentSettings
//
//  PURPOSE:	Save emulation settings to the registry
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCABoard::SaveCurrentSettings(void)
{
#if 0
	// Hardware Options
	SetRegistryValue("RamExpansion", RamExPresent ? "TRUE" : "FALSE");
	SetRegistryValue("Graphics1", G1Present ? "TRUE" : "FALSE");
	SetRegistryValue("Graphics2", G2Present ? "TRUE" : "FALSE");

	// General Options
	SetRegistryValue("ScanDouble", ScanDouble ? "TRUE" : "FALSE");
#endif
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::CanClose
//
//  PURPOSE:	Determine whether the app is allowed to close (to allow
//					chance to preserve unwritten sd changes etc)
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if it's ok to close, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCABoard::CanClose(void)
{
	if ((this->fWriteSupport) && (this->SDMMCDevice->GetChangesPending()))
	{
		int result = IDYES;

		if (!this->fSuppressWritePrompt)
		{
			result = Application->MessageBoxW(L"There are writes to the SD Card which have not\r"
			L"yet been committed to disk. Should these be\rwritten before quitting?\r"
			L"(NOTE: If you select NO, uncommitted writes\rwill be discarded!!)",
			L"Unwritten SD Card Changes", MB_YESNOCANCEL | MB_ICONQUESTION);
		}

		if (result == IDYES)
		{
			this->SDMMCDevice->WriteToImage(this->fImageFilename);
			return true;
		}
		else if (result == IDCANCEL)
		{
			return false;
		}
	}

	return true;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::PowerOnReset
//
//  PURPOSE:	Perform a PowerOnReset of the whole architecture
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void TOSCABoard::PowerOnReset(void)
{
	char classname[100];
	char load_hex[100];
	char romname[MAX_PATH];
	unsigned int load_addr;

	TFileStream *RFS;

	if (this->m_pCPUThread)
	{
		this->m_pCPUThread->Reset(true);
		do {
			Sleep(10);
		} while (!this->m_pCPUThread->IsReset());
	}

	if ((this->fWriteSupport) && (this->SDMMCDevice->GetChangesPending()))
	{
		int result = IDYES;

		if (!this->fSuppressWritePrompt)
		{
			result = Application->MessageBoxW(L"There are writes to the SD Card which have not\r"
			L"yet been committed to disk. Should these be\rwritten before a reset is performed?\r"
			L"(NOTE: If you select NO, uncommitted writes\rwill be discarded!!)",
			L"Unwritten SD Card Changes", MB_YESNOCANCEL | MB_ICONQUESTION);
		}

		if (result == IDYES)
		{
			this->SDMMCDevice->WriteToImage(this->fImageFilename);
		}

		if (result == IDCANCEL)
		{
			return;
		}
	}

	// Reset Memory
#ifdef RANDOM_RAM
	TrashMemory(&this->MainRAM, (16 * 0x8000));
	TrashMemory(&this->BootROM, 512);
#else
	memset(&this->MainRAM, 0, (16 * 0x8000));    // Clear main RAM
	memset(&this->BootROM, 0, 512);					// Clear Boot ROM
#endif

	if (!FileExists(L"config.txt"))
	{
		Application->MessageBox(L"Configuration file not found", L"Fatal Configuration Error", MB_OK | MB_ICONSTOP);
		Application->Terminate();
		return;
	}

	// Set some sane defaults here each reset
	this->fWriteSupport				= false;
	this->fSuppressWritePrompt		= false;
	this->fImageFilename				= L"";

	FILE *fp = fopen("config.txt", "r");
	while (!feof(fp))
	{
		fscanf( fp, "%s", classname);
		strcpy(&classname[0], strlwr(&classname[0]));

		if (feof(fp))
		{
			break;
		}

		if (!strcmp(classname, "bootrom"))
		{
			fscanf( fp, "%s\n", romname);

			if (!FileExists((UnicodeString)romname))
			{
				Application->MessageBox(L"Boot ROM file not found", L"Fatal File Load Error", MB_OK | MB_ICONSTOP);
				Application->Terminate();
				return;
			}

			// Load Boot ROM image
			RFS = new TFileStream((UnicodeString)romname, fmOpenRead);
			RFS->Read(&this->BootROM[0], 0x200);
			RFS->Free();
		}
		else if (!strcmp(classname, "eeprom"))
		{
			fscanf(fp, "%s %s\n", load_hex, romname);
			load_addr = HToI((AnsiString)load_hex);

			if (FileExists((UnicodeString)romname))
			{
				// Load EEPROM image
				this->PICModule->LoadEEPROM((UnicodeString)romname, load_addr);
			}
			else
			{
				Application->MessageBox(L"EEPROM file not found, skipping", L"File Load Error", MB_OK | MB_ICONWARNING);
			}
		}
		else if (!strcmp(classname, "sdcard"))
		{
			fscanf(fp, "%s %s\n", load_hex, romname);
			load_addr = HToI((AnsiString)load_hex);
			this->fImageFilename = (UnicodeString)romname;

			if (FileExists(this->fImageFilename))
			{
				// Load EEPROM image
				this->SDMMCDevice->AttachImage(this->fImageFilename);//, load_addr);
			}
			else
			{
				Application->MessageBox(L"SDCARD image not found, skipping", L"File Load Error", MB_OK | MB_ICONWARNING);
			}
		}
		else if (!strcmp(classname, "writeenable"))
		{
			this->fWriteSupport = true;
		}
		else if (!strcmp(classname, "nowriteprompt"))
		{
			this->fSuppressWritePrompt = true;
		}
		else
		{
			fscanf(fp, "\n");
		}
	}
	fclose(fp);

	// Reset Devices
	this->PICModule->Reset();
	this->VideoModule->Reset();
	this->TimerModule->Reset();
	this->KeyboardDevice->Reset();
	this->MouseDevice->Reset();
	this->MathsUnit->Reset();
	this->SDMMCDevice->Reset();

	this->Z80_Reset();

	// Reset Internal Flags
	this->fSysMemSelect 	= 0x00;
	this->fSysAltWrPg		= 0x00;
	this->fSysLowPage		= 0x00;
	this->fSysIRQEn		= 0x00;
	this->NMIRequired 	= false;
	this->IRQRequired		= false;
	this->fJoystickPort	= false;
	this->fVRAMLocation	= 0x2000;

	// TODO: Clean up these variables
	this->CycleCount = 0;
	this->AudioCycleCount = 0;
	this->VideoCycleCount = 0;

	if ((MainForm) && (MainForm->StatusBar))
		MainForm->StatusBar->Refresh();

	if (this->m_pCPUThread)
	{
		this->m_pCPUThread->Reset(false);
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::KeyPressed
//
//  PURPOSE:	Notification from the UI that a key was pressed
//
//  INPUTS:		WORD					Key code of pressed key
//					int					Shift modifier flags
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void TOSCABoard::KeyPressed(WORD Key, int Shift)
{
	char buf[100];

	if (KeyboardMap[Key & 0xff] != 0x0000)
	{
		if (DiagForm)
		{
			sprintf(buf, "Key Press 0x%4.4x, 0x%4.4x", Key, Shift);
			DiagForm->AddMessage(DIAG_KBMOUSE, (UnicodeString)buf);
		}

		if ((KeyboardMap[Key & 0xff] & 0xff00) == 0xe000)
		{
			this->KeyboardDevice->PushByte(0xe0);
		}

		this->KeyboardDevice->PushByte(KeyboardMap[Key & 0xff] & 0xff);
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::KeyReleased
//
//  PURPOSE:	Notification from the UI that a key was released
//
//  INPUTS:		WORD					Key code of released key
//					int					Shift modifier flags
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void TOSCABoard::KeyReleased(WORD Key, int Shift)
{
	char buf[100];

	if (KeyboardMap[Key & 0xff] != 0x0000)
	{
		if (DiagForm)
		{
			sprintf(buf, "Key Release 0x%4.4x, 0x%4.4x", Key, Shift);
			DiagForm->AddMessage(DIAG_KBMOUSE, (UnicodeString)buf);
		}

		if ((KeyboardMap[Key & 0xff] & 0xff00) == 0xe000)
		{
			KeyboardDevice->PushByte(0xe0);
		}

		this->KeyboardDevice->PushByte(0xf0);
		this->KeyboardDevice->PushByte(KeyboardMap[Key & 0xff] & 0xff);
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::SDReading
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					hardware is in the process of performing a Read operation
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if the device is reading, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCABoard::SDReading(void)
{
	if (this->SDMMCDevice == NULL)
	{
		return false;
	}
	else
	{
		return this->SDMMCDevice->IsReading();
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::SDWriting
//
//  PURPOSE:	Helper function used to determine whether the SD/MMC
//					hardware is in the process of performing a Write operation
//
//  INPUTS:		void
//
//  OUTPUT:		bool					true if the device is writing, else false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCABoard::SDWriting(void)
{
	if (this->SDMMCDevice == NULL)
	{
		return false;
	}
	else
	{
		return this->SDMMCDevice->IsWriting();
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::GetCyclesPerSecond
//
//  PURPOSE:	Obtain required MHz CPU Speed
//
//  INPUTS:		void
//
//  OUTPUT:		int					Number of cycles per second required
//
///////////////////////////////////////////////////////////////////////
int __fastcall TOSCABoard::GetCyclesPerSecond(void)
{
	return OSCA_CyclesPerSecond;
}

TOSCAVideo * __fastcall TOSCABoard::GetVideoModule(void)
{
	return this->VideoModule;
}

void __fastcall TOSCABoard::ShutdownThreads(void)
{
	if (m_pVideoThread)
	{
		if (m_pVideoThread->Suspended)
		{
			// Need to resume to terminate
			m_pVideoThread->Resume();
		}

		m_pVideoThread->Terminate();
		m_pVideoThread->WaitFor();

		delete m_pVideoThread;
		m_pVideoThread = NULL;
	}

	if (m_pCPUThread)
	{
		if (m_pCPUThread->Suspended)
		{
			// Need to resume to terminate
			m_pCPUThread->Resume();
		}

		m_pCPUThread->Terminate();
		m_pCPUThread->WaitFor();

		delete m_pCPUThread;
		m_pCPUThread = NULL;
	}

	// Dump RAM here
	#if 0
	TFileStream *fs = new TFileStream(L"main.ram", fmCreate);
	fs->WriteBuffer(this->MainRAM, 512 * 1024);
	delete fs;

	fs = new TFileStream(L"video.ram", fmCreate);
	fs->WriteBuffer(this->VideoModule->VideoRAM, 512 * 1024);
	delete fs;

	fs = new TFileStream(L"sprite.ram", fmCreate);
	fs->WriteBuffer(this->VideoModule->SpriteRAM, 128 * 1024);
	delete fs;

	fs = new TFileStream(L"palette.ram", fmCreate);
	fs->WriteBuffer(this->VideoModule->PaletteRAM, 2 * 512);
	delete fs;
	#endif
}

void __fastcall TOSCABoard::Execute(void)
{
	z80emu->Z80_ICount = GetCyclesPerSecond() / 120;
	z80emu->Z80_IPeriod = Z80_ICount >> 4;
	z80emu->Z80_Execute();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::TOSCABoard [CONSTRUCTOR]
//
//  PURPOSE:	Constructor for the main OSCA Board Class
//
//  INPUTS:		HWND              Handle for main window
//
//  OUTPUT:		None
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCABoard::TOSCABoard(HWND handle) :
	mainHandle(handle)
{
	// Read current configuration settings
	this->LoadCurrentSettings();

	// Create ancillary hardware classes
	this->VideoModule 		= new TOSCAVideo(this);
	this->PICModule 			= new TOSCAPIC();
	this->TimerModule 		= new TOSCATimer();
	this->KeyboardDevice 	= new TOSCAKeyboard();
	this->MouseDevice 		= new TOSCAMouse();
	this->MathsUnit			= new TOSCAMathsAssist();
	this->SDMMCDevice			= new TOSCASDCard();
	this->AudioSystem			= new TOSCAAudioSystem(mainHandle, this);

	this->fWriteSupport				= false;
	this->fSuppressWritePrompt		= false;
	this->fImageFilename				= L"";
	this->fSmoothStretch				= true;

	// Power-On Reset the whole architecture
	this->PowerOnReset();

	m_pVideoThread				= new VideoThread(this);
	if (m_pVideoThread)
	{
		m_pVideoThread->Start();
	}

	m_pCPUThread				= new Z80Thread(this);
	if (m_pCPUThread)
	{
		m_pCPUThread->Start();
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCABoard::~TOSCABoard [DESTRUCTOR]
//
//  PURPOSE:	Destructor for the main OSCA Board Class
//
//  INPUTS:		None
//
//  OUTPUT:		None
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCABoard::~TOSCABoard()
{
	this->SaveCurrentSettings();

	if (this->SDMMCDevice)
	{
		delete this->SDMMCDevice;
		this->SDMMCDevice = NULL;
	}

	if (this->MathsUnit)
	{
		delete this->MathsUnit;
		this->MathsUnit = NULL;
	}

	if (this->MouseDevice)
	{
		delete this->MouseDevice;
		this->MouseDevice = NULL;
	}

	if (this->KeyboardDevice)
	{
		delete this->KeyboardDevice;
		this->KeyboardDevice = NULL;
	}

	if (this->TimerModule)
	{
		delete this->TimerModule;
		this->TimerModule = NULL;
	}

	if (this->PICModule)
	{
		delete this->PICModule;
		this->PICModule = NULL;
	}

	if (this->VideoModule)
	{
		delete this->VideoModule;
		this->VideoModule = NULL;
	}

	if (this->AudioSystem)
	{
		delete this->AudioSystem;
		this->AudioSystem = NULL;
	}
}

//---------------------------------------------------------------------------

#pragma package(smart_init)

