diff options
| author | 2025-09-01 23:16:51 -0700 | |
|---|---|---|
| committer | 2025-09-02 08:16:51 +0200 | |
| commit | cde6bb48fd79408dd790fa60999148591a22b3e1 (patch) | |
| tree | e419112c0b2246c5169433c6a4dcdf4ed2d82307 | |
| parent | bed7e71b01f62cacd33e6c5f4c458ac658acdc68 (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.lst | 3 | ||||
| -rw-r--r-- | src/mame/sequential/prophet5.cpp | 970 |
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) |
