/***************************************************************************

  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 <conio.h>
#include "Z80.h"
#include "machine.h"
#include "vidhrdw.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 720000        /* 0.72 MHz, is this right? */
#define FRAMES_PER_SECOND 60



unsigned char RAM[0x8000];             /* 32k of RAM */



/* default dip switches settings */
int dsw1 = ~(
        DSW1_VBLANK);



/***************************************************************************

  Initialize the emulated machine (load the roms, initialize the various
  subsystems...). Returns 0 if successful.

***************************************************************************/
int init_machine(const char *gamename)
{
	int i;

	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;
        }

	return 0;
}



/***************************************************************************

  Run the emulation. Start the various subsystems and the CPU emulation.
  Returns non zero in case of error.

***************************************************************************/
int run_machine(void)
{
        if (vh_start() == 0)     /* start the video hardware */ 
	{
		reg StartRegs;


                IPeriod = CPU_SPEED / FRAMES_PER_SECOND;      /*  Number of T-states per interrupt (should be 12000) */
		ResetZ80(&StartRegs);
                Z80(&StartRegs);               /*  start the CPU emulation */

		vh_stop();

		return 0;
	}
        else printf("Unable to setup display\n");
        return 1;
}



/***************************************************************************

  Perform a memory read. This function is called by the CPU emulation.

***************************************************************************/
int vblank;

byte M_RDMEM (dword A)
{

	/* handle input ports (see memmap.h for details) */
        if (A >= DSW1_PORT_START && A <= DSW1_PORT_END) {
                byte res = 0x00;

                if (vblank) {
                        vh_screenrefresh();
                        
                        vblank = 0;
                        res |= DSW1_VBLANK;
                }
                return res;
        }
        else if (A >= IN1_PORT_START && A <= IN1_PORT_END) {
                byte res = 0xff;

                if (osd_key_pressed(OSD_KEY_1)) res &= ~IN1_START1;
                if (osd_key_pressed(OSD_KEY_2)) res &= ~IN1_START2;
                if (osd_key_pressed(OSD_KEY_3)) res &= ~IN1_COIN;
                if (osd_key_pressed(OSD_KEY_RIGHT)) res &= ~IN1_RIGHT;
                if (osd_key_pressed(OSD_KEY_LEFT)) res &= ~IN1_LEFT;
                if (osd_key_pressed(OSD_KEY_CONTROL)) res &= ~IN1_FIRE;
                if (osd_key_pressed(OSD_KEY_ALT)) res &= ~IN1_BARRIER;
                return res;
        }
        else return RAM[A];
}



/***************************************************************************

  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 RAM[A] = V;
}



/***************************************************************************

  Interrupt handler. This function is called at regular intervals
  (determined by IPeriod) by the CPU emulation.

  Phoenix doesn't have VBlank interrupts; the software polls port IN1 to
  know when a vblank is happening. Therefore we set a flag here, to let
  M_RDMEM() know when it has to report a vblank.

***************************************************************************/
int Interrupt(void)
{
	static uclock_t prev;
	uclock_t curr;

	/* first of all, refresh the screen */
        /* This is done for now when M_RDMEM() discovers a vblank */
        /*vh_screenrefresh();*/

	/* if the user pressed ESC, stop the emulation */
	if (osd_key_pressed(OSD_KEY_ESC)) CPURunning = 0;

	if (osd_key_pressed(OSD_KEY_P)) /* pause the game */
	{
		while (osd_key_pressed(OSD_KEY_P));
		while (!osd_key_pressed(OSD_KEY_P));
		while (osd_key_pressed(OSD_KEY_P));
	}

	/* now wait until it's time to trigger the interrupt */
	do
	{
		curr = uclock();
	} while ((curr - prev) < UCLOCKS_PER_SEC/FRAMES_PER_SECOND);

	prev = curr;

	/* let M_RDMEM() know that it is time to report a vblank */
	vblank = 1;

	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)
{
}



byte DoIn(byte A)
{
	return 0;
}



void Patch (reg *R)
{
}
