// license:BSD-3-Clause // copyright-holders:Chris Moore /*************************************************************************** GAME PLAN driver driver by Chris Moore ****************************************************************************/ #include "emu.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) /************************************* * * Timer handling * *************************************/ void gameplan_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (id) { case TIMER_CLEAR_SCREEN_DONE: clear_screen_done_callback(ptr, param); break; case TIMER_VIA_IRQ_DELAYED: via_irq_delayed(ptr, param); break; default: throw emu_fatalerror("Unknown id in gameplan_state::device_timer"); } } /************************************* * * Palette handling * *************************************/ void gameplan_state::gameplan_get_pens( pen_t *pens ) { offs_t i; for (i = 0; i < GAMEPLAN_NUM_PENS; i++) pens[i] = rgb_t(pal1bit(i >> 0), pal1bit(i >> 1), pal1bit(i >> 2)); } /* RGBI palette. Is it correct, or does it use the standard RGB? */ void gameplan_state::leprechn_get_pens( pen_t *pens ) { offs_t i; for (i = 0; i < LEPRECHN_NUM_PENS; i++) { uint8_t bk = (i & 8) ? 0x40 : 0x00; uint8_t r = (i & 1) ? 0xff : bk; uint8_t g = (i & 2) ? 0xff : bk; uint8_t b = (i & 4) ? 0xff : bk; pens[i] = rgb_t(r, g, b); } } /************************************* * * Update * *************************************/ uint32_t gameplan_state::screen_update_gameplan(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { pen_t pens[GAMEPLAN_NUM_PENS]; offs_t offs; gameplan_get_pens(pens); for (offs = 0; offs < m_videoram_size; offs++) { uint8_t y = offs >> 8; uint8_t x = offs & 0xff; bitmap.pix32(y, x) = pens[m_videoram[offs] & 0x07]; } return 0; } uint32_t gameplan_state::screen_update_leprechn(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { pen_t pens[LEPRECHN_NUM_PENS]; offs_t offs; leprechn_get_pens(pens); for (offs = 0; offs < m_videoram_size; offs++) { uint8_t y = offs >> 8; uint8_t x = offs & 0xff; bitmap.pix32(y, x) = pens[m_videoram[offs] & (LEPRECHN_NUM_PENS-1)]; } return 0; } /************************************* * * VIA * *************************************/ WRITE8_MEMBER(gameplan_state::video_data_w) { m_video_data = data; } WRITE8_MEMBER(gameplan_state::gameplan_video_command_w) { m_video_command = data & 0x07; } WRITE8_MEMBER(gameplan_state::leprechn_video_command_w) { m_video_command = (data >> 3) & 0x07; } READ8_MEMBER(gameplan_state::leprechn_videoram_r) { return m_videoram[m_video_y * (HBSTART - HBEND) + m_video_x]; } TIMER_CALLBACK_MEMBER(gameplan_state::clear_screen_done_callback) { /* indicate that the we are done clearing the screen */ m_via_0->write_ca1(0); } WRITE_LINE_MEMBER(gameplan_state::video_command_trigger_w) { if (state == 0) { switch (m_video_command) { /* draw pixel */ case 0: /* auto-adjust X? */ if (m_video_data & 0x10) { if (m_video_data & 0x40) m_video_x = m_video_x - 1; else m_video_x = m_video_x + 1; } /* auto-adjust Y? */ if (m_video_data & 0x20) { if (m_video_data & 0x80) m_video_y = m_video_y - 1; else m_video_y = m_video_y + 1; } m_videoram[m_video_y * (HBSTART - HBEND) + m_video_x] = m_video_data & 0x0f; break; /* load X register */ case 1: m_video_x = m_video_data; break; /* load Y register */ case 2: m_video_y = m_video_data; break; /* clear screen */ case 3: /* indicate that the we are busy */ { m_via_0->write_ca1(1); } memset(m_videoram.get(), m_video_data & 0x0f, 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 */ synchronize(TIMER_CLEAR_SCREEN_DONE); break; } } } TIMER_CALLBACK_MEMBER(gameplan_state::via_irq_delayed) { m_maincpu->set_input_line(0, param); } WRITE_LINE_MEMBER(gameplan_state::via_irq) { /* 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. */ timer_set(attotime::from_usec(50), TIMER_VIA_IRQ_DELAYED, state); } /************************************* * * Start * *************************************/ void gameplan_state::video_start() { m_videoram_size = (HBSTART - HBEND) * (VBSTART - VBEND); m_videoram = std::make_unique(m_videoram_size); /* register for save states */ save_pointer(NAME(m_videoram), m_videoram_size); } /************************************* * * Machine drivers * *************************************/ void gameplan_state::gameplan_video(machine_config &config) { SCREEN(config, m_screen, SCREEN_TYPE_RASTER); m_screen->set_raw(GAMEPLAN_PIXEL_CLOCK, HTOTAL, HBEND, HBSTART, VTOTAL, VBEND, VBSTART); m_screen->set_screen_update(FUNC(gameplan_state::screen_update_gameplan)); m_screen->screen_vblank().set(m_via_0, FUNC(via6522_device::write_ca1)).invert(); // !VBLANK is connected to CA1 } void gameplan_state::leprechn_video(machine_config &config) { m_screen->set_screen_update(FUNC(gameplan_state::screen_update_leprechn)); } void gameplan_state::trvquest_video(machine_config &config) { gameplan_video(config); m_screen->set_screen_update(FUNC(gameplan_state::screen_update_gameplan)); m_screen->screen_vblank().set(m_via_2, FUNC(via6522_device::write_ca1)); }