laserbat.cpp: Quantise area effect 2/shell effect for catnmous. (#7964)

This commit is contained in:
Vas Crabb 2021-04-22 04:14:19 +10:00 committed by GitHub
parent 6b8a176ac2
commit a1a6ff7087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 98 deletions

View File

@ -130,7 +130,7 @@ void laserbat_state_base::csound2_w(uint8_t data)
void laserbat_state::csound2_w(uint8_t data) void laserbat_state::csound2_w(uint8_t data)
{ {
// there are a bunch of edge-triggered things, so grab changes // there are a bunch of edge-triggered things, so grab changes
unsigned const diff = data ^ m_csound2; uint8_t const diff = data ^ m_csound2;
// SN76477 and distortion control // SN76477 and distortion control
if (data & diff & 0x01) if (data & diff & 0x01)
@ -317,10 +317,10 @@ void catnmous_state::csound1_w(uint8_t data)
void catnmous_state::csound2_w(uint8_t data) void catnmous_state::csound2_w(uint8_t data)
{ {
// the bottom bit is used for sprite banking, of all things // the bottom bit is used for sprite banking, of all things
m_gfx2 = memregion("gfx2")->base() + ((data & 0x01) ? 0x0800 : 0x0000); m_gfx2_base = uint16_t(BIT(data, 0)) << 11;
// the top bit is called RESET on the wiring diagram // the top bit is called RESET on the wiring diagram
m_audiopcb->reset_w((data & 0x80) ? 1 : 0); m_audiopcb->reset_w(BIT(data, 7));
m_csound2 = data; m_csound2 = data;
} }

View File

@ -65,6 +65,11 @@
* The sprite ROM is twice the size as Laser Battle with the bank * The sprite ROM is twice the size as Laser Battle with the bank
selected using bit 9 of the 16-bit sound interface (there's a wire selected using bit 9 of the 16-bit sound interface (there's a wire
making this connection visible on the component side of the PCB) making this connection visible on the component side of the PCB)
* At least some boards have IC13I pins 8, 9, 10 and 11 bent out of
the socket, tied together, and pulled high via a 4k7 resistor,
which quantises the shell/area effect 2 to four-pixel boundaries
(implemented as m_eff2_mask) - would be good to see whether this
mod is present on all boards
* If demo sounds are enabled (using DIP switches), background music * If demo sounds are enabled (using DIP switches), background music
is played every sixth time through the attract loop is played every sixth time through the attract loop
* Sound board emulation is based on tracing the program and guessing * Sound board emulation is based on tracing the program and guessing
@ -412,9 +417,14 @@ INTERRUPT_GEN_MEMBER(laserbat_state_base::laserbat_interrupt)
m_maincpu->set_input_line(0, ASSERT_LINE); m_maincpu->set_input_line(0, ASSERT_LINE);
} }
void laserbat_state_base::init_laserbat() void laserbat_state_base::machine_start()
{ {
// start rendering scanlines
m_screen->register_screen_bitmap(m_bitmap);
m_scanline_timer = timer_alloc(TIMER_SCANLINE); m_scanline_timer = timer_alloc(TIMER_SCANLINE);
m_scanline_timer->adjust(m_screen->time_until_pos(1, 0));
save_item(NAME(m_gfx2_base));
save_item(NAME(m_input_mux)); save_item(NAME(m_input_mux));
save_item(NAME(m_mpx_p_1_2)); save_item(NAME(m_mpx_p_1_2));
@ -436,10 +446,10 @@ void laserbat_state_base::init_laserbat()
save_item(NAME(m_neg1)); save_item(NAME(m_neg1));
save_item(NAME(m_neg2)); save_item(NAME(m_neg2));
save_item(NAME(m_csound1));
save_item(NAME(m_csound2));
save_item(NAME(m_rhsc)); save_item(NAME(m_rhsc));
save_item(NAME(m_whsc)); save_item(NAME(m_whsc));
save_item(NAME(m_csound1));
save_item(NAME(m_csound2));
} }
void laserbat_state::machine_start() void laserbat_state::machine_start()
@ -732,7 +742,7 @@ ROM_START( catnmousa )
ROM_END ROM_END
GAME( 1981, laserbat, 0, laserbat, laserbat, laserbat_state, init_laserbat, ROT0, "Zaccaria", "Laser Battle", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1981, laserbat, 0, laserbat, laserbat, laserbat_state, empty_init, ROT0, "Zaccaria", "Laser Battle", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
GAME( 1981, lazarian, laserbat, laserbat, lazarian, laserbat_state, init_laserbat, ROT0, "Zaccaria (Bally Midway license)", "Lazarian", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1981, lazarian, laserbat, laserbat, lazarian, laserbat_state, empty_init, ROT0, "Zaccaria (Bally Midway license)", "Lazarian", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
GAME( 1982, catnmous, 0, catnmous, catnmous, catnmous_state, init_laserbat, ROT90, "Zaccaria", "Cat and Mouse (type 02 program)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1982, catnmous, 0, catnmous, catnmous, catnmous_state, empty_init, ROT90, "Zaccaria", "Cat and Mouse (type 02 program)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
GAME( 1982, catnmousa, catnmous, catnmous, catnmous, catnmous_state, init_laserbat, ROT90, "Zaccaria", "Cat and Mouse (type 01 program)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1982, catnmousa, catnmous, catnmous, catnmous, catnmous_state, empty_init, ROT90, "Zaccaria", "Cat and Mouse (type 01 program)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )

View File

@ -29,7 +29,14 @@
class laserbat_state_base : public driver_device class laserbat_state_base : public driver_device
{ {
public: public:
laserbat_state_base(const machine_config &mconfig, device_type type, const char *tag) void laserbat_base(machine_config &config);
void laserbat_io_map(address_map &map);
void laserbat_map(address_map &map);
protected:
enum { TIMER_SCANLINE };
laserbat_state_base(const machine_config &mconfig, device_type type, const char *tag, uint8_t eff2_mask)
: driver_device(mconfig, type, tag) : driver_device(mconfig, type, tag)
, m_mux_ports(*this, {"ROW0", "ROW1", "SW1", "SW2"}) , m_mux_ports(*this, {"ROW0", "ROW1", "SW1", "SW2"})
, m_row1(*this, "ROW1") , m_row1(*this, "ROW1")
@ -40,39 +47,12 @@ public:
, m_gfxmix(*this, "gfxmix") , m_gfxmix(*this, "gfxmix")
, m_pvi(*this, "pvi%u", 1U) , m_pvi(*this, "pvi%u", 1U)
, m_gfxdecode(*this, "gfxdecode") , m_gfxdecode(*this, "gfxdecode")
, m_scanline_timer(nullptr) , m_gfx1(*this, "gfx1")
, m_gfx1(nullptr) , m_gfx2(*this, "gfx2")
, m_gfx2(nullptr) , m_eff2_mask(eff2_mask)
, m_input_mux(0)
, m_mpx_p_1_2(false)
, m_mpx_bkeff(false)
, m_nave(false)
, m_clr_lum(0)
, m_shp(0)
, m_wcoh(0)
, m_wcov(0)
, m_abeff1(false)
, m_abeff2(false)
, m_mpx_eff2_sh(false)
, m_coleff(0)
, m_neg1(false)
, m_neg2(false)
, m_rhsc(0)
, m_whsc(0)
, m_csound1(0)
, m_csound2(0)
{ {
} }
void init_laserbat();
void laserbat_base(machine_config &config);
void laserbat_io_map(address_map &map);
void laserbat_map(address_map &map);
protected:
enum { TIMER_SCANLINE };
// control ports // control ports
void ct_io_w(uint8_t data); void ct_io_w(uint8_t data);
uint8_t rrowx_r(); uint8_t rrowx_r();
@ -93,7 +73,7 @@ protected:
virtual void csound2_w(uint8_t data); virtual void csound2_w(uint8_t data);
// running the video // running the video
virtual void video_start() override; virtual void machine_start() override;
uint32_t screen_update_laserbat(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); uint32_t screen_update_laserbat(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
@ -102,55 +82,57 @@ protected:
TIMER_CALLBACK_MEMBER(video_line); TIMER_CALLBACK_MEMBER(video_line);
// input lines // input lines
required_ioport_array<4> m_mux_ports; required_ioport_array<4> m_mux_ports;
required_ioport m_row1; required_ioport m_row1;
required_ioport m_row2; required_ioport m_row2;
// main CPU device // main CPU device
required_device<s2650_device> m_maincpu; required_device<s2650_device> m_maincpu;
// video devices // video devices
required_device<screen_device> m_screen; required_device<screen_device> m_screen;
required_device<palette_device> m_palette; required_device<palette_device> m_palette;
required_device<pla_device> m_gfxmix; required_device<pla_device> m_gfxmix;
required_device_array<s2636_device, 3> m_pvi; required_device_array<s2636_device, 3> m_pvi;
required_device<gfxdecode_device> m_gfxdecode; required_device<gfxdecode_device> m_gfxdecode;
// stuff for rendering video // stuff for rendering video
emu_timer *m_scanline_timer; required_region_ptr<uint8_t> m_gfx1;
bitmap_ind16 m_bitmap; required_region_ptr<uint8_t> m_gfx2;
uint8_t const *m_gfx1; emu_timer *m_scanline_timer = nullptr;
uint8_t const *m_gfx2; bitmap_ind16 m_bitmap;
uint16_t m_gfx2_base = 0;
uint8_t const m_eff2_mask;
// control lines // control lines
unsigned m_input_mux; uint8_t m_input_mux;
bool m_mpx_p_1_2; bool m_mpx_p_1_2;
// RAM used by TTL video hardware, writable by CPU // RAM used by TTL video hardware, writable by CPU
uint8_t m_bg_ram[0x400]; // background tilemap uint8_t m_bg_ram[0x400]; // background tilemap
uint8_t m_eff_ram[0x400]; // per-scanline effects (A8 not wired meaning only half is usable) uint8_t m_eff_ram[0x400]; // per-scanline effects (A8 not wired meaning only half is usable)
bool m_mpx_bkeff; // select between writing background and effects memory bool m_mpx_bkeff = false; // select between writing background and effects memory
// signals affecting the TTL-generated 32x32 sprite // signals affecting the TTL-generated 32x32 sprite
bool m_nave; // 1-bit enable bool m_nave = false; // 1-bit enable
unsigned m_clr_lum; // 3-bit colour/luminance uint8_t m_clr_lum = 0; // 3-bit colour/luminance
unsigned m_shp; // 3-bit shape uint8_t m_shp = 0; // 3-bit shape
unsigned m_wcoh; // 8-bit offset horizontal uint8_t m_wcoh = 0; // 8-bit offset horizontal
unsigned m_wcov; // 8-bit offset vertical uint8_t m_wcov = 0; // 8-bit offset vertical
// video effects signals // video effects signals
bool m_abeff1; // 1-bit effect enable bool m_abeff1 = false; // 1-bit effect enable
bool m_abeff2; // 1-bit effect enable bool m_abeff2 = false; // 1-bit effect enable
bool m_mpx_eff2_sh; // 1-bit effect selection bool m_mpx_eff2_sh = false; // 1-bit effect selection
unsigned m_coleff; // 2-bit colour effect uint8_t m_coleff = 0; // 2-bit colour effect
bool m_neg1; // 1-bit area selection bool m_neg1 = false; // 1-bit area selection
bool m_neg2; // 1-bit area selection bool m_neg2 = false; // 1-bit area selection
// sound board I/O signals // sound board I/O signals
unsigned m_rhsc; // 8-bit input from J7 uint8_t m_rhsc = 0; // 8-bit input from J7
unsigned m_whsc; // 8-bit output to J7 uint8_t m_whsc = 0; // 8-bit output to J7
unsigned m_csound1; // bits 1-8 on J3 uint8_t m_csound1 = 0; // bits 1-8 on J3
unsigned m_csound2; // bits 9-16 on J3 uint8_t m_csound2 = 0; // bits 9-16 on J3
}; };
@ -158,11 +140,10 @@ class laserbat_state : public laserbat_state_base
{ {
public: public:
laserbat_state(const machine_config &mconfig, device_type type, const char *tag) laserbat_state(const machine_config &mconfig, device_type type, const char *tag)
: laserbat_state_base(mconfig, type, tag) : laserbat_state_base(mconfig, type, tag, 0x00)
, m_csg(*this, "csg") , m_csg(*this, "csg")
, m_synth_low(*this, "synth_low") , m_synth_low(*this, "synth_low")
, m_synth_high(*this, "synth_high") , m_synth_high(*this, "synth_high")
, m_keys(0)
{ {
} }
@ -184,7 +165,7 @@ protected:
required_device<tms3615_device> m_synth_high; required_device<tms3615_device> m_synth_high;
// register state // register state
unsigned m_keys; // low octave keys 1-13 and high octave keys 2-12 (24 bits) uint32_t m_keys = 0; // low octave keys 1-13 and high octave keys 2-12 (24 bits)
}; };
@ -192,7 +173,7 @@ class catnmous_state : public laserbat_state_base
{ {
public: public:
catnmous_state(const machine_config &mconfig, device_type type, const char *tag) catnmous_state(const machine_config &mconfig, device_type type, const char *tag)
: laserbat_state_base(mconfig, type, tag) : laserbat_state_base(mconfig, type, tag, 0x03)
, m_audiopcb(*this, "audiopcb") , m_audiopcb(*this, "audiopcb")
{ {
} }

View File

@ -157,18 +157,6 @@ void laserbat_state_base::cnt_nav_w(uint8_t data)
} }
void laserbat_state_base::video_start()
{
// we render straight from ROM
m_gfx1 = memregion("gfx1")->base();
m_gfx2 = memregion("gfx2")->base();
// start rendering scanlines
m_screen->register_screen_bitmap(m_bitmap);
m_scanline_timer->adjust(m_screen->time_until_pos(1, 0));
}
uint32_t laserbat_state_base::screen_update_laserbat(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) uint32_t laserbat_state_base::screen_update_laserbat(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{ {
bool const flip_y = flip_screen_y(), flip_x = flip_screen_x(); bool const flip_y = flip_screen_y(), flip_x = flip_screen_x();
@ -281,16 +269,14 @@ TIMER_CALLBACK_MEMBER(laserbat_state_base::video_line)
for (int x = 0, px = x_offset; max_x >= px; x++) for (int x = 0, px = x_offset; max_x >= px; x++)
{ {
// calculate area effects // calculate area effects
// I have no idea where the magical x offset comes from but it's necessary bool const right_half = bool(x & 0x80);
bool const right_half = bool((x + 0) & 0x80); bool const eff1_cmp = right_half ? (uint8_t(x & 0x7f) < (eff1_val & 0x7f)) : (uint8_t(x & 0x7f) > (~eff1_val & 0x7f));
bool const eff1_cmp = right_half ? (uint8_t((x + 0) & 0x7f) < (eff1_val & 0x7f)) : (uint8_t((x + 0) & 0x7f) > (~eff1_val & 0x7f)); bool const eff2_cmp = right_half ? ((uint8_t(x & 0x7f) | m_eff2_mask) < ((eff2_val & 0x7f) | m_eff2_mask)) : ((uint8_t(x & 0x7f) | m_eff2_mask) > ((~eff2_val & 0x7f) | m_eff2_mask));
bool const eff2_cmp = right_half ? (uint8_t((x + 0) & 0x7f) < (eff2_val & 0x7f)) : (uint8_t((x + 0) & 0x7f) > (~eff2_val & 0x7f));
bool const eff1 = m_abeff1 && (m_neg1 ? !eff1_cmp : eff1_cmp); bool const eff1 = m_abeff1 && (m_neg1 ? !eff1_cmp : eff1_cmp);
bool const eff2 = m_abeff2 && (m_neg2 ? !eff2_cmp : eff2_cmp) && m_mpx_eff2_sh; bool const eff2 = m_abeff2 && (m_neg2 ? !eff2_cmp : eff2_cmp) && m_mpx_eff2_sh;
// calculate shell point effect // calculate shell point effect
// using the same magical offset as the area effects bool const shell = m_abeff2 && ((uint8_t(x & 0xff) | m_eff2_mask) == ((eff2_val & 0xff) | m_eff2_mask)) && !m_mpx_eff2_sh;
bool const shell = m_abeff2 && (uint8_t((x + 0) & 0xff) == (eff2_val & 0xff)) && !m_mpx_eff2_sh;
// set effect bits, and mix in PVI graphics while we're here // set effect bits, and mix in PVI graphics while we're here
uint16_t const effect_bits = (shell ? 0x0800 : 0x0000) | (eff1 ? 0x1000 : 0x0000) | (eff2 ? 0x2000 : 0x0000); uint16_t const effect_bits = (shell ? 0x0800 : 0x0000) | (eff1 ? 0x1000 : 0x0000) | (eff2 ? 0x2000 : 0x0000);
@ -305,7 +291,7 @@ TIMER_CALLBACK_MEMBER(laserbat_state_base::video_line)
} }
// render the TTL-generated sprite // render the TTL-generated sprite
// more magic offsets here I don't understand the source of // magic offsets here I don't understand the source of
if (m_nave) if (m_nave)
{ {
int const sprite_row = y + y_offset - ((256 - m_wcov) & 0x0ff); int const sprite_row = y + y_offset - ((256 - m_wcov) & 0x0ff);
@ -313,7 +299,7 @@ TIMER_CALLBACK_MEMBER(laserbat_state_base::video_line)
{ {
for (unsigned byte = 0, x = x_offset + (3 * ((256 - m_wcoh + 5) & 0x0ff)); 8 > byte; byte++) for (unsigned byte = 0, x = x_offset + (3 * ((256 - m_wcoh + 5) & 0x0ff)); 8 > byte; byte++)
{ {
uint8_t bits = m_gfx2[((m_shp << 8) & 0x700) | ((sprite_row << 3) & 0x0f8) | (byte & 0x07)]; uint8_t bits = m_gfx2[m_gfx2_base | ((m_shp << 8) & 0x700) | ((sprite_row << 3) & 0x0f8) | (byte & 0x07)];
for (unsigned pixel = 0; 4 > pixel; pixel++, bits <<= 2) for (unsigned pixel = 0; 4 > pixel; pixel++, bits <<= 2)
{ {
if (max_x >= x) row[x++] |= (bits >> 6) & 0x03; if (max_x >= x) row[x++] |= (bits >> 6) & 0x03;