/*
 * Acorn TUBE emulation.
 */

#include "AcornTUBE.h"

#include "dlc/dlc_register.h"

AcornTUBE::AcornTUBE (void):
	MMIO_Dev (0xffe0, 0xfee0)
{
	PH_R1 = new ByteFIFO (24);
	PH_R3 = new ByteFIFO (2);
	HP_R3 = new ByteFIFO (2);
	paracomp = NULL;
	flags = MMIO_IS_ACORNTUBE;
}

void AcornTUBE::Reset (void)
{
	if (paracomp)
		paracomp->Reset ();

	PH_R1->Reset ();
	PH_R3->Reset ();
	HP_R3->Reset ();
	HP_R1 = PH_R2 = HP_R2 = PH_R4 = HP_R4 = -1;
	status = 0;		// I'm guessing here...
}

AcornTUBE::~AcornTUBE (void)
{
	delete PH_R1;
	delete PH_R3;
	delete HP_R3;
}

byte AcornTUBE::MMIO_Read (int addr)
{
	int t;
	
	//printf ("Host Reads AcornTUBE! %x\n", addr);
	
	switch (addr)
	{
		case 0x00:	// Status flags and R1 flags
		t = status;
		if (HP_R1 == -1)
			t |= 0x40;
		if (!PH_R1->Empty ())
			t |= 0x80;
		return t;
		
		case 0x01:	// R1DATA
		if (!PH_R1->Empty ())
		{
			t = PH_R1->Read ();
			//printf ("Host reads 24-byte FIFO %x\n", t);
			return t;
		}
		else
			return 0;
		
		case 0x02:	// R2STATUS
		t = ~(0x40 | 0x80);
		if (HP_R2 == -1)
			t |= 0x40;
		if (PH_R2 != -1)
			t |= 0x80;
		return t;
		
		case 0x03:	// R2DATA
		t = PH_R2;
		PH_R2 = -1;
		return (t == -1) ? 0 : t;
		
		case 0x04:	// R3STATUS
		t = ~(0x40 | 0x80);
		if (status & FIFO_R3)
		{
			if (!HP_R3->Full ())
				t |= 0x40;
			if (!PH_R3->Empty ())
				t |= 0x80;
		}
		else
		{
			if (HP_R3_byte == -1)
				t |= 0x40;
			if (PH_R3_byte != -1)
				t |= 0x80;
		}
		return t;
		
		case 0x05:	// R3DATA
		if (status & FIFO_R3)
		{
			if (!PH_R3->Empty ())
				return PH_R3->Read ();
			else
				return 0;
		}
		else
		{
			t = PH_R3_byte;
			PH_R3_byte = -1;
			return (t == -1) ? 0 : t;
		}
		
		case 0x06:	// R4STATUS
		t = ~(0x40 | 0x80);
		if (HP_R4 == -1)
			t |= 0x40;
		if (PH_R4 != -1)
			t |= 0x80;
		return t;
		
		case 0x07:	// R4DATA
		t = PH_R4;
		PH_R4 = -1;
		return (t == -1) ? 0 : t;
		
		default:	// Shouldn't happen
		return 0xfe;
	}
}

void AcornTUBE::MMIO_Write (int addr, byte val)
{
	//printf ("Host Writes AcornTUBE! %x=%x\n", addr, val);
	
	switch (addr)
	{
		case 0x00:	// Status flags and R1 flags
		if (val & 0x80)
			status |= val & 0x3f;
		else
			status &= ~(val & 0x3f);
		if (status & PARA_RESET)
		{
			if (paracomp)
				paracomp->cpu->ResetCPU ();
			status &= ~PARA_RESET;
		}
		break;
		
		case 0x01:	// R1DATA
		HP_R1 = val;
		if ((status & IRQ_R1) && paracomp)
			paracomp->cpu->SignalIRQ ();
		break;
		
		case 0x02:	// R2STATUS
		break;
		
		case 0x03:	// R2DATA
		HP_R2 = val;
		break;
		
		case 0x04:	// R3STATUS
		break;
		
		case 0x05:	// R3DATA
		if (status & FIFO_R3)
		{
			if (HP_R3->Full ())
				HP_R3->Read ();
			HP_R3->Write (val);
		}
		else
			HP_R3_byte = val;
		if ((status & NMI_R3) && paracomp)
			paracomp->cpu->SignalNMI ();
		break;
		
		case 0x06:	// R4STATUS
		break;
		
		case 0x07:	// R4DATA
		HP_R4 = val;
		if ((status & IRQ_R4) && paracomp)
			paracomp->cpu->SignalIRQ ();
		break;
	}
}

