/***************************************************************************

  machine.c

  Functions to emulate general aspects of the machine (RAM, ROM, interrupts,
  I/O ports)

***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include "Z80.h"
#include "machine.h"
#include "vidhrdw.h"
#include "sndhrdw.h"
#include "roms.h"
#include "memmap.h"
#include "osdepend.h"

#ifdef UNIX
#define uclock_t clock_t
#define	uclock clock
#define UCLOCKS_PER_SEC CLOCKS_PER_SEC
#endif



/* CPU_SPEED is the speed of the CPU in Hz. It is used together with */
/* FRAMES_PER_SECOND to calculate how much CPU cycles must pass between */
/* interrupts. */
#define CPU_SPEED 3072000       /* 3.072 Mhz */

#define FRAMES_PER_SECOND 60


unsigned char RAM[0x10000];		/* 64k of RAM */

/* dip switches */
int dsw[2];
const struct DSW *dswsettings;

const byte *evetab,*oddtab;
extern const byte ccevetab[],ccoddtab[],ccbootevetab[],ccbootoddtab[];

/***************************************************************************

  Initialize the emulated machine (load the roms, initialize the various
  subsystems...). Returns 0 if successful.

***************************************************************************/
int init_machine(const char *gamename)
{
	int i;
	FILE *f;
	char name[100];


	i = 0;
	while (gameinfo[i].name && stricmp(gamename,gameinfo[i].name) != 0)
		i++;

	if (readroms(RAM,gameinfo[i].rom,gamename) != 0)
		return 1;

	if (vh_init(gamename))
	{
		printf("Cannot initialize video emulation\n");
		return 1;
	}

	if (sh_init(gamename))
	{
		printf("Cannot initialize sound emulation\n");
		return 1;
	}

	dswsettings = gameinfo[i].dswsettings;
	dsw[0] = gameinfo[i].defaultdsw[0];
	dsw[1] = gameinfo[i].defaultdsw[1];

	/* read dipswitch settings from disk */
	sprintf(name,"%s/%s.dsw",gamename,gamename);
	if ((f = fopen(name,"rb")) != 0)
	{
		fread(dsw,1,2,f);
		fclose(f);
	}

	return 0;
}



/***************************************************************************

  Run the emulation. Start the various subsystems and the CPU emulation.
  Returns non zero in case of error.

***************************************************************************/
int run_machine(const char *gamename)
{

        if (vh_start() == 0)	/* start the video hardware */
	{
		if (sh_start() == 0)	/* start the audio hardware */
		{
			reg StartRegs;
			FILE *f;
			char name[100];


			IPeriod = CPU_SPEED / FRAMES_PER_SECOND;	/* Number of T-states per interrupt */
			ResetZ80(&StartRegs);
			Z80(&StartRegs);		/* start the CPU emulation */

			sh_stop();
			vh_stop();

			/* write dipswitch settings from disk */
			sprintf(name,"%s/%s.dsw",gamename,gamename);
			if ((f = fopen(name,"wb")) != 0)
			{
				fwrite(dsw,1,2,f);
				fclose(f);
			}

			return 0;
		}
		else printf("Unable to setup audio\n");

		vh_stop();
	}
	else printf("Unable to setup display\n");

	return 1;
}



