summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author m1macrophage <168948267+m1macrophage@users.noreply.github.com>2025-09-01 23:16:51 -0700
committer GitHub <noreply@github.com>2025-09-02 08:16:51 +0200
commitcde6bb48fd79408dd790fa60999148591a22b3e1 (patch)
treee419112c0b2246c5169433c6a4dcdf4ed2d82307
parentbed7e71b01f62cacd33e6c5f4c458ac658acdc68 (diff)
sequential/prophet5.cpp: Preliminary driver for the Prophet 5 Rev 3.0 (#14100)
* New systems marked not working -------------------- Sequential Circuits Prophet 5 Rev 3.0 [DBWBP]
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/sequential/prophet5.cpp970
2 files changed, 973 insertions, 0 deletions
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 84b837411dc..a872fc1e0d4 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -42469,6 +42469,9 @@ wiz
wizt
wizta
+@source:sequential/prophet5.cpp
+prophet5rev30
+
@source:sequential/prophet600.cpp
prpht600
diff --git a/src/mame/sequential/prophet5.cpp b/src/mame/sequential/prophet5.cpp
new file mode 100644
index 00000000000..514cbcfa9d3
--- /dev/null
+++ b/src/mame/sequential/prophet5.cpp
@@ -0,0 +1,970 @@
+// license:BSD-3-Clause
+// copyright-holders:m1macrophage
+
+/*
+The Prophet 5 (aka Model 1000) is a digitally-controlled, 5-voice analog
+synthesizer.
+
+The firwmare, running on a Z80, is responsible for:
+* Scanning the keyboard and buttons.
+* Scanning the value of potentiometers.
+* Driving LEDs and digit displays.
+* Reacting to inputs (footswitch, external CV and gate) and driving outputs
+ (CV, gate).
+* Cassette I/O.
+* Voice control: setting control voltages (CVs) for voice parameters, asserting
+ voice gates, and routing signals via CMOS switches.
+
+The rest of the description is for the Prophet 5 Rev 3.0, which is the revision
+being emulated. Most of this info should also apply to Revs 3.1, 3.2 and 3.3.
+
+CVs are generated by DAC71-CSB-I, a 16-bit unipolar current output DAC. Some
+units used the DAC71-CSB-V, which outputs a voltage instead. Only the 14 MSbits
+of the DAC are utilized. Frequency CVs use the full 14 bits, whereas other CVs
+just use the 7 MSbits. See update_vdac() for details.
+
+CVs are routed to 38 sample & hold (S&H) circuits via CD4051 MUXes. There are 3
+CVs for each of the 5 voices (3 x 5 = 15 CVs), controlling oscillator and filter
+frequencies. The rest (23 CVs) are common to all voices. This setup results in a
+1-part multitimbrality. See update_sh() for details.
+
+There is no dedicated ADC chip. The knobs and external CV are scanned by routing
+the knob (or input) voltages to a window comparator. Those voltages are compared
+with the ADC reference, which is controlled by the firmware. See update_vmux()
+and adc_r() for details.
+
+TODO: Outline of voice architecture.
+
+This driver is based on the Prophet 5 Rev 3.0 technical manual, and is intended
+as an education tool.
+
+There is no layout and no audio. Running with `-oslog -output console` will
+display CVs, and voice and LED control signals.
+*/
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "machine/7474.h"
+#include "machine/pit8253.h"
+#include "machine/nvram.h"
+#include "machine/output_latch.h"
+#include "machine/rescap.h"
+#include "machine/timer.h"
+#include "video/pwm.h"
+
+#define LOG_SWITCHES (1U << 1)
+#define LOG_CV (1U << 2)
+#define LOG_ADC (1U << 3)
+
+#define VERBOSE (LOG_GENERAL | LOG_CV)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+
+#include "logmacro.h"
+
+namespace {
+
+class prophet5_state : public driver_device
+{
+public:
+ static constexpr feature_type unemulated_features() { return feature::TAPE; }
+
+ prophet5_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;
+
+ void prophet5rev30(machine_config &config) ATTR_COLD;
+
+ DECLARE_INPUT_CHANGED_MEMBER(record_changed);
+ DECLARE_INPUT_CHANGED_MEMBER(gate_in_changed);
+ DECLARE_INPUT_CHANGED_MEMBER(pot_adjusted);
+ DECLARE_INPUT_CHANGED_MEMBER(dac_trimmer_adjusted);
+ DECLARE_INPUT_CHANGED_MEMBER(adc_trimmer_adjusted);
+ DECLARE_INPUT_CHANGED_MEMBER(cv_in_changed);
+ DECLARE_INPUT_CHANGED_MEMBER(seq_trimmer_adjusted);
+
+protected:
+ void machine_start() override ATTR_COLD;
+ void machine_reset() override ATTR_COLD;
+
+private:
+ static double i_bias(const required_ioport &rp, double rp_max, double r, double v);
+
+ void switch_w(u8 data);
+ u8 switch_r();
+ u8 misc_r();
+ u8 adc_r();
+
+ void led_drive_w(u8 data);
+ void led_sink_w(u8 data);
+ void led_update_w(offs_t offset, u8 data);
+
+ void update_sh();
+ void update_vdac();
+ void update_vmux();
+ void mux_abc_w(u8 data);
+ void sh_mux_inh_w(u8 data);
+ void dac_w(offs_t offset, u8 data);
+ void pot_mux_w(u8 data);
+
+ void update_gate5();
+ void latch_gate5_w(int state);
+ void clr_int_w(u8 data);
+ TIMER_DEVICE_CALLBACK_MEMBER(gate_in_delay_elapsed);
+
+ void update_nvram_record();
+
+ void memory_map(address_map &map) ATTR_COLD;
+ void io_map(address_map &map) ATTR_COLD;
+
+ required_device<z80_device> m_maincpu; // U311
+ required_device<ttl7474_device> m_tune_ff; // U322A
+ required_device<timer_device> m_gate_in_delay; // R311, C316, U331A
+ required_device<pwm_display_device> m_led_matrix_pwm;
+ required_device<pwm_display_device> m_digit_pwm;
+ memory_view m_nvram_write_view;
+ required_ioport_array<16> m_switches;
+ required_ioport m_record;
+ required_ioport m_release_footswitch;
+ required_ioport m_gate_in;
+ required_ioport m_gate_in_connected;
+ required_ioport m_test_points;
+ required_ioport_array<24> m_pots;
+ required_ioport m_dac_gain;
+ required_ioport m_adc_gain;
+ required_ioport m_seq_cv_in;
+ required_ioport m_seq_offset;
+ required_ioport m_seq_scale;
+ output_finder<> m_tune_mux_select;
+ output_finder<> m_tuning;
+ output_finder<> m_gate5;
+ std::vector<std::vector<output_finder<>>> m_leds;
+
+ u8 m_switch_row = 0; // U212 input (CD4514 decoder).
+ u8 m_mux_abc = 0; // U338 (CD4174 latch): Q3, Q2, Q5 (MSbit to LSbit).
+ u8 m_sh_mux_inh = 0x1f; // U339 (CD4174): Q4, Q1, Q3, Q2, Q5.
+ bool m_seq_cv_enabled = false; // U339 (CD4174): Q0.
+ u16 m_dac_latch = 0; // 2 x CD4174 (U336, U337) + 2 x CD4013 (U342A, B).
+ u8 m_pot_mux_abc = 0; // U211 (CD4174): Q1, Q5, Q0 (MSbit to LSbit).
+ u8 m_pot_mux_inh = 0x07; // U211 (CD4174): Q3, Q2, Q4.
+ double m_vdac = 0;
+ double m_adc_vmux = 0;
+ double m_adc_vref = 0;
+ bool m_tune_counter_out = false; // 8253 (U315) counter 0 output.
+ bool m_latch_gate5 = false; // U340 (CD4174) output Q4 (pin 12).
+ bool m_ext_gate5 = false; // U330B (CD4013) output Q (pin 13).
+ std::array<double, 40> m_cv;
+
+ static inline constexpr double VPLUS = 15.0;
+ static inline constexpr double VMINUS = -15.0;
+};
+
+prophet5_state::prophet5_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_tune_ff(*this, "tune_ff")
+ , m_gate_in_delay(*this, "gate_in_delay")
+ , m_led_matrix_pwm(*this, "led_matrix_pwm")
+ , m_digit_pwm(*this, "led_digit_pwm")
+ , m_nvram_write_view(*this, "nvram_write_view")
+ , m_switches(*this, "switch_row_%u", 0U)
+ , m_record(*this, "record")
+ , m_release_footswitch(*this, "release_footswitch")
+ , m_gate_in(*this, "seq_gate_in")
+ , m_gate_in_connected(*this, "gate_in_connected")
+ , m_test_points(*this, "test_points")
+ , m_pots(*this, "pot_%u", 0U)
+ , m_dac_gain(*this, "trimmer_dac_gain")
+ , m_adc_gain(*this, "trimmer_adc_gain")
+ , m_seq_cv_in(*this, "seq_cv_in")
+ , m_seq_offset(*this, "trimmer_seq_offset")
+ , m_seq_scale(*this, "trimmer_seq_scale")
+ , m_tune_mux_select(*this, "tune_mux_select")
+ , m_tuning(*this, "tuning")
+ , m_gate5(*this, "gate5")
+{
+ std::fill(m_cv.begin(), m_cv.end(), 0);
+
+ static constexpr const char *LED_NAMES[8][5] =
+ {
+ {"osc_a_sqr", "pmod_freq_a", "wmod_freq_a", "ps1", "record"},
+ {"osc_a_saw", "pmod_pw_a", "wmod_freq_b", "ps2", "unused_1"},
+ {"osc_a_sync", "pmod_filt", "wmod_pw_a", "ps3", "a_440"},
+ {"osc_b_saw", "lfo_saw", "wmod_pw_b", "ps4", "tune"},
+ {"osc_b_tri", "lfo_tri", "wmod_filt", "ps5", "to_cass"},
+ {"osc_b_sqr", "lfo_sqr", "osc_b_lo", "ps6", "from_cass"},
+ {"osc_b_kbd", "filt_kbd", "unused_2", "ps7", "unused_3"},
+ {"unison", "release", "unused_4", "ps8", "preset"},
+ };
+
+ for (int y = 0; y < 8; ++y)
+ {
+ m_leds.push_back(std::vector<output_finder<>>());
+ for (int x = 0; x < 5; ++x)
+ m_leds[y].push_back(output_finder<>(*this, std::string("led_") + LED_NAMES[y][x]));
+ }
+}
+
+// Computes the current through resistor R, from the junction of the resistors
+// towards V. Rp1 and Rp2 are the two sides of a single potentiometer.
+//
+// V+ --- Rp1 --*-- Rp2 --- V-
+// |
+// R
+// |
+// V
+double prophet5_state::i_bias(const required_ioport &rp, double rp_max, double r, double v)
+{
+ const double rp1 = rp_max * rp->read() / 100.0;
+ const double rp2 = rp_max - rp1;
+ // Compute voltage at the junction of all resistors.
+ const double vx = (r * rp1 * VMINUS + r * rp2 * VPLUS + rp1 * rp2 * v) / (r * rp1 + r * rp2 + rp1 * rp2);
+ return (vx - v) / r;
+}
+
+void prophet5_state::switch_w(u8 data)
+{
+ m_switch_row = data & 0x0f;
+}
+
+u8 prophet5_state::switch_r()
+{
+ const u8 pressed = m_switches[m_switch_row]->read();
+ if (pressed)
+ LOGMASKED(LOG_SWITCHES, "Switches - row: %d, pressed: %02x\n", m_switch_row, pressed);
+ return pressed;
+}
+
+u8 prophet5_state::misc_r()
+{
+ const u8 d0 = 1; // Cassette in. Will settle to 1 when there is no cassette input.
+ const u8 d1 = BIT(m_release_footswitch->read(), 0);
+ const u8 d2 = m_tune_ff->output_comp_r();
+ const u8 d3 = BIT(m_gate_in_connected->read(), 0); // External gate enabled (input connected).
+ const u8 d4 = BIT(m_record->read(), 0); // Record enabled (NVRAM write protect disabled).
+ const u8 d5 = m_tune_counter_out ? 1 : 0;
+ return (d5 << 5) | (d4 << 4) | (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
+}
+
+u8 prophet5_state::adc_r()
+{
+ // The ADC consists of two comparators (U365C,D, LM339). A network of
+ // resistors and diodes create the reference inputs to the two comparators,
+ // by adding and subtracting 34mV to the ADC reference. The ADC reference is
+ // nominally half the DAC output voltage.
+ const u8 d0 = (m_adc_vmux < m_adc_vref - 0.034) ? 1 : 0; // ADC LO
+ const u8 d1 = (m_adc_vmux > m_adc_vref + 0.034) ? 1 : 0; // ADC HI
+ if (d0 || d1)
+ {
+ LOGMASKED(LOG_ADC, "ADC: Vmux: %f, Vref: %f - lo: %d, hi: %d\n",
+ m_adc_vmux, m_adc_vref, d0, d1);
+ }
+
+ const u8 test_points = m_test_points->read();
+ const u8 d2 = BIT(test_points, 1); // TP301
+ const u8 d3 = BIT(test_points, 4); // TP304
+ const u8 d4 = 0; // Connected to GND.
+ const u8 d5 = BIT(test_points, 6); // TP306
+
+ return (d5 << 5) | (d4 << 4) | (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
+}
+
+void prophet5_state::led_drive_w(u8 data)
+{
+ m_led_matrix_pwm->write_my(data);
+ m_digit_pwm->write_my(data & 0x7f);
+}
+
+void prophet5_state::led_sink_w(u8 data)
+{
+ // The full LED matrix size is 8x7. Columns 0-4 control individual LEDs
+ // (m_led_matrix_pwm), and columns 5 and 6 control the "bank" and "program"
+ // 7-segment digit displays, respectively (m_digit_pwm). Only the first 7
+ // rows are used for the digit displays.
+ m_led_matrix_pwm->write_mx(data & 0x1f);
+ m_digit_pwm->write_mx((data >> 5) & 0x03);
+}
+
+void prophet5_state::led_update_w(offs_t offset, u8 data)
+{
+ m_leds[offset & 0x3f][offset >> 6] = data;
+}
+
+void prophet5_state::update_sh()
+{
+ constexpr const char *CV_NAMES[40] =
+ {
+ "FILT ATTACK CV", "FILT DECAY CV", "FILT SUSTAIN CV", "FILT RELEASE CV",
+ "AMP ATTACK CV", "AMP DECAY CV", "AMP SUSTAIN CV", "AMP RELEASE CV",
+ "FILT CUTOFF CV", "FILT ENV AMT CC", "MIX OSC B CC", "OSC B PW CV",
+ "MIX OSC A CC", "OSC A PW CV", "MIX NOISE CC", "FILT RESONANCE CV",
+ "GLIDE CV", "LFO FREQ CV", "WMOD SRC MIX CV", "PMOD OSC B CC",
+ "PMOD ENV AMT CC", "UNISON CV", "SEQ CV OUT", "NOT CONNECTED 1",
+ "OSC 1A S/H", "OSC 1B S/H", "OSC 2A S/H", "OSC 2B S/H",
+ "OSC 3A S/H", "OSC 3B S/H", "OSC 4A S/H", "OSC 4B S/H",
+ "OSC 5A S/H", "OSC 5B S/H", "FILT 1 S/H", "FILT 2 S/H",
+ "FILT 3 S/H", "FILT 4 S/H", "FILT 5 S/H", "NOT CONNECTED 2",
+ };
+
+ if ((m_sh_mux_inh & 0x1f) == 0x1f)
+ return; // Exit early if no S&H is selected.
+
+ for (int i = 0; i < 5; ++i)
+ {
+ if (!BIT(m_sh_mux_inh, i)) // Active low.
+ {
+ const int cv_index = 8 * i + m_mux_abc;
+ if (m_vdac != m_cv[cv_index])
+ {
+ m_cv[cv_index] = m_vdac;
+ LOGMASKED(LOG_CV, "Set CV: %d - %s to %04x (%f V)\n",
+ cv_index, CV_NAMES[cv_index], m_dac_latch, m_vdac);
+ }
+ }
+ }
+}
+
+void prophet5_state::update_vdac()
+{
+ // CVs are produced by DAC71-CSB-I, a 16-bit, unipolar, current output DAC.
+ // Some units shipped with the DAC71-CSB-V (voltage output), with
+ // corresponding changes to the DAC output buffer.
+
+ constexpr double I_FS_NOMINAL = 1.99997e-3; // From the datasheet.
+ constexpr double DAC_RF = RES_K(5); // Internal DAC71-CSB feedback resistor.
+
+ // The contents of the DAC latch are inverted by U343, U344 and U345
+ // (CD4049). While the DAC71 is a 16-bit DAC, only the 14 MSbits are used.
+ // The 2 LSbits are pulled high.
+ const u16 dac_input = (~m_dac_latch << 2) | 0x03;
+
+ // Compute the full-scale (max) output current.
+ const double i_fs = I_FS_NOMINAL + i_bias(m_dac_gain, RES_K(100), RES_K(100), 0); // R333, R329
+ // Adding i_bias() is a guess. There were no details found on the
+ // quantitative effects of DAC Gain. But the implementation is qualitatively
+ // correct: according to graphs on the datasheet, gain affects i_out
+ // proportionally with scale. Furthermore, the adjustment range that results
+ // from adding i_bias() (+/- ~0.78V) seems reasonable.
+
+ // The DAC sinks its max current (i_fs) when the input is 0, and sinks no
+ // current when the input is 0xffff.
+ const double i_out = i_fs * (0xffff - dac_input) / double(0xffff);
+
+ // The current is converted to a voltage by U347 (LF356 op-amp) and
+ // surrounding resistors.
+ m_vdac = i_out * (DAC_RF + RES_R(332)); // R330
+ update_sh();
+
+ // The DAC voltage is scaled down and used as a reference for the ADC. It
+ // will be divided by ~2 if ADC Gain is properly calibrated.
+ const double adc_gain = RES_K(5) * m_adc_gain->read() / 100.0; // R334
+ m_adc_vref = m_vdac * RES_VOLTAGE_DIVIDER(adc_gain + RES_K(18.2), RES_K(20.0)); // R335, R336
+}
+
+void prophet5_state::update_vmux()
+{
+ // The Vmux signal is one of the inputs to the ADC comparator. The other
+ // one is the ADC reference (m_adc_vref).
+
+ double vmux_sum = 0;
+ int n_active_switches = 0;
+
+ constexpr double POT_V_MAX = 5.0;
+ for (int mux = 0; mux < 3; ++mux)
+ {
+ if (!BIT(m_pot_mux_inh, mux)) // Active low.
+ {
+ const int pot_index = 8 * mux + m_pot_mux_abc;
+ vmux_sum += POT_V_MAX * m_pots[pot_index]->read() / 100.0;
+ ++n_active_switches;
+ }
+ }
+
+ if (m_seq_cv_enabled) // U371C (CD4016) control input.
+ {
+ // CV input is expected to be 0-10V.
+ const double cv_in = 10.0 * m_seq_cv_in->read() / 100.0;
+
+ // The external CV is buffered, scaled, and offsetted by U374A (LM348
+ // op-amp) and surrounding circuit.
+
+ // Scale resistor network.
+ constexpr double R396 = RES_R(470);
+ constexpr double R392 = RES_K(24.9);
+ constexpr double R391 = RES_K(30.1);
+ const double R386 = RES_K(10) * m_seq_scale->read() / 100.0;
+ const double scaled_cv = cv_in * RES_VOLTAGE_DIVIDER(R396 + R392 + R386, R391);
+
+ // Offset resistor network.
+ constexpr double R389 = RES_K(1);
+ constexpr double R390 = RES_M(1);
+ constexpr double R385_MAX = RES_K(100);
+ const double offset = R389 * i_bias(m_seq_offset, R385_MAX, R390, scaled_cv);
+
+ vmux_sum += scaled_cv + offset;
+ ++n_active_switches;
+ }
+
+ // Each MUX output and the SEQ CV switch output have a 1K resistor to protect
+ // from short circuits during startup. Under normal operation, only one
+ // switch should be selected. If (buggy) firmware activates more than one,
+ // the protection resistors will average out the voltages.
+
+ if (n_active_switches > 0)
+ m_adc_vmux = vmux_sum / n_active_switches;
+ // Else, Vmux is floating. Happens transiently under normal operation.
+ // Leaving m_adc_vmux unchanged when that happens.
+}
+
+void prophet5_state::mux_abc_w(u8 data) // U338, CD4174 latch.
+{
+ // D0-D2: ABC inputs for all S&H and Tune MUXes.
+ m_mux_abc = BIT(data, 0, 3);
+ update_sh();
+
+ // D3: -TUNE.
+ m_tuning = BIT(data, 3) ? 0 : 1;
+
+ // D4-D5: INH inputs of individual Tune MUXes.
+ m_tune_mux_select = ~BIT(data, 4, 2) & 0x03;
+}
+
+void prophet5_state::sh_mux_inh_w(u8 data) // U339, CD4174 latch.
+{
+ // D0-D4: INH inputs of individual S&H MUXes.
+ m_sh_mux_inh = BIT(data, 0, 5);
+ update_sh();
+
+ // D5: EN SEQ CV
+ m_seq_cv_enabled = BIT(data, 5);
+ update_vmux();
+}
+
+void prophet5_state::pot_mux_w(u8 data) // U211, CD4174 latch.
+{
+ m_pot_mux_abc = BIT(data, 0, 3);
+ m_pot_mux_inh = BIT(data, 3, 3);
+ update_vmux();
+}
+
+void prophet5_state::dac_w(offs_t offset, u8 data)
+{
+ if (offset == 0) // Latch low 7 bits.
+ m_dac_latch = (m_dac_latch & 0x3f80) | (data & 0x7f);
+ else if (offset == 1) // Latch high 7 bits.
+ m_dac_latch = (u16(data & 0x7f) << 7) | (m_dac_latch & 0x007f);
+ else
+ assert(false); // Should not happen.
+ update_vdac();
+}
+
+void prophet5_state::update_gate5()
+{
+ // U321B (74LS02) -> U331F (CD4049)
+ m_gate5 = (m_latch_gate5 || m_ext_gate5) ? 1 : 0;
+}
+
+void prophet5_state::latch_gate5_w(int state)
+{
+ m_latch_gate5 = bool(state);
+ update_gate5();
+}
+
+void prophet5_state::clr_int_w(u8 data)
+{
+ // Flipflop U330A (4013) R input asserted, making /Q (-> /INT) = 1.
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+
+ // Flipflop U330B (4013) S input asserted, making Q (-> m_ext_gate5) = 1.
+ m_ext_gate5 = true;
+ update_gate5();
+
+ if (!BIT(m_gate_in->read(), 0))
+ {
+ // In this case, U330B input R is also asserted. While both R and S are
+ // asserted, Q will be 1. But S is only asserted for the duration of the
+ // I/O strobe. Once that ends, only R will be asserted, and Q will
+ // transition to 0.
+ // This situation should not occur, unless the firmware is misbehaving.
+ m_ext_gate5 = false;
+ update_gate5();
+ }
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(prophet5_state::gate_in_delay_elapsed)
+{
+ // Flipflop U330A (4013) is clocked with D = 1, making /Q (-> /INT) = 0.
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+}
+
+void prophet5_state::update_nvram_record()
+{
+ m_nvram_write_view.select(BIT(m_record->read(), 0));
+}
+
+void prophet5_state::memory_map(address_map &map)
+{
+ map.global_mask(0x9fff); // Z80 A13 and A14 are not connected.
+
+ // Memory decoding done by U318 (74LS138).
+ map(0x0000, 0x0bff).mirror(0x8000).rom();
+ map(0x0c00, 0x0fff).readonly().share("nvram"); // 8 x 1K x 1bit RAMs (6508, U301-U308).
+ map(0x1000, 0x13ff).mirror(0x8000).ram(); // 2 x 1K x 4bit RAMs (2114, U316, U317).
+ map(0x1800, 0x1803).mirror(0x83fc).rw("tune_pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
+ map(0x8c00, 0x8fff).view(m_nvram_write_view);
+
+ // The "record" switch write protects the NVRAM. When "record" is enabled,
+ // U320C (74LS00) will assert RAM /WR when A15=1. When "record" is disabled,
+ // /WR will not be asserted, which will result in an NVRAM read.
+ m_nvram_write_view[0](0x8c00, 0x8fff).readonly().share("nvram");
+ m_nvram_write_view[1](0x8c00, 0x8fff).writeonly().share("nvram");
+}
+
+void prophet5_state::io_map(address_map &map)
+{
+ // The signal names in the comments below (e.g. "CSI0, KBD/SW") match those
+ // in the schematics.
+ map.global_mask(0x3f); // Only A0-A5 are used for port decoding.
+
+ // Input port decoding done by U310 A,B,C (74LS00).
+ map(0x01, 0x01).mirror(0x3e).r(FUNC(prophet5_state::switch_r)); // CSI0, KBD/SW
+ map(0x02, 0x02).mirror(0x3d).r(FUNC(prophet5_state::misc_r)); // CSI1, CASS/MISC
+ map(0x04, 0x04).mirror(0x3b).r(FUNC(prophet5_state::adc_r)); // CSI2, ADC
+
+ // Output port decoding for TTL chips done by U319 (74LS138).
+ map(0x00, 0x00).mirror(0x07).w(FUNC(prophet5_state::led_drive_w)); // CSOL0, LED DRVR
+ map(0x08, 0x08).mirror(0x07).w(FUNC(prophet5_state::led_sink_w)); // CSOL1, LED SINK
+ map(0x10, 0x10).mirror(0x07).w(FUNC(prophet5_state::switch_w)); // CSOL2, KBD/SW DRVR
+ map(0x18, 0x18).mirror(0x07).w(FUNC(prophet5_state::pot_mux_w)); // CSOL3, POT MUX ADR
+ map(0x20, 0x20).mirror(0x07).w("misc_latch", FUNC(output_latch_device::write)); // CSOL4, CASS/TUNE
+ map(0x28, 0x28).mirror(0x07).w(FUNC(prophet5_state::clr_int_w)); // CSOL5, CLEAR INT
+
+ // Output port decoding for 15V CMOS chips done by U329 (CD4556).
+ map(0x30, 0x30).mirror(0x04).w("program_latch_0", FUNC(output_latch_device::write)); // CSOH0, PROG SW 0
+ map(0x31, 0x31).mirror(0x04).w("program_latch_1", FUNC(output_latch_device::write)); // CSOH1, PROG SW 1
+ map(0x32, 0x32).mirror(0x04).w("program_latch_2", FUNC(output_latch_device::write)); // CSOH2, PROG SW 2
+ map(0x33, 0x33).mirror(0x04).w(FUNC(prophet5_state::mux_abc_w)); // CSOH3, S/H ABC/TUNE
+ map(0x38, 0x38).mirror(0x04).w(FUNC(prophet5_state::sh_mux_inh_w)); // CSOH4, S/H
+ map(0x39, 0x39).mirror(0x04).w("gate_latch", FUNC(output_latch_device::write)); // CSOH5, GATES
+ map(0x3a, 0x3b).mirror(0x04).w(FUNC(prophet5_state::dac_w)); // CSOH6, DAC LSB - CSOH7, DAC MSB
+}
+
+void prophet5_state::machine_start()
+{
+ save_item(NAME(m_switch_row));
+ save_item(NAME(m_mux_abc));
+ save_item(NAME(m_sh_mux_inh));
+ save_item(NAME(m_seq_cv_enabled));
+ save_item(NAME(m_dac_latch));
+ save_item(NAME(m_pot_mux_abc));
+ save_item(NAME(m_pot_mux_inh));
+ save_item(NAME(m_vdac));
+ save_item(NAME(m_adc_vmux));
+ save_item(NAME(m_adc_vref));
+ save_item(NAME(m_tune_counter_out));
+ save_item(NAME(m_latch_gate5));
+ save_item(NAME(m_ext_gate5));
+ save_item(NAME(m_cv));
+
+ m_tune_mux_select.resolve();
+ m_tuning.resolve();
+ m_gate5.resolve();
+ for (auto &led_row : m_leds)
+ for (auto &led : led_row)
+ led.resolve();
+}
+
+void prophet5_state::machine_reset()
+{
+ update_nvram_record();
+}
+
+void prophet5_state::prophet5rev30(machine_config &config)
+{
+ Z80(config, m_maincpu, 5_MHz_XTAL / 2); // Divided by U325.
+ m_maincpu->set_addrmap(AS_PROGRAM, &prophet5_state::memory_map);
+ m_maincpu->set_addrmap(AS_IO, &prophet5_state::io_map);
+
+ NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
+
+ auto &pit = PIT8253(config, "tune_pit"); // U315
+ pit.out_handler<0>().set("tune_pit", FUNC(pit8253_device::write_gate2)).invert();
+ pit.out_handler<0>().append([this] (int state) { m_tune_counter_out = bool(state); });
+ pit.set_clk<1>(5_MHz_XTAL / 2);
+ pit.set_clk<2>(5_MHz_XTAL / 2);
+
+ TTL7474(config, m_tune_ff, 0).comp_output_cb().set("tune_pit", FUNC(pit8253_device::write_clk0));
+
+ TIMER(config, m_gate_in_delay).configure_generic(FUNC(prophet5_state::gate_in_delay_elapsed));
+
+ PWM_DISPLAY(config, m_led_matrix_pwm).set_size(8, 5);
+ m_led_matrix_pwm->output_x().set(FUNC(prophet5_state::led_update_w));
+
+ PWM_DISPLAY(config, m_digit_pwm).set_size(2, 7);
+ m_digit_pwm->set_segmask(0x03, 0x7f);
+
+ auto &u332 = OUTPUT_LATCH(config, "misc_latch");
+ u332.bit_handler<0>().set(m_tune_ff, FUNC(ttl7474_device::clear_w));
+ u332.bit_handler<1>().set(m_tune_ff, FUNC(ttl7474_device::preset_w));
+ u332.bit_handler<2>().set_output("cassette_out");
+ u332.bit_handler<3>().set(m_tune_ff, FUNC(ttl7474_device::d_w));
+ u332.bit_handler<4>().set("tune_pit", FUNC(pit8253_device::write_gate0));
+ u332.bit_handler<5>().set("tune_pit", FUNC(pit8253_device::write_gate1));
+ u332.bit_handler<5>().append_output("select_440");
+
+ auto &u335 = OUTPUT_LATCH(config, "program_latch_0");
+ u335.bit_handler<0>().set_output("osc_a_sqr");
+ u335.bit_handler<1>().set_output("osc_a_saw");
+ u335.bit_handler<2>().set_output("osc_a_sync");
+ u335.bit_handler<3>().set_output("osc_b_saw");
+ u335.bit_handler<4>().set_output("osc_b_tri");
+ u335.bit_handler<5>().set_output("osc_b_sqr");
+
+ auto &u334 = OUTPUT_LATCH(config, "program_latch_1");
+ u334.bit_handler<0>().set_output("pmod_freq_a");
+ u334.bit_handler<1>().set_output("pmod_pw_a");
+ u334.bit_handler<2>().set_output("pmod_filt");
+ u334.bit_handler<3>().set_output("lfo_saw");
+ u334.bit_handler<4>().set_output("lfo_tri");
+ u334.bit_handler<5>().set_output("lfo_sqr");
+
+ auto &u333 = OUTPUT_LATCH(config, "program_latch_2");
+ u333.bit_handler<0>().set_output("wmod_freq_a");
+ u333.bit_handler<1>().set_output("wmod_freq_b");
+ u333.bit_handler<2>().set_output("wmod_pw_a");
+ u333.bit_handler<3>().set_output("wmod_pw_b");
+ u333.bit_handler<4>().set_output("wmod_filt");
+ u333.bit_handler<5>().set_output("osc_b_lo");
+
+ auto &u340 = OUTPUT_LATCH(config, "gate_latch");
+ u340.bit_handler<0>().set_output("gate1");
+ u340.bit_handler<1>().set_output("gate2");
+ u340.bit_handler<2>().set_output("gate3");
+ u340.bit_handler<3>().set_output("gate4");
+ u340.bit_handler<4>().set(FUNC(prophet5_state::latch_gate5_w));
+ u340.bit_handler<5>().set_output("gate_out");
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::record_changed)
+{
+ update_nvram_record();
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::gate_in_changed)
+{
+ if (newval)
+ {
+ // An RC circuit adds a delay between receiving the gate-in signal and
+ // asserting /INT. `DT` is the time it takes for the RC network to
+ // discharge from 5V to 2.5V and trip the inverter (U331A, CD4049). This
+ // is ~1.4ms nominal. The schematic says "2ms delay". The actual delay
+ // is not well-specified, since it depends on the trip point of the
+ // inverter.
+ const double DT = -RES_K(100) * CAP_U(0.02) * log(2.5 / 5.0); // R311, C316
+ m_gate_in_delay->adjust(attotime::from_double(DT));
+ }
+ else
+ {
+ m_gate_in_delay->reset();
+ m_ext_gate5 = false; // Flipflop U330B (4013) R input asserted, making Q (-> m_ext_gate5) = 0.
+ update_gate5();
+ }
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::pot_adjusted)
+{
+ update_vmux();
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::dac_trimmer_adjusted)
+{
+ update_vdac();
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::adc_trimmer_adjusted)
+{
+ update_vdac();
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::cv_in_changed)
+{
+ update_vmux();
+}
+
+DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::seq_trimmer_adjusted)
+{
+ update_vmux();
+}
+
+INPUT_PORTS_START(prophet5)
+ PORT_START("switch_row_0")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SQR") PORT_CODE(KEYCODE_A)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SAW") PORT_CODE(KEYCODE_S)
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SYNC")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B SAW")
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B TRI")
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B SQR")
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B KBD")
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("UNISON")
+
+ PORT_START("switch_row_1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD FREQ A")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD PW A")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD FILT")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO SAW")
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO TRI")
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO SQR")
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("FILT KBD")
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("RELEASE")
+
+ PORT_START("switch_row_2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FREQ A")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FREQ B")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD PW A")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD PW B")
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FILT")
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B LO")
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("switch_row_3")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS1") PORT_CODE(KEYCODE_1)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS2") PORT_CODE(KEYCODE_2)
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS3") PORT_CODE(KEYCODE_3)
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS4") PORT_CODE(KEYCODE_4)
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS5") PORT_CODE(KEYCODE_5)
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS6") PORT_CODE(KEYCODE_6)
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS7") PORT_CODE(KEYCODE_7)
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS8") PORT_CODE(KEYCODE_8)
+
+ PORT_START("switch_row_4")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("RECORD") PORT_CODE(KEYCODE_R)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("BANK SELECT")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("A-440")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TUNE")
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TO CASS")
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("FROM CASS")
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PRESET")
+
+ PORT_START("switch_row_5")
+ PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("switch_row_6")
+ PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("switch_row_7")
+ PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("switch_row_8") // C0 - G0 in schematic.
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C2 PORT_CODE(KEYCODE_Z)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS2
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D2
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS2
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E2
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F2
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS2
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G2
+
+ PORT_START("switch_row_9") // G#0 - D#1
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS2
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A2
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS2
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B2
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C3 PORT_CODE(KEYCODE_X)
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS3
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D3
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS3
+
+ PORT_START("switch_row_10") // E1 - B1
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E3
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F3
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS3
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G3
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS3
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A3
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS3
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B3
+
+ PORT_START("switch_row_11") // C2 - G2
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C4 PORT_CODE(KEYCODE_C)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS4
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E4
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F4
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G4
+
+ PORT_START("switch_row_12") // G#2 - D#3
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS4
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A4
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS4
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B4
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C5 PORT_CODE(KEYCODE_V)
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS5
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4
+
+ PORT_START("switch_row_13") // E3 - B3
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E5
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F5
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G5
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS5
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A5
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS5
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B5
+
+ PORT_START("switch_row_14") // C4 - G4
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C6 PORT_CODE(KEYCODE_B)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS6
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D6
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS6
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E6
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F6
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS6
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G6
+
+ PORT_START("switch_row_15") // G#4 - C5
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS6
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A6
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS6
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B6
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C7 PORT_CODE(KEYCODE_N)
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ // NVRAM (patch memory) write protect switch on back panel. It electrically
+ // disables writes to the NVRAM (blocks the /WR signal, look for
+ // m_nvram_write_view), and its state can be read by the firmware (see
+ // misc_r()).
+ PORT_START("record")
+ PORT_CONFNAME(0x01, 0x01, "RECORD EN DIS")
+ PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::record_changed), 0)
+ PORT_CONFSETTING(0x00, "Disable")
+ PORT_CONFSETTING(0x01, "Enable")
+
+ PORT_START("gate_in_connected")
+ PORT_CONFNAME(0x01, 0x00, "EXT GATE EN")
+ PORT_CONFSETTING(0x00, "Not connected")
+ PORT_CONFSETTING(0x01, "Connected")
+
+ PORT_START("seq_gate_in")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SEQ GATE IN") PORT_CODE(KEYCODE_G)
+ PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::gate_in_changed), 0)
+
+ PORT_START("release_footswitch")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("REL FT SW")
+
+ PORT_START("test_points")
+ // According to the schematic, TP301 and TP304 have pull-down resistors, and
+ // TP306 does not have a resistor.
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP301") PORT_CODE(KEYCODE_T)
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP304")
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP306")
+
+ // All knob potentiometers are 10K linear.
+
+ PORT_START("pot_0") // R217
+ PORT_ADJUSTER(50, "GLIDE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 0)
+
+ PORT_START("pot_1") // R211
+ PORT_ADJUSTER(50, "LFO FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 1)
+
+ PORT_START("pot_2") // R216
+ PORT_ADJUSTER(50, "WMOD SRC MIX") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 2)
+
+ PORT_START("pot_3") // R202
+ PORT_ADJUSTER(50, "PMOD OSC B") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 3)
+
+ PORT_START("pot_4") // R201
+ PORT_ADJUSTER(50, "PMOD FILT ENV") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 4)
+
+ PORT_START("pot_5") // R204
+ PORT_ADJUSTER(50, "OSC A FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 5)
+
+ PORT_START("pot_6") // R213
+ PORT_ADJUSTER(50, "OSC B FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 6)
+
+ PORT_START("pot_7") // R214
+ PORT_ADJUSTER(50, "OSC B FINE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 7)
+
+ PORT_START("pot_8") // R101
+ PORT_ADJUSTER(50, "FILT CUTOFF") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 8)
+
+ PORT_START("pot_9") // R103
+ PORT_ADJUSTER(50, "FILT ENV AMT") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 9)
+
+ PORT_START("pot_10") // R208
+ PORT_ADJUSTER(50, "MIX OSC B") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 10)
+
+ PORT_START("pot_11") // R215
+ PORT_ADJUSTER(50, "OSC B PW") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 11)
+
+ PORT_START("pot_12") // R207
+ PORT_ADJUSTER(50, "MIX OSC A") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 12)
+
+ PORT_START("pot_13") // R205
+ PORT_ADJUSTER(50, "OSC A PW") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 13)
+
+ PORT_START("pot_14") // R210
+ PORT_ADJUSTER(50, "MIX NOISE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 14)
+
+ PORT_START("pot_15") // R102
+ PORT_ADJUSTER(50, "FILT RESONANCE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 15)
+
+ PORT_START("pot_16") // R105
+ PORT_ADJUSTER(50, "FILT ATTACK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 16)
+
+ PORT_START("pot_17") // R106
+ PORT_ADJUSTER(50, "FILT DECAY") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 17)
+
+ PORT_START("pot_18") // R107
+ PORT_ADJUSTER(50, "FILT SUSTAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 18)
+
+ PORT_START("pot_19") // R108
+ PORT_ADJUSTER(50, "FILT RELEASE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 19)
+
+ PORT_START("pot_20") // R109
+ PORT_ADJUSTER(50, "AMP ATTACK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 20)
+
+ PORT_START("pot_21") // R110
+ PORT_ADJUSTER(50, "AMP DECAY") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 21)
+
+ PORT_START("pot_22") // R111
+ PORT_ADJUSTER(50, "AMP SUSTAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 22)
+
+ PORT_START("pot_23") // R112
+ PORT_ADJUSTER(50, "AMP RELEASE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 23)
+
+ PORT_START("trimmer_dac_gain") // R333, 100K trimmer.
+ // Default value based on calibration instructions, with a small error due
+ // to adjuster resolution.
+ PORT_ADJUSTER(50, "TRIMMER: DAC GAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::dac_trimmer_adjusted), 0)
+
+ PORT_START("trimmer_adc_gain") // R334, 5K trimmer.
+ // Default value calibrated for the required: Vadcref = Vdac / 2.
+ PORT_ADJUSTER(36, "TRIMMER: ADC GAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::adc_trimmer_adjusted), 0)
+
+ PORT_START("seq_cv_in") // J2, external CV input.
+ PORT_ADJUSTER(50, "SEQ CV IN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::cv_in_changed), 0)
+
+ PORT_START("trimmer_seq_offset") // R385, 100K trimmer.
+ PORT_ADJUSTER(50, "TRIMMER: SEQ OFFSET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::seq_trimmer_adjusted), 0)
+
+ PORT_START("trimmer_seq_scale") // R386, 10K trimmer.
+ // Default value calibrated for ADC_CV_SEQ_IN = CV_SEQ_IN / 2, with some
+ // error due to adjuster resolution. Exact calibration works out to 47.3.
+ PORT_ADJUSTER(47, "TRIMMER: SEQ SCALE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::seq_trimmer_adjusted), 1)
+INPUT_PORTS_END
+
+ROM_START(prophet5rev30)
+ ROM_REGION(0xc00, "maincpu", 0) // 3 x 2708 1Kbyte ROMS.
+ ROM_LOAD("0.v8.1.u312", 0x000000, 0x000400, CRC(6337d2ae) SHA1(bad79f6475dc0a8bb139ea0a12258cb3e5bfa0be))
+ ROM_LOAD("1.v8.1.u313", 0x000400, 0x000400, CRC(1e334fd3) SHA1(276b7abf4a13fbae0d09e869f786b3073ee82504))
+ ROM_LOAD("2.v8.1.u314", 0x000800, 0x000400, CRC(ffafaa95) SHA1(9d119fb22270d45e34c1f16899453ae7469d7d20))
+ROM_END
+
+} // anonymous namespace
+
+// Prophet 5 Rev 3.0, serial numbers 1301-2285.
+SYST(1980, prophet5rev30, 0, 0, prophet5rev30, prophet5, prophet5_state, empty_init, "Sequential Circuits", "Prophet 5 (Model 1000) Rev 3.0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE)