diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index e3254b6b096..b4a95fae8e1 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -2798,6 +2798,8 @@ files { MAME_DIR .. "src/mame/video/galaga.cpp", MAME_DIR .. "src/mame/video/bosco.cpp", MAME_DIR .. "src/mame/includes/bosco.h", + MAME_DIR .. "src/mame/video/starfield_05xx.cpp", + MAME_DIR .. "src/mame/video/starfield_05xx.h", MAME_DIR .. "src/mame/video/digdug.cpp", MAME_DIR .. "src/mame/includes/digdug.h", MAME_DIR .. "src/mame/machine/xevious.cpp", diff --git a/src/mame/drivers/galaga.cpp b/src/mame/drivers/galaga.cpp index 8ac7ff85a17..e78270ee467 100644 --- a/src/mame/drivers/galaga.cpp +++ b/src/mame/drivers/galaga.cpp @@ -672,10 +672,6 @@ Notes: TODO: ---- -- bosco & galaga: - - the starfield is wrong. - - The function of STARCLR is unknown. It is not latched and there are no data bits - used... - bosco: is the scrolling tilemap placement correct? It is currently aligned so that the test grid shown on startup is correct, but this way an unerased grey strip @@ -722,6 +718,12 @@ TODO: #define MASTER_CLOCK (XTAL(18'432'000)) +#define STARFIELD_X_OFFSET_GALAGA 16 +#define STARFIELD_X_LIMIT_GALAGA 256 + STARFIELD_X_OFFSET_GALAGA + +#define STARFIELD_Y_OFFSET_BOSCO 16 +#define STARFIELD_X_LIMIT_BOSCO 224 + READ8_MEMBER(galaga_state::bosco_dsw_r) { @@ -1664,6 +1666,9 @@ void bosco_state::bosco(machine_config &config) GFXDECODE(config, m_gfxdecode, m_palette, gfx_bosco); PALETTE(config, m_palette, FUNC(bosco_state::bosco_palette), 64*4 + 64*4 + 4 + 64, 32+64); + STARFIELD_05XX(config, m_starfield, 0); + m_starfield->set_starfield_config(0, STARFIELD_Y_OFFSET_BOSCO, STARFIELD_X_LIMIT_BOSCO); + MCFG_VIDEO_START_OVERRIDE(bosco_state,bosco) /* sound hardware */ @@ -1734,6 +1739,9 @@ void galaga_state::galaga(machine_config &config) GFXDECODE(config, m_gfxdecode, m_palette, gfx_galaga); PALETTE(config, m_palette, FUNC(galaga_state::galaga_palette), 64*4 + 64*4 + 4 + 64, 32+64); + STARFIELD_05XX(config, m_starfield, 0); + m_starfield->set_starfield_config(STARFIELD_X_OFFSET_GALAGA, 0, STARFIELD_X_LIMIT_GALAGA); + MCFG_VIDEO_START_OVERRIDE(galaga_state,galaga) /* sound hardware */ @@ -3478,11 +3486,11 @@ GAME( 1981, boscoo2, bosco, bosco, bosco, bosco_state, empty_init, GAME( 1981, boscomd, bosco, bosco, boscomd, bosco_state, empty_init, ROT0, "Namco (Midway license)", "Bosconian (Midway, new version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) GAME( 1981, boscomdo, bosco, bosco, boscomd, bosco_state, empty_init, ROT0, "Namco (Midway license)", "Bosconian (Midway, old version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) -GAME( 1981, galaga, 0, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco", "Galaga (Namco rev. B)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) -GAME( 1981, galagao, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco", "Galaga (Namco)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) -GAME( 1981, galagamw, galaga, galaga, galagamw, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 1)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) -GAME( 1981, galagamk, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 2)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) -GAME( 1981, galagamf, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 1 with fast shoot hack)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1981, galaga, 0, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco", "Galaga (Namco rev. B)", MACHINE_SUPPORTS_SAVE ) +GAME( 1981, galagao, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco", "Galaga (Namco)", MACHINE_SUPPORTS_SAVE ) +GAME( 1981, galagamw, galaga, galaga, galagamw, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 1)", MACHINE_SUPPORTS_SAVE ) +GAME( 1981, galagamk, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 2)", MACHINE_SUPPORTS_SAVE ) +GAME( 1981, galagamf, galaga, galaga, galaga, galaga_state, init_galaga, ROT90, "Namco (Midway license)", "Galaga (Midway set 1 with fast shoot hack)", MACHINE_SUPPORTS_SAVE ) GAME( 1982, xevious, 0, xevious, xevious, xevious_state, init_xevious, ROT90, "Namco", "Xevious (Namco)", MACHINE_SUPPORTS_SAVE ) GAME( 1982, xeviousa, xevious, xevious, xeviousa, xevious_state, init_xevious, ROT90, "Namco (Atari license)", "Xevious (Atari, harder)", MACHINE_SUPPORTS_SAVE ) @@ -3499,9 +3507,9 @@ GAME( 1982, digsid, digdug, digdug, digdug, digdug_state, empty_init, /* Bootlegs with replacement I/O chips */ -GAME( 1982, gallag, galaga, galagab, galaga, galaga_state, init_galaga, ROT90, "bootleg", "Gallag", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) -GAME( 1984, gatsbee, galaga, gatsbee, gatsbee, galaga_state, init_galaga, ROT90, "hack (Uchida)", "Gatsbee", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) -GAME( 1981, nebulbee, galaga, galagab, galaga, galaga_state, init_galaga, ROT90, "bootleg", "Nebulous Bee", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1982, gallag, galaga, galagab, galaga, galaga_state, init_galaga, ROT90, "bootleg", "Gallag", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) +GAME( 1984, gatsbee, galaga, gatsbee, gatsbee, galaga_state, init_galaga, ROT90, "hack (Uchida)", "Gatsbee", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) +GAME( 1981, nebulbee, galaga, galagab, galaga, galaga_state, init_galaga, ROT90, "bootleg", "Nebulous Bee", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) GAME( 1982, xevios, xevious, xevious, xevious, xevious_state, init_xevios, ROT90, "bootleg", "Xevios", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1982, battles, xevious, battles, xevious, battles_state, driver_init, ROT90, "bootleg", "Battles (set 1)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/includes/bosco.h b/src/mame/includes/bosco.h index 77eeddae20e..efab2b90bad 100644 --- a/src/mame/includes/bosco.h +++ b/src/mame/includes/bosco.h @@ -25,6 +25,8 @@ public: uint8_t *m_bosco_radarx; uint8_t *m_bosco_radary; + uint8_t m_bosco_starclr; + uint8_t *m_spriteram; uint8_t *m_spriteram2; uint32_t m_spriteram_size; diff --git a/src/mame/includes/galaga.h b/src/mame/includes/galaga.h index 0256aa94552..013966950e2 100644 --- a/src/mame/includes/galaga.h +++ b/src/mame/includes/galaga.h @@ -5,6 +5,7 @@ #pragma once +#include "video/starfield_05xx.h" #include "machine/74259.h" #include "sound/discrete.h" #include "sound/namco.h" @@ -13,6 +14,7 @@ #include "screen.h" #include "tilemap.h" + class galaga_state : public driver_device { public: @@ -31,6 +33,7 @@ public: , m_screen(*this, "screen") , m_palette(*this, "palette") , m_leds(*this, "led%u", 0U) + , m_starfield(*this, "starfield") { } DECLARE_READ8_MEMBER(bosco_dsw_r); @@ -55,6 +58,7 @@ public: DECLARE_WRITE_LINE_MEMBER(vblank_irq); TIMER_CALLBACK_MEMBER(cpu3_interrupt_callback); void draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect ); + uint16_t get_next_lfsr_state(uint16_t lfsr); void draw_stars(bitmap_ind16 &bitmap, const rectangle &cliprect ); void galaga(machine_config &config); void gatsbee(machine_config &config); @@ -64,8 +68,6 @@ public: void galaga_mem4(address_map &map); void gatsbee_main_map(address_map &map); - void starfield_init(); - protected: virtual void machine_start() override; virtual void machine_reset() override; @@ -84,12 +86,9 @@ protected: required_device m_screen; required_device m_palette; output_finder<2> m_leds; + optional_device m_starfield; // not present on battles, digdug, xevious emu_timer *m_cpu3_interrupt_timer; - /* machine state */ - uint32_t m_stars_scrollx; - uint32_t m_stars_scrolly; - uint32_t m_galaga_gfxbank; // used by catsbee /* devices */ @@ -103,15 +102,6 @@ protected: uint8_t m_main_irq_mask; uint8_t m_sub_irq_mask; uint8_t m_sub2_nmi_mask; - - struct star - { - uint16_t x,y; - uint8_t col,set; - }; - - static star const s_star_seed_tab[]; - }; DISCRETE_SOUND_EXTERN( galaga_discrete ); diff --git a/src/mame/video/bosco.cpp b/src/mame/video/bosco.cpp index 7484346281a..a84bd544200 100644 --- a/src/mame/video/bosco.cpp +++ b/src/mame/video/bosco.cpp @@ -13,10 +13,9 @@ #include "includes/bosco.h" -#define MAX_STARS 252 -#define STARS_COLOR_BASE (64*4+64*4+4) #define VIDEO_RAM_SIZE 0x400 + void bosco_state::bosco_palette(palette_device &palette) const { const uint8_t *color_prom = memregion("proms")->base(); @@ -132,8 +131,9 @@ VIDEO_START_MEMBER(bosco_state,bosco) m_bosco_radarx = m_videoram + 0x03f0; m_bosco_radary = m_bosco_radarx + 0x0800; - save_item(NAME(m_stars_scrollx)); - save_item(NAME(m_stars_scrolly)); + m_bosco_starclr = 1; + + save_item(NAME(m_bosco_starclr)); } @@ -165,6 +165,8 @@ WRITE8_MEMBER( bosco_state::bosco_scrolly_w ) WRITE8_MEMBER( bosco_state::bosco_starclr_w ) { + // On any write to $9840, turn on starfield + m_bosco_starclr = 0; } @@ -225,39 +227,6 @@ void bosco_state::draw_bullets(bitmap_ind16 &bitmap, const rectangle &cliprect, } -void bosco_state::draw_stars(bitmap_ind16 &bitmap, const rectangle &cliprect, int flip) -{ - if (1) - { - int star_cntr; - int set_a, set_b; - - /* two sets of stars controlled by these bits */ - set_a = m_videolatch->q4_r(); - set_b = m_videolatch->q5_r() | 2; - - for (star_cntr = 0;star_cntr < MAX_STARS;star_cntr++) - { - int x,y; - - if ((set_a == s_star_seed_tab[star_cntr].set) || (set_b == s_star_seed_tab[star_cntr].set)) - { - x = (s_star_seed_tab[star_cntr].x + m_stars_scrollx) % 256; - y = (s_star_seed_tab[star_cntr].y + m_stars_scrolly) % 256; - - /* don't draw the stars that are off the screen */ - if (x < 224) - { - if (flip) x += 64; - - if (cliprect.contains(x, y)) - bitmap.pix16(y, x) = STARS_COLOR_BASE + s_star_seed_tab[star_cntr].col; - } - } - } - } -} - uint32_t bosco_state::screen_update_bosco(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { @@ -278,7 +247,7 @@ uint32_t bosco_state::screen_update_bosco(screen_device &screen, bitmap_ind16 &b } bitmap.fill(m_palette->black_pen(), cliprect); - draw_stars(bitmap,cliprect,flip); + m_starfield->draw_starfield(bitmap,cliprect,flip); m_bg_tilemap->draw(screen, bitmap, bg_clip, 0,0); m_fg_tilemap->draw(screen, bitmap, fg_clip, 0,0); @@ -300,10 +269,14 @@ WRITE_LINE_MEMBER(bosco_state::screen_vblank_bosco) // falling edge if (!state) { - static const int speedsx[8] = { -1, -2, -3, 0, 3, 2, 1, 0 }; - static const int speedsy[8] = { 0, -1, -2, -3, 0, 3, 2, 1 }; + // Bosconian scrolls in X and Y directions + const uint8_t speed_index_X = m_bosco_starcontrol[0] & 0x07; + const uint8_t speed_index_Y = (m_bosco_starcontrol[0] & 0x38) >> 3; + m_starfield->set_scroll_speed(speed_index_X,speed_index_Y); - m_stars_scrollx += speedsx[m_bosco_starcontrol[0] & 0x07]; - m_stars_scrolly += speedsy[(m_bosco_starcontrol[0] & 0x38) >> 3]; + m_starfield->set_active_starfield_sets(m_videolatch->q4_r(), m_videolatch->q5_r() | 2); + + // _STARCLR signal enables/disables starfield + m_starfield->enable_starfield(!m_bosco_starclr); } } diff --git a/src/mame/video/galaga.cpp b/src/mame/video/galaga.cpp index 23fe7653665..edfa4499b31 100644 --- a/src/mame/video/galaga.cpp +++ b/src/mame/video/galaga.cpp @@ -18,293 +18,6 @@ #include "logmacro.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 - -*/ - -galaga_state::star const galaga_state::s_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} -}; - - /*************************************************************************** @@ -378,100 +91,6 @@ void galaga_state::galaga_palette(palette_device &palette) const palette.set_pen_indirect(64*4 + 64*4 + i, 32 + i); } -/*************************************************************************** - - Star field documentation - - Couriersud, May 2019: - - The code below was manually applied based on a pull request from - Jindřich Makovička. He based his work on information published by - Wolfgang Scherr about the 05XX on pin4.at. - - Wolfgang shared is VHDL implementation with the MAME team. I have - created a spreadsheet implementation for his decode logic. - - Both implementations use the same Galois type LFSR( tap 15,12,10,5). - Using the same seed value, the following holds true: - - Decode_W(lfsr[t-4]) = Decode_J(lfsr[t]) - - The two decoding algorithm (Wolfgang, Jindřich) thus deliver the same - results but with a 4 clock difference. - - Jindřich's code filters out stars with y<4. This matches the starfield - measurements documented above. Wolfgang states that his code matches his - measurements and there are stars with y<4. - We need to have a closer look at this. - - Both implementations are complex compared to other star field gnerators - used in the industry. We thus now have two decoding solutions matching - the output. I wonder if there is a simpler one. - -***************************************************************************/ - -void galaga_state::starfield_init() -{ - const uint16_t feed = 0x9420; - - int idx = 0; - for (uint16_t sf = 0; sf < 4; ++sf) - { - // starfield select flags - uint16_t sf1 = (sf >> 1) & 1; - uint16_t sf2 = sf & 1; - - uint16_t i = 0x70cc; - for (int cnt = 0; cnt < 65535; ++cnt) - { - // output enable lookup - uint16_t xor1 = i ^ (i >> 3); - uint16_t xor2 = xor1 ^ (i >> 2); - uint16_t oe = (sf1 ? 0 : 0x4000) | ((sf1 ^ sf2) ? 0 : 0x1000); - if ((i & 0x8007) == 0x8007 - && (~i & 0x2008) == 0x2008 - && (xor1 & 0x0100) == (sf1 ? 0 : 0x0100) - && (xor2 & 0x0040) == (sf2 ? 0 : 0x0040) - && (i & 0x5000) == oe - && cnt >= 256 * 4) - { - // color lookup - uint16_t xor3 = (i >> 1) ^ (i >> 6); - uint16_t clr = - (((i >> 9) & 0x07) - | ((xor3 ^ (i >> 4) ^ (i >> 7)) & 0x08) - | (~xor3 & 0x10) - | (((i >> 2) ^ (i >> 5)) & 0x20)) - ^ ((i & 0x4000) ? 0 : 0x24) - ^ ((((i >> 2) ^ i) & 0x1000) ? 0x21 : 0); -#if 0 - m_star_seed_tab[idx].x = cnt % 256; - m_star_seed_tab[idx].y = cnt / 256; - m_star_seed_tab[idx].col = clr; - m_star_seed_tab[idx].set = sf; -#else - int x = cnt % 256; - int y = cnt / 256; - int col = clr; - int set = sf; - - if ((x != s_star_seed_tab[idx].x) || (y != s_star_seed_tab[idx].y) - || (col != s_star_seed_tab[idx].col) || (s_star_seed_tab[idx].set != set)) - LOGMASKED(LOG_DEBUG, "Mismatch: %d %d %d %d %d %d %d %d\n", x, y, col, set, s_star_seed_tab[idx].x, - s_star_seed_tab[idx].y, s_star_seed_tab[idx].col, s_star_seed_tab[idx].set); -#endif - ++idx; - } - - // update the LFSR - if (i & 1) - i = (i >> 1) ^ feed; - else - i = (i >> 1); - } - } -} - /*************************************************************************** Callbacks for the TileMap code @@ -524,11 +143,7 @@ VIDEO_START_MEMBER(galaga_state,galaga) m_galaga_gfxbank = 0; - save_item(NAME(m_stars_scrollx)); - save_item(NAME(m_stars_scrolly)); save_item(NAME(m_galaga_gfxbank)); - - starfield_init(); } @@ -610,42 +225,11 @@ void galaga_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect } -void galaga_state::draw_stars(bitmap_ind16 &bitmap, const rectangle &cliprect ) -{ - /* draw the stars */ - - /* $a005 controls the stars ON/OFF */ - if ( m_videolatch->q5_r() == 1 ) - { - int star_cntr; - int set_a, set_b; - - /* two sets of stars controlled by these bits */ - set_a = m_videolatch->q3_r(); - set_b = m_videolatch->q4_r() | 2; - - for (star_cntr = 0;star_cntr < MAX_STARS ;star_cntr++) - { - int x,y; - - if ((set_a == s_star_seed_tab[star_cntr].set) || (set_b == s_star_seed_tab[star_cntr].set)) - { - x = (s_star_seed_tab[star_cntr].x + m_stars_scrollx) % 256 + 16; - y = (112 + s_star_seed_tab[star_cntr].y + m_stars_scrolly) % 256; - /* 112 is a tweak to get alignment about perfect */ - - if (cliprect.contains(x, y)) - bitmap.pix16(y, x) = STARS_COLOR_BASE + s_star_seed_tab[ star_cntr ].col; - } - - } - } -} uint32_t galaga_state::screen_update_galaga(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { bitmap.fill(m_palette->black_pen(), cliprect); - draw_stars(bitmap,cliprect); + m_starfield->draw_starfield(bitmap,cliprect,0); draw_sprites(bitmap,cliprect); m_fg_tilemap->draw(screen, bitmap, cliprect, 0,0); return 0; @@ -658,14 +242,15 @@ WRITE_LINE_MEMBER(galaga_state::screen_vblank_galaga) // falling edge if (!state) { - /* 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 }; + // Galaga only scrolls in X direction - the SCROLL_Y pins + // of the 05XX chip are tied to ground. + const uint8_t speed_index_X = (m_videolatch->q2_r()<<2) | (m_videolatch->q1_r()<<1) | (m_videolatch->q0_r()<<0); + const uint8_t speed_index_Y = 0; + m_starfield->set_scroll_speed(speed_index_X,speed_index_Y); - s0 = m_videolatch->q0_r(); - s1 = m_videolatch->q1_r(); - s2 = m_videolatch->q2_r(); + m_starfield->set_active_starfield_sets(m_videolatch->q3_r(), m_videolatch->q4_r() | 2); - m_stars_scrollx += speeds[s0 + s1*2 + s2*4]; + // _STARCLR signal enables/disables starfield + m_starfield->enable_starfield(m_videolatch->q5_r()); } } diff --git a/src/mame/video/starfield_05xx.cpp b/src/mame/video/starfield_05xx.cpp new file mode 100644 index 00000000000..23c349eae33 --- /dev/null +++ b/src/mame/video/starfield_05xx.cpp @@ -0,0 +1,680 @@ +// license:BSD-3-Clause +// copyright-holders:Robert Hildinger + + +/*************************************************************************** + + Starfield generator documentation + + R. Hildinger, based on RE effort Aug. 2019 + + ----- + + * These notes are based on what is presumably an original Namco 05xx starfield + generator from an original 1981 Namco/Midway Galaga board. This particular + chip is marked only with the text "0521" on the chip package. The ROMS on + this board are all marked "0508-00803 Galaga (c) Midway 1981" + + INTRO: + ------ + + The starfields for Galaga and Bosconian are driven by a custom Namco 05XX chip that + is contains an internal 16-bit Linear Feedback Shift Register (LFSR) and all the + necessary support logic for generating a 6-bit RGB signal. The chip is fed all the + required signals from the video system to allow it to output a colored "star" at + pseudo-random intervals and to scroll these stars in both horizontal and vertical + directions. + + The chip can generate a total of 256 stars in 4 banks of 64 for each vertical + frame. Two of these banks of stars will be active at any one time, controlled + by two starfield selector pins. Some stars will be hidden by the vertical and + horizontal blanking, so there will always be less than 128 stars on screen at one + time. + + The 05xx is a typical seventies-era 5v logic chip in a 24 pin, .6" width package: + + Pin arrangement: + + + |--------------------------| + CLK <| 1 ++ /-\ 24 |> Gnd + | ++ \-/ | + _HSYNC <| 2 23 |> SF1 + | | + LFSR_RUN <| 3 0 22 |> SF0 + | 5 | + _VSYNC <| 4 2 21 |> LFSR_OUT + | 1 | + _OE <| 5 20 |> OE_CHAIN + | | + BLUE_HI <| 6 19 |> _STARCLR + | | + BLUE_LO <| 7 18 |> SCROLL_Y2 + | | + GREEN_HI <| 8 17 |> SCROLL_Y1 + | | + GEEEN_LO <| 9 16 |> SCROLL_Y0 + | | + RED_HI <| 10 15 |> SCROLL_X2 + | | + RED_LO <| 11 14 |> SCROLL_X1 + | | + Vcc (+5v) <| 12 13 |> SCROLL_X0 + |--------------------------| + + + NOTE: Pin names are not official - just my best approximation of their function. + + + INPUTS: + ------- + + Pin 1 (CLK): This signal drives the internal linear feedback shift register (LFSR), + as well as some internal state logic. + + Pin 2 (_HSYNC): Video system horizontal sync signal (pulse low). Rising edge marks + the start of each horizontal line in monitor's natural orientation. + + Pin 3 (LFSR_RUN): Enable the internal LFSR to update with every CLK cycle. This + signal is gated internally with the SCROLL and _STARCLR signals + to implement horizontal and vertical starfield scrolling, and + to clear the starfield altogether. + + Pin 4 (_VSYNC): Video system vertical sync signal (pulse low). Rising edge marks + the start of each video frame. + + Pin 5 (_OE): Output Enable (Active low): When low, the RGB outputs are active. When + high, the RGB outputs are placed in a high impedance state to prevent + interference with sprite and tile map generators. This line is used to + effectively place stars in the background and prevent them from + overwriting sprites and tiles. + + Pins 13-15 (SCROLL_X): These 3 lines control the horizontal speed and direction of + the starfield scrolling. SCROLL_X2, SCROLL_X1, and + SCROLL_X0 map to scroll speed as follows: + + SCROLL_X2 SCROLL_X1 SCROLL_X0 speed and direction + --------- --------- --------- ------------------- + low low low 1 pixels per frame reverse (-X) + low low high 2 pixels per frame reverse (-X) + low high low 3 pixels per frame reverse (-X) + low high high 4 pixels per frame reverse (-X) + + high low low 3 pixels per frame forward (+X) + high low high 2 pixels per frame forward (+X) + high high low 1 pixels per frame forward (+X) + high high high 0 pixels per frame stationary + + Pins 16-18 (SCROLL_Y): These 3 lines control the vertical speed and direction of the + starfield scrolling. SCROLL_Y2, SCROLL_Y1, and SCROLL_Y0 map to + scroll speed as follows: + + SCROLL_Y2 SCROLL_Y1 SCROLL_Y0 speed + --------- --------- --------- ----- + low low low 0 lines per frame stationary + low low high 1 lines per frame reverse (-Y) + low high low 2 lines per frame reverse (-Y) + low high high 3 lines per frame reverse (-Y) + + high low low 4 lines per frame forward (+Y) + high low high 3 lines per frame forward (+Y) + high high low 2 lines per frame forward (+Y) + high high high 1 lines per frame forward (+Y) + + Pin 19 (_STARCLR): Gates the LFSR_RUN signal when active, stopping the LFSR from + running which effectively stops the starfield from being drawn. + It also resets the LFSR seed back to boot value. + + Pins 22-23 (SF): These two lines control which of the 4 star sets are currently being + displayed on screen. SF1 and SF0 map to the star sets as follows: + + SF1 SF0 Star sets + --- --- --------- + low low 0 and 2 + low high 1 and 2 + high high 1 and 3 + High low 0 and 3 + + + + OUTPUTS: + -------- + + Pins 6-11 (RGB Output): These are the red, green, and blue outputs designed to be + fed in to a digital to analog converter and sent to the picture + tube. These outputs are active every time the LFSR has a "hit" + that indicates a star should be generated at this point. + Together they form a 6-bit RGB color value as follows: + + Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 + ---- ---- ---- ---- ---- ---- + BLUE_HI BLUE_LO GREEN_HI GREEN_LO RED_HI RED_LO + + Pin 20 (OE_CHAIN): This signal (chained output enable) is high for 1 CLK cycle every + time the LFSR has a "hit" that indicates a star should be generated + at this point. It is active at the same time as the RGB outputs. + + Pin 21 (LFSR_OUT): This is the output bit of the internal 16-bit LFSR. + + + MACHINE NOTES: + -------------- + ( G = Galaga, B = Bosconian, GB = both) + + GB: CLK: - driven at 1/3 of the Master clock signal ( 18.432 Mhz / 3 = 6.144 MHz ). + + GB: _HSYNC: - driven at 1/384 of the CLK signal ( 6.144 Mhz / 384 = 16 KHz ) + - pulse width = 32 CLK cycles low, 352 CLK cycles high (total = 384) + - High pulse width limits horizontal resolution to maximum of 352 pixels. + - 256 pulses per vertical frame during _VSYNC high + + GB: _VSYNC: - driven at 1/264 of the _HSYNC signal ( 16 KHz / 264 = 60.60606 Hz) + - pulse width = 8 _HSYNC pulses low, 256 _HSYNC pulses high (total = 264) + - High pulse width limits vertical resolution to maximum of 256 lines. + + GB: LFSR_RUN: - driven at 1/384 of the CLK signal ( 6.144 Mhz / 384 = 16 KHz ) + - pulse width = 128 CLK cycles low, 256 CLK cycles high (total = 384) + - 256 pulses per vertical frame during _VSYNC high + - High pulse position sits inside _HSYNC high between CLK + cycles 56 and 311, which effectively limits starfield width + to 256 horizontal pixels + + GB: _OE: - Semi-periodic signal driven at 1/384 of the CLK signal ( 16 KHz ) + - Low portion of signal is driven high at any point where a tile or + sprite is generated + - Low pulse width = 288 CLK cycles, High pulse width = 96 CLK cycles + - Low pulse position inside _HSYNC high between CLK cycles 40 and 327 + - Low pulse width further limits horizontal resolution to 288 pixels + - Signal is only actively generated by video system hardware between _HSYNC + pulses 25 and 248 for a total of 224 pulses, which further limits vertical + esolution to 224 lines. + + G: SCROLL_Y: - All SCROLL_Y signals are tied to ground + + GB: RGB OUTPUT: These outputs are sent to the simple resistance ladders that form the + 8-bit RGB DAC within the video system: + + BLUE_HI ----> 220 ohm resistor --------> Blue video signal + BLUE_LO ----> 470 ohm resistor ----^ + + GREEN_HI ---> 220 ohm resistor --------> Green video signal + GREEN_LO ---> 470 ohm resistor ----^ + ......---------> 1K ohm resistor ----^ + + RED_HI -----> 220 ohm resistor --------> Red video signal + RED_LO -----> 470 ohm resistor ----^ + ......---------> 1K ohm resistor ----^ + + GB: OE_CHAIN: - This signal is sent to the color LUT PROM that handles the output + from the tile and sprite generators. It forces the PROM outputs to + go high impedance when the signal is high. + + + THEORY OF OPERATION: + -------------------- + + The operation of the 05xx starfield generator is fairly simple, but subject to a + fairly complex clocking scheme when scrolling is taken into account. + + The 05xx is designed as a beam-following color generator much like other starfield + generators in games like Galaxian, etc. It uses an internal LFSR to function + a pseudo-random number generator, and it runs one step for every pixel clock except + when gated by the LFSR_RUN, SCROLL and _STARCLR input signals. + + The LFSR is a 16-bit fibonacci-style shift register with taps at 16,13, 11, and 6; + producing a maximal sequence of 65,535 steps before starting over. It is run + over a 256x256 pixel portion of the video frame, and does not reset with each new + frame. Since the sequence period is 65,535 steps, and the pixel field is 65,536 steps, + the generated starfield will scroll in the -X direction 1 pixel per frame. To make the + starfield stationary, the LFSR must skip 1 pixel clock per frame. In fact all + scrolling is achieved by either running the LFSR extra steps during the blanking + interval, or skipping pixel clocks. + + The individual stars are generated by a logic function that looks at the state of the + LFSR and triggers based on the value of certain bits within the state. When the bits + match pre-determined values (a "hit"), a logic function is applied to the remaining + bits to generate both a starfield set value and a color value. If the starfield value + matches an active set, the color value is applied the RGB outputs for the duration of + the pixel clock, and the OE_CHAIN signal is raised to block the output from the sprite + and tilemap video sections. + + An LFSR hit is detected when the following is true: + + bits 14, 13, 12, and 11 = 1 + bits 15, 9, 4, and 2 = 0 + + LFSR state: Bf Be Bd Bc - Bb Ba B9 B8 - B7 B6 B5 B4 - B3 B2 B1 B0 + | | | | | | | | | | | | | | | | + and'ed with: 1 1 1 1 1 0 1 0 0 0 0 1 0 1 0 0 (FA14) + | | | | | | | | | | | | | | | | + equals: 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 (7800) + + Therefore an LFSR hit looks like this: + + 0 1 1 1 - 1 Ba 0 B8 - B7 B6 B5 0 - B4 0 B1 B0 + + By limiting LFSR hits to only those values where 8 of the 16 bits match a specific + value, only 256 hits will be detected per LFSR period, and thus 256 possible stars. + Because the hit detection values contain at least one positive bit, it avoids the + issue where an LFSR never reaches the all-zeroes state. + + Once a hit is detected, set and color values are extracted from the remaining 8 + bits as follows: + + star set value = (Ba B8)b [0..3] + + color value (BBGGRR) = (!B4 !B1 !B0 !B7 !B6 !B5)b [0..63] + + + NOTE: -------------------------------------------------------------------------- + + The Fibonacci LFSR described above was found to produce the right star + colors and positions with the least complex hit detection and color + decoding logic. It is not, however, the only way to produce the same + sequence. + + Wolfgang Scherr over at pin4.at reverse-engineered the 05xx with the + aim of creating an FPGA replacement for the NAmco original 05xx. He + produced a VHDL implementation that was later translated into a + MAME-suitable C++ implementation by Jindřich Makovička. + + Both of these implementations used the Galois form of the Fibonacci + LFSR described above (and thus the same taps). The Galois form is + slightly more efficient to implement than the Fibonacci form, and + produces the same output bit stream. However, the internal state + sequence of the Galois form is different, and therefore requires + a different set of hit and decode logic. It was found that the + logic required for the Galois form was much more complicated than + the Fibonacci form, which is why the latter is used in this + implementation. + + --------------------------------------------------------------------------- + + + NUMBER OF STARS ON SCREEN: + -------------------------- + + As stated before, during any frame only 2 of the 4 banks of 64 stars will be displayed, + for a maximum of 128 stars. This maximum is reduced to 126 by the fact that 1 star in + every bank has the color 0x00, or completely black and therefore invisible on screen. + Additionally, some stars are generated outside the vertical boundaries of the + visible portion of the screen and are also invisible on screen, making the total + number of stars on screen at any one time less than 126. + + The durations of the horizontal and vertical sync pulses for Galaga and Bosconian allow + for a frame size of 352 x 256. The LFSR_RUN signal pulse sits inside _HSYNC signal pulse + between pixel clock cycles 56 and 311, thus limiting the size of the frame where stars + are generated to 256 x 256: + + 0 56 311 351 + 0 +---------+===============================================+------+ + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | Starfield generation area: | | + | | (56,0) -> (311,255) [256x256] | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + 255 +---------+===============================================+------+ + + The _OE signal restricts the visible area of the video frame. The _OE signal pulse sits + between pixel clock cycles 40 and 327, limiting the visible horizontal dimension to + 288 pixels. Also, the _OE signal is only generated between lines 24 and 247, limiting + the vertical dimension to 224 lines, hence the final output resolution of 288 x 224: + + 0 56 311 351 + 0 +---------+===============================================+------+ + | | | | + | 40 | | 327 | + | 24 +--+-----------------------------------------------+--+ | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | Starfield visible area: | | | + | | | (56,24) -> (311,247) [256x224] | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | 247 +--+-----------------------------------------------+--+ | + | | | | + 255 +---------+===============================================+------+ + + In this manner, all stars generated in the starfield generation area that fall outside + the starfield visible area are not shown on screen. + + + HORIZONTAL SCROLLING: + --------------------- + + Horizontal and vertical scrolling of the starfield is accomplished by either advancing + the LFSR or delaying the advance for some number of pixel clocks per frame + + For horizontal scrolling, advancing the LFSR an additional Z number of clocks results + in a starfield shift in the negative X direction of Z+1 pixels, while delaying the LFSR + advance results in shift in the positive X direction of Z-1 pixels. The scrolling + amount and direction is controlled by the SCROLL_X input signals as follows: + + SCROLL_X LFSR ACTION / FRAME NUM LFSR ADVs / FRAME X SHIFT RESULT + -------------------------------------------------------------------------------- + 000 nothing 256*256 + 0 = 65536 -1 pixels + 001 Advance 1 extra 256*256 + 1 = 65537 -2 pixels + 010 Advance 2 extra 256*256 + 2 = 65538 -3 pixels + 011 Advance 3 extra 256*256 + 3 = 65539 -4 pixels + 100 Delay 4 clock 256*256 - 4 = 65532 +3 pixels + 101 Delay 3 clocks 256*256 - 3 = 65533 +2 pixels + 110 Delay 2 clocks 256*256 - 2 = 65534 +1 pixels + 111 Delay 1 clocks 256*256 - 1 = 65535 No X Shift + + NOTE: The advance or delay of the LFSR occurs at pixel clock 0x500 (1280) after + the start of each frame. This is during the non-visible portion of starfield + generation. + + + VERTICAL SCROLLING: + ------------------- + + For vertical scrolling, advancing the LFSR an additional (Z * the horizontal frame + size) number of clocks results in a starfield shift in the negative Y direction of Z+1 + lines, while delaying the LFSR advance results in shift in the positive Y direction of + Z-1 pixels. The scrolling amount and direction is controlled by the SCROLL_Y input + signals as follows: + + SCROLL_Y LFSR ACTION / FRAME NUM LFSR ADVs / FRAME Y SHIFT RESULT + -------------------------------------------------------------------------------- + 000 nothing 256*256 + 0 = 65536 No Y Shift + 001 Advance 1*256 extra 256*256 + 256 = 65792 -1 lines + 010 Advance 2*256 extra 256*256 + 512 = 66048 -2 lines + 011 Advance 3*256 extra 256*256 + 768 = 66304 -3 lines + 100 Delay 4*256 clock 256*256 - 1024 = 64512 +4 lines + 101 Delay 3*256 clocks 256*256 - 768 = 64768 +3 lines + 110 Delay 2*256 clocks 256*256 - 512 = 65024 +2 lines + 111 Delay 1*256 clocks 256*256 - 256 = 65280 +1 lines + + Vertical scrolling is accomplished in much the same way as horizontal scrolling in that + the primary difference between the two is that for vertical scrolling we have to + advance or delay the LFSR in multiples of the horizontal starfield frame size to + effectively scroll a single vertical line. Also, the points at which these delays + or advances occur are not at the same point in every case. + + For example, the default SCROLL_Y value of 000 equates to no Y scrolling. In this case + the LFSR is run for 256 pixel clocks per line starting at line 2 within the frame and + ending at line 257, for a total of 256*256 = 65,526 pixel clocks. The start and end + lines for other SCROLL_Y values are different. The following table lists the lines over + which the LFSR is run (keeping in mind that running the LFSR over a line means 256 LFSR + advances): + + SCROLL_Y SKIP PRE_VIS VISIBLE POST_VIS TOTAL_LINES + ------------------------------------------------------------------------------------- + 000 2 [2..23] (22) [24..247] (224) [248..257] (10) (256) + 001 1 [1..23] (23) [24..247] (224) [248..257] (10) (257) + 010 2 [2..23] (22) [24..247] (224) [248..259] (12) (258) + 011 1 [1..23] (23) [24..247] (224) [248..259] (12) (259) + 100 5 [5..23] (19) [24..247] (224) [248..256] (9) (252) + 101 4 [4..23] (20) [24..247] (224) [248..256] (9) (253) + 110 4 [4..23] (20) [24..247] (224) [248..257] (10) (254) + 111 2 [2..23] (22) [24..247] (224) [248..256] (9) (255) + + NOTE: SKIP = number of lines that are skipped at the start of the frame before + running the LFSR + PRE_VIS, VISIBLE, POST_VIS = line range of LFSR runs during pre-visible, + visible, and post-visible potions of frame + TOTAL_LINES = Total number of lines over which the LFSR is run + + NOTE2: Lines are indexed from 0 + Lines > 255 are inside the vertical blanking interval + + + + _STARCLR AND THE LFSR SEED: + ---------------------------------------- + + The _STARCLR input signal is used to stop the LFSR from running (and therefore + generating any stars), and to reset its internal state. At no other time is the + internal state of the LFSR reset. When the _STARCLR signal is held low, the + LFSR_RUN signal is blocked, and the internal state of the LFSR is loaded with + it's seed value, which is 0x7FFF. + + NOTE: Testing with a Galaga machine reveals that during normal operation, the + _STARCLR signal is held low at power on and then raised when the initial + attract screen displays. The low-to-high transition arrives approximately + 14,191 starfield pixel clocks in to the first frame of attract screen animation, + resulting in a starfield that is half-filled. There is no real way to simulate + this in the MAME galaga video driver as it fills the starfield between frames + rather than mid-frame. + +***************************************************************************/ + + + + + +#include "emu.h" +#include "starfield_05xx.h" + + +DEFINE_DEVICE_TYPE(STARFIELD_05XX, starfield_05xx_device, "starfield_05xx_stars", "Galaga/Bosconian starfield") + + + + +#define STARS_COLOR_BASE (64*4+64*4) + +#define VISIBLE_LINES 224 +#define STARFIELD_PIXEL_WIDTH 256 + +#define LFSR_CYCLES_PER_LINE 256 +#define LFSR_HIT_MASK 0xFA14 +#define LFSR_HIT_VALUE 0x7800 +#define LFSR_SEED 0x7FFF + + +static const int speed_X_cycle_count_offset[] = +{ + 0, 1, 2, 3, -4, -3, -2, -1 +}; + +static const int pre_vis_cycle_count_values[] = +{ + 22 * LFSR_CYCLES_PER_LINE, + 23 * LFSR_CYCLES_PER_LINE, + 22 * LFSR_CYCLES_PER_LINE, + 23 * LFSR_CYCLES_PER_LINE, + 19 * LFSR_CYCLES_PER_LINE, + 20 * LFSR_CYCLES_PER_LINE, + 20 * LFSR_CYCLES_PER_LINE, + 22 * LFSR_CYCLES_PER_LINE +}; + +static const int post_vis_cycle_count_values[] = +{ + 10 * LFSR_CYCLES_PER_LINE, + 10 * LFSR_CYCLES_PER_LINE, + 12 * LFSR_CYCLES_PER_LINE, + 12 * LFSR_CYCLES_PER_LINE, + 9 * LFSR_CYCLES_PER_LINE, + 9 * LFSR_CYCLES_PER_LINE, + 10 * LFSR_CYCLES_PER_LINE, + 9 * LFSR_CYCLES_PER_LINE +}; + + + + +starfield_05xx_device::starfield_05xx_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, STARFIELD_05XX, tag, owner, clock) + , m_enable(0) + , m_lfsr(LFSR_SEED) + , m_pre_vis_cycle_count(0) + , m_post_vis_cycle_count(0) + , m_set_a(0) + , m_set_b(0) + , m_offset_x(0) + , m_offset_y(0) + , m_limit_x(0) + +{ +} + + + +void starfield_05xx_device::enable_starfield(uint8_t on) +{ + if (!on) m_lfsr = LFSR_SEED; + + m_enable = on ? 1 : 0; +} + + + +void starfield_05xx_device::set_scroll_speed(uint8_t index_x, uint8_t index_y) +{ + // Set initial pre- and post- visible cycle counts based on vertical + // scroll registers + m_pre_vis_cycle_count = pre_vis_cycle_count_values[index_y]; + m_post_vis_cycle_count = post_vis_cycle_count_values[index_y]; + + // X scrolling occurs during pre-visible portion, so adjust + // pre-visible cycle count to based on horizontal scroll registers + m_pre_vis_cycle_count += speed_X_cycle_count_offset[index_x]; +} + + +void starfield_05xx_device::set_active_starfield_sets(uint8_t set_a, uint8_t set_b) +{ + // Set active starfield sets based on starfield select registers + m_set_a = set_a; + m_set_b = set_b; +} + + +void starfield_05xx_device::set_starfield_config(uint16_t off_x, uint16_t off_y, uint16_t lim_x) +{ + // Set X and Y starfield position offsets + m_offset_x = off_x; + m_offset_y = off_y; + + // Set X range limit + m_limit_x = lim_x; +} + + +uint16_t starfield_05xx_device::get_next_lfsr_state(uint16_t lfsr) +{ + uint16_t bit; + + // 16-bit FIBONACCI-style LFSR with taps at 16,13,11, and 6 + // These taps produce a maximal sequence of 65,535 steps. + + bit = ((lfsr >> 0) ^ (lfsr >> 3) ^ (lfsr >> 5) ^ (lfsr >> 10)); + lfsr = (lfsr >> 1) | (bit << 15); + + return lfsr; +} + + +void starfield_05xx_device::draw_starfield(bitmap_ind16 &bitmap, const rectangle &cliprect, int flip) +{ + uint16_t pre_vis_cycle_count = m_pre_vis_cycle_count; + uint16_t post_vis_cycle_count = m_post_vis_cycle_count; + + if (m_enable) + { + int x,y; + + // Advance the LFSR during the pre-visible portion of the frame + do { m_lfsr = get_next_lfsr_state(m_lfsr); } while (--pre_vis_cycle_count); + + // Now we are in visible portion of the frame - Output all LFSR hits here + for (y = m_offset_y; y < VISIBLE_LINES + m_offset_y; y++) + { + for (x = m_offset_x; x < STARFIELD_PIXEL_WIDTH + m_offset_x; x++) + { + // Check lfsr for hit + if ((m_lfsr&LFSR_HIT_MASK) == LFSR_HIT_VALUE) + { + uint8_t star_set = bitswap<2>(m_lfsr, 10, 8); + + if ((m_set_a == star_set) || (m_set_b == star_set)) + { + // don't draw the stars that are beyond the X limit + if (x < m_limit_x) + { + int dx = x; + + if (flip) dx += 64; + + if (cliprect.contains(dx, y)) + { + uint8_t color; + + color = (m_lfsr>>5)&0x7; + color |= (m_lfsr<<3)&0x18; + color |= (m_lfsr<<2)&0x20; + color = (~color)&0x3F; + + bitmap.pix16(y, dx) = STARS_COLOR_BASE + color; + } + } + } + } + + // Advance LFSR + m_lfsr = get_next_lfsr_state(m_lfsr); + } + } + + // Advance the LFSR during the post-visible portion of the frame + do { m_lfsr = get_next_lfsr_state(m_lfsr); } while (--post_vis_cycle_count); + } +} + + + +void starfield_05xx_device::device_start() +{ + save_item(NAME(m_enable)); + save_item(NAME(m_lfsr)); + save_item(NAME(m_pre_vis_cycle_count)); + save_item(NAME(m_post_vis_cycle_count)); + save_item(NAME(m_set_a)); + save_item(NAME(m_set_b)); + + save_item(NAME(m_offset_x)); + save_item(NAME(m_offset_y)); + save_item(NAME(m_limit_x)); +} + + + +void starfield_05xx_device::device_reset() +{ + m_enable = 0; + m_lfsr = LFSR_SEED; + m_pre_vis_cycle_count = 0; + m_post_vis_cycle_count = 0; + m_set_a = 0; + m_set_b = 0; +} diff --git a/src/mame/video/starfield_05xx.h b/src/mame/video/starfield_05xx.h new file mode 100644 index 00000000000..805e16c1cc4 --- /dev/null +++ b/src/mame/video/starfield_05xx.h @@ -0,0 +1,40 @@ +// license:BSD-3-Clause +// copyright-holders:Robert Hildinger +#ifndef MAME_VIDEO_STARFIELD_05XX_H +#define MAME_VIDEO_STARFIELD_05XX_H + +// used by galaga, bosconian, and their various clones +class starfield_05xx_device : public device_t +{ +public: + starfield_05xx_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + + void enable_starfield(uint8_t on); + void set_scroll_speed(uint8_t index_x, uint8_t index_y); + void set_active_starfield_sets(uint8_t set_a, uint8_t set_b); + void set_starfield_config(uint16_t off_x, uint16_t off_y, uint16_t lim_x); + void draw_starfield(bitmap_ind16 &bitmap, const rectangle &cliprect, int flip); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + uint16_t get_next_lfsr_state(uint16_t lfsr); + + uint8_t m_enable; + uint16_t m_lfsr; + uint16_t m_pre_vis_cycle_count; + uint16_t m_post_vis_cycle_count; + uint8_t m_set_a; + uint8_t m_set_b; + + uint16_t m_offset_x; + uint16_t m_offset_y; + uint16_t m_limit_x; +}; + + +DECLARE_DEVICE_TYPE(STARFIELD_05XX, starfield_05xx_device) + +#endif // MAME_VIDEO_STARFIELD_05XX_H