From b46f3beddcf5ae7692bed06afd7c91c5eb959712 Mon Sep 17 00:00:00 2001 From: Devin Acker Date: Sat, 25 Nov 2023 10:03:22 -0500 Subject: [PATCH] casio/cz1.cpp: Added driver for Casio CZ-1 and MZ-1 prototype. (#11779) * casio/ra3.cpp: Added support for software list ROM cartridges. * sound/upd933.cpp: Clear pending interrupt flag when read. * sound/upd933.cpp: Implemented presumed pitch modulator register. New working systems -------------------- Casio CZ-1 [Mastropiero, Devin Acker] New working clones -------------------- Casio MZ-1 (prototype) [Mastropiero, Devin Acker] New working software items (cz1_cart.xml) ------------------------------------ Orchestra (RC-10) [Edward d-tech] Organ / Synth. Sound / Sound Effect (RC-30) [Edward d-tech] Piano / Guitar / Percussion (RC-20) [unknown] --- hash/cz1_cart.xml | 44 ++ scripts/src/machine.lua | 12 + src/devices/machine/msm6200.cpp | 99 ++++ src/devices/machine/msm6200.h | 40 ++ src/devices/sound/upd933.cpp | 60 +- src/devices/sound/upd933.h | 2 +- src/mame/casio/cz1.cpp | 950 ++++++++++++++++++++++++++++++++ src/mame/casio/cz101.cpp | 6 +- src/mame/casio/ra3.cpp | 80 ++- src/mame/casio/ra3.h | 26 +- src/mame/layout/cz1.lay | 779 ++++++++++++++++++++++++++ src/mame/layout/mz1.lay | 654 ++++++++++++++++++++++ src/mame/mame.lst | 4 + 13 files changed, 2709 insertions(+), 47 deletions(-) create mode 100644 hash/cz1_cart.xml create mode 100644 src/devices/machine/msm6200.cpp create mode 100644 src/devices/machine/msm6200.h create mode 100644 src/mame/casio/cz1.cpp create mode 100644 src/mame/layout/cz1.lay create mode 100644 src/mame/layout/mz1.lay diff --git a/hash/cz1_cart.xml b/hash/cz1_cart.xml new file mode 100644 index 00000000000..00da859cc30 --- /dev/null +++ b/hash/cz1_cart.xml @@ -0,0 +1,44 @@ + + + + + + + Orchestra (RC-10) + 1986 + Casio + + + + + + + + + + Piano / Guitar / Percussion (RC-20) + 1986 + Casio + + + + + + + + + + Organ / Synth. Sound / Sound Effect (RC-30) + 1986 + Casio + + + + + + + + + diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index cae26f7e89f..e6b9f0cb8fa 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2652,6 +2652,18 @@ if (MACHINES["MSM58321"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/msm6200.h,MACHINES["MSM6200"] = true +--------------------------------------------------- + +if (MACHINES["MSM6200"]~=null) then + files { + MAME_DIR .. "src/devices/machine/msm6200.cpp", + MAME_DIR .. "src/devices/machine/msm6200.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/msm6242.h,MACHINES["MSM6242"] = true diff --git a/src/devices/machine/msm6200.cpp b/src/devices/machine/msm6200.cpp new file mode 100644 index 00000000000..70e05aa1ddf --- /dev/null +++ b/src/devices/machine/msm6200.cpp @@ -0,0 +1,99 @@ +// license:BSD-3-Clause +// copyright-holders:Devin Acker +/*************************************************************************** + OKI MSM6200 keyboard controller (HLE) +***************************************************************************/ + +#include "emu.h" +#include "msm6200.h" + +DEFINE_DEVICE_TYPE(MSM6200, msm6200_device, "msm6200", "OKI MSM6200 keyboard controller") + +/**************************************************************************/ +msm6200_device::msm6200_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, MSM6200, tag, owner, clock), + m_keys(*this, "KI%u", 1u), + m_velocity(*this, "VELOCITY"), + m_irq_cb(*this) +{ +} + +/**************************************************************************/ +void msm6200_device::device_start() +{ + m_cmd = 0xf; + + save_item(NAME(m_cmd)); + save_item(NAME(m_row)); + save_item(NAME(m_key_data)); + save_item(NAME(m_key_state)); + save_item(NAME(m_last_state)); +} + +/**************************************************************************/ +void msm6200_device::device_reset() +{ + m_row = 0; + m_key_data = 0; + m_key_state = 0; + std::fill(std::begin(m_last_state), std::end(m_last_state), 0); +} + +/**************************************************************************/ +void msm6200_device::write(offs_t offset, u8 data) +{ + // 8-bit multiplexed address/data bus, upper 4 bits are output only + // on write, the lower 4 bits of the address are latched and the data is ignored + m_cmd = offset & 0xf; + m_key_data = 0xff; + + switch (m_cmd) + { + case 0: // read key number + for (int i = 0; i < 2; i++) + { + if (BIT(m_key_state ^ m_last_state[m_row], i)) + { + m_last_state[m_row] ^= (1 << i); + m_key_data = (BIT(m_key_state, i) << 7) | ((m_row + 1) << 1) | i; + break; + } + } + if (m_key_state == m_last_state[m_row]) + m_irq_cb(0); + break; + + case 1: // read velocity + m_key_data = m_velocity.read_safe(0x3f); + break; + + case 2: // next row? + (++m_row) %= m_keys.size(); + // TODO: what should this one actually be? + // the cz1/ht6000 key MCU code outputs the result to port 1 for debugging + m_key_data = m_row; + break; + + case 7: // capture current row? + m_key_state = m_keys[m_row].read_safe(0); + if (m_key_state != m_last_state[m_row]) + m_irq_cb(1); + break; + + case 8: // init all rows + for (int i = 0; i < m_keys.size(); i++) + m_last_state[i] = m_keys[i].read_safe(0); + m_irq_cb(0); + break; + + default: + logerror("%s: unknown cmd 0x%x\n", machine().describe_context(), m_cmd); + break; + } +} + +/**************************************************************************/ +u8 msm6200_device::read() +{ + return m_key_data; +} diff --git a/src/devices/machine/msm6200.h b/src/devices/machine/msm6200.h new file mode 100644 index 00000000000..034950d10e7 --- /dev/null +++ b/src/devices/machine/msm6200.h @@ -0,0 +1,40 @@ +// license:BSD-3-Clause +// copyright-holders: Devin Acker +/*************************************************************************** + OKI MSM6200 keyboard controller (HLE) +***************************************************************************/ + +#ifndef MAME_MACHINE_MSM6200_H +#define MAME_MACHINE_MSM6200_H + +#pragma once + +class msm6200_device : public device_t +{ +public: + msm6200_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + auto irq_cb() { return m_irq_cb.bind(); } + + void write(offs_t offset, u8 data); + u8 read(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + optional_ioport_array<38> m_keys; + optional_ioport m_velocity; + + devcb_write_line m_irq_cb; + + u8 m_cmd, m_row, m_key_data; + u8 m_key_state; + u8 m_last_state[38]; +}; + +// device type definition +DECLARE_DEVICE_TYPE(MSM6200, msm6200_device) + +#endif // MAME_MACHINE_MSM6200_H diff --git a/src/devices/sound/upd933.cpp b/src/devices/sound/upd933.cpp index 8b9ae286b1b..38c352ce78c 100644 --- a/src/devices/sound/upd933.cpp +++ b/src/devices/sound/upd933.cpp @@ -115,6 +115,9 @@ void upd933_device::device_reset() m_sample_count = 0; m_last_sample = 0; + + m_irq_timer->adjust(attotime::never); + m_irq_cb(0); } /**************************************************************************/ @@ -153,23 +156,35 @@ void upd933_device::id_w(int state) } /**************************************************************************/ -u8 upd933_device::irq_data() const +u8 upd933_device::irq_data() { // TODO: do these have the correct priority? for (int i = 0; i < 8; i++) { if (m_dco[i].m_irq) + { + if (!machine().side_effects_disabled()) + m_dco[i].m_irq = false; return 4 | (i << 3); + } } for (int i = 0; i < 8; i++) { if (m_dcw[i].m_irq) + { + if (!machine().side_effects_disabled()) + m_dcw[i].m_irq = false; return 2 | (i << 2); + } } for (int i = 0; i < 8; i++) { if (m_dca[i].m_irq) + { + if (!machine().side_effects_disabled()) + m_dca[i].m_irq = false; return 1 | (i << 1); + } } return 0; } @@ -188,10 +203,10 @@ void upd933_device::update_pending_irq() | m_dcw[i].calc_timeout(new_time)); } - if (!new_time) - m_irq_pending = 1; - else if (env_active) + if (env_active) m_irq_timer->adjust(clocks_to_attotime((u64)new_time * CLOCKS_PER_SAMPLE)); + else + m_irq_timer->adjust(attotime::never); } /**************************************************************************/ @@ -223,6 +238,7 @@ void upd933_device::write(u8 data) { m_stream->update(); + bool ok = true; const u8 reg = m_sound_data[0]; const u16 value = m_sound_regs[reg] = (m_sound_data[1] << 8) | data; @@ -310,10 +326,13 @@ void upd933_device::write(u8 data) else voice.m_wave[1] = voice.m_wave[0]; voice.m_window = BIT(value, 6, 3); - // see earlier comment - these bits actually control a different voice - mod_voice.m_ring_mod = BIT(value, 5); - mod_voice.m_pitch_mod = BIT(value, 3, 2); - mod_voice.m_mute_other = BIT(value, 2); + if (!BIT(vnum, 0)) + { + // see earlier comment - these bits actually control a different voice + mod_voice.m_ring_mod = BIT(value, 5); + mod_voice.m_pitch_mod = BIT(value, 3, 2); + mod_voice.m_mute_other = BIT(value, 2); + } break; case 0x13: // 98-9f: phase counter @@ -324,11 +343,20 @@ void upd933_device::write(u8 data) voice.m_position = value << (PITCH_SHIFT - 4); break; + case 0x17: // b8-bb: pitch modulator (probably - cz1 sets to zero when disabling noise) + if (vnum < 4) + m_voice[vnum << 1].m_pm_level = (s16)value; + else + ok = false; + break; + default: - logerror("%s: unknown sound reg write: %02x %04x\n", - machine().describe_context(), reg, value); + ok = false; break; } + + if (!ok) + logerror("%s: unknown sound reg write: %02x %04x\n", machine().describe_context(), reg, value); } else { @@ -554,20 +582,20 @@ void upd933_device::env_t::update() } if (!m_sustain && (m_current == m_target)) - m_irq = true; + m_irq = m_sustain = true; // set sustain too to make sure this only causes an interrupt once } /**************************************************************************/ bool upd933_device::env_t::calc_timeout(unsigned &samples) { - if (m_sustain || !m_rate) - { - return false; - } - else if (m_irq) + if (m_irq) { samples = 0; } + else if (m_sustain || !m_rate) + { + return false; + } else { const unsigned remaining = m_direction ? (m_current - m_target) : (m_target - m_current); diff --git a/src/devices/sound/upd933.h b/src/devices/sound/upd933.h index 7c2747a654a..b4023fb8311 100644 --- a/src/devices/sound/upd933.h +++ b/src/devices/sound/upd933.h @@ -67,7 +67,7 @@ private: TIMER_CALLBACK_MEMBER(timer_tick); s16 update(int vnum); - u8 irq_data() const; + u8 irq_data(); void update_pending_irq(); void update_irq(); diff --git a/src/mame/casio/cz1.cpp b/src/mame/casio/cz1.cpp new file mode 100644 index 00000000000..d3f9e59eb2e --- /dev/null +++ b/src/mame/casio/cz1.cpp @@ -0,0 +1,950 @@ +// license: BSD-3-Clause +// copyright-holders: Devin Acker +/*************************************************************************** + + Casio CZ-1 Digital Synthesizer + + Also includes support for the MZ-1, an unreleased rack mount version supported by + the same ROM. This has no key/wheel/pedal inputs and never sends the corresponding + MIDI messages, but otherwise works identically to the CZ-1. + + Misc. notes: + + Hold all three Line 1 envelope buttons (DCO/DCW/DCA) on boot to perform a RAM test. + + Afterwards, the firmware will attempt to load and run a program from cartridge + if a valid 5-byte header is detected at the beginning: + - bytes 0-1: ignored + - bytes 2-3: program load address (valid within 0x8000-9fff, includes this header) + - byte 4: constant 0xD1 + - bytes 5+: start of program + + TODO: + + Both machines have MACHINE_IMPERFECT_SOUND due to unemulated stereo chorus. + +***************************************************************************/ + +#include "emu.h" + +#include "ra3.h" +#include "bus/midi/midiinport.h" +#include "bus/midi/midioutport.h" +#include "cpu/mcs48/mcs48.h" +#include "cpu/upd7810/upd7811.h" +#include "machine/clock.h" +#include "machine/input_merger.h" +#include "machine/msm6200.h" +#include "machine/nvram.h" +#include "sound/mixer.h" +#include "sound/upd933.h" +#include "video/hd44780.h" + +#include "emupal.h" +#include "screen.h" +#include "softlist.h" +#include "speaker.h" + +#include + +#include "cz1.lh" +#include "mz1.lh" + +namespace { + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class cz1_state : public driver_device +{ +public: + cz1_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_subcpu(*this, "subcpu"), + m_mcu(*this, "mcu"), + m_hd44780(*this, "hd44780"), + m_upd933(*this, "upd933_%u", 0U), + m_cart(*this, "cart"), + m_mixer(*this, "mixer%u", 0U), + m_keys(*this, "KC%u", 0U), + m_leds(*this, "led_%u.%u", 0U, 0U), + m_led_env(*this, "led_env%u", 0U), + m_led_bank(*this, "led_bank%u", 0U), + m_led_tone(*this, "led_tone%u", 0U) + { } + + void mz1(machine_config &config); + void cz1(machine_config &config); + + int cont_r(); + int sync_r(); + + int cont49_r(); + int sync49_r(); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + void cz1_palette(palette_device &palette) const; + HD44780_PIXEL_UPDATE(lcd_pixel_update); + + void mz1_main_map(address_map &map); + void cz1_main_map(address_map &map); + void sub_map(address_map &map); + void mcu_map(address_map &map); + + // main CPU r/w methods + u8 keys_r(); + void led_w(offs_t offset, u8 data); + void volume_w(u8 data); + void stereo_w(u8 data); + + void cart_addr_w(u8 data); + void cart_addr_hi_w(u8 data); + + void main_pa_w(u8 data); + u8 main_pa_r(); + void main_pb_w(u8 data); + void main_pc_w(u8 data); + + // sub CPU r/w methods + void sound_w(u8 data); + u8 sound_r(); + + void sub_pa_w(u8 data); + void sub_pb_w(u8 data); + void sub_pc_w(u8 data); + + // main/sub CPU comm methods + void main_to_sub_0_w(u8 data); + TIMER_CALLBACK_MEMBER(main_to_sub_0_cb); + u8 main_to_sub_0_r(); + void main_to_sub_1_w(u8 data); + TIMER_CALLBACK_MEMBER(main_to_sub_1_cb); + u8 main_to_sub_1_r(); + void sub_to_main_w(u8 data); + TIMER_CALLBACK_MEMBER(sub_to_main_cb); + u8 sub_to_main_r(); + + void sync_clr_w(u8); + TIMER_CALLBACK_MEMBER(sync_clr_cb); + + void main_irq_w(u8); + void main_irq_ack_w(u8); + + // main CPU / key MCU comm methods + u8 mcu_r(); + void mcu_p2_w(u8 data); + + required_device m_maincpu; + required_device m_subcpu; + optional_device m_mcu; + required_device m_hd44780; + required_device_array m_upd933; + required_device m_cart; + required_device_array m_mixer; + optional_ioport_array<16> m_keys; + output_finder<5, 6> m_leds; + output_finder<16> m_led_env; + output_finder<8> m_led_bank, m_led_tone; + + float m_volume[0x40]; + + u8 m_main_port[3]; + u8 m_mcu_p2; + u8 m_midi_rx; + + u8 m_main_to_sub[2]; + u8 m_sub_to_main; + u8 m_sync, m_sync49; + + u16 m_cart_addr; +}; + + +//************************************************************************** +// ADDRESS MAPS +//************************************************************************** + +void cz1_state::mz1_main_map(address_map &map) +{ + map.unmap_value_high(); + + map(0x0000, 0x7fff).rom(); + map(0x8000, 0x9fff).ram().share("mainram"); + map(0xb000, 0xbfff).w(FUNC(cz1_state::volume_w)); + map(0xc000, 0xc000).mirror(0x1ff0).w(FUNC(cz1_state::main_to_sub_0_w)); + map(0xc001, 0xc001).mirror(0x1ff0).w(FUNC(cz1_state::main_to_sub_1_w)); + map(0xc002, 0xc002).mirror(0x1ff0).r(FUNC(cz1_state::sub_to_main_r)); + map(0xc004, 0xc004).mirror(0x1ff0).r(FUNC(cz1_state::keys_r)); + map(0xc005, 0xc005).mirror(0x1ff0).w(FUNC(cz1_state::sync_clr_w)); + map(0xc006, 0xc006).mirror(0x1ff0).w(FUNC(cz1_state::main_irq_ack_w)); + map(0xc007, 0xc00b).mirror(0x1ff0).w(FUNC(cz1_state::led_w)); + map(0xc00c, 0xc00c).mirror(0x1ff0).w(FUNC(cz1_state::stereo_w)); + map(0xc00e, 0xc00e).mirror(0x1ff0).w(FUNC(cz1_state::cart_addr_w)); + map(0xc00f, 0xc00f).mirror(0x1ff0).w(FUNC(cz1_state::cart_addr_hi_w)); +} + +/**************************************************************************/ +void cz1_state::cz1_main_map(address_map &map) +{ + mz1_main_map(map); + map(0xc00d, 0xc00d).mirror(0x1ff0).r(FUNC(cz1_state::mcu_r)); +} + +/**************************************************************************/ +void cz1_state::sub_map(address_map &map) +{ + map.unmap_value_high(); + + map(0x0000, 0x3fff).rom(); + map(0x4000, 0x7fff).ram().share("subram"); + map(0x8000, 0x9fff).rw(FUNC(cz1_state::sound_r), FUNC(cz1_state::sound_w)); + map(0xa000, 0xbfff).rw(FUNC(cz1_state::main_to_sub_1_r), FUNC(cz1_state::sub_to_main_w)); + map(0xc000, 0xdfff).rw(FUNC(cz1_state::main_to_sub_0_r), FUNC(cz1_state::main_irq_w)); +} + +/**************************************************************************/ +void cz1_state::mcu_map(address_map &map) +{ + map(0x00, 0xff).rw("kbd", FUNC(msm6200_device::read), FUNC(msm6200_device::write)); +} + +//************************************************************************** +// INPUT PORT DEFINITIONS +//************************************************************************** + +static INPUT_PORTS_START( mz1 ) + + PORT_START("KC0") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Normal") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Mix") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Key Split") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Operation Memory") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Solo") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MIDI") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC1") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Portamento On/Off") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Glide On/Off") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Key Transpose") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Master Tune") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Exchange") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cartridge") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC2") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Bank A") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Bank B") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Bank C") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Bank D") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Bank E") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Bank F") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC3") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Memory 5") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Memory 6") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Memory 7") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Memory 8") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Bank G") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Bank H") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC4") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Cursor Left / No") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right / Yes") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Memory 1") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Memory 2") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Memory 3") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Memory 4") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC5") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Page Down") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Page Up") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Env. Point Sustain") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Env. Point End") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME("Value Down / Save") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Value Up / Load") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC6") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Wheel / Aftertouch") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Bend Range") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Glide") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Portamento") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Name") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cartridge/MIDI Save/Load") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC7") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Initialize") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Octave") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Vibrato") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Line Select") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Ring") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Noise") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC8") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 1 Wave") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 1 Env") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 1 Key Follow") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 1 Env") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Key Follow") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Env") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC9") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 2 Wave") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 2 Env") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 2 Key Follow") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 2 Env") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Key Follow") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Env") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC10") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Velocity") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Velocity") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Level") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Level") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Parameter Copy") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Detune") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC11") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MIDI On/Off") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Compare/Recall") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Write") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Modulation On/Off") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_TOGGLE PORT_NAME("Memory Protect") + PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("KC12") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", casio_ram_cart_device, exists) + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) // low = MZ-1, high = CZ-1 + PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("MAIN_PB") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(cz1_state, sync_r) + PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("SUB_PB") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933_0", upd933_device, rq_r) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933_1", upd933_device, rq_r) + PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("SUB_PC") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(cz1_state, sync_r) + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(cz1_state, cont_r) + PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED) +INPUT_PORTS_END + +static INPUT_PORTS_START( cz1 ) + PORT_INCLUDE(mz1) + + PORT_START("kbd:KI8") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6") + + PORT_START("kbd:KI9") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#5") + + PORT_START("kbd:KI10") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#5") + + PORT_START("kbd:KI11") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#5") + + PORT_START("kbd:KI12") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5") + + PORT_START("kbd:KI13") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5") + + PORT_START("kbd:KI14") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#5") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5") + + PORT_START("kbd:KI15") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#4") + + PORT_START("kbd:KI16") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#4") + + PORT_START("kbd:KI17") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#4") + + PORT_START("kbd:KI18") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4") + + PORT_START("kbd:KI19") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4") + + PORT_START("kbd:KI20") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#4") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4") + + PORT_START("kbd:KI21") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#3") + + PORT_START("kbd:KI22") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#3") + + PORT_START("kbd:KI23") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#3") + + PORT_START("kbd:KI24") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3") + + PORT_START("kbd:KI25") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3") + + PORT_START("kbd:KI26") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#3") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3") + + PORT_START("kbd:KI27") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#2") + + PORT_START("kbd:KI28") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#2") + + PORT_START("kbd:KI29") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#2") + + PORT_START("kbd:KI30") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2") + + PORT_START("kbd:KI31") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2") + + PORT_START("kbd:KI32") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#2") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2") + + PORT_START("kbd:KI33") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#1") + + PORT_START("kbd:KI34") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#1") + + PORT_START("kbd:KI35") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#1") + + PORT_START("kbd:KI36") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1") + + PORT_START("kbd:KI37") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D1") + + PORT_START("kbd:KI38") + PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#1") + PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C1") + + PORT_START("kbd:VELOCITY") + PORT_BIT(0x3f, 0x3f, IPT_POSITIONAL_V) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) + + PORT_START("AN0") + PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH) + + PORT_START("AN1") + PORT_BIT(0xff, 0x00, IPT_POSITIONAL_V) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH) + + PORT_START("AN2") + PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Aftertouch") PORT_REVERSE PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(3) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH) + + PORT_MODIFY("KC11") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Sustain Pedal") + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_MODIFY("KC12") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_CUSTOM) // low = MZ-1, high = CZ-1 + + PORT_START("MAIN_PC") + PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(cz1_state, cont49_r) + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(cz1_state, sync49_r) + PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) +INPUT_PORTS_END + + +//************************************************************************** +// MACHINE EMULATION +//************************************************************************** + +void cz1_state::cz1_palette(palette_device &palette) const +{ + palette.set_pen_color(0, rgb_t(138, 146, 148)); // background + palette.set_pen_color(1, rgb_t( 63, 59, 62)); // LCD pixel on + palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off +} + +/**************************************************************************/ +HD44780_PIXEL_UPDATE( cz1_state::lcd_pixel_update ) +{ + // char size is 5x8 + if (x > 4 || y > 7) + return; + + if (line < 2 && pos < 16) + bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2; +} + + +/**************************************************************************/ +u8 cz1_state::keys_r() +{ + return m_keys[m_main_port[0] & 0xf].read_safe(0xffff); +} + +/**************************************************************************/ +void cz1_state::led_w(offs_t offset, u8 data) +{ + for (int i = 0; i < 6; i++) + m_leds[offset][i] = BIT(data, i); +} + +/**************************************************************************/ +void cz1_state::volume_w(u8 data) +{ + const float vol = m_volume[~data & 0x3f]; + m_mixer[0]->set_output_gain(ALL_OUTPUTS, vol); + m_mixer[1]->set_output_gain(ALL_OUTPUTS, vol); +} + +/**************************************************************************/ +void cz1_state::stereo_w(u8 data) +{ + /* + bit 0: sound chip #1 routing (0: center, 1: left) + bit 1: sound chip #2 routing (0: center, 2: right) + bit 2: center channel stereo chorus (0: on, 1: off) + */ + m_mixer[0]->set_input_gain(1, BIT(data, 1) ? 0.0 : 1.0); + m_mixer[1]->set_input_gain(0, BIT(data, 0) ? 0.0 : 1.0); +} + +/**************************************************************************/ +void cz1_state::cart_addr_w(u8 data) +{ + m_cart_addr &= 0x3f00; + m_cart_addr |= ~data; +} + +/**************************************************************************/ +void cz1_state::cart_addr_hi_w(u8 data) +{ + m_cart_addr &= 0xff; + m_cart_addr |= (~data & 0x3f) << 8; +} + +/**************************************************************************/ +void cz1_state::main_pa_w(u8 data) +{ + m_hd44780->db_w(data); + m_main_port[0] = data; +} + +/**************************************************************************/ +u8 cz1_state::main_pa_r() +{ + u8 data = m_hd44780->db_r(); + if (!BIT(m_main_port[1], 2)) + data &= m_cart->read(m_cart_addr); + return data; +} + +/**************************************************************************/ +void cz1_state::main_pb_w(u8 data) +{ + if (BIT(data ^ m_main_port[1], 2)) + m_subcpu->set_input_line(UPD7810_INTF1, BIT(data, 2)); + + if (!BIT(data, 6) && BIT(m_main_port[1], 6) && !BIT(m_main_port[2], 2)) + m_cart->write(m_cart_addr, m_main_port[0]); + + m_hd44780->e_w(BIT(~data, 7)); + m_hd44780->rw_w(BIT(data, 6)); + m_hd44780->rs_w(BIT(data, 5)); + + m_main_port[1] = data; +} + +/**************************************************************************/ +void cz1_state::main_pc_w(u8 data) +{ + m_main_port[2] = data; +} + + +/**************************************************************************/ +void cz1_state::sound_w(u8 data) +{ + m_upd933[0]->write(data); + m_upd933[1]->write(data); +} + +/**************************************************************************/ +u8 cz1_state::sound_r() +{ + return m_upd933[0]->read() & m_upd933[1]->read(); +} + +/**************************************************************************/ +void cz1_state::sub_pa_w(u8 data) +{ + for (int i = 0; i < 15; i++) + m_led_env[i] = (BIT(data, 0, 4) == i); + for (int i = 0; i < 8; i++) + m_led_tone[i] = !BIT(data, 7) && (BIT(data, 4, 3) == i); +} + +/**************************************************************************/ +void cz1_state::sub_pb_w(u8 data) +{ + for (int i = 0; i < 2; i++) + { + m_upd933[i]->id_w(BIT(data, 5)); + m_upd933[i]->cs_w(BIT(data, 2 + i)); + + m_upd933[i]->set_output_gain(ALL_OUTPUTS, BIT(data, 6) ? 0.0 : 1.0); + } +} + +/**************************************************************************/ +void cz1_state::sub_pc_w(u8 data) +{ + for (int i = 0; i < 8; i++) + m_led_bank[i] = !BIT(data, 0) && (BIT(data, 5, 3) == i); +} + +/**************************************************************************/ +void cz1_state::main_to_sub_0_w(u8 data) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::main_to_sub_0_cb), this), data); +} + +/**************************************************************************/ +TIMER_CALLBACK_MEMBER(cz1_state::main_to_sub_0_cb) +{ + m_main_to_sub[0] = param; + m_sync = 1; +} + +/**************************************************************************/ +u8 cz1_state::main_to_sub_0_r() +{ + if (!machine().side_effects_disabled()) + m_sync = 0; + return m_main_to_sub[0]; +} + +/**************************************************************************/ +void cz1_state::main_to_sub_1_w(u8 data) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::main_to_sub_1_cb), this), data); +} + +/**************************************************************************/ +TIMER_CALLBACK_MEMBER(cz1_state::main_to_sub_1_cb) +{ + m_main_to_sub[1] = param; +} + +/**************************************************************************/ +u8 cz1_state::main_to_sub_1_r() +{ + return m_main_to_sub[1]; +} + +/**************************************************************************/ +void cz1_state::sub_to_main_w(u8 data) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::sub_to_main_cb), this), data); +} + +/**************************************************************************/ +TIMER_CALLBACK_MEMBER(cz1_state::sub_to_main_cb) +{ + m_sub_to_main = param; +} + +/**************************************************************************/ +u8 cz1_state::sub_to_main_r() +{ + return m_sub_to_main; +} + +/**************************************************************************/ +int cz1_state::cont_r() +{ + return BIT(m_main_port[1], 3); +} + +/**************************************************************************/ +int cz1_state::sync_r() +{ + return m_sync; +} + +/**************************************************************************/ +void cz1_state::sync_clr_w(u8) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::sync_clr_cb), this), 0); +} + +/**************************************************************************/ +TIMER_CALLBACK_MEMBER(cz1_state::sync_clr_cb) +{ + m_sync = 0; +} + +/**************************************************************************/ +void cz1_state::main_irq_w(u8) +{ + m_maincpu->set_input_line(UPD7810_INTF1, ASSERT_LINE); +} + +/**************************************************************************/ +void cz1_state::main_irq_ack_w(u8) +{ + m_maincpu->set_input_line(UPD7810_INTF1, CLEAR_LINE); +} + +/**************************************************************************/ +u8 cz1_state::mcu_r() +{ + if (!machine().side_effects_disabled()) + m_sync49 = 0; + + return ~m_mcu->p1_r(); +} + +/**************************************************************************/ +void cz1_state::mcu_p2_w(u8 data) +{ + if (BIT(data ^ m_mcu_p2, 6)) + m_maincpu->set_input_line(UPD7810_INTF2, BIT(~data, 6)); + + if (BIT(~data & m_mcu_p2, 7)) + m_sync49 = 1; + + m_mcu_p2 = data; +} + +/**************************************************************************/ +int cz1_state::cont49_r() +{ + return BIT(m_mcu_p2, 5); +} + +/**************************************************************************/ +int cz1_state::sync49_r() +{ + return m_sync49; +} + +/**************************************************************************/ +void cz1_state::machine_start() +{ + m_leds.resolve(); + m_led_env.resolve(); + m_led_bank.resolve(); + m_led_tone.resolve(); + + // aftertouch amp levels (TODO: are these correct?) + for (int i = 0; i < 0x40; i++) + m_volume[i] = pow(2, (float)i / 0x3f) - 1.0; + + m_main_port[0] = m_main_port[1] = m_main_port[2] = 0xff; + m_mcu_p2 = 0xff; + + // register for save states + save_item(NAME(m_main_port)); + save_item(NAME(m_mcu_p2)); + save_item(NAME(m_midi_rx)); + save_item(NAME(m_cart_addr)); + save_item(NAME(m_main_to_sub)); + save_item(NAME(m_sub_to_main)); + save_item(NAME(m_sync)); + save_item(NAME(m_sync49)); +} + +/**************************************************************************/ +void cz1_state::machine_reset() +{ + m_main_to_sub[0] = m_main_to_sub[1] = 0; + m_sub_to_main = 0; + m_sync = 0; + m_sync49 = 1; + + m_cart_addr = 0; + m_midi_rx = 1; +} + + +//************************************************************************** +// MACHINE DEFINTIONS +//************************************************************************** + +void cz1_state::mz1(machine_config &config) +{ + UPD7810(config, m_maincpu, 15_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &cz1_state::mz1_main_map); + m_maincpu->pa_in_cb().set(FUNC(cz1_state::main_pa_r)); + m_maincpu->pa_out_cb().set(FUNC(cz1_state::main_pa_w)); + m_maincpu->pb_in_cb().set_ioport("MAIN_PB"); + m_maincpu->pb_out_cb().set(FUNC(cz1_state::main_pb_w)); + m_maincpu->pc_out_cb().set(FUNC(cz1_state::main_pc_w)); + + CLOCK(config, "midi_clock", 2_MHz_XTAL).signal_handler().set(m_maincpu, FUNC(upd7810_device::sck_w)); + + UPD7810(config, m_subcpu, 15_MHz_XTAL); + m_subcpu->set_addrmap(AS_PROGRAM, &cz1_state::sub_map); + m_subcpu->pa_out_cb().set(FUNC(cz1_state::sub_pa_w)); + m_subcpu->pb_in_cb().set_ioport("SUB_PB"); + m_subcpu->pb_out_cb().set(FUNC(cz1_state::sub_pb_w)); + m_subcpu->pc_in_cb().set_ioport("SUB_PC"); + m_subcpu->pc_out_cb().set(FUNC(cz1_state::sub_pc_w)); + + INPUT_MERGER_ANY_HIGH(config, "irq").output_handler().set_inputline(m_subcpu, UPD7810_INTF2); + + NVRAM(config, "mainram"); + NVRAM(config, "subram"); + CASIO_RA6(config, m_cart); + SOFTWARE_LIST(config, "cart_list").set_original("cz1_cart"); + + midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin")); + mdin.rxd_handler().set([this] (int state) { m_midi_rx = state; }); + m_maincpu->rxd_func().set([this] () { return m_midi_rx; }); + + MIDI_PORT(config, "mdout", midiout_slot, "midiout"); + m_maincpu->txd_func().set("mdout", FUNC(midi_port_device::write_txd)); + + MIDI_PORT(config, "mdthru", midiout_slot, "midiout"); + mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd)); + + // video hardware + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); + screen.set_refresh_hz(50); + screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ + screen.set_size(6*16 + 1, 19); + screen.set_visarea_full(); + screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update)); + screen.set_palette("palette"); + + PALETTE(config, "palette", FUNC(cz1_state::cz1_palette), 3); + + HD44780(config, m_hd44780, 250'000); // TODO: clock not measured, datasheet typical clock used + m_hd44780->set_lcd_size(2, 16); + m_hd44780->set_function_set_at_any_time(); + m_hd44780->set_pixel_update_cb(FUNC(cz1_state::lcd_pixel_update)); + + config.set_default_layout(layout_mz1); + + // sound hardware + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + + MIXER(config, m_mixer[0]).add_route(0, "lspeaker", 1.0); + MIXER(config, m_mixer[1]).add_route(0, "rspeaker", 1.0); + + UPD933(config, m_upd933[0], 8.96_MHz_XTAL / 2); + m_upd933[0]->irq_cb().set("irq", FUNC(input_merger_any_high_device::in_w<0>)); + m_upd933[0]->add_route(0, m_mixer[0], 1.0); + m_upd933[0]->add_route(0, m_mixer[1], 1.0); + + UPD933(config, m_upd933[1], 8.96_MHz_XTAL / 2); + m_upd933[1]->irq_cb().set("irq", FUNC(input_merger_any_high_device::in_w<1>)); + m_upd933[1]->add_route(0, m_mixer[0], 1.0); + m_upd933[1]->add_route(0, m_mixer[1], 1.0); +} + +/**************************************************************************/ +void cz1_state::cz1(machine_config &config) +{ + mz1(config); + m_maincpu->set_addrmap(AS_PROGRAM, &cz1_state::cz1_main_map); + m_maincpu->pc_in_cb().set_ioport("MAIN_PC"); + m_maincpu->an0_func().set_ioport("AN0"); + m_maincpu->an1_func().set_ioport("AN1"); + m_maincpu->an2_func().set_ioport("AN2"); + + I8049(config, m_mcu, 8.96_MHz_XTAL); + m_mcu->set_addrmap(AS_IO, &cz1_state::mcu_map); + m_mcu->p2_out_cb().set(FUNC(cz1_state::mcu_p2_w)); + m_mcu->t0_in_cb().set(FUNC(cz1_state::sync49_r)); + m_mcu->t1_in_cb().set([this] () { return BIT(m_main_port[2], 7); }); + + MSM6200(config, "kbd").irq_cb().set_inputline(m_mcu, MCS48_INPUT_IRQ); + + config.set_default_layout(layout_cz1); +} + +//************************************************************************** +// ROM DEFINITIONS +//************************************************************************** + +ROM_START( cz1 ) + ROM_REGION(0x8000, "maincpu", 0) + ROM_LOAD("upd27c256c-20a154.bin", 0x0000, 0x8000, CRC(a970ee65) SHA1(269f2e823ac6353eca9fdb682deebeb7d4d0f585)) + + ROM_REGION(0x2000, "mainram", 0) + ROM_LOAD("init_main.bin", 0x0000, 0x2000, CRC(25fbf88a) SHA1(b7eee5af1d3470ea951df3a019ba2e2a055e84c7)) + + ROM_REGION(0x4000, "subcpu", 0) + ROM_LOAD("upd23c128ec-036.bin", 0x0000, 0x4000, CRC(3cf23c4e) SHA1(b27ee664c31526058defd8e8666ec8e7828059a2)) + + ROM_REGION(0x4000, "subram", 0) + ROM_LOAD("init_sub.bin", 0x0000, 0x4000, CRC(c0b498af) SHA1(73c48bf5df0d3660c50c370286559a8d4cdb6b99)) + + ROM_REGION(0x800, "mcu", 0) // this dump is actually uPD80C49HC-187 from the HT-6000, though it appears functionally identical + ROM_LOAD("upd8049hc-672.bin", 0x000, 0x800, BAD_DUMP CRC(47b47af7) SHA1(8f0515f95dcc6e224a8a59e0c2cd7ddb4796e34e)) +ROM_END + +#define rom_mz1 rom_cz1 + +} // anonymous namespace + + +//************************************************************************** +// SYSTEM DRIVERS +//************************************************************************** + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +SYST( 1986, cz1, 0, 0, cz1, cz1, cz1_state, empty_init, "Casio", "CZ-1", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK | MACHINE_IMPERFECT_SOUND ) +SYST( 1986, mz1, cz1, 0, mz1, mz1, cz1_state, empty_init, "Casio", "MZ-1 (prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK | MACHINE_IMPERFECT_SOUND ) diff --git a/src/mame/casio/cz101.cpp b/src/mame/casio/cz101.cpp index 5ebbc82490e..731bf392259 100644 --- a/src/mame/casio/cz101.cpp +++ b/src/mame/casio/cz101.cpp @@ -94,7 +94,7 @@ private: required_device m_maincpu; required_device m_hd44780; required_device m_upd933; - required_device m_cart; + required_device m_cart; required_ioport_array<16> m_keys; output_finder<16> m_leds; output_finder<3, 4> m_led_env; @@ -117,7 +117,7 @@ void cz101_state::maincpu_map(address_map &map) map(0x0000, 0x7fff).rom().region("program", 0); map(0x8000, 0x8fff).ram().share("nvram"); - map(0x9000, 0x97ff).rw(m_cart, FUNC(casio_ra3_device::read), FUNC(casio_ra3_device::write)); + map(0x9000, 0x97ff).rw(m_cart, FUNC(casio_ram_cart_device::read), FUNC(casio_ram_cart_device::write)); map(0x9800, 0x9fff).w(FUNC(cz101_state::led_4_w)); map(0xa000, 0xa7ff).w(FUNC(cz101_state::led_3_w)); map(0xa800, 0xafff).w(FUNC(cz101_state::led_2_w)); @@ -249,7 +249,7 @@ static INPUT_PORTS_START( cz101 ) PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Env. Point End") PORT_START("kc13") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", casio_ra3_device, present) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", casio_ram_cart_device, exists) PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Vibrato") PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("DCO1 Wave Form") PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("DCO1 Envelope") diff --git a/src/mame/casio/ra3.cpp b/src/mame/casio/ra3.cpp index acf8e73d533..a56da78426b 100644 --- a/src/mame/casio/ra3.cpp +++ b/src/mame/casio/ra3.cpp @@ -13,67 +13,103 @@ // device type definition DEFINE_DEVICE_TYPE(CASIO_RA3, casio_ra3_device, "casio_ra3", "Casio RA-3 RAM cartridge") +DEFINE_DEVICE_TYPE(CASIO_RA6, casio_ra6_device, "casio_ra6", "Casio RA-6 RAM cartridge") + +/**************************************************************************/ +casio_ram_cart_device::casio_ram_cart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, unsigned max_size) + : device_t(mconfig, type, tag, owner, clock) + , device_memcard_image_interface(mconfig, *this) + , m_max_size(max_size) +{ + m_mask = max_size - 1; + m_size = 0; + + // only power-of-two sizes are supported + assert(!(m_max_size & m_mask)); +} /**************************************************************************/ casio_ra3_device::casio_ra3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, CASIO_RA3, tag, owner, clock) - , device_memcard_image_interface(mconfig, *this) + : casio_ram_cart_device(mconfig, CASIO_RA3, tag, owner, clock, 0x1000) { } /**************************************************************************/ -void casio_ra3_device::device_start() +casio_ra6_device::casio_ra6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : casio_ram_cart_device(mconfig, CASIO_RA6, tag, owner, clock, 0x4000) { - m_ram.resize(0x1000, 0xff); +} + +/**************************************************************************/ +void casio_ram_cart_device::device_start() +{ + m_ram.resize(m_max_size, 0xff); save_item(NAME(m_ram)); } /**************************************************************************/ -std::pair casio_ra3_device::call_load() +std::pair casio_ram_cart_device::call_load() { - const size_t size = m_ram.size(); - if (length() != size) + const u32 size = loaded_through_softlist() ? get_software_region_length("rom") : length(); + + // size must be a power of two and at least 4kb + if (size < 0x1000 || (size & (size - 1))) return std::make_pair(image_error::INVALIDLENGTH, std::string()); - fseek(0, SEEK_SET); - const size_t ret = fread(m_ram.data(), size); - if (ret != size) - return std::make_pair(std::errc::io_error, "Error reading file"); + // allow loading larger than the maximum size (e.g. for CZ-1 ROM carts that use oversized ROMs) + m_size = std::min(size, m_max_size); + m_mask = m_size - 1; + + if (loaded_through_softlist()) + { + memcpy(m_ram.data(), get_software_region("rom"), m_size); + } + else + { + fseek(0, SEEK_SET); + const size_t ret = fread(m_ram.data(), m_size); + if (ret != m_size) + return std::make_pair(std::errc::io_error, "Error reading file"); + } return std::make_pair(std::error_condition(), std::string()); } /**************************************************************************/ -void casio_ra3_device::call_unload() +void casio_ram_cart_device::call_unload() { - fseek(0, SEEK_SET); - fwrite(m_ram.data(), m_ram.size()); + if (is_loaded()) + { + fseek(0, SEEK_SET); + fwrite(m_ram.data(), m_size); + } std::fill(m_ram.begin(), m_ram.end(), 0xff); } /**************************************************************************/ -std::pair casio_ra3_device::call_create(int format_type, util::option_resolution *format_options) +std::pair casio_ram_cart_device::call_create(int format_type, util::option_resolution *format_options) { std::fill(m_ram.begin(), m_ram.end(), 0); - const size_t size = m_ram.size(); - const size_t ret = fwrite(m_ram.data(), size); - if (ret != size) + m_size = m_max_size; + m_mask = m_max_size - 1; + const size_t ret = fwrite(m_ram.data(), m_size); + if (ret != m_size) return std::make_pair(std::errc::io_error, "Error writing file"); return std::make_pair(std::error_condition(), std::string()); } /**************************************************************************/ -u8 casio_ra3_device::read(offs_t offset) +u8 casio_ram_cart_device::read(offs_t offset) { - return m_ram[offset & 0xfff]; + return m_ram[offset & m_mask]; } /**************************************************************************/ -void casio_ra3_device::write(offs_t offset, u8 data) +void casio_ram_cart_device::write(offs_t offset, u8 data) { if (is_loaded()) - m_ram[offset & 0xfff] = data; + m_ram[offset & m_mask] = data; } diff --git a/src/mame/casio/ra3.h b/src/mame/casio/ra3.h index 48933c1e8a2..e2eb6269dd3 100644 --- a/src/mame/casio/ra3.h +++ b/src/mame/casio/ra3.h @@ -9,14 +9,14 @@ #pragma once #include "imagedev/memcard.h" +#include "softlist_dev.h" #include -class casio_ra3_device : public device_t, public device_memcard_image_interface +class casio_ram_cart_device : public device_t, public device_memcard_image_interface { public: - casio_ra3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); - + virtual const char *image_interface() const noexcept override { return "cz_cart"; } virtual bool is_reset_on_load() const noexcept override { return false; } virtual const char *file_extensions() const noexcept override { return "bin"; } virtual const char *image_type_name() const noexcept override { return "cartridge"; } @@ -29,16 +29,32 @@ public: u8 read(offs_t offset); void write(offs_t offset, u8 data); - bool present() { return is_loaded(); } - protected: + casio_ram_cart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, unsigned max_size); + virtual void device_start() override; + virtual const software_list_loader &get_software_list_loader() const override { return rom_software_list_loader::instance(); } private: std::vector m_ram; + const unsigned m_max_size; + unsigned m_size, m_mask; +}; + +class casio_ra3_device : public casio_ram_cart_device +{ +public: + casio_ra3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); +}; + +class casio_ra6_device : public casio_ram_cart_device +{ +public: + casio_ra6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); }; // device type definition DECLARE_DEVICE_TYPE(CASIO_RA3, casio_ra3_device) +DECLARE_DEVICE_TYPE(CASIO_RA6, casio_ra6_device) #endif // MAME_CASIO_RA3_H diff --git a/src/mame/layout/cz1.lay b/src/mame/layout/cz1.lay new file mode 100644 index 00000000000..6454435abe1 --- /dev/null +++ b/src/mame/layout/cz1.lay @@ -0,0 +1,779 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/layout/mz1.lay b/src/mame/layout/mz1.lay new file mode 100644 index 00000000000..c7d39743f1d --- /dev/null +++ b/src/mame/layout/mz1.lay @@ -0,0 +1,654 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 8af28f1c3e7..28f1d130435 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -15884,6 +15884,10 @@ ctk551 // ctk601 // gz70sp // +@source:casio/cz1.cpp +cz1 // 1986 Casio +mz1 // 1986 Casio (unreleased) + @source:casio/cz101.cpp cz101 // 1984 Casio