byte AcornTUBE::ReadParasiteReg (int addr)
{
	int t;
	
	//printf ("Parasite Reads AcornTUBE! %x\n", addr);
		
	switch (addr)
	{
		case 0x00:	// Status flags and R1 flags
		t = status;
		if (!PH_R1->Full ())
			t |= 0x40;
		if (HP_R1 != -1)
			t |= 0x80;
		return t;
		
		case 0x01:	// R1DATA
		t = HP_R1;
		HP_R1 = -1;
		return (t == -1) ? 0 : t;
		
		case 0x02:	// R2STATUS
		t = ~(0x40 | 0x80);
		if (PH_R2 == -1)
			t |= 0x40;
		if (HP_R2 != -1)
			t |= 0x80;
		return t;
		
		case 0x03:	// R2DATA
		t = HP_R2;
		HP_R2 = -1;
		return (t == -1) ? 0 : t;
		
		case 0x04:	// R3STATUS
		t = ~(0x40 | 0x80);
		if (status & FIFO_R3)
		{
			if (!PH_R3->Full ())
				t |= 0x40;
			if (!HP_R3->Empty ())
				t |= 0x80;
		}
		else
		{
			if (PH_R3_byte == -1)
				t |= 0x40;
			if (HP_R3_byte != -1)
				t |= 0x80;
		}
		return t;
		
		case 0x05:	// R3DATA
		if (status & FIFO_R3)
		{
			if (!HP_R3->Empty ())
				return HP_R3->Read ();
			else
				return 0;
		}
		else
		{
			t = HP_R3_byte;
			HP_R3_byte = -1;
			return (t == -1) ? 0 : t;
		}
		
		case 0x06:	// R4STATUS
		t = ~(0x40 | 0x80);
		if (PH_R4 == -1)
			t |= 0x40;
		if (HP_R4 != -1)
			t |= 0x80;
		return t;
		
		case 0x07:	// R4DATA
		t = HP_R4;
		HP_R4 = -1;
		return (t == -1) ? 0 : t;
		
		default:	// Shouldn't happen
		return 0xfe;
	}
}

void AcornTUBE::WriteParasiteReg (int addr, byte val)
{
	//printf ("Parasite Writes AcornTUBE! %x=%x\n", addr, val);
	
	switch (addr)
	{
		case 0x00:	// Status flags and R1 flags
		if (val & 0x80)
			status |= val & 0x3f;
		else
			status &= ~(val & 0x3f);
		break;
		
		case 0x01:	// R1DATA
		if (PH_R1->Full ())
			PH_R1->Read ();
		PH_R1->Write (val);
		break;
		
		case 0x02:	// R2STATUS
		break;
		
		case 0x03:	// R2DATA
		PH_R2 = val;
		break;
		
		case 0x04:	// R3STATUS
		break;
		
		case 0x05:	// R3DATA
		if (status & FIFO_R3)
		{
			if (PH_R3->Full ())
				PH_R3->Read ();
			PH_R3->Write (val);
		}
		else
			PH_R3_byte = val;
		break;
		
		case 0x06:	// R4STATUS
		break;
		
		case 0x07:	// R4DATA
		PH_R4 = val;
		if (status & HOST_IRQ_R4)
			comp->cpu->SignalIRQ ();
		break;
	}
}

void AcornTUBE::SetParasite (CompEmul *parasite)
{
	paracomp = parasite;
}

void AcornTUBE::AddOK (void)
{
	printf ("Acorn TUBE emulation added OK.\n");
}

DLC_Register (AcornTUBE);

/* End of file. */
