/***************************************************************************

    Midway 8080-based black and white hardware

****************************************************************************/

#include "driver.h"
#include "invaders.h"


/*************************************
 *
 *  Interrupt generation
 *
 *************************************/

static emu_timer *interrupt_timer;


static UINT8 vpos_to_vysnc_chain_counter(int vpos)
{
	/* convert from a vertical position to the actual values on the vertical sync counters */
	UINT8 counter;
	int vblank = (vpos >= INVADERS_VBSTART);

	if (vblank)
		counter = vpos - INVADERS_VBSTART + INVADERS_VCOUNTER_START_VBLANK;
	else
		counter = vpos + INVADERS_VCOUNTER_START_NO_VBLANK;

	return counter;
}


static int vysnc_chain_counter_to_vpos(UINT8 counter, int vblank)
{
	/* convert from the vertical sync counters to an actual vertical position */
	int vpos;

	if (vblank)
		vpos = counter - INVADERS_VCOUNTER_START_VBLANK + INVADERS_VBSTART;
	else
		vpos = counter - INVADERS_VCOUNTER_START_NO_VBLANK;

	return vpos;
}


static TIMER_CALLBACK( invaders_interrupt_callback )
{
	UINT8 next_counter;
	int next_vpos;
	int next_vblank;

	/* compute vector and set the interrupt line */
	int vpos = video_screen_get_vpos(machine->primary_screen);
	UINT8 counter = vpos_to_vysnc_chain_counter(vpos);
	UINT8 vector = 0xc7 | ((counter & 0x40) >> 2) | ((~counter & 0x40) >> 3);
	cpunum_set_input_line_and_vector(machine, 0, 0, HOLD_LINE, vector);

	/* set up for next interrupt */
	if (counter == INVADERS_INT_TRIGGER_COUNT_1)
	{
		next_counter = INVADERS_INT_TRIGGER_COUNT_2;
		next_vblank = INVADERS_INT_TRIGGER_VBLANK_2;
	}
	else
	{
		next_counter = INVADERS_INT_TRIGGER_COUNT_1;
		next_vblank = INVADERS_INT_TRIGGER_VBLANK_1;
	}

	next_vpos = vysnc_chain_counter_to_vpos(next_counter, next_vblank);
	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, next_vpos, 0), 0);
}


static void invaders_create_interrupt_timer(void)
{
	interrupt_timer = timer_alloc(invaders_interrupt_callback, NULL);
}


static void invaders_start_interrupt_timer(running_machine *machine)
{
	int vpos = vysnc_chain_counter_to_vpos(INVADERS_INT_TRIGGER_COUNT_1, INVADERS_INT_TRIGGER_VBLANK_1);
	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, vpos, 0), 0);
}


/********************************************* BEAMINV ********************************************/
/* the interrupt scanlines are a guess */

#define INTERRUPTS_PER_FRAME	(2)

static const int beaminv_interrupt_lines[INTERRUPTS_PER_FRAME] = { 0x00, 0x80 };


static TIMER_CALLBACK( interrupt_callback )
{
	int interrupt_number = param;
	int next_interrupt_number;
	int next_vpos;

	cpunum_set_input_line(machine, 0, 0, HOLD_LINE);

	/* set up for next interrupt */
	next_interrupt_number = (interrupt_number + 1) % INTERRUPTS_PER_FRAME;
	next_vpos = beaminv_interrupt_lines[next_interrupt_number];

	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, next_vpos, 0), next_interrupt_number);
}


static void beaminv_create_interrupt_timer(void)
{
	interrupt_timer = timer_alloc(interrupt_callback, NULL);
}


static void beaminv_start_interrupt_timer(running_machine *machine)
{
	int vpos = beaminv_interrupt_lines[0];
	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, vpos, 0), 0);
}


/*************************************
 *
 *  Machine setup
 *
 *************************************/

MACHINE_START( invaders_common )
{
	invaders_create_interrupt_timer();
}

MACHINE_START( beaminv )
{
	beaminv_create_interrupt_timer();
}


/*************************************
 *
 *  Machine reset
 *
 *************************************/

MACHINE_RESET( invaders_common )
{
	invaders_start_interrupt_timer(machine);
}

MACHINE_RESET( beaminv )
{
	beaminv_start_interrupt_timer(machine);
}
