From a97718f3b3f8290863a3623d898b983e26f2385d Mon Sep 17 00:00:00 2001 From: Devin Acker Date: Mon, 18 Mar 2024 01:35:01 -0400 Subject: misc/jaminator.cpp: Added driver and software list for Noise Toys Jaminator guitar toy. (#12146) sound/cf61909.cpp: Added Texas Instruments CF61909 sound chip device. New working systems ---------- Noise Toys Inc. Jaminator [Devin Acker] New working software list items ---------- jaminator.xml: Classic Rock I (USA), Classic Rock II (USA), Hard Rock I (USA), Hard Rock II (USA), Lead Rock Guitarists (USA), Modern Rock (USA) [Devin Acker] --- hash/jaminator.xml | 196 ++++++++++++++++++++++++++++ scripts/src/sound.lua | 12 ++ src/devices/sound/cf61909.cpp | 161 +++++++++++++++++++++++ src/devices/sound/cf61909.h | 63 +++++++++ src/mame/mame.lst | 3 + src/mame/misc/jaminator.cpp | 292 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 727 insertions(+) create mode 100644 hash/jaminator.xml create mode 100644 src/devices/sound/cf61909.cpp create mode 100644 src/devices/sound/cf61909.h create mode 100644 src/mame/misc/jaminator.cpp diff --git a/hash/jaminator.xml b/hash/jaminator.xml new file mode 100644 index 00000000000..30f02f7a941 --- /dev/null +++ b/hash/jaminator.xml @@ -0,0 +1,196 @@ + + + + + + + + + + Classic Rock I (USA) + 1990 + Worlds of Wonder + + + + + + + + + + + + Classic Rock II (USA) + 1990 + Worlds of Wonder + + + + + + + + + + + + Hard Rock I (USA) + 1990 + Worlds of Wonder + + + + + + + + + + + + Hard Rock II (USA) + 1990 + Worlds of Wonder + + + + + + + + + + + + Lead Rock Guitarists (USA) + 1990 + Worlds of Wonder + + + + + + + + + + + + Modern Rock (USA) + 1990 + Worlds of Wonder + + + + + + + + + + diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 522aff46042..63683ba3c48 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1702,3 +1702,15 @@ if (SOUNDS["AP2010"]~=null) then MAME_DIR .. "src/devices/sound/ap2010pcm.h", } end + +--------------------------------------------------- +-- Texas Instruments CF61909 +--@src/devices/sound/cf61909.h,SOUNDS["CF61909"] = true +--------------------------------------------------- + +if (SOUNDS["CF61909"]~=null) then + files { + MAME_DIR .. "src/devices/sound/cf61909.cpp", + MAME_DIR .. "src/devices/sound/cf61909.h", + } +end diff --git a/src/devices/sound/cf61909.cpp b/src/devices/sound/cf61909.cpp new file mode 100644 index 00000000000..77b8af0b5de --- /dev/null +++ b/src/devices/sound/cf61909.cpp @@ -0,0 +1,161 @@ +// license: BSD-3-Clause +// copyright-holders: Devin Acker + +/*************************************************************************** + Texas Instruments CF61909 "DEVO" + + This is the sound and mapper ASIC used in the Jaminator. + It generates 8 channels of PCM at ~44.5 kHz, and also handles all + ROM access and clock generation for the 8039 MCU. + +***************************************************************************/ + +#include "emu.h" +#include "cf61909.h" + +#include + +DEFINE_DEVICE_TYPE(CF61909, cf61909_device, "cf61909", "Texas Instruments CF61909 (DEVO)") + +cf61909_device::cf61909_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, CF61909, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , device_rom_interface(mconfig, *this) + , m_sample_clock(*this, "sample_clock") +{ +} + +/**************************************************************************/ +void cf61909_device::device_add_mconfig(machine_config &config) +{ + // DEVO outputs a brief low pulse every 4 samples (~11.127 kHz), which the Jaminator MCU uses + // for syncing to the sample rate when updating sound registers + CLOCK(config, m_sample_clock, DERIVED_CLOCK(1, CLOCKS_PER_SAMPLE * 4)); + m_sample_clock->set_duty_cycle(1.0 - (16.0 / CLOCKS_PER_SAMPLE)); +} + +/**************************************************************************/ +void cf61909_device::device_start() +{ + m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE); + + save_item(NAME(m_data_offset)); + + save_item(STRUCT_MEMBER(m_voice, m_regs)); + save_item(STRUCT_MEMBER(m_voice, m_start)); + save_item(STRUCT_MEMBER(m_voice, m_loop)); + save_item(STRUCT_MEMBER(m_voice, m_pos)); + save_item(STRUCT_MEMBER(m_voice, m_pitch)); + save_item(STRUCT_MEMBER(m_voice, m_pitch_counter)); + save_item(STRUCT_MEMBER(m_voice, m_volume)); +} + +/**************************************************************************/ +void cf61909_device::device_reset() +{ + std::fill(m_voice.begin(), m_voice.end(), voice_t()); + + m_data_offset = 0; +} + +/**************************************************************************/ +void cf61909_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE); +} + +/**************************************************************************/ +void cf61909_device::rom_bank_pre_change() +{ + m_stream->update(); +} + +/**************************************************************************/ +u8 cf61909_device::read(offs_t offset) +{ + return read_byte(m_data_offset | (offset & 0xff)); +} + +/**************************************************************************/ +void cf61909_device::write(offs_t offset, u8 data) +{ + voice_t &voice = m_voice[BIT(offset, 4, 3)]; + const u8 reg = offset & 0xf; + + m_stream->update(); + voice.m_regs[reg] = data; + + switch (reg) + { + case 0x1: // position lsb + voice.m_pos = (voice.m_regs[0x2] << 8) | data; + break; + + case 0x2: // pitch / position msb + break; + + case 0x3: // pitch lsb + voice.m_pitch = (voice.m_regs[0x2] << 8) | data; + break; + + case 0x4: // volume low nibble + voice.m_volume = (voice.m_regs[0xc] << 4) | (data & 0xf); + break; + + case 0x5: // program bank (TODO) + break; + + case 0x6: // data bank + m_data_offset = (data & 0x7f) << 8; + if (BIT(data, 7)) + m_data_offset |= 0x20000; // cartridge memory + break; + + case 0x9: // sample start lsb + voice.m_start = (voice.m_regs[0xa] << 10) | (data << 2); + break; + + case 0xa: // sample start / loop msb + break; + + case 0xb: // sample loop lsb + voice.m_loop = (voice.m_regs[0xa] << 8) | data; + break; + + case 0xc: // volume high nibble + break; + + default: + logerror("%s: unknown register write %02x = %02x\n", machine().describe_context(), offset & 0xff, data); + break; + } +} + +/**************************************************************************/ +void cf61909_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + for (int i = 0; i < outputs[0].samples(); i++) + { + s32 sample = 0; + + for (voice_t &voice : m_voice) + { + if (!voice.m_pitch) continue; + + s16 data = read_byte(voice.m_start + voice.m_pos); + if (!data) + { + voice.m_pos += voice.m_loop; + data = read_byte(voice.m_start + voice.m_pos); + } + sample += (data - 0x80) * voice.m_volume; + + voice.m_pitch_counter += voice.m_pitch; + voice.m_pos += (voice.m_pitch_counter >> 14); + voice.m_pitch_counter &= 0x3fff; + } + + // Jaminator patent shows 10-bit sampling, assume that's actually true + outputs[0].put_int_clamp(i, sample >> 9, 1 << 9); + } +} diff --git a/src/devices/sound/cf61909.h b/src/devices/sound/cf61909.h new file mode 100644 index 00000000000..dbb165ac27e --- /dev/null +++ b/src/devices/sound/cf61909.h @@ -0,0 +1,63 @@ +// license: BSD-3-Clause +// copyright-holders: Devin Acker + +#ifndef MAME_SOUND_CF61909_H +#define MAME_SOUND_CF61909_H + +#pragma once + +#include "machine/clock.h" +#include "dirom.h" + +#include + + +class cf61909_device : public device_t, public device_sound_interface, public device_rom_interface<18> +{ +public: + cf61909_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + u8 read(offs_t offset); + void write(offs_t offset, u8 data); + + int sync_r() { return m_sample_clock->signal_r(); } + +protected: + virtual void device_add_mconfig(machine_config &config) override; + + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + virtual void rom_bank_pre_change() override; + +private: + /* + * Jaminator patent specifies 11.127 kHz sample rate, but the real thing sounds like it has 4x + * oversampling. The sync clock output (m_sample_clock) seems to fluctuate quite a bit, but + * 11.127 kHz is pretty close to average. + */ + static constexpr unsigned CLOCKS_PER_SAMPLE = 247; // based on 11 MHz clock + + struct voice_t + { + u8 m_regs[16] = {0}; + u32 m_start = 0; + u16 m_loop = 0; + u16 m_pos = 0; + u16 m_pitch = 0; + u16 m_pitch_counter = 0; + u8 m_volume = 0; + }; + std::array m_voice; + + u32 m_data_offset; + + required_device m_sample_clock; + sound_stream *m_stream; +}; + +DECLARE_DEVICE_TYPE(CF61909, cf61909_device) + +#endif // MAME_SOUND_CF61909_H diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 98dca5a7752..3827cdebd11 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -30519,6 +30519,9 @@ jackpool // (c) 1997 Electronic Projects jackpot jackpota +@source:misc/jaminator.cpp +jaminator // 1990 Noise Toys Inc. + @source:misc/jankenmn.cpp jankenmn // (c) 1985 Sunwise diff --git a/src/mame/misc/jaminator.cpp b/src/mame/misc/jaminator.cpp new file mode 100644 index 00000000000..754af617fd8 --- /dev/null +++ b/src/mame/misc/jaminator.cpp @@ -0,0 +1,292 @@ +// license: BSD-3-Clause +// copyright-holders: Devin Acker + +/*************************************************************************** + Noise Toys Inc. "Jaminator" + + This toy guitar was originally sold by Worlds of Wonder in 1990, along with + six optional ROM cartridges. In 1993, it was also licensed to Arrow Micro-Techs + (AMT) and Yamaha, who distributed it along with several new cartridges. + + A message from the development team is used as ROM padding: + """ + (C)1990 Noise Toys Inc + Code by Steve Capps(MAD - Je t'aime beaucoup) + Hardware by Ray DuFlon + Music by Ed Bogas(Des - te amo) + """ + + Main hardware: + U101: "DEVO" sound and mapper ASIC + ("(C)1987 NOISE TOYS INC", "WOW DEVO 33073-01 CF61909N" or "AMT DEVO CF61909N") + U102: OKI MSM80C39 + U104: 1Mbit mask ROM (DIP28, 23C1000 pinout) + + TODO: + - Link cable + - Clickable layout? + +***************************************************************************/ + + +#include "emu.h" + +#include "bus/generic/carts.h" +#include "bus/generic/slot.h" +#include "cpu/mcs48/mcs48.h" +#include "machine/rescap.h" +#include "sound/cf61909.h" +#include "sound/flt_biquad.h" +#include "sound/flt_rc.h" + +#include "softlist_dev.h" +#include "speaker.h" + +namespace { + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class jaminator_state : public driver_device +{ +public: + jaminator_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_devo(*this, "devo"), + m_cart(*this, "cart"), + m_inputs(*this, "COL%u", 1), // labeling from PCB silkscreen + m_bender(*this, "BENDER"), + m_led_power(*this, "led_power") + { } + + void jaminator(machine_config &config); + + void input_sel_w(u8 data); + DECLARE_CUSTOM_INPUT_MEMBER(input_r); + DECLARE_CUSTOM_INPUT_MEMBER(bender_r); + + // link cable not emulated yet, but output needs to be looped back too (used for starting songs, etc) + void link_data_w(u8 data) { m_link_data = data; } + DECLARE_CUSTOM_INPUT_MEMBER(link_data_r) { return m_link_data; } + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + void main_map(address_map &map); + void io_map(address_map &map); + void sound_map(address_map &map); + + required_device m_maincpu; + required_device m_devo; + required_device m_cart; + required_ioport_array<7> m_inputs; + required_ioport m_bender; + output_finder<> m_led_power; + + u8 m_input_sel; + u8 m_link_data; +}; + + +//************************************************************************** +// ADDRESS MAPS +//************************************************************************** + +void jaminator_state::main_map(address_map &map) +{ + // TODO: program ROM banking for executable cartridges (do any exist?) + map(0x000, 0x7ff).mirror(0x800).rom().region("devo", 0); +} + +void jaminator_state::io_map(address_map &map) +{ + map(0x00, 0xff).rw(m_devo, FUNC(cf61909_device::read), FUNC(cf61909_device::write)); +} + +void jaminator_state::sound_map(address_map &map) +{ + map(0x00000, 0x1ffff).rom().region("devo", 0); + map(0x20000, 0x3ffff).nopr(); // cart +} + +//************************************************************************** +// INPUT PORT DEFINITIONS +//************************************************************************** + +static INPUT_PORTS_START( jaminator ) + PORT_START("COL1") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("String 1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("String 2") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("String 3") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Start / Next") + + PORT_START("COL2") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Key 1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Key 2") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Key 3") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Key 4") + + PORT_START("COL3") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Drum Pad 1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Drum Pad 2") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Drum Pad 3") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Finale") + + PORT_START("COL4") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_1) PORT_NAME("Fret 1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_2) PORT_NAME("Fret 2") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_3) PORT_NAME("Fret 3") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_4) PORT_NAME("Fret 4") + + PORT_START("COL5") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_5) PORT_NAME("Fret 5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_6) PORT_NAME("Fret 6") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_7) PORT_NAME("Fret 7") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_8) PORT_NAME("Fret 8") + + PORT_START("COL6") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_9) PORT_NAME("Fret 9") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_0) PORT_NAME("Fret 10") + PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Fret 11") + PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Fret 12") + + PORT_START("COL7") + PORT_BIT(0xf, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(jaminator_state, bender_r) + + PORT_START("BENDER") + PORT_BIT(0xff, 0x78, IPT_PADDLE) PORT_NAME("Bender Bar") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xef) + + PORT_START("P1") + PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(jaminator_state, input_r) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OUTPUT ) // link cable clock + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(jaminator_state, link_data_w) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Select") + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(jaminator_state, link_data_r) + + PORT_START("P2") + PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(jaminator_state, input_sel_w) + + /* + * T0 is connected to pin 1 on the link port, which is pulled up by a 10k resistor. + * Connecting it to ground causes percussion tracks to be omitted when playing songs + */ + PORT_START("T0") + PORT_CONFNAME(0x1, 0x1, "Percussion Tracks") + PORT_CONFSETTING(0x0, DEF_STR( Off )) + PORT_CONFSETTING(0x1, DEF_STR( On )) +INPUT_PORTS_END + + +//************************************************************************** +// MACHINE EMULATION +//************************************************************************** + +void jaminator_state::machine_start() +{ + m_led_power.resolve(); + m_input_sel = 0; + m_link_data = 0; + + if (m_cart->exists()) + m_devo->space().install_read_handler(0x20000, 0x3ffff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom))); + + save_item(NAME(m_input_sel)); + save_item(NAME(m_link_data)); +} + +//************************************************************************** +void jaminator_state::machine_reset() +{ + m_led_power = 1; +} + +//************************************************************************** +void jaminator_state::input_sel_w(u8 data) +{ + m_input_sel = data & 0xf; + + if (m_input_sel == 0x7) + { + m_led_power = 0; + m_devo->reset(); + m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + } +} + +//************************************************************************** +CUSTOM_INPUT_MEMBER(jaminator_state::input_r) +{ + if (m_input_sel < 0x7) + return m_inputs[m_input_sel]->read(); + + return 0; +} + +//************************************************************************** +CUSTOM_INPUT_MEMBER(jaminator_state::bender_r) +{ + // the bender PCB only has 15 contact positions (0-14), but the ROM recognizes 16 values + static const u8 bendval[] = { + 0xf, 0x7, 0x3, 0xb, 0x9, 0x1, 0x5, 0xd, + 0xc, 0x4, 0x0, 0x8, 0xa, 0x2, 0x6, 0xe + }; + return bendval[m_bender->read() >> 4]; +} + +//************************************************************************** +// MACHINE DEFINTIONS +//************************************************************************** + +void jaminator_state::jaminator(machine_config &config) +{ + I8039(config, m_maincpu, 11_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &jaminator_state::main_map); + m_maincpu->set_addrmap(AS_IO, &jaminator_state::io_map); + m_maincpu->p1_in_cb().set_ioport("P1"); + m_maincpu->p1_out_cb().set_ioport("P1"); + m_maincpu->p2_out_cb().set_ioport("P2"); + m_maincpu->t0_in_cb().set_ioport("T0"); + + GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "jaminator", "bin"); + SOFTWARE_LIST(config, "cart_list").set_original("jaminator"); + + SPEAKER(config, "speaker").front_center(); + + CF61909(config, m_devo, 11_MHz_XTAL); + m_devo->set_addrmap(0, &jaminator_state::sound_map); + m_devo->add_route(0, "rcfilter", 1.0); + m_maincpu->t1_in_cb().set(m_devo, FUNC(cf61909_device::sync_r)); + + filter_rc_device &rcfilter(FILTER_RC(config, "rcfilter")); + rcfilter.set_lowpass(RES_R(510) + RES_K(15), CAP_N(6.8)); + rcfilter.add_route(0, "biquad", 1.0); + + filter_biquad_device &biquad(FILTER_BIQUAD(config, "biquad")); + biquad.opamp_sk_lowpass_setup(RES_K(10), RES_K(10), RES_K(39), RES_K(1), CAP_N(6.8), CAP_N(6.8)); + biquad.add_route(0, "speaker", 1.0); +} + + +//************************************************************************** +// ROM DEFINITIONS +//************************************************************************** + +ROM_START( jaminator ) + ROM_REGION(0x20000, "devo", 0) + ROM_LOAD("amta361.u104", 0x00000, 0x20000, CRC(f3f798ed) SHA1(08bef43e9689608f40a57b77724de5f6d2652693)) +ROM_END + +} // anonymous namespace + + +//************************************************************************** +// SYSTEM DRIVERS +//************************************************************************** + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +SYST( 1990, jaminator, 0, 0, jaminator, jaminator, jaminator_state, empty_init, "Noise Toys Inc.", "Jaminator", MACHINE_SUPPORTS_SAVE ) -- cgit v1.2.3