// license:BSD-3-Clause // copyright-holders:Manuel Abadia, Ernesto Corvi, Nicola Salmoria /*************************************************************************** gaplus.c Functions to emulate the video hardware of the machine. ***************************************************************************/ #include "emu.h" #include "includes/gaplus.h" /*************************************************************************** Convert the color PROMs. The palette PROMs are connected to the RGB output this way: bit 3 -- 220 ohm resistor -- RED/GREEN/BLUE -- 470 ohm resistor -- RED/GREEN/BLUE -- 1 kohm resistor -- RED/GREEN/BLUE bit 0 -- 2.2kohm resistor -- RED/GREEN/BLUE ***************************************************************************/ void gaplus_base_state::gaplus_palette(palette_device &palette) const { const uint8_t *color_prom = m_proms_region->base(); for (int i = 0; i < 256; i++) { int bit0, bit1, bit2, bit3; // red component bit0 = BIT(color_prom[i], 0); bit1 = BIT(color_prom[i], 1); bit2 = BIT(color_prom[i], 2); bit3 = BIT(color_prom[i], 3); int const r = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3; // green component bit0 = BIT(color_prom[i + 0x100], 0); bit1 = BIT(color_prom[i + 0x100], 1); bit2 = BIT(color_prom[i + 0x100], 2); bit3 = BIT(color_prom[i + 0x100], 3); int const g = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3; // blue component bit0 = BIT(color_prom[i + 0x200], 0); bit1 = BIT(color_prom[i + 0x200], 1); bit2 = BIT(color_prom[i + 0x200], 2); bit3 = BIT(color_prom[i + 0x200], 3); int const b = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3; palette.set_indirect_color(i, rgb_t(r, g, b)); } color_prom += 0x300; // color_prom now points to the beginning of the lookup table // characters use colors 0xf0-0xff for (int i = 0; i < m_gfxdecode->gfx(0)->colors() * m_gfxdecode->gfx(0)->granularity(); i++) palette.set_pen_indirect(m_gfxdecode->gfx(0)->colorbase() + i, 0xf0 + (*color_prom++ & 0x0f)); /* sprites */ for (int i = 0; i < m_gfxdecode->gfx(1)->colors() * m_gfxdecode->gfx(1)->granularity(); i++) { palette.set_pen_indirect(m_gfxdecode->gfx(1)->colorbase() + i, (color_prom[0] & 0x0f) + ((color_prom[0x200] & 0x0f) << 4)); color_prom++; } } /*************************************************************************** Callbacks for the TileMap code ***************************************************************************/ /* convert from 32x32 to 36x28 */ TILEMAP_MAPPER_MEMBER(gaplus_base_state::tilemap_scan) { row += 2; col -= 2; if (col & 0x20) return row + ((col & 0x1f) << 5); return col + (row << 5); } TILE_GET_INFO_MEMBER(gaplus_base_state::get_tile_info) { const uint8_t attr = m_videoram[tile_index + 0x400]; tileinfo.category = (attr & 0x40) >> 6; tileinfo.group = attr & 0x3f; SET_TILE_INFO_MEMBER(0, m_videoram[tile_index] + ((attr & 0x80) << 1), attr & 0x3f, 0); } /*************************************************************************** Starfield information There's 3 sets of stars planes at different speeds. a000 ---> (bit 0 = 1) enable starfield. (bit 0 = 0) disable starfield. a001 ---> starfield plane 0 control a002 ---> starfield plane 1 control a003 ---> starfield plane 2 control ***************************************************************************/ /* starfield speed constants (bigger = faster) */ #define SPEED_1 0.5f #define SPEED_2 1.0f #define SPEED_3 2.0f void gaplus_base_state::starfield_init() { int generator = 0; int set = 0; const int width = m_screen->width(); const int height = m_screen->height(); m_total_stars = 0; /* precalculate the star background */ /* this comes from the Galaxian hardware, Gaplus is probably different */ for (int y = 0; y < height; y++) { for (int x = width * 2 - 1; x >= 0; x--) { generator <<= 1; const int bit1 = (~generator >> 17) & 1; const int bit2 = (generator >> 5) & 1; if (bit1 ^ bit2) generator |= 1; if (BIT(~generator, 16) && (generator & 0xff) == 0xff) { const int color = ~(generator >> 8) & 0x3f; if (color && m_total_stars < MAX_STARS) { m_stars[m_total_stars].x = x; m_stars[m_total_stars].y = y; m_stars[m_total_stars].col = color; m_stars[m_total_stars].set = set++; if (set == 3) set = 0; m_total_stars++; } } } } } /*************************************************************************** Start the video hardware emulation. ***************************************************************************/ void gaplus_base_state::video_start() { m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(gaplus_state::get_tile_info)), tilemap_mapper_delegate(*this, FUNC(gaplus_state::tilemap_scan)), 8,8,36,28); m_bg_tilemap->configure_groups(*m_gfxdecode->gfx(0), 0xff); starfield_init(); save_item(NAME(m_starfield_control)); for (int i = 0; i < MAX_STARS; i++) { save_item(NAME(m_stars[i].x), i); save_item(NAME(m_stars[i].y), i); // col and set aren't changed after init } } /*************************************************************************** Memory handlers ***************************************************************************/ WRITE8_MEMBER(gaplus_base_state::videoram_w) { m_videoram[offset] = data; m_bg_tilemap->mark_tile_dirty(offset & 0x3ff); } WRITE8_MEMBER(gaplus_base_state::starfield_control_w) { m_starfield_control[offset & 3] = data; } /*************************************************************************** Display Refresh ***************************************************************************/ void gaplus_base_state::starfield_render(bitmap_ind16 &bitmap) { /* check if we're running */ if ((m_starfield_control[0] & 1) == 0) return; const int width = m_screen->width(); const int height = m_screen->height(); /* draw the starfields */ for (int i = 0; i < m_total_stars; i++) { int x = m_stars[i].x; int y = m_stars[i].y; if (x >= 0 && x < width && y >= 0 && y < height) { bitmap.pix16(y, x) = m_stars[i].col; } } } void gaplus_base_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect ) const { uint8_t *spriteram = m_spriteram + 0x780; uint8_t *spriteram_2 = spriteram + 0x800; uint8_t *spriteram_3 = spriteram_2 + 0x800; for (int offs = 0;offs < 0x80;offs += 2) { /* is it on? */ if ((spriteram_3[offs+1] & 2) == 0) { static const int gfx_offs[2][2] = { { 0, 1 }, { 2, 3 } }; const int sprite = spriteram[offs] | ((spriteram_3[offs] & 0x40) << 2); const int color = spriteram[offs+1] & 0x3f; const int sx = spriteram_2[offs+1] + 0x100 * (spriteram_3[offs+1] & 1) - 71; int sy = 256 - spriteram_2[offs] - 8; int flipx = BIT(spriteram_3[offs], 0); int flipy = BIT(spriteram_3[offs], 1); const int sizex = BIT(spriteram_3[offs], 3); const int sizey = BIT(spriteram_3[offs], 5); const int duplicate = spriteram_3[offs] & 0x80; if (flip_screen()) { flipx ^= 1; flipy ^= 1; } sy -= 16 * sizey; sy = (sy & 0xff) - 32; // fix wraparound for (int y = 0;y <= sizey; y++) { for (int x = 0; x <= sizex; x++) { m_gfxdecode->gfx(1)->transmask(bitmap, cliprect, sprite + (duplicate ? 0 : (gfx_offs[y ^ (sizey * flipy)][x ^ (sizex * flipx)])), color, flipx,flipy, sx + 16 * x, sy + 16 * y, m_palette->transpen_mask(*m_gfxdecode->gfx(1), color, 0xff)); } } } } } uint32_t gaplus_base_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { /* flip screen control is embedded in RAM */ flip_screen_set(m_spriteram[0x1f7f - 0x800] & 1); bitmap.fill(0, cliprect); starfield_render(bitmap); /* draw the low priority characters */ m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0); draw_sprites(bitmap, cliprect); /* draw the high priority characters */ /* (I don't know if this feature is used by Gaplus, but it's shown in the schematics) */ m_bg_tilemap->draw(screen, bitmap, cliprect, 1, 0); return 0; } WRITE_LINE_MEMBER(gaplus_base_state::screen_vblank)/* update starfields */ { // falling edge if (!state) { struct star *stars = m_stars; int i; int width = m_screen->width(); int height = m_screen->height(); /* check if we're running */ if ( ( m_starfield_control[0] & 1 ) == 0 ) return; /* update the starfields */ for ( i = 0; i < m_total_stars; i++ ) { switch( m_starfield_control[stars[i].set + 1] ) { case 0x87: /* stand still */ break; case 0x86: /* scroll down (speed 1) */ stars[i].x += SPEED_1; break; case 0x85: /* scroll down (speed 2) */ stars[i].x += SPEED_2; break; case 0x06: /* scroll down (speed 3) */ stars[i].x += SPEED_3; break; case 0x80: /* scroll up (speed 1) */ stars[i].x -= SPEED_1; break; case 0x82: /* scroll up (speed 2) */ stars[i].x -= SPEED_2; break; case 0x81: /* scroll up (speed 3) */ stars[i].x -= SPEED_3; break; case 0x9f: /* scroll left (speed 2) */ stars[i].y += SPEED_2; break; case 0xaf: /* scroll left (speed 1) */ stars[i].y += SPEED_1; break; } /* wrap */ if ( stars[i].x < 0 ) stars[i].x = ( float )( width*2 ) + stars[i].x; if ( stars[i].x >= ( float )( width*2 ) ) stars[i].x -= ( float )( width*2 ); if ( stars[i].y < 0 ) stars[i].y = ( float )( height ) + stars[i].y; if ( stars[i].y >= ( float )( height ) ) stars[i].y -= ( float )( height ); } } }