/***************************************************************************

  Perform a memory read. This function is called by the CPU emulation.

***************************************************************************/
byte M_RDMEM (dword A)
{
	/* handle input ports (see memmap.h for details) */
	switch (A)
	{
		case IN0_PORT:
		{
			byte res = 0;


                       /* if (osd_key_pressed(OSD_KEY_E)) res |= IN0_LEFT_UP;
			if (osd_key_pressed(OSD_KEY_D)) res |= IN0_LEFT_DOWN;
			if (osd_key_pressed(OSD_KEY_S)) res |= IN0_LEFT_LEFT;
                       */
                        if (osd_key_pressed(OSD_KEY_CONTROL)) res |= IN0_LEFT_RIGHT;
                        if (osd_key_pressed(OSD_KEY_UP)) res |= IN0_RIGHT_UP;
                        if (osd_key_pressed(OSD_KEY_DOWN)) res |= IN0_RIGHT_DOWN;
                        if (osd_key_pressed(OSD_KEY_LEFT)) res |= IN0_RIGHT_LEFT;
                        if (osd_key_pressed(OSD_KEY_RIGHT)) res |= IN0_RIGHT_RIGHT;
			return res;
			break;
		}
		case IN2_PORT:
		{
                        byte res = 0xff;//IN2_STANDUP;


                        if (osd_key_pressed(OSD_KEY_2)) res ^= IN2_START2;
                        if (osd_key_pressed(OSD_KEY_1)) res ^= IN2_START1;
                        if (osd_key_pressed(OSD_KEY_3)) res ^= IN2_CREDIT;
			return res;
			break;
		}
		case DSW1_PORT:
		{
			byte res = dsw[0];


			if (osd_key_pressed(OSD_KEY_F1)) res |= DSW1_RACK_TEST;
			return res;
			break;
		}
		default:
			return RAM[A];
			break;
	}
}



/***************************************************************************

  Perform a memory write. This function is called by the CPU emulation.

***************************************************************************/
void M_WRMEM (dword A,byte V)
{
	if (A <= ROM_END) return;	/* Do nothing, it's ROM */
	else if (vh_wrmem(A,V)) return;	/* the video hardware handled the write */
	else if (sh_wrmem(A,V)) return;	/* the sound hardware handled the write */
	else RAM[A] = V;
}



/***************************************************************************

  Interrupt handler. This function is called at regular intervals
  (determined by IPeriod) by the CPU emulation.

***************************************************************************/
int Interrupt(void)
{
	static uclock_t prev;
	uclock_t curr;
        
      sh_update(); /* update sound */
        if(RAM[INTERRUPT_ENABLE])
        vh_screenrefresh(); /*update screen */

	/* if the user pressed ESC, stop the emulation */
	if (osd_key_pressed(OSD_KEY_ESC)) CPURunning = 0;

	/* if the user pressed F2, reset the machine */
	if (osd_key_pressed(OSD_KEY_F2))
	{
		ResetZ80(&R);
		return IGNORE_INT;
	}

	/* if TAB, go to dipswitch setup menu */
	if (osd_key_pressed(OSD_KEY_TAB)) setdipswitches(dsw,dswsettings);

	if (osd_key_pressed(OSD_KEY_P)) /* pause the game */
	{
		struct DisplayText dt[] =
		{
			{ "PAUSED", RED_TEXT, 13, 16 },
			{ 0, 0, 0, 0 }
		};
		int key;


		displaytext(dt,0);

		while (osd_key_pressed(OSD_KEY_P));	/* wait for key release */
		do
		{
			key = osd_read_key();

			if (key == OSD_KEY_ESC) CPURunning = 0;
			else if (key == OSD_KEY_TAB)
			{
				setdipswitches(dsw,dswsettings);	/* might set CPURunning to 0 */
				displaytext(dt,0);
			}
		} while (CPURunning && key != OSD_KEY_P);
		while (osd_key_pressed(key));	/* wait for key release */

		vh_screenrefresh();
	}

	/* now wait until it's time to trigger the interrupt */
        do
        {
                curr = uclock();
        } while ((curr - prev) < UCLOCKS_PER_SEC/FRAMES_PER_SECOND);

	prev = curr;

	if (RAM[INTERRUPT_ENABLE]) return NMI_INT;
	else return IGNORE_INT;
}



/***************************************************************************

  This function is called by the CPU emulation when the EI instruction is
  executed. We don't need to do anything fancy.

***************************************************************************/
int InterruptsEnabled(void)
{
	return IGNORE_INT;
}



/***************************************************************************

  Execute an OUT instruction. This function is called by the CPU emulation.

***************************************************************************/
void DoOut(byte A,byte V)
{
	sh_doout(A,V);
}



byte DoIn(byte A)
{       byte B;
        if(sh_doin(A,&B)) return B;
        return 0;
}



void Patch (reg *R)
{
}
