// license:BSD-3-Clause
// copyright-holders:Luca Elia
/***************************************************************************
orca40c.cpp
ORCA OVG-40c GFX board used by at least:
* all games in zodiack.cpp
* dogfightp in vastar.cpp
***************************************************************************/
#include "emu.h"
#include "video/orca40c.h"
#include "emupal.h"
#include "screen.h"
#define PIXEL_CLOCK (XTAL(18'432'000)/3)
#define HTOTAL (396)
#define HBEND (0)
#define HBSTART (256)
#define VTOTAL (256)
#define VBEND (16)
#define VBSTART (240)
DEFINE_DEVICE_TYPE(ORCA_OVG_40C, orca_ovg_40c_device, "orca_ovg_40c", "Orca OVG 40c video PCB")
orca_ovg_40c_device::orca_ovg_40c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ORCA_OVG_40C, tag, owner, clock),
device_gfx_interface(mconfig, *this, nullptr, "palette"),
device_video_interface(mconfig, *this),
m_videoram(*this, "videoram"),
m_videoram_2(*this,"videoram_2"),
m_attributeram(*this, "attributeram"),
m_spriteram(*this, "spriteram"),
m_bulletsram(*this, "bulletsram"),
m_percuss_hardware(false),
m_flip_screen(false)
{
}
/*************************************
*
* Graphics definitions
*
*************************************/
static const gfx_layout charlayout =
{
8,8, /* 8*8 chars */
256, /* 256 characters */
1, /* 1 bit per pixel */
{ 0 } , /* single bitplane */
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
8*8 /* every char takes 8 consecutive bytes */
};
static const gfx_layout charlayout_2 =
{
8,8, /* 8*8 chars */
256, /* 256 characters */
2, /* 2 bits per pixel */
{ 0, 512*8*8 }, /* The bitplanes are separate */
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
8*8 /* every char takes 8 consecutive bytes */
};
static const gfx_layout spritelayout =
{
16,16, /* 16*16 sprites */
64, /* 64 sprites */
2, /* 2 bits per pixel */
{ 0, 128*32*8 }, /* the two bitplanes are separated */
{ 0, 1, 2, 3, 4, 5, 6, 7,
8*8+0, 8*8+1, 8*8+2, 8*8+3, 8*8+4, 8*8+5, 8*8+6, 8*8+7 },
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,
16*8, 17*8, 18*8, 19*8, 20*8, 21*8, 22*8, 23*8 },
32*8 /* every sprite takes 32 consecutive bytes */
};
static const gfx_layout bulletlayout =
{
/* there is no gfx ROM for this one, it is generated by the hardware */
7,1, /* it's just 1 pixel, but we use 7*1 to position it correctly */
1, /* just one */
1, /* 1 bit per pixel */
{ 10*8*8 }, /* point to letter "A" */
{ 3, 7, 7, 7, 7, 7, 7 }, /* I "know" that this bit of the */
{ 1*8 }, /* graphics ROMs is 1 */
0 /* no use */
};
GFXDECODE_MEMBER(orca_ovg_40c_device::gfxinfo)
GFXDECODE_DEVICE( DEVICE_SELF, 0x0000, charlayout, 8*4 , 8 )
GFXDECODE_DEVICE( DEVICE_SELF, 0x0800, spritelayout, 0 , 8 )
GFXDECODE_DEVICE( DEVICE_SELF, 0x0000, bulletlayout, 8*4+8*2, 1 )
GFXDECODE_DEVICE( DEVICE_SELF, 0x1000, charlayout_2, 0 , 8 )
GFXDECODE_END
//-------------------------------------------------
// device_start: Start up the device
//-------------------------------------------------
void orca_ovg_40c_device::device_start()
{
decode_gfx(gfxinfo);
m_bg_tilemap = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(orca_ovg_40c_device::get_bg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
m_fg_tilemap = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(orca_ovg_40c_device::get_fg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
m_fg_tilemap->set_transparent_pen(0);
m_fg_tilemap->set_scroll_cols(32);
save_item(NAME(m_flip_screen));
}
WRITE8_MEMBER(orca_ovg_40c_device::videoram_w)
{
m_videoram[offset] = data;
m_fg_tilemap->mark_tile_dirty(offset);
}
WRITE8_MEMBER(orca_ovg_40c_device::videoram2_w)
{
m_videoram_2[offset] = data;
m_bg_tilemap->mark_tile_dirty(offset);
}
WRITE8_MEMBER(orca_ovg_40c_device::attributes_w)
{
if ((offset & 1) && m_attributeram[offset] != data)
{
int i;
for (i = offset / 2; i < m_videoram.bytes(); i += 32)
{
m_bg_tilemap->mark_tile_dirty(i);
m_fg_tilemap->mark_tile_dirty(i);
}
}
m_attributeram[offset] = data;
}
WRITE8_MEMBER(orca_ovg_40c_device::flipscreen_w)
{
m_flip_screen = ~data & 1;
m_bg_tilemap->set_flip(m_flip_screen ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
m_fg_tilemap->set_flip(m_flip_screen ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
}
void orca_ovg_40c_device::palette_init(palette_device &palette)
{
const uint8_t *color_prom = memregion("proms")->base();
/* create a lookup table for the palette */
for (int i = 0; i < 0x30; i++)
{
/* red component */
int bit0 = (color_prom[i] >> 0) & 0x01;
int bit1 = (color_prom[i] >> 1) & 0x01;
int bit2 = (color_prom[i] >> 2) & 0x01;
int r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
/* green component */
bit0 = (color_prom[i] >> 3) & 0x01;
bit1 = (color_prom[i] >> 4) & 0x01;
bit2 = (color_prom[i] >> 5) & 0x01;
int g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
/* blue component */
bit0 = 0;
bit1 = (color_prom[i] >> 6) & 0x01;
bit2 = (color_prom[i] >> 7) & 0x01;
int b = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
palette.set_indirect_color(i, rgb_t(r, g, b));
}
/* white for bullets */
palette.set_indirect_color(0x30, rgb_t::white());
for (int i = 0; i < 0x20; i++)
palette.set_pen_indirect(i, (i & 3) ? i : 0);
for (int i = 0; i < 0x10; i += 2)
{
palette.set_pen_indirect(0x20 + i, 32 + (i / 2));
palette.set_pen_indirect(0x21 + i, 40 + (i / 2));
}
/* bullet */
palette.set_pen_indirect(0x30, 0);
palette.set_pen_indirect(0x31, 0x30);
}
TILE_GET_INFO_MEMBER(orca_ovg_40c_device::get_bg_tile_info)
{
int code = m_videoram_2[tile_index];
int color = (m_attributeram[(tile_index & 0x1f) << 1 | 1] >> 4) & 0x07;
SET_TILE_INFO_MEMBER(0, code, color, 0);
}
TILE_GET_INFO_MEMBER(orca_ovg_40c_device::get_fg_tile_info)
{
int code = m_videoram[tile_index];
int color = (m_attributeram[(tile_index & 0x1f) << 1 | 1] >> 0) & 0x07;
SET_TILE_INFO_MEMBER(3, code, color, 0);
}
void orca_ovg_40c_device::draw_bullets( bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
for (int offs = 0; offs < m_bulletsram.bytes(); offs += 4)
{
int sx = m_bulletsram[offs + 3] + 7;
int sy = m_bulletsram[offs + 1];
if (!(m_flip_screen && m_percuss_hardware))
sy = 255 - sy;
gfx(2)->transpen(
bitmap,
cliprect,
0, /* this is just a dot, generated by the hardware */
0,
0, 0,
sx, sy, 0);
}
}
void orca_ovg_40c_device::draw_sprites( bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
for (int offs = m_spriteram.bytes() - 4; offs >= 0; offs -= 4)
{
int sx = 240 - m_spriteram[offs + 3];
int sy = 240 - m_spriteram[offs];
int flipx = !(m_spriteram[offs + 1] & 0x40);
int flipy = m_spriteram[offs + 1] & 0x80;
int spritecode = m_spriteram[offs + 1] & 0x3f;
if (m_flip_screen && m_percuss_hardware)
{
sy = 240 - sy;
flipy = !flipy;
}
gfx(1)->transpen(bitmap,cliprect,
spritecode,
m_spriteram[offs + 2] & 0x07,
flipx, flipy,
sx, sy, 0);
}
}
uint32_t orca_ovg_40c_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
for (int i = 0; i < 32; i++)
m_fg_tilemap->set_scrolly(i, m_attributeram[i * 2]);
m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
m_fg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
draw_bullets(bitmap, cliprect);
draw_sprites(bitmap, cliprect);
return 0;
}
void orca_ovg_40c_device::device_config_complete()
{
if (!has_screen())
return;
if (!screen().refresh_attoseconds())
screen().set_raw(PIXEL_CLOCK, HTOTAL, HBEND, HBSTART, VTOTAL, VBEND, VBSTART);
if (!screen().has_screen_update())
screen().set_screen_update(*this, FUNC(orca_ovg_40c_device::screen_update));
}
void orca_ovg_40c_device::device_add_mconfig(machine_config &config)
{
PALETTE(config, "palette", FUNC(orca_ovg_40c_device::palette_init), 4*8+2*8+2*1, 48+1);
}