/*
 * BBC Model B+ emulation
 */

#include "BPlus.h"
#include "utils.h"

#include "ACIA.h"

#include "dlc/dlc_register.h"

#define CPU_6512
#define HACK_WRCH
#define HACK_RDCH
#include "CPU6502Emul.cc"
template CPU6502Emul<BPlusMem>;

#define _cpu (CPU6502Emul<BPlusMem> *)(cpu)
#define _mem (BPlusMem *)(mem)

// Inline functions

byte BPlusMem::Read (int addr)
{
	int pc;
	
	if (paged_in && addr >= 0x8000 && addr <= 0xafff)
		return paged12K[addr - 0x8000];
	if (shadow && addr >= 0x3000 && addr <= 0x7fff)
	{
		pc = ((CPU6502Emul<BPlusMem> *)(parent->cpu))->GetPC ();
		if ((pc >= 0xc000 && pc <= 0xdfff) || (paged_in &&
			pc >= 0xa000 && pc <= 0xafff))
			return shadowRAM[addr - 0x3000];
	}
	return MemEmul::Read (addr);
}

void BPlusMem::Write (int addr, byte val)
{
	int pc;
	
	if (addr == 0xfe30)
	{
		Switch_SWbank (val & 0xf);
		paged_in = val & 0x80;
		return;
	}
	else if (addr == 0xfe34)
	{
		shadow = val & 0x80;
		return;
	}
	else if (paged_in && addr >= 0x8000 && addr <= 0xafff)
	{
		paged12K[addr - 0x8000] = val;
		return;
	}
	else if (shadow && addr >= 0x3000 && addr <= 0x7fff)
	{
		pc = ((CPU6502Emul<BPlusMem> *)(parent->cpu))->GetPC ();
		if ((pc >= 0xc000 && pc <= 0xdfff) || (paged_in &&
			pc >= 0xa000 && pc <= 0xafff))
		{
			if (addr >= displaymem_begin)
				display[addr - displaymem_begin] = 1;
			shadowRAM[addr - 0x3000] = val;
		}
		else
			coremem[addr] = val;
		return;
	}
	MemEmul::Write (addr, val);
}

BPlusEmul::BPlusEmul (void)
{
	printf ("Model B+ emulation: BPlus.cc $Revision: 1.2 $\n\n");
	
	mem = new BPlusMem (this);
	dprintf ("Memory emulator created OK.\n");
	cpu = new CPU6502Emul<BPlusMem> (_mem);
	dprintf ("CPU Emulator created OK.\n");
}

BPlusEmul::~BPlusEmul (void)
{
	delete cpu;
	delete mem;
}

void BPlusEmul::ExecThread (void)
{
	while (1)
	{
		if (trace)
			cpu->Trace ();
		cpu->ExecInstruction ();
		if (cpu->check_cycles (200, 0))
		{
			// 1MHz bus ticks
			DoTicks (100);
		}
	}
}

BPlusMem::BPlusMem (BPlusEmul *p)
{
	parent = p;
	
	// Load the standard ROMs
	ReadFile ("b+os.rom", &coremem[0xc000], 0x4000);
	ReadFile ("basic2.rom", swbanks[0xf], 0x4000);
	dprintf ("Standard ROMs added OK.\n");
	
	cur_swbank = -1;
	cur_sw_mod = 0;
	shadow = 0;
	paged_in = 0;
	Switch_SWbank (0xf);
	
	coremem[0x20e] = coremem[0x20f] = coremem[0x210] = coremem[0x211] = 0xff;
	
	// Add the standard devices
	sysVIA = new BPlusSysVIA; AddDev (sysVIA, 0xfe40, 0xffe0);
	usrVIA = new BPlusUsrVIA; AddDev (usrVIA, 0xfe60, 0xffe0);
	acia = new ACIA; AddDev (acia, 0xfe08, 0xfff8);
	parent->Add_Ticker (sysVIA);
	parent->Add_Ticker (usrVIA);
	parent->Add_Ticker (acia);
}

BPlusMem::~BPlusMem (void)
{
	delete acia;
	delete usrVIA;
	delete sysVIA;
}

#define SYSVIA BPlusSysVIA
#include "StdSysVIA.cc"

#define USRVIA BPlusUsrVIA
#include "StdUsrVIA.cc"

DLC_Register (BPlusEmul);

/* End of file. */
