/*************************************************************************** video.c Functions to emulate the video hardware of the machine. ***************************************************************************/ #include "emu.h" #include "includes/galaga.h" #define MAX_STARS 252 #define STARS_COLOR_BASE (64*4+64*4) /* Galaga star line and pixel locations pulled directly from a clocked stepping of the 05 starfield. The chip was clocked on a test rig with hblank and vblank simulated, each X & Y location of a star being recorded along with it's color value. Because the starfield begins generating stars at the point in time it's enabled the exact horiz location of the stars on Galaga depends on the length of time of the POST for the original board. Two control bits determine which of two sets are displayed set 0 or 1 and simultaneously 2 or 3. There are 63 stars in each set, 126 displayed at any one time */ const struct star star_seed_tab[252]= { /* also shared by Bosconian */ /* star set 0 */ {0x0085,0x0006,0x35, 0x00}, {0x008F,0x0008,0x30, 0x00}, {0x00E5,0x001B,0x07, 0x00}, {0x0022,0x001C,0x31, 0x00}, {0x00E5,0x0025,0x1D, 0x00}, {0x0015,0x0026,0x29, 0x00}, {0x0080,0x002D,0x3B, 0x00}, {0x0097,0x002E,0x1C, 0x00}, {0x00BA,0x003B,0x05, 0x00}, {0x0036,0x003D,0x36, 0x00}, {0x0057,0x0044,0x09, 0x00}, {0x00CF,0x0044,0x3D, 0x00}, {0x0061,0x004E,0x27, 0x00}, {0x0087,0x0064,0x1A, 0x00}, {0x00D6,0x0064,0x17, 0x00}, {0x000B,0x006C,0x3C, 0x00}, {0x0006,0x006D,0x24, 0x00}, {0x0018,0x006E,0x3A, 0x00}, {0x00A9,0x0079,0x23, 0x00}, {0x008A,0x007B,0x11, 0x00}, {0x00D6,0x0080,0x0C, 0x00}, {0x0067,0x0082,0x3F, 0x00}, {0x0039,0x0083,0x38, 0x00}, {0x0072,0x0083,0x14, 0x00}, {0x00EC,0x0084,0x16, 0x00}, {0x008E,0x0085,0x10, 0x00}, {0x0020,0x0088,0x25, 0x00}, {0x0095,0x008A,0x0F, 0x00}, {0x000E,0x008D,0x00, 0x00}, {0x0006,0x0091,0x2E, 0x00}, {0x0007,0x0094,0x0D, 0x00}, {0x00AE,0x0097,0x0B, 0x00}, {0x0000,0x0098,0x2D, 0x00}, {0x0086,0x009B,0x01, 0x00}, {0x0058,0x00A1,0x34, 0x00}, {0x00FE,0x00A1,0x3E, 0x00}, {0x00A2,0x00A8,0x1F, 0x00}, {0x0041,0x00AA,0x0A, 0x00}, {0x003F,0x00AC,0x32, 0x00}, {0x00DE,0x00AC,0x03, 0x00}, {0x00D4,0x00B9,0x26, 0x00}, {0x006D,0x00BB,0x1B, 0x00}, {0x0062,0x00BD,0x39, 0x00}, {0x00C9,0x00BE,0x18, 0x00}, {0x006C,0x00C1,0x04, 0x00}, {0x0059,0x00C3,0x21, 0x00}, {0x0060,0x00CC,0x0E, 0x00}, {0x0091,0x00CC,0x12, 0x00}, {0x003F,0x00CF,0x06, 0x00}, {0x00F7,0x00CF,0x22, 0x00}, {0x0044,0x00D0,0x33, 0x00}, {0x0034,0x00D2,0x08, 0x00}, {0x00D3,0x00D9,0x20, 0x00}, {0x0071,0x00DD,0x37, 0x00}, {0x0073,0x00E1,0x2C, 0x00}, {0x00B9,0x00E3,0x2F, 0x00}, {0x00A9,0x00E4,0x13, 0x00}, {0x00D3,0x00E7,0x19, 0x00}, {0x0037,0x00ED,0x02, 0x00}, {0x00BD,0x00F4,0x15, 0x00}, {0x000F,0x00F6,0x28, 0x00}, {0x004F,0x00F7,0x2B, 0x00}, {0x00FB,0x00FF,0x2A, 0x00}, /* star set 1 */ {0x00FE,0x0004,0x3D, 0x01}, {0x00C4,0x0006,0x10, 0x01}, {0x001E,0x0007,0x2D, 0x01}, {0x0083,0x000B,0x1F, 0x01}, {0x002E,0x000D,0x3C, 0x01}, {0x001F,0x000E,0x00, 0x01}, {0x00D8,0x000E,0x2C, 0x01}, {0x0003,0x000F,0x17, 0x01}, {0x0095,0x0011,0x3F, 0x01}, {0x006A,0x0017,0x35, 0x01}, {0x00CC,0x0017,0x02, 0x01}, {0x0000,0x0018,0x32, 0x01}, {0x0092,0x001D,0x36, 0x01}, {0x00E3,0x0021,0x04, 0x01}, {0x002F,0x002D,0x37, 0x01}, {0x00F0,0x002F,0x0C, 0x01}, {0x009B,0x003E,0x06, 0x01}, {0x00A4,0x004C,0x07, 0x01}, {0x00EA,0x004D,0x13, 0x01}, {0x0084,0x004E,0x21, 0x01}, {0x0033,0x0052,0x0F, 0x01}, {0x0070,0x0053,0x0E, 0x01}, {0x0006,0x0059,0x08, 0x01}, {0x0081,0x0060,0x28, 0x01}, {0x0037,0x0061,0x29, 0x01}, {0x008F,0x0067,0x2F, 0x01}, {0x001B,0x006A,0x1D, 0x01}, {0x00BF,0x007C,0x12, 0x01}, {0x0051,0x007F,0x31, 0x01}, {0x0061,0x0086,0x25, 0x01}, {0x006A,0x008F,0x0D, 0x01}, {0x006A,0x0091,0x19, 0x01}, {0x0090,0x0092,0x05, 0x01}, {0x003B,0x0096,0x24, 0x01}, {0x008C,0x0097,0x0A, 0x01}, {0x0006,0x0099,0x03, 0x01}, {0x0038,0x0099,0x38, 0x01}, {0x00A8,0x0099,0x18, 0x01}, {0x0076,0x00A6,0x20, 0x01}, {0x00AD,0x00A6,0x1C, 0x01}, {0x00EC,0x00A6,0x1E, 0x01}, {0x0086,0x00AC,0x15, 0x01}, {0x0078,0x00AF,0x3E, 0x01}, {0x007B,0x00B3,0x09, 0x01}, {0x0027,0x00B8,0x39, 0x01}, {0x0088,0x00C2,0x23, 0x01}, {0x0044,0x00C3,0x3A, 0x01}, {0x00CF,0x00C5,0x34, 0x01}, {0x0035,0x00C9,0x30, 0x01}, {0x006E,0x00D1,0x3B, 0x01}, {0x00D6,0x00D7,0x16, 0x01}, {0x003A,0x00D9,0x2B, 0x01}, {0x00AB,0x00E0,0x11, 0x01}, {0x00E0,0x00E2,0x1B, 0x01}, {0x006F,0x00E6,0x0B, 0x01}, {0x00B8,0x00E8,0x14, 0x01}, {0x00D9,0x00E8,0x1A, 0x01}, {0x00F9,0x00E8,0x22, 0x01}, {0x0004,0x00F1,0x2E, 0x01}, {0x0049,0x00F8,0x26, 0x01}, {0x0010,0x00F9,0x01, 0x01}, {0x0039,0x00FB,0x33, 0x01}, {0x0028,0x00FC,0x27, 0x01}, /* star set 2 */ {0x00FA,0x0006,0x19, 0x02}, {0x00E4,0x0007,0x2D, 0x02}, {0x0072,0x000A,0x03, 0x02}, {0x0084,0x001B,0x00, 0x02}, {0x00BA,0x001D,0x29, 0x02}, {0x00E3,0x0022,0x04, 0x02}, {0x00D1,0x0026,0x2A, 0x02}, {0x0089,0x0032,0x30, 0x02}, {0x005B,0x0036,0x27, 0x02}, {0x0084,0x003A,0x36, 0x02}, {0x0053,0x003F,0x0D, 0x02}, {0x0008,0x0040,0x1D, 0x02}, {0x0055,0x0040,0x1A, 0x02}, {0x00AA,0x0041,0x31, 0x02}, {0x00FB,0x0041,0x2B, 0x02}, {0x00BC,0x0046,0x16, 0x02}, {0x0093,0x0052,0x39, 0x02}, {0x00B9,0x0057,0x10, 0x02}, {0x0054,0x0059,0x28, 0x02}, {0x00E6,0x005A,0x01, 0x02}, {0x00A7,0x005D,0x1B, 0x02}, {0x002D,0x005E,0x35, 0x02}, {0x0014,0x0062,0x21, 0x02}, {0x0069,0x006D,0x1F, 0x02}, {0x00CE,0x006F,0x0B, 0x02}, {0x00DF,0x0075,0x2F, 0x02}, {0x00CB,0x0077,0x12, 0x02}, {0x004E,0x007C,0x23, 0x02}, {0x004A,0x0084,0x0F, 0x02}, {0x0012,0x0086,0x25, 0x02}, {0x0068,0x008C,0x32, 0x02}, {0x0003,0x0095,0x20, 0x02}, {0x000A,0x009C,0x17, 0x02}, {0x005B,0x00A3,0x08, 0x02}, {0x005F,0x00A4,0x3E, 0x02}, {0x0072,0x00A4,0x2E, 0x02}, {0x00CC,0x00A6,0x06, 0x02}, {0x008A,0x00AB,0x0C, 0x02}, {0x00E0,0x00AD,0x26, 0x02}, {0x00F3,0x00AF,0x0A, 0x02}, {0x0075,0x00B4,0x13, 0x02}, {0x0068,0x00B7,0x11, 0x02}, {0x006D,0x00C2,0x2C, 0x02}, {0x0076,0x00C3,0x14, 0x02}, {0x00CF,0x00C4,0x1E, 0x02}, {0x0004,0x00C5,0x1C, 0x02}, {0x0013,0x00C6,0x3F, 0x02}, {0x00B9,0x00C7,0x3C, 0x02}, {0x0005,0x00D7,0x34, 0x02}, {0x0095,0x00D7,0x3A, 0x02}, {0x00FC,0x00D8,0x02, 0x02}, {0x00E7,0x00DC,0x09, 0x02}, {0x001D,0x00E1,0x05, 0x02}, {0x0005,0x00E6,0x33, 0x02}, {0x001C,0x00E9,0x3B, 0x02}, {0x00A2,0x00ED,0x37, 0x02}, {0x0028,0x00EE,0x07, 0x02}, {0x00DD,0x00EF,0x18, 0x02}, {0x006D,0x00F0,0x38, 0x02}, {0x00A1,0x00F2,0x0E, 0x02}, {0x0074,0x00F7,0x3D, 0x02}, {0x0069,0x00F9,0x22, 0x02}, {0x003F,0x00FF,0x24, 0x02}, /* star set 3 */ {0x0071,0x0010,0x34, 0x03}, {0x00AF,0x0011,0x23, 0x03}, {0x00A0,0x0014,0x26, 0x03}, {0x0002,0x0017,0x02, 0x03}, {0x004B,0x0019,0x31, 0x03}, {0x0093,0x001C,0x0E, 0x03}, {0x001B,0x001E,0x25, 0x03}, {0x0032,0x0020,0x2E, 0x03}, {0x00EE,0x0020,0x3A, 0x03}, {0x0079,0x0022,0x2F, 0x03}, {0x006C,0x0023,0x17, 0x03}, {0x00BC,0x0025,0x11, 0x03}, {0x0041,0x0029,0x30, 0x03}, {0x001C,0x002E,0x32, 0x03}, {0x00B9,0x0031,0x01, 0x03}, {0x0083,0x0032,0x05, 0x03}, {0x0095,0x003A,0x12, 0x03}, {0x000D,0x003F,0x07, 0x03}, {0x0020,0x0041,0x33, 0x03}, {0x0092,0x0045,0x2C, 0x03}, {0x00D4,0x0047,0x08, 0x03}, {0x00A1,0x004B,0x2D, 0x03}, {0x00D2,0x004B,0x3B, 0x03}, {0x00D6,0x0052,0x24, 0x03}, {0x009A,0x005F,0x1C, 0x03}, {0x0016,0x0060,0x3D, 0x03}, {0x001A,0x0063,0x1F, 0x03}, {0x00CD,0x0066,0x28, 0x03}, {0x00FF,0x0067,0x10, 0x03}, {0x0035,0x0069,0x20, 0x03}, {0x008F,0x006C,0x04, 0x03}, {0x00CA,0x006C,0x2A, 0x03}, {0x005A,0x0074,0x09, 0x03}, {0x0060,0x0078,0x38, 0x03}, {0x0072,0x0079,0x1E, 0x03}, {0x0037,0x007F,0x29, 0x03}, {0x0012,0x0080,0x14, 0x03}, {0x0029,0x0082,0x2B, 0x03}, {0x0084,0x0098,0x36, 0x03}, {0x0032,0x0099,0x37, 0x03}, {0x00BB,0x00A0,0x19, 0x03}, {0x003E,0x00A3,0x3E, 0x03}, {0x004A,0x00A6,0x1A, 0x03}, {0x0029,0x00A7,0x21, 0x03}, {0x009D,0x00B7,0x22, 0x03}, {0x006C,0x00B9,0x15, 0x03}, {0x000C,0x00C0,0x0A, 0x03}, {0x00C2,0x00C3,0x0F, 0x03}, {0x002F,0x00C9,0x0D, 0x03}, {0x00D2,0x00CE,0x16, 0x03}, {0x00F3,0x00CE,0x0B, 0x03}, {0x0075,0x00CF,0x27, 0x03}, {0x001A,0x00D5,0x35, 0x03}, {0x0026,0x00D6,0x39, 0x03}, {0x0080,0x00DA,0x3C, 0x03}, {0x00A9,0x00DD,0x00, 0x03}, {0x00BC,0x00EB,0x03, 0x03}, {0x0032,0x00EF,0x1B, 0x03}, {0x0067,0x00F0,0x3F, 0x03}, {0x00EF,0x00F1,0x18, 0x03}, {0x00A8,0x00F3,0x0C, 0x03}, {0x00DE,0x00F9,0x1D, 0x03}, {0x002C,0x00FA,0x13, 0x03} }; /*************************************************************************** Convert the color PROMs. Galaga has one 32x8 palette PROM and two 256x4 color lookup table PROMs (one for characters, one for sprites). Only the first 128 bytes of the lookup tables seem to be used. The palette PROM is connected to the RGB output this way: bit 7 -- 220 ohm resistor -- BLUE -- 470 ohm resistor -- BLUE -- 220 ohm resistor -- GREEN -- 470 ohm resistor -- GREEN -- 1 kohm resistor -- GREEN -- 220 ohm resistor -- RED -- 470 ohm resistor -- RED bit 0 -- 1 kohm resistor -- RED ***************************************************************************/ PALETTE_INIT( galaga ) { int i; machine.colortable = colortable_alloc(machine, 32+64); /* core palette */ for (i = 0;i < 32;i++) { int bit0,bit1,bit2,r,g,b; bit0 = ((*color_prom) >> 0) & 0x01; bit1 = ((*color_prom) >> 1) & 0x01; bit2 = ((*color_prom) >> 2) & 0x01; r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; bit0 = ((*color_prom) >> 3) & 0x01; bit1 = ((*color_prom) >> 4) & 0x01; bit2 = ((*color_prom) >> 5) & 0x01; g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; bit0 = 0; bit1 = ((*color_prom) >> 6) & 0x01; bit2 = ((*color_prom) >> 7) & 0x01; b = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; colortable_palette_set_color(machine.colortable,i,MAKE_RGB(r,g,b)); color_prom++; } /* palette for the stars */ for (i = 0;i < 64;i++) { int bits,r,g,b; static const int map[4] = { 0x00, 0x47, 0x97 ,0xde }; bits = (i >> 0) & 0x03; r = map[bits]; bits = (i >> 2) & 0x03; g = map[bits]; bits = (i >> 4) & 0x03; b = map[bits]; colortable_palette_set_color(machine.colortable,32 + i,MAKE_RGB(r,g,b)); } /* characters */ for (i = 0;i < 64*4;i++) colortable_entry_set_value(machine.colortable, i, (*(color_prom++) & 0x0f) + 0x10); /* chars */ /* sprites */ for (i = 0;i < 64*4;i++) colortable_entry_set_value(machine.colortable, 64*4+i, (*(color_prom++) & 0x0f)); /* now the stars */ for (i = 0;i < 64;i++) colortable_entry_set_value(machine.colortable, 64*4+64*4+i, 32 + i); } /*************************************************************************** Callbacks for the TileMap code ***************************************************************************/ /* convert from 32x32 to 36x28 */ static TILEMAP_MAPPER( tilemap_scan ) { int offs; row += 2; col -= 2; if (col & 0x20) offs = row + ((col & 0x1f) << 5); else offs = col + (row << 5); return offs; } static TILE_GET_INFO( get_tile_info ) { /* the hardware has two character sets, one normal and one x-flipped. When screen is flipped, character y flip is done by the hardware inverting the timing signals, while x flip is done by selecting the 2nd character set. We reproduce this here, but since the tilemap system automatically flips characters when screen is flipped, we have to flip them back. */ galaga_state *state = machine.driver_data(); int color = state->m_videoram[tile_index + 0x400] & 0x3f; SET_TILE_INFO( 0, (state->m_videoram[tile_index] & 0x7f) | (flip_screen_get(machine) ? 0x80 : 0) | (state->m_galaga_gfxbank << 8), color, flip_screen_get(machine) ? TILE_FLIPX : 0); tileinfo->group = color; } /*************************************************************************** Start the video hardware emulation. ***************************************************************************/ VIDEO_START( galaga ) { galaga_state *state = machine.driver_data(); state->m_fg_tilemap = tilemap_create(machine, get_tile_info,tilemap_scan,8,8,36,28); colortable_configure_tilemap_groups(machine.colortable, state->m_fg_tilemap, machine.gfx[0], 0x1f); state->m_galaga_gfxbank = 0; state->save_item(NAME(state->m_stars_scrollx)); state->save_item(NAME(state->m_stars_scrolly)); state->save_item(NAME(state->m_galaga_gfxbank)); } /*************************************************************************** Memory handlers ***************************************************************************/ WRITE8_HANDLER( galaga_videoram_w ) { galaga_state *state = space->machine().driver_data(); state->m_videoram[offset] = data; tilemap_mark_tile_dirty(state->m_fg_tilemap,offset & 0x3ff); } WRITE8_HANDLER ( gatsbee_bank_w ) { galaga_state *state = space->machine().driver_data(); state->m_galaga_gfxbank = data & 0x1; tilemap_mark_all_tiles_dirty(state->m_fg_tilemap); } /*************************************************************************** Display refresh ***************************************************************************/ static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect ) { galaga_state *state = machine.driver_data(); UINT8 *spriteram = state->m_galaga_ram1 + 0x380; UINT8 *spriteram_2 = state->m_galaga_ram2 + 0x380; UINT8 *spriteram_3 = state->m_galaga_ram3 + 0x380; int offs; for (offs = 0;offs < 0x80;offs += 2) { static const int gfx_offs[2][2] = { { 0, 1 }, { 2, 3 } }; int sprite = spriteram[offs] & 0x7f; int color = spriteram[offs+1] & 0x3f; int sx = spriteram_2[offs+1] - 40 + 0x100*(spriteram_3[offs+1] & 3); int sy = 256 - spriteram_2[offs] + 1; // sprites are buffered and delayed by one scanline int flipx = (spriteram_3[offs] & 0x01); int flipy = (spriteram_3[offs] & 0x02) >> 1; int sizex = (spriteram_3[offs] & 0x04) >> 2; int sizey = (spriteram_3[offs] & 0x08) >> 3; int x,y; sy -= 16 * sizey; sy = (sy & 0xff) - 32; // fix wraparound if (flip_screen_get(machine)) { flipx ^= 1; flipy ^= 1; sy += 40; sx += 96; } for (y = 0;y <= sizey;y++) { for (x = 0;x <= sizex;x++) { drawgfx_transmask(bitmap,cliprect,machine.gfx[1], sprite + gfx_offs[y ^ (sizey * flipy)][x ^ (sizex * flipx)], color, flipx,flipy, sx + 16*x, sy + 16*y, colortable_get_transpen_mask(machine.colortable, machine.gfx[1], color, 0x0f)); } } } } static void draw_stars(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect ) { galaga_state *state = machine.driver_data(); /* draw the stars */ /* $a005 controls the stars ON/OFF */ if ( (state->m_galaga_starcontrol[5] & 1) == 1 ) { int y_align = 112; /* 112 is a tweak to get alignment about perfect */ int x_align = flip_screen_get(machine) ? 112 : 16; int star_cntr; int set_a, set_b; /* two sets of stars controlled by these bits */ set_a = (state->m_galaga_starcontrol[3] & 1); set_b = (state->m_galaga_starcontrol[4] & 1) | 2; for (star_cntr = 0;star_cntr < MAX_STARS ;star_cntr++) { int x,y; if ( (set_a == star_seed_tab[star_cntr].set) || ( set_b == star_seed_tab[star_cntr].set) ) { x = (star_seed_tab[star_cntr].x + state->m_stars_scrollx) % 256 + x_align; y = (y_align + star_seed_tab[star_cntr].y + state->m_stars_scrolly) % 256; if (x >= cliprect->min_x && x <= cliprect->max_x && y >= cliprect->min_y && y <= cliprect->max_y) *BITMAP_ADDR16(bitmap, y, x) = STARS_COLOR_BASE + star_seed_tab[ star_cntr ].col; } } } } SCREEN_UPDATE( galaga ) { galaga_state *state = screen->machine().driver_data(); bitmap_fill(bitmap,cliprect,get_black_pen(screen->machine())); draw_stars(screen->machine(),bitmap,cliprect); draw_sprites(screen->machine(),bitmap,cliprect); tilemap_draw(bitmap,cliprect,state->m_fg_tilemap,0,0); return 0; } SCREEN_EOF( galaga ) { galaga_state *state = machine.driver_data(); /* this function is called by galaga_interrupt_1() */ int s0,s1,s2; static const int speeds[8] = { -1, -2, -3, 0, 3, 2, 1, 0 }; s0 = (state->m_galaga_starcontrol[0] & 1); s1 = (state->m_galaga_starcontrol[1] & 1); s2 = (state->m_galaga_starcontrol[2] & 1); state->m_stars_scrollx += speeds[s0 + s1*2 + s2*4]; }