summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/atari/starshp1.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/atari/starshp1.cpp')
-rw-r--r--src/mame/atari/starshp1.cpp699
1 files changed, 617 insertions, 82 deletions
diff --git a/src/mame/atari/starshp1.cpp b/src/mame/atari/starshp1.cpp
index 3b6b1436816..0f676d253f6 100644
--- a/src/mame/atari/starshp1.cpp
+++ b/src/mame/atari/starshp1.cpp
@@ -1,5 +1,6 @@
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo, Stefan Jokisch
+
/***************************************************************************
Atari Starship 1 driver
@@ -7,26 +8,538 @@ Atari Starship 1 driver
"starshp1" -> regular version, bonus time for 3500 points
"starshpp" -> possible prototype, bonus time for 2700 points
+ * The schematics don't seem to make a lot of sense when it
+ * comes to the video timing chain::
+ *
+ * * there are clear typos -- what is H132???
+ * * there are two HBLANK/HSYNC periods as the horizontal
+ * chain is drawn, which would give an alternating long
+ * line followed by a much shorter one. This cannot be right
+ * * the carry-out/load circuit on LS161@J8 is superfluous
+ *
+ * These values also give a frame rate of about 45Hz, which is
+ * probably too low. I suspect that screen is not really
+ * 512 pixels wide -- most likely 384, which would give 60Hz
+ *
+ * Based on photographs of the PCB, and analysis of videos of
+ * actual gameplay, the horizontal screen really is 384 clocks.
+ *
+ * However, some of the graphics, like the starfield, are
+ * clocked with the 12MHz signal. This effectively doubles
+ * the horizontal resolution:
+ *
+ * 6.048Mhz clocks 12.096Mhz clocks
+ * Horizontal Visible Area 384 (0x180) 768 (0x300)
+ * Horizontal Blanking Time 128 (0x080) 256 (0x100)
+
***************************************************************************/
#include "emu.h"
-#include "starshp1.h"
+
+#include "starshp1_a.h"
#include "cpu/m6502/m6502.h"
#include "machine/74259.h"
+#include "sound/discrete.h"
+#include "emupal.h"
+#include "screen.h"
#include "speaker.h"
+#include "tilemap.h"
+
+
+namespace {
+
+class starshp1_state : public driver_device
+{
+public:
+ starshp1_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_discrete(*this, "discrete"),
+ m_gfxdecode(*this, "gfxdecode"),
+ m_screen(*this, "screen"),
+ m_palette(*this, "palette"),
+ m_playfield_ram(*this, "playfield_ram"),
+ m_hpos_ram(*this, "hpos_ram"),
+ m_vpos_ram(*this, "vpos_ram"),
+ m_obj_ram(*this, "obj_ram"),
+ m_system(*this, "SYSTEM"),
+ m_playtime(*this, "PLAYTIME"),
+ m_stick(*this, "STICK%c", 'X')
+ { }
+
+ void starshp1(machine_config &config);
+
+ DECLARE_CUSTOM_INPUT_MEMBER(analog_r);
+ DECLARE_CUSTOM_INPUT_MEMBER(collision_latch_r);
+
+protected:
+ virtual void machine_start() override;
+ virtual void video_start() override;
+
+private:
+ required_device<cpu_device> m_maincpu;
+ required_device<discrete_device> m_discrete;
+ required_device<gfxdecode_device> m_gfxdecode;
+ required_device<screen_device> m_screen;
+ required_device<palette_device> m_palette;
+
+ required_shared_ptr<uint8_t> m_playfield_ram;
+ required_shared_ptr<uint8_t> m_hpos_ram;
+ required_shared_ptr<uint8_t> m_vpos_ram;
+ required_shared_ptr<uint8_t> m_obj_ram;
+
+ required_ioport m_system;
+ required_ioport m_playtime;
+ required_ioport_array<2> m_stick;
+
+ uint8_t m_analog_in_select = 0;
+ uint8_t m_attract = 0;
+ uint8_t m_ship_explode = 0;
+ uint8_t m_ship_picture = 0;
+ uint8_t m_ship_hoffset = 0;
+ uint8_t m_ship_voffset = 0;
+ uint8_t m_ship_size = 0;
+ uint8_t m_circle_hpos = 0;
+ uint8_t m_circle_vpos = 0;
+ uint8_t m_circle_size = 0;
+ uint8_t m_circle_mod = 0;
+ uint8_t m_circle_kill = 0;
+ uint8_t m_phasor = 0;
+ uint8_t m_collision_latch = 0;
+ uint8_t m_starfield_kill = 0;
+ uint8_t m_mux = 0;
+ uint8_t m_inverse = 0;
+ std::unique_ptr<uint16_t[]> m_lsfr;
+ bitmap_ind16 m_helper;
+ tilemap_t *m_bg_tilemap = nullptr;
+
+ void collision_reset_w(uint8_t data);
+ void analog_in_w(offs_t offset, uint8_t data);
+ void ship_explode_w(int state);
+ void circle_mod_w(int state);
+ void circle_kill_w(int state);
+ void starfield_kill_w(int state);
+ void inverse_w(int state);
+ void mux_w(int state);
+ uint8_t rng_r();
+ void ssadd_w(offs_t offset, uint8_t data);
+ void sspic_w(uint8_t data);
+ void playfield_w(offs_t offset, uint8_t data);
+ void attract_w(int state);
+ void phasor_w(int state);
+ void analog_out_w(offs_t offset, uint8_t data);
+ TILE_GET_INFO_MEMBER(get_tile_info);
+ void palette(palette_device &palette) const;
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void screen_vblank(int state);
+ INTERRUPT_GEN_MEMBER(interrupt);
+ void set_pens();
+ void draw_starfield(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ int get_sprite_hpos(int i);
+ int get_sprite_vpos(int i);
+ void draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void draw_spaceship(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void draw_phasor(bitmap_ind16 &bitmap);
+ int get_radius();
+ int get_circle_hpos();
+ int get_circle_vpos();
+ void draw_circle_line(bitmap_ind16 &bitmap, int x, int y, int l);
+ void draw_circle(bitmap_ind16 &bitmap);
+ int spaceship_collision(bitmap_ind16 &bitmap, const rectangle &rect);
+ int point_in_circle(int x, int y, int center_x, int center_y, int r);
+ int circle_collision(const rectangle &rect);
+ void program_map(address_map &map);
+};
+
+
+void starshp1_state::set_pens()
+{
+ m_palette->set_indirect_color(m_inverse ? 7 : 0, rgb_t(0x00, 0x00, 0x00));
+ m_palette->set_indirect_color(m_inverse ? 6 : 1, rgb_t(0x1e, 0x1e, 0x1e));
+ m_palette->set_indirect_color(m_inverse ? 5 : 2, rgb_t(0x4e, 0x4e, 0x4e));
+ m_palette->set_indirect_color(m_inverse ? 4 : 3, rgb_t(0x6c, 0x6c, 0x6c));
+ m_palette->set_indirect_color(m_inverse ? 3 : 4, rgb_t(0x93, 0x93, 0x93));
+ m_palette->set_indirect_color(m_inverse ? 2 : 5, rgb_t(0xb1, 0xb1, 0xb1));
+ m_palette->set_indirect_color(m_inverse ? 1 : 6, rgb_t(0xe1, 0xe1, 0xe1));
+ m_palette->set_indirect_color(m_inverse ? 0 : 7, rgb_t(0xff, 0xff, 0xff));
+}
+
+
+void starshp1_state::palette(palette_device &palette) const
+{
+ static constexpr uint16_t colortable_source[] =
+ {
+ 0, 3, // 0x00 - 0x01 - alpha numerics
+ 0, 2, // 0x02 - 0x03 - sprites (Z=0)
+ 0, 5, // 0x04 - 0x05 - sprites (Z=1)
+ 0, 2, 4, 6, // 0x06 - 0x09 - spaceship (EXPLODE=0)
+ 0, 6, 6, 7, // 0x0a - 0x0d - spaceship (EXPLODE=1)
+ 5, 2, // 0x0e - 0x0f - star field
+ 7, // 0x10 - phasor
+ 5, 7 // 0x11 - circle
+ };
+
+ for (unsigned i = 0; i < std::size(colortable_source); i++)
+ palette.set_pen_indirect(i, colortable_source[i]);
+}
+
+
+TILE_GET_INFO_MEMBER(starshp1_state::get_tile_info)
+{
+ uint8_t const code = m_playfield_ram[tile_index];
+
+ tileinfo.set(0, code & 0x3f, 0, 0);
+}
+
+
+void starshp1_state::video_start()
+{
+ m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(starshp1_state::get_tile_info)), TILEMAP_SCAN_ROWS, 16, 8, 32, 8);
+ m_bg_tilemap->set_transparent_pen(0);
+ m_bg_tilemap->set_scrollx(0, -8);
+
+ m_lsfr = std::make_unique<uint16_t[]>(0x10000);
+
+ uint16_t val = 0;
+ for (int i = 0; i < 0x10000; i++)
+ {
+ int bit = (val >> 0xf) ^
+ (val >> 0xc) ^
+ (val >> 0x7) ^
+ (val >> 0x1) ^ 1;
+
+ m_lsfr[i] = val;
+
+ val = (val << 1) | (bit & 1);
+ }
+
+ m_screen->register_screen_bitmap(m_helper);
+}
+
+
+uint8_t starshp1_state::rng_r()
+{
+ int const width = m_screen->width();
+ int const height = m_screen->height();
+ int x = m_screen->hpos();
+ int y = m_screen->vpos();
+
+ /* the LFSR is only running in the non-blank region
+ of the screen, so this is not quite right */
+ if (x > width - 1)
+ x = width - 1;
+ if (y > height - 1)
+ y = height - 1;
+
+ return m_lsfr[x + (uint16_t) (512 * y)];
+}
+
+
+void starshp1_state::ssadd_w(offs_t offset, uint8_t data)
+{
+ /*
+ * The range of sprite position values doesn't suffice to
+ * move the zoomed spaceship sprite over the top and left
+ * edges of the screen. These additional values are used
+ * to compensate for this. Technically, they cut off the
+ * first columns and rows of the spaceship sprite, but in
+ * practice they work like offsets in zoomed pixels.
+ */
+
+ m_ship_voffset = ((offset & 0xf0) >> 4);
+ m_ship_hoffset = ((offset & 0x0f) << 2) | (data & 3);
+}
+
+
+void starshp1_state::sspic_w(uint8_t data)
+{
+ /*
+ * Some mysterious game code at address $2CCE causes
+ * erratic images in the target explosion sequence. But
+ * this is the way the actual game worked!
+ */
+
+ m_ship_picture = data;
+}
+
+
+void starshp1_state::playfield_w(offs_t offset, uint8_t data)
+{
+ if (m_mux != 0)
+ {
+ offset ^= 0x1f;
+ m_playfield_ram[offset] = data;
+ m_bg_tilemap->mark_tile_dirty(offset);
+ }
+}
+
+
+void starshp1_state::draw_starfield(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ /*
+ * The LSFR is reset once per frame at the position of
+ * sprite 15. This behavior is quite pointless and not
+ * really needed by the game. Not emulated.
+ */
+
+ for (int y = 0; y <= cliprect.bottom(); y++)
+ {
+ uint16_t const *const p = m_lsfr.get() + (uint16_t) (512 * y);
+ uint16_t *const pLine = &bitmap.pix(y);
+
+ for (int x = 0; x <= cliprect.right(); x++)
+ if ((p[x] & 0x5b56) == 0x5b44)
+ pLine[x] = (p[x] & 0x0400) ? 0x0e : 0x0f;
+ }
+}
+
+
+int starshp1_state::get_sprite_hpos(int i)
+{
+ return 2 * (m_hpos_ram[i] ^ 0xff);
+}
+int starshp1_state::get_sprite_vpos(int i)
+{
+ return 1 * (m_vpos_ram[i] - 0x07);
+}
+
+
+void starshp1_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ for (int i = 0; i < 14; i++)
+ {
+ int const code = (m_obj_ram[i] & 0xf) ^ 0xf;
+
+ m_gfxdecode->gfx(1)->transpen(bitmap, cliprect,
+ code % 8,
+ code / 8,
+ 0, 0,
+ get_sprite_hpos(i),
+ get_sprite_vpos(i), 0);
+ }
+}
+
+
+void starshp1_state::draw_spaceship(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ double scaler = -5 * log(1 - m_ship_size / 256.0); /* ? */
+
+ unsigned xzoom = 2 * 0x10000 * scaler;
+ unsigned yzoom = 1 * 0x10000 * scaler;
+
+ int x = get_sprite_hpos(14);
+ int y = get_sprite_vpos(14);
+
+ if (x <= 0)
+ x -= (xzoom * m_ship_hoffset) >> 16;
+
+ if (y <= 0)
+ y -= (yzoom * m_ship_voffset) >> 16;
+
+ m_gfxdecode->gfx(2)->zoom_transpen(bitmap, cliprect,
+ m_ship_picture & 0x03,
+ m_ship_explode,
+ m_ship_picture & 0x80, 0,
+ x, y,
+ xzoom, yzoom, 0);
+}
+void starshp1_state::draw_phasor(bitmap_ind16 &bitmap)
+{
+ for (int i = 128; i < 240; i++)
+ if (i >= get_sprite_vpos(13))
+ {
+ bitmap.pix(i, 2 * i + 0) = 0x10;
+ bitmap.pix(i, 2 * i + 1) = 0x10;
+ bitmap.pix(i, 2 * (255 - i) + 0) = 0x10;
+ bitmap.pix(i, 2 * (255 - i) + 1) = 0x10;
+ }
+}
+
+
+int starshp1_state::get_radius()
+{
+ return 6 * sqrt((double)m_circle_size); // size calibrated by hand
+}
+int starshp1_state::get_circle_hpos()
+{
+ return 2 * (3 * m_circle_hpos / 2 - 64);
+}
+int starshp1_state::get_circle_vpos()
+{
+ return 1 * (3 * m_circle_vpos / 2 - 64);
+}
+
-INTERRUPT_GEN_MEMBER(starshp1_state::starshp1_interrupt)
+void starshp1_state::draw_circle_line(bitmap_ind16 &bitmap, int x, int y, int l)
{
- if ((ioport("SYSTEM")->read() & 0x90) != 0x90)
+ if (y >= 0 && y <= bitmap.height() - 1)
+ {
+ uint16_t const *const p = m_lsfr.get() + uint16_t(512 * y);
+
+ uint16_t *const pLine = &bitmap.pix(y);
+
+ int h1 = x - 2 * l;
+ int h2 = x + 2 * l;
+
+ if (h1 < 0)
+ h1 = 0;
+ if (h2 > bitmap.width() - 1)
+ h2 = bitmap.width() - 1;
+
+ for (x = h1; x <= h2; x++)
+ if (m_circle_mod)
+ {
+ if (p[x] & 1)
+ pLine[x] = 0x11;
+ }
+ else
+ pLine[x] = 0x12;
+ }
+}
+
+
+void starshp1_state::draw_circle(bitmap_ind16 &bitmap)
+{
+ int const cx = get_circle_hpos();
+ int const cy = get_circle_vpos();
+
+ int x = 0;
+ int y = get_radius();
+
+ // Bresenham's circle algorithm
+
+ int d = 3 - 2 * get_radius();
+
+ while (x <= y)
+ {
+ draw_circle_line(bitmap, cx, cy - x, y);
+ draw_circle_line(bitmap, cx, cy + x, y);
+ draw_circle_line(bitmap, cx, cy - y, x);
+ draw_circle_line(bitmap, cx, cy + y, x);
+
+ x++;
+
+ if (d < 0)
+ d += 4 * x + 6;
+ else
+ d += 4 * (x - y--) + 10;
+ }
+}
+
+
+int starshp1_state::spaceship_collision(bitmap_ind16 &bitmap, const rectangle &rect)
+{
+ for (int y = rect.top(); y <= rect.bottom(); y++)
+ {
+ uint16_t const *const pLine = &m_helper.pix(y);
+
+ for (int x = rect.left(); x <= rect.right(); x++)
+ if (pLine[x] != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int starshp1_state::point_in_circle(int x, int y, int center_x, int center_y, int r)
+{
+ int const dx = abs(x - center_x) / 2;
+ int const dy = abs(y - center_y) / 1;
+
+ return dx * dx + dy * dy < r * r;
+}
+
+
+int starshp1_state::circle_collision(const rectangle &rect)
+{
+ int const center_x = get_circle_hpos();
+ int const center_y = get_circle_vpos();
+
+ int const r = get_radius();
+
+ return point_in_circle(rect.left(), rect.top(), center_x, center_y, r) ||
+ point_in_circle(rect.left(), rect.bottom(), center_x, center_y, r) ||
+ point_in_circle(rect.right(), rect.top(), center_x, center_y, r) ||
+ point_in_circle(rect.right(), rect.bottom(), center_x, center_y, r);
+}
+
+
+uint32_t starshp1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ set_pens();
+
+ bitmap.fill(0, cliprect);
+
+ if (m_starfield_kill == 0)
+ draw_starfield(bitmap, cliprect);
+
+ draw_sprites(bitmap, cliprect);
+
+ if (m_circle_kill == 0 && m_circle_mod != 0)
+ draw_circle(bitmap);
+
+ if (m_attract == 0)
+ draw_spaceship(bitmap, cliprect);
+
+ if (m_circle_kill == 0 && m_circle_mod == 0)
+ draw_circle(bitmap);
+
+ rectangle tilemaprect(0, m_bg_tilemap->width(), 0, m_bg_tilemap->height());
+ tilemaprect &= cliprect;
+ m_bg_tilemap->draw(screen, bitmap, tilemaprect, 0, 0);
+
+ if (m_phasor != 0)
+ draw_phasor(bitmap);
+
+ return 0;
+}
+
+
+void starshp1_state::screen_vblank(int state)
+{
+ // rising edge
+ if (state)
+ {
+ const rectangle &visarea = m_screen->visible_area();
+
+ rectangle rect(get_sprite_hpos(13), 0, get_sprite_vpos(13), 0);
+ rect.set_size(m_gfxdecode->gfx(1)->width(), m_gfxdecode->gfx(1)->height());
+
+ rect &= m_helper.cliprect();
+
+ m_helper.fill(0, visarea);
+
+ if (m_attract == 0)
+ draw_spaceship(m_helper, visarea);
+
+ if (circle_collision(visarea))
+ m_collision_latch |= 1;
+
+ if (circle_collision(rect))
+ m_collision_latch |= 2;
+
+ if (spaceship_collision(m_helper, rect))
+ m_collision_latch |= 4;
+
+ if (spaceship_collision(m_helper, visarea))
+ m_collision_latch |= 8;
+ }
+}
+
+
+INTERRUPT_GEN_MEMBER(starshp1_state::interrupt)
+{
+ if ((m_system->read() & 0x90) != 0x90)
device.execute().pulse_input_line(0, device.execute().minimum_quantum_time());
}
-WRITE_LINE_MEMBER(starshp1_state::attract_w)
+void starshp1_state::attract_w(int state)
{
m_attract = state;
m_discrete->write(STARSHP1_ATTRACT, state);
@@ -36,36 +549,36 @@ WRITE_LINE_MEMBER(starshp1_state::attract_w)
}
-WRITE_LINE_MEMBER(starshp1_state::phasor_w)
+void starshp1_state::phasor_w(int state)
{
m_phasor = state;
m_discrete->write(STARSHP1_PHASOR_ON, state);
}
-void starshp1_state::starshp1_collision_reset_w(uint8_t data)
+void starshp1_state::collision_reset_w(uint8_t data)
{
m_collision_latch = 0;
}
-CUSTOM_INPUT_MEMBER(starshp1_state::starshp1_analog_r)
+CUSTOM_INPUT_MEMBER(starshp1_state::analog_r)
{
int val = 0;
switch (m_analog_in_select)
{
case 0:
- val = ioport("STICKY")->read();
+ val = m_stick[1]->read();
break;
case 1:
- val = ioport("STICKX")->read();
+ val = m_stick[0]->read();
break;
case 2:
- val = 0x20; /* DAC feedback, not used */
+ val = 0x20; // DAC feedback, not used
break;
case 3:
- val = ioport("PLAYTIME")->read();
+ val = m_playtime->read();
break;
}
@@ -79,13 +592,13 @@ CUSTOM_INPUT_MEMBER(starshp1_state::collision_latch_r)
}
-void starshp1_state::starshp1_analog_in_w(offs_t offset, uint8_t data)
+void starshp1_state::analog_in_w(offs_t offset, uint8_t data)
{
m_analog_in_select = offset & 3;
}
-void starshp1_state::starshp1_analog_out_w(offs_t offset, uint8_t data)
+void starshp1_state::analog_out_w(offs_t offset, uint8_t data)
{
switch (offset & 7)
{
@@ -114,92 +627,102 @@ void starshp1_state::starshp1_analog_out_w(offs_t offset, uint8_t data)
}
-WRITE_LINE_MEMBER(starshp1_state::ship_explode_w)
+void starshp1_state::ship_explode_w(int state)
{
m_ship_explode = state;
}
-WRITE_LINE_MEMBER(starshp1_state::circle_mod_w)
+void starshp1_state::circle_mod_w(int state)
{
m_circle_mod = state;
}
-WRITE_LINE_MEMBER(starshp1_state::circle_kill_w)
+void starshp1_state::circle_kill_w(int state)
{
m_circle_kill = !state;
}
-WRITE_LINE_MEMBER(starshp1_state::starfield_kill_w)
+void starshp1_state::starfield_kill_w(int state)
{
m_starfield_kill = state;
}
-WRITE_LINE_MEMBER(starshp1_state::inverse_w)
+void starshp1_state::inverse_w(int state)
{
m_inverse = state;
}
-WRITE_LINE_MEMBER(starshp1_state::mux_w)
+void starshp1_state::mux_w(int state)
{
m_mux = state;
}
-WRITE_LINE_MEMBER(starshp1_state::led_w)
-{
- m_led = state ? 0 : 1;
-}
-
-
-void starshp1_state::starshp1_map(address_map &map)
+void starshp1_state::program_map(address_map &map)
{
map(0x0000, 0x00ff).ram().mirror(0x100);
map(0x2c00, 0x3fff).rom();
map(0xa000, 0xa000).portr("SYSTEM");
map(0xb000, 0xb000).portr("VBLANK");
- map(0xc300, 0xc3ff).w(FUNC(starshp1_state::starshp1_sspic_w)); /* spaceship picture */
+ map(0xc300, 0xc3ff).w(FUNC(starshp1_state::sspic_w)); // spaceship picture
map(0xc400, 0xc400).portr("COINAGE");
- map(0xc400, 0xc4ff).w(FUNC(starshp1_state::starshp1_ssadd_w)); /* spaceship address */
- map(0xc800, 0xc8ff).ram().w(FUNC(starshp1_state::starshp1_playfield_w)).share("playfield_ram");
- map(0xcc00, 0xcc0f).writeonly().share("hpos_ram");
- map(0xd000, 0xd00f).writeonly().share("vpos_ram");
- map(0xd400, 0xd40f).writeonly().share("obj_ram");
- map(0xd800, 0xd800).r(FUNC(starshp1_state::starshp1_rng_r));
- map(0xd800, 0xd80f).w(FUNC(starshp1_state::starshp1_collision_reset_w));
+ map(0xc400, 0xc4ff).w(FUNC(starshp1_state::ssadd_w)); // spaceship address
+ map(0xc800, 0xc8ff).ram().w(FUNC(starshp1_state::playfield_w)).share(m_playfield_ram);
+ map(0xcc00, 0xcc0f).writeonly().share(m_hpos_ram);
+ map(0xd000, 0xd00f).writeonly().share(m_vpos_ram);
+ map(0xd400, 0xd40f).writeonly().share(m_obj_ram);
+ map(0xd800, 0xd800).r(FUNC(starshp1_state::rng_r));
+ map(0xd800, 0xd80f).w(FUNC(starshp1_state::collision_reset_w));
map(0xdc00, 0xdc07).mirror(0x0008).w("misclatch", FUNC(f9334_device::write_d0));
- map(0xdd00, 0xdd0f).w(FUNC(starshp1_state::starshp1_analog_in_w));
+ map(0xdd00, 0xdd0f).w(FUNC(starshp1_state::analog_in_w));
map(0xde00, 0xde07).mirror(0x0008).w("audiolatch", FUNC(f9334_device::write_d0));
- map(0xdf00, 0xdf0f).w(FUNC(starshp1_state::starshp1_analog_out_w));
+ map(0xdf00, 0xdf0f).w(FUNC(starshp1_state::analog_out_w));
map(0xf000, 0xffff).rom();
}
void starshp1_state::machine_start()
{
- m_led.resolve();
+ save_item(NAME(m_analog_in_select));
+ save_item(NAME(m_attract));
+ save_item(NAME(m_ship_explode));
+ save_item(NAME(m_ship_picture));
+ save_item(NAME(m_ship_hoffset));
+ save_item(NAME(m_ship_voffset));
+ save_item(NAME(m_ship_size));
+ save_item(NAME(m_circle_hpos));
+ save_item(NAME(m_circle_vpos));
+ save_item(NAME(m_circle_size));
+ save_item(NAME(m_circle_mod));
+ save_item(NAME(m_circle_kill));
+ save_item(NAME(m_phasor));
+ save_item(NAME(m_collision_latch));
+ save_item(NAME(m_starfield_kill));
+ save_item(NAME(m_mux));
+ save_item(NAME(m_inverse));
}
static INPUT_PORTS_START( starshp1 )
PORT_START("SYSTEM")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
- PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* SWA1? */
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) // SWA1?
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN2 )
PORT_DIPNAME( 0x20, 0x20, "Extended Play" )
PORT_DIPSETTING( 0x00, DEF_STR( No ) )
PORT_DIPSETTING( 0x20, DEF_STR( Yes ) )
- PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) // Speed lever - spring-returned to SLOW unless held down for FAST
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Thrust Control") // Speed lever - spring-returned to SLOW unless held down for FAST
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN1 )
PORT_START("VBLANK")
- PORT_BIT( 0x3f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(starshp1_state, starshp1_analog_r) // analog in
+ PORT_BIT( 0x3f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(starshp1_state, analog_r) // analog in
PORT_SERVICE( 0x40, IP_ACTIVE_LOW )
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_VBLANK("screen")
@@ -209,10 +732,10 @@ static INPUT_PORTS_START( starshp1 )
PORT_DIPSETTING( 0x10, DEF_STR( 2C_1C ) )
PORT_DIPSETTING( 0x20, DEF_STR( 1C_1C ) )
PORT_DIPSETTING( 0x40, DEF_STR( 1C_2C ) )
- PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) /* ground */
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // ground
PORT_START("PLAYTIME")
- PORT_DIPNAME( 0x3f, 0x20, "Play Time" ) /* potentiometer */
+ PORT_DIPNAME( 0x3f, 0x20, "Play Time" ) // potentiometer
PORT_DIPSETTING( 0x00, "60 Seconds" )
PORT_DIPSETTING( 0x20, "90 Seconds" )
PORT_DIPSETTING( 0x3f, "120 Seconds" )
@@ -227,9 +750,9 @@ INPUT_PORTS_END
static const gfx_layout tilelayout =
{
- 16, 8, /* 16x8 tiles */
- 64, /* 64 tiles */
- 1, /* 1 bit per pixel */
+ 16, 8, // 16x8 tiles
+ 64, // 64 tiles
+ 1, // 1 bit per pixel
{ 0 },
{
0x204, 0x204, 0x205, 0x205, 0x206, 0x206, 0x207, 0x207,
@@ -239,15 +762,15 @@ static const gfx_layout tilelayout =
0x0000, 0x0400, 0x0800, 0x0c00,
0x1000, 0x1400, 0x1800, 0x1c00
},
- 8 /* step */
+ 8 // step
};
static const gfx_layout spritelayout =
{
- 16, 8, /* 16x8 sprites */
- 8, /* 8 sprites */
- 1, /* 1 bit per pixel */
+ 16, 8, // 16x8 sprites
+ 8, // 8 sprites
+ 1, // 1 bit per pixel
{ 0 },
{
0x04, 0x05, 0x06, 0x07, 0x0c, 0x0d, 0x0e, 0x0f,
@@ -256,7 +779,7 @@ static const gfx_layout spritelayout =
{
0x00, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0
},
- 0x100 /* step */
+ 0x100 // step
};
static const uint32_t shiplayout_xoffset[64] =
@@ -273,32 +796,36 @@ static const uint32_t shiplayout_xoffset[64] =
static const gfx_layout shiplayout =
{
- 64, 16, /* 64x16 sprites */
- 4, /* 4 sprites */
- 2, /* 2 bits per pixel */
+ 64, 16, // 64x16 sprites
+ 4, // 4 sprites
+ 2, // 2 bits per pixel
{ 0, 0x2000 },
EXTENDED_XOFFS,
{ STEP16(0x000, 0x080) },
- 0x800, /* step */
+ 0x800, // step
shiplayout_xoffset,
nullptr
};
static GFXDECODE_START( gfx_starshp1 )
- GFXDECODE_ENTRY( "gfx1", 0, tilelayout, 0, 1 )
- GFXDECODE_ENTRY( "gfx2", 0, spritelayout, 2, 2 )
- GFXDECODE_ENTRY( "gfx3", 0, shiplayout, 6, 2 )
+ GFXDECODE_ENTRY( "tiles", 0, tilelayout, 0, 1 )
+ GFXDECODE_ENTRY( "sprites", 0, spritelayout, 2, 2 )
+ GFXDECODE_ENTRY( "ship", 0, shiplayout, 6, 2 )
GFXDECODE_END
void starshp1_state::starshp1(machine_config &config)
{
- /* basic machine hardware */
+ constexpr XTAL MASTER_CLOCK = 12.096_MHz_XTAL;
+ constexpr XTAL CPU_CLOCK = MASTER_CLOCK / 16;
+ constexpr XTAL PIXEL_CLOCK = MASTER_CLOCK;
- M6502(config, m_maincpu, STARSHP1_CPU_CLOCK);
- m_maincpu->set_addrmap(AS_PROGRAM, &starshp1_state::starshp1_map);
- m_maincpu->set_vblank_int("screen", FUNC(starshp1_state::starshp1_interrupt));
+ // basic machine hardware
+
+ M6502(config, m_maincpu, CPU_CLOCK);
+ m_maincpu->set_addrmap(AS_PROGRAM, &starshp1_state::program_map);
+ m_maincpu->set_vblank_int("screen", FUNC(starshp1_state::interrupt));
f9334_device &misclatch(F9334(config, "misclatch")); // C8
misclatch.q_out_cb<0>().set(FUNC(starshp1_state::ship_explode_w));
@@ -308,21 +835,27 @@ void starshp1_state::starshp1(machine_config &config)
misclatch.q_out_cb<4>().set(FUNC(starshp1_state::inverse_w));
misclatch.q_out_cb<5>().set_nop(); // BLACK HOLE, not used
misclatch.q_out_cb<6>().set(FUNC(starshp1_state::mux_w));
- misclatch.q_out_cb<7>().set(FUNC(starshp1_state::led_w));
+ misclatch.q_out_cb<7>().set_output("led0").invert();
- /* video hardware */
+ // video hardware
+ constexpr int HTOTAL = 0x300;
+ constexpr int HBEND = 0x000;
+ constexpr int HBSTART = 0x200;
+ constexpr int VTOTAL = 0x106;
+ constexpr int VBEND = 0x000;
+ constexpr int VBSTART = 0x0f0;
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
- m_screen->set_raw(STARSHP1_PIXEL_CLOCK, STARSHP1_HTOTAL, STARSHP1_HBEND, STARSHP1_HBSTART, STARSHP1_VTOTAL, STARSHP1_VBEND, STARSHP1_VBSTART);
- m_screen->set_screen_update(FUNC(starshp1_state::screen_update_starshp1));
- m_screen->screen_vblank().set(FUNC(starshp1_state::screen_vblank_starshp1));
+ m_screen->set_raw(PIXEL_CLOCK, HTOTAL, HBEND, HBSTART, VTOTAL, VBEND, VBSTART);
+ m_screen->set_screen_update(FUNC(starshp1_state::screen_update));
+ m_screen->screen_vblank().set(FUNC(starshp1_state::screen_vblank));
m_screen->set_palette(m_palette);
GFXDECODE(config, m_gfxdecode, m_palette, gfx_starshp1);
- PALETTE(config, m_palette, FUNC(starshp1_state::starshp1_palette), 19, 8);
+ PALETTE(config, m_palette, FUNC(starshp1_state::palette), 19, 8);
- /* sound hardware */
+ // sound hardware
SPEAKER(config, "mono").front_center();
DISCRETE(config, m_discrete, starshp1_discrete).add_route(ALL_OUTPUTS, "mono", 1.0);
@@ -353,20 +886,20 @@ ROM_START( starshp1 )
ROM_LOAD( "7531-02.e3", 0x3800, 0x0800, CRC(b35b2c0e) SHA1(e52240cdfbba3dc380ba63f24cfc07b44feafd53) )
ROM_RELOAD( 0xf800, 0x0800 )
- ROM_REGION( 0x0400, "gfx1", 0 )
+ ROM_REGION( 0x0400, "tiles", 0 )
ROM_LOAD( "7513-01.n7", 0x0000, 0x0400, CRC(8fb0045d) SHA1(fb311c6977dec6e2a04179406e9ffdb920989a47) )
- ROM_REGION( 0x0100, "gfx2", 0 )
+ ROM_REGION( 0x0100, "sprites", 0 )
ROM_LOAD( "7515-01.j5", 0x0000, 0x0100, CRC(fcbcbf2e) SHA1(adf3cc43b77ad18eddbe39ee11625e552d1abab9) )
- ROM_REGION( 0x0800, "gfx3", 0 )
+ ROM_REGION( 0x0800, "ship", 0 )
ROM_LOAD( "7517-01.r1", 0x0000, 0x0400, CRC(1531f85f) SHA1(291822614fc6d3a71bf56607c796e18779f8cfc9) )
ROM_LOAD( "7516-01.p1", 0x0400, 0x0400, CRC(64fbfe4c) SHA1(b2dfdcc1c9927c693fe43b2e1411d0f14375fdeb) )
ROM_REGION( 0x0220, "proms", 0 )
- ROM_LOAD( "7518-01.r10", 0x0000, 0x0100, CRC(80877f7e) SHA1(8b28f48936a4247c583ca6713bfbaf4772c7a4f5) ) /* video output */
- ROM_LOAD( "7514-01.n9", 0x0100, 0x0100, CRC(3610b453) SHA1(9e33ee04f22a9174c29fafb8e71781fa330a7a08) ) /* sync */
- ROM_LOAD( "7519-01.b5", 0x0200, 0x0020, CRC(23b9cd3c) SHA1(220f9f73d86cdcf1b390c52c591750a73402af50) ) /* address */
+ ROM_LOAD( "7518-01.r10", 0x0000, 0x0100, CRC(80877f7e) SHA1(8b28f48936a4247c583ca6713bfbaf4772c7a4f5) ) // video output
+ ROM_LOAD( "7514-01.n9", 0x0100, 0x0100, CRC(3610b453) SHA1(9e33ee04f22a9174c29fafb8e71781fa330a7a08) ) // sync
+ ROM_LOAD( "7519-01.b5", 0x0200, 0x0020, CRC(23b9cd3c) SHA1(220f9f73d86cdcf1b390c52c591750a73402af50) ) // address
ROM_END
ROM_START( starshpp )
@@ -390,22 +923,24 @@ ROM_START( starshpp )
ROM_LOAD_NIB_LOW ( "d1", 0x3c00, 0x0400, CRC(be4050b6) SHA1(03ca4833769efb10f18f52b7ba4d016568d3cab9) )
ROM_RELOAD( 0xfc00, 0x0400 )
- ROM_REGION( 0x0400, "gfx1", 0 )
+ ROM_REGION( 0x0400, "tiles", 0 )
ROM_LOAD( "7513-01.n7", 0x0000, 0x0400, CRC(8fb0045d) SHA1(fb311c6977dec6e2a04179406e9ffdb920989a47) )
- ROM_REGION( 0x0100, "gfx2", 0 )
+ ROM_REGION( 0x0100, "sprites", 0 )
ROM_LOAD( "7515-01.j5", 0x0000, 0x0100, CRC(fcbcbf2e) SHA1(adf3cc43b77ad18eddbe39ee11625e552d1abab9) )
- ROM_REGION( 0x0800, "gfx3", 0 )
+ ROM_REGION( 0x0800, "ship", 0 )
ROM_LOAD( "7517-01.r1", 0x0000, 0x0400, CRC(1531f85f) SHA1(291822614fc6d3a71bf56607c796e18779f8cfc9) )
ROM_LOAD( "7516-01.p1", 0x0400, 0x0400, CRC(64fbfe4c) SHA1(b2dfdcc1c9927c693fe43b2e1411d0f14375fdeb) )
ROM_REGION( 0x0220, "proms", 0 )
- ROM_LOAD( "7518-01.r10", 0x0000, 0x0100, CRC(80877f7e) SHA1(8b28f48936a4247c583ca6713bfbaf4772c7a4f5) ) /* video output */
- ROM_LOAD( "7514-01.n9", 0x0100, 0x0100, CRC(3610b453) SHA1(9e33ee04f22a9174c29fafb8e71781fa330a7a08) ) /* sync */
- ROM_LOAD( "7519-01.b5", 0x0200, 0x0020, CRC(23b9cd3c) SHA1(220f9f73d86cdcf1b390c52c591750a73402af50) ) /* address */
+ ROM_LOAD( "7518-01.r10", 0x0000, 0x0100, CRC(80877f7e) SHA1(8b28f48936a4247c583ca6713bfbaf4772c7a4f5) ) // video output
+ ROM_LOAD( "7514-01.n9", 0x0100, 0x0100, CRC(3610b453) SHA1(9e33ee04f22a9174c29fafb8e71781fa330a7a08) ) // sync
+ ROM_LOAD( "7519-01.b5", 0x0200, 0x0020, CRC(23b9cd3c) SHA1(220f9f73d86cdcf1b390c52c591750a73402af50) ) // address
ROM_END
+} // anonymous namespace
+
-GAME( 1977, starshp1, 0, starshp1, starshp1, starshp1_state, empty_init, ORIENTATION_FLIP_X, "Atari", "Starship 1", MACHINE_IMPERFECT_SOUND )
-GAME( 1977, starshpp, starshp1, starshp1, starshp1, starshp1_state, empty_init, ORIENTATION_FLIP_X, "Atari", "Starship 1 (prototype?)", MACHINE_IMPERFECT_SOUND )
+GAME( 1977, starshp1, 0, starshp1, starshp1, starshp1_state, empty_init, ORIENTATION_FLIP_X, "Atari", "Starship 1", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
+GAME( 1977, starshpp, starshp1, starshp1, starshp1, starshp1_state, empty_init, ORIENTATION_FLIP_X, "Atari", "Starship 1 (prototype?)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )