summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Robert Hildinger <rdh_pub@me.com>2019-11-03 03:16:45 -0600
committer Vas Crabb <cuavas@users.noreply.github.com>2019-11-03 20:16:45 +1100
commit74ade68194006625397f4ef8aa1fdf223d633ba3 (patch)
treee5b12ef937d27d4f0304e91a6ff4d2a7f622c4b2
parent3cc8a8558057991df4da0c3ace1966ce59c64f0b (diff)
galaga: Implement accurate starfield based on reverse eng. 05xx (#5824)
* galaga: Implement accurate starfield based on reverse eng. 05xx ---------------------------------------------------------------- This commit implements an accurate starfield for Galaga and Bosconian based on reverse engineering the Namco 05xx chip. Documentation and notes have been added inline to the code for the galaga video driver. Changes in this commit: 1. Regenerates starfield colors based on LFSR state for every frame 2. Pixel accurate to Namco original 05xx 3. Restores one line horizontal starfield shift for every 256 vertical pixel shifts 4. Properly handles LFSR reset (_STARCLR) 5. Restores missing 4 stars from previous implementations 6. Fixes potential issue with the number of stars on screen at any one time 7. Restores 4 pixel/line scrolling capability (not currently used for any driver) * galaga: inplement accurate starfield - change request 1 ------------------------------------------------------- 1. Remove stdlib.h from src/mame/video/galaga.cpp * galaga: inplement accurate starfield - change request 2 ------------------------------------------------------- 1. Refactor starfield generator into stand-alone video device 2. Simplify Bosconian and Galaga video drivers 3. Add in documentation about different LFSR form used by Wolfgang Scherr and Jindřich Makovička. * galaga: inplement accurate starfield - change request 3 --------------------------------------------------------- Minor changes from pull request review
-rw-r--r--scripts/target/mame/arcade.lua2
-rw-r--r--src/mame/drivers/galaga.cpp32
-rw-r--r--src/mame/includes/bosco.h2
-rw-r--r--src/mame/includes/galaga.h20
-rw-r--r--src/mame/video/bosco.cpp57
-rw-r--r--src/mame/video/galaga.cpp433
-rw-r--r--src/mame/video/starfield_05xx.cpp680
-rw-r--r--src/mame/video/starfield_05xx.h40
8 files changed, 773 insertions, 493 deletions
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<screen_device> m_screen;
required_device<palette_device> m_palette;
output_finder<2> m_leds;
+ optional_device<starfield_05xx_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_starfield->set_active_starfield_sets(m_videolatch->q4_r(), m_videolatch->q5_r() | 2);
- m_stars_scrollx += speedsx[m_bosco_starcontrol[0] & 0x07];
- m_stars_scrolly += speedsy[(m_bosco_starcontrol[0] & 0x38) >> 3];
+ // _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}
-};
-
-
/***************************************************************************
@@ -380,100 +93,6 @@ void galaga_state::galaga_palette(palette_device &palette) const
/***************************************************************************
- 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