/*
 * Implementation of the MemEmul and MMIO_Dev base classes
 */

#include "MemEmul.h"
#include "utils.h"

#include <stdio.h>

void MMIO_Dev::SignalInt (void)
{
	SignalIRQ ();
}

void MMIO_Dev::ClearInt (void)
{
	ClearIRQ ();
}

MMIO_Dev::~MMIO_Dev (void)
{
	ListElem<MemEmul *> *i;
	
	for (i = mememuls.getFirst (); i != NULL; i = i->getNext ())
		(**i)->RemoveDev (this);
}

void MMIO_Dev::AddOK (void)
{

}

void MMIO_Dev::RemoveOK (void)
{

}

void MMIO_Dev::Reset (void)
{

}

void MMIO_Dev::Break (void)
{
	Reset ();
}

void MMIO_Dev::DoTicks (int ticks)
{

}

MemEmul::MemEmul (void)
{
	int i;
	
	for (i = 0; i < 0xff00-0xfc00; i++)
		mmio_tbl[i].dev = NULL;
	displaymem_begin = 0x8000;
}

MemEmul::~MemEmul (void)
{

}

void MemEmul::Switch_SWbank (int bank)
{
	if (bank == cur_swbank) return;
	if (cur_sw_mod)
		memcpy (swbanks[cur_swbank], &coremem[0x8000], 0x4000);
	cur_sw_mod = 0;
	memcpy (&coremem[0x8000], swbanks[bank], 0x4000);
	cur_swbank = bank;
	cur_sw_rdonly = swbank_rdonly[bank];
}

#define MT mmio_tbl[addr&0x3ff]

byte MemEmul::Read_MMIO (int addr)
{
	if (MT.dev)
		return MT.dev->MMIO_Read (MT.reg);
	else
		return addr>>8;
}


void MemEmul::OtherWrite (int addr, byte val)
{
	if (addr >= 0xfc00 && addr < 0xff00)
	{
		if (MT.dev)
			MT.dev->MMIO_Write (MT.reg, val);
	}
	else if (addr < 0xc000 && !cur_sw_rdonly)
	{
		coremem[addr] = val;
		cur_sw_mod = 1;
	}
}

void MemEmul::AddDev (MMIO_Dev *d, int addr, int mask)
{
	int a, m;
	int i, max;
	
	a = (addr == -1) ? d->addr.value : addr;
	m = (mask == -1) ? d->addr.mask : mask;
	
	if (((a & 0xff00) < 0xfc00) || ((a & 0xff00) > 0xfe00))
	{
		error ("Can't add MMIO device at %x!", a);
		return;
	}
	
	// Calculate the max address
	for (i = 7; i >= 0; i--)
		if (!(m & (1<<i)))
			break;
	
	max = a+(1<<(i+1));
	
	for (i = a; i < max; i++)
		if ((i & m) == a)
		{
			if (mmio_tbl[i-0xfc00].dev != NULL)
				warning ("overwriting existing MMIO device at %x!", i);
			mmio_tbl[i-0xfc00].dev = d;
			mmio_tbl[i-0xfc00].reg = i & d->addr.reg_mask;
		}
	d->mememuls.Add (this);
	mmio.Add (d);
}

void MemEmul::RemoveDev (MMIO_Dev *d)
{
	int i;
	
	for (i = 0; i < 0xff00-0xfc00; i++)
		if (mmio_tbl[i].dev == d)
			mmio_tbl[i].dev = NULL;
	d->mememuls.Remove (this);
}

void MemEmul::ReadROM (int slot, char *fname)
{
	if (slot < 0 || slot > 0xf)
	{
		error ("Unable to read ROM into slot %d!", slot);
		return;
	}
	ReadFile (fname, swbanks[slot], 0x4000, 1);
}

void MemEmul::SetSW (int slot, int rw)
{
	if (slot < 0 || slot > 0xf)
	{
		error ("Unable to read ROM into slot %d!", slot);
		return;
	}
	swbank_rdonly[slot] = rw;
}

/* End of file. */
