From a1a6ff7087af43adc4b739f8ffdccd77354d7e4f Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Thu, 22 Apr 2021 04:14:19 +1000 Subject: [PATCH] laserbat.cpp: Quantise area effect 2/shell effect for catnmous. (#7964) --- src/mame/audio/laserbat.cpp | 6 +- src/mame/drivers/laserbat.cpp | 24 +++++-- src/mame/includes/laserbat.h | 117 ++++++++++++++-------------------- src/mame/video/laserbat.cpp | 26 ++------ 4 files changed, 75 insertions(+), 98 deletions(-) diff --git a/src/mame/audio/laserbat.cpp b/src/mame/audio/laserbat.cpp index 864b0c11c1f..66ec4dc2c10 100644 --- a/src/mame/audio/laserbat.cpp +++ b/src/mame/audio/laserbat.cpp @@ -130,7 +130,7 @@ void laserbat_state_base::csound2_w(uint8_t data) void laserbat_state::csound2_w(uint8_t data) { // 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 if (data & diff & 0x01) @@ -317,10 +317,10 @@ void catnmous_state::csound1_w(uint8_t data) void catnmous_state::csound2_w(uint8_t data) { // 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 - m_audiopcb->reset_w((data & 0x80) ? 1 : 0); + m_audiopcb->reset_w(BIT(data, 7)); m_csound2 = data; } diff --git a/src/mame/drivers/laserbat.cpp b/src/mame/drivers/laserbat.cpp index 2bd84687d19..833ff5806b0 100644 --- a/src/mame/drivers/laserbat.cpp +++ b/src/mame/drivers/laserbat.cpp @@ -65,6 +65,11 @@ * 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 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 is played every sixth time through the attract loop * 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); } -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->adjust(m_screen->time_until_pos(1, 0)); + + save_item(NAME(m_gfx2_base)); save_item(NAME(m_input_mux)); 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_neg2)); - save_item(NAME(m_csound1)); - save_item(NAME(m_csound2)); save_item(NAME(m_rhsc)); save_item(NAME(m_whsc)); + save_item(NAME(m_csound1)); + save_item(NAME(m_csound2)); } void laserbat_state::machine_start() @@ -732,7 +742,7 @@ ROM_START( catnmousa ) ROM_END -GAME( 1981, laserbat, 0, laserbat, laserbat, laserbat_state, init_laserbat, 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( 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, catnmousa, catnmous, catnmous, catnmous, catnmous_state, init_laserbat, ROT90, "Zaccaria", "Cat and Mouse (type 01 program)", 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, empty_init, ROT0, "Zaccaria (Bally Midway license)", "Lazarian", 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, empty_init, ROT90, "Zaccaria", "Cat and Mouse (type 01 program)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/includes/laserbat.h b/src/mame/includes/laserbat.h index 621c10838d3..66aa0a955c9 100644 --- a/src/mame/includes/laserbat.h +++ b/src/mame/includes/laserbat.h @@ -29,7 +29,14 @@ class laserbat_state_base : public driver_device { 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) , m_mux_ports(*this, {"ROW0", "ROW1", "SW1", "SW2"}) , m_row1(*this, "ROW1") @@ -40,39 +47,12 @@ public: , m_gfxmix(*this, "gfxmix") , m_pvi(*this, "pvi%u", 1U) , m_gfxdecode(*this, "gfxdecode") - , m_scanline_timer(nullptr) - , m_gfx1(nullptr) - , m_gfx2(nullptr) - , 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) + , m_gfx1(*this, "gfx1") + , m_gfx2(*this, "gfx2") + , m_eff2_mask(eff2_mask) { } - 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 void ct_io_w(uint8_t data); uint8_t rrowx_r(); @@ -93,7 +73,7 @@ protected: virtual void csound2_w(uint8_t data); // 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); 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); // input lines - required_ioport_array<4> m_mux_ports; - required_ioport m_row1; - required_ioport m_row2; + required_ioport_array<4> m_mux_ports; + required_ioport m_row1; + required_ioport m_row2; // main CPU device - required_device m_maincpu; + required_device m_maincpu; // video devices - required_device m_screen; - required_device m_palette; - required_device m_gfxmix; - required_device_array m_pvi; - required_device m_gfxdecode; + required_device m_screen; + required_device m_palette; + required_device m_gfxmix; + required_device_array m_pvi; + required_device m_gfxdecode; // stuff for rendering video - emu_timer *m_scanline_timer; - bitmap_ind16 m_bitmap; - uint8_t const *m_gfx1; - uint8_t const *m_gfx2; + required_region_ptr m_gfx1; + required_region_ptr m_gfx2; + emu_timer *m_scanline_timer = nullptr; + bitmap_ind16 m_bitmap; + uint16_t m_gfx2_base = 0; + uint8_t const m_eff2_mask; // control lines - unsigned m_input_mux; + uint8_t m_input_mux; bool m_mpx_p_1_2; // RAM used by TTL video hardware, writable by CPU - 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) - bool m_mpx_bkeff; // select between writing background and effects memory + 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) + bool m_mpx_bkeff = false; // select between writing background and effects memory // signals affecting the TTL-generated 32x32 sprite - bool m_nave; // 1-bit enable - unsigned m_clr_lum; // 3-bit colour/luminance - unsigned m_shp; // 3-bit shape - unsigned m_wcoh; // 8-bit offset horizontal - unsigned m_wcov; // 8-bit offset vertical + bool m_nave = false; // 1-bit enable + uint8_t m_clr_lum = 0; // 3-bit colour/luminance + uint8_t m_shp = 0; // 3-bit shape + uint8_t m_wcoh = 0; // 8-bit offset horizontal + uint8_t m_wcov = 0; // 8-bit offset vertical // video effects signals - bool m_abeff1; // 1-bit effect enable - bool m_abeff2; // 1-bit effect enable - bool m_mpx_eff2_sh; // 1-bit effect selection - unsigned m_coleff; // 2-bit colour effect - bool m_neg1; // 1-bit area selection - bool m_neg2; // 1-bit area selection + bool m_abeff1 = false; // 1-bit effect enable + bool m_abeff2 = false; // 1-bit effect enable + bool m_mpx_eff2_sh = false; // 1-bit effect selection + uint8_t m_coleff = 0; // 2-bit colour effect + bool m_neg1 = false; // 1-bit area selection + bool m_neg2 = false; // 1-bit area selection // sound board I/O signals - unsigned m_rhsc; // 8-bit input from J7 - unsigned m_whsc; // 8-bit output to J7 - unsigned m_csound1; // bits 1-8 on J3 - unsigned m_csound2; // bits 9-16 on J3 + uint8_t m_rhsc = 0; // 8-bit input from J7 + uint8_t m_whsc = 0; // 8-bit output to J7 + uint8_t m_csound1 = 0; // bits 1-8 on J3 + uint8_t m_csound2 = 0; // bits 9-16 on J3 }; @@ -158,11 +140,10 @@ class laserbat_state : public laserbat_state_base { public: 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_synth_low(*this, "synth_low") , m_synth_high(*this, "synth_high") - , m_keys(0) { } @@ -184,7 +165,7 @@ protected: required_device m_synth_high; // 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: 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") { } diff --git a/src/mame/video/laserbat.cpp b/src/mame/video/laserbat.cpp index 09af495c61b..efcd021d2ad 100644 --- a/src/mame/video/laserbat.cpp +++ b/src/mame/video/laserbat.cpp @@ -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) { 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++) { // calculate area effects - // I have no idea where the magical x offset comes from but it's necessary - bool const right_half = bool((x + 0) & 0x80); - 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 + 0) & 0x7f) < (eff2_val & 0x7f)) : (uint8_t((x + 0) & 0x7f) > (~eff2_val & 0x7f)); + bool const right_half = bool(x & 0x80); + bool const eff1_cmp = right_half ? (uint8_t(x & 0x7f) < (eff1_val & 0x7f)) : (uint8_t(x & 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 eff1 = m_abeff1 && (m_neg1 ? !eff1_cmp : eff1_cmp); bool const eff2 = m_abeff2 && (m_neg2 ? !eff2_cmp : eff2_cmp) && m_mpx_eff2_sh; // calculate shell point effect - // using the same magical offset as the area effects - bool const shell = m_abeff2 && (uint8_t((x + 0) & 0xff) == (eff2_val & 0xff)) && !m_mpx_eff2_sh; + bool const shell = m_abeff2 && ((uint8_t(x & 0xff) | m_eff2_mask) == ((eff2_val & 0xff) | m_eff2_mask)) && !m_mpx_eff2_sh; // 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); @@ -305,7 +291,7 @@ TIMER_CALLBACK_MEMBER(laserbat_state_base::video_line) } // 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) { 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++) { - 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) { if (max_x >= x) row[x++] |= (bits >> 6) & 0x03;