From 3574a5aa74188c84e7493806678a6b815048c8e6 Mon Sep 17 00:00:00 2001 From: Devin Acker Date: Tue, 17 Oct 2023 12:00:22 -0400 Subject: [PATCH] casio/cz101.cpp: Added sound output and RAM cartridge, and promoted system to working. (#11613) * casio/ra3.cpp: Added simple Casio RA-3 RAM cartridge device. * sound/upd933.cpp: Emulated Casio/NEC uPD933 phase distortion synthesis chip. Systems promoted to working ---------------- Casio CZ-101 [Devin Acker] --- scripts/src/sound.lua | 12 + src/devices/sound/upd933.cpp | 531 ++++++++++++++++++++++ src/devices/sound/upd933.h | 96 ++++ src/mame/casio/cz101.cpp | 403 +++++++++-------- src/mame/casio/ra3.cpp | 79 ++++ src/mame/casio/ra3.h | 44 ++ src/mame/layout/cz101.lay | 824 ++++++++++++++++++++++++++++++----- 7 files changed, 1713 insertions(+), 276 deletions(-) create mode 100644 src/devices/sound/upd933.cpp create mode 100644 src/devices/sound/upd933.h create mode 100644 src/mame/casio/ra3.cpp create mode 100644 src/mame/casio/ra3.h diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 4959be4e3a1..7cb136380c7 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1465,6 +1465,18 @@ if (SOUNDS["LC82310"]~=null) then } end +--------------------------------------------------- +-- NEC uPD933 +--@src/devices/sound/upd933.h,SOUNDS["UPD933"] = true +--------------------------------------------------- + +if (SOUNDS["UPD933"]~=null) then + files { + MAME_DIR .. "src/devices/sound/upd933.cpp", + MAME_DIR .. "src/devices/sound/upd933.h", + } +end + --------------------------------------------------- -- NEC uPD934G --@src/devices/sound/upd934g.h,SOUNDS["UPD934G"] = true diff --git a/src/devices/sound/upd933.cpp b/src/devices/sound/upd933.cpp new file mode 100644 index 00000000000..763caf45a4d --- /dev/null +++ b/src/devices/sound/upd933.cpp @@ -0,0 +1,531 @@ +// license:BSD-3-Clause +// copyright-holders:Devin Acker + +/*************************************************************************** + NEC/Casio uPD933 "Phase Distortion" synthesis chip +***************************************************************************/ + +#include "emu.h" +#include "upd933.h" + +#include +#include + +DEFINE_DEVICE_TYPE(UPD933, upd933_device, "upd933", "NEC uPD933") + +upd933_device::upd933_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, UPD933, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_irq_cb(*this) +{ +} + +/**************************************************************************/ +void upd933_device::device_start() +{ + m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE); + + for (int i = 0; i < 0x800; i++) + m_cosine[i] = 0xfff * (1 - cos(2.0 * M_PI * i / 0x7ff)) / 2; + + for (int i = 0; i < 0x80; i++) + { + // A4 is note 62, 442 Hz + const double freq = 442.0 * pow(2, (i - 62) / 12.0); + m_pitch[i] = (1 << PITCH_SHIFT) * (freq * 0x800 / 40000); + } + + for (int i = 0; i < 0x200; i++) + m_pitch_fine[i] = (1 << PITCH_FINE_SHIFT) * (pow(2, (double)i / (12.0 * 0x200)) - 1); + + // logarithmic volume curve, also scales 12-bit waveform to 13-bit range + // (also allows pitch modulation to cover the same spectrum with no extra scaling) + for (int i = 1; i < 0x200; i++) + m_volume[i] = pow(2 << VOLUME_SHIFT, (double)i / 0x1ff); + m_volume[0] = 0; + + m_cs = 1; + + save_item(NAME(m_irq_state)); + save_item(NAME(m_cs)); + save_item(NAME(m_sound_data)); + save_item(NAME(m_sound_data_pos)); + save_item(NAME(m_sound_regs)); + save_item(NAME(m_sample_count)); + save_item(NAME(m_last_sample)); + save_item(NAME(m_irq_data)); + + save_item(STRUCT_MEMBER(m_voice, m_wave)); + save_item(STRUCT_MEMBER(m_voice, m_window)); + save_item(STRUCT_MEMBER(m_voice, m_ring_mod)); + save_item(STRUCT_MEMBER(m_voice, m_pitch_mod)); + save_item(STRUCT_MEMBER(m_voice, m_mute_other)); + save_item(STRUCT_MEMBER(m_voice, m_pitch)); + save_item(STRUCT_MEMBER(m_voice, m_position)); + save_item(STRUCT_MEMBER(m_voice, m_pitch_step)); + save_item(STRUCT_MEMBER(m_voice, m_dcw_limit)); + save_item(STRUCT_MEMBER(m_voice, m_pm_level)); + + save_item(STRUCT_MEMBER(m_dca, m_direction)); + save_item(STRUCT_MEMBER(m_dca, m_sustain)); + save_item(STRUCT_MEMBER(m_dca, m_irq)); + save_item(STRUCT_MEMBER(m_dca, m_rate)); + save_item(STRUCT_MEMBER(m_dca, m_target)); + save_item(STRUCT_MEMBER(m_dca, m_current)); + + save_item(STRUCT_MEMBER(m_dcw, m_direction)); + save_item(STRUCT_MEMBER(m_dcw, m_sustain)); + save_item(STRUCT_MEMBER(m_dcw, m_irq)); + save_item(STRUCT_MEMBER(m_dcw, m_rate)); + save_item(STRUCT_MEMBER(m_dcw, m_target)); + save_item(STRUCT_MEMBER(m_dcw, m_current)); + + save_item(STRUCT_MEMBER(m_dco, m_direction)); + save_item(STRUCT_MEMBER(m_dco, m_sustain)); + save_item(STRUCT_MEMBER(m_dco, m_irq)); + save_item(STRUCT_MEMBER(m_dco, m_rate)); + save_item(STRUCT_MEMBER(m_dco, m_target)); + save_item(STRUCT_MEMBER(m_dco, m_current)); +} + +/**************************************************************************/ +void upd933_device::device_reset() +{ + m_irq_state = 0; + + m_sound_data[0] = m_sound_data[1] = 0; + m_sound_data_pos = 0; + std::fill(m_sound_regs.begin(), m_sound_regs.end(), 0); + + std::fill(m_voice.begin(), m_voice.end(), voice_t()); + std::fill(m_dca.begin(), m_dca.end(), env_t()); + std::fill(m_dco.begin(), m_dco.end(), env_t()); + std::fill(m_dcw.begin(), m_dcw.end(), env_t()); + + m_sample_count = 0; + m_last_sample = 0; + m_irq_data = 0; +} + +/**************************************************************************/ +void upd933_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE); +} + +/**************************************************************************/ +void upd933_device::cs_w(int state) +{ + m_stream->update(); + + if (!m_cs && state) + check_irq(); + m_cs = state; +} + +/**************************************************************************/ +void upd933_device::check_irq() +{ + for (int i = 0; i < 8; i++) + { + if (m_dca[i].m_irq) + { + m_irq_data = 1 | (i << 1); + break; + } + if (m_dcw[i].m_irq) + { + m_irq_data = 2 | (i << 2); + break; + } + if (m_dco[i].m_irq) + { + m_irq_data = 4 | (i << 3); + break; + } + } + + if (m_irq_data) + m_irq_cb(m_irq_state = 1); +} + +/**************************************************************************/ +u8 upd933_device::read() +{ + if (!machine().side_effects_disabled()) + m_stream->update(); + + return m_cs ? 0xff : m_irq_data; +} + +/**************************************************************************/ +void upd933_device::write(u8 data) +{ + if (m_cs) return; + + if (m_sound_data_pos >= 2) + { + m_stream->update(); + + m_irq_data = 0; + m_irq_cb(m_irq_state = 0); + + const u8 reg = m_sound_data[0]; + const u16 value = m_sound_regs[reg] = (m_sound_data[1] << 8) | data; + + // the low 3 bits of the register number determine which voice is controlled by per-voice registers... + const int vnum = reg & 7; + voice_t &voice = m_voice[vnum]; + // ...except for registers 68-6f, which control waveform for voice 'n', but modulation for voice 'n-2' + // (even though those two voices don't actually modulate each other...) + voice_t &mod_voice = m_voice[(vnum + 6) & 7]; + + m_sound_data_pos = 0; + switch (reg >> 3) + { + case 0x0: // 00-07: DCA step (volume envelope) + /* + msb lsb + n------- - direction (0 = up, 1 = down) + -nnnnnnn - rate + n------- - sustain flag + -nnnnnnn - level + */ + { + env_t &dca = m_dca[vnum]; + dca.m_direction = BIT(value, 15); + dca.m_rate = env_rate(BIT(value, 8, 7)); + dca.m_sustain = BIT(value, 7); + dca.m_target = BIT(value, 0, 7) << (ENV_DCA_SHIFT + 2); + dca.m_irq = false; + } + break; + + case 0x2: // 10-17: DCO step (pitch envelope) + /* + msb lsb + n------- - direction (0 = up, 1 = down) + -nnnnnnn - rate + n------- - sustain flag + -n------ - level units (1 = 2-semitone intervals, 0 = 6.25-cent intervals) + --nnnnnn - level + */ + { + env_t &dco = m_dco[vnum]; + dco.m_direction = BIT(value, 15); + dco.m_rate = env_rate(BIT(value, 8, 7)); + dco.m_sustain = BIT(value, 7); + dco.m_target = BIT(value, 0, 6) << (ENV_DCO_SHIFT + 5); + if (BIT(value, 6)) + dco.m_target <<= 5; + dco.m_irq = false; + } + break; + + case 0x4: // 20-27: DCW step (waveform envelope) + // same bits as DCA step + { + env_t &dcw = m_dcw[vnum]; + dcw.m_direction = BIT(value, 15); + dcw.m_rate = env_rate(BIT(value, 8, 7)); + dcw.m_sustain = BIT(value, 7); + dcw.m_target = BIT(value, 0, 7) << (ENV_DCW_SHIFT + 3); + dcw.m_irq = false; + } + break; + + case 0xc: // 60-67: pitch (in semitones, as 7.9 fixed point) + voice.m_pitch = value; + update_pitch_step(vnum); + break; + + case 0xd: // 68-6f: waveform + /* + msb lsb + nnn----- - first waveform + ---nnn-- - second waveform + ------n- - enable second + -------n nn------ - window function + --n----- - ring modulation enable + ---n---- - pitch modulation enable + ----n--- - pitch modulation source (0 = other voice, 1 = noise) + -----n-- - output (0 = normal, 1 = mute previous voice) + */ + voice.m_wave[0] = BIT(value, 13, 3); + if (BIT(value, 9)) + voice.m_wave[1] = BIT(value, 10, 3); + 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); + break; + + case 0x13: // 98-9f + // unknown, but cz101 sets these to zero when starting a note, probably to reset the oscillator + voice.m_position = value << PITCH_SHIFT; + break; + + default: + logerror("%s: unknown sound reg write: %02x %04x\n", + machine().describe_context(), reg, value); + break; + } + } + else + { + m_sound_data[m_sound_data_pos++] = data; + } +} + +/**************************************************************************/ +u32 upd933_device::env_rate(u8 data) const +{ + return (8 | (data & 7)) << (data >> 3); +} + +/**************************************************************************/ +void upd933_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; + + /* + Voices need to be processed in a certain order for modulation to work correctly, + i.e. to match each odd-numbered voice ("line 1") with the corresponding even-numbered one ("line 2"). + */ + static const int voice_map[] = {5, 0, 7, 2, 1, 4, 3, 6}; + for (int j : voice_map) + sample += update(j); + + outputs[0].put_int_clamp(i, sample, 1 << 15); + m_sample_count++; + + if (!m_irq_data && m_cs) + check_irq(); + } +} + +/**************************************************************************/ +s16 upd933_device::update(int vnum) +{ + voice_t &voice = m_voice[vnum]; + s16 sample = 0; + + const u16 pos = BIT(voice.m_position, PITCH_SHIFT, 11); + const u8 wave = BIT(voice.m_position, PITCH_SHIFT + 11); + + const u16 dcw = std::min(u16(m_dcw[vnum].m_current >> ENV_DCW_SHIFT), voice.m_dcw_limit); + const u16 pivot = 0x400 - dcw; + u16 phase = 0; + u16 window = 0; + + // + // apply transfer function + // + switch (voice.m_wave[wave] & 7) + { + case 0: // sawtooth - rises from [0, pivot) and falls from [pivot, 800) + if (pos < pivot) + phase = pos * 0x400 / pivot; + else + phase = 0x400 + (pos - pivot) * 0x400 / (0x800 - pivot); + break; + + case 1: // square - rises from [0, pivot), stays high from [pivot, 400), then inverts + if ((pos & 0x3ff) < pivot) + phase = (pos & 0x3ff) * 0x400 / pivot; + else + phase = 0x3ff; + + phase |= (pos & 0x400); + break; + + case 2: // pulse - rises & falls from [0, pivot*2), then stays low + if (pos < pivot * 2) + phase = pos * 0x800 / (pivot * 2); + else + phase = 0x7ff; + break; + + case 3: // silent (undocumented) + break; + + case 4: // double sine - rises & falls from [0, pivot), then again from [pivot, 800) + if (pos < pivot) + phase = pos * 0x800 / pivot; + else + phase = (pos - pivot) * 0x800 / (0x800 - pivot); + break; + + case 5: // saw pulse - rises from [0, 400), falls from [400, 400+pivot), then stays low + if (pos < 0x400) + phase = pos; + else if (pos < (pivot + 0x400)) + phase = 0x400 + (pos & 0x3ff) * 0x400 / pivot; + else + phase = 0x7ff; + break; + + case 6: // resonance + // this is a special case that just multiplies the frequency by the DCW level... + phase = pos + ((pos * dcw) >> 6); + // ...and hardsyncs to the fundamental frequency + phase &= 0x7ff; + break; + + case 7: // double pulse (undocumented) - same as regular pulse but double frequency + if ((pos & 0x3ff) < pivot) + phase = (pos & 0x3ff) * 0x400 / pivot; + else + phase = 0x7ff; + break; + } + + // + // apply window function + // + switch (voice.m_window & 7) + { + case 0: // none + break; + + case 1: // sawtooth - falls from [0, 800) + window = pos; + break; + + case 2: // triangle - rises from [0, 400), falls from [400, 800) + window = (pos & 0x3ff) * 2; + if (pos < 0x400) + window ^= 0x7fe; + break; + + case 3: // trapezoid - falls from [400, 800) + if (pos >= 0x400) + window = (pos & 0x3ff) * 2; + break; + + case 4: // pulse (undocumented) - falls from [0, 400) + if (pos < 0x400) + window = pos * 2; + else + window = 0x7ff; + break; + + default: // double saw (undocumented) - rises from [0, 400) and [400, 800) + window = (0x3ff ^ (pos & 0x3ff)) * 2; + break; + } + + sample = m_cosine[phase]; + if (window) + sample = ((s32)sample * (0x800 - window)) / 0x800; + + // center sample around zero, apply volume and ring mod + const u16 volume = m_dca[vnum].m_current >> ENV_DCA_SHIFT; + sample = ((s32)sample * m_volume[volume]) >> VOLUME_SHIFT; + sample -= m_volume[volume] / 2; + + if (voice.m_ring_mod) + sample = ((s32)sample * m_last_sample) / 0x1000; + + // 'mute' actually negates the other voice in a modulating pair + if (voice.m_mute_other) + sample -= m_last_sample; + + // + // update envelopes and pitch modulation, recalculate DCO pitch step if needed + // + const u32 old_dco = m_dco[vnum].m_current; + const s16 old_pm = voice.m_pm_level; + + m_dca[vnum].update(); + m_dcw[vnum].update(); + m_dco[vnum].update(); + + // pitch/noise modulation latches a new pitch multiplier every 8 samples + if (!(m_sample_count & 7)) + { + switch (voice.m_pitch_mod & 3) + { + default: + voice.m_pm_level = 0; + break; + + case 2: + // pitch modulated by other voice (normally unused, up to about +/- 7.5 semitones) + voice.m_pm_level = m_last_sample; + break; + + case 3: + // pitch modulated by noise (0 or 32 semitones above base pitch) + voice.m_pm_level = machine().rand() & (32 << NOTE_SHIFT); + break; + } + } + + if ((old_dco ^ m_dco[vnum].m_current) >> ENV_DCO_SHIFT + || old_pm != voice.m_pm_level) + update_pitch_step(vnum); + + voice.m_position += voice.m_pitch_step; + + m_last_sample = sample; + return sample; +} + +/**************************************************************************/ +void upd933_device::env_t::update() +{ + if (m_current != m_target) + { + if (!m_direction) // increasing + { + if (m_current > m_target + || m_target - m_current <= m_rate) + m_current = m_target; + else + m_current += m_rate; + } + else // decreasing + { + if (m_current < m_target + || m_current - m_target <= m_rate) + m_current = m_target; + else + m_current -= m_rate; + } + } + + if (!m_sustain && (m_current == m_target)) + m_irq = true; +} + +/**************************************************************************/ +void upd933_device::update_pitch_step(int vnum) +{ + voice_t &voice = m_voice[vnum]; + const s32 pitch = s32(voice.m_pitch + (m_dco[vnum].m_current >> ENV_DCO_SHIFT)) + voice.m_pm_level; + u32 step = 0; + + if (pitch > 0 && pitch < (1 << 16)) + { + const u8 note = pitch >> NOTE_SHIFT; + const u16 fine = pitch & ((1 << NOTE_SHIFT) - 1); + step = m_pitch[note]; + if (fine) + step += (step >> PITCH_FINE_SHIFT) * m_pitch_fine[fine]; + } + + voice.m_pitch_step = step; + + /* + The effective DCW envelope value is limited for higher pitch values. + This allows e.g. narrow pulse waves to remain correctly audible + and also prevents aliasing noise for extremely high pitch values. + */ + voice.m_dcw_limit = 0x400 - std::min(0x400U, (step >> (PITCH_SHIFT - 2))); +} diff --git a/src/devices/sound/upd933.h b/src/devices/sound/upd933.h new file mode 100644 index 00000000000..ecaa6045cf8 --- /dev/null +++ b/src/devices/sound/upd933.h @@ -0,0 +1,96 @@ +// license:BSD-3-Clause +// copyright-holders: Devin Acker + +/*************************************************************************** + NEC/Casio uPD933 "Phase Distortion" synthesis chip +***************************************************************************/ + +#ifndef MAME_SOUND_UPD933_H +#define MAME_SOUND_UPD933_H + +#pragma once + +#include + +class upd933_device : public device_t, public device_sound_interface +{ +public: + upd933_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + auto irq_cb() { return m_irq_cb.bind(); } + + int rq_r() const { return m_irq_state; } + void cs_w(int state); // chip select, active low + + void write(u8 data); + u8 read(); + +protected: + 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; + +private: + static constexpr unsigned NOTE_SHIFT = 9; + static constexpr unsigned PITCH_SHIFT = 20; + static constexpr unsigned PITCH_FINE_SHIFT = 12; + static constexpr unsigned VOLUME_SHIFT = 12; + + static constexpr unsigned ENV_DCA_SHIFT = 16; + static constexpr unsigned ENV_DCW_SHIFT = 16; + static constexpr unsigned ENV_DCO_SHIFT = 11; + + struct env_t + { + u8 m_direction = 0, m_sustain = 1, m_irq = 0; + u32 m_rate = 0, m_target = 0, m_current = 0; + + void update(); + }; + + struct voice_t + { + u8 m_wave[2] = {0}; + u8 m_window = 0, m_ring_mod = 0, m_pitch_mod = 0, m_mute_other = 0; + + u16 m_pitch = 0; + u32 m_position = 0, m_pitch_step = 0; + u16 m_dcw_limit = 0; + s16 m_pm_level = 0; + }; + + s16 update(int vnum); + void check_irq(); + + u32 env_rate(u8 data) const; + void update_pitch_step(int vnum); + + sound_stream *m_stream; + static constexpr unsigned CLOCKS_PER_SAMPLE = 112; + + devcb_write_line m_irq_cb; + u8 m_irq_state; + u8 m_cs; + + u16 m_cosine[0x800]; + u32 m_pitch[0x80]; + u16 m_pitch_fine[0x200]; + u16 m_volume[0x200]; + + u8 m_sound_data[2]; + u8 m_sound_data_pos; + std::array m_sound_regs; + + u32 m_sample_count; + s16 m_last_sample; + u8 m_irq_data; + + std::array m_voice; + std::array m_dca, m_dco, m_dcw; +}; + +DECLARE_DEVICE_TYPE(UPD933, upd933_device) + +#endif // MAME_SOUND_UPD933_H diff --git a/src/mame/casio/cz101.cpp b/src/mame/casio/cz101.cpp index 7c0567fa19f..38533cf2782 100644 --- a/src/mame/casio/cz101.cpp +++ b/src/mame/casio/cz101.cpp @@ -1,23 +1,41 @@ // license: BSD-3-Clause -// copyright-holders: Dirk Best +// copyright-holders: Dirk Best, Devin Acker /*************************************************************************** Casio CZ-101 Digital Synthesizer + Misc. notes: + + To run a (currently undumped) test/diagnostic/debug cartridge: + Hold "env step +", "env step -", "initialize", and "write" all at once, then press "load". + If a cartridge is inserted that begins with the 8 bytes "5a 96 5a 96 5a 96 5a 96", then + the CZ-101 firmware will copy the first 2kb of the cart to $8800-8fff and then call $8810. + This works even when the normal "cart detect" signal isn't present. + (Note that this will wipe out all patches saved to the internal RAM.) + + Unused input matrix bits: + Bit 7 of KC15 is normally unused, but if pulled low using a diode, then bits 2-5 of KC8 + (also normally unused) become DIP switches that override the normal MIDI "basic channel" + setting from the front panel (possibly planned for use on a screenless MIDI module). + ***************************************************************************/ #include "emu.h" +#include "ra3.h" #include "bus/midi/midiinport.h" #include "bus/midi/midioutport.h" #include "cpu/upd7810/upd7811.h" #include "machine/clock.h" +#include "machine/nvram.h" +#include "sound/upd933.h" #include "video/hd44780.h" #include "emupal.h" #include "screen.h" +#include "speaker.h" //#define VERBOSE 1 #include "logmacro.h" @@ -38,8 +56,13 @@ public: driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_hd44780(*this, "hd44780"), + m_upd933(*this, "upd933"), + m_cart(*this, "cart"), m_keys(*this, "kc%u", 0), m_leds(*this, "led_%u", 0U), + m_led_env(*this, "led_env%u.%u", 0U, 0U), + m_led_tone(*this, "led_tone%u.%u", 0U, 0U), + m_power(0), m_port_b(0), m_port_c(0), m_midi_rx(1) @@ -50,6 +73,8 @@ public: void cz101_palette(palette_device &palette) const; HD44780_PIXEL_UPDATE(lcd_pixel_update); + DECLARE_INPUT_CHANGED_MEMBER(power_w); + protected: virtual void machine_start() override; virtual void machine_reset() override; @@ -65,19 +90,20 @@ private: void led_3_w(uint8_t data); void led_4_w(uint8_t data); uint8_t keys_r(); - void sound_w(uint8_t data); required_device m_maincpu; required_device m_hd44780; + required_device m_upd933; + required_device m_cart; required_ioport_array<16> m_keys; - output_finder<32> m_leds; + output_finder<16> m_leds; + output_finder<3, 4> m_led_env; + output_finder<3, 3> m_led_tone; + uint8_t m_power; uint8_t m_port_b; uint8_t m_port_c; uint8_t m_midi_rx; - - uint8_t m_sound_data[2]; - uint8_t m_sound_data_pos; }; @@ -87,15 +113,17 @@ private: void cz101_state::maincpu_map(address_map &map) { + map.unmap_value_high(); + map(0x0000, 0x7fff).rom().region("program", 0); - map(0x8000, 0x8fff).ram(); - map(0x9000, 0x97ff).noprw(); // rampack + map(0x8000, 0x8fff).ram().share("nvram"); + map(0x9000, 0x97ff).rw(m_cart, FUNC(casio_ra3_device::read), FUNC(casio_ra3_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)); map(0xb000, 0xb7ff).w(FUNC(cz101_state::led_1_w)); map(0xb800, 0xbfff).r(FUNC(cz101_state::keys_r)); - map(0xc000, 0xfeff).w(FUNC(cz101_state::sound_w)); + map(0xc000, 0xfeff).rw(m_upd933, FUNC(upd933_device::read), FUNC(upd933_device::write)); } @@ -105,153 +133,164 @@ void cz101_state::maincpu_map(address_map &map) static INPUT_PORTS_START( cz101 ) PORT_START("kc0") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C2") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C#2") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D2") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D#2") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E2") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C2") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#2") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D2") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#2") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E2") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F2") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc1") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F#2") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G2") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G#2") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A2") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A#2") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B2") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#2") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G2") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#2") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A2") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#2") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B2") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc2") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C3") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C#3") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D3") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D#3") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E3") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C3") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#3") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D3") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#3") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E3") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F3") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc3") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F#3") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G3") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G#3") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A3") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A#3") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B3") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#3") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G3") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#3") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A3") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#3") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B3") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc4") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C4") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C#4") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D4") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D#4") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E4") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C4") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#4") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D4") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#4") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E4") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F4") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc5") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F#4") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G4") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G#4") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A4") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A#4") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B4") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#4") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G4") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#4") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A4") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#4") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B4") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc6") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C5") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C#5") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D5") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D#5") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E5") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C5") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#5") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D5") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#5") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E5") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F5") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc7") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F#5") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G5") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G#5") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A5") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A#5") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B5") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#5") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G5") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#5") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A5") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#5") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B5") PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc8") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C6") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C6") PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("kc9") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PORTMNT ON/OFF") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PORTMT TIME") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("VIBRATO ON/OFF") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BEND RANGE") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PRESET") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INTERNAL") - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CARTRIDGE") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COMP/RECALL") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Portamento On/Off") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Portamento Time") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Vibrato On/Off") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Bend Range") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Preset") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Internal") + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Cartridge") + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Compare/Recall") PORT_START("kc10") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SOLO") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE MIX") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRANSPOSE") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WRITE") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PROTECT") - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SELECT") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Solo") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Tone Mix") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Key Transpose") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Write") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("MIDI") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_TOGGLE PORT_NAME("Memory Protect") + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Select") + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MEMORY_RESET) PORT_NAME("P (Reset RAM)") PORT_START("kc11") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 1") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 2") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 3") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 4") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 5") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 6") - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 7") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TONE 8") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Tone 1") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Tone 2") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Tone 3") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Tone 4") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Tone 5") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Tone 6") + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Tone 7") + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Tone 8") PORT_START("kc12") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SAVE") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOAD") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"CURSOR \u9665") // ◁ - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"CURSOR \u9655") // ▷ - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ENV \u9661") // ▽ - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ENV \u9651") // △ - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENV SUST.") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENV END") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME(u8"Value \u25bd / Save") // ▽ + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME(u8"Value \u25b3 / Load") // △ + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME(u8"Cursor \u25c1") // ◁ + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(u8"Cursor \u25b7") // ▷ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_END) PORT_NAME(u8"Env. Step \u25bd") // ▽ + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_HOME) PORT_NAME(u8"Env. Step \u25b3") // △ + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Env. Point Sustain") + 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_KEYBOARD) PORT_NAME("PACK DETECT") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("VIBRAT") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCO1 WAVE") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCO1 ENV") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCW1 KEY") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCW1 ENV") - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCA1 KEY") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCA1 ENV") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", casio_ra3_device, present) + 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") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("DCW1 Key Follow") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("DCW1 Envelope") + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("DCA1 Key Follow") + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("DCA1 Envelope") PORT_START("kc14") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INITIALIZE") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OCTAVE") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCO2 WAVE") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCO2 ENV") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCW2 KEY") - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCW2 ENV") - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCA2 KEY") - PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DCA2 ENV") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Initialize") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Octave") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("DCO2 Wave Form") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("DCO2 Envelope") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("DCW2 Key Follow") + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("DCW2 Envelope") + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("DCA2 Key Follow") + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("DCA2 Envelope") PORT_START("kc15") - PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DETUNE") - PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINE SELECT") - PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RING") - PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NOISE") - PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"TUNE \u9661") // ▽ - PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"TUNE \u9651") // △ - PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("APO ON/OFF") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("Detune") + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Line Select") + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("Ring Modulation") + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Noise Modulation") + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME(u8"Master Tune \u25bd") // ▽ + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME(u8"Master Tune \u25b3") // △ + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_TOGGLE PORT_NAME("Auto Power Off") PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_START("PB") + PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933", upd933_device, rq_r) + PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_NAME("Power") PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, cz101_state, power_w, 0) + PORT_START("AN1") - PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) + 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("AN2") + PORT_CONFNAME(0xff, 0xff, "Battery Level") + PORT_CONFSETTING( 0xff, "Normal") + PORT_CONFSETTING( 0x00, "Low") INPUT_PORTS_END @@ -262,66 +301,68 @@ INPUT_PORTS_END void cz101_state::cz101_palette(palette_device &palette) const { palette.set_pen_color(0, rgb_t(138, 146, 148)); // background - palette.set_pen_color(1, rgb_t( 92, 83, 88)); // LCD pixel on + 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( cz101_state::lcd_pixel_update ) { // char size is 5x8 - if (x > 4 || y > 7) + if (!m_power || x > 4 || y > 7) return; if (line < 2 && pos < 16) bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2; } +INPUT_CHANGED_MEMBER(cz101_state::power_w) +{ + if (!newval) + { + m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); + } + else + { + m_power = 1; + m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); + m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + } +} + void cz101_state::led_4_w(uint8_t data) { - m_leds[0] = BIT(~data, 7); - m_leds[1] = BIT(~data, 6); - m_leds[2] = BIT(~data, 5); - m_leds[3] = BIT(~data, 4); - m_leds[4] = BIT(~data, 3); - m_leds[5] = BIT(~data, 2); - m_leds[6] = BIT(~data, 1); - m_leds[7] = BIT(~data, 0); + if (!m_power) data = 0xff; + + for (int i = 0; i < 7; i++) + m_leds[i] = BIT(~data, i); } void cz101_state::led_3_w(uint8_t data) { - m_leds[8] = BIT(~data, 7); - m_leds[9] = BIT(~data, 6); - m_leds[10] = BIT(~data, 5); - m_leds[11] = BIT(~data, 4); - m_leds[12] = BIT(~data, 3); - m_leds[13] = BIT(~data, 2); - m_leds[14] = BIT(~data, 1); - m_leds[15] = BIT(~data, 0); + if (!m_power) data = 0xff; + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 4; j++) + m_led_env[i][j] = BIT(data, 4+i) & BIT(~data, j); } void cz101_state::led_2_w(uint8_t data) { - m_leds[16] = BIT(~data, 7); - m_leds[17] = BIT(~data, 6); - m_leds[18] = BIT(~data, 5); - m_leds[19] = BIT(~data, 4); - m_leds[20] = BIT(~data, 3); - m_leds[21] = BIT(~data, 2); - m_leds[22] = BIT(~data, 1); - m_leds[23] = BIT(~data, 0); + if (!m_power) data = 0xff; + + m_leds[7] = BIT(~data, 7); + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + m_led_tone[i][j] = BIT(data, 1+i) & BIT(~data, 4+j); } void cz101_state::led_1_w(uint8_t data) { - m_leds[24] = BIT(~data, 7); - m_leds[25] = BIT(~data, 6); - m_leds[26] = BIT(~data, 5); - m_leds[27] = BIT(~data, 4); - m_leds[28] = BIT(~data, 3); - m_leds[29] = BIT(~data, 2); - m_leds[30] = BIT(~data, 1); - m_leds[31] = BIT(~data, 0); + if (!m_power) data = 0xff; + + for (int i = 0; i < 8; i++) + m_leds[8+i] = BIT(~data, i); } uint8_t cz101_state::keys_r() @@ -329,19 +370,6 @@ uint8_t cz101_state::keys_r() return m_keys[m_port_b & 0x0f]->read(); } -void cz101_state::sound_w(uint8_t data) -{ - if (m_sound_data_pos >= 2) - { - logerror("sound reg write: %02x %02x %02x\n", m_sound_data[0], m_sound_data[1], data); - m_sound_data_pos = 0; - } - else - { - m_sound_data[m_sound_data_pos++] = data; - } -} - // 7------- power switch input (also connected to /NMI) // -6------ music lsi write enable // --5----- music lsi chip select @@ -352,14 +380,16 @@ void cz101_state::port_b_w(uint8_t data) { LOG("port_b_w: %02x\n", data); + m_upd933->cs_w(BIT(data, 5)); + m_port_b = data; } // 7------- lcd e // -6------ lcd rw // --5----- lcd rs -// ---4---- not used -// ----3--- power down detection output +// ---4---- not used / debug? (MIDI CC #7 writes data to port A and then toggles this bit) +// ----3--- power down output // -----2-- midi clock // ------1- midi input // -------0 midi output @@ -369,27 +399,40 @@ void cz101_state::port_c_w(uint8_t data) LOG("port_c_w: %02x\n", data); m_port_c = data; - m_hd44780->e_w(!BIT(data, 7)); - m_hd44780->rw_w(BIT(data, 6)); - m_hd44780->rs_w(BIT(data, 5)); + if (BIT(data, 3)) + { + m_hd44780->e_w(BIT(~data, 7)); + m_hd44780->rw_w(BIT(data, 6)); + m_hd44780->rs_w(BIT(data, 5)); + } + else + { + m_power = 0; + m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + m_hd44780->reset(); + m_upd933->reset(); + led_1_w(0xff); + led_2_w(0xff); + led_3_w(0xff); + led_4_w(0xff); + } } void cz101_state::machine_start() { m_leds.resolve(); + m_led_env.resolve(); + m_led_tone.resolve(); // register for save states + save_item(NAME(m_power)); save_item(NAME(m_port_b)); save_item(NAME(m_port_c)); save_item(NAME(m_midi_rx)); - save_item(NAME(m_sound_data)); - save_item(NAME(m_sound_data_pos)); } void cz101_state::machine_reset() { - m_sound_data[0] = m_sound_data[1] = 0; - m_sound_data_pos = 0; } @@ -399,18 +442,21 @@ void cz101_state::machine_reset() void cz101_state::cz101(machine_config &config) { - UPD7810(config, m_maincpu, 10_MHz_XTAL); // actually 7811, but internal ROM disabled + UPD7811(config, m_maincpu, 10_MHz_XTAL); m_maincpu->set_addrmap(AS_PROGRAM, &cz101_state::maincpu_map); m_maincpu->pa_in_cb().set(m_hd44780, FUNC(hd44780_device::db_r)); m_maincpu->pa_out_cb().set(m_hd44780, FUNC(hd44780_device::db_w)); - m_maincpu->pb_in_cb().set_constant(0x80); // TODO: power switch + m_maincpu->pb_in_cb().set_ioport("PB"); m_maincpu->pb_out_cb().set(FUNC(cz101_state::port_b_w)); m_maincpu->pc_out_cb().set(FUNC(cz101_state::port_c_w)); m_maincpu->an1_func().set_ioport("AN1"); - m_maincpu->set_pc_pullups(0x03); + m_maincpu->an2_func().set_ioport("AN2"); CLOCK(config, "midi_clock", 2_MHz_XTAL).signal_handler().set(m_maincpu, FUNC(upd7810_device::sck_w)); + NVRAM(config, "nvram"); // backed by external battery when RAM cartridge is inserted + CASIO_RA3(config, m_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; }); @@ -436,7 +482,11 @@ void cz101_state::cz101(machine_config &config) config.set_default_layout(layout_cz101); - //UPD933(config, "music", 8.96_MHz_XTAL / 2); + SPEAKER(config, "speaker").front_center(); + + UPD933(config, m_upd933, 8.96_MHz_XTAL / 2); + m_upd933->irq_cb().set_inputline(m_maincpu, UPD7810_INTF1); + m_upd933->add_route(0, "speaker", 1.0); } @@ -459,6 +509,9 @@ ROM_START( cz101 ) ROMX_LOAD("hn613256ps40.bin", 0x0000, 0x8000, CRC(c417bc57) SHA1(2aa5bfb76dc0a56797cf5dd547197816cedfa370), ROM_BIOS(0)) ROM_SYSTEM_BIOS(1, "v1", "Version I" ) ROMX_LOAD("hn613256pn26.bin", 0x0000, 0x8000, CRC(e6c7780e) SHA1(52ff2c280392e104e84b05288c2b952fc29b50f4), ROM_BIOS(1)) + + ROM_REGION(0x1000, "nvram", 0) + ROM_LOAD("init_ram.bin", 0x0000, 0x1000, CRC(11010fa5) SHA1(a5f33408cc2852c8429b828849675d231c370831)) ROM_END } // anonymous namespace @@ -469,4 +522,4 @@ ROM_END //************************************************************************** // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS -CONS( 1984, cz101, 0, 0, cz101, cz101, cz101_state, empty_init, "Casio", "CZ-101", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) +CONS( 1984, cz101, 0, 0, cz101, cz101, cz101_state, empty_init, "Casio", "CZ-101", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK ) diff --git a/src/mame/casio/ra3.cpp b/src/mame/casio/ra3.cpp new file mode 100644 index 00000000000..64967093950 --- /dev/null +++ b/src/mame/casio/ra3.cpp @@ -0,0 +1,79 @@ +// license:BSD-3-Clause +// copyright-holders:Devin Acker +/********************************************************************* + Casio CZ-series RAM cartridges +*********************************************************************/ + +#include "emu.h" +#include "ra3.h" + +#include "emuopts.h" + +#include + +// device type definition +DEFINE_DEVICE_TYPE(CASIO_RA3, casio_ra3_device, "casio_ra3", "Casio RA-3 RAM cartridge") + +/**************************************************************************/ +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) +{ +} + +/**************************************************************************/ +void casio_ra3_device::device_start() +{ + m_ram.resize(0x1000, 0xff); + + save_item(NAME(m_ram)); +} + +/**************************************************************************/ +std::pair casio_ra3_device::call_load() +{ + const size_t size = m_ram.size(); + if (length() != size) + 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"); + + return std::make_pair(std::error_condition(), std::string()); +} + +/**************************************************************************/ +void casio_ra3_device::call_unload() +{ + fseek(0, SEEK_SET); + fwrite(m_ram.data(), m_ram.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::fill(m_ram.begin(), m_ram.end(), 0xff); + + const size_t size = m_ram.size(); + const size_t ret = fwrite(m_ram.data(), size); + if (ret != 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) +{ + return m_ram[offset & 0xfff]; +} + +/**************************************************************************/ +void casio_ra3_device::write(offs_t offset, u8 data) +{ + if (is_loaded()) + m_ram[offset & 0xfff] = data; +} diff --git a/src/mame/casio/ra3.h b/src/mame/casio/ra3.h new file mode 100644 index 00000000000..48933c1e8a2 --- /dev/null +++ b/src/mame/casio/ra3.h @@ -0,0 +1,44 @@ +// license:BSD-3-Clause +// copyright-holders:Devin Acker +/********************************************************************* + Casio CZ-series RAM cartridges +*********************************************************************/ +#ifndef MAME_CASIO_RA3_H +#define MAME_CASIO_RA3_H + +#pragma once + +#include "imagedev/memcard.h" + +#include + +class casio_ra3_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 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"; } + virtual const char *image_brief_type_name() const noexcept override { return "cart"; } + + virtual std::pair call_load() override; + virtual void call_unload() override; + virtual std::pair call_create(int format_type, util::option_resolution *format_options) override; + + u8 read(offs_t offset); + void write(offs_t offset, u8 data); + + bool present() { return is_loaded(); } + +protected: + virtual void device_start() override; + +private: + std::vector m_ram; +}; + +// device type definition +DECLARE_DEVICE_TYPE(CASIO_RA3, casio_ra3_device) + +#endif // MAME_CASIO_RA3_H diff --git a/src/mame/layout/cz101.lay b/src/mame/layout/cz101.lay index 9b36ad9af63..4bdcaac94e6 100644 --- a/src/mame/layout/cz101.lay +++ b/src/mame/layout/cz101.lay @@ -4,114 +4,736 @@ license:CC0-1.0 --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +