/***************************************************************************
GAME PLAN driver
driver by Chris Moore
****************************************************************************/
#include "emu.h"
#include "machine/6522via.h"
#include "includes/gameplan.h"
/*************************************
*
* Constants
*
*************************************/
#define HTOTAL (0x160)
#define HBEND (0x000)
#define HBSTART (0x100)
#define VTOTAL (0x118)
#define VBEND (0x000)
#define VBSTART (0x100)
#define GAMEPLAN_NUM_PENS (0x08)
#define LEPRECHN_NUM_PENS (0x10)
/*************************************
*
* Palette handling
*
*************************************/
static void gameplan_get_pens( pen_t *pens )
{
offs_t i;
for (i = 0; i < GAMEPLAN_NUM_PENS; i++)
pens[i] = MAKE_RGB(pal1bit(i >> 0), pal1bit(i >> 1), pal1bit(i >> 2));
}
/* RGBI palette. Is it correct, or does it use the standard RGB? */
static void leprechn_get_pens( pen_t *pens )
{
offs_t i;
for (i = 0; i < LEPRECHN_NUM_PENS; i++)
{
UINT8 bk = (i & 8) ? 0x40 : 0x00;
UINT8 r = (i & 1) ? 0xff : bk;
UINT8 g = (i & 2) ? 0xff : bk;
UINT8 b = (i & 4) ? 0xff : bk;
pens[i] = MAKE_RGB(r, g, b);
}
}
/*************************************
*
* Update
*
*************************************/
static SCREEN_UPDATE( gameplan )
{
gameplan_state *state = screen->machine().driver_data<gameplan_state>();
pen_t pens[GAMEPLAN_NUM_PENS];
offs_t offs;
gameplan_get_pens(pens);
for (offs = 0; offs < state->m_videoram_size; offs++)
{
UINT8 y = offs >> 8;
UINT8 x = offs & 0xff;
*BITMAP_ADDR32(bitmap, y, x) = pens[state->m_videoram[offs] & 0x07];
}
return 0;
}
static SCREEN_UPDATE( leprechn )
{
gameplan_state *state = screen->machine().driver_data<gameplan_state>();
pen_t pens[LEPRECHN_NUM_PENS];
offs_t offs;
leprechn_get_pens(pens);
for (offs = 0; offs < state->m_videoram_size; offs++)
{
UINT8 y = offs >> 8;
UINT8 x = offs & 0xff;
*BITMAP_ADDR32(bitmap, y, x) = pens[state->m_videoram[offs]];
}
return 0;
}
/*************************************
*
* VIA
*
*************************************/
static WRITE8_DEVICE_HANDLER( video_data_w )
{
gameplan_state *state = device->machine().driver_data<gameplan_state>();
state->m_video_data = data;
}
static WRITE8_DEVICE_HANDLER( gameplan_video_command_w )
{
gameplan_state *state = device->machine().driver_data<gameplan_state>();
state->m_video_command = data & 0x07;
}
static WRITE8_DEVICE_HANDLER( leprechn_video_command_w )
{
gameplan_state *state = device->machine().driver_data<gameplan_state>();
state->m_video_command = (data >> 3) & 0x07;
}
static TIMER_CALLBACK( clear_screen_done_callback )
{
gameplan_state *state = machine.driver_data<gameplan_state>();
/* indicate that the we are done clearing the screen */
state->m_via_0->write_ca1(0);
}
static WRITE_LINE_DEVICE_HANDLER( video_command_trigger_w )
{
gameplan_state *driver_state = device->machine().driver_data<gameplan_state>();
if (state == 0)
{
switch (driver_state->m_video_command)
{
/* draw pixel */
case 0:
/* auto-adjust X? */
if (driver_state->m_video_data & 0x10)
{
if (driver_state->m_video_data & 0x40)
driver_state->m_video_x = driver_state->m_video_x - 1;
else
driver_state->m_video_x = driver_state->m_video_x + 1;
}
/* auto-adjust Y? */
if (driver_state->m_video_data & 0x20)
{
if (driver_state->m_video_data & 0x80)
driver_state->m_video_y = driver_state->m_video_y - 1;
else
driver_state->m_video_y = driver_state->m_video_y + 1;
}
driver_state->m_videoram[driver_state->m_video_y * (HBSTART - HBEND) + driver_state->m_video_x] = driver_state->m_video_data & 0x0f;
break;
/* load X register */
case 1:
driver_state->m_video_x = driver_state->m_video_data;
break;
/* load Y register */
case 2:
driver_state->m_video_y = driver_state->m_video_data;
break;
/* clear screen */
case 3:
/* indicate that the we are busy */
{
driver_state->m_via_0->write_ca1(1);
}
memset(driver_state->m_videoram, driver_state->m_video_data & 0x0f, driver_state->m_videoram_size);
/* set a timer for an arbitrarily short period.
The real time it takes to clear to screen is not
important to the software */
device->machine().scheduler().synchronize(FUNC(clear_screen_done_callback));
break;
}
}
}
static TIMER_CALLBACK( via_irq_delayed )
{
gameplan_state *state = machine.driver_data<gameplan_state>();
device_set_input_line(state->m_maincpu, 0, param);
}
static void via_irq(device_t *device, int state)
{
/* Kaos sits in a tight loop polling the VIA irq flags register, but that register is
cleared by the irq handler. Therefore, I wait a bit before triggering the irq to
leave time for the program to see the flag change. */
device->machine().scheduler().timer_set(attotime::from_usec(50), FUNC(via_irq_delayed), state);
}
static READ8_DEVICE_HANDLER( vblank_r )
{
/* this is needed for trivia quest */
return 0x20;
}
const via6522_interface gameplan_via_0_interface =
{
DEVCB_NULL, DEVCB_HANDLER(vblank_r), /*inputs : A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, /*inputs : CA/B1,CA/B2 */
DEVCB_HANDLER(video_data_w), DEVCB_HANDLER(gameplan_video_command_w), /*outputs: A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_LINE(video_command_trigger_w), DEVCB_NULL, /*outputs: CA/B1,CA/B2 */
DEVCB_LINE(via_irq) /*irq */
};
const via6522_interface leprechn_via_0_interface =
{
DEVCB_NULL, DEVCB_HANDLER(vblank_r), /*inputs : A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, /*inputs : CA/B1,CA/B2 */
DEVCB_HANDLER(video_data_w), DEVCB_HANDLER(leprechn_video_command_w), /*outputs: A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_LINE(video_command_trigger_w), DEVCB_NULL, /*outputs: CA/B1,CA/B2 */
DEVCB_LINE(via_irq) /*irq */
};
const via6522_interface trvquest_via_0_interface =
{
DEVCB_NULL, DEVCB_HANDLER(vblank_r), /*inputs : A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, /*inputs : CA/B1,CA/B2 */
DEVCB_HANDLER(video_data_w), DEVCB_HANDLER(gameplan_video_command_w), /*outputs: A/B */
DEVCB_NULL, DEVCB_NULL, DEVCB_LINE(video_command_trigger_w), DEVCB_NULL, /*outputs: CA/B1,CA/B2 */
DEVCB_NULL /*irq */
};
static TIMER_CALLBACK( via_0_ca1_timer_callback )
{
gameplan_state *state = machine.driver_data<gameplan_state>();
/* !VBLANK is connected to CA1 */
state->m_via_0->write_ca1(param);
if (param)
state->m_via_0_ca1_timer->adjust(machine.primary_screen->time_until_pos(VBSTART));
else
state->m_via_0_ca1_timer->adjust(machine.primary_screen->time_until_pos(VBEND), 1);
}
/*************************************
*
* Start
*
*************************************/
static VIDEO_START( common )
{
gameplan_state *state = machine.driver_data<gameplan_state>();
state->m_videoram_size = (HBSTART - HBEND) * (VBSTART - VBEND);
state->m_videoram = auto_alloc_array(machine, UINT8, state->m_videoram_size);
state->m_via_0_ca1_timer = machine.scheduler().timer_alloc(FUNC(via_0_ca1_timer_callback));
/* register for save states */
state->save_pointer(NAME(state->m_videoram), state->m_videoram_size);
}
static VIDEO_START( gameplan )
{
VIDEO_START_CALL(common);
}
static VIDEO_START( leprechn )
{
VIDEO_START_CALL(common);
}
static VIDEO_START( trvquest )
{
VIDEO_START_CALL(common);
}
/*************************************
*
* Reset
*
*************************************/
static VIDEO_RESET( gameplan )
{
gameplan_state *state = machine.driver_data<gameplan_state>();
state->m_via_0_ca1_timer->adjust(machine.primary_screen->time_until_pos(VBSTART));
}
/*************************************
*
* Machine drivers
*
*************************************/
MACHINE_CONFIG_FRAGMENT( gameplan_video )
MCFG_VIDEO_START(gameplan)
MCFG_VIDEO_RESET(gameplan)
MCFG_VIDEO_START(gameplan)
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
MCFG_SCREEN_RAW_PARAMS(GAMEPLAN_PIXEL_CLOCK, HTOTAL, HBEND, HBSTART, VTOTAL, VBEND, VBSTART)
MCFG_SCREEN_UPDATE(gameplan)
MACHINE_CONFIG_END
MACHINE_CONFIG_FRAGMENT( leprechn_video )
MCFG_VIDEO_START(leprechn)
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_UPDATE(leprechn)
MACHINE_CONFIG_END
MACHINE_CONFIG_DERIVED( trvquest_video, gameplan_video )
MCFG_VIDEO_START(trvquest)
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_UPDATE(gameplan)
MACHINE_CONFIG_END